From 4479aa540f349531d574cfdb5d23ca676e449b68 Mon Sep 17 00:00:00 2001 From: Toomas Soome Date: Thu, 30 Mar 2017 16:23:31 +0000 Subject: [PATCH] loader: efipart should check disk size from partition table While testing 32bit UEFI OVMF (which has bug about how the disk size is presented), I did witness the errors from blkio->ReadBlocks(). It became apparent we can not entirely trust UEFI interfaces either, so additional checks are needed. So we use disk_ioctl(DIOCGMEDIASIZE) for disks, with fallback of Media->LastBlock for other media. In addition, we need to check if there is media present. + small fixes for error printout, and avoiding multiple blk * 512. Reviewed by: allanjude Approved by: allanjude (mentor) Differential Revision: https://reviews.freebsd.org/D10197 --- sys/boot/efi/libefi/efipart.c | 37 ++++++++++++++++++++++++++--------- 1 file changed, 28 insertions(+), 9 deletions(-) diff --git a/sys/boot/efi/libefi/efipart.c b/sys/boot/efi/libefi/efipart.c index d4332d822e33..d4f7edff756b 100644 --- a/sys/boot/efi/libefi/efipart.c +++ b/sys/boot/efi/libefi/efipart.c @@ -834,8 +834,10 @@ efipart_readwrite(EFI_BLOCK_IO *blkio, int rw, daddr_t blk, daddr_t nblks, return (ENOSYS); } - if (EFI_ERROR(status)) - printf("%s: rw=%d, status=%lu\n", __func__, rw, (u_long)status); + if (EFI_ERROR(status)) { + printf("%s: rw=%d, blk=%ju size=%ju status=%lu\n", __func__, rw, + blk, nblks, EFI_ERROR_CODE(status)); + } return (efi_status_to_errno(status)); } @@ -859,6 +861,10 @@ efipart_strategy(void *devdata, int rw, daddr_t blk, size_t size, if (pd == NULL) return (EINVAL); + if (pd->pd_blkio->Media->RemovableMedia && + !pd->pd_blkio->Media->MediaPresent) + return (EIO); + bcd.dv_strategy = efipart_realstrategy; bcd.dv_devdata = devdata; bcd.dv_cache = pd->pd_bcache; @@ -878,7 +884,7 @@ efipart_realstrategy(void *devdata, int rw, daddr_t blk, size_t size, pdinfo_list_t *pdi; pdinfo_t *pd; EFI_BLOCK_IO *blkio; - off_t off; + uint64_t off, disk_blocks, d_offset = 0; char *blkbuf; size_t blkoff, blksz; int error; @@ -902,11 +908,24 @@ efipart_realstrategy(void *devdata, int rw, daddr_t blk, size_t size, return (EIO); off = blk * 512; + /* + * Get disk blocks, this value is either for whole disk or for + * partition. + */ + disk_blocks = 0; + if (dev->d_dev->dv_type == DEVT_DISK) { + if (disk_ioctl(dev, DIOCGMEDIASIZE, &disk_blocks) == 0) { + /* DIOCGMEDIASIZE does return bytes. */ + disk_blocks /= blkio->Media->BlockSize; + } + d_offset = dev->d_offset; + } + if (disk_blocks == 0) + disk_blocks = blkio->Media->LastBlock + 1 - d_offset; + /* make sure we don't read past disk end */ - if ((off + size) / blkio->Media->BlockSize - 1 > - blkio->Media->LastBlock) { - size = blkio->Media->LastBlock + 1 - - off / blkio->Media->BlockSize; + if ((off + size) / blkio->Media->BlockSize > d_offset + disk_blocks) { + size = d_offset + disk_blocks - off / blkio->Media->BlockSize; size = size * blkio->Media->BlockSize; } @@ -914,9 +933,9 @@ efipart_realstrategy(void *devdata, int rw, daddr_t blk, size_t size, *rsize = size; if ((size % blkio->Media->BlockSize == 0) && - ((blk * 512) % blkio->Media->BlockSize == 0)) + (off % blkio->Media->BlockSize == 0)) return (efipart_readwrite(blkio, rw, - blk * 512 / blkio->Media->BlockSize, + off / blkio->Media->BlockSize, size / blkio->Media->BlockSize, buf)); /*