xref: /openbsd-src/sys/arch/amd64/stand/libsa/softraid_amd64.c (revision 855f8c0393a31efa1a61652932a7d72cbafe4cfa)
1*855f8c03Sstsp /*	$OpenBSD: softraid_amd64.c,v 1.8 2022/08/12 20:17:46 stsp Exp $	*/
265f4a3c7Sjsing 
365f4a3c7Sjsing /*
465f4a3c7Sjsing  * Copyright (c) 2012 Joel Sing <jsing@openbsd.org>
565f4a3c7Sjsing  *
665f4a3c7Sjsing  * Permission to use, copy, modify, and distribute this software for any
765f4a3c7Sjsing  * purpose with or without fee is hereby granted, provided that the above
865f4a3c7Sjsing  * copyright notice and this permission notice appear in all copies.
965f4a3c7Sjsing  *
1065f4a3c7Sjsing  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1165f4a3c7Sjsing  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1265f4a3c7Sjsing  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1365f4a3c7Sjsing  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1465f4a3c7Sjsing  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1565f4a3c7Sjsing  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1665f4a3c7Sjsing  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1765f4a3c7Sjsing  */
1865f4a3c7Sjsing 
1965f4a3c7Sjsing #include <sys/param.h>
2065f4a3c7Sjsing #include <sys/queue.h>
2165f4a3c7Sjsing #include <sys/disklabel.h>
2265f4a3c7Sjsing #include <sys/reboot.h>
2365f4a3c7Sjsing 
2465f4a3c7Sjsing #include <dev/biovar.h>
2565f4a3c7Sjsing #include <dev/softraidvar.h>
2665f4a3c7Sjsing 
2765f4a3c7Sjsing #include <lib/libsa/aes_xts.h>
28e876def9Sjsing #include <lib/libsa/softraid.h>
2965f4a3c7Sjsing #include <lib/libz/zlib.h>
3065f4a3c7Sjsing 
3165f4a3c7Sjsing #include "libsa.h"
3265f4a3c7Sjsing #include "disk.h"
3365f4a3c7Sjsing #include "softraid_amd64.h"
3465f4a3c7Sjsing 
3565f4a3c7Sjsing static int gpt_chk_mbr(struct dos_partition *, u_int64_t);
3665f4a3c7Sjsing static uint64_t findopenbsd_gpt(struct sr_boot_volume *, const char **);
3765f4a3c7Sjsing 
3865f4a3c7Sjsing void
srprobe_meta_opt_load(struct sr_metadata * sm,struct sr_meta_opt_head * som)3965f4a3c7Sjsing srprobe_meta_opt_load(struct sr_metadata *sm, struct sr_meta_opt_head *som)
4065f4a3c7Sjsing {
4165f4a3c7Sjsing 	struct sr_meta_opt_hdr	*omh;
4265f4a3c7Sjsing 	struct sr_meta_opt_item *omi;
4365f4a3c7Sjsing #if 0
4465f4a3c7Sjsing 	u_int8_t checksum[MD5_DIGEST_LENGTH];
4565f4a3c7Sjsing #endif
4665f4a3c7Sjsing 	int			i;
4765f4a3c7Sjsing 
4865f4a3c7Sjsing 	/* Process optional metadata. */
4965f4a3c7Sjsing 	omh = (struct sr_meta_opt_hdr *)((u_int8_t *)(sm + 1) +
5065f4a3c7Sjsing 	    sizeof(struct sr_meta_chunk) * sm->ssdi.ssd_chunk_no);
5165f4a3c7Sjsing 	for (i = 0; i < sm->ssdi.ssd_opt_no; i++) {
5265f4a3c7Sjsing 
5365f4a3c7Sjsing #ifdef BIOS_DEBUG
5465f4a3c7Sjsing 		printf("Found optional metadata of type %u, length %u\n",
5565f4a3c7Sjsing 		    omh->som_type, omh->som_length);
5665f4a3c7Sjsing #endif
5765f4a3c7Sjsing 
5865f4a3c7Sjsing 		/* Unsupported old fixed length optional metadata. */
5965f4a3c7Sjsing 		if (omh->som_length == 0) {
6065f4a3c7Sjsing 			omh = (struct sr_meta_opt_hdr *)((void *)omh +
6165f4a3c7Sjsing 			    SR_OLD_META_OPT_SIZE);
6265f4a3c7Sjsing 			continue;
6365f4a3c7Sjsing 		}
6465f4a3c7Sjsing 
6565f4a3c7Sjsing 		/* Load variable length optional metadata. */
6665f4a3c7Sjsing 		omi = alloc(sizeof(struct sr_meta_opt_item));
6765f4a3c7Sjsing 		bzero(omi, sizeof(struct sr_meta_opt_item));
6865f4a3c7Sjsing 		SLIST_INSERT_HEAD(som, omi, omi_link);
6965f4a3c7Sjsing 		omi->omi_som = alloc(omh->som_length);
7065f4a3c7Sjsing 		bzero(omi->omi_som, omh->som_length);
7165f4a3c7Sjsing 		bcopy(omh, omi->omi_som, omh->som_length);
7265f4a3c7Sjsing 
7365f4a3c7Sjsing #if 0
7465f4a3c7Sjsing 		/* XXX - Validate checksum. */
7565f4a3c7Sjsing 		bcopy(&omi->omi_som->som_checksum, &checksum,
7665f4a3c7Sjsing 		    MD5_DIGEST_LENGTH);
7765f4a3c7Sjsing 		bzero(&omi->omi_som->som_checksum, MD5_DIGEST_LENGTH);
7865f4a3c7Sjsing 		sr_checksum(sc, omi->omi_som,
7965f4a3c7Sjsing 		    &omi->omi_som->som_checksum, omh->som_length);
8065f4a3c7Sjsing 		if (bcmp(&checksum, &omi->omi_som->som_checksum,
8165f4a3c7Sjsing 		    sizeof(checksum)))
8265f4a3c7Sjsing 			panic("%s: invalid optional metadata checksum",
8365f4a3c7Sjsing 			    DEVNAME(sc));
8465f4a3c7Sjsing #endif
8565f4a3c7Sjsing 
8665f4a3c7Sjsing 		omh = (struct sr_meta_opt_hdr *)((void *)omh +
8765f4a3c7Sjsing 		    omh->som_length);
8865f4a3c7Sjsing 	}
8965f4a3c7Sjsing }
9065f4a3c7Sjsing 
9165f4a3c7Sjsing void
srprobe_keydisk_load(struct sr_metadata * sm)9265f4a3c7Sjsing srprobe_keydisk_load(struct sr_metadata *sm)
9365f4a3c7Sjsing {
9465f4a3c7Sjsing 	struct sr_meta_opt_hdr	*omh;
9565f4a3c7Sjsing 	struct sr_meta_keydisk	*skm;
9665f4a3c7Sjsing 	struct sr_boot_keydisk	*kd;
9765f4a3c7Sjsing 	int i;
9865f4a3c7Sjsing 
9965f4a3c7Sjsing 	/* Process optional metadata. */
10065f4a3c7Sjsing 	omh = (struct sr_meta_opt_hdr *)((u_int8_t *)(sm + 1) +
10165f4a3c7Sjsing 	    sizeof(struct sr_meta_chunk) * sm->ssdi.ssd_chunk_no);
10265f4a3c7Sjsing 	for (i = 0; i < sm->ssdi.ssd_opt_no; i++) {
10365f4a3c7Sjsing 
10465f4a3c7Sjsing 		/* Unsupported old fixed length optional metadata. */
10565f4a3c7Sjsing 		if (omh->som_length == 0) {
10665f4a3c7Sjsing 			omh = (struct sr_meta_opt_hdr *)((void *)omh +
10765f4a3c7Sjsing 			    SR_OLD_META_OPT_SIZE);
10865f4a3c7Sjsing 			continue;
10965f4a3c7Sjsing 		}
11065f4a3c7Sjsing 
11165f4a3c7Sjsing 		if (omh->som_type != SR_OPT_KEYDISK) {
11265f4a3c7Sjsing 			omh = (struct sr_meta_opt_hdr *)((void *)omh +
11365f4a3c7Sjsing 			    omh->som_length);
11465f4a3c7Sjsing 			continue;
11565f4a3c7Sjsing 		}
11665f4a3c7Sjsing 
11765f4a3c7Sjsing 		kd = alloc(sizeof(struct sr_boot_keydisk));
11865f4a3c7Sjsing 		bcopy(&sm->ssdi.ssd_uuid, &kd->kd_uuid, sizeof(kd->kd_uuid));
11965f4a3c7Sjsing 		skm = (struct sr_meta_keydisk*)omh;
12065f4a3c7Sjsing 		bcopy(&skm->skm_maskkey, &kd->kd_key, sizeof(kd->kd_key));
12165f4a3c7Sjsing 		SLIST_INSERT_HEAD(&sr_keydisks, kd, kd_link);
12265f4a3c7Sjsing 	}
12365f4a3c7Sjsing }
12465f4a3c7Sjsing 
12565f4a3c7Sjsing void
srprobe(void)12665f4a3c7Sjsing srprobe(void)
12765f4a3c7Sjsing {
12865f4a3c7Sjsing 	struct sr_boot_volume *bv, *bv1, *bv2;
12965f4a3c7Sjsing 	struct sr_boot_chunk *bc, *bc1, *bc2;
13065f4a3c7Sjsing 	struct sr_meta_chunk *mc;
13165f4a3c7Sjsing 	struct sr_metadata *md;
13265f4a3c7Sjsing 	struct diskinfo *dip;
13365f4a3c7Sjsing 	struct partition *pp;
13465f4a3c7Sjsing 	int i, error, volno;
13565f4a3c7Sjsing 	dev_t bsd_dev;
13665f4a3c7Sjsing 	daddr_t off;
13765f4a3c7Sjsing 
13865f4a3c7Sjsing 	/* Probe for softraid volumes. */
13965f4a3c7Sjsing 	SLIST_INIT(&sr_volumes);
14065f4a3c7Sjsing 	SLIST_INIT(&sr_keydisks);
14165f4a3c7Sjsing 
14265f4a3c7Sjsing 	md = alloc(SR_META_SIZE * DEV_BSIZE);
14365f4a3c7Sjsing 
14465f4a3c7Sjsing 	TAILQ_FOREACH(dip, &disklist, list) {
14565f4a3c7Sjsing 
14665f4a3c7Sjsing 		/* Only check hard disks, skip those with I/O errors. */
14765f4a3c7Sjsing 		if ((dip->bios_info.bios_number & 0x80) == 0 ||
14865f4a3c7Sjsing 		    (dip->bios_info.flags & BDI_INVALID))
14965f4a3c7Sjsing 			continue;
15065f4a3c7Sjsing 
15165f4a3c7Sjsing 		/* Make sure disklabel has been read. */
15265f4a3c7Sjsing 		if ((dip->bios_info.flags & (BDI_BADLABEL|BDI_GOODLABEL)) == 0)
15365f4a3c7Sjsing 			continue;
15465f4a3c7Sjsing 
15565f4a3c7Sjsing 		for (i = 0; i < MAXPARTITIONS; i++) {
15665f4a3c7Sjsing 
15765f4a3c7Sjsing 			pp = &dip->disklabel.d_partitions[i];
15865f4a3c7Sjsing 			if (pp->p_fstype != FS_RAID || pp->p_size == 0)
15965f4a3c7Sjsing 				continue;
16065f4a3c7Sjsing 
16165f4a3c7Sjsing 			/* Read softraid metadata. */
16265f4a3c7Sjsing 			bzero(md, SR_META_SIZE * DEV_BSIZE);
16365f4a3c7Sjsing 			off = DL_SECTOBLK(&dip->disklabel, DL_GETPOFFSET(pp));
16465f4a3c7Sjsing 			off += SR_META_OFFSET;
16565f4a3c7Sjsing 			error = dip->diskio(F_READ, dip, off, SR_META_SIZE, md);
16665f4a3c7Sjsing 			if (error)
16765f4a3c7Sjsing 				continue;
16865f4a3c7Sjsing 
16965f4a3c7Sjsing 			/* Is this valid softraid metadata? */
17065f4a3c7Sjsing 			if (md->ssdi.ssd_magic != SR_MAGIC)
17165f4a3c7Sjsing 				continue;
17265f4a3c7Sjsing 
17365f4a3c7Sjsing 			/* XXX - validate checksum. */
17465f4a3c7Sjsing 
17565f4a3c7Sjsing 			/* Handle key disks separately... */
17665f4a3c7Sjsing 			if (md->ssdi.ssd_level == SR_KEYDISK_LEVEL) {
17765f4a3c7Sjsing 				srprobe_keydisk_load(md);
17865f4a3c7Sjsing 				continue;
17965f4a3c7Sjsing 			}
18065f4a3c7Sjsing 
18165f4a3c7Sjsing 			/* Locate chunk-specific metadata for this chunk. */
18265f4a3c7Sjsing 			mc = (struct sr_meta_chunk *)(md + 1);
18365f4a3c7Sjsing 			mc += md->ssdi.ssd_chunk_id;
18465f4a3c7Sjsing 
18565f4a3c7Sjsing 			bc = alloc(sizeof(struct sr_boot_chunk));
18665f4a3c7Sjsing 			bc->sbc_diskinfo = dip;
18765f4a3c7Sjsing 			bc->sbc_disk = dip->bios_info.bios_number;
18865f4a3c7Sjsing 			bc->sbc_part = 'a' + i;
18965f4a3c7Sjsing 
19065f4a3c7Sjsing 			bsd_dev = dip->bios_info.bsd_dev;
19165f4a3c7Sjsing 			bc->sbc_mm = MAKEBOOTDEV(B_TYPE(bsd_dev),
19265f4a3c7Sjsing 			    B_ADAPTOR(bsd_dev), B_CONTROLLER(bsd_dev),
19365f4a3c7Sjsing 			    B_UNIT(bsd_dev), bc->sbc_part - 'a');
19465f4a3c7Sjsing 
19565f4a3c7Sjsing 			bc->sbc_chunk_id = md->ssdi.ssd_chunk_id;
19665f4a3c7Sjsing 			bc->sbc_ondisk = md->ssd_ondisk;
19765f4a3c7Sjsing 			bc->sbc_state = mc->scm_status;
19865f4a3c7Sjsing 
19965f4a3c7Sjsing 			SLIST_FOREACH(bv, &sr_volumes, sbv_link) {
20065f4a3c7Sjsing 				if (bcmp(&md->ssdi.ssd_uuid, &bv->sbv_uuid,
20165f4a3c7Sjsing 				    sizeof(md->ssdi.ssd_uuid)) == 0)
20265f4a3c7Sjsing 					break;
20365f4a3c7Sjsing 			}
20465f4a3c7Sjsing 
20565f4a3c7Sjsing 			if (bv == NULL) {
20665f4a3c7Sjsing 				bv = alloc(sizeof(struct sr_boot_volume));
20765f4a3c7Sjsing 				bzero(bv, sizeof(struct sr_boot_volume));
20865f4a3c7Sjsing 				bv->sbv_level = md->ssdi.ssd_level;
20965f4a3c7Sjsing 				bv->sbv_volid = md->ssdi.ssd_volid;
21065f4a3c7Sjsing 				bv->sbv_chunk_no = md->ssdi.ssd_chunk_no;
21165f4a3c7Sjsing 				bv->sbv_flags = md->ssdi.ssd_vol_flags;
21265f4a3c7Sjsing 				bv->sbv_size = md->ssdi.ssd_size;
213f252f90fSyasuoka 				bv->sbv_secsize = md->ssdi.ssd_secsize;
21465f4a3c7Sjsing 				bv->sbv_data_blkno = md->ssd_data_blkno;
21565f4a3c7Sjsing 				bcopy(&md->ssdi.ssd_uuid, &bv->sbv_uuid,
21665f4a3c7Sjsing 				    sizeof(md->ssdi.ssd_uuid));
21765f4a3c7Sjsing 				SLIST_INIT(&bv->sbv_chunks);
21865f4a3c7Sjsing 				SLIST_INIT(&bv->sbv_meta_opt);
21965f4a3c7Sjsing 
22065f4a3c7Sjsing 				/* Load optional metadata for this volume. */
22165f4a3c7Sjsing 				srprobe_meta_opt_load(md, &bv->sbv_meta_opt);
22265f4a3c7Sjsing 
22365f4a3c7Sjsing 				/* Maintain volume order. */
22465f4a3c7Sjsing 				bv2 = NULL;
22565f4a3c7Sjsing 				SLIST_FOREACH(bv1, &sr_volumes, sbv_link) {
22665f4a3c7Sjsing 					if (bv1->sbv_volid > bv->sbv_volid)
22765f4a3c7Sjsing 						break;
22865f4a3c7Sjsing 					bv2 = bv1;
22965f4a3c7Sjsing 				}
23065f4a3c7Sjsing 				if (bv2 == NULL)
23165f4a3c7Sjsing 					SLIST_INSERT_HEAD(&sr_volumes, bv,
23265f4a3c7Sjsing 					    sbv_link);
23365f4a3c7Sjsing 				else
23465f4a3c7Sjsing 					SLIST_INSERT_AFTER(bv2, bv, sbv_link);
23565f4a3c7Sjsing 			}
23665f4a3c7Sjsing 
23765f4a3c7Sjsing 			/* Maintain chunk order. */
23865f4a3c7Sjsing 			bc2 = NULL;
23965f4a3c7Sjsing 			SLIST_FOREACH(bc1, &bv->sbv_chunks, sbc_link) {
24065f4a3c7Sjsing 				if (bc1->sbc_chunk_id > bc->sbc_chunk_id)
24165f4a3c7Sjsing 					break;
24265f4a3c7Sjsing 				bc2 = bc1;
24365f4a3c7Sjsing 			}
24465f4a3c7Sjsing 			if (bc2 == NULL)
24565f4a3c7Sjsing 				SLIST_INSERT_HEAD(&bv->sbv_chunks,
24665f4a3c7Sjsing 				    bc, sbc_link);
24765f4a3c7Sjsing 			else
24865f4a3c7Sjsing 				SLIST_INSERT_AFTER(bc2, bc, sbc_link);
24965f4a3c7Sjsing 
25065f4a3c7Sjsing 			bv->sbv_chunks_found++;
25165f4a3c7Sjsing 		}
25265f4a3c7Sjsing 	}
25365f4a3c7Sjsing 
25465f4a3c7Sjsing 	/*
25565f4a3c7Sjsing 	 * Assemble RAID volumes.
25665f4a3c7Sjsing 	 */
25765f4a3c7Sjsing 	volno = 0;
25865f4a3c7Sjsing 	SLIST_FOREACH(bv, &sr_volumes, sbv_link) {
25965f4a3c7Sjsing 
26065f4a3c7Sjsing 		/* Skip if this is a hotspare "volume". */
26165f4a3c7Sjsing 		if (bv->sbv_level == SR_HOTSPARE_LEVEL &&
26265f4a3c7Sjsing 		    bv->sbv_chunk_no == 1)
26365f4a3c7Sjsing 			continue;
26465f4a3c7Sjsing 
26565f4a3c7Sjsing 		/* Determine current ondisk version. */
26665f4a3c7Sjsing 		bv->sbv_ondisk = 0;
26765f4a3c7Sjsing 		SLIST_FOREACH(bc, &bv->sbv_chunks, sbc_link) {
26865f4a3c7Sjsing 			if (bc->sbc_ondisk > bv->sbv_ondisk)
26965f4a3c7Sjsing 				bv->sbv_ondisk = bc->sbc_ondisk;
27065f4a3c7Sjsing 		}
27165f4a3c7Sjsing 		SLIST_FOREACH(bc, &bv->sbv_chunks, sbc_link) {
27265f4a3c7Sjsing 			if (bc->sbc_ondisk != bv->sbv_ondisk)
27365f4a3c7Sjsing 				bc->sbc_state = BIOC_SDOFFLINE;
27465f4a3c7Sjsing 		}
27565f4a3c7Sjsing 
27665f4a3c7Sjsing 		/* XXX - Check for duplicate chunks. */
27765f4a3c7Sjsing 
27865f4a3c7Sjsing 		/*
27965f4a3c7Sjsing 		 * Validate that volume has sufficient chunks for
28065f4a3c7Sjsing 		 * read-only access.
28165f4a3c7Sjsing 		 *
28265f4a3c7Sjsing 		 * XXX - check chunk states.
28365f4a3c7Sjsing 		 */
28465f4a3c7Sjsing 		bv->sbv_state = BIOC_SVOFFLINE;
28565f4a3c7Sjsing 		switch (bv->sbv_level) {
28665f4a3c7Sjsing 		case 0:
28765f4a3c7Sjsing 		case 'C':
28865f4a3c7Sjsing 		case 'c':
28965f4a3c7Sjsing 			if (bv->sbv_chunk_no == bv->sbv_chunks_found)
29065f4a3c7Sjsing 				bv->sbv_state = BIOC_SVONLINE;
29165f4a3c7Sjsing 			break;
29265f4a3c7Sjsing 
29365f4a3c7Sjsing 		case 1:
294*855f8c03Sstsp 		case 0x1C:
29565f4a3c7Sjsing 			if (bv->sbv_chunk_no == bv->sbv_chunks_found)
29665f4a3c7Sjsing 				bv->sbv_state = BIOC_SVONLINE;
29765f4a3c7Sjsing 			else if (bv->sbv_chunks_found > 0)
29865f4a3c7Sjsing 				bv->sbv_state = BIOC_SVDEGRADED;
29965f4a3c7Sjsing 			break;
30065f4a3c7Sjsing 		}
30165f4a3c7Sjsing 
30265f4a3c7Sjsing 		bv->sbv_unit = volno++;
30365f4a3c7Sjsing 		if (bv->sbv_state != BIOC_SVOFFLINE)
30465f4a3c7Sjsing 			printf(" sr%d%s", bv->sbv_unit,
30565f4a3c7Sjsing 			    bv->sbv_flags & BIOC_SCBOOTABLE ? "*" : "");
30665f4a3c7Sjsing 	}
30765f4a3c7Sjsing 
30865f4a3c7Sjsing 	explicit_bzero(md, SR_META_SIZE * DEV_BSIZE);
309bac5600aSfcambus 	free(md, SR_META_SIZE * DEV_BSIZE);
31065f4a3c7Sjsing }
31165f4a3c7Sjsing 
31265f4a3c7Sjsing int
sr_strategy(struct sr_boot_volume * bv,int rw,daddr_t blk,size_t size,void * buf,size_t * rsize)3133e58d19eSkrw sr_strategy(struct sr_boot_volume *bv, int rw, daddr_t blk, size_t size,
31465f4a3c7Sjsing     void *buf, size_t *rsize)
31565f4a3c7Sjsing {
31665f4a3c7Sjsing 	struct diskinfo *sr_dip, *dip;
31765f4a3c7Sjsing 	struct sr_boot_chunk *bc;
31865f4a3c7Sjsing 	struct aes_xts_ctx ctx;
31965f4a3c7Sjsing 	size_t i, j, nsect;
32065f4a3c7Sjsing 	daddr_t blkno;
32165f4a3c7Sjsing 	u_char iv[8];
32265f4a3c7Sjsing 	u_char *bp;
32365f4a3c7Sjsing 	int err;
32465f4a3c7Sjsing 
32565f4a3c7Sjsing 	/* We only support read-only softraid. */
32665f4a3c7Sjsing 	if (rw != F_READ)
32765f4a3c7Sjsing 		return ENOTSUP;
32865f4a3c7Sjsing 
32965f4a3c7Sjsing 	/* Partition offset within softraid volume. */
33065f4a3c7Sjsing 	sr_dip = (struct diskinfo *)bv->sbv_diskinfo;
331f252f90fSyasuoka 	blk += DL_SECTOBLK(&sr_dip->disklabel,
332f252f90fSyasuoka 	    sr_dip->disklabel.d_partitions[bv->sbv_part - 'a'].p_offset);
33365f4a3c7Sjsing 
33465f4a3c7Sjsing 	if (bv->sbv_level == 0) {
33565f4a3c7Sjsing 		return ENOTSUP;
33665f4a3c7Sjsing 	} else if (bv->sbv_level == 1) {
33765f4a3c7Sjsing 
33865f4a3c7Sjsing 		/* Select first online chunk. */
33965f4a3c7Sjsing 		SLIST_FOREACH(bc, &bv->sbv_chunks, sbc_link)
34065f4a3c7Sjsing 			if (bc->sbc_state == BIOC_SDONLINE)
34165f4a3c7Sjsing 				break;
34265f4a3c7Sjsing 		if (bc == NULL)
34365f4a3c7Sjsing 			return EIO;
34465f4a3c7Sjsing 
34565f4a3c7Sjsing 		dip = (struct diskinfo *)bc->sbc_diskinfo;
34665f4a3c7Sjsing 		dip->bsddev = bc->sbc_mm;
34765f4a3c7Sjsing 		blk += bv->sbv_data_blkno;
34865f4a3c7Sjsing 
34965f4a3c7Sjsing 		/* XXX - If I/O failed we should try another chunk... */
35065f4a3c7Sjsing 		return dip->strategy(dip, rw, blk, size, buf, rsize);
35165f4a3c7Sjsing 
352*855f8c03Sstsp 	} else if (bv->sbv_level == 'C' || bv->sbv_level == 0x1C) {
35365f4a3c7Sjsing 
35465f4a3c7Sjsing 		/* Select first online chunk. */
35565f4a3c7Sjsing 		SLIST_FOREACH(bc, &bv->sbv_chunks, sbc_link)
35665f4a3c7Sjsing 			if (bc->sbc_state == BIOC_SDONLINE)
35765f4a3c7Sjsing 				break;
35865f4a3c7Sjsing 		if (bc == NULL)
35965f4a3c7Sjsing 			return EIO;
36065f4a3c7Sjsing 
36165f4a3c7Sjsing 		dip = (struct diskinfo *)bc->sbc_diskinfo;
36265f4a3c7Sjsing 		dip->bsddev = bc->sbc_mm;
36365f4a3c7Sjsing 
36465f4a3c7Sjsing 		/* XXX - select correct key. */
36565f4a3c7Sjsing 		aes_xts_setkey(&ctx, (u_char *)bv->sbv_keys, 64);
36665f4a3c7Sjsing 
36765f4a3c7Sjsing 		nsect = (size + DEV_BSIZE - 1) / DEV_BSIZE;
36865f4a3c7Sjsing 		for (i = 0; i < nsect; i++) {
36965f4a3c7Sjsing 			blkno = blk + i;
37065f4a3c7Sjsing 			bp = ((u_char *)buf) + i * DEV_BSIZE;
37165f4a3c7Sjsing 			err = dip->strategy(dip, rw, bv->sbv_data_blkno + blkno,
37265f4a3c7Sjsing 			    DEV_BSIZE, bp, NULL);
37365f4a3c7Sjsing 			if (err != 0)
37465f4a3c7Sjsing 				return err;
37565f4a3c7Sjsing 
37665f4a3c7Sjsing 			bcopy(&blkno, iv, sizeof(blkno));
37765f4a3c7Sjsing 			aes_xts_reinit(&ctx, iv);
37865f4a3c7Sjsing 			for (j = 0; j < DEV_BSIZE; j += AES_XTS_BLOCKSIZE)
37965f4a3c7Sjsing 				aes_xts_decrypt(&ctx, bp + j);
38065f4a3c7Sjsing 		}
38165f4a3c7Sjsing 		if (rsize != NULL)
38265f4a3c7Sjsing 			*rsize = nsect * DEV_BSIZE;
38365f4a3c7Sjsing 
38465f4a3c7Sjsing 		return err;
38565f4a3c7Sjsing 
38665f4a3c7Sjsing 	} else
38765f4a3c7Sjsing 		return ENOTSUP;
38865f4a3c7Sjsing }
38965f4a3c7Sjsing 
39065f4a3c7Sjsing /*
39165f4a3c7Sjsing  * Returns 0 if the MBR with the provided partition array is a GPT protective
39265f4a3c7Sjsing  * MBR, and returns 1 otherwise. A GPT protective MBR would have one and only
39365f4a3c7Sjsing  * one MBR partition, an EFI partition that either covers the whole disk or as
39465f4a3c7Sjsing  * much of it as is possible with a 32bit size field.
39565f4a3c7Sjsing  *
39665f4a3c7Sjsing  * Taken from kern/subr_disk.c.
39765f4a3c7Sjsing  *
39865f4a3c7Sjsing  * NOTE: MS always uses a size of UINT32_MAX for the EFI partition!**
39965f4a3c7Sjsing  */
40065f4a3c7Sjsing static int
gpt_chk_mbr(struct dos_partition * dp,u_int64_t dsize)40165f4a3c7Sjsing gpt_chk_mbr(struct dos_partition *dp, u_int64_t dsize)
40265f4a3c7Sjsing {
40365f4a3c7Sjsing 	struct dos_partition *dp2;
40465f4a3c7Sjsing 	int efi, found, i;
40565f4a3c7Sjsing 	u_int32_t psize;
40665f4a3c7Sjsing 
40765f4a3c7Sjsing 	found = efi = 0;
40865f4a3c7Sjsing 	for (dp2=dp, i=0; i < NDOSPART; i++, dp2++) {
40965f4a3c7Sjsing 		if (dp2->dp_typ == DOSPTYP_UNUSED)
41065f4a3c7Sjsing 			continue;
41165f4a3c7Sjsing 		found++;
41265f4a3c7Sjsing 		if (dp2->dp_typ != DOSPTYP_EFI)
41365f4a3c7Sjsing 			continue;
41471c0bb0bSkrw 		if (letoh32(dp2->dp_start) != GPTSECTOR)
41571c0bb0bSkrw 			continue;
41665f4a3c7Sjsing 		psize = letoh32(dp2->dp_size);
41771c0bb0bSkrw 		if (psize <= (dsize - GPTSECTOR) || psize == UINT32_MAX)
41865f4a3c7Sjsing 			efi++;
41965f4a3c7Sjsing 	}
42065f4a3c7Sjsing 	if (found == 1 && efi == 1)
42165f4a3c7Sjsing 		return (0);
42265f4a3c7Sjsing 
42365f4a3c7Sjsing 	return (1);
42465f4a3c7Sjsing }
42565f4a3c7Sjsing 
42665f4a3c7Sjsing static uint64_t
findopenbsd_gpt(struct sr_boot_volume * bv,const char ** err)42765f4a3c7Sjsing findopenbsd_gpt(struct sr_boot_volume *bv, const char **err)
42865f4a3c7Sjsing {
42965f4a3c7Sjsing 	struct			 gpt_header gh;
43065f4a3c7Sjsing 	int			 i, part, found;
43165f4a3c7Sjsing 	uint64_t		 lba;
43265f4a3c7Sjsing 	uint32_t		 orig_csum, new_csum;
43365f4a3c7Sjsing 	uint32_t		 ghsize, ghpartsize, ghpartnum, ghpartspersec;
43465f4a3c7Sjsing 	uint32_t		 gpsectors;
43565f4a3c7Sjsing 	const char		 openbsd_uuid_code[] = GPT_UUID_OPENBSD;
43665f4a3c7Sjsing 	struct gpt_partition	 gp;
43765f4a3c7Sjsing 	static struct uuid	*openbsd_uuid = NULL, openbsd_uuid_space;
4385bffc6a7Sstsp 	u_char		 	*buf;
43965f4a3c7Sjsing 
44065f4a3c7Sjsing 	/* Prepare OpenBSD UUID */
44165f4a3c7Sjsing 	if (openbsd_uuid == NULL) {
44265f4a3c7Sjsing 		/* XXX: should be replaced by uuid_dec_be() */
44365f4a3c7Sjsing 		memcpy(&openbsd_uuid_space, openbsd_uuid_code,
44465f4a3c7Sjsing 		    sizeof(openbsd_uuid_space));
44565f4a3c7Sjsing 		openbsd_uuid_space.time_low =
44665f4a3c7Sjsing 		    betoh32(openbsd_uuid_space.time_low);
44765f4a3c7Sjsing 		openbsd_uuid_space.time_mid =
44865f4a3c7Sjsing 		    betoh16(openbsd_uuid_space.time_mid);
44965f4a3c7Sjsing 		openbsd_uuid_space.time_hi_and_version =
45065f4a3c7Sjsing 		    betoh16(openbsd_uuid_space.time_hi_and_version);
45165f4a3c7Sjsing 
45265f4a3c7Sjsing 		openbsd_uuid = &openbsd_uuid_space;
45365f4a3c7Sjsing 	}
45465f4a3c7Sjsing 
455f252f90fSyasuoka 	if (bv->sbv_secsize > 4096) {
456f252f90fSyasuoka 		*err = "disk sector > 4096 bytes\n";
457f252f90fSyasuoka 		return (-1);
458f252f90fSyasuoka 	}
4595bffc6a7Sstsp 	buf = alloc(bv->sbv_secsize);
4605bffc6a7Sstsp 	if (buf == NULL) {
4615bffc6a7Sstsp 		*err = "out of memory\n";
4625bffc6a7Sstsp 		return (-1);
4635bffc6a7Sstsp 	}
4645bffc6a7Sstsp 	bzero(buf, bv->sbv_secsize);
465f252f90fSyasuoka 
46671c0bb0bSkrw 	/* GPT Header */
46771c0bb0bSkrw 	lba = GPTSECTOR;
468f252f90fSyasuoka 	sr_strategy(bv, F_READ, lba * (bv->sbv_secsize / DEV_BSIZE), DEV_BSIZE,
469f252f90fSyasuoka 	    buf, NULL);
47065f4a3c7Sjsing 	memcpy(&gh, buf, sizeof(gh));
47165f4a3c7Sjsing 
47265f4a3c7Sjsing 	/* Check signature */
47365f4a3c7Sjsing 	if (letoh64(gh.gh_sig) != GPTSIGNATURE) {
47465f4a3c7Sjsing 		*err = "bad GPT signature\n";
4755bffc6a7Sstsp 		free(buf, bv->sbv_secsize);
47665f4a3c7Sjsing 		return (-1);
47765f4a3c7Sjsing 	}
47865f4a3c7Sjsing 
47965f4a3c7Sjsing 	if (letoh32(gh.gh_rev) != GPTREVISION) {
48065f4a3c7Sjsing 		*err = "bad GPT revision\n";
4815bffc6a7Sstsp 		free(buf, bv->sbv_secsize);
48265f4a3c7Sjsing 		return (-1);
48365f4a3c7Sjsing 	}
48465f4a3c7Sjsing 
48565f4a3c7Sjsing 	ghsize = letoh32(gh.gh_size);
48665f4a3c7Sjsing 	if (ghsize < GPTMINHDRSIZE || ghsize > sizeof(struct gpt_header)) {
48765f4a3c7Sjsing 		*err = "bad GPT header size\n";
4885bffc6a7Sstsp 		free(buf, bv->sbv_secsize);
48965f4a3c7Sjsing 		return (-1);
49065f4a3c7Sjsing 	}
49165f4a3c7Sjsing 
49265f4a3c7Sjsing 	/* Check checksum */
49365f4a3c7Sjsing 	orig_csum = gh.gh_csum;
49465f4a3c7Sjsing 	gh.gh_csum = 0;
49565f4a3c7Sjsing 	new_csum = crc32(0, (unsigned char *)&gh, ghsize);
49665f4a3c7Sjsing 	gh.gh_csum = orig_csum;
49765f4a3c7Sjsing 	if (letoh32(orig_csum) != new_csum) {
49865f4a3c7Sjsing 		*err = "bad GPT header checksum\n";
4995bffc6a7Sstsp 		free(buf, bv->sbv_secsize);
50065f4a3c7Sjsing 		return (-1);
50165f4a3c7Sjsing 	}
50265f4a3c7Sjsing 
50365f4a3c7Sjsing 	lba = letoh64(gh.gh_part_lba);
50465f4a3c7Sjsing 	ghpartsize = letoh32(gh.gh_part_size);
505f252f90fSyasuoka 	ghpartspersec = bv->sbv_secsize / ghpartsize;
50665f4a3c7Sjsing 	ghpartnum = letoh32(gh.gh_part_num);
50765f4a3c7Sjsing 	gpsectors = (ghpartnum + ghpartspersec - 1) / ghpartspersec;
50865f4a3c7Sjsing 	new_csum = crc32(0L, Z_NULL, 0);
50965f4a3c7Sjsing 	found = 0;
51065f4a3c7Sjsing 	for (i = 0; i < gpsectors; i++, lba++) {
511f252f90fSyasuoka 		sr_strategy(bv, F_READ, lba * (bv->sbv_secsize / DEV_BSIZE),
512f252f90fSyasuoka 		    bv->sbv_secsize, buf, NULL);
51365f4a3c7Sjsing 		for (part = 0; part < ghpartspersec; part++) {
51465f4a3c7Sjsing 			if (ghpartnum == 0)
51565f4a3c7Sjsing 				break;
51665f4a3c7Sjsing 			new_csum = crc32(new_csum, buf + part * sizeof(gp),
51765f4a3c7Sjsing 			    sizeof(gp));
51865f4a3c7Sjsing 			ghpartnum--;
51965f4a3c7Sjsing 			if (found)
52065f4a3c7Sjsing 				continue;
52165f4a3c7Sjsing 			memcpy(&gp, buf + part * sizeof(gp), sizeof(gp));
52265f4a3c7Sjsing 			if (memcmp(&gp.gp_type, openbsd_uuid,
52365f4a3c7Sjsing 			    sizeof(struct uuid)) == 0)
52465f4a3c7Sjsing 				found = 1;
52565f4a3c7Sjsing 		}
52665f4a3c7Sjsing 	}
5275bffc6a7Sstsp 
5285bffc6a7Sstsp 	free(buf, bv->sbv_secsize);
5295bffc6a7Sstsp 
53065f4a3c7Sjsing 	if (new_csum != letoh32(gh.gh_part_csum)) {
53165f4a3c7Sjsing 		*err = "bad GPT entries checksum\n";
53265f4a3c7Sjsing 		return (-1);
53365f4a3c7Sjsing 	}
53465f4a3c7Sjsing 	if (found)
53565f4a3c7Sjsing 		return (letoh64(gp.gp_lba_start));
53665f4a3c7Sjsing 
53765f4a3c7Sjsing 	return (-1);
53865f4a3c7Sjsing }
53965f4a3c7Sjsing 
54065f4a3c7Sjsing const char *
sr_getdisklabel(struct sr_boot_volume * bv,struct disklabel * label)54165f4a3c7Sjsing sr_getdisklabel(struct sr_boot_volume *bv, struct disklabel *label)
54265f4a3c7Sjsing {
54365f4a3c7Sjsing 	struct dos_partition *dp;
54465f4a3c7Sjsing 	struct dos_mbr mbr;
54565f4a3c7Sjsing 	const char *err = NULL;
54665f4a3c7Sjsing 	u_int start = 0;
54765f4a3c7Sjsing 	char buf[DEV_BSIZE];
54865f4a3c7Sjsing 	int i;
54965f4a3c7Sjsing 
55065f4a3c7Sjsing 	/* Check for MBR to determine partition offset. */
55165f4a3c7Sjsing 	bzero(&mbr, sizeof(mbr));
55265f4a3c7Sjsing 	sr_strategy(bv, F_READ, DOSBBSECTOR, sizeof(mbr), &mbr, NULL);
553f252f90fSyasuoka 	if (gpt_chk_mbr(mbr.dmbr_parts, bv->sbv_size /
554f252f90fSyasuoka 		    (bv->sbv_secsize / DEV_BSIZE)) == 0) {
55565f4a3c7Sjsing 		start = findopenbsd_gpt(bv, &err);
55665f4a3c7Sjsing 		if (start == (u_int)-1) {
55765f4a3c7Sjsing 			if (err != NULL)
55865f4a3c7Sjsing 				return (err);
55965f4a3c7Sjsing 			return "no OpenBSD partition\n";
56065f4a3c7Sjsing 		}
56165f4a3c7Sjsing 	} else if (mbr.dmbr_sign == DOSMBR_SIGNATURE) {
56265f4a3c7Sjsing 
56365f4a3c7Sjsing 		/* Search for OpenBSD partition */
56465f4a3c7Sjsing 		for (i = 0; i < NDOSPART; i++) {
56565f4a3c7Sjsing 			dp = &mbr.dmbr_parts[i];
56665f4a3c7Sjsing 			if (!dp->dp_size)
56765f4a3c7Sjsing 				continue;
56865f4a3c7Sjsing 			if (dp->dp_typ == DOSPTYP_OPENBSD) {
56965f4a3c7Sjsing 				start = dp->dp_start;
57065f4a3c7Sjsing 				break;
57165f4a3c7Sjsing 			}
57265f4a3c7Sjsing 		}
57365f4a3c7Sjsing 	}
57465f4a3c7Sjsing 
57565f4a3c7Sjsing 	/* Read the disklabel. */
576f252f90fSyasuoka 	sr_strategy(bv, F_READ,
577f252f90fSyasuoka 	    start * (bv->sbv_secsize / DEV_BSIZE) + DOS_LABELSECTOR,
57865f4a3c7Sjsing 	    sizeof(struct disklabel), buf, NULL);
57965f4a3c7Sjsing 
58065f4a3c7Sjsing #ifdef BIOS_DEBUG
58165f4a3c7Sjsing 	printf("sr_getdisklabel: magic %lx\n",
58265f4a3c7Sjsing 	    ((struct disklabel *)buf)->d_magic);
58365f4a3c7Sjsing 	for (i = 0; i < MAXPARTITIONS; i++)
58465f4a3c7Sjsing 		printf("part %c: type = %d, size = %d, offset = %d\n", 'a' + i,
58565f4a3c7Sjsing 		    (int)((struct disklabel *)buf)->d_partitions[i].p_fstype,
58665f4a3c7Sjsing 		    (int)((struct disklabel *)buf)->d_partitions[i].p_size,
58765f4a3c7Sjsing 		    (int)((struct disklabel *)buf)->d_partitions[i].p_offset);
58865f4a3c7Sjsing #endif
58965f4a3c7Sjsing 
59065f4a3c7Sjsing 	/* Fill in disklabel */
59165f4a3c7Sjsing 	return (getdisklabel(buf, label));
59265f4a3c7Sjsing }
593