8796 loader.efi: efipart does not recognize partitionless disks
[unleashed.git] / usr / src / boot / sys / boot / efi / libefi / efipart.c
blob347c14f68a93028cea1ac1b19acb8f390651d50b
1 /*-
2 * Copyright (c) 2010 Marcel Moolenaar
3 * All rights reserved.
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.
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.
27 #include <sys/cdefs.h>
29 #include <sys/disk.h>
30 #include <sys/param.h>
31 #include <sys/time.h>
32 #include <sys/queue.h>
33 #include <stddef.h>
34 #include <stdarg.h>
36 #include <bootstrap.h>
38 #include <efi.h>
39 #include <efilib.h>
40 #include <efiprot.h>
41 #include <disk.h>
43 static EFI_GUID blkio_guid = BLOCK_IO_PROTOCOL;
45 static int efipart_initfd(void);
46 static int efipart_initcd(void);
47 static int efipart_inithd(void);
49 static int efipart_strategy(void *, int, daddr_t, size_t, char *, size_t *);
50 static int efipart_realstrategy(void *, int, daddr_t, size_t, char *, size_t *);
52 static int efipart_open(struct open_file *, ...);
53 static int efipart_close(struct open_file *);
54 static int efipart_ioctl(struct open_file *, u_long, void *);
56 static int efipart_printfd(int);
57 static int efipart_printcd(int);
58 static int efipart_printhd(int);
60 /* EISA PNP ID's for floppy controllers */
61 #define PNP0604 0x604
62 #define PNP0700 0x700
63 #define PNP0701 0x701
65 struct devsw efipart_fddev = {
66 .dv_name = "fd",
67 .dv_type = DEVT_FD,
68 .dv_init = efipart_initfd,
69 .dv_strategy = efipart_strategy,
70 .dv_open = efipart_open,
71 .dv_close = efipart_close,
72 .dv_ioctl = efipart_ioctl,
73 .dv_print = efipart_printfd,
74 .dv_cleanup = NULL
77 struct devsw efipart_cddev = {
78 .dv_name = "cd",
79 .dv_type = DEVT_CD,
80 .dv_init = efipart_initcd,
81 .dv_strategy = efipart_strategy,
82 .dv_open = efipart_open,
83 .dv_close = efipart_close,
84 .dv_ioctl = efipart_ioctl,
85 .dv_print = efipart_printcd,
86 .dv_cleanup = NULL
89 struct devsw efipart_hddev = {
90 .dv_name = "disk",
91 .dv_type = DEVT_DISK,
92 .dv_init = efipart_inithd,
93 .dv_strategy = efipart_strategy,
94 .dv_open = efipart_open,
95 .dv_close = efipart_close,
96 .dv_ioctl = efipart_ioctl,
97 .dv_print = efipart_printhd,
98 .dv_cleanup = NULL
101 static pdinfo_list_t fdinfo;
102 static pdinfo_list_t cdinfo;
103 static pdinfo_list_t hdinfo;
105 static EFI_HANDLE *efipart_handles = NULL;
106 static UINTN efipart_nhandles = 0;
108 static pdinfo_t *
109 efiblk_get_pdinfo(pdinfo_list_t *pdi, int unit)
111 pdinfo_t *pd;
113 STAILQ_FOREACH(pd, pdi, pd_link) {
114 if (pd->pd_unit == unit)
115 return (pd);
117 return (NULL);
120 static int
121 efiblk_pdinfo_count(pdinfo_list_t *pdi)
123 pdinfo_t *pd;
124 int i = 0;
126 STAILQ_FOREACH(pd, pdi, pd_link) {
127 i++;
129 return (i);
132 static int
133 efipart_inithandles(void)
135 UINTN sz;
136 EFI_HANDLE *hin;
137 EFI_STATUS status;
139 if (efipart_nhandles != 0) {
140 free(efipart_handles);
141 efipart_handles = NULL;
142 efipart_nhandles = 0;
145 sz = 0;
146 hin = NULL;
147 status = BS->LocateHandle(ByProtocol, &blkio_guid, 0, &sz, hin);
148 if (status == EFI_BUFFER_TOO_SMALL) {
149 hin = malloc(sz);
150 status = BS->LocateHandle(ByProtocol, &blkio_guid, 0, &sz,
151 hin);
152 if (EFI_ERROR(status))
153 free(hin);
155 if (EFI_ERROR(status))
156 return (efi_status_to_errno(status));
158 efipart_handles = hin;
159 efipart_nhandles = sz;
160 return (0);
163 static ACPI_HID_DEVICE_PATH *
164 efipart_floppy(EFI_DEVICE_PATH *node)
166 ACPI_HID_DEVICE_PATH *acpi;
168 if (DevicePathType(node) == ACPI_DEVICE_PATH &&
169 DevicePathSubType(node) == ACPI_DP) {
170 acpi = (ACPI_HID_DEVICE_PATH *) node;
171 if (acpi->HID == EISA_PNP_ID(PNP0604) ||
172 acpi->HID == EISA_PNP_ID(PNP0700) ||
173 acpi->HID == EISA_PNP_ID(PNP0701)) {
174 return (acpi);
177 return (NULL);
181 * Determine if the provided device path is hdd.
183 * There really is no simple fool proof way to classify the devices.
184 * Since we do build three lists of devices - floppy, cd and hdd, we
185 * will try to see if the device is floppy or cd, and list anything else
186 * as hdd.
188 static bool
189 efipart_hdd(EFI_DEVICE_PATH *dp)
191 unsigned i, nin;
192 EFI_DEVICE_PATH *devpath, *node;
193 EFI_BLOCK_IO *blkio;
194 EFI_STATUS status;
196 if (dp == NULL)
197 return (false);
199 if ((node = efi_devpath_last_node(dp)) == NULL)
200 return (false);
202 if (efipart_floppy(node) != NULL)
203 return (false);
206 * Test every EFI BLOCK IO handle to make sure dp is not device path
207 * for CD/DVD.
209 nin = efipart_nhandles / sizeof (*efipart_handles);
210 for (i = 0; i < nin; i++) {
211 devpath = efi_lookup_devpath(efipart_handles[i]);
212 if (devpath == NULL)
213 return (false);
215 /* Only continue testing when dp is prefix in devpath. */
216 if (!efi_devpath_is_prefix(dp, devpath))
217 continue;
220 * The device path has to have last node describing the
221 * device, or we can not test the type.
223 if ((node = efi_devpath_last_node(devpath)) == NULL)
224 return (false);
226 if (DevicePathType(node) == MEDIA_DEVICE_PATH &&
227 DevicePathSubType(node) == MEDIA_CDROM_DP) {
228 return (false);
231 /* Make sure we do have the media. */
232 status = BS->HandleProtocol(efipart_handles[i],
233 &blkio_guid, (void **)&blkio);
234 if (EFI_ERROR(status))
235 return (false);
237 /* USB or SATA cd without the media. */
238 if (blkio->Media->RemovableMedia &&
239 !blkio->Media->MediaPresent) {
240 return (false);
243 return (true);
247 * Add or update entries with new handle data.
249 static int
250 efipart_fdinfo_add(EFI_HANDLE handle, uint32_t uid, EFI_DEVICE_PATH *devpath)
252 pdinfo_t *fd;
254 fd = calloc(1, sizeof(pdinfo_t));
255 if (fd == NULL) {
256 printf("Failed to register floppy %d, out of memory\n", uid);
257 return (ENOMEM);
259 STAILQ_INIT(&fd->pd_part);
261 fd->pd_unit = uid;
262 fd->pd_handle = handle;
263 fd->pd_devpath = devpath;
264 STAILQ_INSERT_TAIL(&fdinfo, fd, pd_link);
265 return (0);
268 static void
269 efipart_updatefd(void)
271 EFI_DEVICE_PATH *devpath, *node;
272 ACPI_HID_DEVICE_PATH *acpi;
273 int i, nin;
275 nin = efipart_nhandles / sizeof (*efipart_handles);
276 for (i = 0; i < nin; i++) {
277 devpath = efi_lookup_devpath(efipart_handles[i]);
278 if (devpath == NULL)
279 continue;
281 if ((node = efi_devpath_last_node(devpath)) == NULL)
282 continue;
283 if ((acpi = efipart_floppy(node)) != NULL) {
284 efipart_fdinfo_add(efipart_handles[i], acpi->UID,
285 devpath);
290 static int
291 efipart_initfd(void)
293 int rv;
295 rv = efipart_inithandles();
296 if (rv != 0)
297 return (rv);
298 STAILQ_INIT(&fdinfo);
300 efipart_updatefd();
302 bcache_add_dev(efiblk_pdinfo_count(&fdinfo));
303 return (0);
307 * Add or update entries with new handle data.
309 static int
310 efipart_cdinfo_add(EFI_HANDLE handle, EFI_HANDLE alias,
311 EFI_DEVICE_PATH *devpath)
313 int unit;
314 pdinfo_t *cd;
315 pdinfo_t *pd;
317 unit = 0;
318 STAILQ_FOREACH(pd, &cdinfo, pd_link) {
319 if (efi_devpath_match(pd->pd_devpath, devpath) == true) {
320 pd->pd_handle = handle;
321 pd->pd_alias = alias;
322 return (0);
324 unit++;
327 cd = calloc(1, sizeof(pdinfo_t));
328 if (cd == NULL) {
329 printf("Failed to add cd %d, out of memory\n", unit);
330 return (ENOMEM);
332 STAILQ_INIT(&cd->pd_part);
334 cd->pd_handle = handle;
335 cd->pd_unit = unit;
336 cd->pd_alias = alias;
337 cd->pd_devpath = devpath;
338 STAILQ_INSERT_TAIL(&cdinfo, cd, pd_link);
339 return (0);
342 static void
343 efipart_updatecd(void)
345 int i, nin;
346 EFI_DEVICE_PATH *devpath, *devpathcpy, *tmpdevpath, *node;
347 EFI_HANDLE handle;
348 EFI_BLOCK_IO *blkio;
349 EFI_STATUS status;
351 nin = efipart_nhandles / sizeof (*efipart_handles);
352 for (i = 0; i < nin; i++) {
353 devpath = efi_lookup_devpath(efipart_handles[i]);
354 if (devpath == NULL)
355 continue;
357 if ((node = efi_devpath_last_node(devpath)) == NULL)
358 continue;
360 if (efipart_floppy(node) != NULL)
361 continue;
363 if (efipart_hdd(devpath))
364 continue;
366 status = BS->HandleProtocol(efipart_handles[i],
367 &blkio_guid, (void **)&blkio);
368 if (EFI_ERROR(status))
369 continue;
371 * If we come across a logical partition of subtype CDROM
372 * it doesn't refer to the CD filesystem itself, but rather
373 * to any usable El Torito boot image on it. In this case
374 * we try to find the parent device and add that instead as
375 * that will be the CD filesystem.
377 if (DevicePathType(node) == MEDIA_DEVICE_PATH &&
378 DevicePathSubType(node) == MEDIA_CDROM_DP) {
379 devpathcpy = efi_devpath_trim(devpath);
380 if (devpathcpy == NULL)
381 continue;
382 tmpdevpath = devpathcpy;
383 status = BS->LocateDevicePath(&blkio_guid, &tmpdevpath,
384 &handle);
385 free(devpathcpy);
386 if (EFI_ERROR(status))
387 continue;
388 devpath = efi_lookup_devpath(handle);
389 efipart_cdinfo_add(handle, efipart_handles[i],
390 devpath);
391 continue;
394 if (DevicePathType(node) == MESSAGING_DEVICE_PATH &&
395 DevicePathSubType(node) == MSG_ATAPI_DP) {
396 efipart_cdinfo_add(efipart_handles[i], NULL,
397 devpath);
398 continue;
401 /* USB or SATA cd without the media. */
402 if (blkio->Media->RemovableMedia &&
403 !blkio->Media->MediaPresent) {
404 efipart_cdinfo_add(efipart_handles[i], NULL,
405 devpath);
410 static int
411 efipart_initcd(void)
413 int rv;
415 rv = efipart_inithandles();
416 if (rv != 0)
417 return (rv);
418 STAILQ_INIT(&cdinfo);
420 efipart_updatecd();
422 bcache_add_dev(efiblk_pdinfo_count(&cdinfo));
423 return (0);
426 static int
427 efipart_hdinfo_add(EFI_HANDLE disk_handle, EFI_HANDLE part_handle)
429 EFI_DEVICE_PATH *disk_devpath, *part_devpath;
430 HARDDRIVE_DEVICE_PATH *node;
431 int unit;
432 pdinfo_t *hd, *pd, *last;
434 disk_devpath = efi_lookup_devpath(disk_handle);
435 if (disk_devpath == NULL)
436 return (ENOENT);
438 if (part_handle != NULL) {
439 part_devpath = efi_lookup_devpath(part_handle);
440 if (part_devpath == NULL)
441 return (ENOENT);
442 node = (HARDDRIVE_DEVICE_PATH *)
443 efi_devpath_last_node(part_devpath);
444 if (node == NULL)
445 return (ENOENT); /* This should not happen. */
446 } else {
447 part_devpath = NULL;
448 node = NULL;
451 pd = calloc(1, sizeof(pdinfo_t));
452 if (pd == NULL) {
453 printf("Failed to add disk, out of memory\n");
454 return (ENOMEM);
456 STAILQ_INIT(&pd->pd_part);
458 STAILQ_FOREACH(hd, &hdinfo, pd_link) {
459 if (efi_devpath_match(hd->pd_devpath, disk_devpath) == true) {
460 if (part_devpath == NULL)
461 return (0);
463 /* Add the partition. */
464 pd->pd_handle = part_handle;
465 pd->pd_unit = node->PartitionNumber;
466 pd->pd_devpath = part_devpath;
467 STAILQ_INSERT_TAIL(&hd->pd_part, pd, pd_link);
468 return (0);
472 last = STAILQ_LAST(&hdinfo, pdinfo, pd_link);
473 if (last != NULL)
474 unit = last->pd_unit + 1;
475 else
476 unit = 0;
478 /* Add the disk. */
479 hd = pd;
480 hd->pd_handle = disk_handle;
481 hd->pd_unit = unit;
482 hd->pd_devpath = disk_devpath;
483 STAILQ_INSERT_TAIL(&hdinfo, hd, pd_link);
485 if (part_devpath == NULL)
486 return (0);
488 pd = calloc(1, sizeof(pdinfo_t));
489 if (pd == NULL) {
490 printf("Failed to add partition, out of memory\n");
491 return (ENOMEM);
493 STAILQ_INIT(&pd->pd_part);
495 /* Add the partition. */
496 pd->pd_handle = part_handle;
497 pd->pd_unit = node->PartitionNumber;
498 pd->pd_devpath = part_devpath;
499 STAILQ_INSERT_TAIL(&hd->pd_part, pd, pd_link);
501 return (0);
505 * The MEDIA_FILEPATH_DP has device name.
506 * From U-Boot sources it looks like names are in the form
507 * of typeN:M, where type is interface type, N is disk id
508 * and M is partition id.
510 static int
511 efipart_hdinfo_add_filepath(EFI_HANDLE disk_handle)
513 EFI_DEVICE_PATH *devpath;
514 FILEPATH_DEVICE_PATH *node;
515 char *pathname, *p;
516 int unit, len;
517 pdinfo_t *pd, *last;
519 /* First collect and verify all the data */
520 if ((devpath = efi_lookup_devpath(disk_handle)) == NULL)
521 return (ENOENT);
522 node = (FILEPATH_DEVICE_PATH *)efi_devpath_last_node(devpath);
523 if (node == NULL)
524 return (ENOENT); /* This should not happen. */
526 pd = calloc(1, sizeof(pdinfo_t));
527 if (pd == NULL) {
528 printf("Failed to add disk, out of memory\n");
529 return (ENOMEM);
531 STAILQ_INIT(&pd->pd_part);
532 last = STAILQ_LAST(&hdinfo, pdinfo, pd_link);
533 if (last != NULL)
534 unit = last->pd_unit + 1;
535 else
536 unit = 0;
538 /* FILEPATH_DEVICE_PATH has 0 terminated string */
539 for (len = 0; node->PathName[len] != 0; len++)
541 if ((pathname = malloc(len + 1)) == NULL) {
542 printf("Failed to add disk, out of memory\n");
543 free(pd);
544 return (ENOMEM);
546 cpy16to8(node->PathName, pathname, len + 1);
547 p = strchr(pathname, ':');
550 * Assume we are receiving handles in order, first disk handle,
551 * then partitions for this disk. If this assumption proves
552 * false, this code would need update.
554 if (p == NULL) { /* no colon, add the disk */
555 pd->pd_handle = disk_handle;
556 pd->pd_unit = unit;
557 pd->pd_devpath = devpath;
558 STAILQ_INSERT_TAIL(&hdinfo, pd, pd_link);
559 free(pathname);
560 return (0);
562 p++; /* skip the colon */
563 errno = 0;
564 unit = (int)strtol(p, NULL, 0);
565 if (errno != 0) {
566 printf("Bad unit number for partition \"%s\"\n", pathname);
567 free(pathname);
568 free(pd);
569 return (EUNIT);
573 * We should have disk registered, if not, we are receiving
574 * handles out of order, and this code should be reworked
575 * to create "blank" disk for partition, and to find the
576 * disk based on PathName compares.
578 if (last == NULL) {
579 printf("BUG: No disk for partition \"%s\"\n", pathname);
580 free(pathname);
581 free(pd);
582 return (EINVAL);
584 /* Add the partition. */
585 pd->pd_handle = disk_handle;
586 pd->pd_unit = unit;
587 pd->pd_devpath = devpath;
588 STAILQ_INSERT_TAIL(&last->pd_part, pd, pd_link);
589 free(pathname);
590 return (0);
593 static void
594 efipart_updatehd(void)
596 int i, nin;
597 EFI_DEVICE_PATH *devpath, *devpathcpy, *tmpdevpath, *node;
598 EFI_HANDLE handle;
599 EFI_BLOCK_IO *blkio;
600 EFI_STATUS status;
602 nin = efipart_nhandles / sizeof (*efipart_handles);
603 for (i = 0; i < nin; i++) {
604 devpath = efi_lookup_devpath(efipart_handles[i]);
605 if (devpath == NULL)
606 continue;
608 if ((node = efi_devpath_last_node(devpath)) == NULL)
609 continue;
611 if (!efipart_hdd(devpath))
612 continue;
614 status = BS->HandleProtocol(efipart_handles[i],
615 &blkio_guid, (void **)&blkio);
616 if (EFI_ERROR(status))
617 continue;
619 if (DevicePathType(node) == MEDIA_DEVICE_PATH &&
620 DevicePathSubType(node) == MEDIA_FILEPATH_DP) {
621 efipart_hdinfo_add_filepath(efipart_handles[i]);
622 continue;
625 if (DevicePathType(node) == MEDIA_DEVICE_PATH &&
626 DevicePathSubType(node) == MEDIA_HARDDRIVE_DP) {
627 devpathcpy = efi_devpath_trim(devpath);
628 if (devpathcpy == NULL)
629 continue;
630 tmpdevpath = devpathcpy;
631 status = BS->LocateDevicePath(&blkio_guid, &tmpdevpath,
632 &handle);
633 free(devpathcpy);
634 if (EFI_ERROR(status))
635 continue;
637 * We do not support nested partitions.
639 devpathcpy = efi_lookup_devpath(handle);
640 if (devpathcpy == NULL)
641 continue;
642 if ((node = efi_devpath_last_node(devpathcpy)) == NULL)
643 continue;
645 if (DevicePathType(node) == MEDIA_DEVICE_PATH &&
646 DevicePathSubType(node) == MEDIA_HARDDRIVE_DP)
647 continue;
649 efipart_hdinfo_add(handle, efipart_handles[i]);
650 continue;
653 efipart_hdinfo_add(efipart_handles[i], NULL);
657 static int
658 efipart_inithd(void)
660 int rv;
662 rv = efipart_inithandles();
663 if (rv != 0)
664 return (rv);
665 STAILQ_INIT(&hdinfo);
667 efipart_updatehd();
669 bcache_add_dev(efiblk_pdinfo_count(&hdinfo));
670 return (0);
673 static int
674 efipart_print_common(struct devsw *dev, pdinfo_list_t *pdlist, int verbose)
676 int ret = 0;
677 EFI_BLOCK_IO *blkio;
678 EFI_STATUS status;
679 EFI_HANDLE h;
680 pdinfo_t *pd;
681 CHAR16 *text;
682 struct disk_devdesc pd_dev;
683 char line[80];
685 if (STAILQ_EMPTY(pdlist))
686 return (0);
688 printf("%s devices:", dev->dv_name);
689 if ((ret = pager_output("\n")) != 0)
690 return (ret);
692 STAILQ_FOREACH(pd, pdlist, pd_link) {
693 h = pd->pd_handle;
694 if (verbose) { /* Output the device path. */
695 text = efi_devpath_name(efi_lookup_devpath(h));
696 if (text != NULL) {
697 printf(" %S", text);
698 efi_free_devpath_name(text);
699 if ((ret = pager_output("\n")) != 0)
700 break;
703 snprintf(line, sizeof(line),
704 " %s%d", dev->dv_name, pd->pd_unit);
705 printf("%s:", line);
706 status = BS->HandleProtocol(h, &blkio_guid, (void **)&blkio);
707 if (!EFI_ERROR(status)) {
708 printf(" %llu",
709 blkio->Media->LastBlock == 0? 0:
710 (unsigned long long) (blkio->Media->LastBlock + 1));
711 if (blkio->Media->LastBlock != 0) {
712 printf(" X %u", blkio->Media->BlockSize);
714 printf(" blocks");
715 if (blkio->Media->MediaPresent) {
716 if (blkio->Media->RemovableMedia)
717 printf(" (removable)");
718 } else {
719 printf(" (no media)");
721 if ((ret = pager_output("\n")) != 0)
722 break;
723 if (!blkio->Media->MediaPresent)
724 continue;
726 pd->pd_blkio = blkio;
727 pd_dev.d_dev = dev;
728 pd_dev.d_unit = pd->pd_unit;
729 pd_dev.d_slice = -1;
730 pd_dev.d_partition = -1;
731 pd_dev.d_opendata = blkio;
732 ret = disk_open(&pd_dev, blkio->Media->BlockSize *
733 (blkio->Media->LastBlock + 1),
734 blkio->Media->BlockSize);
735 if (ret == 0) {
736 ret = disk_print(&pd_dev, line, verbose);
737 disk_close(&pd_dev);
738 if (ret != 0)
739 return (ret);
740 } else {
741 /* Do not fail from disk_open() */
742 ret = 0;
744 } else {
745 if ((ret = pager_output("\n")) != 0)
746 break;
749 return (ret);
752 static int
753 efipart_printfd(int verbose)
755 return (efipart_print_common(&efipart_fddev, &fdinfo, verbose));
758 static int
759 efipart_printcd(int verbose)
761 return (efipart_print_common(&efipart_cddev, &cdinfo, verbose));
764 static int
765 efipart_printhd(int verbose)
767 return (efipart_print_common(&efipart_hddev, &hdinfo, verbose));
770 pdinfo_list_t *
771 efiblk_get_pdinfo_list(struct devsw *dev)
773 if (dev->dv_type == DEVT_DISK)
774 return (&hdinfo);
775 if (dev->dv_type == DEVT_CD)
776 return (&cdinfo);
777 if (dev->dv_type == DEVT_FD)
778 return (&fdinfo);
779 return (NULL);
782 static int
783 efipart_open(struct open_file *f, ...)
785 va_list args;
786 struct disk_devdesc *dev;
787 pdinfo_list_t *pdi;
788 pdinfo_t *pd;
789 EFI_BLOCK_IO *blkio;
790 EFI_STATUS status;
792 va_start(args, f);
793 dev = va_arg(args, struct disk_devdesc*);
794 va_end(args);
795 if (dev == NULL)
796 return (EINVAL);
798 pdi = efiblk_get_pdinfo_list(dev->d_dev);
799 if (pdi == NULL)
800 return (EINVAL);
802 pd = efiblk_get_pdinfo(pdi, dev->d_unit);
803 if (pd == NULL)
804 return (EIO);
806 if (pd->pd_blkio == NULL) {
807 status = BS->HandleProtocol(pd->pd_handle, &blkio_guid,
808 (void **)&pd->pd_blkio);
809 if (EFI_ERROR(status))
810 return (efi_status_to_errno(status));
813 blkio = pd->pd_blkio;
814 if (!blkio->Media->MediaPresent)
815 return (EAGAIN);
817 pd->pd_open++;
818 if (pd->pd_bcache == NULL)
819 pd->pd_bcache = bcache_allocate();
821 if (dev->d_dev->dv_type == DEVT_DISK) {
822 int rc;
824 rc = disk_open(dev,
825 blkio->Media->BlockSize * (blkio->Media->LastBlock + 1),
826 blkio->Media->BlockSize);
827 if (rc != 0) {
828 pd->pd_open--;
829 if (pd->pd_open == 0) {
830 pd->pd_blkio = NULL;
831 bcache_free(pd->pd_bcache);
832 pd->pd_bcache = NULL;
835 return (rc);
837 return (0);
840 static int
841 efipart_close(struct open_file *f)
843 struct disk_devdesc *dev;
844 pdinfo_list_t *pdi;
845 pdinfo_t *pd;
847 dev = (struct disk_devdesc *)(f->f_devdata);
848 if (dev == NULL)
849 return (EINVAL);
850 pdi = efiblk_get_pdinfo_list(dev->d_dev);
851 if (pdi == NULL)
852 return (EINVAL);
854 pd = efiblk_get_pdinfo(pdi, dev->d_unit);
855 if (pd == NULL)
856 return (EINVAL);
858 pd->pd_open--;
859 if (pd->pd_open == 0) {
860 pd->pd_blkio = NULL;
861 bcache_free(pd->pd_bcache);
862 pd->pd_bcache = NULL;
864 if (dev->d_dev->dv_type == DEVT_DISK)
865 return (disk_close(dev));
866 return (0);
869 static int
870 efipart_ioctl(struct open_file *f, u_long cmd, void *data)
872 struct disk_devdesc *dev;
873 pdinfo_list_t *pdi;
874 pdinfo_t *pd;
875 int rc;
877 dev = (struct disk_devdesc *)(f->f_devdata);
878 if (dev == NULL)
879 return (EINVAL);
880 pdi = efiblk_get_pdinfo_list(dev->d_dev);
881 if (pdi == NULL)
882 return (EINVAL);
884 pd = efiblk_get_pdinfo(pdi, dev->d_unit);
885 if (pd == NULL)
886 return (EINVAL);
888 if (dev->d_dev->dv_type == DEVT_DISK) {
889 rc = disk_ioctl(dev, cmd, data);
890 if (rc != ENOTTY)
891 return (rc);
894 switch (cmd) {
895 case DIOCGSECTORSIZE:
896 *(u_int *)data = pd->pd_blkio->Media->BlockSize;
897 break;
898 case DIOCGMEDIASIZE:
899 *(uint64_t *)data = pd->pd_blkio->Media->BlockSize *
900 (pd->pd_blkio->Media->LastBlock + 1);
901 break;
902 default:
903 return (ENOTTY);
906 return (0);
910 * efipart_readwrite()
911 * Internal equivalent of efipart_strategy(), which operates on the
912 * media-native block size. This function expects all I/O requests
913 * to be within the media size and returns an error if such is not
914 * the case.
916 static int
917 efipart_readwrite(EFI_BLOCK_IO *blkio, int rw, daddr_t blk, daddr_t nblks,
918 char *buf)
920 EFI_STATUS status;
922 if (blkio == NULL)
923 return (ENXIO);
924 if (blk < 0 || blk > blkio->Media->LastBlock)
925 return (EIO);
926 if ((blk + nblks - 1) > blkio->Media->LastBlock)
927 return (EIO);
929 switch (rw & F_MASK) {
930 case F_READ:
931 status = blkio->ReadBlocks(blkio, blkio->Media->MediaId, blk,
932 nblks * blkio->Media->BlockSize, buf);
933 break;
934 case F_WRITE:
935 if (blkio->Media->ReadOnly)
936 return (EROFS);
937 status = blkio->WriteBlocks(blkio, blkio->Media->MediaId, blk,
938 nblks * blkio->Media->BlockSize, buf);
939 break;
940 default:
941 return (ENOSYS);
944 if (EFI_ERROR(status)) {
945 printf("%s: rw=%d, blk=%ju size=%ju status=%lu\n", __func__, rw,
946 blk, nblks, EFI_ERROR_CODE(status));
948 return (efi_status_to_errno(status));
951 static int
952 efipart_strategy(void *devdata, int rw, daddr_t blk, size_t size,
953 char *buf, size_t *rsize)
955 struct bcache_devdata bcd;
956 struct disk_devdesc *dev;
957 pdinfo_list_t *pdi;
958 pdinfo_t *pd;
960 dev = (struct disk_devdesc *)devdata;
961 if (dev == NULL)
962 return (EINVAL);
963 pdi = efiblk_get_pdinfo_list(dev->d_dev);
964 if (pdi == NULL)
965 return (EINVAL);
967 pd = efiblk_get_pdinfo(pdi, dev->d_unit);
968 if (pd == NULL)
969 return (EINVAL);
971 if (pd->pd_blkio->Media->RemovableMedia &&
972 !pd->pd_blkio->Media->MediaPresent)
973 return (ENXIO);
975 bcd.dv_strategy = efipart_realstrategy;
976 bcd.dv_devdata = devdata;
977 bcd.dv_cache = pd->pd_bcache;
979 if (dev->d_dev->dv_type == DEVT_DISK) {
980 daddr_t offset;
982 offset = dev->d_offset * pd->pd_blkio->Media->BlockSize;
983 offset /= 512;
984 return (bcache_strategy(&bcd, rw, blk + offset,
985 size, buf, rsize));
987 return (bcache_strategy(&bcd, rw, blk, size, buf, rsize));
990 static int
991 efipart_realstrategy(void *devdata, int rw, daddr_t blk, size_t size,
992 char *buf, size_t *rsize)
994 struct disk_devdesc *dev = (struct disk_devdesc *)devdata;
995 pdinfo_list_t *pdi;
996 pdinfo_t *pd;
997 EFI_BLOCK_IO *blkio;
998 uint64_t off, disk_blocks, d_offset = 0;
999 char *blkbuf;
1000 size_t blkoff, blksz;
1001 int error;
1002 uint64_t diskend, readstart;
1004 if (dev == NULL || blk < 0)
1005 return (EINVAL);
1007 pdi = efiblk_get_pdinfo_list(dev->d_dev);
1008 if (pdi == NULL)
1009 return (EINVAL);
1011 pd = efiblk_get_pdinfo(pdi, dev->d_unit);
1012 if (pd == NULL)
1013 return (EINVAL);
1015 blkio = pd->pd_blkio;
1016 if (blkio == NULL)
1017 return (ENXIO);
1019 if (size == 0 || (size % 512) != 0)
1020 return (EIO);
1022 off = blk * 512;
1024 * Get disk blocks, this value is either for whole disk or for
1025 * partition.
1027 disk_blocks = 0;
1028 if (dev->d_dev->dv_type == DEVT_DISK) {
1029 if (disk_ioctl(dev, DIOCGMEDIASIZE, &disk_blocks) == 0) {
1030 /* DIOCGMEDIASIZE does return bytes. */
1031 disk_blocks /= blkio->Media->BlockSize;
1033 d_offset = dev->d_offset;
1035 if (disk_blocks == 0)
1036 disk_blocks = blkio->Media->LastBlock + 1 - d_offset;
1038 /* make sure we don't read past disk end */
1039 if ((off + size) / blkio->Media->BlockSize > d_offset + disk_blocks) {
1040 diskend = d_offset + disk_blocks;
1041 readstart = off / blkio->Media->BlockSize;
1043 if (diskend <= readstart) {
1044 if (rsize != NULL)
1045 *rsize = 0;
1047 return (EIO);
1049 size = diskend - readstart;
1050 size = size * blkio->Media->BlockSize;
1053 if (rsize != NULL)
1054 *rsize = size;
1056 if ((size % blkio->Media->BlockSize == 0) &&
1057 (off % blkio->Media->BlockSize == 0))
1058 return (efipart_readwrite(blkio, rw,
1059 off / blkio->Media->BlockSize,
1060 size / blkio->Media->BlockSize, buf));
1063 * The buffer size is not a multiple of the media block size.
1065 blkbuf = malloc(blkio->Media->BlockSize);
1066 if (blkbuf == NULL)
1067 return (ENOMEM);
1069 error = 0;
1070 blk = off / blkio->Media->BlockSize;
1071 blkoff = off % blkio->Media->BlockSize;
1072 blksz = blkio->Media->BlockSize - blkoff;
1073 while (size > 0) {
1074 error = efipart_readwrite(blkio, rw, blk, 1, blkbuf);
1075 if (error)
1076 break;
1077 if (size < blksz)
1078 blksz = size;
1079 bcopy(blkbuf + blkoff, buf, blksz);
1080 buf += blksz;
1081 size -= blksz;
1082 blk++;
1083 blkoff = 0;
1084 blksz = blkio->Media->BlockSize;
1087 free(blkbuf);
1088 return (error);