]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - stand/efi/libefi/efipart.c
Merge OpenSSL 1.0.2n.
[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 <disk.h>
43
44 static EFI_GUID blkio_guid = BLOCK_IO_PROTOCOL;
45
46 static int efipart_initfd(void);
47 static int efipart_initcd(void);
48 static int efipart_inithd(void);
49
50 static int efipart_strategy(void *, int, daddr_t, size_t, char *, size_t *);
51 static int efipart_realstrategy(void *, int, daddr_t, size_t, char *, size_t *);
52
53 static int efipart_open(struct open_file *, ...);
54 static int efipart_close(struct open_file *);
55 static int efipart_ioctl(struct open_file *, u_long, void *);
56
57 static int efipart_printfd(int);
58 static int efipart_printcd(int);
59 static int efipart_printhd(int);
60
61 /* EISA PNP ID's for floppy controllers */
62 #define PNP0604 0x604
63 #define PNP0700 0x700
64 #define PNP0701 0x701
65
66 struct devsw efipart_fddev = {
67         .dv_name = "fd",
68         .dv_type = DEVT_FD,
69         .dv_init = efipart_initfd,
70         .dv_strategy = efipart_strategy,
71         .dv_open = efipart_open,
72         .dv_close = efipart_close,
73         .dv_ioctl = efipart_ioctl,
74         .dv_print = efipart_printfd,
75         .dv_cleanup = NULL
76 };
77
78 struct devsw efipart_cddev = {
79         .dv_name = "cd",
80         .dv_type = DEVT_CD,
81         .dv_init = efipart_initcd,
82         .dv_strategy = efipart_strategy,
83         .dv_open = efipart_open,
84         .dv_close = efipart_close,
85         .dv_ioctl = efipart_ioctl,
86         .dv_print = efipart_printcd,
87         .dv_cleanup = NULL
88 };
89
90 struct devsw efipart_hddev = {
91         .dv_name = "disk",
92         .dv_type = DEVT_DISK,
93         .dv_init = efipart_inithd,
94         .dv_strategy = efipart_strategy,
95         .dv_open = efipart_open,
96         .dv_close = efipart_close,
97         .dv_ioctl = efipart_ioctl,
98         .dv_print = efipart_printhd,
99         .dv_cleanup = NULL
100 };
101
102 static pdinfo_list_t fdinfo;
103 static pdinfo_list_t cdinfo;
104 static pdinfo_list_t hdinfo;
105
106 static EFI_HANDLE *efipart_handles = NULL;
107 static UINTN efipart_nhandles = 0;
108
109 pdinfo_list_t *
110 efiblk_get_pdinfo_list(struct devsw *dev)
111 {
112         if (dev->dv_type == DEVT_DISK)
113                 return (&hdinfo);
114         if (dev->dv_type == DEVT_CD)
115                 return (&cdinfo);
116         if (dev->dv_type == DEVT_FD)
117                 return (&fdinfo);
118         return (NULL);
119 }
120
121 pdinfo_t *
122 efiblk_get_pdinfo(struct devdesc *dev)
123 {
124         pdinfo_list_t *pdi;
125         pdinfo_t *pd = NULL;
126
127         pdi = efiblk_get_pdinfo_list(dev->d_dev);
128         if (pdi == NULL)
129                 return (pd);
130
131         STAILQ_FOREACH(pd, pdi, pd_link) {
132                 if (pd->pd_unit == dev->d_unit)
133                         return (pd);
134         }
135         return (pd);
136 }
137
138 static int
139 efiblk_pdinfo_count(pdinfo_list_t *pdi)
140 {
141         pdinfo_t *pd;
142         int i = 0;
143
144         STAILQ_FOREACH(pd, pdi, pd_link) {
145                 i++;
146         }
147         return (i);
148 }
149
150 static int
151 efipart_inithandles(void)
152 {
153         UINTN sz;
154         EFI_HANDLE *hin;
155         EFI_STATUS status;
156
157         if (efipart_nhandles != 0) {
158                 free(efipart_handles);
159                 efipart_handles = NULL;
160                 efipart_nhandles = 0;
161         }
162
163         sz = 0;
164         hin = NULL;
165         status = BS->LocateHandle(ByProtocol, &blkio_guid, 0, &sz, hin);
166         if (status == EFI_BUFFER_TOO_SMALL) {
167                 hin = malloc(sz);
168                 status = BS->LocateHandle(ByProtocol, &blkio_guid, 0, &sz,
169                     hin);
170                 if (EFI_ERROR(status))
171                         free(hin);
172         }
173         if (EFI_ERROR(status))
174                 return (efi_status_to_errno(status));
175
176         efipart_handles = hin;
177         efipart_nhandles = sz;
178         return (0);
179 }
180
181 static ACPI_HID_DEVICE_PATH *
182 efipart_floppy(EFI_DEVICE_PATH *node)
183 {
184         ACPI_HID_DEVICE_PATH *acpi;
185
186         if (DevicePathType(node) == ACPI_DEVICE_PATH &&
187             DevicePathSubType(node) == ACPI_DP) {
188                 acpi = (ACPI_HID_DEVICE_PATH *) node;
189                 if (acpi->HID == EISA_PNP_ID(PNP0604) ||
190                     acpi->HID == EISA_PNP_ID(PNP0700) ||
191                     acpi->HID == EISA_PNP_ID(PNP0701)) {
192                         return (acpi);
193                 }
194         }
195         return (NULL);
196 }
197
198 /*
199  * Determine if the provided device path is hdd.
200  *
201  * There really is no simple fool proof way to classify the devices.
202  * Since we do build three lists of devices - floppy, cd and hdd, we
203  * will try to see  if the device is floppy or cd, and list anything else
204  * as hdd.
205  */
206 static bool
207 efipart_hdd(EFI_DEVICE_PATH *dp)
208 {
209         unsigned i, nin;
210         EFI_DEVICE_PATH *devpath, *node;
211         EFI_BLOCK_IO *blkio;
212         EFI_STATUS status;
213
214         if (dp == NULL)
215                 return (false);
216
217         if ((node = efi_devpath_last_node(dp)) == NULL)
218                 return (false);
219
220         if (efipart_floppy(node) != NULL)
221                 return (false);
222
223         /*
224          * Test every EFI BLOCK IO handle to make sure dp is not device path
225          * for CD/DVD.
226          */
227         nin = efipart_nhandles / sizeof (*efipart_handles);
228         for (i = 0; i < nin; i++) {
229                 devpath = efi_lookup_devpath(efipart_handles[i]);
230                 if (devpath == NULL)
231                         return (false);
232
233                 /* Only continue testing when dp is prefix in devpath. */
234                 if (!efi_devpath_is_prefix(dp, devpath))
235                         continue;
236
237                 /*
238                  * The device path has to have last node describing the
239                  *  device, or we can not test the type.
240                  */
241                 if ((node = efi_devpath_last_node(devpath)) == NULL)
242                         return (false);
243
244                 if (DevicePathType(node) == MEDIA_DEVICE_PATH &&
245                     DevicePathSubType(node) == MEDIA_CDROM_DP) {
246                         return (false);
247                 }
248
249                 /* Make sure we do have the media. */
250                 status = BS->HandleProtocol(efipart_handles[i],
251                     &blkio_guid, (void **)&blkio);
252                 if (EFI_ERROR(status))
253                         return (false);
254
255                 /* USB or SATA cd without the media. */
256                 if (blkio->Media->RemovableMedia &&
257                     !blkio->Media->MediaPresent) {
258                         return (false);
259                 }
260
261                 /*
262                  * We assume the block size 512 or greater power of 2. 
263                  * iPXE is known to insert stub BLOCK IO device with
264                  * BlockSize 1.
265                  */
266                 if (blkio->Media->BlockSize < 512 ||
267                     !powerof2(blkio->Media->BlockSize)) {
268                         return (false);
269                 }
270         }
271         return (true);
272 }
273
274 /*
275  * Add or update entries with new handle data.
276  */
277 static int
278 efipart_fdinfo_add(EFI_HANDLE handle, uint32_t uid, EFI_DEVICE_PATH *devpath)
279 {
280         pdinfo_t *fd;
281
282         fd = calloc(1, sizeof(pdinfo_t));
283         if (fd == NULL) {
284                 printf("Failed to register floppy %d, out of memory\n", uid);
285                 return (ENOMEM);
286         }
287         STAILQ_INIT(&fd->pd_part);
288
289         fd->pd_unit = uid;
290         fd->pd_handle = handle;
291         fd->pd_devpath = devpath;
292         STAILQ_INSERT_TAIL(&fdinfo, fd, pd_link);
293         return (0);
294 }
295
296 static void
297 efipart_updatefd(void)
298 {
299         EFI_DEVICE_PATH *devpath, *node;
300         ACPI_HID_DEVICE_PATH *acpi;
301         int i, nin;
302
303         nin = efipart_nhandles / sizeof (*efipart_handles);
304         for (i = 0; i < nin; i++) {
305                 devpath = efi_lookup_devpath(efipart_handles[i]);
306                 if (devpath == NULL)
307                         continue;
308
309                 if ((node = efi_devpath_last_node(devpath)) == NULL)
310                         continue;
311                 if ((acpi = efipart_floppy(node)) != NULL) {
312                         efipart_fdinfo_add(efipart_handles[i], acpi->UID,
313                             devpath);
314                 }
315         }
316 }
317
318 static int
319 efipart_initfd(void)
320 {
321         int rv;
322
323         rv = efipart_inithandles();
324         if (rv != 0)
325                 return (rv);
326         STAILQ_INIT(&fdinfo);
327
328         efipart_updatefd();
329
330         bcache_add_dev(efiblk_pdinfo_count(&fdinfo));
331         return (0);
332 }
333
334 /*
335  * Add or update entries with new handle data.
336  */
337 static int
338 efipart_cdinfo_add(EFI_HANDLE handle, EFI_HANDLE alias,
339     EFI_DEVICE_PATH *devpath)
340 {
341         int unit;
342         pdinfo_t *cd;
343         pdinfo_t *pd;
344
345         unit = 0;
346         STAILQ_FOREACH(pd, &cdinfo, pd_link) {
347                 if (efi_devpath_match(pd->pd_devpath, devpath) == true) {
348                         pd->pd_handle = handle;
349                         pd->pd_alias = alias;
350                         return (0);
351                 }
352                 unit++;
353         }
354
355         cd = calloc(1, sizeof(pdinfo_t));
356         if (cd == NULL) {
357                 printf("Failed to add cd %d, out of memory\n", unit);
358                 return (ENOMEM);
359         }
360         STAILQ_INIT(&cd->pd_part);
361
362         cd->pd_handle = handle;
363         cd->pd_unit = unit;
364         cd->pd_alias = alias;
365         cd->pd_devpath = devpath;
366         STAILQ_INSERT_TAIL(&cdinfo, cd, pd_link);
367         return (0);
368 }
369
370 static void
371 efipart_updatecd(void)
372 {
373         int i, nin;
374         EFI_DEVICE_PATH *devpath, *devpathcpy, *tmpdevpath, *node;
375         EFI_HANDLE handle;
376         EFI_BLOCK_IO *blkio;
377         EFI_STATUS status;
378
379         nin = efipart_nhandles / sizeof (*efipart_handles);
380         for (i = 0; i < nin; i++) {
381                 devpath = efi_lookup_devpath(efipart_handles[i]);
382                 if (devpath == NULL)
383                         continue;
384
385                 if ((node = efi_devpath_last_node(devpath)) == NULL)
386                         continue;
387
388                 if (efipart_floppy(node) != NULL)
389                         continue;
390
391                 if (efipart_hdd(devpath))
392                         continue;
393
394                 status = BS->HandleProtocol(efipart_handles[i],
395                     &blkio_guid, (void **)&blkio);
396                 if (EFI_ERROR(status))
397                         continue;
398                 /*
399                  * If we come across a logical partition of subtype CDROM
400                  * it doesn't refer to the CD filesystem itself, but rather
401                  * to any usable El Torito boot image on it. In this case
402                  * we try to find the parent device and add that instead as
403                  * that will be the CD filesystem.
404                  */
405                 if (DevicePathType(node) == MEDIA_DEVICE_PATH &&
406                     DevicePathSubType(node) == MEDIA_CDROM_DP) {
407                         devpathcpy = efi_devpath_trim(devpath);
408                         if (devpathcpy == NULL)
409                                 continue;
410                         tmpdevpath = devpathcpy;
411                         status = BS->LocateDevicePath(&blkio_guid, &tmpdevpath,
412                             &handle);
413                         free(devpathcpy);
414                         if (EFI_ERROR(status))
415                                 continue;
416                         devpath = efi_lookup_devpath(handle);
417                         efipart_cdinfo_add(handle, efipart_handles[i],
418                             devpath);
419                         continue;
420                 }
421
422                 if (DevicePathType(node) == MESSAGING_DEVICE_PATH &&
423                     DevicePathSubType(node) == MSG_ATAPI_DP) {
424                         efipart_cdinfo_add(efipart_handles[i], NULL,
425                             devpath);
426                         continue;
427                 }
428
429                 /* USB or SATA cd without the media. */
430                 if (blkio->Media->RemovableMedia &&
431                     !blkio->Media->MediaPresent) {
432                         efipart_cdinfo_add(efipart_handles[i], NULL,
433                             devpath);
434                 }
435         }
436 }
437
438 static int
439 efipart_initcd(void)
440 {
441         int rv;
442
443         rv = efipart_inithandles();
444         if (rv != 0)
445                 return (rv);
446         STAILQ_INIT(&cdinfo);
447
448         efipart_updatecd();
449
450         bcache_add_dev(efiblk_pdinfo_count(&cdinfo));
451         return (0);
452 }
453
454 static int
455 efipart_hdinfo_add(EFI_HANDLE disk_handle, EFI_HANDLE part_handle)
456 {
457         EFI_DEVICE_PATH *disk_devpath, *part_devpath;
458         HARDDRIVE_DEVICE_PATH *node;
459         int unit;
460         pdinfo_t *hd, *pd, *last;
461
462         disk_devpath = efi_lookup_devpath(disk_handle);
463         if (disk_devpath == NULL)
464                 return (ENOENT);
465
466         if (part_handle != NULL) {
467                 part_devpath = efi_lookup_devpath(part_handle);
468                 if (part_devpath == NULL)
469                         return (ENOENT);
470                 node = (HARDDRIVE_DEVICE_PATH *)
471                     efi_devpath_last_node(part_devpath);
472                 if (node == NULL)
473                         return (ENOENT);        /* This should not happen. */
474         } else {
475                 part_devpath = NULL;
476                 node = NULL;
477         }
478
479         pd = calloc(1, sizeof(pdinfo_t));
480         if (pd == NULL) {
481                 printf("Failed to add disk, out of memory\n");
482                 return (ENOMEM);
483         }
484         STAILQ_INIT(&pd->pd_part);
485
486         STAILQ_FOREACH(hd, &hdinfo, pd_link) {
487                 if (efi_devpath_match(hd->pd_devpath, disk_devpath) == true) {
488                         if (part_devpath == NULL)
489                                 return (0);
490
491                         /* Add the partition. */
492                         pd->pd_handle = part_handle;
493                         pd->pd_unit = node->PartitionNumber;
494                         pd->pd_devpath = part_devpath;
495                         STAILQ_INSERT_TAIL(&hd->pd_part, pd, pd_link);
496                         return (0);
497                 }
498         }
499
500         last = STAILQ_LAST(&hdinfo, pdinfo, pd_link);
501         if (last != NULL)
502                 unit = last->pd_unit + 1;
503         else
504                 unit = 0;
505
506         /* Add the disk. */
507         hd = pd;
508         hd->pd_handle = disk_handle;
509         hd->pd_unit = unit;
510         hd->pd_devpath = disk_devpath;
511         STAILQ_INSERT_TAIL(&hdinfo, hd, pd_link);
512
513         if (part_devpath == NULL)
514                 return (0);
515
516         pd = calloc(1, sizeof(pdinfo_t));
517         if (pd == NULL) {
518                 printf("Failed to add partition, out of memory\n");
519                 return (ENOMEM);
520         }
521         STAILQ_INIT(&pd->pd_part);
522
523         /* Add the partition. */
524         pd->pd_handle = part_handle;
525         pd->pd_unit = node->PartitionNumber;
526         pd->pd_devpath = part_devpath;
527         STAILQ_INSERT_TAIL(&hd->pd_part, pd, pd_link);
528
529         return (0);
530 }
531
532 /*
533  * The MEDIA_FILEPATH_DP has device name.
534  * From U-Boot sources it looks like names are in the form
535  * of typeN:M, where type is interface type, N is disk id
536  * and M is partition id.
537  */
538 static int
539 efipart_hdinfo_add_filepath(EFI_HANDLE disk_handle)
540 {
541         EFI_DEVICE_PATH *devpath;
542         FILEPATH_DEVICE_PATH *node;
543         char *pathname, *p;
544         int unit, len;
545         pdinfo_t *pd, *last;
546
547         /* First collect and verify all the data */
548         if ((devpath = efi_lookup_devpath(disk_handle)) == NULL)
549                 return (ENOENT);
550         node = (FILEPATH_DEVICE_PATH *)efi_devpath_last_node(devpath);
551         if (node == NULL)
552                 return (ENOENT);        /* This should not happen. */
553
554         pd = calloc(1, sizeof(pdinfo_t));
555         if (pd == NULL) {
556                 printf("Failed to add disk, out of memory\n");
557                 return (ENOMEM);
558         }
559         STAILQ_INIT(&pd->pd_part);
560         last = STAILQ_LAST(&hdinfo, pdinfo, pd_link);
561         if (last != NULL)
562                 unit = last->pd_unit + 1;
563         else
564                 unit = 0;
565
566         /* FILEPATH_DEVICE_PATH has 0 terminated string */
567         for (len = 0; node->PathName[len] != 0; len++)
568                 ;
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 }