grub-devel
[Top][All Lists]
Advanced

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

Re: Which partitioning schemes should be supported by GRUB?


From: Colin Watson
Subject: Re: Which partitioning schemes should be supported by GRUB?
Date: Tue, 15 Jun 2010 12:21:11 +0100
User-agent: Mutt/1.5.18 (2008-05-17)

On Mon, Jun 14, 2010 at 07:12:38PM +0200, Grégoire Sutre wrote:
> On 06/14/2010 18:43, Colin Watson wrote:
>> Do you have any suggestions on how to deal with that?  I'm not familiar
>> with multiboot and need guidance.
>
> A possible solution would be to use the multiboot-command line.  AFAIK,
> the boot_device field of the multiboot information structure is supposed
> to pass this kind of partition information, but you cannot specify the
> partmaps in this field, hence its interpretation is ambiguous.

That would potentially allow a user to override things, but doesn't help
with users who don't change their configuration.  Unless the user
explicitly configures things, boot_device is all we've got.

Thus, I guess we end up with a two-part fix:

  1) Honour key=value pairs in the multiboot command line when booting
     GRUB itself as a multiboot image.  These would simply become
     environment variables.  Presumably this goes in grub_machine_init,
     by analogy with kern/ieee1275/init.c.  This allows users to
     override the prefix on the command line as if they had changed the
     image itself.

  2) If multiboot_trampoline needs to change install_dos_part or
     install_bsd_part based on the value of boot_device in the MBI, then
     we know that the drive/partition part of prefix (which was
     calculated in the same way as install_dos_part and install_bsd_part
     when grub-setup was run) is now invalid and should be ignored.
     This fact needs to be passed on to make_install_device.

Something like this?  I'm afraid I have no idea how to test this (GRUB's
multiboot command doesn't seem to accept command-line arguments?), not
to mention that this is the first time I've been anywhere near most of
this code.  I would greatly appreciate advice and review.

2010-06-15  Colin Watson  <address@hidden>

        Fix i386-pc prefix handling with nested partitions (Debian bug
        #585068).

        * include/grub/i386/pc/kernel.h (grub_multiboot_change_prefix): New
        declaration.
        * kern/i386/pc/startup.S (multiboot_entry): Save a pointer to the
        MBI in startup_multiboot_info.
        (multiboot_trampoline): If the new partition numbers differ from the
        previous ones, then set grub_multiboot_change_prefix.
        (grub_multiboot_change_prefix): New definition.
        * kern/i386/pc/init.c (make_install_device): Invalidate any
        drive/partition part of the prefix if grub_multiboot_change_prefix
        is set.  After that, if the prefix starts with "(,", fill the boot
        drive in between those two characters, but expect that a full
        partition specification including partition map names will follow.
        (grub_parse_multiboot_cmdline): New function.
        (grub_machine_init): If we have an MBI, copy it, then call
        grub_parse_multiboot_cmdline.
        * util/i386/pc/grub-setup.c (setup): Unless an explicit prefix was
        specified, write a prefix without the drive name but including a
        full partition specification.

=== modified file 'include/grub/i386/pc/kernel.h'
--- include/grub/i386/pc/kernel.h       2010-04-26 19:11:16 +0000
+++ include/grub/i386/pc/kernel.h       2010-06-15 11:02:34 +0000
@@ -44,6 +44,10 @@ extern grub_int32_t grub_install_bsd_par
 /* The boot BIOS drive number.  */
 extern grub_uint8_t EXPORT_VAR(grub_boot_drive);
 
+/* Set if multiboot changed the partition numbers, so grub_prefix is now
+   invalid.  */
+extern grub_uint8_t grub_multiboot_change_prefix;
+
 #endif /* ! ASM_FILE */
 
 #endif /* ! KERNEL_MACHINE_HEADER */

=== modified file 'kern/i386/pc/init.c'
--- kern/i386/pc/init.c 2010-05-21 18:08:48 +0000
+++ kern/i386/pc/init.c 2010-06-15 11:06:20 +0000
@@ -32,6 +32,7 @@
 #include <grub/cache.h>
 #include <grub/time.h>
 #include <grub/cpu/tsc.h>
+#include <grub/multiboot.h>
 
 struct mem_region
 {
@@ -47,12 +48,29 @@ static int num_regions;
 grub_addr_t grub_os_area_addr;
 grub_size_t grub_os_area_size;
 
+/* A pointer to the MBI in its initial location.  */
+struct multiboot_info *startup_multiboot_info = NULL;
+
+/* The MBI has to be copied to our BSS so that it won't be
+   overwritten.  This is its final location.  */
+static struct multiboot_info kern_multiboot_info;
+
 static char *
 make_install_device (void)
 {
   /* XXX: This should be enough.  */
   char dev[100], *ptr = dev;
 
+  if (grub_prefix[0] == '(' && grub_multiboot_change_prefix)
+    {
+      /* The multiboot information invalidated our hardcoded prefix because
+         partition numbers differed.  Eliminate the drive/partition part of
+         the prefix, if possible.  */
+      char *ket = grub_strchr (grub_prefix, ')');
+      if (ket)
+       grub_memmove (grub_prefix, ket + 1, grub_strlen (ket));
+    }
+
   if (grub_prefix[0] != '(')
     {
       /* No hardcoded root partition - make it from the boot drive and the
@@ -83,6 +101,14 @@ make_install_device (void)
       grub_snprintf (ptr, sizeof (dev) - (ptr - dev), ")%s", grub_prefix);
       grub_strcpy (grub_prefix, dev);
     }
+  else if (grub_prefix[1] == ',' || grub_prefix[1] == ')')
+    {
+      /* We have a prefix, but still need to fill in the boot drive.  */
+      grub_snprintf (dev, sizeof (dev),
+                    "(%cd%u%s", (grub_boot_drive & 0x80) ? 'h' : 'f',
+                    grub_boot_drive & 0x7f, grub_prefix + 1);
+      grub_strcpy (grub_prefix, dev);
+    }
 
   return grub_prefix;
 }
@@ -134,6 +160,45 @@ compact_mem_regions (void)
       }
 }
 
