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