chainloader: patch in BPB's sectors_per_track and num_heads
authorC. Masloch <pushbx@38.de>
Thu, 1 Feb 2018 14:51:55 +0000 (15:51 +0100)
committerDaniel Kiper <daniel.kiper@oracle.com>
Wed, 14 Mar 2018 12:23:27 +0000 (13:23 +0100)
These fields must reflect the ROM-BIOS's geometry for CHS-based
loaders to correctly load their next stage. Most loaders do not
query the ROM-BIOS (Int13.08), relying on the BPB fields to hold
the correct values already.

Tested with lDebug booted in qemu via grub2's
FreeDOS direct loading support, refer to
https://bitbucket.org/ecm/ldosboot + https://bitbucket.org/ecm/ldebug
(For this test, lDebug's iniload.asm must be assembled with
-D_QUERY_GEOMETRY=0 to leave the BPB values provided by grub.)

Signed-off-by: C. Masloch <pushbx@38.de>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
grub-core/loader/i386/pc/chainloader.c

index 18220b7..ef3a322 100644 (file)
@@ -19,6 +19,7 @@
 
 #include <grub/loader.h>
 #include <grub/machine/chainloader.h>
+#include <grub/machine/biosdisk.h>
 #include <grub/machine/memory.h>
 #include <grub/file.h>
 #include <grub/err.h>
@@ -86,9 +87,16 @@ grub_chainloader_unload (void)
 void
 grub_chainloader_patch_bpb (void *bs, grub_device_t dev, grub_uint8_t dl)
 {
-  grub_uint32_t part_start = 0;
+  grub_uint32_t part_start = 0, heads = 0, sectors = 0;
   if (dev && dev->disk)
-    part_start = grub_partition_get_start (dev->disk->partition);
+    {
+      part_start = grub_partition_get_start (dev->disk->partition);
+      if (dev->disk->data)
+        {
+          heads = ((struct grub_biosdisk_data *)(dev->disk->data))->heads;
+          sectors = ((struct grub_biosdisk_data *)(dev->disk->data))->sectors;
+        }
+    }
   if (grub_memcmp ((char *) &((struct grub_ntfs_bpb *) bs)->oem_name,
                   "NTFS", 4) == 0)
     {
@@ -127,12 +135,20 @@ grub_chainloader_patch_bpb (void *bs, grub_device_t dev, grub_uint8_t dl)
        {
          bpb->num_hidden_sectors = grub_cpu_to_le32 (part_start);
          bpb->version_specific.fat12_or_fat16.num_ph_drive = dl;
+          if (sectors)
+            bpb->sectors_per_track = grub_cpu_to_le16 (sectors);
+          if (heads)
+            bpb->num_heads = grub_cpu_to_le16 (heads);
          return;
        }
       if (bpb->version_specific.fat32.sectors_per_fat_32)
        {
          bpb->num_hidden_sectors = grub_cpu_to_le32 (part_start);
          bpb->version_specific.fat32.num_ph_drive = dl;
+          if (sectors)
+            bpb->sectors_per_track = grub_cpu_to_le16 (sectors);
+          if (heads)
+            bpb->num_heads = grub_cpu_to_le16 (heads);
          return;
        }
       break;