+static void
+grub_parse_multiboot_cmdline (void)
+{
+  char *cmdline;
+  char *p;
+
+  if (! (kern_multiboot_info.flags & MULTIBOOT_INFO_CMDLINE))
+    return;
+
+  p = cmdline = grub_strdup ((char *) kern_multiboot_info.cmdline);
+
+  while (*p)
+    {
+      char *command = p;
+      char *end;
+      char *val;
+
+      end = grub_strchr (command, ' ');
+      if (end)
+       {
+         *end = '\0';
+         p = end + 1;
+         while (*p == ' ')
+           ++p;
+       }
+
+      /* Process command.  */
+      val = grub_strchr (command, '=');
+      if (val)
+       {
+         *val = '\0';
+         grub_env_set (command, val + 1);
+
+         if (grub_strcmp (command, "prefix") == 0)
+           grub_multiboot_change_prefix = 0;
+       }
+    }
+}
+
 void
 grub_machine_init (void)
 {
@@ -214,6 +279,15 @@ grub_machine_init (void)
   if (! grub_os_area_addr)
     grub_fatal ("no upper memory");
 
+  if (startup_multiboot_info)
+    {
+      /* Move MBI to a safe place.  */
+      grub_memmove (&kern_multiboot_info, startup_multiboot_info,
+                   sizeof (struct multiboot_info));
+
+      grub_parse_multiboot_cmdline ();
+    }
+
   grub_tsc_init ();
 }
 

=== modified file 'kern/i386/pc/startup.S'
--- kern/i386/pc/startup.S      2010-04-05 13:59:32 +0000
+++ kern/i386/pc/startup.S      2010-06-15 11:02:19 +0000
@@ -142,6 +142,8 @@ multiboot_header:
 
 multiboot_entry:
        .code32
+       movl    %ebx, EXT_C(startup_multiboot_info)
+
        /* obtain the boot device */
        movl    12(%ebx), %edx
 
@@ -161,20 +163,38 @@ multiboot_entry:
        jmp     *%eax
 
 multiboot_trampoline:
+       /* remember the original boot information */
+       movl    EXT_C(grub_install_dos_part), %eax
+       orl     %eax, %eax
+       jns     1f
+       movb    $0xFF, %al
+1:
+       movl    EXT_C(grub_install_bsd_part), %ebx
+       orl     %ebx, %ebx
+       jns     2f
+       movb    $0xFF, %bl
+2:
+       movb    %al, %bh
+
        /* fill the boot information */
        movl    %edx, %eax
        shrl    $8, %eax
+       cmpw    %ax, %bx
+       je      3f
+       /* doesn't match the original */
+       movb    $1, EXT_C(grub_multiboot_change_prefix)
+3:
        xorl    %ebx, %ebx
        cmpb    $0xFF, %ah
-       je      1f
+       je      4f
        movb    %ah, %bl
        movl    %ebx, EXT_C(grub_install_dos_part)
-1:
+4:
        cmpb    $0xFF, %al
-       je      2f
+       je      5f
        movb    %al, %bl
        movl    %ebx, EXT_C(grub_install_bsd_part)
-2:
+5:
        shrl    $24, %edx
         movb    $0xFF, %dh
        /* enter the usual booting */
@@ -285,6 +305,8 @@ LOCAL (codestart):
 
 VARIABLE(grub_boot_drive)
        .byte   0
+VARIABLE(grub_multiboot_change_prefix)
+       .byte   0
 
        .p2align        2       /* force 4-byte alignment */
 

=== modified file 'util/i386/pc/grub-setup.c'
--- util/i386/pc/grub-setup.c   2010-06-11 20:31:16 +0000
+++ util/i386/pc/grub-setup.c   2010-06-14 16:29:54 +0000
@@ -99,6 +99,7 @@ setup (const char *dir,
   struct grub_boot_blocklist *first_block, *block;
   grub_int32_t *install_dos_part, *install_bsd_part;
   grub_int32_t dos_part, bsd_part;
+  char *prefix;
   char *tmp_img;
   int i;
   grub_disk_addr_t first_sector;
@@ -230,6 +231,8 @@ setup (const char *dir,
                                       + GRUB_KERNEL_MACHINE_INSTALL_DOS_PART);
   install_bsd_part = (grub_int32_t *) (core_img + GRUB_DISK_SECTOR_SIZE
                                       + GRUB_KERNEL_MACHINE_INSTALL_BSD_PART);
+  prefix = (char *) (core_img + GRUB_DISK_SECTOR_SIZE +
+                    GRUB_KERNEL_MACHINE_PREFIX);
 
   /* Open the root device and the destination device.  */
   root_dev = grub_device_open (root);
@@ -305,6 +308,18 @@ setup (const char *dir,
              dos_part = root_dev->disk->partition->number;
              bsd_part = -1;
            }
+
+         if (prefix[0] != '(')
+           {
+             char *root_part_name, *new_prefix;
+
+             root_part_name =
+               grub_partition_get_name (root_dev->disk->partition);
+             new_prefix = xasprintf ("(,%s)%s", root_part_name, prefix);
+             strcpy (prefix, new_prefix);
+             free (new_prefix);
+             free (root_part_name);
+           }
        }
       else
        dos_part = bsd_part = -1;

Thanks,

-- 
Colin Watson                                       address@hidden



reply via email to

[Prev in Thread] Current Thread [Next in Thread]