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