* grub-core/loader/multiboot.c: Add support for multiboot kernels
authorVladimir Serbinenko <phcoder@gmail.com>
Mon, 28 Oct 2013 14:23:46 +0000 (15:23 +0100)
committerVladimir Serbinenko <phcoder@gmail.com>
Mon, 28 Oct 2013 14:23:46 +0000 (15:23 +0100)
quirks.

ChangeLog
docs/grub.texi
grub-core/loader/i386/multiboot_mbi.c
grub-core/loader/multiboot.c
grub-core/loader/multiboot_elfxx.c
include/grub/multiboot.h

index f2e8dd9..62d5e7b 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,8 @@
+2013-10-28  Vladimir Serbinenko  <phcoder@gmail.com>
+
+       * grub-core/loader/multiboot.c: Add support for multiboot kernels
+       quirks.
+
 2013-10-28  Vladimir Serbinenko  <phcoder@gmail.com>
 
        * grub-core/loader/i386/linux.c (allocate_pages): Allocate at least
index 6b30a51..f504d91 100644 (file)
@@ -3680,6 +3680,8 @@ you forget a command, you can run the command @command{help}
 * lsfonts::                     List loaded fonts
 * lsmod::                       Show loaded modules
 * md5sum::                      Compute or check MD5 hash
+* module::                      Load module for multiboot kernel
+* multiboot::                   Load multiboot compliant kernel
 * nativedisk::                  Switch to native disk drivers
 * normal::                      Enter normal mode
 * normal_exit::                 Exit from normal mode
@@ -4369,7 +4371,6 @@ List loaded fonts.
 Show list of loaded modules.
 @end deffn
 
-
 @node md5sum
 @subsection md5sum
 
@@ -4378,6 +4379,34 @@ Alias for @code{hashsum --hash md5 arg @dots{}}. See command @command{hashsum}
 (@pxref{hashsum}) for full description.
 @end deffn
 
+@node module
+@subsection module
+
+@deffn Command module [--nounzip] file [arguments]
+Load a module for multiboot kernel image.  The rest of the
+line is passed verbatim as the module command line.
+@end deffn
+
+@node multiboot
+@subsection multiboot
+
+@deffn Command multiboot [--quirk-bad-kludge] [--quirk-modules-after-kernel] file @dots{}
+Load a multiboot kernel image from @var{file}.  The rest of the
+line is passed verbatim as the @dfn{kernel command-line}.  Any module must
+be reloaded after using this command (@pxref{module}).
+
+Some kernels have known problems. You need to specify --quirk-* for those.
+--quirk-bad-kludge is a problem seen in several products that they include
+loading kludge information with invalid data in ELF file. GRUB prior to 0.97
+and some custom builds prefered ELF information while 0.97 and GRUB 2
+use kludge. Use this option to ignore kludge.
+Known affected systems: old Solaris, SkyOS.
+
+--quirk-modules-after-kernel is needed for kernels which load at relatively
+high address e.g. 16MiB mark and can't cope with modules stuffed between
+1MiB mark and beginning of the kernel.
+Known afftected systems: VMWare.
+@end deffn
 
 @node nativedisk
 @subsection nativedisk
index 37cf36d..e78b482 100644 (file)
@@ -58,7 +58,63 @@ static int bootdev_set;
 static grub_size_t elf_sec_num, elf_sec_entsize;
 static unsigned elf_sec_shstrndx;
 static void *elf_sections;
+grub_multiboot_quirks_t grub_multiboot_quirks;
 
