xref: /freebsd/stand/efi/libefi/efipart.c (revision 8a0a413e)
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 <disk.h>
43 
44 static EFI_GUID blkio_guid = BLOCK_IO_PROTOCOL;
45 
46 static int efipart_initfd(void);
47 static int efipart_initcd(void);
48 static int efipart_inithd(void);
49 
50 static int efipart_strategy(void *, int, daddr_t, size_t, char *, size_t *);
51 static int efipart_realstrategy(void *, int, daddr_t, size_t, char *, size_t *);
52 
53 static int efipart_open(struct open_file *, ...);
54 static int efipart_close(struct open_file *);
55 static int efipart_ioctl(struct open_file *, u_long, void *);
56 
57 static int efipart_printfd(int);
58 static int efipart_printcd(int);
59 static int efipart_printhd(int);
60 
61 /* EISA PNP ID's for floppy controllers */
62 #define	PNP0604	0x604
63 #define	PNP0700	0x700
64 #define	PNP0701	0x701
65 
66 struct devsw efipart_fddev = {
67 	.dv_name = "fd",
68 	.dv_type = DEVT_FD,
69 	.dv_init = efipart_initfd,
70 	.dv_strategy = efipart_strategy,
71 	.dv_open = efipart_open,
72 	.dv_close = efipart_close,
73 	.dv_ioctl = efipart_ioctl,
74 	.dv_print = efipart_printfd,
75 	.dv_cleanup = NULL
76 };
77 
78 struct devsw efipart_cddev = {
79 	.dv_name = "cd",
80 	.dv_type = DEVT_CD,
81 	.dv_init = efipart_initcd,
82 	.dv_strategy = efipart_strategy,
83 	.dv_open = efipart_open,
84 	.dv_close = efipart_close,
85 	.dv_ioctl = efipart_ioctl,
86 	.dv_print = efipart_printcd,
87 	.dv_cleanup = NULL
88 };
89 
90 struct devsw efipart_hddev = {
91 	.dv_name = "disk",
92 	.dv_type = DEVT_DISK,
93 	.dv_init = efipart_inithd,
94 	.dv_strategy = efipart_strategy,
95 	.dv_open = efipart_open,
96 	.dv_close = efipart_close,
97 	.dv_ioctl = efipart_ioctl,
98 	.dv_print = efipart_printhd,
99 	.dv_cleanup = NULL
100 };
101 
102 static pdinfo_list_t fdinfo;
103 static pdinfo_list_t cdinfo;
104 static pdinfo_list_t hdinfo;
105 
106 static EFI_HANDLE *efipart_handles = NULL;
107 static UINTN efipart_nhandles = 0;
108 
109 pdinfo_list_t *
110 efiblk_get_pdinfo_list(struct devsw *dev)
111 {
112 	if (dev->dv_type == DEVT_DISK)
113 		return (&hdinfo);
114 	if (dev->dv_type == DEVT_CD)
115 		return (&cdinfo);
116 	if (dev->dv_type == DEVT_FD)
117 		return (&fdinfo);
118 	return (NULL);
119 }
120 
121 pdinfo_t *
122 efiblk_get_pdinfo(struct devdesc *dev)
123 {
124 	pdinfo_list_t *pdi;
125 	pdinfo_t *pd = NULL;
126 
127 	pdi = efiblk_get_pdinfo_list(dev->d_dev);
128 	if (pdi == NULL)
129 		return (pd);
130 
131 	STAILQ_FOREACH(pd, pdi, pd_link) {
132 		if (pd->pd_unit == dev->d_unit)
133 			return (pd);
134 	}
135 	return (pd);
136 }
137 
138 static int
139 efiblk_pdinfo_count(pdinfo_list_t *pdi)
140 {
141 	pdinfo_t *pd;
142 	int i = 0;
143 
144 	STAILQ_FOREACH(pd, pdi, pd_link) {
145 		i++;
146 	}
147 	return (i);
148 }
149 
150 static int
151 efipart_inithandles(void)
152 {
153 	UINTN sz;
154 	EFI_HANDLE *hin;
155 	EFI_STATUS status;
156 
157 	if (efipart_nhandles != 0) {
158 		free(efipart_handles);
159 		efipart_handles = NULL;
160 		efipart_nhandles = 0;
161 	}
162 
163 	sz = 0;
164 	hin = NULL;
165 	status = BS->LocateHandle(ByProtocol, &blkio_guid, 0, &sz, hin);
166 	if (status == EFI_BUFFER_TOO_SMALL) {
167 		hin = malloc(sz);
168 		status = BS->LocateHandle(ByProtocol, &blkio_guid, 0, &sz,
169 		    hin);
170 		if (EFI_ERROR(status))
171 			free(hin);
172 	}
173 	if (EFI_ERROR(status))
174 		return (efi_status_to_errno(status));
175 
176 	efipart_handles = hin;
177 	efipart_nhandles = sz;
178 	return (0);
179 }
180 
181 static ACPI_HID_DEVICE_PATH *
182 efipart_floppy(EFI_DEVICE_PATH *node)
183 {
184 	ACPI_HID_DEVICE_PATH *acpi;
185 
186 	if (DevicePathType(node) == ACPI_DEVICE_PATH &&
187 	    DevicePathSubType(node) == ACPI_DP) {
188 		acpi = (ACPI_HID_DEVICE_PATH *) node;
189 		if (acpi->HID == EISA_PNP_ID(PNP0604) ||
190 		    acpi->HID == EISA_PNP_ID(PNP0700) ||
191 		    acpi->HID == EISA_PNP_ID(PNP0701)) {
192 			return (acpi);
193 		}
194 	}
195 	return (NULL);
196 }
197 
198 /*
199  * Add or update entries with new handle data.
200  */
201 static int
202 efipart_fdinfo_add(EFI_HANDLE handle, uint32_t uid, EFI_DEVICE_PATH *devpath)
203 {
204 	pdinfo_t *fd;
205 
206 	fd = calloc(1, sizeof(pdinfo_t));
207 	if (fd == NULL) {
208 		printf("Failed to register floppy %d, out of memory\n", uid);
209 		return (ENOMEM);
210 	}
211 	STAILQ_INIT(&fd->pd_part);
212 
213 	fd->pd_unit = uid;
214 	fd->pd_handle = handle;
215 	fd->pd_devpath = devpath;
216 	STAILQ_INSERT_TAIL(&fdinfo, fd, pd_link);
217 	return (0);
218 }
219 
220 static void
221 efipart_updatefd(void)
222 {
223 	EFI_DEVICE_PATH *devpath, *node;
224 	ACPI_HID_DEVICE_PATH *acpi;
225 	int i, nin;
226 
227 	nin = efipart_nhandles / sizeof (*efipart_handles);
228 	for (i = 0; i < nin; i++) {
229 		devpath = efi_lookup_devpath(efipart_handles[i]);
230 		if (devpath == NULL)
231 			continue;
232 
233 		if ((node = efi_devpath_last_node(devpath)) == NULL)
234 			continue;
235 		if ((acpi = efipart_floppy(node)) != NULL) {
236 			efipart_fdinfo_add(efipart_handles[i], acpi->UID,
237 			    devpath);
238 		}
239 	}
240 }
241 
242 static int
243 efipart_initfd(void)
244 {
245 	int rv;
246 
247 	rv = efipart_inithandles();
248 	if (rv != 0)
249 		return (rv);
250 	STAILQ_INIT(&fdinfo);
251 
252 	efipart_updatefd();
253 
254 	bcache_add_dev(efiblk_pdinfo_count(&fdinfo));
255 	return (0);
256 }
257 
258 /*
259  * Add or update entries with new handle data.
260  */
261 static int
262 efipart_cdinfo_add(EFI_HANDLE handle, EFI_HANDLE alias,
263     EFI_DEVICE_PATH *devpath)
264 {
265 	int unit;
266 	pdinfo_t *cd;
267 	pdinfo_t *pd;
268 
269 	unit = 0;
270 	STAILQ_FOREACH(pd, &cdinfo, pd_link) {
271 		if (efi_devpath_match(pd->pd_devpath, devpath) == true) {
272 			pd->pd_handle = handle;
273 			pd->pd_alias = alias;
274 			return (0);
275 		}
276 		unit++;
277 	}
278 
279 	cd = calloc(1, sizeof(pdinfo_t));
280 	if (cd == NULL) {
281 		printf("Failed to add cd %d, out of memory\n", unit);
282 		return (ENOMEM);
283 	}
284 	STAILQ_INIT(&cd->pd_part);
285 
286 	cd->pd_handle = handle;
287 	cd->pd_unit = unit;
288 	cd->pd_alias = alias;
289 	cd->pd_devpath = devpath;
290 	STAILQ_INSERT_TAIL(&cdinfo, cd, pd_link);
291 	return (0);
292 }
293 
294 static void
295 efipart_updatecd(void)
296 {
297 	int i, nin;
298 	EFI_DEVICE_PATH *devpath, *devpathcpy, *tmpdevpath, *node;
299 	EFI_HANDLE handle;
300 	EFI_BLOCK_IO *blkio;
301 	EFI_STATUS status;
302 
303 	nin = efipart_nhandles / sizeof (*efipart_handles);
304 	for (i = 0; i < nin; i++) {
305 		devpath = efi_lookup_devpath(efipart_handles[i]);
306 		if (devpath == NULL)
307 			continue;
308 
309 		if ((node = efi_devpath_last_node(devpath)) == NULL)
310 			continue;
311 		if (efipart_floppy(node) != NULL)
312 			continue;
313 
314 		status = BS->HandleProtocol(efipart_handles[i],
315 		    &blkio_guid, (void **)&blkio);
316 		if (EFI_ERROR(status))
317 			continue;
318 		/*
319 		 * If we come across a logical partition of subtype CDROM
320 		 * it doesn't refer to the CD filesystem itself, but rather
321 		 * to any usable El Torito boot image on it. In this case
322 		 * we try to find the parent device and add that instead as
323 		 * that will be the CD filesystem.
324 		 */
325 		if (DevicePathType(node) == MEDIA_DEVICE_PATH &&
326 		    DevicePathSubType(node) == MEDIA_CDROM_DP) {
327 			devpathcpy = efi_devpath_trim(devpath);
328 			if (devpathcpy == NULL)
329 				continue;
330 			tmpdevpath = devpathcpy;
331 			status = BS->LocateDevicePath(&blkio_guid, &tmpdevpath,
332 			    &handle);
333 			free(devpathcpy);
334 			if (EFI_ERROR(status))
335 				continue;
336 			devpath = efi_lookup_devpath(handle);
337 			efipart_cdinfo_add(handle, efipart_handles[i],
338 			    devpath);
339 			continue;
340 		}
341 
342 		if (DevicePathType(node) == MESSAGING_DEVICE_PATH &&
343 		    DevicePathSubType(node) == MSG_ATAPI_DP) {
344 			efipart_cdinfo_add(efipart_handles[i], NULL,
345 			    devpath);
346 			continue;
347 		}
348 
349 		/* USB or SATA cd without the media. */
350 		if (blkio->Media->RemovableMedia &&
351 		    !blkio->Media->MediaPresent) {
352 			efipart_cdinfo_add(efipart_handles[i], NULL,
353 			    devpath);
354 		}
355 	}
356 }
357 
358 static int
359 efipart_initcd(void)
360 {
361 	int rv;
362 
363 	rv = efipart_inithandles();
364 	if (rv != 0)
365 		return (rv);
366 	STAILQ_INIT(&cdinfo);
367 
368 	efipart_updatecd();
369 
370 	bcache_add_dev(efiblk_pdinfo_count(&cdinfo));
371 	return (0);
372 }
373 
374 static int
375 efipart_hdinfo_add(EFI_HANDLE disk_handle, EFI_HANDLE part_handle)
376 {
377 	EFI_DEVICE_PATH *disk_devpath, *part_devpath;
378 	HARDDRIVE_DEVICE_PATH *node;
379 	int unit;
380 	pdinfo_t *hd, *pd, *last;
381 
382 	disk_devpath = efi_lookup_devpath(disk_handle);
383 	part_devpath = efi_lookup_devpath(part_handle);
384 	if (disk_devpath == NULL || part_devpath == NULL) {
385 		return (ENOENT);
386 	}
387 	node = (HARDDRIVE_DEVICE_PATH *)efi_devpath_last_node(part_devpath);
388 	if (node == NULL)
389 		return (ENOENT);	/* This should not happen. */
390 
391 	pd = calloc(1, sizeof(pdinfo_t));
392 	if (pd == NULL) {
393 		printf("Failed to add disk, out of memory\n");
394 		return (ENOMEM);
395 	}
396 	STAILQ_INIT(&pd->pd_part);
397 
398 	STAILQ_FOREACH(hd, &hdinfo, pd_link) {
399 		if (efi_devpath_match(hd->pd_devpath, disk_devpath) == true) {
400 			/* Add the partition. */
401 			pd->pd_handle = part_handle;
402 			pd->pd_unit = node->PartitionNumber;
403 			pd->pd_devpath = part_devpath;
404 			STAILQ_INSERT_TAIL(&hd->pd_part, pd, pd_link);
405 			return (0);
406 		}
407 	}
408 
409 	last = STAILQ_LAST(&hdinfo, pdinfo, pd_link);
410 	if (last != NULL)
411 		unit = last->pd_unit + 1;
412 	else
413 		unit = 0;
414 
415 	/* Add the disk. */
416 	hd = pd;
417 	hd->pd_handle = disk_handle;
418 	hd->pd_unit = unit;
419 	hd->pd_devpath = disk_devpath;
420 	STAILQ_INSERT_TAIL(&hdinfo, hd, pd_link);
421 
422 	pd = calloc(1, sizeof(pdinfo_t));
423 	if (pd == NULL) {
424 		printf("Failed to add partition, out of memory\n");
425 		return (ENOMEM);
426 	}
427 	STAILQ_INIT(&pd->pd_part);
428 
429 	/* Add the partition. */
430 	pd->pd_handle = part_handle;
431 	pd->pd_unit = node->PartitionNumber;
432 	pd->pd_devpath = part_devpath;
433 	STAILQ_INSERT_TAIL(&hd->pd_part, pd, pd_link);
434 
435 	return (0);
436 }
437 
438 /*
439  * The MEDIA_FILEPATH_DP has device name.
440  * From U-Boot sources it looks like names are in the form
441  * of typeN:M, where type is interface type, N is disk id
442  * and M is partition id.
443  */
444 static int
445 efipart_hdinfo_add_filepath(EFI_HANDLE disk_handle)
446 {
447 	EFI_DEVICE_PATH *devpath;
448 	FILEPATH_DEVICE_PATH *node;
449 	char *pathname, *p;
450 	int unit, len;
451 	pdinfo_t *pd, *last;
452 
453 	/* First collect and verify all the data */
454 	if ((devpath = efi_lookup_devpath(disk_handle)) == NULL)
455 		return (ENOENT);
456 	node = (FILEPATH_DEVICE_PATH *)efi_devpath_last_node(devpath);
457 	if (node == NULL)
458 		return (ENOENT);	/* This should not happen. */
459 
460 	pd = calloc(1, sizeof(pdinfo_t));
461 	if (pd == NULL) {
462 		printf("Failed to add disk, out of memory\n");
463 		return (ENOMEM);
464 	}
465 	STAILQ_INIT(&pd->pd_part);
466 	last = STAILQ_LAST(&hdinfo, pdinfo, pd_link);
467 	if (last != NULL)
468 		unit = last->pd_unit + 1;
469 	else
470 		unit = 0;
471 
472 	/* FILEPATH_DEVICE_PATH has 0 terminated string */
473 	for (len = 0; node->PathName[len] != 0; len++)
474 		;
475 	if ((pathname = malloc(len + 1)) == NULL) {
476 		printf("Failed to add disk, out of memory\n");
477 		free(pd);
478 		return (ENOMEM);
479 	}
480 	cpy16to8(node->PathName, pathname, len + 1);
481 	p = strchr(pathname, ':');
482 
483 	/*
484 	 * Assume we are receiving handles in order, first disk handle,
485 	 * then partitions for this disk. If this assumption proves
486 	 * false, this code would need update.
487 	 */
488 	if (p == NULL) {	/* no colon, add the disk */
489 		pd->pd_handle = disk_handle;
490 		pd->pd_unit = unit;
491 		pd->pd_devpath = devpath;
492 		STAILQ_INSERT_TAIL(&hdinfo, pd, pd_link);
493 		free(pathname);
494 		return (0);
495 	}
496 	p++;	/* skip the colon */
497 	errno = 0;
498 	unit = (int)strtol(p, NULL, 0);
499 	if (errno != 0) {
500 		printf("Bad unit number for partition \"%s\"\n", pathname);
501 		free(pathname);
502 		free(pd);
503 		return (EUNIT);
504 	}
505 
506 	/*
507 	 * We should have disk registered, if not, we are receiving
508 	 * handles out of order, and this code should be reworked
509 	 * to create "blank" disk for partition, and to find the
510 	 * disk based on PathName compares.
511 	 */
512 	if (last == NULL) {
513 		printf("BUG: No disk for partition \"%s\"\n", pathname);
514 		free(pathname);
515 		free(pd);
516 		return (EINVAL);
517 	}
518 	/* Add the partition. */
519 	pd->pd_handle = disk_handle;
520 	pd->pd_unit = unit;
521 	pd->pd_devpath = devpath;
522 	STAILQ_INSERT_TAIL(&last->pd_part, pd, pd_link);
523 	free(pathname);
524 	return (0);
525 }
526 
527 static void
528 efipart_updatehd(void)
529 {
530 	int i, nin;
531 	EFI_DEVICE_PATH *devpath, *devpathcpy, *tmpdevpath, *node;
532 	EFI_HANDLE handle;
533 	EFI_BLOCK_IO *blkio;
534 	EFI_STATUS status;
535 
536 	nin = efipart_nhandles / sizeof (*efipart_handles);
537 	for (i = 0; i < nin; i++) {
538 		devpath = efi_lookup_devpath(efipart_handles[i]);
539 		if (devpath == NULL)
540 			continue;
541 
542 		if ((node = efi_devpath_last_node(devpath)) == NULL)
543 			continue;
544 		if (efipart_floppy(node) != NULL)
545 			continue;
546 
547 		status = BS->HandleProtocol(efipart_handles[i],
548 		    &blkio_guid, (void **)&blkio);
549 		if (EFI_ERROR(status))
550 			continue;
551 
552 		if (DevicePathType(node) == MEDIA_DEVICE_PATH &&
553 		    DevicePathSubType(node) == MEDIA_HARDDRIVE_DP) {
554 			devpathcpy = efi_devpath_trim(devpath);
555 			if (devpathcpy == NULL)
556 				continue;
557 			tmpdevpath = devpathcpy;
558 			status = BS->LocateDevicePath(&blkio_guid, &tmpdevpath,
559 			    &handle);
560 			free(devpathcpy);
561 			if (EFI_ERROR(status))
562 				continue;
563 			/*
564 			 * We do not support nested partitions.
565 			 */
566 			devpathcpy = efi_lookup_devpath(handle);
567 			if (devpathcpy == NULL)
568 				continue;
569 			if ((node = efi_devpath_last_node(devpathcpy)) == NULL)
570 				continue;
571 			if (DevicePathType(node) == MEDIA_DEVICE_PATH &&
572 			    DevicePathSubType(node) == MEDIA_HARDDRIVE_DP)
573 				continue;
574 			efipart_hdinfo_add(handle, efipart_handles[i]);
575 			continue;
576 		}
577 
578 		if (DevicePathType(node) == MEDIA_DEVICE_PATH &&
579 		    DevicePathSubType(node) == MEDIA_FILEPATH_DP) {
580 			efipart_hdinfo_add_filepath(efipart_handles[i]);
581 			continue;
582 		}
583 	}
584 }
585 
586 static int
587 efipart_inithd(void)
588 {
589 	int rv;
590 
591 	rv = efipart_inithandles();
592 	if (rv != 0)
593 		return (rv);
594 	STAILQ_INIT(&hdinfo);
595 
596 	efipart_updatehd();
597 
598 	bcache_add_dev(efiblk_pdinfo_count(&hdinfo));
599 	return (0);
600 }
601 
602 static int
603 efipart_print_common(struct devsw *dev, pdinfo_list_t *pdlist, int verbose)
604 {
605 	int ret = 0;
606 	EFI_BLOCK_IO *blkio;
607 	EFI_STATUS status;
608 	EFI_HANDLE h;
609 	pdinfo_t *pd;
610 	CHAR16 *text;
611 	struct disk_devdesc pd_dev;
612 	char line[80];
613 
614 	if (STAILQ_EMPTY(pdlist))
615 		return (0);
616 
617 	printf("%s devices:", dev->dv_name);
618 	if ((ret = pager_output("\n")) != 0)
619 		return (ret);
620 
621 	STAILQ_FOREACH(pd, pdlist, pd_link) {
622 		h = pd->pd_handle;
623 		if (verbose) {	/* Output the device path. */
624 			text = efi_devpath_name(efi_lookup_devpath(h));
625 			if (text != NULL) {
626 				printf("  %S", text);
627 				efi_free_devpath_name(text);
628 				if ((ret = pager_output("\n")) != 0)
629 					break;
630 			}
631 		}
632 		snprintf(line, sizeof(line),
633 		    "    %s%d", dev->dv_name, pd->pd_unit);
634 		printf("%s:", line);
635 		status = BS->HandleProtocol(h, &blkio_guid, (void **)&blkio);
636 		if (!EFI_ERROR(status)) {
637 			printf("    %llu",
638 			    blkio->Media->LastBlock == 0? 0:
639 			    (unsigned long long) (blkio->Media->LastBlock + 1));
640 			if (blkio->Media->LastBlock != 0) {
641 				printf(" X %u", blkio->Media->BlockSize);
642 			}
643 			printf(" blocks");
644 			if (blkio->Media->MediaPresent) {
645 				if (blkio->Media->RemovableMedia)
646 					printf(" (removable)");
647 			} else {
648 				printf(" (no media)");
649 			}
650 			if ((ret = pager_output("\n")) != 0)
651 				break;
652 			if (!blkio->Media->MediaPresent)
653 				continue;
654 
655 			pd->pd_blkio = blkio;
656 			pd_dev.d_dev = dev;
657 			pd_dev.d_unit = pd->pd_unit;
658 			pd_dev.d_slice = -1;
659 			pd_dev.d_partition = -1;
660 			pd_dev.d_opendata = blkio;
661 			ret = disk_open(&pd_dev, blkio->Media->BlockSize *
662 			    (blkio->Media->LastBlock + 1),
663 			    blkio->Media->BlockSize);
664 			if (ret == 0) {
665 				ret = disk_print(&pd_dev, line, verbose);
666 				disk_close(&pd_dev);
667 				if (ret != 0)
668 					return (ret);
669 			} else {
670 				/* Do not fail from disk_open() */
671 				ret = 0;
672 			}
673 		} else {
674 			if ((ret = pager_output("\n")) != 0)
675 				break;
676 		}
677 	}
678 	return (ret);
679 }
680 
681 static int
682 efipart_printfd(int verbose)
683 {
684 	return (efipart_print_common(&efipart_fddev, &fdinfo, verbose));
685 }
686 
687 static int
688 efipart_printcd(int verbose)
689 {
690 	return (efipart_print_common(&efipart_cddev, &cdinfo, verbose));
691 }
692 
693 static int
694 efipart_printhd(int verbose)
695 {
696 	return (efipart_print_common(&efipart_hddev, &hdinfo, verbose));
697 }
698 
699 static int
700 efipart_open(struct open_file *f, ...)
701 {
702 	va_list args;
703 	struct disk_devdesc *dev;
704 	pdinfo_t *pd;
705 	EFI_BLOCK_IO *blkio;
706 	EFI_STATUS status;
707 
708 	va_start(args, f);
709 	dev = va_arg(args, struct disk_devdesc*);
710 	va_end(args);
711 	if (dev == NULL)
712 		return (EINVAL);
713 
714 	pd = efiblk_get_pdinfo((struct devdesc *)dev);
715 	if (pd == NULL)
716 		return (EIO);
717 
718 	if (pd->pd_blkio == NULL) {
719 		status = BS->HandleProtocol(pd->pd_handle, &blkio_guid,
720 		    (void **)&pd->pd_blkio);
721 		if (EFI_ERROR(status))
722 			return (efi_status_to_errno(status));
723 	}
724 
725 	blkio = pd->pd_blkio;
726 	if (!blkio->Media->MediaPresent)
727 		return (EAGAIN);
728 
729 	pd->pd_open++;
730 	if (pd->pd_bcache == NULL)
731 		pd->pd_bcache = bcache_allocate();
732 
733 	if (dev->d_dev->dv_type == DEVT_DISK) {
734 		int rc;
735 
736 		rc = disk_open(dev,
737 		    blkio->Media->BlockSize * (blkio->Media->LastBlock + 1),
738 		    blkio->Media->BlockSize);
739 		if (rc != 0) {
740 			pd->pd_open--;
741 			if (pd->pd_open == 0) {
742 				pd->pd_blkio = NULL;
743 				bcache_free(pd->pd_bcache);
744 				pd->pd_bcache = NULL;
745 			}
746 		}
747 		return (rc);
748 	}
749 	return (0);
750 }
751 
752 static int
753 efipart_close(struct open_file *f)
754 {
755 	struct disk_devdesc *dev;
756 	pdinfo_t *pd;
757 
758 	dev = (struct disk_devdesc *)(f->f_devdata);
759 	if (dev == NULL)
760 		return (EINVAL);
761 
762 	pd = efiblk_get_pdinfo((struct devdesc *)dev);
763 	if (pd == NULL)
764 		return (EINVAL);
765 
766 	pd->pd_open--;
767 	if (pd->pd_open == 0) {
768 		pd->pd_blkio = NULL;
769 		bcache_free(pd->pd_bcache);
770 		pd->pd_bcache = NULL;
771 	}
772 	if (dev->d_dev->dv_type == DEVT_DISK)
773 		return (disk_close(dev));
774 	return (0);
775 }
776 
777 static int
778 efipart_ioctl(struct open_file *f, u_long cmd, void *data)
779 {
780 	struct disk_devdesc *dev;
781 	pdinfo_t *pd;
782 	int rc;
783 
784 	dev = (struct disk_devdesc *)(f->f_devdata);
785 	if (dev == NULL)
786 		return (EINVAL);
787 
788 	pd = efiblk_get_pdinfo((struct devdesc *)dev);
789 	if (pd == NULL)
790 		return (EINVAL);
791 
792 	if (dev->d_dev->dv_type == DEVT_DISK) {
793 		rc = disk_ioctl(dev, cmd, data);
794 		if (rc != ENOTTY)
795 			return (rc);
796 	}
797 
798 	switch (cmd) {
799 	case DIOCGSECTORSIZE:
800 		*(u_int *)data = pd->pd_blkio->Media->BlockSize;
801 		break;
802 	case DIOCGMEDIASIZE:
803 		*(uint64_t *)data = pd->pd_blkio->Media->BlockSize *
804 		    (pd->pd_blkio->Media->LastBlock + 1);
805 		break;
806 	default:
807 		return (ENOTTY);
808 	}
809 
810 	return (0);
811 }
812 
813 /*
814  * efipart_readwrite()
815  * Internal equivalent of efipart_strategy(), which operates on the
816  * media-native block size. This function expects all I/O requests
817  * to be within the media size and returns an error if such is not
818  * the case.
819  */
820 static int
821 efipart_readwrite(EFI_BLOCK_IO *blkio, int rw, daddr_t blk, daddr_t nblks,
822     char *buf)
823 {
824 	EFI_STATUS status;
825 
826 	if (blkio == NULL)
827 		return (ENXIO);
828 	if (blk < 0 || blk > blkio->Media->LastBlock)
829 		return (EIO);
830 	if ((blk + nblks - 1) > blkio->Media->LastBlock)
831 		return (EIO);
832 
833 	switch (rw & F_MASK) {
834 	case F_READ:
835 		status = blkio->ReadBlocks(blkio, blkio->Media->MediaId, blk,
836 		    nblks * blkio->Media->BlockSize, buf);
837 		break;
838 	case F_WRITE:
839 		if (blkio->Media->ReadOnly)
840 			return (EROFS);
841 		status = blkio->WriteBlocks(blkio, blkio->Media->MediaId, blk,
842 		    nblks * blkio->Media->BlockSize, buf);
843 		break;
844 	default:
845 		return (ENOSYS);
846 	}
847 
848 	if (EFI_ERROR(status)) {
849 		printf("%s: rw=%d, blk=%ju size=%ju status=%lu\n", __func__, rw,
850 		    blk, nblks, EFI_ERROR_CODE(status));
851 	}
852 	return (efi_status_to_errno(status));
853 }
854 
855 static int
856 efipart_strategy(void *devdata, int rw, daddr_t blk, size_t size,
857     char *buf, size_t *rsize)
858 {
859 	struct bcache_devdata bcd;
860 	struct disk_devdesc *dev;
861 	pdinfo_t *pd;
862 
863 	dev = (struct disk_devdesc *)devdata;
864 	if (dev == NULL)
865 		return (EINVAL);
866 
867 	pd = efiblk_get_pdinfo((struct devdesc *)dev);
868 	if (pd == NULL)
869 		return (EINVAL);
870 
871 	if (pd->pd_blkio->Media->RemovableMedia &&
872 	    !pd->pd_blkio->Media->MediaPresent)
873 		return (ENXIO);
874 
875 	bcd.dv_strategy = efipart_realstrategy;
876 	bcd.dv_devdata = devdata;
877 	bcd.dv_cache = pd->pd_bcache;
878 
879 	if (dev->d_dev->dv_type == DEVT_DISK) {
880 		daddr_t offset;
881 
882 		offset = dev->d_offset * pd->pd_blkio->Media->BlockSize;
883 		offset /= 512;
884 		return (bcache_strategy(&bcd, rw, blk + offset,
885 		    size, buf, rsize));
886 	}
887 	return (bcache_strategy(&bcd, rw, blk, size, buf, rsize));
888 }
889 
890 static int
891 efipart_realstrategy(void *devdata, int rw, daddr_t blk, size_t size,
892     char *buf, size_t *rsize)
893 {
894 	struct disk_devdesc *dev = (struct disk_devdesc *)devdata;
895 	pdinfo_t *pd;
896 	EFI_BLOCK_IO *blkio;
897 	uint64_t off, disk_blocks, d_offset = 0;
898 	char *blkbuf;
899 	size_t blkoff, blksz;
900 	int error;
901 	size_t diskend, readstart;
902 
903 	if (dev == NULL || blk < 0)
904 		return (EINVAL);
905 
906 	pd = efiblk_get_pdinfo((struct devdesc *)dev);
907 	if (pd == NULL)
908 		return (EINVAL);
909 
910 	blkio = pd->pd_blkio;
911 	if (blkio == NULL)
912 		return (ENXIO);
913 
914 	if (size == 0 || (size % 512) != 0)
915 		return (EIO);
916 
917 	off = blk * 512;
918 	/*
919 	 * Get disk blocks, this value is either for whole disk or for
920 	 * partition.
921 	 */
922 	disk_blocks = 0;
923 	if (dev->d_dev->dv_type == DEVT_DISK) {
924 		if (disk_ioctl(dev, DIOCGMEDIASIZE, &disk_blocks) == 0) {
925 			/* DIOCGMEDIASIZE does return bytes. */
926 			disk_blocks /= blkio->Media->BlockSize;
927 		}
928 		d_offset = dev->d_offset;
929 	}
930 	if (disk_blocks == 0)
931 		disk_blocks = blkio->Media->LastBlock + 1 - d_offset;
932 
933 	/* make sure we don't read past disk end */
934 	if ((off + size) / blkio->Media->BlockSize > d_offset + disk_blocks) {
935 		diskend = d_offset + disk_blocks;
936 		readstart = off / blkio->Media->BlockSize;
937 
938 		if (diskend <= readstart) {
939 			if (rsize != NULL)
940 				*rsize = 0;
941 
942 			return (EIO);
943 		}
944 		size = diskend - readstart;
945 		size = size * blkio->Media->BlockSize;
946 	}
947 
948 	if (rsize != NULL)
949 		*rsize = size;
950 
951 	if ((size % blkio->Media->BlockSize == 0) &&
952 	    (off % blkio->Media->BlockSize == 0))
953 		return (efipart_readwrite(blkio, rw,
954 		    off / blkio->Media->BlockSize,
955 		    size / blkio->Media->BlockSize, buf));
956 
957 	/*
958 	 * The block size of the media is not a multiple of I/O.
959 	 */
960 	blkbuf = malloc(blkio->Media->BlockSize);
961 	if (blkbuf == NULL)
962 		return (ENOMEM);
963 
964 	error = 0;
965 	blk = off / blkio->Media->BlockSize;
966 	blkoff = off % blkio->Media->BlockSize;
967 	blksz = blkio->Media->BlockSize - blkoff;
968 	while (size > 0) {
969 		error = efipart_readwrite(blkio, rw, blk, 1, blkbuf);
970 		if (error)
971 			break;
972 		if (size < blksz)
973 			blksz = size;
974 		bcopy(blkbuf + blkoff, buf, blksz);
975 		buf += blksz;
976 		size -= blksz;
977 		blk++;
978 		blkoff = 0;
979 		blksz = blkio->Media->BlockSize;
980 	}
981 
982 	free(blkbuf);
983 	return (error);
984 }
985