]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - stand/efi/libefi/efipart.c
THIS BRANCH IS OBSOLETE, PLEASE READ:
[FreeBSD/FreeBSD.git] / stand / efi / libefi / efipart.c
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 = NULL
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 = NULL
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 = NULL
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 *
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 *
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 *
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
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 *
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
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 *
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
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
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", EFI_ERROR_CODE(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 *
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 *
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
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
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
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
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
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
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
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
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
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
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
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
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
865 efipart_printfd(int verbose)
866 {
867         return (efipart_print_common(&efipart_fddev, &fdinfo, verbose));
868 }
869
870 static int
871 efipart_printcd(int verbose)
872 {
873         return (efipart_print_common(&efipart_cddev, &cdinfo, verbose));
874 }
875
876 static int
877 efipart_printhd(int verbose)
878 {
879         return (efipart_print_common(&efipart_hddev, &hdinfo, verbose));
880 }
881
882 static int
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
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                 bcache_free(pd->pd_bcache);
953                 pd->pd_bcache = NULL;
954         }
955         if (dev->dd.d_dev->dv_type == DEVT_DISK)
956                 return (disk_close(dev));
957         return (0);
958 }
959
960 static int
961 efipart_ioctl(struct open_file *f, u_long cmd, void *data)
962 {
963         struct disk_devdesc *dev;
964         pdinfo_t *pd;
965         int rc;
966
967         dev = (struct disk_devdesc *)(f->f_devdata);
968         if (dev == NULL)
969                 return (EINVAL);
970
971         pd = efiblk_get_pdinfo((struct devdesc *)dev);
972         if (pd == NULL)
973                 return (EINVAL);
974
975         if (dev->dd.d_dev->dv_type == DEVT_DISK) {
976                 rc = disk_ioctl(dev, cmd, data);
977                 if (rc != ENOTTY)
978                         return (rc);
979         }
980
981         switch (cmd) {
982         case DIOCGSECTORSIZE:
983                 *(u_int *)data = pd->pd_blkio->Media->BlockSize;
984                 break;
985         case DIOCGMEDIASIZE:
986                 *(uint64_t *)data = pd->pd_blkio->Media->BlockSize *
987                     (pd->pd_blkio->Media->LastBlock + 1);
988                 break;
989         default:
990                 return (ENOTTY);
991         }
992
993         return (0);
994 }
995
996 /*
997  * efipart_readwrite()
998  * Internal equivalent of efipart_strategy(), which operates on the
999  * media-native block size. This function expects all I/O requests
1000  * to be within the media size and returns an error if such is not
1001  * the case.
1002  */
1003 static int
1004 efipart_readwrite(EFI_BLOCK_IO *blkio, int rw, daddr_t blk, daddr_t nblks,
1005     char *buf)
1006 {
1007         EFI_STATUS status;
1008
1009         if (blkio == NULL)
1010                 return (ENXIO);
1011         if (blk < 0 || blk > blkio->Media->LastBlock)
1012                 return (EIO);
1013         if ((blk + nblks - 1) > blkio->Media->LastBlock)
1014                 return (EIO);
1015
1016         switch (rw & F_MASK) {
1017         case F_READ:
1018                 status = blkio->ReadBlocks(blkio, blkio->Media->MediaId, blk,
1019                     nblks * blkio->Media->BlockSize, buf);
1020                 break;
1021         case F_WRITE:
1022                 if (blkio->Media->ReadOnly)
1023                         return (EROFS);
1024                 status = blkio->WriteBlocks(blkio, blkio->Media->MediaId, blk,
1025                     nblks * blkio->Media->BlockSize, buf);
1026                 break;
1027         default:
1028                 return (ENOSYS);
1029         }
1030
1031         if (EFI_ERROR(status)) {
1032                 printf("%s: rw=%d, blk=%ju size=%ju status=%lu\n", __func__, rw,
1033                     blk, nblks, EFI_ERROR_CODE(status));
1034         }
1035         return (efi_status_to_errno(status));
1036 }
1037
1038 static int
1039 efipart_strategy(void *devdata, int rw, daddr_t blk, size_t size,
1040     char *buf, size_t *rsize)
1041 {
1042         struct bcache_devdata bcd;
1043         struct disk_devdesc *dev;
1044         pdinfo_t *pd;
1045
1046         dev = (struct disk_devdesc *)devdata;
1047         if (dev == NULL)
1048                 return (EINVAL);
1049
1050         pd = efiblk_get_pdinfo((struct devdesc *)dev);
1051         if (pd == NULL)
1052                 return (EINVAL);
1053
1054         if (pd->pd_blkio->Media->RemovableMedia &&
1055             !pd->pd_blkio->Media->MediaPresent)
1056                 return (ENXIO);
1057
1058         bcd.dv_strategy = efipart_realstrategy;
1059         bcd.dv_devdata = devdata;
1060         bcd.dv_cache = pd->pd_bcache;
1061
1062         if (dev->dd.d_dev->dv_type == DEVT_DISK) {
1063                 daddr_t offset;
1064
1065                 offset = dev->d_offset * pd->pd_blkio->Media->BlockSize;
1066                 offset /= 512;
1067                 return (bcache_strategy(&bcd, rw, blk + offset,
1068                     size, buf, rsize));
1069         }
1070         return (bcache_strategy(&bcd, rw, blk, size, buf, rsize));
1071 }
1072
1073 static int
1074 efipart_realstrategy(void *devdata, int rw, daddr_t blk, size_t size,
1075     char *buf, size_t *rsize)
1076 {
1077         struct disk_devdesc *dev = (struct disk_devdesc *)devdata;
1078         pdinfo_t *pd;
1079         EFI_BLOCK_IO *blkio;
1080         uint64_t off, disk_blocks, d_offset = 0;
1081         char *blkbuf;
1082         size_t blkoff, blksz, bio_size;
1083         unsigned ioalign;
1084         bool need_buf;
1085         int rc;
1086         uint64_t diskend, readstart;
1087
1088         if (dev == NULL || blk < 0)
1089                 return (EINVAL);
1090
1091         pd = efiblk_get_pdinfo((struct devdesc *)dev);
1092         if (pd == NULL)
1093                 return (EINVAL);
1094
1095         blkio = pd->pd_blkio;
1096         if (blkio == NULL)
1097                 return (ENXIO);
1098
1099         if (size == 0 || (size % 512) != 0)
1100                 return (EIO);
1101
1102         off = blk * 512;
1103         /*
1104          * Get disk blocks, this value is either for whole disk or for
1105          * partition.
1106          */
1107         disk_blocks = 0;
1108         if (dev->dd.d_dev->dv_type == DEVT_DISK) {
1109                 if (disk_ioctl(dev, DIOCGMEDIASIZE, &disk_blocks) == 0) {
1110                         /* DIOCGMEDIASIZE does return bytes. */
1111                         disk_blocks /= blkio->Media->BlockSize;
1112                 }
1113                 d_offset = dev->d_offset;
1114         }
1115         if (disk_blocks == 0)
1116                 disk_blocks = blkio->Media->LastBlock + 1 - d_offset;
1117
1118         /* make sure we don't read past disk end */
1119         if ((off + size) / blkio->Media->BlockSize > d_offset + disk_blocks) {
1120                 diskend = d_offset + disk_blocks;
1121                 readstart = off / blkio->Media->BlockSize;
1122
1123                 if (diskend <= readstart) {
1124                         if (rsize != NULL)
1125                                 *rsize = 0;
1126
1127                         return (EIO);
1128                 }
1129                 size = diskend - readstart;
1130                 size = size * blkio->Media->BlockSize;
1131         }
1132
1133         need_buf = true;
1134         /* Do we need bounce buffer? */
1135         if ((size % blkio->Media->BlockSize == 0) &&
1136             (off % blkio->Media->BlockSize == 0))
1137                 need_buf = false;
1138
1139         /* Do we have IO alignment requirement? */
1140         ioalign = blkio->Media->IoAlign;
1141         if (ioalign == 0)
1142                 ioalign++;
1143
1144         if (ioalign > 1 && (uintptr_t)buf != roundup2((uintptr_t)buf, ioalign))
1145                 need_buf = true;
1146
1147         if (need_buf) {
1148                 for (bio_size = BIO_BUFFER_SIZE; bio_size > 0;
1149                     bio_size -= blkio->Media->BlockSize) {
1150                         blkbuf = memalign(ioalign, bio_size);
1151                         if (blkbuf != NULL)
1152                                 break;
1153                 }
1154         } else {
1155                 blkbuf = buf;
1156                 bio_size = size;
1157         }
1158
1159         if (blkbuf == NULL)
1160                 return (ENOMEM);
1161
1162         if (rsize != NULL)
1163                 *rsize = size;
1164
1165         rc = 0;
1166         blk = off / blkio->Media->BlockSize;
1167         blkoff = off % blkio->Media->BlockSize;
1168
1169         while (size > 0) {
1170                 size_t x = min(size, bio_size);
1171
1172                 if (x < blkio->Media->BlockSize)
1173                         x = 1;
1174                 else
1175                         x /= blkio->Media->BlockSize;
1176
1177                 switch (rw & F_MASK) {
1178                 case F_READ:
1179                         blksz = blkio->Media->BlockSize * x - blkoff;
1180                         if (size < blksz)
1181                                 blksz = size;
1182
1183                         rc = efipart_readwrite(blkio, rw, blk, x, blkbuf);
1184                         if (rc != 0)
1185                                 goto error;
1186
1187                         if (need_buf)
1188                                 bcopy(blkbuf + blkoff, buf, blksz);
1189                         break;
1190                 case F_WRITE:
1191                         rc = 0;
1192                         if (blkoff != 0) {
1193                                 /*
1194                                  * We got offset to sector, read 1 sector to
1195                                  * blkbuf.
1196                                  */
1197                                 x = 1;
1198                                 blksz = blkio->Media->BlockSize - blkoff;
1199                                 blksz = min(blksz, size);
1200                                 rc = efipart_readwrite(blkio, F_READ, blk, x,
1201                                     blkbuf);
1202                         } else if (size < blkio->Media->BlockSize) {
1203                                 /*
1204                                  * The remaining block is not full
1205                                  * sector. Read 1 sector to blkbuf.
1206                                  */
1207                                 x = 1;
1208                                 blksz = size;
1209                                 rc = efipart_readwrite(blkio, F_READ, blk, x,
1210                                     blkbuf);
1211                         } else {
1212                                 /* We can write full sector(s). */
1213                                 blksz = blkio->Media->BlockSize * x;
1214                         }
1215
1216                         if (rc != 0)
1217                                 goto error;
1218                         /*
1219                          * Put your Data In, Put your Data out,
1220                          * Put your Data In, and shake it all about
1221                          */
1222                         if (need_buf)
1223                                 bcopy(buf, blkbuf + blkoff, blksz);
1224                         rc = efipart_readwrite(blkio, F_WRITE, blk, x, blkbuf);
1225                         if (rc != 0)
1226                                 goto error;
1227                         break;
1228                 default:
1229                         /* DO NOTHING */
1230                         rc = EROFS;
1231                         goto error;
1232                 }
1233
1234                 blkoff = 0;
1235                 buf += blksz;
1236                 size -= blksz;
1237                 blk += x;
1238         }
1239
1240 error:
1241         if (rsize != NULL)
1242                 *rsize -= size;
1243
1244         if (need_buf)
1245                 free(blkbuf);
1246         return (rc);
1247 }