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