]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - stand/efi/libefi/efipart.c
Upgrade our copies of clang, llvm, lld, lldb, compiler-rt and libc++ to
[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 struct devsw efipart_fddev = {
68         .dv_name = "fd",
69         .dv_type = DEVT_FD,
70         .dv_init = efipart_initfd,
71         .dv_strategy = efipart_strategy,
72         .dv_open = efipart_open,
73         .dv_close = efipart_close,
74         .dv_ioctl = efipart_ioctl,
75         .dv_print = efipart_printfd,
76         .dv_cleanup = NULL
77 };
78
79 struct devsw efipart_cddev = {
80         .dv_name = "cd",
81         .dv_type = DEVT_CD,
82         .dv_init = efipart_initcd,
83         .dv_strategy = efipart_strategy,
84         .dv_open = efipart_open,
85         .dv_close = efipart_close,
86         .dv_ioctl = efipart_ioctl,
87         .dv_print = efipart_printcd,
88         .dv_cleanup = NULL
89 };
90
91 struct devsw efipart_hddev = {
92         .dv_name = "disk",
93         .dv_type = DEVT_DISK,
94         .dv_init = efipart_inithd,
95         .dv_strategy = efipart_strategy,
96         .dv_open = efipart_open,
97         .dv_close = efipart_close,
98         .dv_ioctl = efipart_ioctl,
99         .dv_print = efipart_printhd,
100         .dv_cleanup = NULL
101 };
102
103 static pdinfo_list_t fdinfo;
104 static pdinfo_list_t cdinfo;
105 static pdinfo_list_t hdinfo;
106
107 static EFI_HANDLE *efipart_handles = NULL;
108 static UINTN efipart_nhandles = 0;
109
110 pdinfo_list_t *
111 efiblk_get_pdinfo_list(struct devsw *dev)
112 {
113         if (dev->dv_type == DEVT_DISK)
114                 return (&hdinfo);
115         if (dev->dv_type == DEVT_CD)
116                 return (&cdinfo);
117         if (dev->dv_type == DEVT_FD)
118                 return (&fdinfo);
119         return (NULL);
120 }
121
122 pdinfo_t *
123 efiblk_get_pdinfo(struct devdesc *dev)
124 {
125         pdinfo_list_t *pdi;
126         pdinfo_t *pd = NULL;
127
128         pdi = efiblk_get_pdinfo_list(dev->d_dev);
129         if (pdi == NULL)
130                 return (pd);
131
132         STAILQ_FOREACH(pd, pdi, pd_link) {
133                 if (pd->pd_unit == dev->d_unit)
134                         return (pd);
135         }
136         return (pd);
137 }
138
139 static int
140 efiblk_pdinfo_count(pdinfo_list_t *pdi)
141 {
142         pdinfo_t *pd;
143         int i = 0;
144
145         STAILQ_FOREACH(pd, pdi, pd_link) {
146                 i++;
147         }
148         return (i);
149 }
150
151 static int
152 efipart_inithandles(void)
153 {
154         UINTN sz;
155         EFI_HANDLE *hin;
156         EFI_STATUS status;
157
158         if (efipart_nhandles != 0) {
159                 free(efipart_handles);
160                 efipart_handles = NULL;
161                 efipart_nhandles = 0;
162         }
163
164         sz = 0;
165         hin = NULL;
166         status = BS->LocateHandle(ByProtocol, &blkio_guid, 0, &sz, hin);
167         if (status == EFI_BUFFER_TOO_SMALL) {
168                 hin = malloc(sz);
169                 status = BS->LocateHandle(ByProtocol, &blkio_guid, 0, &sz,
170                     hin);
171                 if (EFI_ERROR(status))
172                         free(hin);
173         }
174         if (EFI_ERROR(status))
175                 return (efi_status_to_errno(status));
176
177         efipart_handles = hin;
178         efipart_nhandles = sz;
179         return (0);
180 }
181
182 static ACPI_HID_DEVICE_PATH *
183 efipart_floppy(EFI_DEVICE_PATH *node)
184 {
185         ACPI_HID_DEVICE_PATH *acpi;
186
187         if (DevicePathType(node) == ACPI_DEVICE_PATH &&
188             DevicePathSubType(node) == ACPI_DP) {
189                 acpi = (ACPI_HID_DEVICE_PATH *) node;
190                 if (acpi->HID == EISA_PNP_ID(PNP0604) ||
191                     acpi->HID == EISA_PNP_ID(PNP0700) ||
192                     acpi->HID == EISA_PNP_ID(PNP0701)) {
193                         return (acpi);
194                 }
195         }
196         return (NULL);
197 }
198
199 /*
200  * Determine if the provided device path is hdd.
201  *
202  * There really is no simple fool proof way to classify the devices.
203  * Since we do build three lists of devices - floppy, cd and hdd, we
204  * will try to see  if the device is floppy or cd, and list anything else
205  * as hdd.
206  */
207 static bool
208 efipart_hdd(EFI_DEVICE_PATH *dp)
209 {
210         unsigned i, nin;
211         EFI_DEVICE_PATH *devpath, *node;
212         EFI_BLOCK_IO *blkio;
213         EFI_STATUS status;
214
215         if (dp == NULL)
216                 return (false);
217
218         if ((node = efi_devpath_last_node(dp)) == NULL)
219                 return (false);
220
221         if (efipart_floppy(node) != NULL)
222                 return (false);
223
224         /*
225          * Test every EFI BLOCK IO handle to make sure dp is not device path
226          * for CD/DVD.
227          */
228         nin = efipart_nhandles / sizeof (*efipart_handles);
229         for (i = 0; i < nin; i++) {
230                 devpath = efi_lookup_devpath(efipart_handles[i]);
231                 if (devpath == NULL)
232                         return (false);
233
234                 /* Only continue testing when dp is prefix in devpath. */
235                 if (!efi_devpath_is_prefix(dp, devpath))
236                         continue;
237
238                 /*
239                  * The device path has to have last node describing the
240                  *  device, or we can not test the type.
241                  */
242                 if ((node = efi_devpath_last_node(devpath)) == NULL)
243                         return (false);
244
245                 if (DevicePathType(node) == MEDIA_DEVICE_PATH &&
246                     DevicePathSubType(node) == MEDIA_CDROM_DP) {
247                         return (false);
248                 }
249
250                 /* Make sure we do have the media. */
251                 status = BS->HandleProtocol(efipart_handles[i],
252                     &blkio_guid, (void **)&blkio);
253                 if (EFI_ERROR(status))
254                         return (false);
255
256                 /* USB or SATA cd without the media. */
257                 if (blkio->Media->RemovableMedia &&
258                     !blkio->Media->MediaPresent) {
259                         return (false);
260                 }
261
262                 /*
263                  * We assume the block size 512 or greater power of 2. 
264                  * iPXE is known to insert stub BLOCK IO device with
265                  * BlockSize 1.
266                  */
267                 if (blkio->Media->BlockSize < 512 ||
268                     !powerof2(blkio->Media->BlockSize)) {
269                         return (false);
270                 }
271         }
272         return (true);
273 }
274
275 /*
276  * Add or update entries with new handle data.
277  */
278 static int
279 efipart_fdinfo_add(EFI_HANDLE handle, uint32_t uid, EFI_DEVICE_PATH *devpath)
280 {
281         pdinfo_t *fd;
282
283         fd = calloc(1, sizeof(pdinfo_t));
284         if (fd == NULL) {
285                 printf("Failed to register floppy %d, out of memory\n", uid);
286                 return (ENOMEM);
287         }
288         STAILQ_INIT(&fd->pd_part);
289
290         fd->pd_unit = uid;
291         fd->pd_handle = handle;
292         fd->pd_devpath = devpath;
293         STAILQ_INSERT_TAIL(&fdinfo, fd, pd_link);
294         return (0);
295 }
296
297 static void
298 efipart_updatefd(void)
299 {
300         EFI_DEVICE_PATH *devpath, *node;
301         ACPI_HID_DEVICE_PATH *acpi;
302         int i, nin;
303
304         nin = efipart_nhandles / sizeof (*efipart_handles);
305         for (i = 0; i < nin; i++) {
306                 devpath = efi_lookup_devpath(efipart_handles[i]);
307                 if (devpath == NULL)
308                         continue;
309
310                 if ((node = efi_devpath_last_node(devpath)) == NULL)
311                         continue;
312                 if ((acpi = efipart_floppy(node)) != NULL) {
313                         efipart_fdinfo_add(efipart_handles[i], acpi->UID,
314                             devpath);
315                 }
316         }
317 }
318
319 static int
320 efipart_initfd(void)
321 {
322         int rv;
323
324         rv = efipart_inithandles();
325         if (rv != 0)
326                 return (rv);
327         STAILQ_INIT(&fdinfo);
328
329         efipart_updatefd();
330
331         bcache_add_dev(efiblk_pdinfo_count(&fdinfo));
332         return (0);
333 }
334
335 /*
336  * Add or update entries with new handle data.
337  */
338 static int
339 efipart_cdinfo_add(EFI_HANDLE handle, EFI_HANDLE alias,
340     EFI_DEVICE_PATH *devpath)
341 {
342         int unit;
343         pdinfo_t *cd;
344         pdinfo_t *pd;
345
346         unit = 0;
347         STAILQ_FOREACH(pd, &cdinfo, pd_link) {
348                 if (efi_devpath_match(pd->pd_devpath, devpath) == true) {
349                         pd->pd_handle = handle;
350                         pd->pd_alias = alias;
351                         return (0);
352                 }
353                 unit++;
354         }
355
356         cd = calloc(1, sizeof(pdinfo_t));
357         if (cd == NULL) {
358                 printf("Failed to add cd %d, out of memory\n", unit);
359                 return (ENOMEM);
360         }
361         STAILQ_INIT(&cd->pd_part);
362
363         cd->pd_handle = handle;
364         cd->pd_unit = unit;
365         cd->pd_alias = alias;
366         cd->pd_devpath = devpath;
367         STAILQ_INSERT_TAIL(&cdinfo, cd, pd_link);
368         return (0);
369 }
370
371 static void
372 efipart_updatecd(void)
373 {
374         int i, nin;
375         EFI_DEVICE_PATH *devpath, *devpathcpy, *tmpdevpath, *node;
376         EFI_HANDLE handle;
377         EFI_BLOCK_IO *blkio;
378         EFI_STATUS status;
379
380         nin = efipart_nhandles / sizeof (*efipart_handles);
381         for (i = 0; i < nin; i++) {
382                 devpath = efi_lookup_devpath(efipart_handles[i]);
383                 if (devpath == NULL)
384                         continue;
385
386                 if ((node = efi_devpath_last_node(devpath)) == NULL)
387                         continue;
388
389                 if (efipart_floppy(node) != NULL)
390                         continue;
391
392                 if (efipart_hdd(devpath))
393                         continue;
394
395                 status = BS->HandleProtocol(efipart_handles[i],
396                     &blkio_guid, (void **)&blkio);
397                 if (EFI_ERROR(status))
398                         continue;
399                 /*
400                  * If we come across a logical partition of subtype CDROM
401                  * it doesn't refer to the CD filesystem itself, but rather
402                  * to any usable El Torito boot image on it. In this case
403                  * we try to find the parent device and add that instead as
404                  * that will be the CD filesystem.
405                  */
406                 if (DevicePathType(node) == MEDIA_DEVICE_PATH &&
407                     DevicePathSubType(node) == MEDIA_CDROM_DP) {
408                         devpathcpy = efi_devpath_trim(devpath);
409                         if (devpathcpy == NULL)
410                                 continue;
411                         tmpdevpath = devpathcpy;
412                         status = BS->LocateDevicePath(&blkio_guid, &tmpdevpath,
413                             &handle);
414                         free(devpathcpy);
415                         if (EFI_ERROR(status))
416                                 continue;
417                         devpath = efi_lookup_devpath(handle);
418                         efipart_cdinfo_add(handle, efipart_handles[i],
419                             devpath);
420                         continue;
421                 }
422
423                 if (DevicePathType(node) == MESSAGING_DEVICE_PATH &&
424                     DevicePathSubType(node) == MSG_ATAPI_DP) {
425                         efipart_cdinfo_add(efipart_handles[i], NULL,
426                             devpath);
427                         continue;
428                 }
429
430                 /* USB or SATA cd without the media. */
431                 if (blkio->Media->RemovableMedia &&
432                     !blkio->Media->MediaPresent) {
433                         efipart_cdinfo_add(efipart_handles[i], NULL,
434                             devpath);
435                 }
436         }
437 }
438
439 static int
440 efipart_initcd(void)
441 {
442         int rv;
443
444         rv = efipart_inithandles();
445         if (rv != 0)
446                 return (rv);
447         STAILQ_INIT(&cdinfo);
448
449         efipart_updatecd();
450
451         bcache_add_dev(efiblk_pdinfo_count(&cdinfo));
452         return (0);
453 }
454
455 static int
456 efipart_hdinfo_add(EFI_HANDLE disk_handle, EFI_HANDLE part_handle)
457 {
458         EFI_DEVICE_PATH *disk_devpath, *part_devpath;
459         HARDDRIVE_DEVICE_PATH *node;
460         int unit;
461         pdinfo_t *hd, *pd, *last;
462
463         disk_devpath = efi_lookup_devpath(disk_handle);
464         if (disk_devpath == NULL)
465                 return (ENOENT);
466
467         if (part_handle != NULL) {
468                 part_devpath = efi_lookup_devpath(part_handle);
469                 if (part_devpath == NULL)
470                         return (ENOENT);
471                 node = (HARDDRIVE_DEVICE_PATH *)
472                     efi_devpath_last_node(part_devpath);
473                 if (node == NULL)
474                         return (ENOENT);        /* This should not happen. */
475         } else {
476                 part_devpath = NULL;
477                 node = NULL;
478         }
479
480         pd = calloc(1, sizeof(pdinfo_t));
481         if (pd == NULL) {
482                 printf("Failed to add disk, out of memory\n");
483                 return (ENOMEM);
484         }
485         STAILQ_INIT(&pd->pd_part);
486
487         STAILQ_FOREACH(hd, &hdinfo, pd_link) {
488                 if (efi_devpath_match(hd->pd_devpath, disk_devpath) == true) {
489                         if (part_devpath == NULL)
490                                 return (0);
491
492                         /* Add the partition. */
493                         pd->pd_handle = part_handle;
494                         pd->pd_unit = node->PartitionNumber;
495                         pd->pd_devpath = part_devpath;
496                         STAILQ_INSERT_TAIL(&hd->pd_part, pd, pd_link);
497                         return (0);
498                 }
499         }
500
501         last = STAILQ_LAST(&hdinfo, pdinfo, pd_link);
502         if (last != NULL)
503                 unit = last->pd_unit + 1;
504         else
505                 unit = 0;
506
507         /* Add the disk. */
508         hd = pd;
509         hd->pd_handle = disk_handle;
510         hd->pd_unit = unit;
511         hd->pd_devpath = disk_devpath;
512         STAILQ_INSERT_TAIL(&hdinfo, hd, pd_link);
513
514         if (part_devpath == NULL)
515                 return (0);
516
517         pd = calloc(1, sizeof(pdinfo_t));
518         if (pd == NULL) {
519                 printf("Failed to add partition, out of memory\n");
520                 return (ENOMEM);
521         }
522         STAILQ_INIT(&pd->pd_part);
523
524         /* Add the partition. */
525         pd->pd_handle = part_handle;
526         pd->pd_unit = node->PartitionNumber;
527         pd->pd_devpath = part_devpath;
528         STAILQ_INSERT_TAIL(&hd->pd_part, pd, pd_link);
529
530         return (0);
531 }
532
533 /*
534  * The MEDIA_FILEPATH_DP has device name.
535  * From U-Boot sources it looks like names are in the form
536  * of typeN:M, where type is interface type, N is disk id
537  * and M is partition id.
538  */
539 static int
540 efipart_hdinfo_add_filepath(EFI_HANDLE disk_handle)
541 {
542         EFI_DEVICE_PATH *devpath;
543         FILEPATH_DEVICE_PATH *node;
544         char *pathname, *p;
545         int unit, len;
546         pdinfo_t *pd, *last;
547
548         /* First collect and verify all the data */
549         if ((devpath = efi_lookup_devpath(disk_handle)) == NULL)
550                 return (ENOENT);
551         node = (FILEPATH_DEVICE_PATH *)efi_devpath_last_node(devpath);
552         if (node == NULL)
553                 return (ENOENT);        /* This should not happen. */
554
555         pd = calloc(1, sizeof(pdinfo_t));
556         if (pd == NULL) {
557                 printf("Failed to add disk, out of memory\n");
558                 return (ENOMEM);
559         }
560         STAILQ_INIT(&pd->pd_part);
561         last = STAILQ_LAST(&hdinfo, pdinfo, pd_link);
562         if (last != NULL)
563                 unit = last->pd_unit + 1;
564         else
565                 unit = 0;
566
567         /* FILEPATH_DEVICE_PATH has 0 terminated string */
568         len = ucs2len(node->PathName);
569         if ((pathname = malloc(len + 1)) == NULL) {
570                 printf("Failed to add disk, out of memory\n");
571                 free(pd);
572                 return (ENOMEM);
573         }
574         cpy16to8(node->PathName, pathname, len + 1);
575         p = strchr(pathname, ':');
576
577         /*
578          * Assume we are receiving handles in order, first disk handle,
579          * then partitions for this disk. If this assumption proves
580          * false, this code would need update.
581          */
582         if (p == NULL) {        /* no colon, add the disk */
583                 pd->pd_handle = disk_handle;
584                 pd->pd_unit = unit;
585                 pd->pd_devpath = devpath;
586                 STAILQ_INSERT_TAIL(&hdinfo, pd, pd_link);
587                 free(pathname);
588                 return (0);
589         }
590         p++;    /* skip the colon */
591         errno = 0;
592         unit = (int)strtol(p, NULL, 0);
593         if (errno != 0) {
594                 printf("Bad unit number for partition \"%s\"\n", pathname);
595                 free(pathname);
596                 free(pd);
597                 return (EUNIT);
598         }
599
600         /*
601          * We should have disk registered, if not, we are receiving
602          * handles out of order, and this code should be reworked
603          * to create "blank" disk for partition, and to find the
604          * disk based on PathName compares.
605          */
606         if (last == NULL) {
607                 printf("BUG: No disk for partition \"%s\"\n", pathname);
608                 free(pathname);
609                 free(pd);
610                 return (EINVAL);
611         }
612         /* Add the partition. */
613         pd->pd_handle = disk_handle;
614         pd->pd_unit = unit;
615         pd->pd_devpath = devpath;
616         STAILQ_INSERT_TAIL(&last->pd_part, pd, pd_link);
617         free(pathname);
618         return (0);
619 }
620
621 static void
622 efipart_updatehd(void)
623 {
624         int i, nin;
625         EFI_DEVICE_PATH *devpath, *devpathcpy, *tmpdevpath, *node;
626         EFI_HANDLE handle;
627         EFI_BLOCK_IO *blkio;
628         EFI_STATUS status;
629
630         nin = efipart_nhandles / sizeof (*efipart_handles);
631         for (i = 0; i < nin; i++) {
632                 devpath = efi_lookup_devpath(efipart_handles[i]);
633                 if (devpath == NULL)
634                         continue;
635
636                 if ((node = efi_devpath_last_node(devpath)) == NULL)
637                         continue;
638
639                 if (!efipart_hdd(devpath))
640                         continue;
641
642                 status = BS->HandleProtocol(efipart_handles[i],
643                     &blkio_guid, (void **)&blkio);
644                 if (EFI_ERROR(status))
645                         continue;
646
647                 if (DevicePathType(node) == MEDIA_DEVICE_PATH &&
648                     DevicePathSubType(node) == MEDIA_FILEPATH_DP) {
649                         efipart_hdinfo_add_filepath(efipart_handles[i]);
650                         continue;
651                 }
652
653                 if (DevicePathType(node) == MEDIA_DEVICE_PATH &&
654                     DevicePathSubType(node) == MEDIA_HARDDRIVE_DP) {
655                         devpathcpy = efi_devpath_trim(devpath);
656                         if (devpathcpy == NULL)
657                                 continue;
658                         tmpdevpath = devpathcpy;
659                         status = BS->LocateDevicePath(&blkio_guid, &tmpdevpath,
660                             &handle);
661                         free(devpathcpy);
662                         if (EFI_ERROR(status))
663                                 continue;
664                         /*
665                          * We do not support nested partitions.
666                          */
667                         devpathcpy = efi_lookup_devpath(handle);
668                         if (devpathcpy == NULL)
669                                 continue;
670                         if ((node = efi_devpath_last_node(devpathcpy)) == NULL)
671                                 continue;
672
673                         if (DevicePathType(node) == MEDIA_DEVICE_PATH &&
674                             DevicePathSubType(node) == MEDIA_HARDDRIVE_DP)
675                                 continue;
676
677                         efipart_hdinfo_add(handle, efipart_handles[i]);
678                         continue;
679                 }
680
681                 efipart_hdinfo_add(efipart_handles[i], NULL);
682         }
683 }
684
685 static int
686 efipart_inithd(void)
687 {
688         int rv;
689
690         rv = efipart_inithandles();
691         if (rv != 0)
692                 return (rv);
693         STAILQ_INIT(&hdinfo);
694
695         efipart_updatehd();
696
697         bcache_add_dev(efiblk_pdinfo_count(&hdinfo));
698         return (0);
699 }
700
701 static int
702 efipart_print_common(struct devsw *dev, pdinfo_list_t *pdlist, int verbose)
703 {
704         int ret = 0;
705         EFI_BLOCK_IO *blkio;
706         EFI_STATUS status;
707         EFI_HANDLE h;
708         pdinfo_t *pd;
709         CHAR16 *text;
710         struct disk_devdesc pd_dev;
711         char line[80];
712
713         if (STAILQ_EMPTY(pdlist))
714                 return (0);
715
716         printf("%s devices:", dev->dv_name);
717         if ((ret = pager_output("\n")) != 0)
718                 return (ret);
719
720         STAILQ_FOREACH(pd, pdlist, pd_link) {
721                 h = pd->pd_handle;
722                 if (verbose) {  /* Output the device path. */
723                         text = efi_devpath_name(efi_lookup_devpath(h));
724                         if (text != NULL) {
725                                 printf("  %S", text);
726                                 efi_free_devpath_name(text);
727                                 if ((ret = pager_output("\n")) != 0)
728                                         break;
729                         }
730                 }
731                 snprintf(line, sizeof(line),
732                     "    %s%d", dev->dv_name, pd->pd_unit);
733                 printf("%s:", line);
734                 status = BS->HandleProtocol(h, &blkio_guid, (void **)&blkio);
735                 if (!EFI_ERROR(status)) {
736                         printf("    %llu",
737                             blkio->Media->LastBlock == 0? 0:
738                             (unsigned long long) (blkio->Media->LastBlock + 1));
739                         if (blkio->Media->LastBlock != 0) {
740                                 printf(" X %u", blkio->Media->BlockSize);
741                         }
742                         printf(" blocks");
743                         if (blkio->Media->MediaPresent) {
744                                 if (blkio->Media->RemovableMedia)
745                                         printf(" (removable)");
746                         } else {
747                                 printf(" (no media)");
748                         }
749                         if ((ret = pager_output("\n")) != 0)
750                                 break;
751                         if (!blkio->Media->MediaPresent)
752                                 continue;
753
754                         pd->pd_blkio = blkio;
755                         pd_dev.d_dev = dev;
756                         pd_dev.d_unit = pd->pd_unit;
757                         pd_dev.d_slice = -1;
758                         pd_dev.d_partition = -1;
759                         pd_dev.d_opendata = blkio;
760                         ret = disk_open(&pd_dev, blkio->Media->BlockSize *
761                             (blkio->Media->LastBlock + 1),
762                             blkio->Media->BlockSize);
763                         if (ret == 0) {
764                                 ret = disk_print(&pd_dev, line, verbose);
765                                 disk_close(&pd_dev);
766                                 if (ret != 0)
767                                         return (ret);
768                         } else {
769                                 /* Do not fail from disk_open() */
770                                 ret = 0;
771                         }
772                 } else {
773                         if ((ret = pager_output("\n")) != 0)
774                                 break;
775                 }
776         }
777         return (ret);
778 }
779
780 static int
781 efipart_printfd(int verbose)
782 {
783         return (efipart_print_common(&efipart_fddev, &fdinfo, verbose));
784 }
785
786 static int
787 efipart_printcd(int verbose)
788 {
789         return (efipart_print_common(&efipart_cddev, &cdinfo, verbose));
790 }
791
792 static int
793 efipart_printhd(int verbose)
794 {
795         return (efipart_print_common(&efipart_hddev, &hdinfo, verbose));
796 }
797
798 static int
799 efipart_open(struct open_file *f, ...)
800 {
801         va_list args;
802         struct disk_devdesc *dev;
803         pdinfo_t *pd;
804         EFI_BLOCK_IO *blkio;
805         EFI_STATUS status;
806
807         va_start(args, f);
808         dev = va_arg(args, struct disk_devdesc*);
809         va_end(args);
810         if (dev == NULL)
811                 return (EINVAL);
812
813         pd = efiblk_get_pdinfo((struct devdesc *)dev);
814         if (pd == NULL)
815                 return (EIO);
816
817         if (pd->pd_blkio == NULL) {
818                 status = BS->HandleProtocol(pd->pd_handle, &blkio_guid,
819                     (void **)&pd->pd_blkio);
820                 if (EFI_ERROR(status))
821                         return (efi_status_to_errno(status));
822         }
823
824         blkio = pd->pd_blkio;
825         if (!blkio->Media->MediaPresent)
826                 return (EAGAIN);
827
828         pd->pd_open++;
829         if (pd->pd_bcache == NULL)
830                 pd->pd_bcache = bcache_allocate();
831
832         if (dev->d_dev->dv_type == DEVT_DISK) {
833                 int rc;
834
835                 rc = disk_open(dev,
836                     blkio->Media->BlockSize * (blkio->Media->LastBlock + 1),
837                     blkio->Media->BlockSize);
838                 if (rc != 0) {
839                         pd->pd_open--;
840                         if (pd->pd_open == 0) {
841                                 pd->pd_blkio = NULL;
842                                 bcache_free(pd->pd_bcache);
843                                 pd->pd_bcache = NULL;
844                         }
845                 }
846                 return (rc);
847         }
848         return (0);
849 }
850
851 static int
852 efipart_close(struct open_file *f)
853 {
854         struct disk_devdesc *dev;
855         pdinfo_t *pd;
856
857         dev = (struct disk_devdesc *)(f->f_devdata);
858         if (dev == NULL)
859                 return (EINVAL);
860
861         pd = efiblk_get_pdinfo((struct devdesc *)dev);
862         if (pd == NULL)
863                 return (EINVAL);
864
865         pd->pd_open--;
866         if (pd->pd_open == 0) {
867                 pd->pd_blkio = NULL;
868                 bcache_free(pd->pd_bcache);
869                 pd->pd_bcache = NULL;
870         }
871         if (dev->d_dev->dv_type == DEVT_DISK)
872                 return (disk_close(dev));
873         return (0);
874 }
875
876 static int
877 efipart_ioctl(struct open_file *f, u_long cmd, void *data)
878 {
879         struct disk_devdesc *dev;
880         pdinfo_t *pd;
881         int rc;
882
883         dev = (struct disk_devdesc *)(f->f_devdata);
884         if (dev == NULL)
885                 return (EINVAL);
886
887         pd = efiblk_get_pdinfo((struct devdesc *)dev);
888         if (pd == NULL)
889                 return (EINVAL);
890
891         if (dev->d_dev->dv_type == DEVT_DISK) {
892                 rc = disk_ioctl(dev, cmd, data);
893                 if (rc != ENOTTY)
894                         return (rc);
895         }
896
897         switch (cmd) {
898         case DIOCGSECTORSIZE:
899                 *(u_int *)data = pd->pd_blkio->Media->BlockSize;
900                 break;
901         case DIOCGMEDIASIZE:
902                 *(uint64_t *)data = pd->pd_blkio->Media->BlockSize *
903                     (pd->pd_blkio->Media->LastBlock + 1);
904                 break;
905         default:
906                 return (ENOTTY);
907         }
908
909         return (0);
910 }
911
912 /*
913  * efipart_readwrite()
914  * Internal equivalent of efipart_strategy(), which operates on the
915  * media-native block size. This function expects all I/O requests
916  * to be within the media size and returns an error if such is not
917  * the case.
918  */
919 static int
920 efipart_readwrite(EFI_BLOCK_IO *blkio, int rw, daddr_t blk, daddr_t nblks,
921     char *buf)
922 {
923         EFI_STATUS status;
924
925         if (blkio == NULL)
926                 return (ENXIO);
927         if (blk < 0 || blk > blkio->Media->LastBlock)
928                 return (EIO);
929         if ((blk + nblks - 1) > blkio->Media->LastBlock)
930                 return (EIO);
931
932         switch (rw & F_MASK) {
933         case F_READ:
934                 status = blkio->ReadBlocks(blkio, blkio->Media->MediaId, blk,
935                     nblks * blkio->Media->BlockSize, buf);
936                 break;
937         case F_WRITE:
938                 if (blkio->Media->ReadOnly)
939                         return (EROFS);
940                 status = blkio->WriteBlocks(blkio, blkio->Media->MediaId, blk,
941                     nblks * blkio->Media->BlockSize, buf);
942                 break;
943         default:
944                 return (ENOSYS);
945         }
946
947         if (EFI_ERROR(status)) {
948                 printf("%s: rw=%d, blk=%ju size=%ju status=%lu\n", __func__, rw,
949                     blk, nblks, EFI_ERROR_CODE(status));
950         }
951         return (efi_status_to_errno(status));
952 }
953
954 static int
955 efipart_strategy(void *devdata, int rw, daddr_t blk, size_t size,
956     char *buf, size_t *rsize)
957 {
958         struct bcache_devdata bcd;
959         struct disk_devdesc *dev;
960         pdinfo_t *pd;
961
962         dev = (struct disk_devdesc *)devdata;
963         if (dev == NULL)
964                 return (EINVAL);
965
966         pd = efiblk_get_pdinfo((struct devdesc *)dev);
967         if (pd == NULL)
968                 return (EINVAL);
969
970         if (pd->pd_blkio->Media->RemovableMedia &&
971             !pd->pd_blkio->Media->MediaPresent)
972                 return (ENXIO);
973
974         bcd.dv_strategy = efipart_realstrategy;
975         bcd.dv_devdata = devdata;
976         bcd.dv_cache = pd->pd_bcache;
977
978         if (dev->d_dev->dv_type == DEVT_DISK) {
979                 daddr_t offset;
980
981                 offset = dev->d_offset * pd->pd_blkio->Media->BlockSize;
982                 offset /= 512;
983                 return (bcache_strategy(&bcd, rw, blk + offset,
984                     size, buf, rsize));
985         }
986         return (bcache_strategy(&bcd, rw, blk, size, buf, rsize));
987 }
988
989 static int
990 efipart_realstrategy(void *devdata, int rw, daddr_t blk, size_t size,
991     char *buf, size_t *rsize)
992 {
993         struct disk_devdesc *dev = (struct disk_devdesc *)devdata;
994         pdinfo_t *pd;
995         EFI_BLOCK_IO *blkio;
996         uint64_t off, disk_blocks, d_offset = 0;
997         char *blkbuf;
998         size_t blkoff, blksz;
999         int error;
1000         size_t diskend, readstart;
1001
1002         if (dev == NULL || blk < 0)
1003                 return (EINVAL);
1004
1005         pd = efiblk_get_pdinfo((struct devdesc *)dev);
1006         if (pd == NULL)
1007                 return (EINVAL);
1008
1009         blkio = pd->pd_blkio;
1010         if (blkio == NULL)
1011                 return (ENXIO);
1012
1013         if (size == 0 || (size % 512) != 0)
1014                 return (EIO);
1015
1016         off = blk * 512;
1017         /*
1018          * Get disk blocks, this value is either for whole disk or for
1019          * partition.
1020          */
1021         disk_blocks = 0;
1022         if (dev->d_dev->dv_type == DEVT_DISK) {
1023                 if (disk_ioctl(dev, DIOCGMEDIASIZE, &disk_blocks) == 0) {
1024                         /* DIOCGMEDIASIZE does return bytes. */
1025                         disk_blocks /= blkio->Media->BlockSize;
1026                 }
1027                 d_offset = dev->d_offset;
1028         }
1029         if (disk_blocks == 0)
1030                 disk_blocks = blkio->Media->LastBlock + 1 - d_offset;
1031
1032         /* make sure we don't read past disk end */
1033         if ((off + size) / blkio->Media->BlockSize > d_offset + disk_blocks) {
1034                 diskend = d_offset + disk_blocks;
1035                 readstart = off / blkio->Media->BlockSize;
1036
1037                 if (diskend <= readstart) {
1038                         if (rsize != NULL)
1039                                 *rsize = 0;
1040
1041                         return (EIO);
1042                 }
1043                 size = diskend - readstart;
1044                 size = size * blkio->Media->BlockSize;
1045         }
1046
1047         if (rsize != NULL)
1048                 *rsize = size;
1049
1050         if ((size % blkio->Media->BlockSize == 0) &&
1051             (off % blkio->Media->BlockSize == 0))
1052                 return (efipart_readwrite(blkio, rw,
1053                     off / blkio->Media->BlockSize,
1054                     size / blkio->Media->BlockSize, buf));
1055
1056         /*
1057          * The block size of the media is not a multiple of I/O.
1058          */
1059         blkbuf = malloc(blkio->Media->BlockSize);
1060         if (blkbuf == NULL)
1061                 return (ENOMEM);
1062
1063         error = 0;
1064         blk = off / blkio->Media->BlockSize;
1065         blkoff = off % blkio->Media->BlockSize;
1066         blksz = blkio->Media->BlockSize - blkoff;
1067         while (size > 0) {
1068                 error = efipart_readwrite(blkio, rw, blk, 1, blkbuf);
1069                 if (error)
1070                         break;
1071                 if (size < blksz)
1072                         blksz = size;
1073                 bcopy(blkbuf + blkoff, buf, blksz);
1074                 buf += blksz;
1075                 size -= blksz;
1076                 blk++;
1077                 blkoff = 0;
1078                 blksz = blkio->Media->BlockSize;
1079         }
1080
1081         free(blkbuf);
1082         return (error);
1083 }