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