Module Name: src Committed By: jmcneill Date: Tue Jun 22 21:56:51 UTC 2021
Modified Files: src/sys/stand/efiboot: efiblock.c Log Message: efiboot: Use EFI_BLOCK_IO_PROTOCOL if EFI_DISK_IO_PROTOCOL is missing UEFI spec says that firmware should automatically add EFI_DISK_IO_PROTOCOL for all produced EFI_BLOCK_IO_PROTOCOL interfaces. Unfortunately U-Boot doesn't do this, so fallback to block I/O if disk I/O is not there. To generate a diff of this commit: cvs rdiff -u -r1.14 -r1.15 src/sys/stand/efiboot/efiblock.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/stand/efiboot/efiblock.c diff -u src/sys/stand/efiboot/efiblock.c:1.14 src/sys/stand/efiboot/efiblock.c:1.15 --- src/sys/stand/efiboot/efiblock.c:1.14 Mon Jun 21 21:18:47 2021 +++ src/sys/stand/efiboot/efiblock.c Tue Jun 22 21:56:51 2021 @@ -1,4 +1,4 @@ -/* $NetBSD: efiblock.c,v 1.14 2021/06/21 21:18:47 jmcneill Exp $ */ +/* $NetBSD: efiblock.c,v 1.15 2021/06/22 21:56:51 jmcneill Exp $ */ /*- * Copyright (c) 2016 Kimihiro Nonaka <non...@netbsd.org> @@ -118,7 +118,74 @@ efi_block_generate_hash_mbr(struct efi_b } static EFI_STATUS -efi_block_disk_readahead(struct efi_block_dev *bdev, UINT64 off, void *buf, +efi_block_do_read_blockio(struct efi_block_dev *bdev, UINT64 off, void *buf, + UINTN bufsize) +{ + UINT8 *blkbuf, *blkbuf_start; + EFI_STATUS status; + EFI_LBA lba_start, lba_end; + UINT64 blkbuf_offset; + UINT64 blkbuf_size; + + lba_start = off / bdev->bio->Media->BlockSize; + lba_end = (off + bufsize + bdev->bio->Media->BlockSize - 1) / + bdev->bio->Media->BlockSize; + blkbuf_offset = off % bdev->bio->Media->BlockSize; + blkbuf_size = (lba_end - lba_start) * bdev->bio->Media->BlockSize; + if (bdev->bio->Media->IoAlign > 1) { + blkbuf_size += bdev->bio->Media->IoAlign - 1; + } + + blkbuf = AllocatePool(blkbuf_size); + if (blkbuf == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + if (bdev->bio->Media->IoAlign > 1) { + blkbuf_start = (void *)roundup2((intptr_t)blkbuf, + bdev->bio->Media->IoAlign); + } else { + blkbuf_start = blkbuf; + } + + status = uefi_call_wrapper(bdev->bio->ReadBlocks, 5, bdev->bio, + bdev->media_id, lba_start, blkbuf_size, blkbuf_start); + if (EFI_ERROR(status)) { + goto done; + } + + memcpy(buf, blkbuf_start + blkbuf_offset, bufsize); + +done: + FreePool(blkbuf); + return status; +} + +static EFI_STATUS +efi_block_do_read_diskio(struct efi_block_dev *bdev, UINT64 off, void *buf, + UINTN bufsize) +{ + return uefi_call_wrapper(bdev->dio->ReadDisk, 5, bdev->dio, + bdev->media_id, off, bufsize, buf); +} + +static EFI_STATUS +efi_block_do_read(struct efi_block_dev *bdev, UINT64 off, void *buf, + UINTN bufsize) +{ + /* + * Perform read access using EFI_DISK_IO_PROTOCOL if available, + * otherwise use EFI_BLOCK_IO_PROTOCOL. + */ + if (bdev->dio != NULL) { + return efi_block_do_read_diskio(bdev, off, buf, bufsize); + } else { + return efi_block_do_read_blockio(bdev, off, buf, bufsize); + } +} + +static EFI_STATUS +efi_block_readahead(struct efi_block_dev *bdev, UINT64 off, void *buf, UINTN bufsize) { EFI_STATUS status; @@ -140,8 +207,7 @@ efi_block_disk_readahead(struct efi_bloc if (len > mediasize - off) { len = mediasize - off; } - status = uefi_call_wrapper(bdev->dio->ReadDisk, 5, bdev->dio, - bdev->media_id, off, len, efi_ra_buffer); + status = efi_block_do_read(bdev, off, efi_ra_buffer, len); if (EFI_ERROR(status)) { efi_ra_start = efi_ra_length = 0; return status; @@ -156,15 +222,14 @@ efi_block_disk_readahead(struct efi_bloc } static EFI_STATUS -efi_block_disk_read(struct efi_block_dev *bdev, UINT64 off, void *buf, +efi_block_read(struct efi_block_dev *bdev, UINT64 off, void *buf, UINTN bufsize) { if (efi_ra_enable) { - return efi_block_disk_readahead(bdev, off, buf, bufsize); + return efi_block_readahead(bdev, off, buf, bufsize); } - return uefi_call_wrapper(bdev->dio->ReadDisk, 5, bdev->dio, - bdev->media_id, off, bufsize, buf); + return efi_block_do_read(bdev, off, buf, bufsize); } static int @@ -176,7 +241,7 @@ efi_block_find_partitions_cd9660(struct EFI_LBA lba; for (lba = 16;; lba++) { - status = efi_block_disk_read(bdev, + status = efi_block_read(bdev, lba * ISO_DEFAULT_BLOCK_SIZE, &vd, sizeof(vd)); if (EFI_ERROR(status)) { goto io_error; @@ -220,7 +285,7 @@ efi_block_find_partitions_disklabel(stru EFI_STATUS status; int n; - status = efi_block_disk_read(bdev, + status = efi_block_read(bdev, ((EFI_LBA)start + LABELSECTOR) * DEV_BSIZE, buf, sizeof(buf)); if (EFI_ERROR(status) || getdisklabel(buf, &d) != NULL) { FreePool(buf); @@ -268,7 +333,7 @@ efi_block_find_partitions_mbr(struct efi EFI_STATUS status; int n; - status = efi_block_disk_read(bdev, 0, &mbr, sizeof(mbr)); + status = efi_block_read(bdev, 0, &mbr, sizeof(mbr)); if (EFI_ERROR(status)) return EIO; @@ -348,7 +413,7 @@ efi_block_find_partitions_gpt(struct efi void *buf; UINTN sz; - status = efi_block_disk_read(bdev, GPT_HDR_BLKNO * DEV_BSIZE, &hdr, + status = efi_block_read(bdev, GPT_HDR_BLKNO * DEV_BSIZE, &hdr, sizeof(hdr)); if (EFI_ERROR(status)) { return EIO; @@ -364,7 +429,7 @@ efi_block_find_partitions_gpt(struct efi if (buf == NULL) return ENOMEM; - status = efi_block_disk_read(bdev, + status = efi_block_read(bdev, le64toh(hdr.hdr_lba_table) * DEV_BSIZE, buf, sz); if (EFI_ERROR(status)) { FreePool(buf); @@ -421,18 +486,22 @@ efi_block_probe(void) } for (n = 0; n < efi_nblock; n++) { + /* EFI_BLOCK_IO_PROTOCOL is required */ status = uefi_call_wrapper(BS->HandleProtocol, 3, efi_block[n], &BlockIoProtocol, (void **)&bio); if (EFI_ERROR(status) || !bio->Media->MediaPresent) continue; + /* Ignore logical partitions (we do our own partition discovery) */ if (bio->Media->LogicalPartition) continue; + /* EFI_DISK_IO_PROTOCOL is optional */ status = uefi_call_wrapper(BS->HandleProtocol, 3, efi_block[n], &DiskIoProtocol, (void **)&dio); - if (EFI_ERROR(status)) - continue; + if (EFI_ERROR(status)) { + dio = NULL; + } bdev = alloc(sizeof(*bdev)); bdev->index = devindex++; @@ -629,7 +698,7 @@ efi_block_strategy(void *devdata, int rw return EINVAL; } - status = efi_block_disk_read(bpart->bdev, off, buf, size); + status = efi_block_read(bpart->bdev, off, buf, size); if (EFI_ERROR(status)) return EIO;