]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - stand/efi/libefi/efipart.c
loader.efi: efipart needs to use ioalign
[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;
317
318         STAILQ_FOREACH(pd, pdi, pd_link) {
319                 if (efi_devpath_is_prefix(pd->pd_devpath, devpath))
320                         return (pd);
321         }
322         return (NULL);
323 }
324
325 static int
326 efipart_initfd(void)
327 {
328         EFI_DEVICE_PATH *node;
329         ACPI_HID_DEVICE_PATH *acpi;
330         pdinfo_t *parent, *fd;
331
332 restart:
333         STAILQ_FOREACH(fd, &pdinfo, pd_link) {
334                 if ((node = efi_devpath_last_node(fd->pd_devpath)) == NULL)
335                         continue;
336
337                 if ((acpi = efipart_floppy(node)) == NULL)
338                         continue;
339
340                 STAILQ_REMOVE(&pdinfo, fd, pdinfo, pd_link);
341                 parent = efipart_find_parent(&pdinfo, fd->pd_devpath);
342                 if (parent != NULL) {
343                         STAILQ_REMOVE(&pdinfo, parent, pdinfo, pd_link);
344                         parent->pd_alias = fd->pd_handle;
345                         parent->pd_unit = acpi->UID;
346                         free(fd);
347                         fd = parent;
348                 } else {
349                         fd->pd_unit = acpi->UID;
350                 }
351                 fd->pd_devsw = &efipart_fddev;
352                 STAILQ_INSERT_TAIL(&fdinfo, fd, pd_link);
353                 goto restart;
354         }
355
356         bcache_add_dev(efiblk_pdinfo_count(&fdinfo));
357         return (0);
358 }
359
360 /*
361  * Add or update entries with new handle data.
362  */
363 static void
364 efipart_cdinfo_add(pdinfo_t *cd)
365 {
366         pdinfo_t *pd, *last;
367
368         STAILQ_FOREACH(pd, &cdinfo, pd_link) {
369                 if (efi_devpath_is_prefix(pd->pd_devpath, cd->pd_devpath)) {
370                         last = STAILQ_LAST(&pd->pd_part, pdinfo, pd_link);
371                         if (last != NULL)
372                                 cd->pd_unit = last->pd_unit + 1;
373                         else
374                                 cd->pd_unit = 0;
375                         cd->pd_parent = pd;
376                         cd->pd_devsw = &efipart_cddev;
377                         STAILQ_INSERT_TAIL(&pd->pd_part, cd, pd_link);
378                         return;
379                 }
380         }
381
382         last = STAILQ_LAST(&cdinfo, pdinfo, pd_link);
383         if (last != NULL)
384                 cd->pd_unit = last->pd_unit + 1;
385         else
386                 cd->pd_unit = 0;
387
388         cd->pd_parent = NULL;
389         cd->pd_devsw = &efipart_cddev;
390         STAILQ_INSERT_TAIL(&cdinfo, cd, pd_link);
391 }
392
393 static bool
394 efipart_testcd(EFI_DEVICE_PATH *node, EFI_BLOCK_IO *blkio)
395 {
396         if (DevicePathType(node) == MEDIA_DEVICE_PATH &&
397             DevicePathSubType(node) == MEDIA_CDROM_DP) {
398                 return (true);
399         }
400
401         /* cd drive without the media. */
402         if (blkio->Media->RemovableMedia &&
403             !blkio->Media->MediaPresent) {
404                 return (true);
405         }
406
407         return (false);
408 }
409
410 static void
411 efipart_updatecd(void)
412 {
413         EFI_DEVICE_PATH *devpath, *node;
414         EFI_STATUS status;
415         pdinfo_t *parent, *cd;
416
417 restart:
418         STAILQ_FOREACH(cd, &pdinfo, pd_link) {
419                 if ((node = efi_devpath_last_node(cd->pd_devpath)) == NULL)
420                         continue;
421
422                 if (efipart_floppy(node) != NULL)
423                         continue;
424
425                 /* Is parent of this device already registered? */
426                 parent = efipart_find_parent(&cdinfo, cd->pd_devpath);
427                 if (parent != NULL) {
428                         STAILQ_REMOVE(&pdinfo, cd, pdinfo, pd_link);
429                         efipart_cdinfo_add(cd);
430                         goto restart;
431                 }
432
433                 if (!efipart_testcd(node, cd->pd_blkio))
434                         continue;
435
436                 /* Find parent and unlink both parent and cd from pdinfo */
437                 STAILQ_REMOVE(&pdinfo, cd, pdinfo, pd_link);
438                 parent = efipart_find_parent(&pdinfo, cd->pd_devpath);
439                 if (parent != NULL) {
440                         STAILQ_REMOVE(&pdinfo, parent, pdinfo, pd_link);
441                         efipart_cdinfo_add(parent);
442                 }
443
444                 if (parent == NULL)
445                         parent = efipart_find_parent(&cdinfo, cd->pd_devpath);
446
447                 /*
448                  * If we come across a logical partition of subtype CDROM
449                  * it doesn't refer to the CD filesystem itself, but rather
450                  * to any usable El Torito boot image on it. In this case
451                  * we try to find the parent device and add that instead as
452                  * that will be the CD filesystem.
453                  */
454                 if (DevicePathType(node) == MEDIA_DEVICE_PATH &&
455                     DevicePathSubType(node) == MEDIA_CDROM_DP &&
456                     parent == NULL) {
457                         parent = calloc(1, sizeof(*parent));
458                         if (parent == NULL) {
459                                 printf("efipart_updatecd: out of memory\n");
460                                 /* this device is lost but try again. */
461                                 free(cd);
462                                 goto restart;
463                         }
464
465                         devpath = efi_devpath_trim(cd->pd_devpath);
466                         if (devpath == NULL) {
467                                 printf("efipart_updatecd: out of memory\n");
468                                 /* this device is lost but try again. */
469                                 free(parent);
470                                 free(cd);
471                                 goto restart;
472                         }
473                         parent->pd_devpath = devpath;
474                         status = BS->LocateDevicePath(&blkio_guid,
475                             &parent->pd_devpath, &parent->pd_handle);
476                         free(devpath);
477                         if (EFI_ERROR(status)) {
478                                 printf("efipart_updatecd: error %lu\n",
479                                     EFI_ERROR_CODE(status));
480                                 free(parent);
481                                 free(cd);
482                                 goto restart;
483                         }
484                         parent->pd_devpath =
485                             efi_lookup_devpath(parent->pd_handle);
486                         efipart_cdinfo_add(parent);
487                 }
488
489                 efipart_cdinfo_add(cd);
490                 goto restart;
491         }
492 }
493
494 static int
495 efipart_initcd(void)
496 {
497         efipart_updatecd();
498
499         bcache_add_dev(efiblk_pdinfo_count(&cdinfo));
500         return (0);
501 }
502
503 static void
504 efipart_hdinfo_add(pdinfo_t *hd, HARDDRIVE_DEVICE_PATH *node)
505 {
506         pdinfo_t *pd, *last;
507
508         STAILQ_FOREACH(pd, &hdinfo, pd_link) {
509                 if (efi_devpath_is_prefix(pd->pd_devpath, hd->pd_devpath)) {
510                         /* Add the partition. */
511                         hd->pd_unit = node->PartitionNumber;
512                         hd->pd_parent = pd;
513                         hd->pd_devsw = &efipart_hddev;
514                         STAILQ_INSERT_TAIL(&pd->pd_part, hd, pd_link);
515                         return;
516                 }
517         }
518
519         last = STAILQ_LAST(&hdinfo, pdinfo, pd_link);
520         if (last != NULL)
521                 hd->pd_unit = last->pd_unit + 1;
522         else
523                 hd->pd_unit = 0;
524
525         /* Add the disk. */
526         hd->pd_devsw = &efipart_hddev;
527         STAILQ_INSERT_TAIL(&hdinfo, hd, pd_link);
528 }
529
530 /*
531  * The MEDIA_FILEPATH_DP has device name.
532  * From U-Boot sources it looks like names are in the form
533  * of typeN:M, where type is interface type, N is disk id
534  * and M is partition id.
535  */
536 static void
537 efipart_hdinfo_add_filepath(pdinfo_t *hd, FILEPATH_DEVICE_PATH *node)
538 {
539         char *pathname, *p;
540         int len;
541         pdinfo_t *last;
542
543         last = STAILQ_LAST(&hdinfo, pdinfo, pd_link);
544         if (last != NULL)
545                 hd->pd_unit = last->pd_unit + 1;
546         else
547                 hd->pd_unit = 0;
548
549         /* FILEPATH_DEVICE_PATH has 0 terminated string */
550         len = ucs2len(node->PathName);
551         if ((pathname = malloc(len + 1)) == NULL) {
552                 printf("Failed to add disk, out of memory\n");
553                 free(hd);
554                 return;
555         }
556         cpy16to8(node->PathName, pathname, len + 1);
557         p = strchr(pathname, ':');
558
559         /*
560          * Assume we are receiving handles in order, first disk handle,
561          * then partitions for this disk. If this assumption proves
562          * false, this code would need update.
563          */
564         if (p == NULL) {        /* no colon, add the disk */
565                 hd->pd_devsw = &efipart_hddev;
566                 STAILQ_INSERT_TAIL(&hdinfo, hd, pd_link);
567                 free(pathname);
568                 return;
569         }
570         p++;    /* skip the colon */
571         errno = 0;
572         hd->pd_unit = (int)strtol(p, NULL, 0);
573         if (errno != 0) {
574                 printf("Bad unit number for partition \"%s\"\n", pathname);
575                 free(pathname);
576                 free(hd);
577                 return;
578         }
579
580         /*
581          * We should have disk registered, if not, we are receiving
582          * handles out of order, and this code should be reworked
583          * to create "blank" disk for partition, and to find the
584          * disk based on PathName compares.
585          */
586         if (last == NULL) {
587                 printf("BUG: No disk for partition \"%s\"\n", pathname);
588                 free(pathname);
589                 free(hd);
590                 return;
591         }
592         /* Add the partition. */
593         hd->pd_parent = last;
594         hd->pd_devsw = &efipart_hddev;
595         STAILQ_INSERT_TAIL(&last->pd_part, hd, pd_link);
596         free(pathname);
597 }
598
599 static void
600 efipart_updatehd(void)
601 {
602         EFI_DEVICE_PATH *devpath, *node;
603         EFI_STATUS status;
604         pdinfo_t *parent, *hd;
605
606 restart:
607         STAILQ_FOREACH(hd, &pdinfo, pd_link) {
608                 if ((node = efi_devpath_last_node(hd->pd_devpath)) == NULL)
609                         continue;
610
611                 if (efipart_floppy(node) != NULL)
612                         continue;
613
614                 if (efipart_testcd(node, hd->pd_blkio))
615                         continue;
616
617                 if (DevicePathType(node) == HARDWARE_DEVICE_PATH &&
618                     (DevicePathSubType(node) == HW_PCI_DP ||
619                      DevicePathSubType(node) == HW_VENDOR_DP)) {
620                         STAILQ_REMOVE(&pdinfo, hd, pdinfo, pd_link);
621                         efipart_hdinfo_add(hd, NULL);
622                         goto restart;
623                 }
624
625                 if (DevicePathType(node) == MEDIA_DEVICE_PATH &&
626                     DevicePathSubType(node) == MEDIA_FILEPATH_DP) {
627                         STAILQ_REMOVE(&pdinfo, hd, pdinfo, pd_link);
628                         efipart_hdinfo_add_filepath(hd,
629                             (FILEPATH_DEVICE_PATH *)node);
630                         goto restart;
631                 }
632
633                 STAILQ_REMOVE(&pdinfo, hd, pdinfo, pd_link);
634                 parent = efipart_find_parent(&pdinfo, hd->pd_devpath);
635                 if (parent != NULL) {
636                         STAILQ_REMOVE(&pdinfo, parent, pdinfo, pd_link);
637                         efipart_hdinfo_add(parent, NULL);
638                 } else {
639                         parent = efipart_find_parent(&hdinfo, hd->pd_devpath);
640                 }
641
642                 if (DevicePathType(node) == MEDIA_DEVICE_PATH &&
643                     DevicePathSubType(node) == MEDIA_HARDDRIVE_DP &&
644                     parent == NULL) {
645                         parent = calloc(1, sizeof(*parent));
646                         if (parent == NULL) {
647                                 printf("efipart_updatehd: out of memory\n");
648                                 /* this device is lost but try again. */
649                                 free(hd);
650                                 goto restart;
651                         }
652
653                         devpath = efi_devpath_trim(hd->pd_devpath);
654                         if (devpath == NULL) {
655                                 printf("efipart_updatehd: out of memory\n");
656                                 /* this device is lost but try again. */
657                                 free(parent);
658                                 free(hd);
659                                 goto restart;
660                         }
661
662                         parent->pd_devpath = devpath;
663                         status = BS->LocateDevicePath(&blkio_guid,
664                             &parent->pd_devpath, &parent->pd_handle);
665                         free(devpath);
666                         if (EFI_ERROR(status)) {
667                                 printf("efipart_updatehd: error %lu\n",
668                                     EFI_ERROR_CODE(status));
669                                 free(parent);
670                                 free(hd);
671                                 goto restart;
672                         }
673
674                         parent->pd_devpath =
675                             efi_lookup_devpath(&parent->pd_handle);
676
677                         efipart_hdinfo_add(parent, NULL);
678                 }
679
680                 efipart_hdinfo_add(hd, (HARDDRIVE_DEVICE_PATH *)node);
681                 goto restart;
682         }
683 }
684
685 static int
686 efipart_inithd(void)
687 {
688
689         efipart_updatehd();
690
691         bcache_add_dev(efiblk_pdinfo_count(&hdinfo));
692         return (0);
693 }
694
695 static int
696 efipart_print_common(struct devsw *dev, pdinfo_list_t *pdlist, int verbose)
697 {
698         int ret = 0;
699         EFI_BLOCK_IO *blkio;
700         EFI_STATUS status;
701         EFI_HANDLE h;
702         pdinfo_t *pd;
703         CHAR16 *text;
704         struct disk_devdesc pd_dev;
705         char line[80];
706
707         if (STAILQ_EMPTY(pdlist))
708                 return (0);
709
710         printf("%s devices:", dev->dv_name);
711         if ((ret = pager_output("\n")) != 0)
712                 return (ret);
713
714         STAILQ_FOREACH(pd, pdlist, pd_link) {
715                 h = pd->pd_handle;
716                 if (verbose) {  /* Output the device path. */
717                         text = efi_devpath_name(efi_lookup_devpath(h));
718                         if (text != NULL) {
719                                 printf("  %S", text);
720                                 efi_free_devpath_name(text);
721                                 if ((ret = pager_output("\n")) != 0)
722                                         break;
723                         }
724                 }
725                 snprintf(line, sizeof(line),
726                     "    %s%d", dev->dv_name, pd->pd_unit);
727                 printf("%s:", line);
728                 status = OpenProtocolByHandle(h, &blkio_guid, (void **)&blkio);
729                 if (!EFI_ERROR(status)) {
730                         printf("    %llu",
731                             blkio->Media->LastBlock == 0? 0:
732                             (unsigned long long) (blkio->Media->LastBlock + 1));
733                         if (blkio->Media->LastBlock != 0) {
734                                 printf(" X %u", blkio->Media->BlockSize);
735                         }
736                         printf(" blocks");
737                         if (blkio->Media->MediaPresent) {
738                                 if (blkio->Media->RemovableMedia)
739                                         printf(" (removable)");
740                         } else {
741                                 printf(" (no media)");
742                         }
743                         if ((ret = pager_output("\n")) != 0)
744                                 break;
745                         if (!blkio->Media->MediaPresent)
746                                 continue;
747
748                         pd->pd_blkio = blkio;
749                         pd_dev.dd.d_dev = dev;
750                         pd_dev.dd.d_unit = pd->pd_unit;
751                         pd_dev.d_slice = -1;
752                         pd_dev.d_partition = -1;
753                         ret = disk_open(&pd_dev, blkio->Media->BlockSize *
754                             (blkio->Media->LastBlock + 1),
755                             blkio->Media->BlockSize);
756                         if (ret == 0) {
757                                 ret = disk_print(&pd_dev, line, verbose);
758                                 disk_close(&pd_dev);
759                                 if (ret != 0)
760                                         return (ret);
761                         } else {
762                                 /* Do not fail from disk_open() */
763                                 ret = 0;
764                         }
765                 } else {
766                         if ((ret = pager_output("\n")) != 0)
767                                 break;
768                 }
769         }
770         return (ret);
771 }
772
773 static int
774 efipart_printfd(int verbose)
775 {
776         return (efipart_print_common(&efipart_fddev, &fdinfo, verbose));
777 }
778
779 static int
780 efipart_printcd(int verbose)
781 {
782         return (efipart_print_common(&efipart_cddev, &cdinfo, verbose));
783 }
784
785 static int
786 efipart_printhd(int verbose)
787 {
788         return (efipart_print_common(&efipart_hddev, &hdinfo, verbose));
789 }
790
791 static int
792 efipart_open(struct open_file *f, ...)
793 {
794         va_list args;
795         struct disk_devdesc *dev;
796         pdinfo_t *pd;
797         EFI_BLOCK_IO *blkio;
798         EFI_STATUS status;
799
800         va_start(args, f);
801         dev = va_arg(args, struct disk_devdesc *);
802         va_end(args);
803         if (dev == NULL)
804                 return (EINVAL);
805
806         pd = efiblk_get_pdinfo((struct devdesc *)dev);
807         if (pd == NULL)
808                 return (EIO);
809
810         if (pd->pd_blkio == NULL) {
811                 status = OpenProtocolByHandle(pd->pd_handle, &blkio_guid,
812                     (void **)&pd->pd_blkio);
813                 if (EFI_ERROR(status))
814                         return (efi_status_to_errno(status));
815         }
816
817         blkio = pd->pd_blkio;
818         if (!blkio->Media->MediaPresent)
819                 return (EAGAIN);
820
821         pd->pd_open++;
822         if (pd->pd_bcache == NULL)
823                 pd->pd_bcache = bcache_allocate();
824
825         if (dev->dd.d_dev->dv_type == DEVT_DISK) {
826                 int rc;
827
828                 rc = disk_open(dev,
829                     blkio->Media->BlockSize * (blkio->Media->LastBlock + 1),
830                     blkio->Media->BlockSize);
831                 if (rc != 0) {
832                         pd->pd_open--;
833                         if (pd->pd_open == 0) {
834                                 pd->pd_blkio = NULL;
835                                 bcache_free(pd->pd_bcache);
836                                 pd->pd_bcache = NULL;
837                         }
838                 }
839                 return (rc);
840         }
841         return (0);
842 }
843
844 static int
845 efipart_close(struct open_file *f)
846 {
847         struct disk_devdesc *dev;
848         pdinfo_t *pd;
849
850         dev = (struct disk_devdesc *)(f->f_devdata);
851         if (dev == NULL)
852                 return (EINVAL);
853
854         pd = efiblk_get_pdinfo((struct devdesc *)dev);
855         if (pd == NULL)
856                 return (EINVAL);
857
858         pd->pd_open--;
859         if (pd->pd_open == 0) {
860                 pd->pd_blkio = NULL;
861                 bcache_free(pd->pd_bcache);
862                 pd->pd_bcache = NULL;
863         }
864         if (dev->dd.d_dev->dv_type == DEVT_DISK)
865                 return (disk_close(dev));
866         return (0);
867 }
868
869 static int
870 efipart_ioctl(struct open_file *f, u_long cmd, void *data)
871 {
872         struct disk_devdesc *dev;
873         pdinfo_t *pd;
874         int rc;
875
876         dev = (struct disk_devdesc *)(f->f_devdata);
877         if (dev == NULL)
878                 return (EINVAL);
879
880         pd = efiblk_get_pdinfo((struct devdesc *)dev);
881         if (pd == NULL)
882                 return (EINVAL);
883
884         if (dev->dd.d_dev->dv_type == DEVT_DISK) {
885                 rc = disk_ioctl(dev, cmd, data);
886                 if (rc != ENOTTY)
887                         return (rc);
888         }
889
890         switch (cmd) {
891         case DIOCGSECTORSIZE:
892                 *(u_int *)data = pd->pd_blkio->Media->BlockSize;
893                 break;
894         case DIOCGMEDIASIZE:
895                 *(uint64_t *)data = pd->pd_blkio->Media->BlockSize *
896                     (pd->pd_blkio->Media->LastBlock + 1);
897                 break;
898         default:
899                 return (ENOTTY);
900         }
901
902         return (0);
903 }
904
905 /*
906  * efipart_readwrite()
907  * Internal equivalent of efipart_strategy(), which operates on the
908  * media-native block size. This function expects all I/O requests
909  * to be within the media size and returns an error if such is not
910  * the case.
911  */
912 static int
913 efipart_readwrite(EFI_BLOCK_IO *blkio, int rw, daddr_t blk, daddr_t nblks,
914     char *buf)
915 {
916         EFI_STATUS status;
917
918         if (blkio == NULL)
919                 return (ENXIO);
920         if (blk < 0 || blk > blkio->Media->LastBlock)
921                 return (EIO);
922         if ((blk + nblks - 1) > blkio->Media->LastBlock)
923                 return (EIO);
924
925         switch (rw & F_MASK) {
926         case F_READ:
927                 status = blkio->ReadBlocks(blkio, blkio->Media->MediaId, blk,
928                     nblks * blkio->Media->BlockSize, buf);
929                 break;
930         case F_WRITE:
931                 if (blkio->Media->ReadOnly)
932                         return (EROFS);
933                 status = blkio->WriteBlocks(blkio, blkio->Media->MediaId, blk,
934                     nblks * blkio->Media->BlockSize, buf);
935                 break;
936         default:
937                 return (ENOSYS);
938         }
939
940         if (EFI_ERROR(status)) {
941                 printf("%s: rw=%d, blk=%ju size=%ju status=%lu\n", __func__, rw,
942                     blk, nblks, EFI_ERROR_CODE(status));
943         }
944         return (efi_status_to_errno(status));
945 }
946
947 static int
948 efipart_strategy(void *devdata, int rw, daddr_t blk, size_t size,
949     char *buf, size_t *rsize)
950 {
951         struct bcache_devdata bcd;
952         struct disk_devdesc *dev;
953         pdinfo_t *pd;
954
955         dev = (struct disk_devdesc *)devdata;
956         if (dev == NULL)
957                 return (EINVAL);
958
959         pd = efiblk_get_pdinfo((struct devdesc *)dev);
960         if (pd == NULL)
961                 return (EINVAL);
962
963         if (pd->pd_blkio->Media->RemovableMedia &&
964             !pd->pd_blkio->Media->MediaPresent)
965                 return (ENXIO);
966
967         bcd.dv_strategy = efipart_realstrategy;
968         bcd.dv_devdata = devdata;
969         bcd.dv_cache = pd->pd_bcache;
970
971         if (dev->dd.d_dev->dv_type == DEVT_DISK) {
972                 daddr_t offset;
973
974                 offset = dev->d_offset * pd->pd_blkio->Media->BlockSize;
975                 offset /= 512;
976                 return (bcache_strategy(&bcd, rw, blk + offset,
977                     size, buf, rsize));
978         }
979         return (bcache_strategy(&bcd, rw, blk, size, buf, rsize));
980 }
981
982 static int
983 efipart_realstrategy(void *devdata, int rw, daddr_t blk, size_t size,
984     char *buf, size_t *rsize)
985 {
986         struct disk_devdesc *dev = (struct disk_devdesc *)devdata;
987         pdinfo_t *pd;
988         EFI_BLOCK_IO *blkio;
989         uint64_t off, disk_blocks, d_offset = 0;
990         char *blkbuf;
991         size_t blkoff, blksz, bio_size;
992         unsigned ioalign;
993         bool need_buf;
994         int rc;
995         uint64_t diskend, readstart;
996
997         if (dev == NULL || blk < 0)
998                 return (EINVAL);
999
1000         pd = efiblk_get_pdinfo((struct devdesc *)dev);
1001         if (pd == NULL)
1002                 return (EINVAL);
1003
1004         blkio = pd->pd_blkio;
1005         if (blkio == NULL)
1006                 return (ENXIO);
1007
1008         if (size == 0 || (size % 512) != 0)
1009                 return (EIO);
1010
1011         off = blk * 512;
1012         /*
1013          * Get disk blocks, this value is either for whole disk or for
1014          * partition.
1015          */
1016         disk_blocks = 0;
1017         if (dev->dd.d_dev->dv_type == DEVT_DISK) {
1018                 if (disk_ioctl(dev, DIOCGMEDIASIZE, &disk_blocks) == 0) {
1019                         /* DIOCGMEDIASIZE does return bytes. */
1020                         disk_blocks /= blkio->Media->BlockSize;
1021                 }
1022                 d_offset = dev->d_offset;
1023         }
1024         if (disk_blocks == 0)
1025                 disk_blocks = blkio->Media->LastBlock + 1 - d_offset;
1026
1027         /* make sure we don't read past disk end */
1028         if ((off + size) / blkio->Media->BlockSize > d_offset + disk_blocks) {
1029                 diskend = d_offset + disk_blocks;
1030                 readstart = off / blkio->Media->BlockSize;
1031
1032                 if (diskend <= readstart) {
1033                         if (rsize != NULL)
1034                                 *rsize = 0;
1035
1036                         return (EIO);
1037                 }
1038                 size = diskend - readstart;
1039                 size = size * blkio->Media->BlockSize;
1040         }
1041
1042         need_buf = true;
1043         /* Do we need bounce buffer? */
1044         if ((size % blkio->Media->BlockSize == 0) &&
1045             (off % blkio->Media->BlockSize == 0))
1046                 need_buf = false;
1047
1048         /* Do we have IO alignment requirement? */
1049         ioalign = blkio->Media->IoAlign;
1050         if (ioalign == 0)
1051                 ioalign++;
1052
1053         if (ioalign > 1 && (uintptr_t)buf != roundup2((uintptr_t)buf, ioalign))
1054                 need_buf = true;
1055
1056         if (need_buf) {
1057                 for (bio_size = BIO_BUFFER_SIZE; bio_size > 0;
1058                     bio_size -= blkio->Media->BlockSize) {
1059                         blkbuf = memalign(ioalign, bio_size);
1060                         if (blkbuf != NULL)
1061                                 break;
1062                 }
1063         } else {
1064                 blkbuf = buf;
1065                 bio_size = size;
1066         }
1067
1068         if (blkbuf == NULL)
1069                 return (ENOMEM);
1070
1071         if (rsize != NULL)
1072                 *rsize = size;
1073
1074         rc = 0;
1075         blk = off / blkio->Media->BlockSize;
1076         blkoff = off % blkio->Media->BlockSize;
1077
1078         while (size > 0) {
1079                 size_t x = min(size, bio_size);
1080
1081                 if (x < blkio->Media->BlockSize)
1082                         x = 1;
1083                 else
1084                         x /= blkio->Media->BlockSize;
1085
1086                 switch (rw & F_MASK) {
1087                 case F_READ:
1088                         blksz = blkio->Media->BlockSize * x - blkoff;
1089                         if (size < blksz)
1090                                 blksz = size;
1091
1092                         rc = efipart_readwrite(blkio, rw, blk, x, blkbuf);
1093                         if (rc != 0)
1094                                 goto error;
1095
1096                         if (need_buf)
1097                                 bcopy(blkbuf + blkoff, buf, blksz);
1098                         break;
1099                 case F_WRITE:
1100                         rc = 0;
1101                         if (blkoff != 0) {
1102                                 /*
1103                                  * We got offset to sector, read 1 sector to
1104                                  * blkbuf.
1105                                  */
1106                                 x = 1;
1107                                 blksz = blkio->Media->BlockSize - blkoff;
1108                                 blksz = min(blksz, size);
1109                                 rc = efipart_readwrite(blkio, F_READ, blk, x,
1110                                     blkbuf);
1111                         } else if (size < blkio->Media->BlockSize) {
1112                                 /*
1113                                  * The remaining block is not full
1114                                  * sector. Read 1 sector to blkbuf.
1115                                  */
1116                                 x = 1;
1117                                 blksz = size;
1118                                 rc = efipart_readwrite(blkio, F_READ, blk, x,
1119                                     blkbuf);
1120                         } else {
1121                                 /* We can write full sector(s). */
1122                                 blksz = blkio->Media->BlockSize * x;
1123                         }
1124
1125                         if (rc != 0)
1126                                 goto error;
1127                         /*
1128                          * Put your Data In, Put your Data out,
1129                          * Put your Data In, and shake it all about
1130                          */
1131                         if (need_buf)
1132                                 bcopy(buf, blkbuf + blkoff, blksz);
1133                         rc = efipart_readwrite(blkio, F_WRITE, blk, x, blkbuf);
1134                         if (rc != 0)
1135                                 goto error;
1136                         break;
1137                 default:
1138                         /* DO NOTHING */
1139                         rc = EROFS;
1140                         goto error;
1141                 }
1142
1143                 blkoff = 0;
1144                 buf += blksz;
1145                 size -= blksz;
1146                 blk += x;
1147         }
1148
1149 error:
1150         if (rsize != NULL)
1151                 *rsize -= size;
1152
1153         if (need_buf)
1154                 free(blkbuf);
1155         return (rc);
1156 }