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