efipart.c revision 332154
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 332154 2018-04-06 21:37: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.dd.d_dev = dev;
748			pd_dev.dd.d_unit = pd->pd_unit;
749			pd_dev.d_slice = -1;
750			pd_dev.d_partition = -1;
751			ret = disk_open(&pd_dev, blkio->Media->BlockSize *
752			    (blkio->Media->LastBlock + 1),
753			    blkio->Media->BlockSize);
754			if (ret == 0) {
755				ret = disk_print(&pd_dev, line, verbose);
756				disk_close(&pd_dev);
757				if (ret != 0)
758					return (ret);
759			} else {
760				/* Do not fail from disk_open() */
761				ret = 0;
762			}
763		} else {
764			if ((ret = pager_output("\n")) != 0)
765				break;
766		}
767	}
768	return (ret);
769}
770
771static int
772efipart_printfd(int verbose)
773{
774	return (efipart_print_common(&efipart_fddev, &fdinfo, verbose));
775}
776
777static int
778efipart_printcd(int verbose)
779{
780	return (efipart_print_common(&efipart_cddev, &cdinfo, verbose));
781}
782
783static int
784efipart_printhd(int verbose)
785{
786	return (efipart_print_common(&efipart_hddev, &hdinfo, verbose));
787}
788
789static int
790efipart_open(struct open_file *f, ...)
791{
792	va_list args;
793	struct disk_devdesc *dev;
794	pdinfo_t *pd;
795	EFI_BLOCK_IO *blkio;
796	EFI_STATUS status;
797
798	va_start(args, f);
799	dev = va_arg(args, struct disk_devdesc*);
800	va_end(args);
801	if (dev == NULL)
802		return (EINVAL);
803
804	pd = efiblk_get_pdinfo((struct devdesc *)dev);
805	if (pd == NULL)
806		return (EIO);
807
808	if (pd->pd_blkio == NULL) {
809		status = BS->HandleProtocol(pd->pd_handle, &blkio_guid,
810		    (void **)&pd->pd_blkio);
811		if (EFI_ERROR(status))
812			return (efi_status_to_errno(status));
813	}
814
815	blkio = pd->pd_blkio;
816	if (!blkio->Media->MediaPresent)
817		return (EAGAIN);
818
819	pd->pd_open++;
820	if (pd->pd_bcache == NULL)
821		pd->pd_bcache = bcache_allocate();
822
823	if (dev->dd.d_dev->dv_type == DEVT_DISK) {
824		int rc;
825
826		rc = disk_open(dev,
827		    blkio->Media->BlockSize * (blkio->Media->LastBlock + 1),
828		    blkio->Media->BlockSize);
829		if (rc != 0) {
830			pd->pd_open--;
831			if (pd->pd_open == 0) {
832				pd->pd_blkio = NULL;
833				bcache_free(pd->pd_bcache);
834				pd->pd_bcache = NULL;
835			}
836		}
837		return (rc);
838	}
839	return (0);
840}
841
842static int
843efipart_close(struct open_file *f)
844{
845	struct disk_devdesc *dev;
846	pdinfo_t *pd;
847
848	dev = (struct disk_devdesc *)(f->f_devdata);
849	if (dev == NULL)
850		return (EINVAL);
851
852	pd = efiblk_get_pdinfo((struct devdesc *)dev);
853	if (pd == NULL)
854		return (EINVAL);
855
856	pd->pd_open--;
857	if (pd->pd_open == 0) {
858		pd->pd_blkio = NULL;
859		bcache_free(pd->pd_bcache);
860		pd->pd_bcache = NULL;
861	}
862	if (dev->dd.d_dev->dv_type == DEVT_DISK)
863		return (disk_close(dev));
864	return (0);
865}
866
867static int
868efipart_ioctl(struct open_file *f, u_long cmd, void *data)
869{
870	struct disk_devdesc *dev;
871	pdinfo_t *pd;
872	int rc;
873
874	dev = (struct disk_devdesc *)(f->f_devdata);
875	if (dev == NULL)
876		return (EINVAL);
877
878	pd = efiblk_get_pdinfo((struct devdesc *)dev);
879	if (pd == NULL)
880		return (EINVAL);
881
882	if (dev->dd.d_dev->dv_type == DEVT_DISK) {
883		rc = disk_ioctl(dev, cmd, data);
884		if (rc != ENOTTY)
885			return (rc);
886	}
887
888	switch (cmd) {
889	case DIOCGSECTORSIZE:
890		*(u_int *)data = pd->pd_blkio->Media->BlockSize;
891		break;
892	case DIOCGMEDIASIZE:
893		*(uint64_t *)data = pd->pd_blkio->Media->BlockSize *
894		    (pd->pd_blkio->Media->LastBlock + 1);
895		break;
896	default:
897		return (ENOTTY);
898	}
899
900	return (0);
901}
902
903/*
904 * efipart_readwrite()
905 * Internal equivalent of efipart_strategy(), which operates on the
906 * media-native block size. This function expects all I/O requests
907 * to be within the media size and returns an error if such is not
908 * the case.
909 */
910static int
911efipart_readwrite(EFI_BLOCK_IO *blkio, int rw, daddr_t blk, daddr_t nblks,
912    char *buf)
913{
914	EFI_STATUS status;
915
916	if (blkio == NULL)
917		return (ENXIO);
918	if (blk < 0 || blk > blkio->Media->LastBlock)
919		return (EIO);
920	if ((blk + nblks - 1) > blkio->Media->LastBlock)
921		return (EIO);
922
923	switch (rw & F_MASK) {
924	case F_READ:
925		status = blkio->ReadBlocks(blkio, blkio->Media->MediaId, blk,
926		    nblks * blkio->Media->BlockSize, buf);
927		break;
928	case F_WRITE:
929		if (blkio->Media->ReadOnly)
930			return (EROFS);
931		status = blkio->WriteBlocks(blkio, blkio->Media->MediaId, blk,
932		    nblks * blkio->Media->BlockSize, buf);
933		break;
934	default:
935		return (ENOSYS);
936	}
937
938	if (EFI_ERROR(status)) {
939		printf("%s: rw=%d, blk=%ju size=%ju status=%lu\n", __func__, rw,
940		    blk, nblks, EFI_ERROR_CODE(status));
941	}
942	return (efi_status_to_errno(status));
943}
944
945static int
946efipart_strategy(void *devdata, int rw, daddr_t blk, size_t size,
947    char *buf, size_t *rsize)
948{
949	struct bcache_devdata bcd;
950	struct disk_devdesc *dev;
951	pdinfo_t *pd;
952
953	dev = (struct disk_devdesc *)devdata;
954	if (dev == NULL)
955		return (EINVAL);
956
957	pd = efiblk_get_pdinfo((struct devdesc *)dev);
958	if (pd == NULL)
959		return (EINVAL);
960
961	if (pd->pd_blkio->Media->RemovableMedia &&
962	    !pd->pd_blkio->Media->MediaPresent)
963		return (ENXIO);
964
965	bcd.dv_strategy = efipart_realstrategy;
966	bcd.dv_devdata = devdata;
967	bcd.dv_cache = pd->pd_bcache;
968
969	if (dev->dd.d_dev->dv_type == DEVT_DISK) {
970		daddr_t offset;
971
972		offset = dev->d_offset * pd->pd_blkio->Media->BlockSize;
973		offset /= 512;
974		return (bcache_strategy(&bcd, rw, blk + offset,
975		    size, buf, rsize));
976	}
977	return (bcache_strategy(&bcd, rw, blk, size, buf, rsize));
978}
979
980static int
981efipart_realstrategy(void *devdata, int rw, daddr_t blk, size_t size,
982    char *buf, size_t *rsize)
983{
984	struct disk_devdesc *dev = (struct disk_devdesc *)devdata;
985	pdinfo_t *pd;
986	EFI_BLOCK_IO *blkio;
987	uint64_t off, disk_blocks, d_offset = 0;
988	char *blkbuf;
989	size_t blkoff, blksz;
990	int error;
991	size_t diskend, readstart;
992
993	if (dev == NULL || blk < 0)
994		return (EINVAL);
995
996	pd = efiblk_get_pdinfo((struct devdesc *)dev);
997	if (pd == NULL)
998		return (EINVAL);
999
1000	blkio = pd->pd_blkio;
1001	if (blkio == NULL)
1002		return (ENXIO);
1003
1004	if (size == 0 || (size % 512) != 0)
1005		return (EIO);
1006
1007	off = blk * 512;
1008	/*
1009	 * Get disk blocks, this value is either for whole disk or for
1010	 * partition.
1011	 */
1012	disk_blocks = 0;
1013	if (dev->dd.d_dev->dv_type == DEVT_DISK) {
1014		if (disk_ioctl(dev, DIOCGMEDIASIZE, &disk_blocks) == 0) {
1015			/* DIOCGMEDIASIZE does return bytes. */
1016			disk_blocks /= blkio->Media->BlockSize;
1017		}
1018		d_offset = dev->d_offset;
1019	}
1020	if (disk_blocks == 0)
1021		disk_blocks = blkio->Media->LastBlock + 1 - d_offset;
1022
1023	/* make sure we don't read past disk end */
1024	if ((off + size) / blkio->Media->BlockSize > d_offset + disk_blocks) {
1025		diskend = d_offset + disk_blocks;
1026		readstart = off / blkio->Media->BlockSize;
1027
1028		if (diskend <= readstart) {
1029			if (rsize != NULL)
1030				*rsize = 0;
1031
1032			return (EIO);
1033		}
1034		size = diskend - readstart;
1035		size = size * blkio->Media->BlockSize;
1036	}
1037
1038	if (rsize != NULL)
1039		*rsize = size;
1040
1041	if ((size % blkio->Media->BlockSize == 0) &&
1042	    (off % blkio->Media->BlockSize == 0))
1043		return (efipart_readwrite(blkio, rw,
1044		    off / blkio->Media->BlockSize,
1045		    size / blkio->Media->BlockSize, buf));
1046
1047	/*
1048	 * The block size of the media is not a multiple of I/O.
1049	 */
1050	blkbuf = malloc(blkio->Media->BlockSize);
1051	if (blkbuf == NULL)
1052		return (ENOMEM);
1053
1054	error = 0;
1055	blk = off / blkio->Media->BlockSize;
1056	blkoff = off % blkio->Media->BlockSize;
1057	blksz = blkio->Media->BlockSize - blkoff;
1058	while (size > 0) {
1059		error = efipart_readwrite(blkio, rw, blk, 1, blkbuf);
1060		if (error)
1061			break;
1062		if (size < blksz)
1063			blksz = size;
1064		bcopy(blkbuf + blkoff, buf, blksz);
1065		buf += blksz;
1066		size -= blksz;
1067		blk++;
1068		blkoff = 0;
1069		blksz = blkio->Media->BlockSize;
1070	}
1071
1072	free(blkbuf);
1073	return (error);
1074}
1075