xref: /openbsd/sys/arch/amd64/stand/efiboot/efidev.c (revision 9b7c3dbb)
1 /*	$OpenBSD: efidev.c,v 1.19 2016/08/31 15:11:22 yasuoka Exp $	*/
2 
3 /*
4  * Copyright (c) 1996 Michael Shalayeff
5  * Copyright (c) 2003 Tobias Weingartner
6  * Copyright (c) 2015 YASUOKA Masahiko <yasuoka@yasuoka.net>
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
22  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28  * SUCH DAMAGE.
29  *
30  */
31 #include <sys/param.h>
32 #include <sys/reboot.h>
33 #include <sys/disklabel.h>
34 #include <lib/libz/zlib.h>
35 #include <isofs/cd9660/iso.h>
36 
37 #include "libsa.h"
38 #include "disk.h"
39 
40 #ifdef SOFTRAID
41 #include <dev/softraidvar.h>
42 #include "softraid.h"
43 #endif
44 
45 #include <efi.h>
46 #include "eficall.h"
47 
48 extern int debug;
49 
50 #include "efidev.h"
51 #include "biosdev.h"	/* for dklookup() */
52 
53 #define EFI_BLKSPERSEC(_ed)	((_ed)->blkio->Media->BlockSize / DEV_BSIZE)
54 #define EFI_SECTOBLK(_ed, _n)	((_n) * EFI_BLKSPERSEC(_ed))
55 
56 struct efi_diskinfo {
57 	EFI_BLOCK_IO		*blkio;
58 	UINT32			 mediaid;
59 };
60 
61 int bios_bootdev;
62 static EFI_STATUS
63 		 efid_io(int, efi_diskinfo_t, u_int, int, void *);
64 static int	 efid_diskio(int, struct diskinfo *, u_int, int, void *);
65 static int	 efi_getdisklabel_cd9660(efi_diskinfo_t, struct disklabel *);
66 static u_int	 findopenbsd(efi_diskinfo_t, const char **);
67 static u_int	 findopenbsd_gpt(efi_diskinfo_t, const char **);
68 static int	 gpt_chk_mbr(struct dos_partition *, u_int64_t);
69 
70 void
71 efid_init(struct diskinfo *dip, void *handle)
72 {
73 	EFI_BLOCK_IO		*blkio = handle;
74 
75 	memset(dip, 0, sizeof(struct diskinfo));
76 	dip->efi_info = alloc(sizeof(struct efi_diskinfo));
77 	dip->efi_info->blkio = blkio;
78 	dip->efi_info->mediaid = blkio->Media->MediaId;
79 	dip->diskio = efid_diskio;
80 	dip->strategy = efistrategy;
81 }
82 
83 static EFI_STATUS
84 efid_io(int rw, efi_diskinfo_t ed, u_int off, int nsect, void *buf)
85 {
86 	u_int		 blks, lba, i_lblks, i_tblks, i_nblks;
87 	EFI_STATUS	 status = EFI_SUCCESS;
88 	static u_char	*iblk = NULL;
89 	static u_int	 iblksz = 0;
90 
91 	/* block count of the intrisic block size in DEV_BSIZE */
92 	blks = EFI_BLKSPERSEC(ed);
93 	if (blks == 0)
94 		/* block size < 512.  HP Stream 13 actually has such a disk. */
95 		return (EFI_UNSUPPORTED);
96 	lba = off / blks;
97 
98 	/* leading and trailing unaligned blocks in intrisic block */
99 	i_lblks = ((off % blks) == 0)? 0 : blks - (off % blks);
100 	i_tblks = (off + nsect) % blks;
101 
102 	/* aligned blocks in intrisic block */
103 	i_nblks = nsect - (i_lblks + i_tblks);
104 
105 	switch (rw) {
106 	case F_READ:
107 		/* allocate the space for reading unaligned blocks */
108 		if (ed->blkio->Media->BlockSize != DEV_BSIZE) {
109 			if (iblk && iblksz < ed->blkio->Media->BlockSize) {
110 				free(iblk, iblksz);
111 				iblk = NULL;
112 			}
113 			if (iblk == NULL) {
114 				iblk = alloc(ed->blkio->Media->BlockSize);
115 				iblksz = ed->blkio->Media->BlockSize;
116 			}
117 		}
118 		if (i_lblks > 0) {
119 			status = EFI_CALL(ed->blkio->ReadBlocks,
120 			    ed->blkio, ed->mediaid, lba - 1,
121 			    ed->blkio->Media->BlockSize, iblk);
122 			if (EFI_ERROR(status))
123 				goto on_eio;
124 			memcpy(buf, iblk + (blks - i_lblks),
125 			    i_lblks * DEV_BSIZE);
126 		}
127 		if (i_nblks > 0) {
128 			status = EFI_CALL(ed->blkio->ReadBlocks,
129 			    ed->blkio, ed->mediaid, lba,
130 			    ed->blkio->Media->BlockSize * (i_nblks / blks),
131 			    buf + (i_lblks * DEV_BSIZE));
132 			if (EFI_ERROR(status))
133 				goto on_eio;
134 		}
135 		if (i_tblks > 0) {
136 			status = EFI_CALL(ed->blkio->ReadBlocks,
137 			    ed->blkio, ed->mediaid, lba + (i_nblks / blks),
138 			    ed->blkio->Media->BlockSize, iblk);
139 			if (EFI_ERROR(status))
140 				goto on_eio;
141 			memcpy(buf + (i_lblks + i_nblks) * DEV_BSIZE, iblk,
142 			    i_tblks * DEV_BSIZE);
143 		}
144 		break;
145 	case F_WRITE:
146 		if (ed->blkio->Media->ReadOnly)
147 			goto on_eio;
148 		/* XXX not yet */
149 		goto on_eio;
150 		break;
151 	}
152 	return (EFI_SUCCESS);
153 
154 on_eio:
155 	return (status);
156 }
157 
158 static int
159 efid_diskio(int rw, struct diskinfo *dip, u_int off, int nsect, void *buf)
160 {
161 	EFI_STATUS status;
162 
163 	status = efid_io(rw, dip->efi_info, off, nsect, buf);
164 
165 	return ((EFI_ERROR(status))? -1 : 0);
166 }
167 
168 /*
169  * Returns 0 if the MBR with the provided partition array is a GPT protective
170  * MBR, and returns 1 otherwise. A GPT protective MBR would have one and only
171  * one MBR partition, an EFI partition that either covers the whole disk or as
172  * much of it as is possible with a 32bit size field.
173  *
174  * Taken from kern/subr_disk.c.
175  *
176  * NOTE: MS always uses a size of UINT32_MAX for the EFI partition!**
177  */
178 static int
179 gpt_chk_mbr(struct dos_partition *dp, u_int64_t dsize)
180 {
181 	struct dos_partition *dp2;
182 	int efi, found, i;
183 	u_int32_t psize;
184 
185 	found = efi = 0;
186 	for (dp2=dp, i=0; i < NDOSPART; i++, dp2++) {
187 		if (dp2->dp_typ == DOSPTYP_UNUSED)
188 			continue;
189 		found++;
190 		if (dp2->dp_typ != DOSPTYP_EFI)
191 			continue;
192 		psize = letoh32(dp2->dp_size);
193 		if (psize == (dsize - 1) ||
194 		    psize == UINT32_MAX) {
195 			if (letoh32(dp2->dp_start) == 1)
196 				efi++;
197 		}
198 	}
199 	if (found == 1 && efi == 1)
200 		return (0);
201 
202 	return (1);
203 }
204 
205 /*
206  * Try to find the disk address of the first MBR OpenBSD partition.
207  *
208  * N.B.: must boot from a partition within first 2^32-1 sectors!
209  *
210  * Called only if the MBR on sector 0 is *not* a protective MBR
211  * and *does* have a valid signature.
212  *
213  * We don't check the signatures of EBR's, and they cannot be
214  * protective MBR's so there is no need to check for that.
215  */
216 static u_int
217 findopenbsd(efi_diskinfo_t ed, const char **err)
218 {
219 	EFI_STATUS status;
220 	struct dos_mbr mbr;
221 	struct dos_partition *dp;
222 	u_int mbroff = DOSBBSECTOR;
223 	u_int mbr_eoff = DOSBBSECTOR;	/* Offset of MBR extended partition. */
224 	int i, maxebr = DOS_MAXEBR, nextebr;
225 
226 again:
227 	if (!maxebr--) {
228 		*err = "too many extended partitions";
229 		return (-1);
230 	}
231 
232 	/* Read MBR */
233 	bzero(&mbr, sizeof(mbr));
234 	status = efid_io(F_READ, ed, mbroff, 1, &mbr);
235 	if (EFI_ERROR(status)) {
236 		*err = "Disk I/O Error";
237 		return (-1);
238 	}
239 
240 	/* Search for OpenBSD partition */
241 	nextebr = 0;
242 	for (i = 0; i < NDOSPART; i++) {
243 		dp = &mbr.dmbr_parts[i];
244 		if (!dp->dp_size)
245 			continue;
246 #ifdef BIOS_DEBUG
247 		if (debug)
248 			printf("found partition %u: "
249 			    "type %u (0x%x) offset %u (0x%x)\n",
250 			    (int)(dp - mbr.dmbr_parts),
251 			    dp->dp_typ, dp->dp_typ,
252 			    dp->dp_start, dp->dp_start);
253 #endif
254 		if (dp->dp_typ == DOSPTYP_OPENBSD) {
255 			if (dp->dp_start > (dp->dp_start + mbroff))
256 				continue;
257 			return (dp->dp_start + mbroff);
258 		}
259 
260 		/*
261 		 * Record location of next ebr if and only if this is the first
262 		 * extended partition in this boot record!
263 		 */
264 		if (!nextebr && (dp->dp_typ == DOSPTYP_EXTEND ||
265 		    dp->dp_typ == DOSPTYP_EXTENDL)) {
266 			nextebr = dp->dp_start + mbr_eoff;
267 			if (nextebr < dp->dp_start)
268 				nextebr = (u_int)-1;
269 			if (mbr_eoff == DOSBBSECTOR)
270 				mbr_eoff = dp->dp_start;
271 		}
272 	}
273 
274 	if (nextebr && nextebr != (u_int)-1) {
275 		mbroff = nextebr;
276 		goto again;
277 	}
278 
279 	return (-1);
280 }
281 
282 /*
283  * Try to find the disk address of the first GPT OpenBSD partition.
284  *
285  * N.B.: must boot from a partition within first 2^32-1 sectors!
286  *
287  * Called only if the MBR on sector 0 *is* a protective MBR
288  * with a valid signature and sector 1 is a valid GPT header.
289  */
290 static u_int
291 findopenbsd_gpt(efi_diskinfo_t ed, const char **err)
292 {
293 	EFI_STATUS		 status;
294 	struct			 gpt_header gh;
295 	int			 i, part, found;
296 	uint64_t		 lba;
297 	uint32_t		 orig_csum, new_csum;
298 	uint32_t		 ghsize, ghpartsize, ghpartnum, ghpartspersec;
299 	uint32_t		 gpsectors;
300 	const char		 openbsd_uuid_code[] = GPT_UUID_OPENBSD;
301 	struct gpt_partition	 gp;
302 	static struct uuid	*openbsd_uuid = NULL, openbsd_uuid_space;
303 	static u_char		 buf[4096];
304 
305 	/* Prepare OpenBSD UUID */
306 	if (openbsd_uuid == NULL) {
307 		/* XXX: should be replaced by uuid_dec_be() */
308 		memcpy(&openbsd_uuid_space, openbsd_uuid_code,
309 		    sizeof(openbsd_uuid_space));
310 		openbsd_uuid_space.time_low =
311 		    betoh32(openbsd_uuid_space.time_low);
312 		openbsd_uuid_space.time_mid =
313 		    betoh16(openbsd_uuid_space.time_mid);
314 		openbsd_uuid_space.time_hi_and_version =
315 		    betoh16(openbsd_uuid_space.time_hi_and_version);
316 
317 		openbsd_uuid = &openbsd_uuid_space;
318 	}
319 
320 	if (EFI_BLKSPERSEC(ed) > 8) {
321 		*err = "disk sector > 4096 bytes\n";
322 		return (-1);
323 	}
324 
325 	/* LBA1: GPT Header */
326 	lba = 1;
327 	status = efid_io(F_READ, ed, EFI_SECTOBLK(ed, lba), EFI_BLKSPERSEC(ed),
328 	    buf);
329 	if (EFI_ERROR(status)) {
330 		*err = "Disk I/O Error";
331 		return (-1);
332 	}
333 	memcpy(&gh, buf, sizeof(gh));
334 
335 	/* Check signature */
336 	if (letoh64(gh.gh_sig) != GPTSIGNATURE) {
337 		*err = "bad GPT signature\n";
338 		return (-1);
339 	}
340 
341 	if (letoh32(gh.gh_rev) != GPTREVISION) {
342 		*err = "bad GPT revision\n";
343 		return (-1);
344 	}
345 
346 	ghsize = letoh32(gh.gh_size);
347 	if (ghsize < GPTMINHDRSIZE || ghsize > sizeof(struct gpt_header)) {
348 		*err = "bad GPT header size\n";
349 		return (-1);
350 	}
351 
352 	/* Check checksum */
353 	orig_csum = gh.gh_csum;
354 	gh.gh_csum = 0;
355 	new_csum = crc32(0, (unsigned char *)&gh, ghsize);
356 	gh.gh_csum = orig_csum;
357 	if (letoh32(orig_csum) != new_csum) {
358 		*err = "bad GPT header checksum\n";
359 		return (-1);
360 	}
361 
362 	lba = letoh64(gh.gh_part_lba);
363 	ghpartsize = letoh32(gh.gh_part_size);
364 	ghpartspersec = ed->blkio->Media->BlockSize / ghpartsize;
365 	ghpartnum = letoh32(gh.gh_part_num);
366 	gpsectors = (ghpartnum + ghpartspersec - 1) / ghpartspersec;
367 	new_csum = crc32(0L, Z_NULL, 0);
368 	found = 0;
369 	for (i = 0; i < gpsectors; i++, lba++) {
370 		status = efid_io(F_READ, ed, EFI_SECTOBLK(ed, lba),
371 		    EFI_BLKSPERSEC(ed), buf);
372 		if (EFI_ERROR(status)) {
373 			*err = "Disk I/O Error";
374 			return (-1);
375 		}
376 		for (part = 0; part < ghpartspersec; part++) {
377 			if (ghpartnum == 0)
378 				break;
379 			new_csum = crc32(new_csum, buf + part * sizeof(gp),
380 			    sizeof(gp));
381 			ghpartnum--;
382 			if (found)
383 				continue;
384 			memcpy(&gp, buf + part * sizeof(gp), sizeof(gp));
385 			if (memcmp(&gp.gp_type, openbsd_uuid,
386 			    sizeof(struct uuid)) == 0)
387 				found = 1;
388 		}
389 	}
390 	if (new_csum != letoh32(gh.gh_part_csum)) {
391 		*err = "bad GPT entries checksum\n";
392 		return (-1);
393 	}
394 	if (found) {
395 		lba = letoh64(gp.gp_lba_start);
396 		/* Bootloaders do not current handle addresses > UINT_MAX! */
397 		if (lba > UINT_MAX || EFI_SECTOBLK(ed, lba) > UINT_MAX) {
398 			*err = "OpenBSD Partition LBA > 2**32 - 1";
399 			return (-1);
400 		}
401 		return (u_int)lba;
402 	}
403 
404 	return (-1);
405 }
406 
407 const char *
408 efi_getdisklabel(efi_diskinfo_t ed, struct disklabel *label)
409 {
410 	u_int start = 0;
411 	uint8_t buf[DEV_BSIZE];
412 	struct dos_partition dosparts[NDOSPART];
413 	EFI_STATUS status;
414 	const char *err = NULL;
415 	int error;
416 
417 	/*
418 	 * Read sector 0. Ensure it has a valid MBR signature.
419 	 *
420 	 * If it's a protective MBR then try to find the disklabel via
421 	 * GPT. If it's not a protective MBR, try to find the disklabel
422 	 * via MBR.
423 	 */
424 	memset(buf, 0, sizeof(buf));
425 	status = efid_io(F_READ, ed, DOSBBSECTOR, 1, buf);
426 	if (EFI_ERROR(status))
427 		return ("Disk I/O Error");
428 
429 	/* Check MBR signature. */
430 	if (buf[510] != 0x55 || buf[511] != 0xaa) {
431 		if (efi_getdisklabel_cd9660(ed, label) == 0)
432 			return (NULL);
433 		return ("invalid MBR signature");
434 	}
435 
436 	memcpy(dosparts, buf+DOSPARTOFF, sizeof(dosparts));
437 
438 	/* check for GPT protective MBR. */
439 	if (gpt_chk_mbr(dosparts, ed->blkio->Media->LastBlock + 1) == 0) {
440 		start = findopenbsd_gpt(ed, &err);
441 		if (start == (u_int)-1) {
442 			if (err != NULL)
443 				return (err);
444 			return ("no OpenBSD GPT partition");
445 		}
446 	} else {
447 		start = findopenbsd(ed, &err);
448 		if (start == (u_int)-1) {
449 			if (err != NULL)
450 				return (err);
451 			return "no OpenBSD MBR partition\n";
452 		}
453 	}
454 
455 	/* Load BSD disklabel */
456 #ifdef BIOS_DEBUG
457 	if (debug)
458 		printf("loading disklabel @ %u\n", start + DOS_LABELSECTOR);
459 #endif
460 	/* read disklabel */
461 	error = efid_io(F_READ, ed, start + DOS_LABELSECTOR, 1, buf);
462 
463 	if (error)
464 		return "failed to read disklabel";
465 
466 	/* Fill in disklabel */
467 	return (getdisklabel(buf, label));
468 }
469 
470 static int
471 efi_getdisklabel_cd9660(efi_diskinfo_t ed, struct disklabel *label)
472 {
473 	int		 off;
474 	uint8_t		 buf[DEV_BSIZE];
475 	EFI_STATUS	 status;
476 
477 	for (off = 0; off < 100; off++) {
478 		status = efid_io(F_READ, ed,
479 		    EFI_BLKSPERSEC(ed) * (16 + off), 1, buf);
480 		if (EFI_ERROR(status))
481 			return (-1);
482 		if (bcmp(buf + 1, ISO_STANDARD_ID, 5) != 0 ||
483 		    buf[0] == ISO_VD_END)
484 			return (-1);
485 		if (buf[0] == ISO_VD_PRIMARY)
486 			break;
487 	}
488 	if (off >= 100)
489 		return (-1);
490 
491 	/* Create an imaginary disk label */
492 	label->d_secsize = 2048;
493 	label->d_ntracks = 1;
494 	label->d_nsectors = 100;
495 	label->d_ncylinders = 1;
496 	label->d_secpercyl = label->d_ntracks * label->d_nsectors;
497 	if (label->d_secpercyl == 0) {
498 		label->d_secpercyl = 100;
499 		/* as long as it's not 0, since readdisklabel divides by it */
500 	}
501 
502 	strncpy(label->d_typename, "ATAPI CD-ROM", sizeof(label->d_typename));
503 	label->d_type = DTYPE_ATAPI;
504 
505 	strncpy(label->d_packname, "fictitious", sizeof(label->d_packname));
506 	DL_SETDSIZE(label, 100);
507 
508 	label->d_bbsize = 2048;
509 	label->d_sbsize = 2048;
510 
511 	/* 'a' partition covering the "whole" disk */
512 	DL_SETPOFFSET(&label->d_partitions[0], 0);
513 	DL_SETPSIZE(&label->d_partitions[0], 100);
514 	label->d_partitions[0].p_fstype = FS_UNUSED;
515 
516 	/* The raw partition is special */
517 	DL_SETPOFFSET(&label->d_partitions[RAW_PART], 0);
518 	DL_SETPSIZE(&label->d_partitions[RAW_PART], 100);
519 	label->d_partitions[RAW_PART].p_fstype = FS_UNUSED;
520 
521 	label->d_npartitions = MAXPARTITIONS;
522 
523 	label->d_magic = DISKMAGIC;
524 	label->d_magic2 = DISKMAGIC;
525 	label->d_checksum = dkcksum(label);
526 
527 	return (0);
528 }
529 
530 int
531 efiopen(struct open_file *f, ...)
532 {
533 #ifdef SOFTRAID
534 	struct sr_boot_volume *bv;
535 #endif
536 	register char *cp, **file;
537 	dev_t maj, unit, part;
538 	struct diskinfo *dip;
539 	int biosdev, devlen;
540 #if 0
541 	const char *st;
542 #endif
543 	va_list ap;
544 	char *dev;
545 
546 	va_start(ap, f);
547 	cp = *(file = va_arg(ap, char **));
548 	va_end(ap);
549 
550 #ifdef EFI_DEBUG
551 	if (debug)
552 		printf("%s\n", cp);
553 #endif
554 
555 	f->f_devdata = NULL;
556 
557 	/* Search for device specification. */
558 	dev = cp;
559 	if (cp[4] == ':')
560 		devlen = 2;
561 	else if (cp[5] == ':')
562 		devlen = 3;
563 	else
564 		return ENOENT;
565 	cp += devlen;
566 
567 	/* Get unit. */
568 	if ('0' <= *cp && *cp <= '9')
569 		unit = *cp++ - '0';
570 	else {
571 		printf("Bad unit number\n");
572 		return EUNIT;
573 	}
574 
575 	/* Get partition. */
576 	if ('a' <= *cp && *cp <= 'p')
577 		part = *cp++ - 'a';
578 	else {
579 		printf("Bad partition\n");
580 		return EPART;
581 	}
582 
583 	/* Get filename. */
584 	cp++;	/* skip ':' */
585 	if (*cp != 0)
586 		*file = cp;
587 	else
588 		f->f_flags |= F_RAW;
589 
590 #ifdef SOFTRAID
591 	/* Intercept softraid disks. */
592 	if (strncmp("sr", dev, 2) == 0) {
593 
594 		/* Create a fake diskinfo for this softraid volume. */
595 		SLIST_FOREACH(bv, &sr_volumes, sbv_link)
596 			if (bv->sbv_unit == unit)
597 				break;
598 		if (bv == NULL) {
599 			printf("Unknown device: sr%d\n", unit);
600 			return EADAPT;
601 		}
602 
603 		if (bv->sbv_level == 'C' && bv->sbv_keys == NULL)
604 			if (sr_crypto_decrypt_keys(bv) != 0)
605 				return EPERM;
606 
607 		if (bv->sbv_diskinfo == NULL) {
608 			dip = alloc(sizeof(struct diskinfo));
609 			bzero(dip, sizeof(*dip));
610 			dip->diskio = efid_diskio;
611 			dip->strategy = efistrategy;
612 			bv->sbv_diskinfo = dip;
613 			dip->sr_vol = bv;
614 			dip->bios_info.flags |= BDI_BADLABEL;
615 		}
616 
617 		dip = bv->sbv_diskinfo;
618 
619 		if (dip->bios_info.flags & BDI_BADLABEL) {
620 			/* Attempt to read disklabel. */
621 			bv->sbv_part = 'c';
622 			if (sr_getdisklabel(bv, &dip->disklabel))
623 				return ERDLAB;
624 			dip->bios_info.flags &= ~BDI_BADLABEL;
625 		}
626 
627 		bv->sbv_part = part + 'a';
628 
629 		bootdev_dip = dip;
630 		f->f_devdata = dip;
631 
632 		return 0;
633 	}
634 #endif
635 	for (maj = 0; maj < nbdevs &&
636 	    strncmp(dev, bdevs[maj], devlen); maj++);
637 	if (maj >= nbdevs) {
638 		printf("Unknown device: ");
639 		for (cp = *file; *cp != ':'; cp++)
640 			putchar(*cp);
641 		putchar('\n');
642 		return EADAPT;
643 	}
644 
645 	biosdev = unit;
646 	switch (maj) {
647 	case 0:  /* wd */
648 	case 4:  /* sd */
649 	case 17: /* hd */
650 		biosdev |= 0x80;
651 		break;
652 	case 2:  /* fd */
653 		break;
654 	case 6:  /* cd */
655 		biosdev = bios_bootdev & 0xff;
656 		break;
657 	default:
658 		return ENXIO;
659 	}
660 
661 	/* Find device */
662 	dip = dklookup(biosdev);
663 	if (dip == NULL)
664 		return ENXIO;
665 	bootdev_dip = dip;
666 
667 	/* Fix up bootdev */
668 	{ dev_t bsd_dev;
669 		bsd_dev = dip->bios_info.bsd_dev;
670 		dip->bsddev = MAKEBOOTDEV(B_TYPE(bsd_dev), B_ADAPTOR(bsd_dev),
671 		    B_CONTROLLER(bsd_dev), unit, part);
672 		dip->bootdev = MAKEBOOTDEV(B_TYPE(bsd_dev), B_ADAPTOR(bsd_dev),
673 		    B_CONTROLLER(bsd_dev), B_UNIT(bsd_dev), part);
674 	}
675 
676 #if 0
677 	dip->bios_info.bsd_dev = dip->bootdev;
678 	bootdev = dip->bootdev;
679 #endif
680 
681 #ifdef EFI_DEBUG
682 	if (debug) {
683 		printf("BIOS geometry: heads=%u, s/t=%u; EDD=%d\n",
684 		    dip->bios_info.bios_heads, dip->bios_info.bios_sectors,
685 		    dip->bios_info.bios_edd);
686 	}
687 #endif
688 
689 #if 0
690 /*
691  * XXX In UEFI, media change can be detected by MediaID
692  */
693 	/* Try for disklabel again (might be removable media) */
694 	if (dip->bios_info.flags & BDI_BADLABEL) {
695 		st = efi_getdisklabel(dip->efi_info, &dip->disklabel);
696 #ifdef EFI_DEBUG
697 		if (debug && st)
698 			printf("%s\n", st);
699 #endif
700 		if (!st) {
701 			dip->bios_info.flags &= ~BDI_BADLABEL;
702 			dip->bios_info.flags |= BDI_GOODLABEL;
703 		} else
704 			return ERDLAB;
705 	}
706 #endif
707 	f->f_devdata = dip;
708 
709 	return 0;
710 }
711 
712 int
713 efistrategy(void *devdata, int rw, daddr32_t blk, size_t size, void *buf,
714     size_t *rsize)
715 {
716 	struct diskinfo *dip = (struct diskinfo *)devdata;
717 	u_int8_t error = 0;
718 	size_t nsect;
719 
720 #ifdef SOFTRAID
721 	/* Intercept strategy for softraid volumes. */
722 	if (dip->sr_vol)
723 		return sr_strategy(dip->sr_vol, rw, blk, size, buf, rsize);
724 #endif
725 	nsect = (size + DEV_BSIZE - 1) / DEV_BSIZE;
726 	blk += dip->disklabel.d_partitions[B_PARTITION(dip->bsddev)].p_offset;
727 
728 	if (blk < 0)
729 		error = EINVAL;
730 	else
731 		error = dip->diskio(rw, dip, blk, nsect, buf);
732 
733 #ifdef EFI_DEBUG
734 	if (debug) {
735 		if (error != 0)
736 			printf("=0x%x(%s)", error, error);
737 		putchar('\n');
738 	}
739 #endif
740 	if (rsize != NULL)
741 		*rsize = nsect * DEV_BSIZE;
742 
743 	return (error);
744 }
745 
746 int
747 eficlose(struct open_file *f)
748 {
749 	f->f_devdata = NULL;
750 
751 	return 0;
752 }
753 
754 int
755 efiioctl(struct open_file *f, u_long cmd, void *data)
756 {
757 
758 	return 0;
759 }
760 
761 void
762 efi_dump_diskinfo(void)
763 {
764 	efi_diskinfo_t	 ed;
765 	struct diskinfo	*dip;
766 	bios_diskinfo_t *bdi;
767 	uint64_t	 siz;
768 	const char	*sizu;
769 
770 	printf("Disk\tBlkSiz\tIoAlign\tSize\tFlags\tChecksum\n");
771 	TAILQ_FOREACH(dip, &disklist, list) {
772 		bdi = &dip->bios_info;
773 		ed = dip->efi_info;
774 
775 		siz = (ed->blkio->Media->LastBlock + 1) *
776 		    ed->blkio->Media->BlockSize;
777 		siz /= 1024 * 1024;
778 		if (siz < 10000)
779 			sizu = "MB";
780 		else {
781 			siz /= 1024;
782 			sizu = "GB";
783 		}
784 
785 		printf("hd%d\t%u\t%u\t%u%s\t0x%x\t0x%x\t%s\n",
786 		    (bdi->bios_number & 0x7f),
787 		    ed->blkio->Media->BlockSize,
788 		    ed->blkio->Media->IoAlign, siz, sizu,
789 		    bdi->flags, bdi->checksum,
790 		    (ed->blkio->Media->RemovableMedia)? "Removable" : "");
791 	}
792 }
793