The use case of a hybrid ISO where an EFI partition with syslinux.efi is set up as El Torrito boot option, all duly equipped, works fine when the media is provided as disk drive, but not when the same media is provided as cdrom drive. The problem cause seems to be in a mixup between the actual and assumed media sector sizes; a cdrom has 2048 byte sectors while a disk has 256 byte sectors. (In particular the partition table on the ISO has sector indexes based on 256 byte sectors) To resolve that, I made the patch below for efi/diskio.c. It resolves the sector size mixup by means of a wrapper function for disk reading/writing that translates between (hardcoded) "wanted" sectors of 256 byte size, and "actual" (bio->Media->BlockSize) when different. The implementation lacks some in efficiency but works well enough for boot loading. A patch for efi/efi.h needed to build the packages was also needed. This patching is applied to the forked debian syslinux package available at https://git.devuan.org/devuan/syslinux and a current build of syslinux packages has been added to the devuan/experimntal repository. That is done for the Devuan installer isohybrid ISOs to have only isolinux/syslinux as boot equipments. Ralph. --- efi/diskio.c | 73 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 69 insertions(+), 4 deletions(-) diff --git a/efi/diskio.c b/efi/diskio.c index d6a160e1..6ece2732 100644 --- a/efi/diskio.c +++ b/efi/diskio.c @@ -6,6 +6,7 @@ #include <ilog2.h> #include <disk.h> #include <dprintf.h> +#include <string.h> #include "efi.h" static inline EFI_STATUS read_blocks(EFI_BLOCK_IO *bio, uint32_t id, @@ -26,7 +27,7 @@ static int efi_rdwr_sectors(struct disk *disk, void *buf, struct efi_disk_private *priv = (struct efi_disk_private *)disk->private; EFI_BLOCK_IO *bio = priv->bio; EFI_STATUS status; - UINTN bytes = count * disk->sector_size; + UINTN bytes = count * bio->Media->BlockSize; if (is_write) status = write_blocks(bio, disk->disk_number, lba, bytes, buf); @@ -38,7 +39,70 @@ static int efi_rdwr_sectors(struct disk *disk, void *buf, is_write ? L"write" : L"read", status); - return count << disk->sector_shift; + return bytes; +} + +/** + * Map read/write of EFI sectors of 512 bytes into read/write of iso + * sectors of 2048 bytes. + * + * real_lba = lba / 4; real_count = ( count + 3 ) / 4; + */ +static int efi_iso_rdwr_sectors(struct disk *disk, void *buf, + sector_t lba, size_t count, bool is_write) { + static int ratio = 0; + + struct efi_disk_private *priv = (struct efi_disk_private *)disk->private; + EFI_BLOCK_IO *bio = priv->bio; + size_t bytes = count * disk->sector_size; + + if ( ! ratio ) { + ratio = bio->Media->BlockSize / disk->sector_size; + } + + sector_t real_lba = lba / ratio; + size_t real_count = ( count + ratio - 1 ) / ratio; + size_t size = bytes + bio->Media->BlockSize -disk->sector_size; + + void *buffer = malloc( size ); + + void *start = buffer + ( lba % ratio ) * disk->sector_size; + + int err = 0; + + if ( !buffer ) { + return EFI_OUT_OF_RESOURCES; + } + if ( is_write ) { + // read the first block from disk if head padding is needed + if ( buffer != start ) { + err = efi_rdwr_sectors( disk, buffer, real_lba, 1, 0 ); + if ( err < 0 ) goto ending; + } + // load last block from disk if tail padding is needed + if ( start + bytes < buffer + size ) { + void *last = buffer + size - bio->Media->BlockSize; + sector_t end_lba = real_lba + real_count - 1; + err = efi_rdwr_sectors( disk, last, end_lba, 1, 0 ); + if ( err < 0 ) goto ending; + } + memcpy( start, buf, bytes ); + err = efi_rdwr_sectors( disk, buffer, real_lba, real_count, 1 ); + if ( err < 0 ) goto ending; + // Assume the whole buffer was written + err = bytes; + } else { + // read into buffer + err = efi_rdwr_sectors( disk, buffer, real_lba, real_count, 0 ); + if ( err < 0 ) goto ending; + // Assume the whole buffer was read + err = bytes; + memcpy( buf, start, err ); + } + + ending: + if ( buffer ) free( buffer ); + return err; } struct disk *efi_disk_init(void *private) @@ -65,8 +129,9 @@ struct disk *efi_disk_init(void *private) */ disk.disk_number = bio->Media->MediaId; - disk.sector_size = bio->Media->BlockSize; - disk.rdwr_sectors = efi_rdwr_sectors; + disk.sector_size = 512; // bio->Media->BlockSize; + disk.rdwr_sectors = ( bio->Media->BlockSize == 512 )? + efi_rdwr_sectors : efi_iso_rdwr_sectors; disk.sector_shift = ilog2(disk.sector_size); dprintf("sector_size=%d, disk_number=%d\n", disk.sector_size, diff --git a/efi/efi.h b/efi/efi.h --- a/efi/efi.h +++ b/efi/efi.h @@ -21,6 +21,7 @@ #endif #include <efi.h> +#include <efi/efisetjmp.h> #include <efilib.h> #include <efistdarg.h> -------------- next part -------------- A non-text attachment was scrubbed... Name: signature.asc Type: application/pgp-signature Size: 488 bytes Desc: not available URL: <https://lists.syslinux.org/archives/syslinux/attachments/20230913/9212b5ce/attachment.sig>