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