From 00bfa988fcdcd77d43ecbe618a78df4a8f124d73 Mon Sep 17 00:00:00 2001 From: Vladimir Serbinenko Date: Mon, 28 Oct 2013 15:23:46 +0100 Subject: [PATCH] * grub-core/loader/multiboot.c: Add support for multiboot kernels quirks. --- ChangeLog | 5 ++ docs/grub.texi | 31 ++++++- grub-core/loader/i386/multiboot_mbi.c | 112 ++++++++++++++------------ grub-core/loader/multiboot.c | 30 ++++++- grub-core/loader/multiboot_elfxx.c | 3 + include/grub/multiboot.h | 10 +++ 6 files changed, 137 insertions(+), 54 deletions(-) diff --git a/ChangeLog b/ChangeLog index f2e8dd9ab..62d5e7bea 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2013-10-28 Vladimir Serbinenko + + * grub-core/loader/multiboot.c: Add support for multiboot kernels + quirks. + 2013-10-28 Vladimir Serbinenko * grub-core/loader/i386/linux.c (allocate_pages): Allocate at least diff --git a/docs/grub.texi b/docs/grub.texi index 6b30a5149..f504d913d 100644 --- a/docs/grub.texi +++ b/docs/grub.texi @@ -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 diff --git a/grub-core/loader/i386/multiboot_mbi.c b/grub-core/loader/i386/multiboot_mbi.c index 37cf36d7a..e78b482a7 100644 --- a/grub-core/loader/i386/multiboot_mbi.c +++ b/grub-core/loader/i386/multiboot_mbi.c @@ -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) + err = load_kernel (file, filename, buffer, header); + if (err) { - 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 = 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) diff --git a/grub-core/loader/multiboot.c b/grub-core/loader/multiboot.c index 7b6734923..093717cde 100644 --- a/grub-core/loader/multiboot.c +++ b/grub-core/loader/multiboot.c @@ -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) diff --git a/grub-core/loader/multiboot_elfxx.c b/grub-core/loader/multiboot_elfxx.c index 7189e016e..9dc21a1ba 100644 --- a/grub-core/loader/multiboot_elfxx.c +++ b/grub-core/loader/multiboot_elfxx.c @@ -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); diff --git a/include/grub/multiboot.h b/include/grub/multiboot.h index af6b0a0df..e13c0843b 100644 --- a/include/grub/multiboot.h +++ b/include/grub/multiboot.h @@ -34,6 +34,16 @@ #include #include +#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[]);