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