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