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