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