diff --git a/ChangeLog b/ChangeLog index a16d539..31c8c10 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,31 @@ +2009-06-12 Vladimir Serbinenko + + Load BSD ELF modules + + * conf/i386-pc.rmk (bsd_mod_SOURCES): add loader/i386/bsd32.c + and loader/i386/bsd64.c + * include/grub/i386/bsd.h (FREEBSD_MODTYPE_MODULE): remove + (FREEBSD_MODTYPE_ELF_MODULE): new definition + (grub_freebsd_load_elfmodule32): new declaration + (grub_freebsd_load_elfmodule64): likewise + (grub_freebsd_load_elf_meta32): likewise + (grub_freebsd_load_elf_meta64): likewise + (grub_freebsd_add_meta): likewise + (grub_freebsd_add_meta_module): likewise + * loader/i386/bsd.c (grub_freebsd_add_meta): make global + (grub_freebsd_add_meta_module): likewise and move module-specific + parts to grub_cmd_freebsd and grub_cmd_freebsd_module + (grub_cmd_freebsd): add elf-kernel specific parts + based on grub_freebsd_add_meta_module + (grub_cmd_freebsd_module): add type parsing moved from + grub_freebsd_add_meta_module + (grub_cmd_freebsd_module_elf): new function + (cmd_freebsd_module_elf): new variable + (GRUB_MOD_INIT): register freebsd_module_elf + * loader/i386/bsd32.c: new file + * loader/i386/bsd64.c: likewise + * loader/i386/bsdXX.c: likewise + 2009-06-11 Pavel Roskin * Makefile.in (uninstall): Uninstall manuals. diff --git a/conf/i386-pc.rmk b/conf/i386-pc.rmk index abb6fd5..47427db 100644 --- a/conf/i386-pc.rmk +++ b/conf/i386-pc.rmk @@ -328,7 +328,7 @@ aout_mod_CFLAGS = $(COMMON_CFLAGS) aout_mod_LDFLAGS = $(COMMON_LDFLAGS) # For bsd.mod -bsd_mod_SOURCES = loader/i386/bsd.c loader/i386/bsd_helper.S loader/i386/bsd_trampoline.S +bsd_mod_SOURCES = loader/i386/bsd.c loader/i386/bsd32.c loader/i386/bsd64.c loader/i386/bsd_helper.S loader/i386/bsd_trampoline.S bsd_mod_CFLAGS = $(COMMON_CFLAGS) bsd_mod_LDFLAGS = $(COMMON_LDFLAGS) bsd_mod_ASFLAGS = $(COMMON_ASFLAGS) diff --git a/include/grub/i386/bsd.h b/include/grub/i386/bsd.h index 321b31f..147bb6f 100644 --- a/include/grub/i386/bsd.h +++ b/include/grub/i386/bsd.h @@ -86,7 +86,7 @@ #define FREEBSD_MODTYPE_KERNEL "elf kernel" #define FREEBSD_MODTYPE_KERNEL64 "elf64 kernel" -#define FREEBSD_MODTYPE_MODULE "elf module" +#define FREEBSD_MODTYPE_ELF_MODULE "elf obj module" #define FREEBSD_MODTYPE_RAW "raw" struct grub_freebsd_bootinfo @@ -229,6 +229,20 @@ struct grub_netbsd_btinfo_bootdisk void grub_unix_real_boot (grub_addr_t entry, ...) __attribute__ ((cdecl,noreturn)); +grub_err_t grub_freebsd_load_elfmodule32 (grub_file_t file, int argc, + char *argv[], grub_addr_t *kern_end); +grub_err_t grub_freebsd_load_elfmodule64 (grub_file_t file, int argc, + char *argv[], grub_addr_t *kern_end); +grub_err_t grub_freebsd_load_elf_meta32 (grub_file_t file, + grub_addr_t *kern_end); +grub_err_t grub_freebsd_load_elf_meta64 (grub_file_t file, + grub_addr_t *kern_end); + +grub_err_t grub_freebsd_add_meta (grub_uint32_t type, void *data, + grub_uint32_t len); +grub_err_t grub_freebsd_add_meta_module (char *filename, char *type, + int argc, char **argv, + grub_addr_t addr, grub_uint32_t size); extern grub_uint8_t grub_bsd64_trampoline_start, grub_bsd64_trampoline_end; extern grub_uint32_t grub_bsd64_trampoline_selfjump; diff --git a/loader/i386/bsd.c b/loader/i386/bsd.c index 6214b00..4b7a0f8 100644 --- a/loader/i386/bsd.c +++ b/loader/i386/bsd.c @@ -116,7 +116,7 @@ grub_bsd_get_device (grub_uint32_t * biosdev, grub_device_close (dev); } -static grub_err_t +grub_err_t grub_freebsd_add_meta (grub_uint32_t type, void *data, grub_uint32_t len) { if (mod_buf_max < mod_buf_len + len + 8) @@ -261,36 +261,21 @@ grub_freebsd_add_mmap (void) return grub_errno; } -static grub_err_t -grub_freebsd_add_meta_module (int is_kern, int argc, char **argv, +grub_err_t +grub_freebsd_add_meta_module (char *filename, char *type, int argc, char **argv, grub_addr_t addr, grub_uint32_t size) { - char *name, *type; - - name = grub_strrchr (argv[0], '/'); + char *name; + name = grub_strrchr (filename, '/'); if (name) name++; else - name = argv[0]; + name = filename; if (grub_freebsd_add_meta (FREEBSD_MODINFO_NAME, name, grub_strlen (name) + 1)) return grub_errno; - argc--; - argv++; - - if ((argc) && (!grub_memcmp (argv[0], "type=", 5))) - { - type = &argv[0][5]; - argc--; - argv++; - } - else - type = ((is_kern) ? - ((is_64bit) ? FREEBSD_MODTYPE_KERNEL64 : FREEBSD_MODTYPE_KERNEL) - : FREEBSD_MODTYPE_RAW); - if (is_64bit) { grub_uint64_t addr64 = addr, size64 = size; @@ -341,23 +326,6 @@ grub_freebsd_add_meta_module (int is_kern, int argc, char **argv, } } - if (is_kern) - { - int len = (is_64bit) ? 8 : 4; - grub_uint64_t data = 0; - - if ((grub_freebsd_add_meta (FREEBSD_MODINFO_METADATA | - FREEBSD_MODINFOMD_HOWTO, &data, 4)) || - (grub_freebsd_add_meta (FREEBSD_MODINFO_METADATA | - FREEBSD_MODINFOMD_ENVP, &data, len)) || - (grub_freebsd_add_meta (FREEBSD_MODINFO_METADATA | - FREEBSD_MODINFOMD_KERNEND, &data, len))) - return grub_errno; - kern_end_mdofs = mod_buf_len - len; - - return grub_freebsd_add_mmap (); - } - return GRUB_ERR_NONE; } @@ -838,10 +806,54 @@ grub_cmd_freebsd (grub_command_t cmd __attribute__ ((unused)), if (grub_bsd_load (argc, argv) == GRUB_ERR_NONE) { kern_end = ALIGN_PAGE (kern_end); - if ((is_elf_kernel) && - (grub_freebsd_add_meta_module (1, argc, argv, kern_start, - kern_end - kern_start))) - return grub_errno; + if (is_elf_kernel) + { + grub_err_t err; + grub_uint64_t data = 0; + grub_file_t file; + int len = is_64bit ? 8 : 4; + + err = grub_freebsd_add_meta_module (argv[0], is_64bit + ? FREEBSD_MODTYPE_KERNEL64 + : FREEBSD_MODTYPE_KERNEL, + argc - 1, argv + 1, + kern_start, + kern_end - kern_start); + if (err) + return err; + + file = grub_gzfile_open (argv[0], 1); + if (! file) + return grub_errno; + + if (is_64bit) + err = grub_freebsd_load_elf_meta64 (file, &kern_end); + else + err = grub_freebsd_load_elf_meta32 (file, &kern_end); + if (err) + return err; + + err = grub_freebsd_add_meta (FREEBSD_MODINFO_METADATA | + FREEBSD_MODINFOMD_HOWTO, &data, 4); + if (err) + return err; + + err = grub_freebsd_add_meta (FREEBSD_MODINFO_METADATA | + FREEBSD_MODINFOMD_ENVP, &data, len); + if (err) + return err; + + err = grub_freebsd_add_meta (FREEBSD_MODINFO_METADATA | + FREEBSD_MODINFOMD_KERNEND, &data, len); + if (err) + return err; + + kern_end_mdofs = mod_buf_len - len; + + err = grub_freebsd_add_mmap (); + if (err) + return err; + } grub_loader_set (grub_freebsd_boot, grub_bsd_unload, 1); } @@ -969,6 +981,10 @@ grub_cmd_freebsd_module (grub_command_t cmd __attribute__ ((unused)), int argc, char *argv[]) { grub_file_t file = 0; + grub_err_t err; + int modargc; + char **modargv; + char *type; if (kernel_type != KERNEL_TYPE_FREEBSD) return grub_error (GRUB_ERR_BAD_ARGUMENT, @@ -996,9 +1012,27 @@ grub_cmd_freebsd_module (grub_command_t cmd __attribute__ ((unused)), } grub_file_read (file, (void *) kern_end, file->size); - if ((!grub_errno) && - (!grub_freebsd_add_meta_module (0, argc, argv, kern_end, file->size))) - kern_end = ALIGN_PAGE (kern_end + file->size); + if (grub_errno) + goto fail; + + modargc = argc - 1; + modargv = argv + 1; + + if (modargc && (! grub_memcmp (modargv[0], "type=", 5))) + { + type = &modargv[0][5]; + modargc--; + modargv++; + } + else + type = FREEBSD_MODTYPE_RAW; + + err = grub_freebsd_add_meta_module (argv[0], type, modargc, modargv, + kern_end, file->size); + if (err) + goto fail; + + kern_end = ALIGN_PAGE (kern_end + file->size); fail: if (file) @@ -1007,8 +1041,56 @@ fail: return grub_errno; } +static grub_err_t +grub_cmd_freebsd_module_elf (grub_command_t cmd __attribute__ ((unused)), + int argc, char *argv[]) +{ + grub_file_t file = 0; + grub_err_t err; + + if (kernel_type != KERNEL_TYPE_FREEBSD) + return grub_error (GRUB_ERR_BAD_ARGUMENT, + "only freebsd support module"); + + if (! is_elf_kernel) + return grub_error (GRUB_ERR_BAD_ARGUMENT, + "only elf kernel support module"); + + /* List the current modules if no parameter. */ + if (! argc) + { + grub_freebsd_list_modules (); + return 0; + } + + file = grub_gzfile_open (argv[0], 1); + if (!file) + return grub_errno; + if (!file->size) + { + grub_file_close (file); + return grub_errno; + } + + if (is_64bit) + err = grub_freebsd_load_elfmodule64 (file, argc, argv, &kern_end); + else + err = grub_freebsd_load_elfmodule32 (file, argc, argv, &kern_end); + + grub_file_close (file); + + if (err) + return err; + + kern_end = ALIGN_PAGE (kern_end + file->size); + + return GRUB_ERR_NONE; +} + + static grub_command_t cmd_freebsd, cmd_openbsd, cmd_netbsd; static grub_command_t cmd_freebsd_loadenv, cmd_freebsd_module; +static grub_command_t cmd_freebsd_module_elf; GRUB_MOD_INIT (bsd) { @@ -1027,6 +1109,9 @@ GRUB_MOD_INIT (bsd) cmd_freebsd_module = grub_register_command ("freebsd_module", grub_cmd_freebsd_module, 0, "load freebsd module"); + cmd_freebsd_module_elf = + grub_register_command ("freebsd_module_elf", grub_cmd_freebsd_module_elf, + 0, "load freebsd ELF module"); my_mod = mod; } @@ -1039,6 +1124,7 @@ GRUB_MOD_FINI (bsd) grub_unregister_command (cmd_freebsd_loadenv); grub_unregister_command (cmd_freebsd_module); + grub_unregister_command (cmd_freebsd_module_elf); if (mod_buf) { diff --git a/loader/i386/bsd32.c b/loader/i386/bsd32.c new file mode 100644 index 0000000..9aa87e3 --- /dev/null +++ b/loader/i386/bsd32.c @@ -0,0 +1,6 @@ +#define SUFFIX(x) x ## 32 +#define Elf_Ehdr Elf32_Ehdr +#define Elf_Shdr Elf32_Shdr +#include +typedef grub_uint32_t grub_freebsd_addr_t; +#include "bsdXX.c" diff --git a/loader/i386/bsd64.c b/loader/i386/bsd64.c new file mode 100644 index 0000000..32fc02c --- /dev/null +++ b/loader/i386/bsd64.c @@ -0,0 +1,6 @@ +#define SUFFIX(x) x ## 64 +#define Elf_Ehdr Elf64_Ehdr +#define Elf_Shdr Elf64_Shdr +#include +typedef grub_uint64_t grub_freebsd_addr_t; +#include "bsdXX.c" diff --git a/loader/i386/bsdXX.c b/loader/i386/bsdXX.c new file mode 100644 index 0000000..a6a8d6c --- /dev/null +++ b/loader/i386/bsdXX.c @@ -0,0 +1,215 @@ +#include +#include +#include +#include +#include +#include + +#define ALIGN_PAGE(a) ALIGN_UP (a, 4096) + +grub_err_t +SUFFIX (grub_freebsd_load_elfmodule) (grub_file_t file, int argc, char *argv[], + grub_addr_t *kern_end) +{ + Elf_Ehdr e; + Elf_Shdr *s; + char *shdr; + grub_addr_t curload, module; + + if (grub_file_seek (file, 0) == (grub_off_t) -1) + return grub_errno; + + if (grub_file_read (file, (char *) &e, sizeof (e)) != sizeof (e)) + { + if (grub_errno) + return grub_errno; + else + return grub_error (GRUB_ERR_BAD_OS, "file is too short"); + } + + if (e.e_ident[EI_MAG0] != ELFMAG0 + || e.e_ident[EI_MAG1] != ELFMAG1 + || e.e_ident[EI_MAG2] != ELFMAG2 + || e.e_ident[EI_MAG3] != ELFMAG3 + || e.e_ident[EI_VERSION] != EV_CURRENT + || e.e_version != EV_CURRENT) + return grub_error (GRUB_ERR_BAD_OS, "invalid arch independent ELF magic"); + + if (e.e_ident[EI_CLASS] != SUFFIX (ELFCLASS)) + return grub_error (GRUB_ERR_BAD_OS, "invalid arch dependent ELF magic"); + + curload = module = ALIGN_PAGE (*kern_end); + + shdr = grub_malloc (e.e_shnum * e.e_shentsize); + if (! shdr) + return grub_errno; + + if (grub_file_seek (file, e.e_shoff) == (grub_off_t) -1) + return grub_errno; + + if (grub_file_read (file, shdr, e.e_shnum * e.e_shentsize) + != e.e_shnum * e.e_shentsize) + { + if (grub_errno) + return grub_errno; + else + return grub_error (GRUB_ERR_BAD_OS, "file is truncated"); + } + + for (s = (Elf_Shdr *) shdr; s < (Elf_Shdr *) ((char *) shdr + + e.e_shnum * e.e_shentsize); + s = (Elf_Shdr *) ((char *) s + e.e_shentsize)) + { + if (s->sh_size == 0) + continue; + + if (s->sh_addralign) + curload = ALIGN_UP (curload, s->sh_addralign); + + if (curload + s->sh_size > grub_os_area_addr + grub_os_area_size) + return grub_error (GRUB_ERR_OUT_OF_RANGE, + "Not enough memory for the module"); + + grub_dprintf ("bsd", "loading section to %x, size %d, align %d\n", + (unsigned) curload, (int) s->sh_size, + (int) s->sh_addralign); + + s->sh_addr = curload; + + switch (s->sh_type) + { + default: + case SHT_PROGBITS: + if (grub_file_seek (file, s->sh_offset) == (grub_off_t) -1) + return grub_errno; + if (grub_file_read (file, UINT_TO_PTR (curload), s->sh_size) + != (grub_ssize_t) s->sh_size) + { + if (grub_errno) + return grub_errno; + else + return grub_error (GRUB_ERR_BAD_OS, "file is truncated"); + } + break; + case SHT_NOBITS: + grub_memset (UINT_TO_PTR (curload), 0, s->sh_size); + break; + } + curload += s->sh_size; + } + + *kern_end = curload; + + grub_freebsd_add_meta_module (argv[0], FREEBSD_MODTYPE_ELF_MODULE, + argc - 1, argv + 1, module, + curload - module); + grub_freebsd_add_meta (FREEBSD_MODINFO_METADATA | FREEBSD_MODINFOMD_ELFHDR, + &e, sizeof (e)); + grub_freebsd_add_meta (FREEBSD_MODINFO_METADATA | FREEBSD_MODINFOMD_SHDR, + shdr, e.e_shnum * e.e_shentsize); + return GRUB_ERR_NONE; +} + +grub_err_t +SUFFIX (grub_freebsd_load_elf_meta) (grub_file_t file, grub_addr_t *kern_end) +{ + grub_err_t err; + Elf_Ehdr e; + Elf_Shdr *s; + char *shdr; + int sym, str, symsize, strsize; + grub_addr_t curload; + grub_freebsd_addr_t symstart, symend; + + if (grub_file_seek (file, 0) == (grub_off_t) -1) + return grub_errno; + + if (grub_file_read (file, (char *) &e, sizeof (e)) != sizeof (e)) + { + if (! grub_errno) + return grub_error (GRUB_ERR_BAD_OS, "invalid elf"); + return grub_errno; + } + + err = grub_freebsd_add_meta (FREEBSD_MODINFO_METADATA | + FREEBSD_MODINFOMD_ELFHDR, &e, + sizeof (e)); + if (err) + return err; + + shdr = grub_malloc (e.e_shentsize * e.e_shnum); + if (! shdr) + return grub_errno; + + if (grub_file_seek (file, e.e_shoff) == (grub_off_t) -1) + return grub_errno; + + if (grub_file_read (file, shdr, e.e_shnum * e.e_shentsize) + != e.e_shnum * e.e_shentsize) + { + if (grub_errno) + return grub_errno; + else + return grub_error (GRUB_ERR_BAD_OS, "file is truncated"); + } + for (s = (Elf_Shdr *) shdr; s < (Elf_Shdr *) (shdr + + e.e_shnum * e.e_shentsize); + s = (Elf_Shdr *) ((char *) s + e.e_shentsize)) + if (s->sh_type == SHT_SYMTAB) + break; + if (s >= (Elf_Shdr *) ((char *) shdr + + e.e_shnum * e.e_shentsize)) + return grub_error (GRUB_ERR_BAD_OS, "no symbol table"); + sym = s->sh_offset; + symsize = s->sh_size; + s = (Elf_Shdr *) (shdr + e.e_shentsize * s->sh_link); + str = s->sh_offset; + strsize = s->sh_size; + + if (*kern_end + 4 * sizeof (grub_freebsd_addr_t) + symsize + strsize + > grub_os_area_addr + grub_os_area_size) + return grub_error (GRUB_ERR_OUT_OF_RANGE, + "Not enough memory for the kernel symbols"); + + symstart = curload = ALIGN_UP (*kern_end, sizeof (grub_freebsd_addr_t)); + *((grub_freebsd_addr_t *) UINT_TO_PTR (curload)) = symsize; + curload += sizeof (grub_freebsd_addr_t); + if (grub_file_seek (file, sym) == (grub_off_t) -1) + return grub_errno; + if (grub_file_read (file, UINT_TO_PTR (curload), symsize) != symsize) + { + if (! grub_errno) + return grub_error (GRUB_ERR_BAD_OS, "invalid elf"); + return grub_errno; + } + curload += symsize; + + *((grub_freebsd_addr_t *) UINT_TO_PTR (curload)) = strsize; + curload += sizeof (grub_freebsd_addr_t); + if (grub_file_seek (file, str) == (grub_off_t) -1) + return grub_errno; + if (grub_file_read (file, UINT_TO_PTR (curload), strsize) != strsize) + { + if (! grub_errno) + return grub_error (GRUB_ERR_BAD_OS, "invalid elf"); + return grub_errno; + } + curload += strsize; + curload = ALIGN_UP (curload, sizeof (grub_freebsd_addr_t)); + symend = curload; + + err = grub_freebsd_add_meta (FREEBSD_MODINFO_METADATA | + FREEBSD_MODINFOMD_SSYM, &symstart, + sizeof (symstart)); + if (err) + return err; + + err = grub_freebsd_add_meta (FREEBSD_MODINFO_METADATA | + FREEBSD_MODINFOMD_ESYM, &symend, + sizeof (symend)); + if (err) + return err; + *kern_end = curload; + + return GRUB_ERR_NONE; +}