xref: /freebsd/stand/efi/libefi/efipart.c (revision 190cef3d52236565eb22e18b33e9e865ec634aa3)
1 /*-
2  * Copyright (c) 2010 Marcel Moolenaar
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  */
26 
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
29 
30 #include <sys/disk.h>
31 #include <sys/param.h>
32 #include <sys/time.h>
33 #include <sys/queue.h>
34 #include <stddef.h>
35 #include <stdarg.h>
36 
37 #include <bootstrap.h>
38 
39 #include <efi.h>
40 #include <efilib.h>
41 #include <efiprot.h>
42 #include <efichar.h>
43 #include <disk.h>
44 
45 static EFI_GUID blkio_guid = BLOCK_IO_PROTOCOL;
46 
47 static int efipart_initfd(void);
48 static int efipart_initcd(void);
49 static int efipart_inithd(void);
50 
51 static int efipart_strategy(void *, int, daddr_t, size_t, char *, size_t *);
52 static int efipart_realstrategy(void *, int, daddr_t, size_t, char *, size_t *);
53 
54 static int efipart_open(struct open_file *, ...);
55 static int efipart_close(struct open_file *);
56 static int efipart_ioctl(struct open_file *, u_long, void *);
57 
58 static int efipart_printfd(int);
59 static int efipart_printcd(int);
60 static int efipart_printhd(int);
61 
62 /* EISA PNP ID's for floppy controllers */
63 #define	PNP0604	0x604
64 #define	PNP0700	0x700
65 #define	PNP0701	0x701
66 
67 struct devsw efipart_fddev = {
68 	.dv_name = "fd",
69 	.dv_type = DEVT_FD,
70 	.dv_init = efipart_initfd,
71 	.dv_strategy = efipart_strategy,
72 	.dv_open = efipart_open,
73 	.dv_close = efipart_close,
74 	.dv_ioctl = efipart_ioctl,
75 	.dv_print = efipart_printfd,
76 	.dv_cleanup = NULL
77 };
78 
79 struct devsw efipart_cddev = {
80 	.dv_name = "cd",
81 	.dv_type = DEVT_CD,
82 	.dv_init = efipart_initcd,
83 	.dv_strategy = efipart_strategy,
84 	.dv_open = efipart_open,
85 	.dv_close = efipart_close,
86 	.dv_ioctl = efipart_ioctl,
87 	.dv_print = efipart_printcd,
88 	.dv_cleanup = NULL
89 };
90 
91 struct devsw efipart_hddev = {
92 	.dv_name = "disk",
93 	.dv_type = DEVT_DISK,
94 	.dv_init = efipart_inithd,
95 	.dv_strategy = efipart_strategy,
96 	.dv_open = efipart_open,
97 	.dv_close = efipart_close,
98 	.dv_ioctl = efipart_ioctl,
99 	.dv_print = efipart_printhd,
100 	.dv_cleanup = NULL
101 };
102 
103 static pdinfo_list_t fdinfo;
104 static pdinfo_list_t cdinfo;
105 static pdinfo_list_t hdinfo;
106 
107 static EFI_HANDLE *efipart_handles = NULL;
108 static UINTN efipart_nhandles = 0;
109 
110 pdinfo_list_t *
111 efiblk_get_pdinfo_list(struct devsw *dev)
112 {
113 	if (dev->dv_type == DEVT_DISK)
114 		return (&hdinfo);
115 	if (dev->dv_type == DEVT_CD)
116 		return (&cdinfo);
117 	if (dev->dv_type == DEVT_FD)
118 		return (&fdinfo);
119 	return (NULL);
120 }
121 
122 /* XXX this gets called way way too often, investigate */
123 pdinfo_t *
124 efiblk_get_pdinfo(struct devdesc *dev)
125 {
126 	pdinfo_list_t *pdi;
127 	pdinfo_t *pd = NULL;
128 
129 	pdi = efiblk_get_pdinfo_list(dev->d_dev);
130 	if (pdi == NULL)
131 		return (pd);
132 
133 	STAILQ_FOREACH(pd, pdi, pd_link) {
134 		if (pd->pd_unit == dev->d_unit)
135 			return (pd);
136 	}
137 	return (pd);
138 }
139 
140 pdinfo_t *
141 efiblk_get_pdinfo_by_device_path(EFI_DEVICE_PATH *path)
142 {
143 	unsigned i;
144 	EFI_DEVICE_PATH *media, *devpath;
145 	EFI_HANDLE h;
146 
147 	media = efi_devpath_to_media_path(path);
148 	if (media == NULL)
149 		return (NULL);
150 	for (i = 0; i < efipart_nhandles; i++) {
151 		h = efipart_handles[i];
152 		devpath = efi_lookup_devpath(h);
153 		if (devpath == NULL)
154 			continue;
155 		if (!efi_devpath_match_node(media, efi_devpath_to_media_path(devpath)))
156 			continue;
157 		return (efiblk_get_pdinfo_by_handle(h));
158 	}
159 	return (NULL);
160 }
161 
162 static bool
163 same_handle(pdinfo_t *pd, EFI_HANDLE h)
164 {
165 
166 	return (pd->pd_handle == h || pd->pd_alias == h);
167 }
168 
169 pdinfo_t *
170 efiblk_get_pdinfo_by_handle(EFI_HANDLE h)
171 {
172 	pdinfo_t *dp, *pp;
173 
174 	/*
175 	 * Check hard disks, then cd, then floppy
176 	 */
177 	STAILQ_FOREACH(dp, &hdinfo, pd_link) {
178 		if (same_handle(dp, h))
179 			return (dp);
180 		STAILQ_FOREACH(pp, &dp->pd_part, pd_link) {
181 			if (same_handle(pp, h))
182 				return (pp);
183 		}
184 	}
185 	STAILQ_FOREACH(dp, &cdinfo, pd_link) {
186 		if (same_handle(dp, h))
187 			return (dp);
188 	}
189 	STAILQ_FOREACH(dp, &fdinfo, pd_link) {
190 		if (same_handle(dp, h))
191 			return (dp);
192 	}
193 	return (NULL);
194 }
195 
196 static int
197 efiblk_pdinfo_count(pdinfo_list_t *pdi)
198 {
199 	pdinfo_t *pd;
200 	int i = 0;
201 
202 	STAILQ_FOREACH(pd, pdi, pd_link) {
203 		i++;
204 	}
205 	return (i);
206 }
207 
208 int
209 efipart_inithandles(void)
210 {
211 	UINTN sz;
212 	EFI_HANDLE *hin;
213 	EFI_STATUS status;
214 
215 	if (efipart_nhandles != 0) {
216 		free(efipart_handles);
217 		efipart_handles = NULL;
218 		efipart_nhandles = 0;
219 	}
220 
221 	sz = 0;
222 	hin = NULL;
223 	status = BS->LocateHandle(ByProtocol, &blkio_guid, 0, &sz, hin);
224 	if (status == EFI_BUFFER_TOO_SMALL) {
225 		hin = malloc(sz);
226 		status = BS->LocateHandle(ByProtocol, &blkio_guid, 0, &sz,
227 		    hin);
228 		if (EFI_ERROR(status))
229 			free(hin);
230 	}
231 	if (EFI_ERROR(status))
232 		return (efi_status_to_errno(status));
233 
234 	efipart_handles = hin;
235 	efipart_nhandles = sz / sizeof(*hin);
236 #ifdef EFIPART_DEBUG
237 	printf("%s: Got %d BLOCK IO MEDIA handle(s)\n", __func__,
238 	    efipart_nhandles);
239 #endif
240 	return (0);
241 }
242 
243 static ACPI_HID_DEVICE_PATH *
244 efipart_floppy(EFI_DEVICE_PATH *node)
245 {
246 	ACPI_HID_DEVICE_PATH *acpi;
247 
248 	if (DevicePathType(node) == ACPI_DEVICE_PATH &&
249 	    DevicePathSubType(node) == ACPI_DP) {
250 		acpi = (ACPI_HID_DEVICE_PATH *) node;
251 		if (acpi->HID == EISA_PNP_ID(PNP0604) ||
252 		    acpi->HID == EISA_PNP_ID(PNP0700) ||
253 		    acpi->HID == EISA_PNP_ID(PNP0701)) {
254 			return (acpi);
255 		}
256 	}
257 	return (NULL);
258 }
259 
260 /*
261  * Determine if the provided device path is hdd.
262  *
263  * There really is no simple fool proof way to classify the devices.
264  * Since we do build three lists of devices - floppy, cd and hdd, we
265  * will try to see  if the device is floppy or cd, and list anything else
266  * as hdd.
267  */
268 static bool
269 efipart_hdd(EFI_DEVICE_PATH *dp)
270 {
271 	unsigned i;
272 	EFI_DEVICE_PATH *devpath, *node;
273 	EFI_BLOCK_IO *blkio;
274 	EFI_STATUS status;
275 
276 	if (dp == NULL)
277 		return (false);
278 
279 	if ((node = efi_devpath_last_node(dp)) == NULL)
280 		return (false);
281 
282 	if (efipart_floppy(node) != NULL)
283 		return (false);
284 
285 	/*
286 	 * Test every EFI BLOCK IO handle to make sure dp is not device path
287 	 * for CD/DVD.
288 	 */
289 	for (i = 0; i < efipart_nhandles; i++) {
290 		devpath = efi_lookup_devpath(efipart_handles[i]);
291 		if (devpath == NULL)
292 			return (false);
293 
294 		/* Only continue testing when dp is prefix in devpath. */
295 		if (!efi_devpath_is_prefix(dp, devpath))
296 			continue;
297 
298 		/*
299 		 * The device path has to have last node describing the
300 		 *  device, or we can not test the type.
301 		 */
302 		if ((node = efi_devpath_last_node(devpath)) == NULL)
303 			return (false);
304 
305 		if (DevicePathType(node) == MEDIA_DEVICE_PATH &&
306 		    DevicePathSubType(node) == MEDIA_CDROM_DP) {
307 			return (false);
308 		}
309 
310 		/* Make sure we do have the media. */
311 		status = BS->HandleProtocol(efipart_handles[i],
312 		    &blkio_guid, (void **)&blkio);
313 		if (EFI_ERROR(status))
314 			return (false);
315 
316 		/* USB or SATA cd without the media. */
317 		if (blkio->Media->RemovableMedia &&
318 		    !blkio->Media->MediaPresent) {
319 			return (false);
320 		}
321 
322 		/*
323 		 * We assume the block size 512 or greater power of 2.
324 		 * iPXE is known to insert stub BLOCK IO device with
325 		 * BlockSize 1.
326 		 */
327 		if (blkio->Media->BlockSize < 512 ||
328 		    !powerof2(blkio->Media->BlockSize)) {
329 			return (false);
330 		}
331 	}
332 	return (true);
333 }
334 
335 /*
336  * Add or update entries with new handle data.
337  */
338 static int
339 efipart_fdinfo_add(EFI_HANDLE handle, uint32_t uid, EFI_DEVICE_PATH *devpath)
340 {
341 	pdinfo_t *fd;
342 
343 	fd = calloc(1, sizeof(pdinfo_t));
344 	if (fd == NULL) {
345 		printf("Failed to register floppy %d, out of memory\n", uid);
346 		return (ENOMEM);
347 	}
348 	STAILQ_INIT(&fd->pd_part);
349 
350 	fd->pd_unit = uid;
351 	fd->pd_handle = handle;
352 	fd->pd_devpath = devpath;
353 	fd->pd_parent = NULL;
354 	fd->pd_devsw = &efipart_fddev;
355 	STAILQ_INSERT_TAIL(&fdinfo, fd, pd_link);
356 	return (0);
357 }
358 
359 static void
360 efipart_updatefd(void)
361 {
362 	EFI_DEVICE_PATH *devpath, *node;
363 	ACPI_HID_DEVICE_PATH *acpi;
364 	int i;
365 
366 	for (i = 0; i < efipart_nhandles; i++) {
367 		devpath = efi_lookup_devpath(efipart_handles[i]);
368 		if (devpath == NULL)
369 			continue;
370 
371 		if ((node = efi_devpath_last_node(devpath)) == NULL)
372 			continue;
373 		if ((acpi = efipart_floppy(node)) != NULL) {
374 			efipart_fdinfo_add(efipart_handles[i], acpi->UID,
375 			    devpath);
376 		}
377 	}
378 }
379 
380 static int
381 efipart_initfd(void)
382 {
383 
384 	STAILQ_INIT(&fdinfo);
385 
386 	efipart_updatefd();
387 
388 	bcache_add_dev(efiblk_pdinfo_count(&fdinfo));
389 	return (0);
390 }
391 
392 /*
393  * Add or update entries with new handle data.
394  */
395 static int
396 efipart_cdinfo_add(EFI_HANDLE handle, EFI_HANDLE alias,
397     EFI_DEVICE_PATH *devpath)
398 {
399 	int unit;
400 	pdinfo_t *cd;
401 	pdinfo_t *pd;
402 
403 	unit = 0;
404 	STAILQ_FOREACH(pd, &cdinfo, pd_link) {
405 		if (efi_devpath_match(pd->pd_devpath, devpath) == true) {
406 			pd->pd_handle = handle;
407 			pd->pd_alias = alias;
408 			return (0);
409 		}
410 		unit++;
411 	}
412 
413 	cd = calloc(1, sizeof(pdinfo_t));
414 	if (cd == NULL) {
415 		printf("Failed to add cd %d, out of memory\n", unit);
416 		return (ENOMEM);
417 	}
418 	STAILQ_INIT(&cd->pd_part);
419 
420 	cd->pd_handle = handle;
421 	cd->pd_unit = unit;
422 	cd->pd_alias = alias;
423 	cd->pd_devpath = devpath;
424 	cd->pd_parent = NULL;
425 	cd->pd_devsw = &efipart_cddev;
426 	STAILQ_INSERT_TAIL(&cdinfo, cd, pd_link);
427 	return (0);
428 }
429 
430 static void
431 efipart_updatecd(void)
432 {
433 	int i;
434 	EFI_DEVICE_PATH *devpath, *devpathcpy, *tmpdevpath, *node;
435 	EFI_HANDLE handle;
436 	EFI_BLOCK_IO *blkio;
437 	EFI_STATUS status;
438 
439 	for (i = 0; i < efipart_nhandles; i++) {
440 		devpath = efi_lookup_devpath(efipart_handles[i]);
441 		if (devpath == NULL)
442 			continue;
443 
444 		if ((node = efi_devpath_last_node(devpath)) == NULL)
445 			continue;
446 
447 		if (efipart_floppy(node) != NULL)
448 			continue;
449 
450 		if (efipart_hdd(devpath))
451 			continue;
452 
453 		status = BS->HandleProtocol(efipart_handles[i],
454 		    &blkio_guid, (void **)&blkio);
455 		if (EFI_ERROR(status))
456 			continue;
457 		/*
458 		 * If we come across a logical partition of subtype CDROM
459 		 * it doesn't refer to the CD filesystem itself, but rather
460 		 * to any usable El Torito boot image on it. In this case
461 		 * we try to find the parent device and add that instead as
462 		 * that will be the CD filesystem.
463 		 */
464 		if (DevicePathType(node) == MEDIA_DEVICE_PATH &&
465 		    DevicePathSubType(node) == MEDIA_CDROM_DP) {
466 			devpathcpy = efi_devpath_trim(devpath);
467 			if (devpathcpy == NULL)
468 				continue;
469 			tmpdevpath = devpathcpy;
470 			status = BS->LocateDevicePath(&blkio_guid, &tmpdevpath,
471 			    &handle);
472 			free(devpathcpy);
473 			if (EFI_ERROR(status))
474 				continue;
475 			devpath = efi_lookup_devpath(handle);
476 			efipart_cdinfo_add(handle, efipart_handles[i],
477 			    devpath);
478 			continue;
479 		}
480 
481 		if (DevicePathType(node) == MESSAGING_DEVICE_PATH &&
482 		    DevicePathSubType(node) == MSG_ATAPI_DP) {
483 			efipart_cdinfo_add(efipart_handles[i], NULL,
484 			    devpath);
485 			continue;
486 		}
487 
488 		/* USB or SATA cd without the media. */
489 		if (blkio->Media->RemovableMedia &&
490 		    !blkio->Media->MediaPresent) {
491 			efipart_cdinfo_add(efipart_handles[i], NULL,
492 			    devpath);
493 		}
494 	}
495 }
496 
497 static int
498 efipart_initcd(void)
499 {
500 
501 	STAILQ_INIT(&cdinfo);
502 
503 	efipart_updatecd();
504 
505 	bcache_add_dev(efiblk_pdinfo_count(&cdinfo));
506 	return (0);
507 }
508 
509 static int
510 efipart_hdinfo_add(EFI_HANDLE disk_handle, EFI_HANDLE part_handle)
511 {
512 	EFI_DEVICE_PATH *disk_devpath, *part_devpath;
513 	HARDDRIVE_DEVICE_PATH *node;
514 	int unit;
515 	pdinfo_t *hd, *pd, *last;
516 
517 	disk_devpath = efi_lookup_devpath(disk_handle);
518 	if (disk_devpath == NULL)
519 		return (ENOENT);
520 
521 	if (part_handle != NULL) {
522 		part_devpath = efi_lookup_devpath(part_handle);
523 		if (part_devpath == NULL)
524 			return (ENOENT);
525 		node = (HARDDRIVE_DEVICE_PATH *)
526 		    efi_devpath_last_node(part_devpath);
527 		if (node == NULL)
528 			return (ENOENT);	/* This should not happen. */
529 	} else {
530 		part_devpath = NULL;
531 		node = NULL;
532 	}
533 
534 	pd = calloc(1, sizeof(pdinfo_t));
535 	if (pd == NULL) {
536 		printf("Failed to add disk, out of memory\n");
537 		return (ENOMEM);
538 	}
539 	STAILQ_INIT(&pd->pd_part);
540 
541 	STAILQ_FOREACH(hd, &hdinfo, pd_link) {
542 		if (efi_devpath_match(hd->pd_devpath, disk_devpath) == true) {
543 			if (part_devpath == NULL)
544 				return (0);
545 
546 			/* Add the partition. */
547 			pd->pd_handle = part_handle;
548 			pd->pd_unit = node->PartitionNumber;
549 			pd->pd_devpath = part_devpath;
550 			pd->pd_parent = hd;
551 			pd->pd_devsw = &efipart_hddev;
552 			STAILQ_INSERT_TAIL(&hd->pd_part, pd, pd_link);
553 			return (0);
554 		}
555 	}
556 
557 	last = STAILQ_LAST(&hdinfo, pdinfo, pd_link);
558 	if (last != NULL)
559 		unit = last->pd_unit + 1;
560 	else
561 		unit = 0;
562 
563 	/* Add the disk. */
564 	hd = pd;
565 	hd->pd_handle = disk_handle;
566 	hd->pd_unit = unit;
567 	hd->pd_devpath = disk_devpath;
568 	hd->pd_parent = NULL;
569 	hd->pd_devsw = &efipart_hddev;
570 	STAILQ_INSERT_TAIL(&hdinfo, hd, pd_link);
571 
572 	if (part_devpath == NULL)
573 		return (0);
574 
575 	pd = calloc(1, sizeof(pdinfo_t));
576 	if (pd == NULL) {
577 		printf("Failed to add partition, out of memory\n");
578 		return (ENOMEM);
579 	}
580 	STAILQ_INIT(&pd->pd_part);
581 
582 	/* Add the partition. */
583 	pd->pd_handle = part_handle;
584 	pd->pd_unit = node->PartitionNumber;
585 	pd->pd_devpath = part_devpath;
586 	pd->pd_parent = hd;
587 	pd->pd_devsw = &efipart_hddev;
588 	STAILQ_INSERT_TAIL(&hd->pd_part, pd, pd_link);
589 
590 	return (0);
591 }
592 
593 /*
594  * The MEDIA_FILEPATH_DP has device name.
595  * From U-Boot sources it looks like names are in the form
596  * of typeN:M, where type is interface type, N is disk id
597  * and M is partition id.
598  */
599 static int
600 efipart_hdinfo_add_filepath(EFI_HANDLE disk_handle)
601 {
602 	EFI_DEVICE_PATH *devpath;
603 	FILEPATH_DEVICE_PATH *node;
604 	char *pathname, *p;
605 	int unit, len;
606 	pdinfo_t *pd, *last;
607 
608 	/* First collect and verify all the data */
609 	if ((devpath = efi_lookup_devpath(disk_handle)) == NULL)
610 		return (ENOENT);
611 	node = (FILEPATH_DEVICE_PATH *)efi_devpath_last_node(devpath);
612 	if (node == NULL)
613 		return (ENOENT);	/* This should not happen. */
614 
615 	pd = calloc(1, sizeof(pdinfo_t));
616 	if (pd == NULL) {
617 		printf("Failed to add disk, out of memory\n");
618 		return (ENOMEM);
619 	}
620 	STAILQ_INIT(&pd->pd_part);
621 	last = STAILQ_LAST(&hdinfo, pdinfo, pd_link);
622 	if (last != NULL)
623 		unit = last->pd_unit + 1;
624 	else
625 		unit = 0;
626 
627 	/* FILEPATH_DEVICE_PATH has 0 terminated string */
628 	len = ucs2len(node->PathName);
629 	if ((pathname = malloc(len + 1)) == NULL) {
630 		printf("Failed to add disk, out of memory\n");
631 		free(pd);
632 		return (ENOMEM);
633 	}
634 	cpy16to8(node->PathName, pathname, len + 1);
635 	p = strchr(pathname, ':');
636 
637 	/*
638 	 * Assume we are receiving handles in order, first disk handle,
639 	 * then partitions for this disk. If this assumption proves
640 	 * false, this code would need update.
641 	 */
642 	if (p == NULL) {	/* no colon, add the disk */
643 		pd->pd_handle = disk_handle;
644 		pd->pd_unit = unit;
645 		pd->pd_devpath = devpath;
646 		pd->pd_parent = NULL;
647 		pd->pd_devsw = &efipart_hddev;
648 		STAILQ_INSERT_TAIL(&hdinfo, pd, pd_link);
649 		free(pathname);
650 		return (0);
651 	}
652 	p++;	/* skip the colon */
653 	errno = 0;
654 	unit = (int)strtol(p, NULL, 0);
655 	if (errno != 0) {
656 		printf("Bad unit number for partition \"%s\"\n", pathname);
657 		free(pathname);
658 		free(pd);
659 		return (EUNIT);
660 	}
661 
662 	/*
663 	 * We should have disk registered, if not, we are receiving
664 	 * handles out of order, and this code should be reworked
665 	 * to create "blank" disk for partition, and to find the
666 	 * disk based on PathName compares.
667 	 */
668 	if (last == NULL) {
669 		printf("BUG: No disk for partition \"%s\"\n", pathname);
670 		free(pathname);
671 		free(pd);
672 		return (EINVAL);
673 	}
674 	/* Add the partition. */
675 	pd->pd_handle = disk_handle;
676 	pd->pd_unit = unit;
677 	pd->pd_devpath = devpath;
678 	pd->pd_parent = last;
679 	pd->pd_devsw = &efipart_hddev;
680 	STAILQ_INSERT_TAIL(&last->pd_part, pd, pd_link);
681 	free(pathname);
682 	return (0);
683 }
684 
685 static void
686 efipart_updatehd(void)
687 {
688 	int i;
689 	EFI_DEVICE_PATH *devpath, *devpathcpy, *tmpdevpath, *node;
690 	EFI_HANDLE handle;
691 	EFI_BLOCK_IO *blkio;
692 	EFI_STATUS status;
693 
694 	for (i = 0; i < efipart_nhandles; i++) {
695 		devpath = efi_lookup_devpath(efipart_handles[i]);
696 		if (devpath == NULL)
697 			continue;
698 
699 		if ((node = efi_devpath_last_node(devpath)) == NULL)
700 			continue;
701 
702 		if (!efipart_hdd(devpath))
703 			continue;
704 
705 		status = BS->HandleProtocol(efipart_handles[i],
706 		    &blkio_guid, (void **)&blkio);
707 		if (EFI_ERROR(status))
708 			continue;
709 
710 		if (DevicePathType(node) == MEDIA_DEVICE_PATH &&
711 		    DevicePathSubType(node) == MEDIA_FILEPATH_DP) {
712 			efipart_hdinfo_add_filepath(efipart_handles[i]);
713 			continue;
714 		}
715 
716 		if (DevicePathType(node) == MEDIA_DEVICE_PATH &&
717 		    DevicePathSubType(node) == MEDIA_HARDDRIVE_DP) {
718 			devpathcpy = efi_devpath_trim(devpath);
719 			if (devpathcpy == NULL)
720 				continue;
721 			tmpdevpath = devpathcpy;
722 			status = BS->LocateDevicePath(&blkio_guid, &tmpdevpath,
723 			    &handle);
724 			free(devpathcpy);
725 			if (EFI_ERROR(status))
726 				continue;
727 			/*
728 			 * We do not support nested partitions.
729 			 */
730 			devpathcpy = efi_lookup_devpath(handle);
731 			if (devpathcpy == NULL)
732 				continue;
733 			if ((node = efi_devpath_last_node(devpathcpy)) == NULL)
734 				continue;
735 
736 			if (DevicePathType(node) == MEDIA_DEVICE_PATH &&
737 			    DevicePathSubType(node) == MEDIA_HARDDRIVE_DP)
738 				continue;
739 
740 			efipart_hdinfo_add(handle, efipart_handles[i]);
741 			continue;
742 		}
743 
744 		efipart_hdinfo_add(efipart_handles[i], NULL);
745 	}
746 }
747 
748 static int
749 efipart_inithd(void)
750 {
751 
752 	STAILQ_INIT(&hdinfo);
753 
754 	efipart_updatehd();
755 
756 	bcache_add_dev(efiblk_pdinfo_count(&hdinfo));
757 	return (0);
758 }
759 
760 static int
761 efipart_print_common(struct devsw *dev, pdinfo_list_t *pdlist, int verbose)
762 {
763 	int ret = 0;
764 	EFI_BLOCK_IO *blkio;
765 	EFI_STATUS status;
766 	EFI_HANDLE h;
767 	pdinfo_t *pd;
768 	CHAR16 *text;
769 	struct disk_devdesc pd_dev;
770 	char line[80];
771 
772 	if (STAILQ_EMPTY(pdlist))
773 		return (0);
774 
775 	printf("%s devices:", dev->dv_name);
776 	if ((ret = pager_output("\n")) != 0)
777 		return (ret);
778 
779 	STAILQ_FOREACH(pd, pdlist, pd_link) {
780 		h = pd->pd_handle;
781 		if (verbose) {	/* Output the device path. */
782 			text = efi_devpath_name(efi_lookup_devpath(h));
783 			if (text != NULL) {
784 				printf("  %S", text);
785 				efi_free_devpath_name(text);
786 				if ((ret = pager_output("\n")) != 0)
787 					break;
788 			}
789 		}
790 		snprintf(line, sizeof(line),
791 		    "    %s%d", dev->dv_name, pd->pd_unit);
792 		printf("%s:", line);
793 		status = BS->HandleProtocol(h, &blkio_guid, (void **)&blkio);
794 		if (!EFI_ERROR(status)) {
795 			printf("    %llu",
796 			    blkio->Media->LastBlock == 0? 0:
797 			    (unsigned long long) (blkio->Media->LastBlock + 1));
798 			if (blkio->Media->LastBlock != 0) {
799 				printf(" X %u", blkio->Media->BlockSize);
800 			}
801 			printf(" blocks");
802 			if (blkio->Media->MediaPresent) {
803 				if (blkio->Media->RemovableMedia)
804 					printf(" (removable)");
805 			} else {
806 				printf(" (no media)");
807 			}
808 			if ((ret = pager_output("\n")) != 0)
809 				break;
810 			if (!blkio->Media->MediaPresent)
811 				continue;
812 
813 			pd->pd_blkio = blkio;
814 			pd_dev.dd.d_dev = dev;
815 			pd_dev.dd.d_unit = pd->pd_unit;
816 			pd_dev.d_slice = -1;
817 			pd_dev.d_partition = -1;
818 			ret = disk_open(&pd_dev, blkio->Media->BlockSize *
819 			    (blkio->Media->LastBlock + 1),
820 			    blkio->Media->BlockSize);
821 			if (ret == 0) {
822 				ret = disk_print(&pd_dev, line, verbose);
823 				disk_close(&pd_dev);
824 				if (ret != 0)
825 					return (ret);
826 			} else {
827 				/* Do not fail from disk_open() */
828 				ret = 0;
829 			}
830 		} else {
831 			if ((ret = pager_output("\n")) != 0)
832 				break;
833 		}
834 	}
835 	return (ret);
836 }
837 
838 static int
839 efipart_printfd(int verbose)
840 {
841 	return (efipart_print_common(&efipart_fddev, &fdinfo, verbose));
842 }
843 
844 static int
845 efipart_printcd(int verbose)
846 {
847 	return (efipart_print_common(&efipart_cddev, &cdinfo, verbose));
848 }
849 
850 static int
851 efipart_printhd(int verbose)
852 {
853 	return (efipart_print_common(&efipart_hddev, &hdinfo, verbose));
854 }
855 
856 static int
857 efipart_open(struct open_file *f, ...)
858 {
859 	va_list args;
860 	struct disk_devdesc *dev;
861 	pdinfo_t *pd;
862 	EFI_BLOCK_IO *blkio;
863 	EFI_STATUS status;
864 
865 	va_start(args, f);
866 	dev = va_arg(args, struct disk_devdesc*);
867 	va_end(args);
868 	if (dev == NULL)
869 		return (EINVAL);
870 
871 	pd = efiblk_get_pdinfo((struct devdesc *)dev);
872 	if (pd == NULL)
873 		return (EIO);
874 
875 	if (pd->pd_blkio == NULL) {
876 		status = BS->HandleProtocol(pd->pd_handle, &blkio_guid,
877 		    (void **)&pd->pd_blkio);
878 		if (EFI_ERROR(status))
879 			return (efi_status_to_errno(status));
880 	}
881 
882 	blkio = pd->pd_blkio;
883 	if (!blkio->Media->MediaPresent)
884 		return (EAGAIN);
885 
886 	pd->pd_open++;
887 	if (pd->pd_bcache == NULL)
888 		pd->pd_bcache = bcache_allocate();
889 
890 	if (dev->dd.d_dev->dv_type == DEVT_DISK) {
891 		int rc;
892 
893 		rc = disk_open(dev,
894 		    blkio->Media->BlockSize * (blkio->Media->LastBlock + 1),
895 		    blkio->Media->BlockSize);
896 		if (rc != 0) {
897 			pd->pd_open--;
898 			if (pd->pd_open == 0) {
899 				pd->pd_blkio = NULL;
900 				bcache_free(pd->pd_bcache);
901 				pd->pd_bcache = NULL;
902 			}
903 		}
904 		return (rc);
905 	}
906 	return (0);
907 }
908 
909 static int
910 efipart_close(struct open_file *f)
911 {
912 	struct disk_devdesc *dev;
913 	pdinfo_t *pd;
914 
915 	dev = (struct disk_devdesc *)(f->f_devdata);
916 	if (dev == NULL)
917 		return (EINVAL);
918 
919 	pd = efiblk_get_pdinfo((struct devdesc *)dev);
920 	if (pd == NULL)
921 		return (EINVAL);
922 
923 	pd->pd_open--;
924 	if (pd->pd_open == 0) {
925 		pd->pd_blkio = NULL;
926 		bcache_free(pd->pd_bcache);
927 		pd->pd_bcache = NULL;
928 	}
929 	if (dev->dd.d_dev->dv_type == DEVT_DISK)
930 		return (disk_close(dev));
931 	return (0);
932 }
933 
934 static int
935 efipart_ioctl(struct open_file *f, u_long cmd, void *data)
936 {
937 	struct disk_devdesc *dev;
938 	pdinfo_t *pd;
939 	int rc;
940 
941 	dev = (struct disk_devdesc *)(f->f_devdata);
942 	if (dev == NULL)
943 		return (EINVAL);
944 
945 	pd = efiblk_get_pdinfo((struct devdesc *)dev);
946 	if (pd == NULL)
947 		return (EINVAL);
948 
949 	if (dev->dd.d_dev->dv_type == DEVT_DISK) {
950 		rc = disk_ioctl(dev, cmd, data);
951 		if (rc != ENOTTY)
952 			return (rc);
953 	}
954 
955 	switch (cmd) {
956 	case DIOCGSECTORSIZE:
957 		*(u_int *)data = pd->pd_blkio->Media->BlockSize;
958 		break;
959 	case DIOCGMEDIASIZE:
960 		*(uint64_t *)data = pd->pd_blkio->Media->BlockSize *
961 		    (pd->pd_blkio->Media->LastBlock + 1);
962 		break;
963 	default:
964 		return (ENOTTY);
965 	}
966 
967 	return (0);
968 }
969 
970 /*
971  * efipart_readwrite()
972  * Internal equivalent of efipart_strategy(), which operates on the
973  * media-native block size. This function expects all I/O requests
974  * to be within the media size and returns an error if such is not
975  * the case.
976  */
977 static int
978 efipart_readwrite(EFI_BLOCK_IO *blkio, int rw, daddr_t blk, daddr_t nblks,
979     char *buf)
980 {
981 	EFI_STATUS status;
982 
983 	if (blkio == NULL)
984 		return (ENXIO);
985 	if (blk < 0 || blk > blkio->Media->LastBlock)
986 		return (EIO);
987 	if ((blk + nblks - 1) > blkio->Media->LastBlock)
988 		return (EIO);
989 
990 	switch (rw & F_MASK) {
991 	case F_READ:
992 		status = blkio->ReadBlocks(blkio, blkio->Media->MediaId, blk,
993 		    nblks * blkio->Media->BlockSize, buf);
994 		break;
995 	case F_WRITE:
996 		if (blkio->Media->ReadOnly)
997 			return (EROFS);
998 		status = blkio->WriteBlocks(blkio, blkio->Media->MediaId, blk,
999 		    nblks * blkio->Media->BlockSize, buf);
1000 		break;
1001 	default:
1002 		return (ENOSYS);
1003 	}
1004 
1005 	if (EFI_ERROR(status)) {
1006 		printf("%s: rw=%d, blk=%ju size=%ju status=%lu\n", __func__, rw,
1007 		    blk, nblks, EFI_ERROR_CODE(status));
1008 	}
1009 	return (efi_status_to_errno(status));
1010 }
1011 
1012 static int
1013 efipart_strategy(void *devdata, int rw, daddr_t blk, size_t size,
1014     char *buf, size_t *rsize)
1015 {
1016 	struct bcache_devdata bcd;
1017 	struct disk_devdesc *dev;
1018 	pdinfo_t *pd;
1019 
1020 	dev = (struct disk_devdesc *)devdata;
1021 	if (dev == NULL)
1022 		return (EINVAL);
1023 
1024 	pd = efiblk_get_pdinfo((struct devdesc *)dev);
1025 	if (pd == NULL)
1026 		return (EINVAL);
1027 
1028 	if (pd->pd_blkio->Media->RemovableMedia &&
1029 	    !pd->pd_blkio->Media->MediaPresent)
1030 		return (ENXIO);
1031 
1032 	bcd.dv_strategy = efipart_realstrategy;
1033 	bcd.dv_devdata = devdata;
1034 	bcd.dv_cache = pd->pd_bcache;
1035 
1036 	if (dev->dd.d_dev->dv_type == DEVT_DISK) {
1037 		daddr_t offset;
1038 
1039 		offset = dev->d_offset * pd->pd_blkio->Media->BlockSize;
1040 		offset /= 512;
1041 		return (bcache_strategy(&bcd, rw, blk + offset,
1042 		    size, buf, rsize));
1043 	}
1044 	return (bcache_strategy(&bcd, rw, blk, size, buf, rsize));
1045 }
1046 
1047 static int
1048 efipart_realstrategy(void *devdata, int rw, daddr_t blk, size_t size,
1049     char *buf, size_t *rsize)
1050 {
1051 	struct disk_devdesc *dev = (struct disk_devdesc *)devdata;
1052 	pdinfo_t *pd;
1053 	EFI_BLOCK_IO *blkio;
1054 	uint64_t off, disk_blocks, d_offset = 0;
1055 	char *blkbuf;
1056 	size_t blkoff, blksz;
1057 	int error;
1058 	size_t diskend, readstart;
1059 
1060 	if (dev == NULL || blk < 0)
1061 		return (EINVAL);
1062 
1063 	pd = efiblk_get_pdinfo((struct devdesc *)dev);
1064 	if (pd == NULL)
1065 		return (EINVAL);
1066 
1067 	blkio = pd->pd_blkio;
1068 	if (blkio == NULL)
1069 		return (ENXIO);
1070 
1071 	if (size == 0 || (size % 512) != 0)
1072 		return (EIO);
1073 
1074 	off = blk * 512;
1075 	/*
1076 	 * Get disk blocks, this value is either for whole disk or for
1077 	 * partition.
1078 	 */
1079 	disk_blocks = 0;
1080 	if (dev->dd.d_dev->dv_type == DEVT_DISK) {
1081 		if (disk_ioctl(dev, DIOCGMEDIASIZE, &disk_blocks) == 0) {
1082 			/* DIOCGMEDIASIZE does return bytes. */
1083 			disk_blocks /= blkio->Media->BlockSize;
1084 		}
1085 		d_offset = dev->d_offset;
1086 	}
1087 	if (disk_blocks == 0)
1088 		disk_blocks = blkio->Media->LastBlock + 1 - d_offset;
1089 
1090 	/* make sure we don't read past disk end */
1091 	if ((off + size) / blkio->Media->BlockSize > d_offset + disk_blocks) {
1092 		diskend = d_offset + disk_blocks;
1093 		readstart = off / blkio->Media->BlockSize;
1094 
1095 		if (diskend <= readstart) {
1096 			if (rsize != NULL)
1097 				*rsize = 0;
1098 
1099 			return (EIO);
1100 		}
1101 		size = diskend - readstart;
1102 		size = size * blkio->Media->BlockSize;
1103 	}
1104 
1105 	if (rsize != NULL)
1106 		*rsize = size;
1107 
1108 	if ((size % blkio->Media->BlockSize == 0) &&
1109 	    (off % blkio->Media->BlockSize == 0))
1110 		return (efipart_readwrite(blkio, rw,
1111 		    off / blkio->Media->BlockSize,
1112 		    size / blkio->Media->BlockSize, buf));
1113 
1114 	/*
1115 	 * The block size of the media is not a multiple of I/O.
1116 	 */
1117 	blkbuf = malloc(blkio->Media->BlockSize);
1118 	if (blkbuf == NULL)
1119 		return (ENOMEM);
1120 
1121 	error = 0;
1122 	blk = off / blkio->Media->BlockSize;
1123 	blkoff = off % blkio->Media->BlockSize;
1124 	blksz = blkio->Media->BlockSize - blkoff;
1125 	while (size > 0) {
1126 		error = efipart_readwrite(blkio, rw, blk, 1, blkbuf);
1127 		if (error)
1128 			break;
1129 		if (size < blksz)
1130 			blksz = size;
1131 		bcopy(blkbuf + blkoff, buf, blksz);
1132 		buf += blksz;
1133 		size -= blksz;
1134 		blk++;
1135 		blkoff = 0;
1136 		blksz = blkio->Media->BlockSize;
1137 	}
1138 
1139 	free(blkbuf);
1140 	return (error);
1141 }
1142