+static grub_err_t
+load_kernel (grub_file_t file, const char *filename,
+            char *buffer, struct multiboot_header *header)
+{
+  grub_err_t err;
+  if (grub_multiboot_quirks & GRUB_MULTIBOOT_QUIRK_BAD_KLUDGE)
+    {
+      err = grub_multiboot_load_elf (file, filename, buffer);
+      if (err == GRUB_ERR_UNKNOWN_OS && (header->flags & MULTIBOOT_AOUT_KLUDGE))
+       grub_errno = err = GRUB_ERR_NONE;
+    }
+  if (header->flags & MULTIBOOT_AOUT_KLUDGE)
+    {
+      int offset = ((char *) header - buffer -
+                   (header->header_addr - header->load_addr));
+      int load_size = ((header->load_end_addr == 0) ? file->size - offset :
+                      header->load_end_addr - header->load_addr);
+      grub_size_t code_size;
+      void *source;
+      grub_relocator_chunk_t ch;
+
+      if (header->bss_end_addr)
+       code_size = (header->bss_end_addr - header->load_addr);
+      else
+       code_size = load_size;
+
+      err = grub_relocator_alloc_chunk_addr (grub_multiboot_relocator, 
+                                            &ch, header->load_addr,
+                                            code_size);
+      if (err)
+       {
+         grub_dprintf ("multiboot_loader", "Error loading aout kludge\n");
+         return err;
+       }
+      source = get_virtual_current_address (ch);
+
+      if ((grub_file_seek (file, offset)) == (grub_off_t) -1)
+       {
+         return grub_errno;
+       }
+
+      grub_file_read (file, source, load_size);
+      if (grub_errno)
+       return grub_errno;
+
+      if (header->bss_end_addr)
+       grub_memset ((grub_uint8_t *) source + load_size, 0,
+                    header->bss_end_addr - header->load_addr - load_size);
+
+      grub_multiboot_payload_eip = header->entry_addr;
+      return GRUB_ERR_NONE;
+    }
+
+  return grub_multiboot_load_elf (file, filename, buffer);
+}
 
 grub_err_t
 grub_multiboot_load (grub_file_t file, const char *filename)
@@ -106,59 +162,11 @@ grub_multiboot_load (grub_file_t file, const char *filename)
                         "unsupported flag: 0x%x", header->flags);
     }
 
-  if (header->flags & MULTIBOOT_AOUT_KLUDGE)
-    {
-      int offset = ((char *) header - buffer -
-                   (header->header_addr - header->load_addr));
-      int load_size = ((header->load_end_addr == 0) ? file->size - offset :
-                      header->load_end_addr - header->load_addr);
-      grub_size_t code_size;
-      void *source;
-      grub_relocator_chunk_t ch;
-
-      if (header->bss_end_addr)
-       code_size = (header->bss_end_addr - header->load_addr);
-      else
-       code_size = load_size;
-
-      err = grub_relocator_alloc_chunk_addr (grub_multiboot_relocator, 
-                                            &ch, header->load_addr,
-                                            code_size);
-      if (err)
-       {
-         grub_dprintf ("multiboot_loader", "Error loading aout kludge\n");
-         grub_free (buffer);
-         return err;
-       }
-      source = get_virtual_current_address (ch);
-
-      if ((grub_file_seek (file, offset)) == (grub_off_t) -1)
-       {
-         grub_free (buffer);
-         return grub_errno;
-       }
-
-      grub_file_read (file, source, load_size);
-      if (grub_errno)
-       {
-         grub_free (buffer);
-         return grub_errno;
-       }
-
-      if (header->bss_end_addr)
-       grub_memset ((grub_uint8_t *) source + load_size, 0,
-                    header->bss_end_addr - header->load_addr - load_size);
-
-      grub_multiboot_payload_eip = header->entry_addr;
-    }
-  else
+  err = load_kernel (file, filename, buffer, header);
+  if (err)
     {
-      err = grub_multiboot_load_elf (file, filename, buffer);
-      if (err)
-       {
-         grub_free (buffer);
-         return err;
-       }
+      grub_free (buffer);
+      return err;
     }
 
   if (header->flags & MULTIBOOT_VIDEO_MODE)
index 7b67349..093717c 100644 (file)
@@ -160,6 +160,8 @@ grub_multiboot_unload (void)
   return GRUB_ERR_NONE;
 }
 
