2 * Copyright (c) 2010 Marcel Moolenaar
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
31 #include <sys/param.h>
33 #include <sys/queue.h>
37 #include <bootstrap.h>
45 static EFI_GUID blkio_guid = BLOCK_IO_PROTOCOL;
47 static int efipart_initfd(void);
48 static int efipart_initcd(void);
49 static int efipart_inithd(void);
51 static int efipart_strategy(void *, int, daddr_t, size_t, char *, size_t *);
52 static int efipart_realstrategy(void *, int, daddr_t, size_t, char *, size_t *);
54 static int efipart_open(struct open_file *, ...);
55 static int efipart_close(struct open_file *);
56 static int efipart_ioctl(struct open_file *, u_long, void *);
58 static int efipart_printfd(int);
59 static int efipart_printcd(int);
60 static int efipart_printhd(int);
62 /* EISA PNP ID's for floppy controllers */
67 struct devsw efipart_fddev = {
70 .dv_init = efipart_initfd,
71 .dv_strategy = efipart_strategy,
72 .dv_open = efipart_open,
73 .dv_close = efipart_close,
74 .dv_ioctl = efipart_ioctl,
75 .dv_print = efipart_printfd,
79 struct devsw efipart_cddev = {
82 .dv_init = efipart_initcd,
83 .dv_strategy = efipart_strategy,
84 .dv_open = efipart_open,
85 .dv_close = efipart_close,
86 .dv_ioctl = efipart_ioctl,
87 .dv_print = efipart_printcd,
91 struct devsw efipart_hddev = {
94 .dv_init = efipart_inithd,
95 .dv_strategy = efipart_strategy,
96 .dv_open = efipart_open,
97 .dv_close = efipart_close,
98 .dv_ioctl = efipart_ioctl,
99 .dv_print = efipart_printhd,
103 static pdinfo_list_t fdinfo = STAILQ_HEAD_INITIALIZER(fdinfo);
104 static pdinfo_list_t cdinfo = STAILQ_HEAD_INITIALIZER(cdinfo);
105 static pdinfo_list_t hdinfo = STAILQ_HEAD_INITIALIZER(hdinfo);
108 * efipart_inithandles() is used to build up the pdinfo list from
109 * block device handles. Then each devsw init callback is used to
110 * pick items from pdinfo and move to proper device list.
111 * In ideal world, we should end up with empty pdinfo once all
112 * devsw initializers are called.
114 static pdinfo_list_t pdinfo = STAILQ_HEAD_INITIALIZER(pdinfo);
117 efiblk_get_pdinfo_list(struct devsw *dev)
119 if (dev->dv_type == DEVT_DISK)
121 if (dev->dv_type == DEVT_CD)
123 if (dev->dv_type == DEVT_FD)
128 /* XXX this gets called way way too often, investigate */
130 efiblk_get_pdinfo(struct devdesc *dev)
135 pdi = efiblk_get_pdinfo_list(dev->d_dev);
139 STAILQ_FOREACH(pd, pdi, pd_link) {
140 if (pd->pd_unit == dev->d_unit)
147 efiblk_get_pdinfo_by_device_path(EFI_DEVICE_PATH *path)
151 EFI_DEVICE_PATH *devp = path;
153 status = BS->LocateDevicePath(&blkio_guid, &devp, &h);
154 if (EFI_ERROR(status))
156 return (efiblk_get_pdinfo_by_handle(h));
160 same_handle(pdinfo_t *pd, EFI_HANDLE h)
163 return (pd->pd_handle == h || pd->pd_alias == h);
167 efiblk_get_pdinfo_by_handle(EFI_HANDLE h)
172 * Check hard disks, then cd, then floppy
174 STAILQ_FOREACH(dp, &hdinfo, pd_link) {
175 if (same_handle(dp, h))
177 STAILQ_FOREACH(pp, &dp->pd_part, pd_link) {
178 if (same_handle(pp, h))
182 STAILQ_FOREACH(dp, &cdinfo, pd_link) {
183 if (same_handle(dp, h))
185 STAILQ_FOREACH(pp, &dp->pd_part, pd_link) {
186 if (same_handle(pp, h))
190 STAILQ_FOREACH(dp, &fdinfo, pd_link) {
191 if (same_handle(dp, h))
198 efiblk_pdinfo_count(pdinfo_list_t *pdi)
203 STAILQ_FOREACH(pd, pdi, pd_link) {
210 efipart_inithandles(void)
215 EFI_DEVICE_PATH *devpath;
220 if (!STAILQ_EMPTY(&pdinfo))
225 status = BS->LocateHandle(ByProtocol, &blkio_guid, 0, &sz, hin);
226 if (status == EFI_BUFFER_TOO_SMALL) {
228 status = BS->LocateHandle(ByProtocol, &blkio_guid, 0, &sz,
230 if (EFI_ERROR(status))
233 if (EFI_ERROR(status))
234 return (efi_status_to_errno(status));
236 nin = sz / sizeof(*hin);
238 printf("%s: Got %d BLOCK IO MEDIA handle(s)\n", __func__, nin);
241 for (i = 0; i < nin; i++) {
243 * Get devpath and open protocol.
244 * We should not get errors here
246 if ((devpath = efi_lookup_devpath(hin[i])) == NULL)
249 status = OpenProtocolByHandle(hin[i], &blkio_guid,
251 if (EFI_ERROR(status)) {
252 printf("error %lu\n", EFI_ERROR_CODE(status));
257 * We assume the block size 512 or greater power of 2.
258 * Also skip devices with block size > 64k (16 is max
259 * ashift supported by zfs).
260 * iPXE is known to insert stub BLOCK IO device with
263 if (blkio->Media->BlockSize < 512 ||
264 blkio->Media->BlockSize > (1 << 16) ||
265 !powerof2(blkio->Media->BlockSize)) {
270 if ((pd = calloc(1, sizeof(*pd))) == NULL) {
271 printf("efipart_inithandles: Out of memory.\n");
275 STAILQ_INIT(&pd->pd_part);
277 pd->pd_handle = hin[i];
278 pd->pd_devpath = devpath;
279 pd->pd_blkio = blkio;
280 STAILQ_INSERT_TAIL(&pdinfo, pd, pd_link);
287 static ACPI_HID_DEVICE_PATH *
288 efipart_floppy(EFI_DEVICE_PATH *node)
290 ACPI_HID_DEVICE_PATH *acpi;
292 if (DevicePathType(node) == ACPI_DEVICE_PATH &&
293 DevicePathSubType(node) == ACPI_DP) {
294 acpi = (ACPI_HID_DEVICE_PATH *) node;
295 if (acpi->HID == EISA_PNP_ID(PNP0604) ||
296 acpi->HID == EISA_PNP_ID(PNP0700) ||
297 acpi->HID == EISA_PNP_ID(PNP0701)) {
305 efipart_find_parent(pdinfo_list_t *pdi, EFI_DEVICE_PATH *devpath)
309 STAILQ_FOREACH(pd, pdi, pd_link) {
310 if (efi_devpath_is_prefix(pd->pd_devpath, devpath))
319 EFI_DEVICE_PATH *node;
320 ACPI_HID_DEVICE_PATH *acpi;
321 pdinfo_t *parent, *fd;
324 STAILQ_FOREACH(fd, &pdinfo, pd_link) {
325 if ((node = efi_devpath_last_node(fd->pd_devpath)) == NULL)
328 if ((acpi = efipart_floppy(node)) == NULL)
331 STAILQ_REMOVE(&pdinfo, fd, pdinfo, pd_link);
332 parent = efipart_find_parent(&pdinfo, fd->pd_devpath);
333 if (parent != NULL) {
334 STAILQ_REMOVE(&pdinfo, parent, pdinfo, pd_link);
335 parent->pd_alias = fd->pd_handle;
336 parent->pd_unit = acpi->UID;
340 fd->pd_unit = acpi->UID;
342 fd->pd_devsw = &efipart_fddev;
343 STAILQ_INSERT_TAIL(&fdinfo, fd, pd_link);
347 bcache_add_dev(efiblk_pdinfo_count(&fdinfo));
352 * Add or update entries with new handle data.
355 efipart_cdinfo_add(pdinfo_t *cd)
359 STAILQ_FOREACH(pd, &cdinfo, pd_link) {
360 if (efi_devpath_is_prefix(pd->pd_devpath, cd->pd_devpath)) {
361 last = STAILQ_LAST(&pd->pd_part, pdinfo, pd_link);
363 cd->pd_unit = last->pd_unit + 1;
367 cd->pd_devsw = &efipart_cddev;
368 STAILQ_INSERT_TAIL(&pd->pd_part, cd, pd_link);
373 last = STAILQ_LAST(&cdinfo, pdinfo, pd_link);
375 cd->pd_unit = last->pd_unit + 1;
379 cd->pd_parent = NULL;
380 cd->pd_devsw = &efipart_cddev;
381 STAILQ_INSERT_TAIL(&cdinfo, cd, pd_link);
385 efipart_testcd(EFI_DEVICE_PATH *node, EFI_BLOCK_IO *blkio)
387 if (DevicePathType(node) == MEDIA_DEVICE_PATH &&
388 DevicePathSubType(node) == MEDIA_CDROM_DP) {
392 /* cd drive without the media. */
393 if (blkio->Media->RemovableMedia &&
394 !blkio->Media->MediaPresent) {
402 efipart_updatecd(void)
404 EFI_DEVICE_PATH *devpath, *node;
406 pdinfo_t *parent, *cd;
409 STAILQ_FOREACH(cd, &pdinfo, pd_link) {
410 if ((node = efi_devpath_last_node(cd->pd_devpath)) == NULL)
413 if (efipart_floppy(node) != NULL)
416 /* Is parent of this device already registered? */
417 parent = efipart_find_parent(&cdinfo, cd->pd_devpath);
418 if (parent != NULL) {
419 STAILQ_REMOVE(&pdinfo, cd, pdinfo, pd_link);
420 efipart_cdinfo_add(cd);
424 if (!efipart_testcd(node, cd->pd_blkio))
427 /* Find parent and unlink both parent and cd from pdinfo */
428 STAILQ_REMOVE(&pdinfo, cd, pdinfo, pd_link);
429 parent = efipart_find_parent(&pdinfo, cd->pd_devpath);
430 if (parent != NULL) {
431 STAILQ_REMOVE(&pdinfo, parent, pdinfo, pd_link);
432 efipart_cdinfo_add(parent);
436 parent = efipart_find_parent(&cdinfo, cd->pd_devpath);
439 * If we come across a logical partition of subtype CDROM
440 * it doesn't refer to the CD filesystem itself, but rather
441 * to any usable El Torito boot image on it. In this case
442 * we try to find the parent device and add that instead as
443 * that will be the CD filesystem.
445 if (DevicePathType(node) == MEDIA_DEVICE_PATH &&
446 DevicePathSubType(node) == MEDIA_CDROM_DP &&
448 parent = calloc(1, sizeof(*parent));
449 if (parent == NULL) {
450 printf("efipart_updatecd: out of memory\n");
451 /* this device is lost but try again. */
456 devpath = efi_devpath_trim(cd->pd_devpath);
457 if (devpath == NULL) {
458 printf("efipart_updatecd: out of memory\n");
459 /* this device is lost but try again. */
464 parent->pd_devpath = devpath;
465 status = BS->LocateDevicePath(&blkio_guid,
466 &parent->pd_devpath, &parent->pd_handle);
468 if (EFI_ERROR(status)) {
469 printf("efipart_updatecd: error %lu\n",
470 EFI_ERROR_CODE(status));
476 efi_lookup_devpath(parent->pd_handle);
477 efipart_cdinfo_add(parent);
480 efipart_cdinfo_add(cd);
490 bcache_add_dev(efiblk_pdinfo_count(&cdinfo));
495 efipart_hdinfo_add(pdinfo_t *hd, HARDDRIVE_DEVICE_PATH *node)
499 STAILQ_FOREACH(pd, &hdinfo, pd_link) {
500 if (efi_devpath_is_prefix(pd->pd_devpath, hd->pd_devpath)) {
501 /* Add the partition. */
502 hd->pd_unit = node->PartitionNumber;
504 hd->pd_devsw = &efipart_hddev;
505 STAILQ_INSERT_TAIL(&pd->pd_part, hd, pd_link);
510 last = STAILQ_LAST(&hdinfo, pdinfo, pd_link);
512 hd->pd_unit = last->pd_unit + 1;
517 hd->pd_devsw = &efipart_hddev;
518 STAILQ_INSERT_TAIL(&hdinfo, hd, pd_link);
522 * The MEDIA_FILEPATH_DP has device name.
523 * From U-Boot sources it looks like names are in the form
524 * of typeN:M, where type is interface type, N is disk id
525 * and M is partition id.
528 efipart_hdinfo_add_filepath(pdinfo_t *hd, FILEPATH_DEVICE_PATH *node)
534 last = STAILQ_LAST(&hdinfo, pdinfo, pd_link);
536 hd->pd_unit = last->pd_unit + 1;
540 /* FILEPATH_DEVICE_PATH has 0 terminated string */
541 len = ucs2len(node->PathName);
542 if ((pathname = malloc(len + 1)) == NULL) {
543 printf("Failed to add disk, out of memory\n");
547 cpy16to8(node->PathName, pathname, len + 1);
548 p = strchr(pathname, ':');
551 * Assume we are receiving handles in order, first disk handle,
552 * then partitions for this disk. If this assumption proves
553 * false, this code would need update.
555 if (p == NULL) { /* no colon, add the disk */
556 hd->pd_devsw = &efipart_hddev;
557 STAILQ_INSERT_TAIL(&hdinfo, hd, pd_link);
561 p++; /* skip the colon */
563 hd->pd_unit = (int)strtol(p, NULL, 0);
565 printf("Bad unit number for partition \"%s\"\n", pathname);
572 * We should have disk registered, if not, we are receiving
573 * handles out of order, and this code should be reworked
574 * to create "blank" disk for partition, and to find the
575 * disk based on PathName compares.
578 printf("BUG: No disk for partition \"%s\"\n", pathname);
583 /* Add the partition. */
584 hd->pd_parent = last;
585 hd->pd_devsw = &efipart_hddev;
586 STAILQ_INSERT_TAIL(&last->pd_part, hd, pd_link);
591 efipart_updatehd(void)
593 EFI_DEVICE_PATH *devpath, *node;
595 pdinfo_t *parent, *hd;
598 STAILQ_FOREACH(hd, &pdinfo, pd_link) {
599 if ((node = efi_devpath_last_node(hd->pd_devpath)) == NULL)
602 if (efipart_floppy(node) != NULL)
605 if (efipart_testcd(node, hd->pd_blkio))
608 if (DevicePathType(node) == HARDWARE_DEVICE_PATH &&
609 (DevicePathSubType(node) == HW_PCI_DP ||
610 DevicePathSubType(node) == HW_VENDOR_DP)) {
611 STAILQ_REMOVE(&pdinfo, hd, pdinfo, pd_link);
612 efipart_hdinfo_add(hd, NULL);
616 if (DevicePathType(node) == MEDIA_DEVICE_PATH &&
617 DevicePathSubType(node) == MEDIA_FILEPATH_DP) {
618 STAILQ_REMOVE(&pdinfo, hd, pdinfo, pd_link);
619 efipart_hdinfo_add_filepath(hd,
620 (FILEPATH_DEVICE_PATH *)node);
624 STAILQ_REMOVE(&pdinfo, hd, pdinfo, pd_link);
625 parent = efipart_find_parent(&pdinfo, hd->pd_devpath);
626 if (parent != NULL) {
627 STAILQ_REMOVE(&pdinfo, parent, pdinfo, pd_link);
628 efipart_hdinfo_add(parent, NULL);
630 parent = efipart_find_parent(&hdinfo, hd->pd_devpath);
633 if (DevicePathType(node) == MEDIA_DEVICE_PATH &&
634 DevicePathSubType(node) == MEDIA_HARDDRIVE_DP &&
636 parent = calloc(1, sizeof(*parent));
637 if (parent == NULL) {
638 printf("efipart_updatehd: out of memory\n");
639 /* this device is lost but try again. */
644 devpath = efi_devpath_trim(hd->pd_devpath);
645 if (devpath == NULL) {
646 printf("efipart_updatehd: out of memory\n");
647 /* this device is lost but try again. */
653 parent->pd_devpath = devpath;
654 status = BS->LocateDevicePath(&blkio_guid,
655 &parent->pd_devpath, &parent->pd_handle);
657 if (EFI_ERROR(status)) {
658 printf("efipart_updatehd: error %lu\n",
659 EFI_ERROR_CODE(status));
666 efi_lookup_devpath(&parent->pd_handle);
668 efipart_hdinfo_add(parent, NULL);
671 efipart_hdinfo_add(hd, (HARDDRIVE_DEVICE_PATH *)node);
682 bcache_add_dev(efiblk_pdinfo_count(&hdinfo));
687 efipart_print_common(struct devsw *dev, pdinfo_list_t *pdlist, int verbose)
695 struct disk_devdesc pd_dev;
698 if (STAILQ_EMPTY(pdlist))
701 printf("%s devices:", dev->dv_name);
702 if ((ret = pager_output("\n")) != 0)
705 STAILQ_FOREACH(pd, pdlist, pd_link) {
707 if (verbose) { /* Output the device path. */
708 text = efi_devpath_name(efi_lookup_devpath(h));
711 efi_free_devpath_name(text);
712 if ((ret = pager_output("\n")) != 0)
716 snprintf(line, sizeof(line),
717 " %s%d", dev->dv_name, pd->pd_unit);
719 status = OpenProtocolByHandle(h, &blkio_guid, (void **)&blkio);
720 if (!EFI_ERROR(status)) {
722 blkio->Media->LastBlock == 0? 0:
723 (unsigned long long) (blkio->Media->LastBlock + 1));
724 if (blkio->Media->LastBlock != 0) {
725 printf(" X %u", blkio->Media->BlockSize);
728 if (blkio->Media->MediaPresent) {
729 if (blkio->Media->RemovableMedia)
730 printf(" (removable)");
732 printf(" (no media)");
734 if ((ret = pager_output("\n")) != 0)
736 if (!blkio->Media->MediaPresent)
739 pd->pd_blkio = blkio;
740 pd_dev.dd.d_dev = dev;
741 pd_dev.dd.d_unit = pd->pd_unit;
742 pd_dev.d_slice = D_SLICENONE;
743 pd_dev.d_partition = D_PARTNONE;
744 ret = disk_open(&pd_dev, blkio->Media->BlockSize *
745 (blkio->Media->LastBlock + 1),
746 blkio->Media->BlockSize);
748 ret = disk_print(&pd_dev, line, verbose);
753 /* Do not fail from disk_open() */
757 if ((ret = pager_output("\n")) != 0)
765 efipart_printfd(int verbose)
767 return (efipart_print_common(&efipart_fddev, &fdinfo, verbose));
771 efipart_printcd(int verbose)
773 return (efipart_print_common(&efipart_cddev, &cdinfo, verbose));
777 efipart_printhd(int verbose)
779 return (efipart_print_common(&efipart_hddev, &hdinfo, verbose));
783 efipart_open(struct open_file *f, ...)
786 struct disk_devdesc *dev;
792 dev = va_arg(args, struct disk_devdesc *);
797 pd = efiblk_get_pdinfo((struct devdesc *)dev);
801 if (pd->pd_blkio == NULL) {
802 status = OpenProtocolByHandle(pd->pd_handle, &blkio_guid,
803 (void **)&pd->pd_blkio);
804 if (EFI_ERROR(status))
805 return (efi_status_to_errno(status));
808 blkio = pd->pd_blkio;
809 if (!blkio->Media->MediaPresent)
813 if (pd->pd_bcache == NULL)
814 pd->pd_bcache = bcache_allocate();
816 if (dev->dd.d_dev->dv_type == DEVT_DISK) {
820 blkio->Media->BlockSize * (blkio->Media->LastBlock + 1),
821 blkio->Media->BlockSize);
824 if (pd->pd_open == 0) {
826 bcache_free(pd->pd_bcache);
827 pd->pd_bcache = NULL;
836 efipart_close(struct open_file *f)
838 struct disk_devdesc *dev;
841 dev = (struct disk_devdesc *)(f->f_devdata);
845 pd = efiblk_get_pdinfo((struct devdesc *)dev);
850 if (pd->pd_open == 0) {
852 bcache_free(pd->pd_bcache);
853 pd->pd_bcache = NULL;
855 if (dev->dd.d_dev->dv_type == DEVT_DISK)
856 return (disk_close(dev));
861 efipart_ioctl(struct open_file *f, u_long cmd, void *data)
863 struct disk_devdesc *dev;
867 dev = (struct disk_devdesc *)(f->f_devdata);
871 pd = efiblk_get_pdinfo((struct devdesc *)dev);
875 if (dev->dd.d_dev->dv_type == DEVT_DISK) {
876 rc = disk_ioctl(dev, cmd, data);
882 case DIOCGSECTORSIZE:
883 *(u_int *)data = pd->pd_blkio->Media->BlockSize;
886 *(uint64_t *)data = pd->pd_blkio->Media->BlockSize *
887 (pd->pd_blkio->Media->LastBlock + 1);
897 * efipart_readwrite()
898 * Internal equivalent of efipart_strategy(), which operates on the
899 * media-native block size. This function expects all I/O requests
900 * to be within the media size and returns an error if such is not
904 efipart_readwrite(EFI_BLOCK_IO *blkio, int rw, daddr_t blk, daddr_t nblks,
911 if (blk < 0 || blk > blkio->Media->LastBlock)
913 if ((blk + nblks - 1) > blkio->Media->LastBlock)
916 switch (rw & F_MASK) {
918 status = blkio->ReadBlocks(blkio, blkio->Media->MediaId, blk,
919 nblks * blkio->Media->BlockSize, buf);
922 if (blkio->Media->ReadOnly)
924 status = blkio->WriteBlocks(blkio, blkio->Media->MediaId, blk,
925 nblks * blkio->Media->BlockSize, buf);
931 if (EFI_ERROR(status)) {
932 printf("%s: rw=%d, blk=%ju size=%ju status=%lu\n", __func__, rw,
933 blk, nblks, EFI_ERROR_CODE(status));
935 return (efi_status_to_errno(status));
939 efipart_strategy(void *devdata, int rw, daddr_t blk, size_t size,
940 char *buf, size_t *rsize)
942 struct bcache_devdata bcd;
943 struct disk_devdesc *dev;
946 dev = (struct disk_devdesc *)devdata;
950 pd = efiblk_get_pdinfo((struct devdesc *)dev);
954 if (pd->pd_blkio->Media->RemovableMedia &&
955 !pd->pd_blkio->Media->MediaPresent)
958 bcd.dv_strategy = efipart_realstrategy;
959 bcd.dv_devdata = devdata;
960 bcd.dv_cache = pd->pd_bcache;
962 if (dev->dd.d_dev->dv_type == DEVT_DISK) {
965 offset = dev->d_offset * pd->pd_blkio->Media->BlockSize;
967 return (bcache_strategy(&bcd, rw, blk + offset,
970 return (bcache_strategy(&bcd, rw, blk, size, buf, rsize));
974 efipart_realstrategy(void *devdata, int rw, daddr_t blk, size_t size,
975 char *buf, size_t *rsize)
977 struct disk_devdesc *dev = (struct disk_devdesc *)devdata;
980 uint64_t off, disk_blocks, d_offset = 0;
982 size_t blkoff, blksz;
984 uint64_t diskend, readstart;
986 if (dev == NULL || blk < 0)
989 pd = efiblk_get_pdinfo((struct devdesc *)dev);
993 blkio = pd->pd_blkio;
997 if (size == 0 || (size % 512) != 0)
1002 * Get disk blocks, this value is either for whole disk or for
1006 if (dev->dd.d_dev->dv_type == DEVT_DISK) {
1007 if (disk_ioctl(dev, DIOCGMEDIASIZE, &disk_blocks) == 0) {
1008 /* DIOCGMEDIASIZE does return bytes. */
1009 disk_blocks /= blkio->Media->BlockSize;
1011 d_offset = dev->d_offset;
1013 if (disk_blocks == 0)
1014 disk_blocks = blkio->Media->LastBlock + 1 - d_offset;
1016 /* make sure we don't read past disk end */
1017 if ((off + size) / blkio->Media->BlockSize > d_offset + disk_blocks) {
1018 diskend = d_offset + disk_blocks;
1019 readstart = off / blkio->Media->BlockSize;
1021 if (diskend <= readstart) {
1027 size = diskend - readstart;
1028 size = size * blkio->Media->BlockSize;
1034 if ((size % blkio->Media->BlockSize == 0) &&
1035 (off % blkio->Media->BlockSize == 0))
1036 return (efipart_readwrite(blkio, rw,
1037 off / blkio->Media->BlockSize,
1038 size / blkio->Media->BlockSize, buf));
1041 * The buffer size is not a multiple of the media block size.
1043 blkbuf = malloc(blkio->Media->BlockSize);
1048 blk = off / blkio->Media->BlockSize;
1049 blkoff = off % blkio->Media->BlockSize;
1050 blksz = blkio->Media->BlockSize - blkoff;
1052 error = efipart_readwrite(blkio, rw, blk, 1, blkbuf);
1057 bcopy(blkbuf + blkoff, buf, blksz);
1062 blksz = blkio->Media->BlockSize;