diff --git a/conf/i386-pc.rmk b/conf/i386-pc.rmk index 78e4f00..430055b 100644 --- a/conf/i386-pc.rmk +++ b/conf/i386-pc.rmk @@ -146,7 +146,8 @@ pkglib_MODULES = biosdisk.mod _chain.mod _linux.mod linux.mod normal.mod \ _multiboot.mod chain.mod multiboot.mod reboot.mod halt.mod \ vbe.mod vbetest.mod vbeinfo.mod video.mod gfxterm.mod \ videotest.mod play.mod bitmap.mod tga.mod cpuid.mod serial.mod \ - ata.mod vga.mod memdisk.mod jpeg.mod png.mod pci.mod lspci.mod + ata.mod vga.mod memdisk.mod jpeg.mod png.mod pci.mod lspci.mod \ + aout.mod _freebsd.mod freebsd.mod # For biosdisk.mod. biosdisk_mod_SOURCES = disk/i386/pc/biosdisk.c @@ -298,4 +299,19 @@ lspci_mod_SOURCES = commands/lspci.c lspci_mod_CFLAGS = $(COMMON_CFLAGS) lspci_mod_LDFLAGS = $(COMMON_LDFLAGS) +# For aout.mod +aout_mod_SOURCES = loader/aout.c +aout_mod_CFLAGS = $(COMMON_CFLAGS) +aout_mod_LDFLAGS = $(COMMON_LDFLAGS) + +# For _freebsd.mod +_freebsd_mod_SOURCES = loader/i386/pc/freebsd.c +_freebsd_mod_CFLAGS = $(COMMON_CFLAGS) +_freebsd_mod_LDFLAGS = $(COMMON_LDFLAGS) + +# For freebsd.mod +freebsd_mod_SOURCES = loader/i386/pc/freebsd_normal.c +freebsd_mod_CFLAGS = $(COMMON_CFLAGS) +freebsd_mod_LDFLAGS = $(COMMON_LDFLAGS) + include $(srcdir)/conf/common.mk diff --git a/include/grub/aout.h b/include/grub/aout.h new file mode 100755 index 0000000..3243b82 --- /dev/null +++ b/include/grub/aout.h @@ -0,0 +1,91 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2008 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#ifndef GRUB_AOUT_HEADER +#define GRUB_AOUT_HEADER 1 + +#include + +struct grub_aout32_header +{ + grub_uint32_t a_midmag; /* htonl(flags<<26 | mid<<16 | magic) */ + grub_uint32_t a_text; /* text segment size */ + grub_uint32_t a_data; /* initialized data size */ + grub_uint32_t a_bss; /* uninitialized data size */ + grub_uint32_t a_syms; /* symbol table size */ + grub_uint32_t a_entry; /* entry point */ + grub_uint32_t a_trsize; /* text relocation size */ + grub_uint32_t a_drsize; /* data relocation size */ +}; + +struct grub_aout64_header +{ + grub_uint32_t a_midmag; /* htonl(flags<<26 | mid<<16 | magic) */ + grub_uint64_t a_text; /* text segment size */ + grub_uint64_t a_data; /* initialized data size */ + grub_uint64_t a_bss; /* uninitialized data size */ + grub_uint64_t a_syms; /* symbol table size */ + grub_uint64_t a_entry; /* entry point */ + grub_uint64_t a_trsize; /* text relocation size */ + grub_uint64_t a_drsize; /* data relocation size */ +}; + +union grub_aout_header +{ + struct grub_aout32_header aout32; + struct grub_aout64_header aout64; +}; + +#define AOUT_TYPE_NONE 0 +#define AOUT_TYPE_AOUT32 1 +#define AOUT_TYPE_AOUT64 6 + +#define AOUT32_OMAGIC 0x107 /* 0407 old impure format */ +#define AOUT32_NMAGIC 0x108 /* 0410 read-only text */ +#define AOUT32_ZMAGIC 0x10b /* 0413 demand load format */ +#define AOUT32_QMAGIC 0xcc /* 0314 "compact" demand load format */ + +#define AOUT64_OMAGIC 0x1001 +#define AOUT64_ZMAGIC 0x1002 +#define AOUT64_NMAGIC 0x1003 + +#define AOUT_MID_ZERO 0 /* unknown - implementation dependent */ +#define AOUT_MID_SUN010 1 /* sun 68010/68020 binary */ +#define AOUT_MID_SUN020 2 /* sun 68020-only binary */ +#define AOUT_MID_I386 134 /* i386 BSD binary */ +#define AOUT_MID_SPARC 138 /* sparc */ +#define AOUT_MID_HP200 200 /* hp200 (68010) BSD binary */ +#define AOUT_MID_HP300 300 /* hp300 (68020+68881) BSD binary */ +#define AOUT_MID_HPUX 0x20C /* hp200/300 HP-UX binary */ +#define AOUT_MID_HPUX800 0x20B /* hp800 HP-UX binary */ + +#define AOUT_FLAG_PIC 0x10 /* contains position independant code */ +#define AOUT_FLAG_DYNAMIC 0x20 /* contains run-time link-edit info */ +#define AOUT_FLAG_DPMASK 0x30 /* mask for the above */ + +#define AOUT_GETMAGIC(header) ((header).a_midmag & 0xffff) +#define AOUT_GETMID(header) ((header).a_midmag >> 16) & 0x03ff) +#define AOUT_GETFLAG(header) ((header).a_midmag >> 26) & 0x3f) + +int EXPORT_FUNC(grub_aout_get_type) (union grub_aout_header *header); + +grub_err_t EXPORT_FUNC(grub_aout_load) (grub_file_t file, int offset, + grub_addr_t load_addr, int load_size, + grub_addr_t bss_end_addr); + +#endif /* ! GRUB_AOUT_HEADER */ diff --git a/include/grub/i386/loader.h b/include/grub/i386/loader.h index 45a1652..c0b10e5 100644 --- a/include/grub/i386/loader.h +++ b/include/grub/i386/loader.h @@ -39,10 +39,17 @@ void EXPORT_FUNC(grub_multiboot_real_boot) (grub_addr_t entry, void EXPORT_FUNC(grub_multiboot2_real_boot) (grub_addr_t entry, struct grub_multiboot_info *mbi) __attribute__ ((noreturn)); +void EXPORT_FUNC(grub_freebsd_real_boot) (grub_addr_t entry, + grub_uint32_t bootdrv, + void *bootinfo) + __attribute__ ((noreturn)); + /* It is necessary to export these functions, because normal mode commands reuse rescue mode commands. */ void grub_rescue_cmd_linux (int argc, char *argv[]); void grub_rescue_cmd_initrd (int argc, char *argv[]); +void grub_rescue_cmd_freebsd (int argc, char *argv[]); +void grub_rescue_cmd_freebsd_loadenv (int argc, char *argv[]); #endif /* ! GRUB_LOADER_CPU_HEADER */ diff --git a/kern/elf.c b/kern/elf.c index ca10b5b..3e90ea0 100644 --- a/kern/elf.c +++ b/kern/elf.c @@ -228,9 +228,9 @@ grub_elf32_load (grub_elf_t _elf, grub_elf32_load_hook_t _load_hook, if (phdr->p_type != PT_LOAD) return 0; - load_addr = phdr->p_paddr; if (load_hook && load_hook (phdr, &load_addr)) return 1; + load_addr = phdr->p_paddr; if (load_addr < load_base) load_base = load_addr; @@ -407,9 +407,9 @@ grub_elf64_load (grub_elf_t _elf, grub_elf64_load_hook_t _load_hook, if (phdr->p_type != PT_LOAD) return 0; - load_addr = phdr->p_paddr; if (load_hook && load_hook (phdr, &load_addr)) return 1; + load_addr = phdr->p_paddr; if (load_addr < load_base) load_base = load_addr; diff --git a/kern/i386/loader.S b/kern/i386/loader.S index 266f4ef..6c5a5c1 100644 --- a/kern/i386/loader.S +++ b/kern/i386/loader.S @@ -162,3 +162,24 @@ FUNCTION(grub_multiboot2_real_boot) movl $MULTIBOOT2_BOOTLOADER_MAGIC,%eax popl %ecx jmp *%ecx + + +#define FREEBSD_RB_BOOTINFO 0x80000000 + +FUNCTION(grub_freebsd_real_boot) + pushl %ecx + xorl %ecx, %ecx + pushl %ecx + pushl %ecx + pushl %ecx + pushl %edx + pushl $FREEBSD_RB_BOOTINFO + pushl %eax + + call EXT_C(grub_dl_unload_all) + call EXT_C(grub_stop_floppy) + + cli + + popl %ecx + call *%ecx diff --git a/loader/aout.c b/loader/aout.c new file mode 100755 index 0000000..2c82b60 --- /dev/null +++ b/loader/aout.c @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2008 Free Software Foundation, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include + +int +grub_aout_get_type (union grub_aout_header *header) +{ + int magic; + + magic = AOUT_GETMAGIC (header->aout32); + if ((magic == AOUT32_OMAGIC) || (magic == AOUT32_NMAGIC) || + (magic == AOUT32_ZMAGIC) || (magic == AOUT32_QMAGIC)) + return AOUT_TYPE_AOUT32; + else if ((magic == AOUT64_OMAGIC) || (magic == AOUT64_NMAGIC) || + (magic == AOUT64_ZMAGIC)) + return AOUT_TYPE_AOUT64; + else + return AOUT_TYPE_NONE; +} + +grub_err_t +grub_aout_load (grub_file_t file, int offset, + grub_addr_t load_addr, + int load_size, + grub_addr_t bss_end_addr) +{ + if ((grub_file_seek (file, offset)) == (grub_off_t) - 1) + return grub_errno; + + if (!load_size) + load_size = file->size - offset; + + grub_file_read (file, (char *) load_addr, load_size); + + if (grub_errno) + return grub_errno; + + if (bss_end_addr) + grub_memset (load_addr + load_size, 0, + bss_end_addr - load_addr - load_size); + + return GRUB_ERR_NONE; +} diff --git a/loader/i386/pc/freebsd.c b/loader/i386/pc/freebsd.c new file mode 100755 index 0000000..3c744aa --- /dev/null +++ b/loader/i386/pc/freebsd.c @@ -0,0 +1,361 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2008 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define BOOTINFO_VERSION 1 +#define N_BIOS_GEOM 8 +#define B_DEVMAGIC 0xa0000000 +#define BASE_SLICE 2 + +struct grub_freebsd_bootinfo +{ + grub_uint32_t bi_version; + grub_uint8_t *bi_kernelname; + struct nfs_diskless *bi_nfs_diskless; + grub_uint32_t bi_n_bios_used; + grub_uint32_t bi_bios_geom[N_BIOS_GEOM]; + grub_uint32_t bi_size; + grub_uint8_t bi_memsizes_valid; + grub_uint8_t bi_bios_dev; + grub_uint8_t bi_pad[2]; + grub_uint32_t bi_basemem; + grub_uint32_t bi_extmem; + grub_uint32_t bi_symtab; + grub_uint32_t bi_esymtab; + grub_uint32_t bi_kernend; + grub_uint32_t bi_envp; + grub_uint32_t bi_modulep; +} __attribute__ ((packed)); + +static grub_dl_t my_mod; + +static grub_addr_t entry, kernend; + +static grub_err_t +grub_freebsd_boot (void) +{ + struct grub_freebsd_bootinfo bi; + char *p; + grub_uint32_t bootdev; + + auto int iterate_env (struct grub_env_var *var); + int iterate_env (struct grub_env_var *var) + { + if ((!grub_memcmp (var->name, "FreeBSD.", 8)) && (var->name[8])) + { + grub_strcpy (p, &var->name[8]); + p += grub_strlen (p); + *(p++) = '='; + grub_strcpy (p, var->value); + p += grub_strlen (p) + 1; + } + + return 0; + } + + grub_memset (&bi, 0, sizeof (bi)); + bi.bi_version = BOOTINFO_VERSION; + bi.bi_size = sizeof (bi); + + bootdev = B_DEVMAGIC + (BASE_SLICE << 20); + + p = grub_env_get ("root"); + if ((p) && ((p[0] == 'h') || (p[0] == 'f')) && (p[1] == 'd') && + (p[2] >= '0') && (p[2] <= '9')) + { + if (p[0] == 'h') + bi.bi_bios_dev = 0x80; + + bi.bi_bios_dev += grub_strtoul (p + 2, &p, 0); + + if ((p) && (p[0] == ',') && (p[1] >= '0') && (p[1] <= '9')) + { + /* Partition number. */ + bootdev |= (grub_strtoul (p + 1, &p, 0)) << 8; + + /* Unit number. */ + bootdev |= (bi.bi_bios_dev & 0x7F) << 16; + } + } + + kernend = (kernend + 3) & (~3); + p = (char *) kernend; + + grub_env_iterate (iterate_env); + + if (p != (char *) kernend) + { + *(p++) = 0; + + bi.bi_envp = kernend; + bi.bi_kernend = ((grub_uint32_t) p + 3) & (~3); + } + else + bi.bi_kernend = kernend; + + grub_freebsd_real_boot (entry, bootdev, &bi); + + /* Not reached. */ + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_freebsd_unload (void) +{ + grub_dl_unref (my_mod); + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_bsd_load_aout (grub_file_t file, union grub_aout_header *ah) +{ + grub_addr_t load_addr, bss_end_addr; + int ofs, align_4k; + + if (grub_aout_get_type (ah) != AOUT_TYPE_AOUT32) + return grub_error (GRUB_ERR_BAD_OS, "invalid a.out header"); + + entry = ah->aout32.a_entry & 0xFFFFFF; + + if (AOUT_GETMAGIC (ah->aout32) == AOUT32_ZMAGIC) + { + load_addr = entry; + ofs = 0x1000; + align_4k = 0; + } + else + { + load_addr = entry & 0xF00000; + ofs = sizeof (struct grub_aout32_header); + align_4k = 1; + } + + if (load_addr < 0x100000) + return grub_error (GRUB_ERR_BAD_OS, "load address below 1M"); + + kernend = load_addr + ah->aout32.a_text + ah->aout32.a_data; + if (align_4k) + kernend = (kernend + 0xFFF) & 0xFFFFF000; + + if (ah->aout32.a_bss) + { + kernend += ah->aout32.a_bss; + if (align_4k) + kernend = (kernend + 0xFFF) & 0xFFFFF000; + + bss_end_addr = kernend; + } + else + bss_end_addr = 0; + + return grub_aout_load (file, ofs, load_addr, + ah->aout32.a_text + ah->aout32.a_data, bss_end_addr); +} + +static grub_err_t +grub_bsd_elf32_hook (Elf32_Phdr * phdr, UNUSED grub_addr_t * addr) +{ + Elf32_Addr paddr; + + phdr->p_paddr &= 0xFFFFFF; + paddr = phdr->p_paddr; + + if ((paddr < grub_os_area_addr) + || (paddr + phdr->p_memsz > grub_os_area_addr + grub_os_area_size)) + return grub_error (GRUB_ERR_OUT_OF_RANGE, "Address 0x%x is out of range", + paddr); + + if (paddr + phdr->p_memsz > kernend) + kernend = paddr + phdr->p_memsz; + + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_bsd_load_elf (grub_file_t file) +{ + grub_elf_t elf = 0; + grub_err_t err; + + kernend = 0; + elf = grub_elf_file (file); + if (!elf) + return grub_errno; + + if (grub_elf_is_elf32 (elf)) + { + entry = elf->ehdr.ehdr32.e_entry & 0xFFFFFF; + err = grub_elf32_load (elf, grub_bsd_elf32_hook, 0, 0); + } + else + err = grub_error (GRUB_ERR_BAD_OS, "invalid elf"); + + return err; +} + +void +grub_rescue_cmd_freebsd_loadenv (int argc, char *argv[]) +{ + grub_file_t file = 0; + char *buf = 0, *curr, *next; + int len; + + if (argc == 0) + { + grub_error (GRUB_ERR_BAD_ARGUMENT, "no filename"); + goto fail; + } + + file = grub_gzfile_open (argv[0], 1); + if ((!file) || (!file->size)) + goto fail; + + len = file->size; + buf = grub_malloc (len + 1); + if (!buf) + goto fail; + + if (grub_file_read (file, buf, len) != len) + goto fail; + + buf[len] = 0; + + next = buf; + while (next) + { + char *p; + + curr = next; + next = grub_strchr (curr, '\n'); + if (next) + { + + p = next - 1; + while (p > curr) + { + if ((*p != '\r') && (*p != ' ') && (*p != '\t')) + break; + p--; + } + + if ((p > curr) && (*p == '"')) + p--; + + *(p + 1) = 0; + next++; + } + + if (*curr == '#') + continue; + + p = grub_strchr (curr, '='); + if (!p) + continue; + + *(p++) = 0; + + if (*curr) + { + char name[grub_strlen (curr) + 8 + 1]; + + if (*p == '"') + p++; + + grub_sprintf (name, "FreeBSD.%s", curr); + if (grub_env_set (name, p)) + goto fail; + } + } + +fail: + grub_free (buf); + + if (file) + grub_file_close (file); +} + +void +grub_rescue_cmd_freebsd (int argc, char *argv[]) +{ + grub_file_t file = 0; + union grub_aout_header ah; + + grub_dl_ref (my_mod); + + if (argc == 0) + { + grub_error (GRUB_ERR_BAD_ARGUMENT, "no kernel specified"); + goto fail; + } + + file = grub_gzfile_open (argv[0], 1); + if (!file) + goto fail; + + if (grub_file_read (file, (char *) &ah, sizeof (ah)) != sizeof (ah)) + { + grub_error (GRUB_ERR_READ_ERROR, "cannot read the a.out header"); + goto fail; + } + + if (grub_aout_get_type (&ah) == AOUT_TYPE_NONE) + grub_bsd_load_elf (file); + else + grub_bsd_load_aout (file, &ah); + + if (grub_errno == GRUB_ERR_NONE) + grub_loader_set (grub_freebsd_boot, grub_freebsd_unload, 1); + +fail: + + if (file) + grub_file_close (file); + + if (grub_errno != GRUB_ERR_NONE) + grub_dl_unref (my_mod); +} + +GRUB_MOD_INIT (aout_freebsd) +{ + grub_rescue_register_command ("freebsd", + grub_rescue_cmd_freebsd, + "load freebsd loader"); + grub_rescue_register_command ("freebsd_loadenv", + grub_rescue_cmd_freebsd_loadenv, + "load freebsd env"); + + my_mod = mod; +} + +GRUB_MOD_FINI (aout_freebsd) +{ + grub_rescue_unregister_command ("freebsd"); + grub_rescue_unregister_command ("freebsd_loadenv"); +} diff --git a/loader/i386/pc/freebsd_normal.c b/loader/i386/pc/freebsd_normal.c new file mode 100755 index 0000000..f9f7d47 --- /dev/null +++ b/loader/i386/pc/freebsd_normal.c @@ -0,0 +1,60 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2008 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include +#include +#include +#include +#include + +static grub_err_t +grub_normal_freebsd_command (struct grub_arg_list *state + __attribute__ ((unused)), int argc, char **args) +{ + grub_rescue_cmd_freebsd (argc, args); + return grub_errno; +} + +static grub_err_t +grub_normal_freebsd_loadenv_command (struct grub_arg_list *state + __attribute__ ((unused)), int argc, + char **args) +{ + grub_rescue_cmd_freebsd_loadenv (argc, args); + return grub_errno; +} + +GRUB_MOD_INIT (aout_freebsd_normal) +{ + (void) mod; /* To stop warning. */ + grub_register_command ("freebsd", grub_normal_freebsd_command, + GRUB_COMMAND_FLAG_BOTH, + "aout_freebsd FILE [ARGS...]", + "Load freebsd loader.", 0); + grub_register_command ("freebsd_loadenv", + grub_normal_freebsd_loadenv_command, + GRUB_COMMAND_FLAG_BOTH, + "freebsd_loadenv FILE", "Load freebsd env.", 0); +} + +GRUB_MOD_FINI (aout_freebsd_normal) +{ + grub_unregister_command ("freebsd"); + grub_unregister_command ("freebsd_loadenv"); +} diff --git a/loader/i386/pc/multiboot.c b/loader/i386/pc/multiboot.c index 893f11b..e9fcc4a 100644 --- a/loader/i386/pc/multiboot.c +++ b/loader/i386/pc/multiboot.c @@ -36,6 +36,7 @@ #include #include #include +#include #include #include #include @@ -315,7 +316,18 @@ grub_multiboot (int argc, char *argv[]) goto fail; } - if (grub_multiboot_load_elf (file, buffer) != GRUB_ERR_NONE) + if (header->flags & MULTIBOOT_AOUT_KLUDGE) + { + if ((grub_aout_load (file, (char *) header - buffer, header->load_addr, + ((header->load_end_addr == 0) ? 0 : + header->load_end_addr - header->load_addr), + header->bss_end_addr)) + !=GRUB_ERR_NONE) + goto fail; + + entry = header->entry_addr; + } + else if (grub_multiboot_load_elf (file, buffer) != GRUB_ERR_NONE) goto fail; mbi = grub_malloc (sizeof (struct grub_multiboot_info));