Hi,

The i386/pc/chainloader.c hangs when attempting to chainload other
disk's boot sector (or mbr) with a syntax like this. Say if current
environment setting root=hd0,msdos2.

  $ chainloader (hd1,msdos1)+1

However doing it this way always works.

  $ set root=hd1,msdos1
  $ chainloader +1

I suppose the first syntax is valid as it should override $root [1]
thus two cases should have identical result. But looking into the
chainloader source it seems that the $root is always used to obtain
the boot drive number and partition table. So there's a discrepancy if
root is not set to the chainloader device which is explicitly
specified in file.

The attached file fixes the problem for me, I'd like to contribute if
you think this is a problem or how to improve the patch.

[1] http://www.gnu.org/software/grub/manual/grub.html#root
[2] http://www.gnu.org/software/grub/manual/grub.html#chainloader

Thanks,
Michael
Index: grub-2.00/grub-core/loader/i386/pc/chainloader.c
===================================================================
--- grub-2.00.orig/grub-core/loader/i386/pc/chainloader.c
+++ grub-2.00/grub-core/loader/i386/pc/chainloader.c
@@ -39,6 +39,7 @@
 #include <grub/fat.h>
 #include <grub/ntfs.h>
 #include <grub/i386/relocator.h>
+#include <grub/env.h>
 
 GRUB_MOD_LICENSE ("GPLv3+");
 
@@ -149,6 +150,7 @@ grub_chainloader_cmd (const char *filena
   int drive = -1;
   grub_addr_t part_addr = 0;
   grub_uint8_t *bs, *ptable;
+  const char *biosnum;
 
   rel = grub_relocator_new ();
   if (!rel)
@@ -198,11 +200,28 @@ grub_chainloader_cmd (const char *filena
       goto fail;
     }
 
-  grub_file_close (file);
 
-  /* Obtain the partition table from the root device.  */
-  drive = grub_get_root_biosnumber ();
-  dev = grub_device_open (0);
+  /* Obtain the partition table from the loaded device.  */
+  dev = file->device;
+  biosnum = grub_env_get ("biosnum");
+
+  if (biosnum)
+    {
+      drive = grub_strtoul (biosnum, 0, 0);
+    }
+  else
+    {
+      if (dev->disk && dev->disk->dev
+          && dev->disk->dev->id == GRUB_DISK_DEVICE_BIOSDISK_ID)
+        drive = (int) dev->disk->id;
+    }
+
+  if (drive == -1)
+    {
+      grub_error (GRUB_ERR_BAD_NUMBER, "invalid biosnum");
+      goto fail;
+    }
+
   if (dev && dev->disk && dev->disk->partition)
     {
       grub_disk_t disk = dev->disk;
@@ -225,15 +244,14 @@ grub_chainloader_cmd (const char *filena
   if (flags & GRUB_CHAINLOADER_BPB)
     grub_chainloader_patch_bpb ((void *) 0x7C00, dev, drive);
 
-  if (dev)
-    grub_device_close (dev);
- 
   /* Ignore errors. Perhaps it's not fatal.  */
   grub_errno = GRUB_ERR_NONE;
 
   boot_drive = drive;
   boot_part_addr = part_addr;
 
+  grub_file_close (file);
+
   grub_loader_set (grub_chainloader_boot, grub_chainloader_unload, 1);
   return;
 
_______________________________________________
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel

Reply via email to