1e3026534SToomas Soome /*
2199767f8SToomas Soome  * Copyright (c) 2010 Marcel Moolenaar
3199767f8SToomas Soome  * All rights reserved.
4199767f8SToomas Soome  *
5199767f8SToomas Soome  * Redistribution and use in source and binary forms, with or without
6199767f8SToomas Soome  * modification, are permitted provided that the following conditions
7199767f8SToomas Soome  * are met:
8199767f8SToomas Soome  * 1. Redistributions of source code must retain the above copyright
9199767f8SToomas Soome  *    notice, this list of conditions and the following disclaimer.
10199767f8SToomas Soome  * 2. Redistributions in binary form must reproduce the above copyright
11199767f8SToomas Soome  *    notice, this list of conditions and the following disclaimer in the
12199767f8SToomas Soome  *    documentation and/or other materials provided with the distribution.
13199767f8SToomas Soome  *
14199767f8SToomas Soome  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15199767f8SToomas Soome  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16199767f8SToomas Soome  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17199767f8SToomas Soome  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18199767f8SToomas Soome  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19199767f8SToomas Soome  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20199767f8SToomas Soome  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21199767f8SToomas Soome  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22199767f8SToomas Soome  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23199767f8SToomas Soome  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24199767f8SToomas Soome  * SUCH DAMAGE.
25199767f8SToomas Soome  */
26199767f8SToomas Soome 
27199767f8SToomas Soome #include <sys/cdefs.h>
28199767f8SToomas Soome 
29edb35047SToomas Soome #include <sys/disk.h>
30199767f8SToomas Soome #include <sys/param.h>
31199767f8SToomas Soome #include <sys/time.h>
32dbacaf56SToomas Soome #include <sys/queue.h>
33199767f8SToomas Soome #include <stddef.h>
34199767f8SToomas Soome #include <stdarg.h>
35199767f8SToomas Soome 
36199767f8SToomas Soome #include <bootstrap.h>
37199767f8SToomas Soome 
38199767f8SToomas Soome #include <efi.h>
39199767f8SToomas Soome #include <efilib.h>
40199767f8SToomas Soome #include <efiprot.h>
411f54f0bbSToomas Soome #include <efichar.h>
42dbacaf56SToomas Soome #include <disk.h>
43199767f8SToomas Soome 
44199767f8SToomas Soome static EFI_GUID blkio_guid = BLOCK_IO_PROTOCOL;
45199767f8SToomas Soome 
46c54162e4SToomas Soome typedef bool (*pd_test_cb_t)(pdinfo_t *, pdinfo_t *);
47dbacaf56SToomas Soome static int efipart_initfd(void);
48dbacaf56SToomas Soome static int efipart_initcd(void);
49dbacaf56SToomas Soome static int efipart_inithd(void);
50c54162e4SToomas Soome static void efipart_cdinfo_add(pdinfo_t *);
51dbacaf56SToomas Soome 
5238dea910SToomas Soome static int efipart_strategy(void *, int, daddr_t, size_t, char *, size_t *);
5338dea910SToomas Soome static int efipart_realstrategy(void *, int, daddr_t, size_t, char *, size_t *);
54dbacaf56SToomas Soome 
55199767f8SToomas Soome static int efipart_open(struct open_file *, ...);
56199767f8SToomas Soome static int efipart_close(struct open_file *);
570f2f3e99SToomas Soome static int efipart_ioctl(struct open_file *, unsigned long, void *);
58199767f8SToomas Soome 
59dbacaf56SToomas Soome static int efipart_printfd(int);
60dbacaf56SToomas Soome static int efipart_printcd(int);
61dbacaf56SToomas Soome static int efipart_printhd(int);
62dbacaf56SToomas Soome 
63dbacaf56SToomas Soome /* EISA PNP ID's for floppy controllers */
64dbacaf56SToomas Soome #define	PNP0604	0x604
65dbacaf56SToomas Soome #define	PNP0700	0x700
66dbacaf56SToomas Soome #define	PNP0701	0x701
67dbacaf56SToomas Soome 
682b292e00SToomas Soome /* Bounce buffer max size */
692b292e00SToomas Soome #define	BIO_BUFFER_SIZE	0x4000
702b292e00SToomas Soome 
71dbacaf56SToomas Soome struct devsw efipart_fddev = {
72dbacaf56SToomas Soome 	.dv_name = "fd",
73dbacaf56SToomas Soome 	.dv_type = DEVT_FD,
74dbacaf56SToomas Soome 	.dv_init = efipart_initfd,
75dbacaf56SToomas Soome 	.dv_strategy = efipart_strategy,
76dbacaf56SToomas Soome 	.dv_open = efipart_open,
77dbacaf56SToomas Soome 	.dv_close = efipart_close,
78dbacaf56SToomas Soome 	.dv_ioctl = efipart_ioctl,
79dbacaf56SToomas Soome 	.dv_print = efipart_printfd,
80dbacaf56SToomas Soome 	.dv_cleanup = NULL
81dbacaf56SToomas Soome };
82dbacaf56SToomas Soome 
83dbacaf56SToomas Soome struct devsw efipart_cddev = {
84dbacaf56SToomas Soome 	.dv_name = "cd",
85dbacaf56SToomas Soome 	.dv_type = DEVT_CD,
86dbacaf56SToomas Soome 	.dv_init = efipart_initcd,
87dbacaf56SToomas Soome 	.dv_strategy = efipart_strategy,
88dbacaf56SToomas Soome 	.dv_open = efipart_open,
89dbacaf56SToomas Soome 	.dv_close = efipart_close,
90dbacaf56SToomas Soome 	.dv_ioctl = efipart_ioctl,
91dbacaf56SToomas Soome 	.dv_print = efipart_printcd,
92dbacaf56SToomas Soome 	.dv_cleanup = NULL
93dbacaf56SToomas Soome };
94dbacaf56SToomas Soome 
95dbacaf56SToomas Soome struct devsw efipart_hddev = {
96dbacaf56SToomas Soome 	.dv_name = "disk",
97199767f8SToomas Soome 	.dv_type = DEVT_DISK,
98dbacaf56SToomas Soome 	.dv_init = efipart_inithd,
99199767f8SToomas Soome 	.dv_strategy = efipart_strategy,
100199767f8SToomas Soome 	.dv_open = efipart_open,
101199767f8SToomas Soome 	.dv_close = efipart_close,
102edb35047SToomas Soome 	.dv_ioctl = efipart_ioctl,
103dbacaf56SToomas Soome 	.dv_print = efipart_printhd,
104199767f8SToomas Soome 	.dv_cleanup = NULL
105199767f8SToomas Soome };
106199767f8SToomas Soome 
1070f2f3e99SToomas Soome static pdinfo_list_t fdinfo = STAILQ_HEAD_INITIALIZER(fdinfo);
1080f2f3e99SToomas Soome static pdinfo_list_t cdinfo = STAILQ_HEAD_INITIALIZER(cdinfo);
1090f2f3e99SToomas Soome static pdinfo_list_t hdinfo = STAILQ_HEAD_INITIALIZER(hdinfo);
110dbacaf56SToomas Soome 
1110f2f3e99SToomas Soome /*
1120f2f3e99SToomas Soome  * efipart_inithandles() is used to build up the pdinfo list from
1130f2f3e99SToomas Soome  * block device handles. Then each devsw init callback is used to
1140f2f3e99SToomas Soome  * pick items from pdinfo and move to proper device list.
1150f2f3e99SToomas Soome  * In ideal world, we should end up with empty pdinfo once all
1160f2f3e99SToomas Soome  * devsw initializers are called.
1170f2f3e99SToomas Soome  */
1180f2f3e99SToomas Soome static pdinfo_list_t pdinfo = STAILQ_HEAD_INITIALIZER(pdinfo);
119dbacaf56SToomas Soome 
120eea30b26SToomas Soome pdinfo_list_t *
efiblk_get_pdinfo_list(struct devsw * dev)121eea30b26SToomas Soome efiblk_get_pdinfo_list(struct devsw *dev)
122199767f8SToomas Soome {
123eea30b26SToomas Soome 	if (dev->dv_type == DEVT_DISK)
124eea30b26SToomas Soome 		return (&hdinfo);
125eea30b26SToomas Soome 	if (dev->dv_type == DEVT_CD)
126eea30b26SToomas Soome 		return (&cdinfo);
127eea30b26SToomas Soome 	if (dev->dv_type == DEVT_FD)
128eea30b26SToomas Soome 		return (&fdinfo);
129eea30b26SToomas Soome 	return (NULL);
130eea30b26SToomas Soome }
131eea30b26SToomas Soome 
132e3026534SToomas Soome /* XXX this gets called way way too often, investigate */
133eea30b26SToomas Soome pdinfo_t *
efiblk_get_pdinfo(struct devdesc * dev)134eea30b26SToomas Soome efiblk_get_pdinfo(struct devdesc *dev)
135eea30b26SToomas Soome {
136eea30b26SToomas Soome 	pdinfo_list_t *pdi;
137eea30b26SToomas Soome 	pdinfo_t *pd = NULL;
138eea30b26SToomas Soome 
139eea30b26SToomas Soome 	pdi = efiblk_get_pdinfo_list(dev->d_dev);
140eea30b26SToomas Soome 	if (pdi == NULL)
141eea30b26SToomas Soome 		return (pd);
142199767f8SToomas Soome 
143dbacaf56SToomas Soome 	STAILQ_FOREACH(pd, pdi, pd_link) {
144eea30b26SToomas Soome 		if (pd->pd_unit == dev->d_unit)
145dbacaf56SToomas Soome 			return (pd);
146dbacaf56SToomas Soome 	}
147eea30b26SToomas Soome 	return (pd);
148dbacaf56SToomas Soome }
149199767f8SToomas Soome 
150e3026534SToomas Soome static bool
same_handle(pdinfo_t * pd,EFI_HANDLE h)151e3026534SToomas Soome same_handle(pdinfo_t *pd, EFI_HANDLE h)
152e3026534SToomas Soome {
153e3026534SToomas Soome 
154e3026534SToomas Soome 	return (pd->pd_handle == h || pd->pd_alias == h);
155e3026534SToomas Soome }
156e3026534SToomas Soome 
157e3026534SToomas Soome pdinfo_t *
efiblk_get_pdinfo_by_handle(EFI_HANDLE h)158e3026534SToomas Soome efiblk_get_pdinfo_by_handle(EFI_HANDLE h)
159e3026534SToomas Soome {
160e3026534SToomas Soome 	pdinfo_t *dp, *pp;
161e3026534SToomas Soome 
162e3026534SToomas Soome 	/*
163e3026534SToomas Soome 	 * Check hard disks, then cd, then floppy
164e3026534SToomas Soome 	 */
165e3026534SToomas Soome 	STAILQ_FOREACH(dp, &hdinfo, pd_link) {
166e3026534SToomas Soome 		if (same_handle(dp, h))
167e3026534SToomas Soome 			return (dp);
168e3026534SToomas Soome 		STAILQ_FOREACH(pp, &dp->pd_part, pd_link) {
169e3026534SToomas Soome 			if (same_handle(pp, h))
170e3026534SToomas Soome 				return (pp);
171e3026534SToomas Soome 		}
172e3026534SToomas Soome 	}
173e3026534SToomas Soome 	STAILQ_FOREACH(dp, &cdinfo, pd_link) {
174e3026534SToomas Soome 		if (same_handle(dp, h))
175e3026534SToomas Soome 			return (dp);
1760f2f3e99SToomas Soome 		STAILQ_FOREACH(pp, &dp->pd_part, pd_link) {
1770f2f3e99SToomas Soome 			if (same_handle(pp, h))
1780f2f3e99SToomas Soome 				return (pp);
1790f2f3e99SToomas Soome 		}
180e3026534SToomas Soome 	}
181e3026534SToomas Soome 	STAILQ_FOREACH(dp, &fdinfo, pd_link) {
182e3026534SToomas Soome 		if (same_handle(dp, h))
183e3026534SToomas Soome 			return (dp);
184e3026534SToomas Soome 	}
185e3026534SToomas Soome 	return (NULL);
186e3026534SToomas Soome }
187e3026534SToomas Soome 
188199767f8SToomas Soome static int
efiblk_pdinfo_count(pdinfo_list_t * pdi)189dbacaf56SToomas Soome efiblk_pdinfo_count(pdinfo_list_t *pdi)
190dbacaf56SToomas Soome {
191dbacaf56SToomas Soome 	pdinfo_t *pd;
192dbacaf56SToomas Soome 	int i = 0;
193dbacaf56SToomas Soome 
194dbacaf56SToomas Soome 	STAILQ_FOREACH(pd, pdi, pd_link) {
195dbacaf56SToomas Soome 		i++;
196dbacaf56SToomas Soome 	}
197dbacaf56SToomas Soome 	return (i);
198dbacaf56SToomas Soome }
199dbacaf56SToomas Soome 
200c54162e4SToomas Soome static pdinfo_t *
efipart_find_parent(pdinfo_list_t * pdi,EFI_DEVICE_PATH * devpath)201c54162e4SToomas Soome efipart_find_parent(pdinfo_list_t *pdi, EFI_DEVICE_PATH *devpath)
202c54162e4SToomas Soome {
203c54162e4SToomas Soome 	pdinfo_t *pd;
204c54162e4SToomas Soome 	EFI_DEVICE_PATH *parent;
205c54162e4SToomas Soome 
206c54162e4SToomas Soome 	/* We want to find direct parent */
207c54162e4SToomas Soome 	parent = efi_devpath_trim(devpath);
208c54162e4SToomas Soome 	/* We should not get out of memory here but be careful. */
209c54162e4SToomas Soome 	if (parent == NULL)
210c54162e4SToomas Soome 		return (NULL);
211c54162e4SToomas Soome 
212c54162e4SToomas Soome 	STAILQ_FOREACH(pd, pdi, pd_link) {
213c54162e4SToomas Soome 		/* We must have exact match. */
214c54162e4SToomas Soome 		if (efi_devpath_match(pd->pd_devpath, parent))
215c54162e4SToomas Soome 			break;
216c54162e4SToomas Soome 	}
217c54162e4SToomas Soome 	free(parent);
218c54162e4SToomas Soome 	return (pd);
219c54162e4SToomas Soome }
220c54162e4SToomas Soome 
221c54162e4SToomas Soome /*
222c54162e4SToomas Soome  * Return true when we should ignore this device.
223c54162e4SToomas Soome  */
224c54162e4SToomas Soome static bool
efipart_ignore_device(EFI_HANDLE h,EFI_BLOCK_IO * blkio,EFI_DEVICE_PATH * devpath)225c54162e4SToomas Soome efipart_ignore_device(EFI_HANDLE h, EFI_BLOCK_IO *blkio,
226c54162e4SToomas Soome     EFI_DEVICE_PATH *devpath)
227c54162e4SToomas Soome {
228c54162e4SToomas Soome 	EFI_DEVICE_PATH *node, *parent;
229c54162e4SToomas Soome 
230c54162e4SToomas Soome 	/*
231c54162e4SToomas Soome 	 * We assume the block size 512 or greater power of 2.
232c54162e4SToomas Soome 	 * Also skip devices with block size > 64k (16 is max
233c54162e4SToomas Soome 	 * ashift supported by zfs).
234c54162e4SToomas Soome 	 * iPXE is known to insert stub BLOCK IO device with
235c54162e4SToomas Soome 	 * BlockSize 1.
236c54162e4SToomas Soome 	 */
237c54162e4SToomas Soome 	if (blkio->Media->BlockSize < 512 ||
238c54162e4SToomas Soome 	    blkio->Media->BlockSize > (1 << 16) ||
239c54162e4SToomas Soome 	    !powerof2(blkio->Media->BlockSize)) {
240c54162e4SToomas Soome 		efi_close_devpath(h);
241c54162e4SToomas Soome 		return (true);
242c54162e4SToomas Soome 	}
243c54162e4SToomas Soome 
244c54162e4SToomas Soome 	/* Allowed values are 0, 1 and power of 2. */
245c54162e4SToomas Soome 	if (blkio->Media->IoAlign > 1 &&
246c54162e4SToomas Soome 	    !powerof2(blkio->Media->IoAlign)) {
247c54162e4SToomas Soome 		efi_close_devpath(h);
248c54162e4SToomas Soome 		return (true);
249c54162e4SToomas Soome 	}
250c54162e4SToomas Soome 
251c54162e4SToomas Soome 	/*
252c54162e4SToomas Soome 	 * With device tree setup:
253c54162e4SToomas Soome 	 * PciRoot(0x0)/Pci(0x14,0x0)/USB(0x5,0)/USB(0x2,0x0)
254c54162e4SToomas Soome 	 * PciRoot(0x0)/Pci(0x14,0x0)/USB(0x5,0)/USB(0x2,0x0)/Unit(0x1)
255c54162e4SToomas Soome 	 * PciRoot(0x0)/Pci(0x14,0x0)/USB(0x5,0)/USB(0x2,0x0)/Unit(0x2)
256c54162e4SToomas Soome 	 * PciRoot(0x0)/Pci(0x14,0x0)/USB(0x5,0)/USB(0x2,0x0)/Unit(0x3)
257c54162e4SToomas Soome 	 * PciRoot(0x0)/Pci(0x14,0x0)/USB(0x5,0)/USB(0x2,0x0)/Unit(0x3)/CDROM..
258c54162e4SToomas Soome 	 * PciRoot(0x0)/Pci(0x14,0x0)/USB(0x5,0)/USB(0x2,0x0)/Unit(0x3)/CDROM..
259c54162e4SToomas Soome 	 * PciRoot(0x0)/Pci(0x14,0x0)/USB(0x5,0)/USB(0x2,0x0)/Unit(0x4)
260c54162e4SToomas Soome 	 * PciRoot(0x0)/Pci(0x14,0x0)/USB(0x5,0)/USB(0x2,0x0)/Unit(0x5)
261c54162e4SToomas Soome 	 * PciRoot(0x0)/Pci(0x14,0x0)/USB(0x5,0)/USB(0x2,0x0)/Unit(0x6)
262c54162e4SToomas Soome 	 * PciRoot(0x0)/Pci(0x14,0x0)/USB(0x5,0)/USB(0x2,0x0)/Unit(0x7)
263c54162e4SToomas Soome 	 *
264c54162e4SToomas Soome 	 * In above exmple only Unit(0x3) has media, all other nodes are
265c54162e4SToomas Soome 	 * missing media and should not be used.
266c54162e4SToomas Soome 	 *
267c54162e4SToomas Soome 	 * No media does not always mean there is no device, but in above
268c54162e4SToomas Soome 	 * case, we can not really assume there is any device.
269c54162e4SToomas Soome 	 * Therefore, if this node is USB, or this node is Unit (LUN) and
270c54162e4SToomas Soome 	 * direct parent is USB and we have no media, we will ignore this
271c54162e4SToomas Soome 	 * device.
272*9d0a7db0SToomas Soome 	 *
273*9d0a7db0SToomas Soome 	 * Variation of the same situation, but with SCSI devices:
274*9d0a7db0SToomas Soome 	 * PciRoot(0x0)/Pci(0x1a,0x0)/USB(0x1,0)/USB(0x3,0x0)/SCSI(0x0,0x1)
275*9d0a7db0SToomas Soome 	 * PciRoot(0x0)/Pci(0x1a,0x0)/USB(0x1,0)/USB(0x3,0x0)/SCSI(0x0,0x2)
276*9d0a7db0SToomas Soome 	 * PciRoot(0x0)/Pci(0x1a,0x0)/USB(0x1,0)/USB(0x3,0x0)/SCSI(0x0,0x3)
277*9d0a7db0SToomas Soome 	 * PciRoot(0x0)/Pci(0x1a,0x0)/USB(0x1,0)/USB(0x3,0x0)/SCSI(0x0,0x3)/CD..
278*9d0a7db0SToomas Soome 	 * PciRoot(0x0)/Pci(0x1a,0x0)/USB(0x1,0)/USB(0x3,0x0)/SCSI(0x0,0x3)/CD..
279*9d0a7db0SToomas Soome 	 * PciRoot(0x0)/Pci(0x1a,0x0)/USB(0x1,0)/USB(0x3,0x0)/SCSI(0x0,0x4)
280*9d0a7db0SToomas Soome 	 *
281*9d0a7db0SToomas Soome 	 * Here above the SCSI luns 1,2 and 4 have no media.
282c54162e4SToomas Soome 	 */
283c54162e4SToomas Soome 
284c54162e4SToomas Soome 	/* Do not ignore device with media. */
285c54162e4SToomas Soome 	if (blkio->Media->MediaPresent)
286c54162e4SToomas Soome 		return (false);
287c54162e4SToomas Soome 
288c54162e4SToomas Soome 	node = efi_devpath_last_node(devpath);
289c54162e4SToomas Soome 	if (node == NULL)
290c54162e4SToomas Soome 		return (false);
291c54162e4SToomas Soome 
292c54162e4SToomas Soome 	/* USB without media present */
293c54162e4SToomas Soome 	if (DevicePathType(node) == MESSAGING_DEVICE_PATH &&
294c54162e4SToomas Soome 	    DevicePathSubType(node) == MSG_USB_DP) {
295c54162e4SToomas Soome 		efi_close_devpath(h);
296c54162e4SToomas Soome 		return (true);
297c54162e4SToomas Soome 	}
298c54162e4SToomas Soome 
299c54162e4SToomas Soome 	parent = efi_devpath_trim(devpath);
300c54162e4SToomas Soome 	if (parent != NULL) {
301c54162e4SToomas Soome 		bool parent_is_usb = false;
302c54162e4SToomas Soome 
303c54162e4SToomas Soome 		node = efi_devpath_last_node(parent);
304c54162e4SToomas Soome 		if (node == NULL) {
305c54162e4SToomas Soome 			free(parent);
306c54162e4SToomas Soome 			return (false);
307c54162e4SToomas Soome 		}
308c54162e4SToomas Soome 		if (DevicePathType(node) == MESSAGING_DEVICE_PATH &&
309c54162e4SToomas Soome 		    DevicePathSubType(node) == MSG_USB_DP)
310c54162e4SToomas Soome 			parent_is_usb = true;
311c54162e4SToomas Soome 		free(parent);
312c54162e4SToomas Soome 
313c54162e4SToomas Soome 		node = efi_devpath_last_node(devpath);
314c54162e4SToomas Soome 		if (node == NULL)
315c54162e4SToomas Soome 			return (false);
316c54162e4SToomas Soome 		if (parent_is_usb &&
317*9d0a7db0SToomas Soome 		    DevicePathType(node) == MESSAGING_DEVICE_PATH) {
318*9d0a7db0SToomas Soome 			/*
319*9d0a7db0SToomas Soome 			 * no media, parent is USB and devicepath is
320*9d0a7db0SToomas Soome 			 * LUN or SCSI.
321*9d0a7db0SToomas Soome 			 */
322*9d0a7db0SToomas Soome 			if (DevicePathSubType(node) ==
323*9d0a7db0SToomas Soome 			    MSG_DEVICE_LOGICAL_UNIT_DP ||
324*9d0a7db0SToomas Soome 			    DevicePathSubType(node) == MSG_SCSI_DP) {
325*9d0a7db0SToomas Soome 				efi_close_devpath(h);
326*9d0a7db0SToomas Soome 				return (true);
327*9d0a7db0SToomas Soome 			}
328c54162e4SToomas Soome 		}
329c54162e4SToomas Soome 	}
330c54162e4SToomas Soome 	return (false);
331c54162e4SToomas Soome }
332c54162e4SToomas Soome 
333c00b6c91SToomas Soome int
efipart_inithandles(void)334dbacaf56SToomas Soome efipart_inithandles(void)
335199767f8SToomas Soome {
3360f2f3e99SToomas Soome 	unsigned i, nin;
337199767f8SToomas Soome 	UINTN sz;
338dbacaf56SToomas Soome 	EFI_HANDLE *hin;
3390f2f3e99SToomas Soome 	EFI_DEVICE_PATH *devpath;
3400f2f3e99SToomas Soome 	EFI_BLOCK_IO *blkio;
341dbacaf56SToomas Soome 	EFI_STATUS status;
3420f2f3e99SToomas Soome 	pdinfo_t *pd;
343dbacaf56SToomas Soome 
3440f2f3e99SToomas Soome 	if (!STAILQ_EMPTY(&pdinfo))
3450f2f3e99SToomas Soome 		return (0);
346199767f8SToomas Soome 
347199767f8SToomas Soome 	sz = 0;
348199767f8SToomas Soome 	hin = NULL;
349dbacaf56SToomas Soome 	status = BS->LocateHandle(ByProtocol, &blkio_guid, 0, &sz, hin);
350199767f8SToomas Soome 	if (status == EFI_BUFFER_TOO_SMALL) {
351dbacaf56SToomas Soome 		hin = malloc(sz);
352199767f8SToomas Soome 		status = BS->LocateHandle(ByProtocol, &blkio_guid, 0, &sz,
353199767f8SToomas Soome 		    hin);
354199767f8SToomas Soome 		if (EFI_ERROR(status))
355199767f8SToomas Soome 			free(hin);
356199767f8SToomas Soome 	}
357199767f8SToomas Soome 	if (EFI_ERROR(status))
358199767f8SToomas Soome 		return (efi_status_to_errno(status));
359199767f8SToomas Soome 
3600f2f3e99SToomas Soome 	nin = sz / sizeof (*hin);
361c00b6c91SToomas Soome #ifdef EFIPART_DEBUG
3620f2f3e99SToomas Soome 	printf("%s: Got %d BLOCK IO MEDIA handle(s)\n", __func__, nin);
363c00b6c91SToomas Soome #endif
364dbacaf56SToomas Soome 
365f58b22cfSToomas Soome 	for (i = 0; i < nin; i++) {
366f58b22cfSToomas Soome 		/*
3670f2f3e99SToomas Soome 		 * Get devpath and open protocol.
3680f2f3e99SToomas Soome 		 * We should not get errors here
369f58b22cfSToomas Soome 		 */
3700f2f3e99SToomas Soome 		if ((devpath = efi_lookup_devpath(hin[i])) == NULL)
3710f2f3e99SToomas Soome 			continue;
372f58b22cfSToomas Soome 
3730f2f3e99SToomas Soome 		status = OpenProtocolByHandle(hin[i], &blkio_guid,
374bf693dc9SToomas Soome 		    (void **)&blkio);
3750f2f3e99SToomas Soome 		if (EFI_ERROR(status)) {
3760f2f3e99SToomas Soome 			printf("error %lu\n", EFI_ERROR_CODE(status));
3770f2f3e99SToomas Soome 			continue;
378f58b22cfSToomas Soome 		}
3796c6e04f2SToomas Soome 
380c54162e4SToomas Soome 		if (efipart_ignore_device(hin[i], blkio, devpath))
3810f2f3e99SToomas Soome 			continue;
3822b292e00SToomas Soome 
3830f2f3e99SToomas Soome 		/* This is bad. */
3840f2f3e99SToomas Soome 		if ((pd = calloc(1, sizeof (*pd))) == NULL) {
3850f2f3e99SToomas Soome 			printf("efipart_inithandles: Out of memory.\n");
3860f2f3e99SToomas Soome 			free(hin);
3870f2f3e99SToomas Soome 			return (ENOMEM);
3880f2f3e99SToomas Soome 		}
3890f2f3e99SToomas Soome 		STAILQ_INIT(&pd->pd_part);
390199767f8SToomas Soome 
3910f2f3e99SToomas Soome 		pd->pd_handle = hin[i];
3920f2f3e99SToomas Soome 		pd->pd_devpath = devpath;
3930f2f3e99SToomas Soome 		pd->pd_blkio = blkio;
3940f2f3e99SToomas Soome 		STAILQ_INSERT_TAIL(&pdinfo, pd, pd_link);
395dbacaf56SToomas Soome 	}
3960f2f3e99SToomas Soome 
397c54162e4SToomas Soome 	/*
398c54162e4SToomas Soome 	 * Walk pdinfo and set parents based on device path.
399c54162e4SToomas Soome 	 */
400c54162e4SToomas Soome 	STAILQ_FOREACH(pd, &pdinfo, pd_link) {
401c54162e4SToomas Soome 		pd->pd_parent = efipart_find_parent(&pdinfo, pd->pd_devpath);
402c54162e4SToomas Soome 	}
4030f2f3e99SToomas Soome 	free(hin);
404dbacaf56SToomas Soome 	return (0);
405dbacaf56SToomas Soome }
406199767f8SToomas Soome 
407c54162e4SToomas Soome /*
408c54162e4SToomas Soome  * Get node identified by pd_test() from plist.
409c54162e4SToomas Soome  */
410c54162e4SToomas Soome static pdinfo_t *
efipart_get_pd(pdinfo_list_t * plist,pd_test_cb_t pd_test,pdinfo_t * data)411c54162e4SToomas Soome efipart_get_pd(pdinfo_list_t *plist, pd_test_cb_t pd_test, pdinfo_t *data)
412c54162e4SToomas Soome {
413c54162e4SToomas Soome 	pdinfo_t *pd;
414c54162e4SToomas Soome 
415c54162e4SToomas Soome 	STAILQ_FOREACH(pd, plist, pd_link) {
416c54162e4SToomas Soome 		if (pd_test(pd, data))
417c54162e4SToomas Soome 			break;
418c54162e4SToomas Soome 	}
419c54162e4SToomas Soome 
420c54162e4SToomas Soome 	return (pd);
421c54162e4SToomas Soome }
422c54162e4SToomas Soome 
4230f2f3e99SToomas Soome static ACPI_HID_DEVICE_PATH *
efipart_floppy(EFI_DEVICE_PATH * node)4240f2f3e99SToomas Soome efipart_floppy(EFI_DEVICE_PATH *node)
425dbacaf56SToomas Soome {
426dbacaf56SToomas Soome 	ACPI_HID_DEVICE_PATH *acpi;
427dbacaf56SToomas Soome 
4280f2f3e99SToomas Soome 	if (DevicePathType(node) == ACPI_DEVICE_PATH &&
4290f2f3e99SToomas Soome 	    DevicePathSubType(node) == ACPI_DP) {
4300f2f3e99SToomas Soome 		acpi = (ACPI_HID_DEVICE_PATH *) node;
4310f2f3e99SToomas Soome 		if (acpi->HID == EISA_PNP_ID(PNP0604) ||
4320f2f3e99SToomas Soome 		    acpi->HID == EISA_PNP_ID(PNP0700) ||
4330f2f3e99SToomas Soome 		    acpi->HID == EISA_PNP_ID(PNP0701)) {
4340f2f3e99SToomas Soome 			return (acpi);
435199767f8SToomas Soome 		}
436dbacaf56SToomas Soome 	}
4370f2f3e99SToomas Soome 	return (NULL);
4380f2f3e99SToomas Soome }
4390f2f3e99SToomas Soome 
440c54162e4SToomas Soome static bool
efipart_testfd(pdinfo_t * fd,pdinfo_t * data __unused)441c54162e4SToomas Soome efipart_testfd(pdinfo_t *fd, pdinfo_t *data __unused)
4420f2f3e99SToomas Soome {
443c54162e4SToomas Soome 	EFI_DEVICE_PATH *node;
4440f2f3e99SToomas Soome 
445c54162e4SToomas Soome 	node = efi_devpath_last_node(fd->pd_devpath);
446c54162e4SToomas Soome 	if (node == NULL)
447c54162e4SToomas Soome 		return (false);
448c54162e4SToomas Soome 
449c54162e4SToomas Soome 	if (efipart_floppy(node) != NULL)
450c54162e4SToomas Soome 		return (true);
451c54162e4SToomas Soome 
452c54162e4SToomas Soome 	return (false);
453dbacaf56SToomas Soome }
454199767f8SToomas Soome 
455dbacaf56SToomas Soome static int
efipart_initfd(void)456dbacaf56SToomas Soome efipart_initfd(void)
457dbacaf56SToomas Soome {
4580f2f3e99SToomas Soome 	EFI_DEVICE_PATH *node;
4590f2f3e99SToomas Soome 	ACPI_HID_DEVICE_PATH *acpi;
4600f2f3e99SToomas Soome 	pdinfo_t *parent, *fd;
461dbacaf56SToomas Soome 
462c54162e4SToomas Soome 	while ((fd = efipart_get_pd(&pdinfo, efipart_testfd, NULL)) != NULL) {
4630f2f3e99SToomas Soome 		if ((node = efi_devpath_last_node(fd->pd_devpath)) == NULL)
4640f2f3e99SToomas Soome 			continue;
465dbacaf56SToomas Soome 
4660f2f3e99SToomas Soome 		if ((acpi = efipart_floppy(node)) == NULL)
4670f2f3e99SToomas Soome 			continue;
4680f2f3e99SToomas Soome 
4690f2f3e99SToomas Soome 		STAILQ_REMOVE(&pdinfo, fd, pdinfo, pd_link);
470c54162e4SToomas Soome 		parent = fd->pd_parent;
4710f2f3e99SToomas Soome 		if (parent != NULL) {
4720f2f3e99SToomas Soome 			STAILQ_REMOVE(&pdinfo, parent, pdinfo, pd_link);
4730f2f3e99SToomas Soome 			parent->pd_alias = fd->pd_handle;
4740f2f3e99SToomas Soome 			parent->pd_unit = acpi->UID;
4750f2f3e99SToomas Soome 			free(fd);
4760f2f3e99SToomas Soome 			fd = parent;
4770f2f3e99SToomas Soome 		} else {
4780f2f3e99SToomas Soome 			fd->pd_unit = acpi->UID;
4790f2f3e99SToomas Soome 		}
4800f2f3e99SToomas Soome 		fd->pd_devsw = &efipart_fddev;
4810f2f3e99SToomas Soome 		STAILQ_INSERT_TAIL(&fdinfo, fd, pd_link);
4820f2f3e99SToomas Soome 	}
483dbacaf56SToomas Soome 
484dbacaf56SToomas Soome 	bcache_add_dev(efiblk_pdinfo_count(&fdinfo));
485dbacaf56SToomas Soome 	return (0);
486dbacaf56SToomas Soome }
487dbacaf56SToomas Soome 
488dbacaf56SToomas Soome /*
489dbacaf56SToomas Soome  * Add or update entries with new handle data.
490dbacaf56SToomas Soome  */
4910f2f3e99SToomas Soome static void
efipart_cdinfo_add(pdinfo_t * cd)4920f2f3e99SToomas Soome efipart_cdinfo_add(pdinfo_t *cd)
493dbacaf56SToomas Soome {
494c54162e4SToomas Soome 	pdinfo_t *parent, *pd, *last;
495c54162e4SToomas Soome 
496c54162e4SToomas Soome 	if (cd == NULL)
497c54162e4SToomas Soome 		return;
498c54162e4SToomas Soome 
499c54162e4SToomas Soome 	parent = cd->pd_parent;
500c54162e4SToomas Soome 	/* Make sure we have parent added */
501c54162e4SToomas Soome 	efipart_cdinfo_add(parent);
502c54162e4SToomas Soome 
503c54162e4SToomas Soome 	STAILQ_FOREACH(pd, &pdinfo, pd_link) {
504c54162e4SToomas Soome 		if (efi_devpath_match(pd->pd_devpath, cd->pd_devpath)) {
505c54162e4SToomas Soome 			STAILQ_REMOVE(&pdinfo, cd, pdinfo, pd_link);
506c54162e4SToomas Soome 			break;
507dbacaf56SToomas Soome 		}
508dbacaf56SToomas Soome 	}
509c54162e4SToomas Soome 	if (pd == NULL) {
510c54162e4SToomas Soome 		/* This device is already added. */
511c54162e4SToomas Soome 		return;
512c54162e4SToomas Soome 	}
513c54162e4SToomas Soome 
514c54162e4SToomas Soome 	if (parent != NULL) {
515c54162e4SToomas Soome 		last = STAILQ_LAST(&parent->pd_part, pdinfo, pd_link);
516c54162e4SToomas Soome 		if (last != NULL)
517c54162e4SToomas Soome 			cd->pd_unit = last->pd_unit + 1;
518c54162e4SToomas Soome 		else
519c54162e4SToomas Soome 			cd->pd_unit = 0;
520c54162e4SToomas Soome 		cd->pd_devsw = &efipart_cddev;
521c54162e4SToomas Soome 		STAILQ_INSERT_TAIL(&parent->pd_part, cd, pd_link);
522c54162e4SToomas Soome 		return;
523c54162e4SToomas Soome 	}
524dbacaf56SToomas Soome 
5250f2f3e99SToomas Soome 	last = STAILQ_LAST(&cdinfo, pdinfo, pd_link);
5260f2f3e99SToomas Soome 	if (last != NULL)
5270f2f3e99SToomas Soome 		cd->pd_unit = last->pd_unit + 1;
5280f2f3e99SToomas Soome 	else
5290f2f3e99SToomas Soome 		cd->pd_unit = 0;
530dbacaf56SToomas Soome 
531e3026534SToomas Soome 	cd->pd_devsw = &efipart_cddev;
532dbacaf56SToomas Soome 	STAILQ_INSERT_TAIL(&cdinfo, cd, pd_link);
5330f2f3e99SToomas Soome }
5340f2f3e99SToomas Soome 
5350f2f3e99SToomas Soome static bool
efipart_testcd(pdinfo_t * cd,pdinfo_t * data __unused)536c54162e4SToomas Soome efipart_testcd(pdinfo_t *cd, pdinfo_t *data __unused)
5370f2f3e99SToomas Soome {
538c54162e4SToomas Soome 	EFI_DEVICE_PATH *node;
539c54162e4SToomas Soome 
540c54162e4SToomas Soome 	node = efi_devpath_last_node(cd->pd_devpath);
541c54162e4SToomas Soome 	if (node == NULL)
542c54162e4SToomas Soome 		return (false);
543c54162e4SToomas Soome 
544c54162e4SToomas Soome 	if (efipart_floppy(node) != NULL)
545c54162e4SToomas Soome 		return (false);
546c54162e4SToomas Soome 
5470f2f3e99SToomas Soome 	if (DevicePathType(node) == MEDIA_DEVICE_PATH &&
5480f2f3e99SToomas Soome 	    DevicePathSubType(node) == MEDIA_CDROM_DP) {
5490f2f3e99SToomas Soome 		return (true);
5500f2f3e99SToomas Soome 	}
5510f2f3e99SToomas Soome 
5520f2f3e99SToomas Soome 	/* cd drive without the media. */
553c54162e4SToomas Soome 	if (cd->pd_blkio->Media->RemovableMedia &&
554c54162e4SToomas Soome 	    !cd->pd_blkio->Media->MediaPresent) {
5550f2f3e99SToomas Soome 		return (true);
5560f2f3e99SToomas Soome 	}
5570f2f3e99SToomas Soome 
5580f2f3e99SToomas Soome 	return (false);
559dbacaf56SToomas Soome }
560dbacaf56SToomas Soome 
561c54162e4SToomas Soome /*
562c54162e4SToomas Soome  * Test if pd is parent for device.
563c54162e4SToomas Soome  */
564c54162e4SToomas Soome static bool
efipart_testchild(pdinfo_t * dev,pdinfo_t * pd)565c54162e4SToomas Soome efipart_testchild(pdinfo_t *dev, pdinfo_t *pd)
566dbacaf56SToomas Soome {
567c54162e4SToomas Soome 	/* device with no parent. */
568c54162e4SToomas Soome 	if (dev->pd_parent == NULL)
569c54162e4SToomas Soome 		return (false);
570dbacaf56SToomas Soome 
571c54162e4SToomas Soome 	if (efi_devpath_match(dev->pd_parent->pd_devpath, pd->pd_devpath)) {
572c54162e4SToomas Soome 		return (true);
573dbacaf56SToomas Soome 	}
574c54162e4SToomas Soome 	return (false);
575199767f8SToomas Soome }
576199767f8SToomas Soome 
577199767f8SToomas Soome static int
efipart_initcd(void)578dbacaf56SToomas Soome efipart_initcd(void)
579199767f8SToomas Soome {
580c54162e4SToomas Soome 	pdinfo_t *cd;
581c54162e4SToomas Soome 
582c54162e4SToomas Soome 	while ((cd = efipart_get_pd(&pdinfo, efipart_testcd, NULL)) != NULL)
583c54162e4SToomas Soome 		efipart_cdinfo_add(cd);
584dbacaf56SToomas Soome 
585c54162e4SToomas Soome 	/* Find all children of CD devices we did add above. */
586c54162e4SToomas Soome 	STAILQ_FOREACH(cd, &cdinfo, pd_link) {
587c54162e4SToomas Soome 		pdinfo_t *child;
588c54162e4SToomas Soome 
589c54162e4SToomas Soome 		for (child = efipart_get_pd(&pdinfo, efipart_testchild, cd);
590c54162e4SToomas Soome 		    child != NULL;
591c54162e4SToomas Soome 		    child = efipart_get_pd(&pdinfo, efipart_testchild, cd))
592c54162e4SToomas Soome 			efipart_cdinfo_add(child);
593c54162e4SToomas Soome 	}
594dbacaf56SToomas Soome 	bcache_add_dev(efiblk_pdinfo_count(&cdinfo));
595dbacaf56SToomas Soome 	return (0);
596dbacaf56SToomas Soome }
597dbacaf56SToomas Soome 
598c54162e4SToomas Soome static void
efipart_hdinfo_add_node(pdinfo_t * hd,EFI_DEVICE_PATH * node)59993d2b6f2SToomas Soome efipart_hdinfo_add_node(pdinfo_t *hd, EFI_DEVICE_PATH *node)
600dbacaf56SToomas Soome {
601c54162e4SToomas Soome 	pdinfo_t *parent, *ptr;
60293d2b6f2SToomas Soome 
60393d2b6f2SToomas Soome 	if (node == NULL)
604c54162e4SToomas Soome 		return;
60593d2b6f2SToomas Soome 
606c54162e4SToomas Soome 	parent = hd->pd_parent;
607c54162e4SToomas Soome 	/*
608c54162e4SToomas Soome 	 * If the node is not MEDIA_HARDDRIVE_DP, it is sub-partition.
609c54162e4SToomas Soome 	 * This can happen with Vendor nodes, and since we do not know
610c54162e4SToomas Soome 	 * the more about those nodes, we just count them.
611c54162e4SToomas Soome 	 */
61293d2b6f2SToomas Soome 	if (DevicePathSubType(node) != MEDIA_HARDDRIVE_DP) {
613c54162e4SToomas Soome 		ptr = STAILQ_LAST(&parent->pd_part, pdinfo, pd_link);
61493d2b6f2SToomas Soome 		if (ptr != NULL)
61593d2b6f2SToomas Soome 			hd->pd_unit = ptr->pd_unit + 1;
61693d2b6f2SToomas Soome 		else
61793d2b6f2SToomas Soome 			hd->pd_unit = 0;
61893d2b6f2SToomas Soome 	} else {
61993d2b6f2SToomas Soome 		hd->pd_unit = ((HARDDRIVE_DEVICE_PATH *)node)->PartitionNumber;
620dbacaf56SToomas Soome 	}
621dbacaf56SToomas Soome 
622e3026534SToomas Soome 	hd->pd_devsw = &efipart_hddev;
623c54162e4SToomas Soome 	STAILQ_INSERT_TAIL(&parent->pd_part, hd, pd_link);
624dbacaf56SToomas Soome }
625dbacaf56SToomas Soome 
626dbacaf56SToomas Soome /*
627dbacaf56SToomas Soome  * The MEDIA_FILEPATH_DP has device name.
628dbacaf56SToomas Soome  * From U-Boot sources it looks like names are in the form
629dbacaf56SToomas Soome  * of typeN:M, where type is interface type, N is disk id
630dbacaf56SToomas Soome  * and M is partition id.
631dbacaf56SToomas Soome  */
6320f2f3e99SToomas Soome static void
efipart_hdinfo_add_filepath(pdinfo_t * hd,FILEPATH_DEVICE_PATH * node)6330f2f3e99SToomas Soome efipart_hdinfo_add_filepath(pdinfo_t *hd, FILEPATH_DEVICE_PATH *node)
634dbacaf56SToomas Soome {
635dbacaf56SToomas Soome 	char *pathname, *p;
6360f2f3e99SToomas Soome 	int len;
6370f2f3e99SToomas Soome 	pdinfo_t *last;
638dbacaf56SToomas Soome 
639dbacaf56SToomas Soome 	last = STAILQ_LAST(&hdinfo, pdinfo, pd_link);
640dbacaf56SToomas Soome 	if (last != NULL)
6410f2f3e99SToomas Soome 		hd->pd_unit = last->pd_unit + 1;
642dbacaf56SToomas Soome 	else
6430f2f3e99SToomas Soome 		hd->pd_unit = 0;
644dbacaf56SToomas Soome 
645dbacaf56SToomas Soome 	/* FILEPATH_DEVICE_PATH has 0 terminated string */
6461f54f0bbSToomas Soome 	len = ucs2len(node->PathName);
647dbacaf56SToomas Soome 	if ((pathname = malloc(len + 1)) == NULL) {
648dbacaf56SToomas Soome 		printf("Failed to add disk, out of memory\n");
6490f2f3e99SToomas Soome 		free(hd);
6500f2f3e99SToomas Soome 		return;
651dbacaf56SToomas Soome 	}
652dbacaf56SToomas Soome 	cpy16to8(node->PathName, pathname, len + 1);
653dbacaf56SToomas Soome 	p = strchr(pathname, ':');
654dbacaf56SToomas Soome 
655dbacaf56SToomas Soome 	/*
656dbacaf56SToomas Soome 	 * Assume we are receiving handles in order, first disk handle,
657dbacaf56SToomas Soome 	 * then partitions for this disk. If this assumption proves
658dbacaf56SToomas Soome 	 * false, this code would need update.
659dbacaf56SToomas Soome 	 */
660dbacaf56SToomas Soome 	if (p == NULL) {	/* no colon, add the disk */
6610f2f3e99SToomas Soome 		hd->pd_devsw = &efipart_hddev;
6620f2f3e99SToomas Soome 		STAILQ_INSERT_TAIL(&hdinfo, hd, pd_link);
663dbacaf56SToomas Soome 		free(pathname);
6640f2f3e99SToomas Soome 		return;
665dbacaf56SToomas Soome 	}
666dbacaf56SToomas Soome 	p++;	/* skip the colon */
667dbacaf56SToomas Soome 	errno = 0;
6680f2f3e99SToomas Soome 	hd->pd_unit = (int)strtol(p, NULL, 0);
669dbacaf56SToomas Soome 	if (errno != 0) {
670dbacaf56SToomas Soome 		printf("Bad unit number for partition \"%s\"\n", pathname);
671dbacaf56SToomas Soome 		free(pathname);
6720f2f3e99SToomas Soome 		free(hd);
6730f2f3e99SToomas Soome 		return;
674dbacaf56SToomas Soome 	}
675dbacaf56SToomas Soome 
676dbacaf56SToomas Soome 	/*
677dbacaf56SToomas Soome 	 * We should have disk registered, if not, we are receiving
678dbacaf56SToomas Soome 	 * handles out of order, and this code should be reworked
679dbacaf56SToomas Soome 	 * to create "blank" disk for partition, and to find the
680dbacaf56SToomas Soome 	 * disk based on PathName compares.
681dbacaf56SToomas Soome 	 */
682dbacaf56SToomas Soome 	if (last == NULL) {
683dbacaf56SToomas Soome 		printf("BUG: No disk for partition \"%s\"\n", pathname);
684dbacaf56SToomas Soome 		free(pathname);
6850f2f3e99SToomas Soome 		free(hd);
6860f2f3e99SToomas Soome 		return;
687dbacaf56SToomas Soome 	}
688dbacaf56SToomas Soome 	/* Add the partition. */
6890f2f3e99SToomas Soome 	hd->pd_parent = last;
6900f2f3e99SToomas Soome 	hd->pd_devsw = &efipart_hddev;
6910f2f3e99SToomas Soome 	STAILQ_INSERT_TAIL(&last->pd_part, hd, pd_link);
692dbacaf56SToomas Soome 	free(pathname);
693dbacaf56SToomas Soome }
694dbacaf56SToomas Soome 
695dbacaf56SToomas Soome static void
efipart_hdinfo_add(pdinfo_t * hd)696c54162e4SToomas Soome efipart_hdinfo_add(pdinfo_t *hd)
697dbacaf56SToomas Soome {
698c54162e4SToomas Soome 	pdinfo_t *parent, *pd, *last;
699c54162e4SToomas Soome 	EFI_DEVICE_PATH *node;
700dbacaf56SToomas Soome 
701c54162e4SToomas Soome 	if (hd == NULL)
702c54162e4SToomas Soome 		return;
703f58b22cfSToomas Soome 
704c54162e4SToomas Soome 	parent = hd->pd_parent;
705c54162e4SToomas Soome 	/* Make sure we have parent added */
706c54162e4SToomas Soome 	efipart_hdinfo_add(parent);
707dbacaf56SToomas Soome 
708c54162e4SToomas Soome 	STAILQ_FOREACH(pd, &pdinfo, pd_link) {
709c54162e4SToomas Soome 		if (efi_devpath_match(pd->pd_devpath, hd->pd_devpath)) {
7100f2f3e99SToomas Soome 			STAILQ_REMOVE(&pdinfo, hd, pdinfo, pd_link);
711c54162e4SToomas Soome 			break;
7120f2f3e99SToomas Soome 		}
713c54162e4SToomas Soome 	}
714c54162e4SToomas Soome 	if (pd == NULL) {
715c54162e4SToomas Soome 		/* This device is already added. */
716c54162e4SToomas Soome 		return;
717c54162e4SToomas Soome 	}
718dbacaf56SToomas Soome 
719c54162e4SToomas Soome 	if ((node = efi_devpath_last_node(hd->pd_devpath)) == NULL)
720c54162e4SToomas Soome 		return;
7210f2f3e99SToomas Soome 
722c54162e4SToomas Soome 	if (DevicePathType(node) == MEDIA_DEVICE_PATH &&
723c54162e4SToomas Soome 	    DevicePathSubType(node) == MEDIA_FILEPATH_DP) {
724c54162e4SToomas Soome 		efipart_hdinfo_add_filepath(hd,
725c54162e4SToomas Soome 		    (FILEPATH_DEVICE_PATH *)node);
726c54162e4SToomas Soome 		return;
727c54162e4SToomas Soome 	}
728f58b22cfSToomas Soome 
729c54162e4SToomas Soome 	if (parent != NULL) {
730c54162e4SToomas Soome 		efipart_hdinfo_add_node(hd, node);
731c54162e4SToomas Soome 		return;
732c54162e4SToomas Soome 	}
733f58b22cfSToomas Soome 
734c54162e4SToomas Soome 	last = STAILQ_LAST(&hdinfo, pdinfo, pd_link);
735c54162e4SToomas Soome 	if (last != NULL)
736c54162e4SToomas Soome 		hd->pd_unit = last->pd_unit + 1;
737c54162e4SToomas Soome 	else
738c54162e4SToomas Soome 		hd->pd_unit = 0;
739f58b22cfSToomas Soome 
740c54162e4SToomas Soome 	/* Add the disk. */
741c54162e4SToomas Soome 	hd->pd_devsw = &efipart_hddev;
742c54162e4SToomas Soome 	STAILQ_INSERT_TAIL(&hdinfo, hd, pd_link);
743c54162e4SToomas Soome }
7440f2f3e99SToomas Soome 
745c54162e4SToomas Soome static bool
efipart_testhd(pdinfo_t * hd,pdinfo_t * data __unused)746c54162e4SToomas Soome efipart_testhd(pdinfo_t *hd, pdinfo_t *data __unused)
747c54162e4SToomas Soome {
748c54162e4SToomas Soome 	if (efipart_testfd(hd, NULL))
749c54162e4SToomas Soome 		return (false);
7500f2f3e99SToomas Soome 
751c54162e4SToomas Soome 	if (efipart_testcd(hd, NULL))
752c54162e4SToomas Soome 		return (false);
753dbacaf56SToomas Soome 
754c54162e4SToomas Soome 	/* Anything else must be HD. */
755c54162e4SToomas Soome 	return (true);
756dbacaf56SToomas Soome }
757dbacaf56SToomas Soome 
758dbacaf56SToomas Soome static int
efipart_inithd(void)759dbacaf56SToomas Soome efipart_inithd(void)
760dbacaf56SToomas Soome {
761c54162e4SToomas Soome 	pdinfo_t *hd;
762dbacaf56SToomas Soome 
763c54162e4SToomas Soome 	while ((hd = efipart_get_pd(&pdinfo, efipart_testhd, NULL)) != NULL)
764c54162e4SToomas Soome 		efipart_hdinfo_add(hd);
765dbacaf56SToomas Soome 
766dbacaf56SToomas Soome 	bcache_add_dev(efiblk_pdinfo_count(&hdinfo));
767dbacaf56SToomas Soome 	return (0);
768dbacaf56SToomas Soome }
769dbacaf56SToomas Soome 
770dbacaf56SToomas Soome static int
efipart_print_common(struct devsw * dev,pdinfo_list_t * pdlist,int verbose)771dbacaf56SToomas Soome efipart_print_common(struct devsw *dev, pdinfo_list_t *pdlist, int verbose)
772dbacaf56SToomas Soome {
773199767f8SToomas Soome 	int ret = 0;
774dbacaf56SToomas Soome 	EFI_BLOCK_IO *blkio;
775dbacaf56SToomas Soome 	EFI_STATUS status;
776dbacaf56SToomas Soome 	EFI_HANDLE h;
777dbacaf56SToomas Soome 	pdinfo_t *pd;
778dbacaf56SToomas Soome 	CHAR16 *text;
779dbacaf56SToomas Soome 	struct disk_devdesc pd_dev;
780dbacaf56SToomas Soome 	char line[80];
781dbacaf56SToomas Soome 
782dbacaf56SToomas Soome 	if (STAILQ_EMPTY(pdlist))
783dbacaf56SToomas Soome 		return (0);
784199767f8SToomas Soome 
785dbacaf56SToomas Soome 	printf("%s devices:", dev->dv_name);
786502b33a5SToomas Soome 	if ((ret = pager_output("\n")) != 0)
787502b33a5SToomas Soome 		return (ret);
788502b33a5SToomas Soome 
789dbacaf56SToomas Soome 	STAILQ_FOREACH(pd, pdlist, pd_link) {
790dbacaf56SToomas Soome 		h = pd->pd_handle;
791dbacaf56SToomas Soome 		if (verbose) {	/* Output the device path. */
792dbacaf56SToomas Soome 			text = efi_devpath_name(efi_lookup_devpath(h));
793dbacaf56SToomas Soome 			if (text != NULL) {
794dbacaf56SToomas Soome 				printf("  %S", text);
795dbacaf56SToomas Soome 				efi_free_devpath_name(text);
796dbacaf56SToomas Soome 				if ((ret = pager_output("\n")) != 0)
797dbacaf56SToomas Soome 					break;
798dbacaf56SToomas Soome 			}
799dbacaf56SToomas Soome 		}
8000f2f3e99SToomas Soome 		snprintf(line, sizeof (line),
801dbacaf56SToomas Soome 		    "    %s%d", dev->dv_name, pd->pd_unit);
802dbacaf56SToomas Soome 		printf("%s:", line);
803bf693dc9SToomas Soome 		status = OpenProtocolByHandle(h, &blkio_guid, (void **)&blkio);
804199767f8SToomas Soome 		if (!EFI_ERROR(status)) {
805dbacaf56SToomas Soome 			printf("    %llu",
806dbacaf56SToomas Soome 			    blkio->Media->LastBlock == 0? 0:
807dbacaf56SToomas Soome 			    (unsigned long long) (blkio->Media->LastBlock + 1));
808dbacaf56SToomas Soome 			if (blkio->Media->LastBlock != 0) {
809dbacaf56SToomas Soome 				printf(" X %u", blkio->Media->BlockSize);
810dbacaf56SToomas Soome 			}
811dbacaf56SToomas Soome 			printf(" blocks");
812dbacaf56SToomas Soome 			if (blkio->Media->MediaPresent) {
813dbacaf56SToomas Soome 				if (blkio->Media->RemovableMedia)
814dbacaf56SToomas Soome 					printf(" (removable)");
815dbacaf56SToomas Soome 			} else {
816dbacaf56SToomas Soome 				printf(" (no media)");
817dbacaf56SToomas Soome 			}
818dbacaf56SToomas Soome 			if ((ret = pager_output("\n")) != 0)
819dbacaf56SToomas Soome 				break;
820dbacaf56SToomas Soome 			if (!blkio->Media->MediaPresent)
821dbacaf56SToomas Soome 				continue;
822dbacaf56SToomas Soome 
823dbacaf56SToomas Soome 			pd->pd_blkio = blkio;
82476b35943SToomas Soome 			pd_dev.dd.d_dev = dev;
82576b35943SToomas Soome 			pd_dev.dd.d_unit = pd->pd_unit;
8269a34674dSToomas Soome 			pd_dev.d_slice = D_SLICENONE;
8279a34674dSToomas Soome 			pd_dev.d_partition = D_PARTNONE;
828dbacaf56SToomas Soome 			ret = disk_open(&pd_dev, blkio->Media->BlockSize *
829dbacaf56SToomas Soome 			    (blkio->Media->LastBlock + 1),
830dbacaf56SToomas Soome 			    blkio->Media->BlockSize);
831dbacaf56SToomas Soome 			if (ret == 0) {
832dbacaf56SToomas Soome 				ret = disk_print(&pd_dev, line, verbose);
833dbacaf56SToomas Soome 				disk_close(&pd_dev);
834dbacaf56SToomas Soome 				if (ret != 0)
835dbacaf56SToomas Soome 					return (ret);
836dbacaf56SToomas Soome 			} else {
837dbacaf56SToomas Soome 				/* Do not fail from disk_open() */
838dbacaf56SToomas Soome 				ret = 0;
839dbacaf56SToomas Soome 			}
840dbacaf56SToomas Soome 		} else {
841dbacaf56SToomas Soome 			if ((ret = pager_output("\n")) != 0)
842dbacaf56SToomas Soome 				break;
843199767f8SToomas Soome 		}
844199767f8SToomas Soome 	}
845199767f8SToom