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