* grub-core/loader/multiboot.c: Add support for multiboot kernels

quirks.
This commit is contained in:
Vladimir Serbinenko 2013-10-28 15:23:46 +01:00
parent 81afc5cce6
commit 00bfa988fc
6 changed files with 137 additions and 54 deletions

View file

@ -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> 2013-10-28 Vladimir Serbinenko <phcoder@gmail.com>
* grub-core/loader/i386/linux.c (allocate_pages): Allocate at least * grub-core/loader/i386/linux.c (allocate_pages): Allocate at least

View file

@ -3680,6 +3680,8 @@ you forget a command, you can run the command @command{help}
* lsfonts:: List loaded fonts * lsfonts:: List loaded fonts
* lsmod:: Show loaded modules * lsmod:: Show loaded modules
* md5sum:: Compute or check MD5 hash * md5sum:: Compute or check MD5 hash
* module:: Load module for multiboot kernel
* multiboot:: Load multiboot compliant kernel
* nativedisk:: Switch to native disk drivers * nativedisk:: Switch to native disk drivers
* normal:: Enter normal mode * normal:: Enter normal mode
* normal_exit:: Exit from normal mode * normal_exit:: Exit from normal mode
@ -4369,7 +4371,6 @@ List loaded fonts.
Show list of loaded modules. Show list of loaded modules.
@end deffn @end deffn
@node md5sum @node md5sum
@subsection md5sum @subsection md5sum
@ -4378,6 +4379,34 @@ Alias for @code{hashsum --hash md5 arg @dots{}}. See command @command{hashsum}
(@pxref{hashsum}) for full description. (@pxref{hashsum}) for full description.
@end deffn @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 @node nativedisk
@subsection nativedisk @subsection nativedisk

View file

@ -58,7 +58,63 @@ static int bootdev_set;
static grub_size_t elf_sec_num, elf_sec_entsize; static grub_size_t elf_sec_num, elf_sec_entsize;
static unsigned elf_sec_shstrndx; static unsigned elf_sec_shstrndx;
static void *elf_sections; 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_err_t
grub_multiboot_load (grub_file_t file, const char *filename) 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); "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 - grub_free (buffer);
(header->header_addr - header->load_addr)); return err;
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;
}
} }
if (header->flags & MULTIBOOT_VIDEO_MODE) if (header->flags & MULTIBOOT_VIDEO_MODE)

View file

@ -160,6 +160,8 @@ grub_multiboot_unload (void)
return GRUB_ERR_NONE; return GRUB_ERR_NONE;
} }
static grub_uint64_t highest_load;
#define MULTIBOOT_LOAD_ELF64 #define MULTIBOOT_LOAD_ELF64
#include "multiboot_elfxx.c" #include "multiboot_elfxx.c"
#undef MULTIBOOT_LOAD_ELF64 #undef MULTIBOOT_LOAD_ELF64
@ -240,6 +242,26 @@ grub_cmd_multiboot (grub_command_t cmd __attribute__ ((unused)),
grub_loader_unset (); 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) if (argc == 0)
return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected")); 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_addr_t target;
grub_err_t err; grub_err_t err;
int nounzip = 0; int nounzip = 0;
grub_uint64_t lowest_addr = 0;
if (argc == 0) if (argc == 0)
return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected")); 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) if (! file)
return grub_errno; 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); size = grub_file_size (file);
if (size) if (size)
{ {
grub_relocator_chunk_t ch; grub_relocator_chunk_t ch;
err = grub_relocator_alloc_chunk_align (grub_multiboot_relocator, &ch, err = grub_relocator_alloc_chunk_align (grub_multiboot_relocator, &ch,
0, (0xffffffff - size) + 1, lowest_addr, (0xffffffff - size) + 1,
size, MULTIBOOT_MOD_ALIGN, size, MULTIBOOT_MOD_ALIGN,
GRUB_RELOCATOR_PREFERENCE_NONE, 0); GRUB_RELOCATOR_PREFERENCE_NONE, 0);
if (err) if (err)

View file

@ -86,6 +86,9 @@ CONCAT(grub_multiboot_load_elf, XX) (grub_file_t file, const char *filename, voi
grub_err_t err; grub_err_t err;
void *source; 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", 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); i, (long) phdr(i)->p_paddr, (long) phdr(i)->p_memsz, (long) phdr(i)->p_vaddr);

View file

@ -34,6 +34,16 @@
#include <grub/types.h> #include <grub/types.h>
#include <grub/err.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; extern struct grub_relocator *grub_multiboot_relocator;
void grub_multiboot (int argc, char *argv[]); void grub_multiboot (int argc, char *argv[]);