[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Bug#1051445: syslinux.efi crashes on isohybrid boot as cdrom



Package: syslinux-efi
Version: 6.04~git20190206.bf6db5b4+dfsg1

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 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)

As an attempt to resolve that, I made teh attached patch 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 from that. The implementation
lacks some in efficiency but works well enough for boot loading.

I also attch the patch for efi/efi.h I needed in order to build the
package.

Hopefully this patching can be applied to the syslinux-efi package, to
allow for isohybrid ISO that has only isolinux/syslinux as boot setup.

Ralph.
--- syslinux-6.04~git20190206.bf6db5b4+dfsg1/efi/diskio.c	2019-02-06 19:30:51.000000000 +0000
+++ fixed/efi/diskio.c	2023-09-08 05:11:08.646579577 +0000
@@ -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 @@
 	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 @@
 			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 ( ( real_count > 1 ) && ( 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 @@
      */
     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,
--- syslinux-6.04~git20190206.bf6db5b4+dfsg1/efi/efi.h	2019-02-06 19:30:51.000000000 +0000
+++ fixed/efi/efi.h	2023-09-05 14:29:41.227060161 +0000
@@ -21,6 +21,7 @@
 #endif
 
 #include <efi.h>
+#include <efi/efisetjmp.h>
 #include <efilib.h>
 #include <efistdarg.h>
 

Attachment: signature.asc
Description: PGP signature


Reply to: