efipart.c revision 344406
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 344406 2019-02-21 02:37:01Z 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
122/* XXX this gets called way way too often, investigate */
123pdinfo_t *
124efiblk_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
140pdinfo_t *
141efiblk_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
162static bool
163same_handle(pdinfo_t *pd, EFI_HANDLE h)
164{
165
166	return (pd->pd_handle == h || pd->pd_alias == h);
167}
168
169pdinfo_t *
170efiblk_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
196static int
197efiblk_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
208int
209efipart_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
243static ACPI_HID_DEVICE_PATH *
244efipart_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 */
268static bool
269efipart_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 */
338static int
339efipart_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
359static void
360efipart_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
380static int
381efipart_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 */
395static int
396efipart_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
430static void
431efipart_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
497static int
498efipart_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
509static int
510efipart_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 */
599static int
600efipart_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
685static void
686efipart_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
748static int
749efipart_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
760static int
761efipart_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
838static int
839efipart_printfd(int verbose)
840{
841	return (efipart_print_common(&efipart_fddev, &fdinfo, verbose));
842}
843
844static int
845efipart_printcd(int verbose)
846{
847	return (efipart_print_common(&efipart_cddev, &cdinfo, verbose));
848}
849
850static int
851efipart_printhd(int verbose)
852{
853	return (efipart_print_common(&efipart_hddev, &hdinfo, verbose));
854}
855
856static int
857efipart_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
909static int
910efipart_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
934static int
935efipart_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 */
977static int
978efipart_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
1012static int
1013efipart_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
1047static int
1048efipart_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