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/disk.h>
28#include <sys/param.h>
29#include <sys/time.h>
30#include <sys/queue.h>
31#include <stddef.h>
32#include <stdarg.h>
33
34#include <bootstrap.h>
35
36#include <efi.h>
37#include <efilib.h>
38#include <efiprot.h>
39#include <efichar.h>
40#include <disk.h>
41
42static EFI_GUID blkio_guid = BLOCK_IO_PROTOCOL;
43
44typedef bool (*pd_test_cb_t)(pdinfo_t *, pdinfo_t *);
45static int efipart_initfd(void);
46static int efipart_initcd(void);
47static int efipart_inithd(void);
48static void efipart_cdinfo_add(pdinfo_t *);
49
50static int efipart_strategy(void *, int, daddr_t, size_t, char *, size_t *);
51static int efipart_realstrategy(void *, int, daddr_t, size_t, char *, size_t *);
52
53static int efipart_open(struct open_file *, ...);
54static int efipart_close(struct open_file *);
55static int efipart_ioctl(struct open_file *, u_long, void *);
56
57static int efipart_printfd(int);
58static int efipart_printcd(int);
59static int efipart_printhd(int);
60
61/* EISA PNP ID's for floppy controllers */
62#define	PNP0604	0x604
63#define	PNP0700	0x700
64#define	PNP0701	0x701
65
66/* Bounce buffer max size */
67#define	BIO_BUFFER_SIZE	0x4000
68
69struct devsw efipart_fddev = {
70	.dv_name = "fd",
71	.dv_type = DEVT_FD,
72	.dv_init = efipart_initfd,
73	.dv_strategy = efipart_strategy,
74	.dv_open = efipart_open,
75	.dv_close = efipart_close,
76	.dv_ioctl = efipart_ioctl,
77	.dv_print = efipart_printfd,
78	.dv_cleanup = nullsys,
79};
80
81struct devsw efipart_cddev = {
82	.dv_name = "cd",
83	.dv_type = DEVT_CD,
84	.dv_init = efipart_initcd,
85	.dv_strategy = efipart_strategy,
86	.dv_open = efipart_open,
87	.dv_close = efipart_close,
88	.dv_ioctl = efipart_ioctl,
89	.dv_print = efipart_printcd,
90	.dv_cleanup = nullsys,
91};
92
93struct devsw efipart_hddev = {
94	.dv_name = "disk",
95	.dv_type = DEVT_DISK,
96	.dv_init = efipart_inithd,
97	.dv_strategy = efipart_strategy,
98	.dv_open = efipart_open,
99	.dv_close = efipart_close,
100	.dv_ioctl = efipart_ioctl,
101	.dv_print = efipart_printhd,
102	.dv_cleanup = nullsys,
103	.dv_fmtdev = disk_fmtdev,
104	.dv_parsedev = disk_parsedev,
105};
106
107static pdinfo_list_t fdinfo = STAILQ_HEAD_INITIALIZER(fdinfo);
108static pdinfo_list_t cdinfo = STAILQ_HEAD_INITIALIZER(cdinfo);
109static pdinfo_list_t hdinfo = STAILQ_HEAD_INITIALIZER(hdinfo);
110
111/*
112 * efipart_inithandles() is used to build up the pdinfo list from
113 * block device handles. Then each devsw init callback is used to
114 * pick items from pdinfo and move to proper device list.
115 * In ideal world, we should end up with empty pdinfo once all
116 * devsw initializers are called.
117 */
118static pdinfo_list_t pdinfo = STAILQ_HEAD_INITIALIZER(pdinfo);
119
120pdinfo_list_t *
121efiblk_get_pdinfo_list(struct devsw *dev)
122{
123	if (dev->dv_type == DEVT_DISK)
124		return (&hdinfo);
125	if (dev->dv_type == DEVT_CD)
126		return (&cdinfo);
127	if (dev->dv_type == DEVT_FD)
128		return (&fdinfo);
129	return (NULL);
130}
131
132/* XXX this gets called way way too often, investigate */
133pdinfo_t *
134efiblk_get_pdinfo(struct devdesc *dev)
135{
136	pdinfo_list_t *pdi;
137	pdinfo_t *pd = NULL;
138
139	pdi = efiblk_get_pdinfo_list(dev->d_dev);
140	if (pdi == NULL)
141		return (pd);
142
143	STAILQ_FOREACH(pd, pdi, pd_link) {
144		if (pd->pd_unit == dev->d_unit)
145			return (pd);
146	}
147	return (pd);
148}
149
150pdinfo_t *
151efiblk_get_pdinfo_by_device_path(EFI_DEVICE_PATH *path)
152{
153	EFI_HANDLE h;
154	EFI_STATUS status;
155	EFI_DEVICE_PATH *devp = path;
156
157	status = BS->LocateDevicePath(&blkio_guid, &devp, &h);
158	if (EFI_ERROR(status))
159		return (NULL);
160	return (efiblk_get_pdinfo_by_handle(h));
161}
162
163static bool
164same_handle(pdinfo_t *pd, EFI_HANDLE h)
165{
166
167	return (pd->pd_handle == h || pd->pd_alias == h);
168}
169
170pdinfo_t *
171efiblk_get_pdinfo_by_handle(EFI_HANDLE h)
172{
173	pdinfo_t *dp, *pp;
174
175	/*
176	 * Check hard disks, then cd, then floppy
177	 */
178	STAILQ_FOREACH(dp, &hdinfo, pd_link) {
179		if (same_handle(dp, h))
180			return (dp);
181		STAILQ_FOREACH(pp, &dp->pd_part, pd_link) {
182			if (same_handle(pp, h))
183				return (pp);
184		}
185	}
186	STAILQ_FOREACH(dp, &cdinfo, pd_link) {
187		if (same_handle(dp, h))
188			return (dp);
189		STAILQ_FOREACH(pp, &dp->pd_part, pd_link) {
190			if (same_handle(pp, h))
191				return (pp);
192		}
193	}
194	STAILQ_FOREACH(dp, &fdinfo, pd_link) {
195		if (same_handle(dp, h))
196			return (dp);
197	}
198	return (NULL);
199}
200
201static int
202efiblk_pdinfo_count(pdinfo_list_t *pdi)
203{
204	pdinfo_t *pd;
205	int i = 0;
206
207	STAILQ_FOREACH(pd, pdi, pd_link) {
208		i++;
209	}
210	return (i);
211}
212
213static pdinfo_t *
214efipart_find_parent(pdinfo_list_t *pdi, EFI_DEVICE_PATH *devpath)
215{
216	pdinfo_t *pd;
217	EFI_DEVICE_PATH *parent;
218
219	/* We want to find direct parent */
220	parent = efi_devpath_trim(devpath);
221	/* We should not get out of memory here but be careful. */
222	if (parent == NULL)
223		return (NULL);
224
225	STAILQ_FOREACH(pd, pdi, pd_link) {
226		/* We must have exact match. */
227		if (efi_devpath_match(pd->pd_devpath, parent))
228			break;
229	}
230	free(parent);
231	return (pd);
232}
233
234/*
235 * Return true when we should ignore this device.
236 */
237static bool
238efipart_ignore_device(EFI_HANDLE h, EFI_BLOCK_IO *blkio,
239    EFI_DEVICE_PATH *devpath)
240{
241	EFI_DEVICE_PATH *node, *parent;
242
243	/*
244	 * We assume the block size 512 or greater power of 2.
245	 * Also skip devices with block size > 64k (16 is max
246	 * ashift supported by zfs).
247	 * iPXE is known to insert stub BLOCK IO device with
248	 * BlockSize 1.
249	 */
250	if (blkio->Media->BlockSize < 512 ||
251	    blkio->Media->BlockSize > (1 << 16) ||
252	    !powerof2(blkio->Media->BlockSize)) {
253		efi_close_devpath(h);
254		return (true);
255	}
256
257	/* Allowed values are 0, 1 and power of 2. */
258	if (blkio->Media->IoAlign > 1 &&
259	    !powerof2(blkio->Media->IoAlign)) {
260		efi_close_devpath(h);
261		return (true);
262	}
263
264	/*
265	 * With device tree setup:
266	 * PciRoot(0x0)/Pci(0x14,0x0)/USB(0x5,0)/USB(0x2,0x0)
267	 * PciRoot(0x0)/Pci(0x14,0x0)/USB(0x5,0)/USB(0x2,0x0)/Unit(0x1)
268	 * PciRoot(0x0)/Pci(0x14,0x0)/USB(0x5,0)/USB(0x2,0x0)/Unit(0x2)
269	 * PciRoot(0x0)/Pci(0x14,0x0)/USB(0x5,0)/USB(0x2,0x0)/Unit(0x3)
270	 * PciRoot(0x0)/Pci(0x14,0x0)/USB(0x5,0)/USB(0x2,0x0)/Unit(0x3)/CDROM..
271	 * PciRoot(0x0)/Pci(0x14,0x0)/USB(0x5,0)/USB(0x2,0x0)/Unit(0x3)/CDROM..
272	 * PciRoot(0x0)/Pci(0x14,0x0)/USB(0x5,0)/USB(0x2,0x0)/Unit(0x4)
273	 * PciRoot(0x0)/Pci(0x14,0x0)/USB(0x5,0)/USB(0x2,0x0)/Unit(0x5)
274	 * PciRoot(0x0)/Pci(0x14,0x0)/USB(0x5,0)/USB(0x2,0x0)/Unit(0x6)
275	 * PciRoot(0x0)/Pci(0x14,0x0)/USB(0x5,0)/USB(0x2,0x0)/Unit(0x7)
276	 *
277	 * In above exmple only Unit(0x3) has media, all other nodes are
278	 * missing media and should not be used.
279	 *
280	 * No media does not always mean there is no device, but in above
281	 * case, we can not really assume there is any device.
282	 * Therefore, if this node is USB, or this node is Unit (LUN) and
283	 * direct parent is USB and we have no media, we will ignore this
284	 * device.
285	 *
286	 * Variation of the same situation, but with SCSI devices:
287	 * PciRoot(0x0)/Pci(0x1a,0x0)/USB(0x1,0)/USB(0x3,0x0)/SCSI(0x0,0x1)
288	 * PciRoot(0x0)/Pci(0x1a,0x0)/USB(0x1,0)/USB(0x3,0x0)/SCSI(0x0,0x2)
289	 * PciRoot(0x0)/Pci(0x1a,0x0)/USB(0x1,0)/USB(0x3,0x0)/SCSI(0x0,0x3)
290	 * PciRoot(0x0)/Pci(0x1a,0x0)/USB(0x1,0)/USB(0x3,0x0)/SCSI(0x0,0x3)/CD..
291	 * PciRoot(0x0)/Pci(0x1a,0x0)/USB(0x1,0)/USB(0x3,0x0)/SCSI(0x0,0x3)/CD..
292	 * PciRoot(0x0)/Pci(0x1a,0x0)/USB(0x1,0)/USB(0x3,0x0)/SCSI(0x0,0x4)
293	 *
294	 * Here above the SCSI luns 1,2 and 4 have no media.
295	 */
296
297	/* Do not ignore device with media. */
298	if (blkio->Media->MediaPresent)
299		return (false);
300
301	node = efi_devpath_last_node(devpath);
302	if (node == NULL)
303		return (false);
304
305	/* USB without media present */
306	if (DevicePathType(node) == MESSAGING_DEVICE_PATH &&
307	    DevicePathSubType(node) == MSG_USB_DP) {
308		efi_close_devpath(h);
309		return (true);
310	}
311
312	parent = efi_devpath_trim(devpath);
313	if (parent != NULL) {
314		bool parent_is_usb = false;
315
316		node = efi_devpath_last_node(parent);
317		if (node == NULL) {
318			free(parent);
319			return (false);
320		}
321		if (DevicePathType(node) == MESSAGING_DEVICE_PATH &&
322		    DevicePathSubType(node) == MSG_USB_DP)
323			parent_is_usb = true;
324		free(parent);
325
326		node = efi_devpath_last_node(devpath);
327		if (node == NULL)
328			return (false);
329		if (parent_is_usb &&
330		    DevicePathType(node) == MESSAGING_DEVICE_PATH) {
331			/*
332			 * no media, parent is USB and devicepath is
333			 * LUN or SCSI.
334			 */
335			if (DevicePathSubType(node) ==
336			    MSG_DEVICE_LOGICAL_UNIT_DP ||
337			    DevicePathSubType(node) == MSG_SCSI_DP) {
338				efi_close_devpath(h);
339				return (true);
340			}
341		}
342	}
343	return (false);
344}
345
346int
347efipart_inithandles(void)
348{
349	unsigned i, nin;
350	UINTN sz;
351	EFI_HANDLE *hin;
352	EFI_DEVICE_PATH *devpath;
353	EFI_BLOCK_IO *blkio;
354	EFI_STATUS status;
355	pdinfo_t *pd;
356
357	if (!STAILQ_EMPTY(&pdinfo))
358		return (0);
359
360	sz = 0;
361	hin = NULL;
362	status = BS->LocateHandle(ByProtocol, &blkio_guid, 0, &sz, hin);
363	if (status == EFI_BUFFER_TOO_SMALL) {
364		hin = malloc(sz);
365		if (hin == NULL)
366			return (ENOMEM);
367		status = BS->LocateHandle(ByProtocol, &blkio_guid, 0, &sz,
368		    hin);
369		if (EFI_ERROR(status))
370			free(hin);
371	}
372	if (EFI_ERROR(status))
373		return (efi_status_to_errno(status));
374
375	nin = sz / sizeof(*hin);
376#ifdef EFIPART_DEBUG
377	printf("%s: Got %d BLOCK IO MEDIA handle(s)\n", __func__, nin);
378#endif
379
380	for (i = 0; i < nin; i++) {
381		/*
382		 * Get devpath and open protocol.
383		 * We should not get errors here
384		 */
385		if ((devpath = efi_lookup_devpath(hin[i])) == NULL)
386			continue;
387
388		status = OpenProtocolByHandle(hin[i], &blkio_guid,
389		    (void **)&blkio);
390		if (EFI_ERROR(status)) {
391			printf("error %lu\n", EFI_ERROR_CODE(status));
392			continue;
393		}
394
395		if (efipart_ignore_device(hin[i], blkio, devpath))
396			continue;
397
398		/* This is bad. */
399		if ((pd = calloc(1, sizeof(*pd))) == NULL) {
400			printf("efipart_inithandles: Out of memory.\n");
401			free(hin);
402			return (ENOMEM);
403		}
404		STAILQ_INIT(&pd->pd_part);
405
406		pd->pd_handle = hin[i];
407		pd->pd_devpath = devpath;
408		pd->pd_blkio = blkio;
409		STAILQ_INSERT_TAIL(&pdinfo, pd, pd_link);
410	}
411
412	/*
413	 * Walk pdinfo and set parents based on device path.
414	 */
415	STAILQ_FOREACH(pd, &pdinfo, pd_link) {
416		pd->pd_parent = efipart_find_parent(&pdinfo, pd->pd_devpath);
417	}
418	free(hin);
419	return (0);
420}
421
422/*
423 * Get node identified by pd_test() from plist.
424 */
425static pdinfo_t *
426efipart_get_pd(pdinfo_list_t *plist, pd_test_cb_t pd_test, pdinfo_t *data)
427{
428	pdinfo_t *pd;
429
430	STAILQ_FOREACH(pd, plist, pd_link) {
431		if (pd_test(pd, data))
432			break;
433	}
434
435	return (pd);
436}
437
438static ACPI_HID_DEVICE_PATH *
439efipart_floppy(EFI_DEVICE_PATH *node)
440{
441	ACPI_HID_DEVICE_PATH *acpi;
442
443	if (DevicePathType(node) == ACPI_DEVICE_PATH &&
444	    DevicePathSubType(node) == ACPI_DP) {
445		acpi = (ACPI_HID_DEVICE_PATH *) node;
446		if (acpi->HID == EISA_PNP_ID(PNP0604) ||
447		    acpi->HID == EISA_PNP_ID(PNP0700) ||
448		    acpi->HID == EISA_PNP_ID(PNP0701)) {
449			return (acpi);
450		}
451	}
452	return (NULL);
453}
454
455static bool
456efipart_testfd(pdinfo_t *fd, pdinfo_t *data __unused)
457{
458	EFI_DEVICE_PATH *node;
459
460	node = efi_devpath_last_node(fd->pd_devpath);
461	if (node == NULL)
462		return (false);
463
464	if (efipart_floppy(node) != NULL)
465		return (true);
466
467	return (false);
468}
469
470static int
471efipart_initfd(void)
472{
473	EFI_DEVICE_PATH *node;
474	ACPI_HID_DEVICE_PATH *acpi;
475	pdinfo_t *parent, *fd;
476
477	while ((fd = efipart_get_pd(&pdinfo, efipart_testfd, NULL)) != NULL) {
478		if ((node = efi_devpath_last_node(fd->pd_devpath)) == NULL)
479			continue;
480
481		if ((acpi = efipart_floppy(node)) == NULL)
482			continue;
483
484		STAILQ_REMOVE(&pdinfo, fd, pdinfo, pd_link);
485		parent = fd->pd_parent;
486		if (parent != NULL) {
487			STAILQ_REMOVE(&pdinfo, parent, pdinfo, pd_link);
488			parent->pd_alias = fd->pd_handle;
489			parent->pd_unit = acpi->UID;
490			free(fd);
491			fd = parent;
492		} else {
493			fd->pd_unit = acpi->UID;
494		}
495		fd->pd_devsw = &efipart_fddev;
496		STAILQ_INSERT_TAIL(&fdinfo, fd, pd_link);
497	}
498
499	bcache_add_dev(efiblk_pdinfo_count(&fdinfo));
500	return (0);
501}
502
503/*
504 * Add or update entries with new handle data.
505 */
506static void
507efipart_cdinfo_add(pdinfo_t *cd)
508{
509	pdinfo_t *parent, *pd, *last;
510
511	if (cd == NULL)
512		return;
513
514	parent = cd->pd_parent;
515	/* Make sure we have parent added */
516	efipart_cdinfo_add(parent);
517
518	STAILQ_FOREACH(pd, &pdinfo, pd_link) {
519		if (efi_devpath_match(pd->pd_devpath, cd->pd_devpath)) {
520			STAILQ_REMOVE(&pdinfo, cd, pdinfo, pd_link);
521			break;
522		}
523	}
524	if (pd == NULL) {
525		/* This device is already added. */
526		return;
527	}
528
529	if (parent != NULL) {
530		last = STAILQ_LAST(&parent->pd_part, pdinfo, pd_link);
531		if (last != NULL)
532			cd->pd_unit = last->pd_unit + 1;
533		else
534			cd->pd_unit = 0;
535		cd->pd_devsw = &efipart_cddev;
536		STAILQ_INSERT_TAIL(&parent->pd_part, cd, pd_link);
537		return;
538	}
539
540	last = STAILQ_LAST(&cdinfo, pdinfo, pd_link);
541	if (last != NULL)
542		cd->pd_unit = last->pd_unit + 1;
543	else
544		cd->pd_unit = 0;
545
546	cd->pd_devsw = &efipart_cddev;
547	STAILQ_INSERT_TAIL(&cdinfo, cd, pd_link);
548}
549
550static bool
551efipart_testcd(pdinfo_t *cd, pdinfo_t *data __unused)
552{
553	EFI_DEVICE_PATH *node;
554
555	node = efi_devpath_last_node(cd->pd_devpath);
556	if (node == NULL)
557		return (false);
558
559	if (efipart_floppy(node) != NULL)
560		return (false);
561
562	if (DevicePathType(node) == MEDIA_DEVICE_PATH &&
563	    DevicePathSubType(node) == MEDIA_CDROM_DP) {
564		return (true);
565	}
566
567	/* cd drive without the media. */
568	if (cd->pd_blkio->Media->RemovableMedia &&
569	    !cd->pd_blkio->Media->MediaPresent) {
570		return (true);
571	}
572
573	return (false);
574}
575
576/*
577 * Test if pd is parent for device.
578 */
579static bool
580efipart_testchild(pdinfo_t *dev, pdinfo_t *pd)
581{
582	/* device with no parent. */
583	if (dev->pd_parent == NULL)
584		return (false);
585
586	if (efi_devpath_match(dev->pd_parent->pd_devpath, pd->pd_devpath)) {
587		return (true);
588	}
589	return (false);
590}
591
592static int
593efipart_initcd(void)
594{
595	pdinfo_t *cd;
596
597	while ((cd = efipart_get_pd(&pdinfo, efipart_testcd, NULL)) != NULL)
598		efipart_cdinfo_add(cd);
599
600	/* Find all children of CD devices we did add above. */
601	STAILQ_FOREACH(cd, &cdinfo, pd_link) {
602		pdinfo_t *child;
603
604		for (child = efipart_get_pd(&pdinfo, efipart_testchild, cd);
605		    child != NULL;
606		    child = efipart_get_pd(&pdinfo, efipart_testchild, cd))
607			efipart_cdinfo_add(child);
608	}
609	bcache_add_dev(efiblk_pdinfo_count(&cdinfo));
610	return (0);
611}
612
613static void
614efipart_hdinfo_add_node(pdinfo_t *hd, EFI_DEVICE_PATH *node)
615{
616	pdinfo_t *parent, *ptr;
617
618	if (node == NULL)
619		return;
620
621	parent = hd->pd_parent;
622	/*
623	 * If the node is not MEDIA_HARDDRIVE_DP, it is sub-partition.
624	 * This can happen with Vendor nodes, and since we do not know
625	 * the more about those nodes, we just count them.
626	 */
627	if (DevicePathSubType(node) != MEDIA_HARDDRIVE_DP) {
628		ptr = STAILQ_LAST(&parent->pd_part, pdinfo, pd_link);
629		if (ptr != NULL)
630			hd->pd_unit = ptr->pd_unit + 1;
631		else
632			hd->pd_unit = 0;
633	} else {
634		hd->pd_unit = ((HARDDRIVE_DEVICE_PATH *)node)->PartitionNumber;
635	}
636
637	hd->pd_devsw = &efipart_hddev;
638	STAILQ_INSERT_TAIL(&parent->pd_part, hd, pd_link);
639}
640
641/*
642 * The MEDIA_FILEPATH_DP has device name.
643 * From U-Boot sources it looks like names are in the form
644 * of typeN:M, where type is interface type, N is disk id
645 * and M is partition id.
646 */
647static void
648efipart_hdinfo_add_filepath(pdinfo_t *hd, FILEPATH_DEVICE_PATH *node)
649{
650	char *pathname, *p;
651	int len;
652	pdinfo_t *last;
653
654	last = STAILQ_LAST(&hdinfo, pdinfo, pd_link);
655	if (last != NULL)
656		hd->pd_unit = last->pd_unit + 1;
657	else
658		hd->pd_unit = 0;
659
660	/* FILEPATH_DEVICE_PATH has 0 terminated string */
661	len = ucs2len(node->PathName);
662	if ((pathname = malloc(len + 1)) == NULL) {
663		printf("Failed to add disk, out of memory\n");
664		free(hd);
665		return;
666	}
667	cpy16to8(node->PathName, pathname, len + 1);
668	p = strchr(pathname, ':');
669
670	/*
671	 * Assume we are receiving handles in order, first disk handle,
672	 * then partitions for this disk. If this assumption proves
673	 * false, this code would need update.
674	 */
675	if (p == NULL) {	/* no colon, add the disk */
676		hd->pd_devsw = &efipart_hddev;
677		STAILQ_INSERT_TAIL(&hdinfo, hd, pd_link);
678		free(pathname);
679		return;
680	}
681	p++;	/* skip the colon */
682	errno = 0;
683	hd->pd_unit = (int)strtol(p, NULL, 0);
684	if (errno != 0) {
685		printf("Bad unit number for partition \"%s\"\n", pathname);
686		free(pathname);
687		free(hd);
688		return;
689	}
690
691	/*
692	 * We should have disk registered, if not, we are receiving
693	 * handles out of order, and this code should be reworked
694	 * to create "blank" disk for partition, and to find the
695	 * disk based on PathName compares.
696	 */
697	if (last == NULL) {
698		printf("BUG: No disk for partition \"%s\"\n", pathname);
699		free(pathname);
700		free(hd);
701		return;
702	}
703	/* Add the partition. */
704	hd->pd_parent = last;
705	hd->pd_devsw = &efipart_hddev;
706	STAILQ_INSERT_TAIL(&last->pd_part, hd, pd_link);
707	free(pathname);
708}
709
710static void
711efipart_hdinfo_add(pdinfo_t *hd)
712{
713	pdinfo_t *parent, *pd, *last;
714	EFI_DEVICE_PATH *node;
715
716	if (hd == NULL)
717		return;
718
719	parent = hd->pd_parent;
720	/* Make sure we have parent added */
721	efipart_hdinfo_add(parent);
722
723	STAILQ_FOREACH(pd, &pdinfo, pd_link) {
724		if (efi_devpath_match(pd->pd_devpath, hd->pd_devpath)) {
725			STAILQ_REMOVE(&pdinfo, hd, pdinfo, pd_link);
726			break;
727		}
728	}
729	if (pd == NULL) {
730		/* This device is already added. */
731		return;
732	}
733
734	if ((node = efi_devpath_last_node(hd->pd_devpath)) == NULL)
735		return;
736
737	if (DevicePathType(node) == MEDIA_DEVICE_PATH &&
738	    DevicePathSubType(node) == MEDIA_FILEPATH_DP) {
739		efipart_hdinfo_add_filepath(hd,
740		    (FILEPATH_DEVICE_PATH *)node);
741		return;
742	}
743
744	if (parent != NULL) {
745		efipart_hdinfo_add_node(hd, node);
746		return;
747	}
748
749	last = STAILQ_LAST(&hdinfo, pdinfo, pd_link);
750	if (last != NULL)
751		hd->pd_unit = last->pd_unit + 1;
752	else
753		hd->pd_unit = 0;
754
755	/* Add the disk. */
756	hd->pd_devsw = &efipart_hddev;
757	STAILQ_INSERT_TAIL(&hdinfo, hd, pd_link);
758}
759
760static bool
761efipart_testhd(pdinfo_t *hd, pdinfo_t *data __unused)
762{
763	if (efipart_testfd(hd, NULL))
764		return (false);
765
766	if (efipart_testcd(hd, NULL))
767		return (false);
768
769	/* Anything else must be HD. */
770	return (true);
771}
772
773static int
774efipart_inithd(void)
775{
776	pdinfo_t *hd;
777
778	while ((hd = efipart_get_pd(&pdinfo, efipart_testhd, NULL)) != NULL)
779		efipart_hdinfo_add(hd);
780
781	bcache_add_dev(efiblk_pdinfo_count(&hdinfo));
782	return (0);
783}
784
785static int
786efipart_print_common(struct devsw *dev, pdinfo_list_t *pdlist, int verbose)
787{
788	int ret = 0;
789	EFI_BLOCK_IO *blkio;
790	EFI_STATUS status;
791	EFI_HANDLE h;
792	pdinfo_t *pd;
793	CHAR16 *text;
794	struct disk_devdesc pd_dev;
795	char line[80];
796
797	if (STAILQ_EMPTY(pdlist))
798		return (0);
799
800	printf("%s devices:", dev->dv_name);
801	if ((ret = pager_output("\n")) != 0)
802		return (ret);
803
804	STAILQ_FOREACH(pd, pdlist, pd_link) {
805		h = pd->pd_handle;
806		if (verbose) {	/* Output the device path. */
807			text = efi_devpath_name(efi_lookup_devpath(h));
808			if (text != NULL) {
809				printf("  %S", text);
810				efi_free_devpath_name(text);
811				if ((ret = pager_output("\n")) != 0)
812					break;
813			}
814		}
815		snprintf(line, sizeof(line),
816		    "    %s%d", dev->dv_name, pd->pd_unit);
817		printf("%s:", line);
818		status = OpenProtocolByHandle(h, &blkio_guid, (void **)&blkio);
819		if (!EFI_ERROR(status)) {
820			printf("    %llu",
821			    blkio->Media->LastBlock == 0? 0:
822			    (unsigned long long) (blkio->Media->LastBlock + 1));
823			if (blkio->Media->LastBlock != 0) {
824				printf(" X %u", blkio->Media->BlockSize);
825			}
826			printf(" blocks");
827			if (blkio->Media->MediaPresent) {
828				if (blkio->Media->RemovableMedia)
829					printf(" (removable)");
830			} else {
831				printf(" (no media)");
832			}
833			if ((ret = pager_output("\n")) != 0)
834				break;
835			if (!blkio->Media->MediaPresent)
836				continue;
837
838			pd->pd_blkio = blkio;
839			pd_dev.dd.d_dev = dev;
840			pd_dev.dd.d_unit = pd->pd_unit;
841			pd_dev.d_slice = D_SLICENONE;
842			pd_dev.d_partition = D_PARTNONE;
843			ret = disk_open(&pd_dev, blkio->Media->BlockSize *
844			    (blkio->Media->LastBlock + 1),
845			    blkio->Media->BlockSize);
846			if (ret == 0) {
847				ret = disk_print(&pd_dev, line, verbose);
848				disk_close(&pd_dev);
849				if (ret != 0)
850					return (ret);
851			} else {
852				/* Do not fail from disk_open() */
853				ret = 0;
854			}
855		} else {
856			if ((ret = pager_output("\n")) != 0)
857				break;
858		}
859	}
860	return (ret);
861}
862
863static int
864efipart_printfd(int verbose)
865{
866	return (efipart_print_common(&efipart_fddev, &fdinfo, verbose));
867}
868
869static int
870efipart_printcd(int verbose)
871{
872	return (efipart_print_common(&efipart_cddev, &cdinfo, verbose));
873}
874
875static int
876efipart_printhd(int verbose)
877{
878	return (efipart_print_common(&efipart_hddev, &hdinfo, verbose));
879}
880
881static int
882efipart_open(struct open_file *f, ...)
883{
884	va_list args;
885	struct disk_devdesc *dev;
886	pdinfo_t *pd;
887	EFI_BLOCK_IO *blkio;
888	EFI_STATUS status;
889
890	va_start(args, f);
891	dev = va_arg(args, struct disk_devdesc *);
892	va_end(args);
893	if (dev == NULL)
894		return (EINVAL);
895
896	pd = efiblk_get_pdinfo((struct devdesc *)dev);
897	if (pd == NULL)
898		return (EIO);
899
900	if (pd->pd_blkio == NULL) {
901		status = OpenProtocolByHandle(pd->pd_handle, &blkio_guid,
902		    (void **)&pd->pd_blkio);
903		if (EFI_ERROR(status))
904			return (efi_status_to_errno(status));
905	}
906
907	blkio = pd->pd_blkio;
908	if (!blkio->Media->MediaPresent)
909		return (EAGAIN);
910
911	pd->pd_open++;
912	if (pd->pd_bcache == NULL)
913		pd->pd_bcache = bcache_allocate();
914
915	if (dev->dd.d_dev->dv_type == DEVT_DISK) {
916		int rc;
917
918		rc = disk_open(dev,
919		    blkio->Media->BlockSize * (blkio->Media->LastBlock + 1),
920		    blkio->Media->BlockSize);
921		if (rc != 0) {
922			pd->pd_open--;
923			if (pd->pd_open == 0) {
924				pd->pd_blkio = NULL;
925				bcache_free(pd->pd_bcache);
926				pd->pd_bcache = NULL;
927			}
928		}
929		return (rc);
930	}
931	return (0);
932}
933
934static int
935efipart_close(struct open_file *f)
936{
937	struct disk_devdesc *dev;
938	pdinfo_t *pd;
939
940	dev = (struct disk_devdesc *)(f->f_devdata);
941	if (dev == NULL)
942		return (EINVAL);
943
944	pd = efiblk_get_pdinfo((struct devdesc *)dev);
945	if (pd == NULL)
946		return (EINVAL);
947
948	pd->pd_open--;
949	if (pd->pd_open == 0) {
950		pd->pd_blkio = NULL;
951		if (dev->dd.d_dev->dv_type != DEVT_DISK) {
952			bcache_free(pd->pd_bcache);
953			pd->pd_bcache = NULL;
954		}
955	}
956	if (dev->dd.d_dev->dv_type == DEVT_DISK)
957		return (disk_close(dev));
958	return (0);
959}
960
961static int
962efipart_ioctl(struct open_file *f, u_long cmd, void *data)
963{
964	struct disk_devdesc *dev;
965	pdinfo_t *pd;
966	int rc;
967
968	dev = (struct disk_devdesc *)(f->f_devdata);
969	if (dev == NULL)
970		return (EINVAL);
971
972	pd = efiblk_get_pdinfo((struct devdesc *)dev);
973	if (pd == NULL)
974		return (EINVAL);
975
976	if (dev->dd.d_dev->dv_type == DEVT_DISK) {
977		rc = disk_ioctl(dev, cmd, data);
978		if (rc != ENOTTY)
979			return (rc);
980	}
981
982	switch (cmd) {
983	case DIOCGSECTORSIZE:
984		*(u_int *)data = pd->pd_blkio->Media->BlockSize;
985		break;
986	case DIOCGMEDIASIZE:
987		*(uint64_t *)data = pd->pd_blkio->Media->BlockSize *
988		    (pd->pd_blkio->Media->LastBlock + 1);
989		break;
990	default:
991		return (ENOTTY);
992	}
993
994	return (0);
995}
996
997/*
998 * efipart_readwrite()
999 * Internal equivalent of efipart_strategy(), which operates on the
1000 * media-native block size. This function expects all I/O requests
1001 * to be within the media size and returns an error if such is not
1002 * the case.
1003 */
1004static int
1005efipart_readwrite(EFI_BLOCK_IO *blkio, int rw, daddr_t blk, daddr_t nblks,
1006    char *buf)
1007{
1008	EFI_STATUS status;
1009
1010	TSENTER();
1011
1012	if (blkio == NULL)
1013		return (ENXIO);
1014	if (blk < 0 || blk > blkio->Media->LastBlock)
1015		return (EIO);
1016	if ((blk + nblks - 1) > blkio->Media->LastBlock)
1017		return (EIO);
1018
1019	switch (rw & F_MASK) {
1020	case F_READ:
1021		status = blkio->ReadBlocks(blkio, blkio->Media->MediaId, blk,
1022		    nblks * blkio->Media->BlockSize, buf);
1023		break;
1024	case F_WRITE:
1025		if (blkio->Media->ReadOnly)
1026			return (EROFS);
1027		status = blkio->WriteBlocks(blkio, blkio->Media->MediaId, blk,
1028		    nblks * blkio->Media->BlockSize, buf);
1029		break;
1030	default:
1031		return (ENOSYS);
1032	}
1033
1034	if (EFI_ERROR(status)) {
1035		printf("%s: rw=%d, blk=%ju size=%ju status=%lu\n", __func__, rw,
1036		    blk, nblks, EFI_ERROR_CODE(status));
1037	}
1038	TSEXIT();
1039	return (efi_status_to_errno(status));
1040}
1041
1042static int
1043efipart_strategy(void *devdata, int rw, daddr_t blk, size_t size,
1044    char *buf, size_t *rsize)
1045{
1046	struct bcache_devdata bcd;
1047	struct disk_devdesc *dev;
1048	pdinfo_t *pd;
1049
1050	dev = (struct disk_devdesc *)devdata;
1051	if (dev == NULL)
1052		return (EINVAL);
1053
1054	pd = efiblk_get_pdinfo((struct devdesc *)dev);
1055	if (pd == NULL)
1056		return (EINVAL);
1057
1058	if (pd->pd_blkio->Media->RemovableMedia &&
1059	    !pd->pd_blkio->Media->MediaPresent)
1060		return (ENXIO);
1061
1062	bcd.dv_strategy = efipart_realstrategy;
1063	bcd.dv_devdata = devdata;
1064	bcd.dv_cache = pd->pd_bcache;
1065
1066	if (dev->dd.d_dev->dv_type == DEVT_DISK) {
1067		daddr_t offset;
1068
1069		offset = dev->d_offset * pd->pd_blkio->Media->BlockSize;
1070		offset /= 512;
1071		return (bcache_strategy(&bcd, rw, blk + offset,
1072		    size, buf, rsize));
1073	}
1074	return (bcache_strategy(&bcd, rw, blk, size, buf, rsize));
1075}
1076
1077static int
1078efipart_realstrategy(void *devdata, int rw, daddr_t blk, size_t size,
1079    char *buf, size_t *rsize)
1080{
1081	struct disk_devdesc *dev = (struct disk_devdesc *)devdata;
1082	pdinfo_t *pd;
1083	EFI_BLOCK_IO *blkio;
1084	uint64_t off, disk_blocks, d_offset = 0;
1085	char *blkbuf;
1086	size_t blkoff, blksz, bio_size;
1087	unsigned ioalign;
1088	bool need_buf;
1089	int rc;
1090	uint64_t diskend, readstart;
1091
1092	if (dev == NULL || blk < 0)
1093		return (EINVAL);
1094
1095	pd = efiblk_get_pdinfo((struct devdesc *)dev);
1096	if (pd == NULL)
1097		return (EINVAL);
1098
1099	blkio = pd->pd_blkio;
1100	if (blkio == NULL)
1101		return (ENXIO);
1102
1103	if (size == 0 || (size % 512) != 0)
1104		return (EIO);
1105
1106	off = blk * 512;
1107	/*
1108	 * Get disk blocks, this value is either for whole disk or for
1109	 * partition.
1110	 */
1111	disk_blocks = 0;
1112	if (dev->dd.d_dev->dv_type == DEVT_DISK) {
1113		if (disk_ioctl(dev, DIOCGMEDIASIZE, &disk_blocks) == 0) {
1114			/* DIOCGMEDIASIZE does return bytes. */
1115			disk_blocks /= blkio->Media->BlockSize;
1116		}
1117		d_offset = dev->d_offset;
1118	}
1119	if (disk_blocks == 0)
1120		disk_blocks = blkio->Media->LastBlock + 1 - d_offset;
1121
1122	/* make sure we don't read past disk end */
1123	if ((off + size) / blkio->Media->BlockSize > d_offset + disk_blocks) {
1124		diskend = d_offset + disk_blocks;
1125		readstart = off / blkio->Media->BlockSize;
1126
1127		if (diskend <= readstart) {
1128			if (rsize != NULL)
1129				*rsize = 0;
1130
1131			return (EIO);
1132		}
1133		size = diskend - readstart;
1134		size = size * blkio->Media->BlockSize;
1135	}
1136
1137	need_buf = true;
1138	/* Do we need bounce buffer? */
1139	if ((size % blkio->Media->BlockSize == 0) &&
1140	    (off % blkio->Media->BlockSize == 0))
1141		need_buf = false;
1142
1143	/* Do we have IO alignment requirement? */
1144	ioalign = blkio->Media->IoAlign;
1145	if (ioalign == 0)
1146		ioalign++;
1147
1148	if (ioalign > 1 && (uintptr_t)buf != roundup2((uintptr_t)buf, ioalign))
1149		need_buf = true;
1150
1151	if (need_buf) {
1152		for (bio_size = BIO_BUFFER_SIZE; bio_size > 0;
1153		    bio_size -= blkio->Media->BlockSize) {
1154			blkbuf = memalign(ioalign, bio_size);
1155			if (blkbuf != NULL)
1156				break;
1157		}
1158	} else {
1159		blkbuf = buf;
1160		bio_size = size;
1161	}
1162
1163	if (blkbuf == NULL)
1164		return (ENOMEM);
1165
1166	if (rsize != NULL)
1167		*rsize = size;
1168
1169	rc = 0;
1170	blk = off / blkio->Media->BlockSize;
1171	blkoff = off % blkio->Media->BlockSize;
1172
1173	while (size > 0) {
1174		size_t x = min(size, bio_size);
1175
1176		if (x < blkio->Media->BlockSize)
1177			x = 1;
1178		else
1179			x /= blkio->Media->BlockSize;
1180
1181		switch (rw & F_MASK) {
1182		case F_READ:
1183			blksz = blkio->Media->BlockSize * x - blkoff;
1184			if (size < blksz)
1185				blksz = size;
1186
1187			rc = efipart_readwrite(blkio, rw, blk, x, blkbuf);
1188			if (rc != 0)
1189				goto error;
1190
1191			if (need_buf)
1192				bcopy(blkbuf + blkoff, buf, blksz);
1193			break;
1194		case F_WRITE:
1195			rc = 0;
1196			if (blkoff != 0) {
1197				/*
1198				 * We got offset to sector, read 1 sector to
1199				 * blkbuf.
1200				 */
1201				x = 1;
1202				blksz = blkio->Media->BlockSize - blkoff;
1203				blksz = min(blksz, size);
1204				rc = efipart_readwrite(blkio, F_READ, blk, x,
1205				    blkbuf);
1206			} else if (size < blkio->Media->BlockSize) {
1207				/*
1208				 * The remaining block is not full
1209				 * sector. Read 1 sector to blkbuf.
1210				 */
1211				x = 1;
1212				blksz = size;
1213				rc = efipart_readwrite(blkio, F_READ, blk, x,
1214				    blkbuf);
1215			} else {
1216				/* We can write full sector(s). */
1217				blksz = blkio->Media->BlockSize * x;
1218			}
1219
1220			if (rc != 0)
1221				goto error;
1222			/*
1223			 * Put your Data In, Put your Data out,
1224			 * Put your Data In, and shake it all about
1225			 */
1226			if (need_buf)
1227				bcopy(buf, blkbuf + blkoff, blksz);
1228			rc = efipart_readwrite(blkio, F_WRITE, blk, x, blkbuf);
1229			if (rc != 0)
1230				goto error;
1231			break;
1232		default:
1233			/* DO NOTHING */
1234			rc = EROFS;
1235			goto error;
1236		}
1237
1238		blkoff = 0;
1239		buf += blksz;
1240		size -= blksz;
1241		blk += x;
1242	}
1243
1244error:
1245	if (rsize != NULL)
1246		*rsize -= size;
1247
1248	if (need_buf)
1249		free(blkbuf);
1250	return (rc);
1251}
1252