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