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