[syslinux] 0020-fix-cdrom-on-uefi.patch

Ralph Ronnquist rrq at rrq.au
Wed Sep 13 06:15:28 PDT 2023


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>


More information about the Syslinux mailing list