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 > 32k.
259 * iPXE is known to insert stub BLOCK IO device with
262 if (blkio->Media->BlockSize < 512 ||
263 blkio->Media->BlockSize > (1 << 15) ||
264 !powerof2(blkio->Media->BlockSize)) {
269 if ((pd = calloc(1, sizeof(*pd))) == NULL) {
270 printf("efipart_inithandles: Out of memory.\n");
274 STAILQ_INIT(&pd->pd_part);
276 pd->pd_handle = hin[i];
277 pd->pd_devpath = devpath;
278 pd->pd_blkio = blkio;
279 STAILQ_INSERT_TAIL(&pdinfo, pd, pd_link);
286 static ACPI_HID_DEVICE_PATH *
287 efipart_floppy(EFI_DEVICE_PATH *node)
289 ACPI_HID_DEVICE_PATH *acpi;
291 if (DevicePathType(node) == ACPI_DEVICE_PATH &&
292 DevicePathSubType(node) == ACPI_DP) {
293 acpi = (ACPI_HID_DEVICE_PATH *) node;
294 if (acpi->HID == EISA_PNP_ID(PNP0604) ||
295 acpi->HID == EISA_PNP_ID(PNP0700) ||
296 acpi->HID == EISA_PNP_ID(PNP0701)) {
304 efipart_find_parent(pdinfo_list_t *pdi, EFI_DEVICE_PATH *devpath)
308 STAILQ_FOREACH(pd, pdi, pd_link) {
309 if (efi_devpath_is_prefix(pd->pd_devpath, devpath))
318 EFI_DEVICE_PATH *node;
319 ACPI_HID_DEVICE_PATH *acpi;
320 pdinfo_t *parent, *fd;
323 STAILQ_FOREACH(fd, &pdinfo, pd_link) {
324 if ((node = efi_devpath_last_node(fd->pd_devpath)) == NULL)
327 if ((acpi = efipart_floppy(node)) == NULL)
330 STAILQ_REMOVE(&pdinfo, fd, pdinfo, pd_link);
331 parent = efipart_find_parent(&pdinfo, fd->pd_devpath);
332 if (parent != NULL) {
333 STAILQ_REMOVE(&pdinfo, parent, pdinfo, pd_link);
334 parent->pd_alias = fd->pd_handle;
335 parent->pd_unit = acpi->UID;
339 fd->pd_unit = acpi->UID;
341 fd->pd_devsw = &efipart_fddev;
342 STAILQ_INSERT_TAIL(&fdinfo, fd, pd_link);
346 bcache_add_dev(efiblk_pdinfo_count(&fdinfo));
351 * Add or update entries with new handle data.
354 efipart_cdinfo_add(pdinfo_t *cd)
358 STAILQ_FOREACH(pd, &cdinfo, pd_link) {
359 if (efi_devpath_is_prefix(pd->pd_devpath, cd->pd_devpath)) {
360 last = STAILQ_LAST(&pd->pd_part, pdinfo, pd_link);
362 cd->pd_unit = last->pd_unit + 1;
366 cd->pd_devsw = &efipart_cddev;
367 STAILQ_INSERT_TAIL(&pd->pd_part, cd, pd_link);
372 last = STAILQ_LAST(&cdinfo, pdinfo, pd_link);
374 cd->pd_unit = last->pd_unit + 1;
378 cd->pd_parent = NULL;
379 cd->pd_devsw = &efipart_cddev;
380 STAILQ_INSERT_TAIL(&cdinfo, cd, pd_link);
384 efipart_testcd(EFI_DEVICE_PATH *node, EFI_BLOCK_IO *blkio)
386 if (DevicePathType(node) == MEDIA_DEVICE_PATH &&
387 DevicePathSubType(node) == MEDIA_CDROM_DP) {
391 /* cd drive without the media. */
392 if (blkio->Media->RemovableMedia &&
393 !blkio->Media->MediaPresent) {
401 efipart_updatecd(void)
403 EFI_DEVICE_PATH *devpath, *node;
405 pdinfo_t *parent, *cd;
408 STAILQ_FOREACH(cd, &pdinfo, pd_link) {
409 if ((node = efi_devpath_last_node(cd->pd_devpath)) == NULL)
412 if (efipart_floppy(node) != NULL)
415 /* Is parent of this device already registered? */
416 parent = efipart_find_parent(&cdinfo, cd->pd_devpath);
417 if (parent != NULL) {
418 STAILQ_REMOVE(&pdinfo, cd, pdinfo, pd_link);
419 efipart_cdinfo_add(cd);
423 if (!efipart_testcd(node, cd->pd_blkio))
426 /* Find parent and unlink both parent and cd from pdinfo */
427 STAILQ_REMOVE(&pdinfo, cd, pdinfo, pd_link);
428 parent = efipart_find_parent(&pdinfo, cd->pd_devpath);
429 if (parent != NULL) {
430 STAILQ_REMOVE(&pdinfo, parent, pdinfo, pd_link);
431 efipart_cdinfo_add(parent);
435 parent = efipart_find_parent(&cdinfo, cd->pd_devpath);
438 * If we come across a logical partition of subtype CDROM
439 * it doesn't refer to the CD filesystem itself, but rather
440 * to any usable El Torito boot image on it. In this case
441 * we try to find the parent device and add that instead as
442 * that will be the CD filesystem.
444 if (DevicePathType(node) == MEDIA_DEVICE_PATH &&
445 DevicePathSubType(node) == MEDIA_CDROM_DP &&
447 parent = calloc(1, sizeof(*parent));
448 if (parent == NULL) {
449 printf("efipart_updatecd: out of memory\n");
450 /* this device is lost but try again. */
455 devpath = efi_devpath_trim(cd->pd_devpath);
456 if (devpath == NULL) {
457 printf("efipart_updatecd: out of memory\n");
458 /* this device is lost but try again. */
463 parent->pd_devpath = devpath;
464 status = BS->LocateDevicePath(&blkio_guid,
465 &parent->pd_devpath, &parent->pd_handle);
467 if (EFI_ERROR(status)) {
468 printf("efipart_updatecd: error %lu\n",
469 EFI_ERROR_CODE(status));
475 efi_lookup_devpath(parent->pd_handle);
476 efipart_cdinfo_add(parent);
479 efipart_cdinfo_add(cd);
489 bcache_add_dev(efiblk_pdinfo_count(&cdinfo));
494 efipart_hdinfo_add(pdinfo_t *hd, HARDDRIVE_DEVICE_PATH *node)
498 STAILQ_FOREACH(pd, &hdinfo, pd_link) {
499 if (efi_devpath_is_prefix(pd->pd_devpath, hd->pd_devpath)) {
500 /* Add the partition. */
501 hd->pd_unit = node->PartitionNumber;
503 hd->pd_devsw = &efipart_hddev;
504 STAILQ_INSERT_TAIL(&pd->pd_part, hd, pd_link);
509 last = STAILQ_LAST(&hdinfo, pdinfo, pd_link);
511 hd->pd_unit = last->pd_unit + 1;
516 hd->pd_devsw = &efipart_hddev;
517 STAILQ_INSERT_TAIL(&hdinfo, hd, pd_link);
521 * The MEDIA_FILEPATH_DP has device name.
522 * From U-Boot sources it looks like names are in the form
523 * of typeN:M, where type is interface type, N is disk id
524 * and M is partition id.
527 efipart_hdinfo_add_filepath(pdinfo_t *hd, FILEPATH_DEVICE_PATH *node)
533 last = STAILQ_LAST(&hdinfo, pdinfo, pd_link);
535 hd->pd_unit = last->pd_unit + 1;
539 /* FILEPATH_DEVICE_PATH has 0 terminated string */
540 len = ucs2len(node->PathName);
541 if ((pathname = malloc(len + 1)) == NULL) {
542 printf("Failed to add disk, out of memory\n");
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 hd->pd_devsw = &efipart_hddev;
556 STAILQ_INSERT_TAIL(&hdinfo, hd, pd_link);
560 p++; /* skip the colon */
562 hd->pd_unit = (int)strtol(p, NULL, 0);
564 printf("Bad unit number for partition \"%s\"\n", pathname);
571 * We should have disk registered, if not, we are receiving
572 * handles out of order, and this code should be reworked
573 * to create "blank" disk for partition, and to find the
574 * disk based on PathName compares.
577 printf("BUG: No disk for partition \"%s\"\n", pathname);
582 /* Add the partition. */
583 hd->pd_parent = last;
584 hd->pd_devsw = &efipart_hddev;
585 STAILQ_INSERT_TAIL(&last->pd_part, hd, pd_link);
590 efipart_updatehd(void)
592 EFI_DEVICE_PATH *devpath, *node;
594 pdinfo_t *parent, *hd;
597 STAILQ_FOREACH(hd, &pdinfo, pd_link) {
598 if ((node = efi_devpath_last_node(hd->pd_devpath)) == NULL)
601 if (efipart_floppy(node) != NULL)
604 if (efipart_testcd(node, hd->pd_blkio))
607 if (DevicePathType(node) == HARDWARE_DEVICE_PATH &&
608 (DevicePathSubType(node) == HW_PCI_DP ||
609 DevicePathSubType(node) == HW_VENDOR_DP)) {
610 STAILQ_REMOVE(&pdinfo, hd, pdinfo, pd_link);
611 efipart_hdinfo_add(hd, NULL);
615 if (DevicePathType(node) == MEDIA_DEVICE_PATH &&
616 DevicePathSubType(node) == MEDIA_FILEPATH_DP) {
617 STAILQ_REMOVE(&pdinfo, hd, pdinfo, pd_link);
618 efipart_hdinfo_add_filepath(hd,
619 (FILEPATH_DEVICE_PATH *)node);
623 STAILQ_REMOVE(&pdinfo, hd, pdinfo, pd_link);
624 parent = efipart_find_parent(&pdinfo, hd->pd_devpath);
625 if (parent != NULL) {
626 STAILQ_REMOVE(&pdinfo, parent, pdinfo, pd_link);
627 efipart_hdinfo_add(parent, NULL);
629 parent = efipart_find_parent(&hdinfo, hd->pd_devpath);
632 if (DevicePathType(node) == MEDIA_DEVICE_PATH &&
633 DevicePathSubType(node) == MEDIA_HARDDRIVE_DP &&
635 parent = calloc(1, sizeof(*parent));
636 if (parent == NULL) {
637 printf("efipart_updatehd: out of memory\n");
638 /* this device is lost but try again. */
643 devpath = efi_devpath_trim(hd->pd_devpath);
644 if (devpath == NULL) {
645 printf("efipart_updatehd: out of memory\n");
646 /* this device is lost but try again. */
652 parent->pd_devpath = devpath;
653 status = BS->LocateDevicePath(&blkio_guid,
654 &parent->pd_devpath, &parent->pd_handle);
656 if (EFI_ERROR(status)) {
657 printf("efipart_updatehd: error %lu\n",
658 EFI_ERROR_CODE(status));
665 efi_lookup_devpath(&parent->pd_handle);
667 efipart_hdinfo_add(parent, NULL);
670 efipart_hdinfo_add(hd, (HARDDRIVE_DEVICE_PATH *)node);
681 bcache_add_dev(efiblk_pdinfo_count(&hdinfo));
686 efipart_print_common(struct devsw *dev, pdinfo_list_t *pdlist, int verbose)
694 struct disk_devdesc pd_dev;
697 if (STAILQ_EMPTY(pdlist))
700 printf("%s devices:", dev->dv_name);
701 if ((ret = pager_output("\n")) != 0)
704 STAILQ_FOREACH(pd, pdlist, pd_link) {
706 if (verbose) { /* Output the device path. */
707 text = efi_devpath_name(efi_lookup_devpath(h));
710 efi_free_devpath_name(text);
711 if ((ret = pager_output("\n")) != 0)
715 snprintf(line, sizeof(line),
716 " %s%d", dev->dv_name, pd->pd_unit);
718 status = OpenProtocolByHandle(h, &blkio_guid, (void **)&blkio);
719 if (!EFI_ERROR(status)) {
721 blkio->Media->LastBlock == 0? 0:
722 (unsigned long long) (blkio->Media->LastBlock + 1));
723 if (blkio->Media->LastBlock != 0) {
724 printf(" X %u", blkio->Media->BlockSize);
727 if (blkio->Media->MediaPresent) {
728 if (blkio->Media->RemovableMedia)
729 printf(" (removable)");
731 printf(" (no media)");
733 if ((ret = pager_output("\n")) != 0)
735 if (!blkio->Media->MediaPresent)
738 pd->pd_blkio = blkio;
739 pd_dev.dd.d_dev = dev;
740 pd_dev.dd.d_unit = pd->pd_unit;
741 pd_dev.d_slice = D_SLICENONE;
742 pd_dev.d_partition = D_PARTNONE;
743 ret = disk_open(&pd_dev, blkio->Media->BlockSize *
744 (blkio->Media->LastBlock + 1),
745 blkio->Media->BlockSize);
747 ret = disk_print(&pd_dev, line, verbose);
752 /* Do not fail from disk_open() */
756 if ((ret = pager_output("\n")) != 0)
764 efipart_printfd(int verbose)
766 return (efipart_print_common(&efipart_fddev, &fdinfo, verbose));
770 efipart_printcd(int verbose)
772 return (efipart_print_common(&efipart_cddev, &cdinfo, verbose));
776 efipart_printhd(int verbose)
778 return (efipart_print_common(&efipart_hddev, &hdinfo, verbose));
782 efipart_open(struct open_file *f, ...)
785 struct disk_devdesc *dev;
791 dev = va_arg(args, struct disk_devdesc *);
796 pd = efiblk_get_pdinfo((struct devdesc *)dev);
800 if (pd->pd_blkio == NULL) {
801 status = OpenProtocolByHandle(pd->pd_handle, &blkio_guid,
802 (void **)&pd->pd_blkio);
803 if (EFI_ERROR(status))
804 return (efi_status_to_errno(status));
807 blkio = pd->pd_blkio;
808 if (!blkio->Media->MediaPresent)
812 if (pd->pd_bcache == NULL)
813 pd->pd_bcache = bcache_allocate();
815 if (dev->dd.d_dev->dv_type == DEVT_DISK) {
819 blkio->Media->BlockSize * (blkio->Media->LastBlock + 1),
820 blkio->Media->BlockSize);
823 if (pd->pd_open == 0) {
825 bcache_free(pd->pd_bcache);
826 pd->pd_bcache = NULL;
835 efipart_close(struct open_file *f)
837 struct disk_devdesc *dev;
840 dev = (struct disk_devdesc *)(f->f_devdata);
844 pd = efiblk_get_pdinfo((struct devdesc *)dev);
849 if (pd->pd_open == 0) {
851 bcache_free(pd->pd_bcache);
852 pd->pd_bcache = NULL;
854 if (dev->dd.d_dev->dv_type == DEVT_DISK)
855 return (disk_close(dev));
860 efipart_ioctl(struct open_file *f, u_long cmd, void *data)
862 struct disk_devdesc *dev;
866 dev = (struct disk_devdesc *)(f->f_devdata);
870 pd = efiblk_get_pdinfo((struct devdesc *)dev);
874 if (dev->dd.d_dev->dv_type == DEVT_DISK) {
875 rc = disk_ioctl(dev, cmd, data);
881 case DIOCGSECTORSIZE:
882 *(u_int *)data = pd->pd_blkio->Media->BlockSize;
885 *(uint64_t *)data = pd->pd_blkio->Media->BlockSize *
886 (pd->pd_blkio->Media->LastBlock + 1);
896 * efipart_readwrite()
897 * Internal equivalent of efipart_strategy(), which operates on the
898 * media-native block size. This function expects all I/O requests
899 * to be within the media size and returns an error if such is not
903 efipart_readwrite(EFI_BLOCK_IO *blkio, int rw, daddr_t blk, daddr_t nblks,
910 if (blk < 0 || blk > blkio->Media->LastBlock)
912 if ((blk + nblks - 1) > blkio->Media->LastBlock)
915 switch (rw & F_MASK) {
917 status = blkio->ReadBlocks(blkio, blkio->Media->MediaId, blk,
918 nblks * blkio->Media->BlockSize, buf);
921 if (blkio->Media->ReadOnly)
923 status = blkio->WriteBlocks(blkio, blkio->Media->MediaId, blk,
924 nblks * blkio->Media->BlockSize, buf);
930 if (EFI_ERROR(status)) {
931 printf("%s: rw=%d, blk=%ju size=%ju status=%lu\n", __func__, rw,
932 blk, nblks, EFI_ERROR_CODE(status));
934 return (efi_status_to_errno(status));
938 efipart_strategy(void *devdata, int rw, daddr_t blk, size_t size,
939 char *buf, size_t *rsize)
941 struct bcache_devdata bcd;
942 struct disk_devdesc *dev;
945 dev = (struct disk_devdesc *)devdata;
949 pd = efiblk_get_pdinfo((struct devdesc *)dev);
953 if (pd->pd_blkio->Media->RemovableMedia &&
954 !pd->pd_blkio->Media->MediaPresent)
957 bcd.dv_strategy = efipart_realstrategy;
958 bcd.dv_devdata = devdata;
959 bcd.dv_cache = pd->pd_bcache;
961 if (dev->dd.d_dev->dv_type == DEVT_DISK) {
964 offset = dev->d_offset * pd->pd_blkio->Media->BlockSize;
966 return (bcache_strategy(&bcd, rw, blk + offset,
969 return (bcache_strategy(&bcd, rw, blk, size, buf, rsize));
973 efipart_realstrategy(void *devdata, int rw, daddr_t blk, size_t size,
974 char *buf, size_t *rsize)
976 struct disk_devdesc *dev = (struct disk_devdesc *)devdata;
979 uint64_t off, disk_blocks, d_offset = 0;
981 size_t blkoff, blksz;
983 uint64_t diskend, readstart;
985 if (dev == NULL || blk < 0)
988 pd = efiblk_get_pdinfo((struct devdesc *)dev);
992 blkio = pd->pd_blkio;
996 if (size == 0 || (size % 512) != 0)
1001 * Get disk blocks, this value is either for whole disk or for
1005 if (dev->dd.d_dev->dv_type == DEVT_DISK) {
1006 if (disk_ioctl(dev, DIOCGMEDIASIZE, &disk_blocks) == 0) {
1007 /* DIOCGMEDIASIZE does return bytes. */
1008 disk_blocks /= blkio->Media->BlockSize;
1010 d_offset = dev->d_offset;
1012 if (disk_blocks == 0)
1013 disk_blocks = blkio->Media->LastBlock + 1 - d_offset;
1015 /* make sure we don't read past disk end */
1016 if ((off + size) / blkio->Media->BlockSize > d_offset + disk_blocks) {
1017 diskend = d_offset + disk_blocks;
1018 readstart = off / blkio->Media->BlockSize;
1020 if (diskend <= readstart) {
1026 size = diskend - readstart;
1027 size = size * blkio->Media->BlockSize;
1033 if ((size % blkio->Media->BlockSize == 0) &&
1034 (off % blkio->Media->BlockSize == 0))
1035 return (efipart_readwrite(blkio, rw,
1036 off / blkio->Media->BlockSize,
1037 size / blkio->Media->BlockSize, buf));
1040 * The buffer size is not a multiple of the media block size.
1042 blkbuf = malloc(blkio->Media->BlockSize);
1047 blk = off / blkio->Media->BlockSize;
1048 blkoff = off % blkio->Media->BlockSize;
1049 blksz = blkio->Media->BlockSize - blkoff;
1051 error = efipart_readwrite(blkio, rw, blk, 1, blkbuf);
1056 bcopy(blkbuf + blkoff, buf, blksz);
1061 blksz = blkio->Media->BlockSize;