+static grub_uint64_t highest_load;
+
 #define MULTIBOOT_LOAD_ELF64
 #include "multiboot_elfxx.c"
 #undef MULTIBOOT_LOAD_ELF64
@@ -240,6 +242,26 @@ grub_cmd_multiboot (grub_command_t cmd __attribute__ ((unused)),
 
   grub_loader_unset ();
 
+  highest_load = 0;
+
+#ifndef GRUB_USE_MULTIBOOT2
+  grub_multiboot_quirks = GRUB_MULTIBOOT_QUIRKS_NONE;
+
+  if (argc != 0 && grub_strcmp (argv[0], "--quirk-bad-kludge") == 0)
+    {
+      argc--;
+      argv++;
+      grub_multiboot_quirks |= GRUB_MULTIBOOT_QUIRK_BAD_KLUDGE;
+    }
+
+  if (argc != 0 && grub_strcmp (argv[0], "--quirk-modules-after-kernel") == 0)
+    {
+      argc--;
+      argv++;
+      grub_multiboot_quirks |= GRUB_MULTIBOOT_QUIRK_MODULES_AFTER_KERNEL;
+    }
+#endif
+
   if (argc == 0)
     return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
 
@@ -290,6 +312,7 @@ grub_cmd_module (grub_command_t cmd __attribute__ ((unused)),
   grub_addr_t target;
   grub_err_t err;
   int nounzip = 0;
+  grub_uint64_t lowest_addr = 0;
 
   if (argc == 0)
     return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
@@ -315,12 +338,17 @@ grub_cmd_module (grub_command_t cmd __attribute__ ((unused)),
   if (! file)
     return grub_errno;
 
+#ifndef GRUB_USE_MULTIBOOT2
+  if (grub_multiboot_quirks & GRUB_MULTIBOOT_QUIRK_MODULES_AFTER_KERNEL)
+    lowest_addr = ALIGN_UP (highest_load + 1048576, 4096);
+#endif
+
   size = grub_file_size (file);
   if (size)
   {
     grub_relocator_chunk_t ch;
     err = grub_relocator_alloc_chunk_align (grub_multiboot_relocator, &ch,
-                                           0, (0xffffffff - size) + 1,
+                                           lowest_addr, (0xffffffff - size) + 1,
                                            size, MULTIBOOT_MOD_ALIGN,
                                            GRUB_RELOCATOR_PREFERENCE_NONE, 0);
     if (err)
index 7189e01..9dc21a1 100644 (file)
@@ -86,6 +86,9 @@ CONCAT(grub_multiboot_load_elf, XX) (grub_file_t file, const char *filename, voi
          grub_err_t err;
          void *source;
 
+         if (phdr(i)->p_paddr + phdr(i)->p_memsz > highest_load)
+           highest_load = phdr(i)->p_paddr + phdr(i)->p_memsz;
+
          grub_dprintf ("multiboot_loader", "segment %d: paddr=0x%lx, memsz=0x%lx, vaddr=0x%lx\n",
                        i, (long) phdr(i)->p_paddr, (long) phdr(i)->p_memsz, (long) phdr(i)->p_vaddr);
 
index af6b0a0..e13c084 100644 (file)
 #include <grub/types.h>
 #include <grub/err.h>
 
+#ifndef GRUB_USE_MULTIBOOT2
+typedef enum
+  {
+    GRUB_MULTIBOOT_QUIRKS_NONE = 0,
+    GRUB_MULTIBOOT_QUIRK_BAD_KLUDGE = 1,
+    GRUB_MULTIBOOT_QUIRK_MODULES_AFTER_KERNEL = 2
+  } grub_multiboot_quirks_t;
+extern grub_multiboot_quirks_t grub_multiboot_quirks;
+#endif
+
 extern struct grub_relocator *grub_multiboot_relocator;
 
 void grub_multiboot (int argc, char *argv[]);