diff options
-rw-r--r-- | README | 2 | ||||
-rw-r--r-- | fw_cfg.c | 53 | ||||
-rw-r--r-- | include/e820.h | 2 | ||||
-rw-r--r-- | include/multiboot.h | 66 | ||||
-rw-r--r-- | linuxboot.c | 5 | ||||
-rw-r--r-- | main.c | 2 |
6 files changed, 125 insertions, 5 deletions
@@ -46,4 +46,4 @@ TODO ==== * SMBIOS tables -* Multiboot loading +* Multiboot loading from pflash @@ -1,9 +1,11 @@ #include "bios.h" +#include "e820.h" #include "stdio.h" #include "ioport.h" #include "string.h" #include "fw_cfg.h" #include "linuxboot.h" +#include "multiboot.h" struct fw_cfg_file { uint32_t size; @@ -54,6 +56,55 @@ void fw_cfg_file_select(int id) fw_cfg_select(files[id].select); } +/* Multiboot trampoline. QEMU does the ELF parsing. */ + +static void boot_multiboot_from_fw_cfg(void) +{ + void *kernel_addr, *kernel_entry; + struct mb_info *mb; + struct mb_mmap_entry *mbmem; + int i; + uint32_t sz; + + fw_cfg_select(FW_CFG_KERNEL_SIZE); + sz = fw_cfg_readl_le(); + if (!sz) + panic(); + + fw_cfg_select(FW_CFG_KERNEL_ADDR); + kernel_addr = (void *) fw_cfg_readl_le(); + fw_cfg_select(FW_CFG_KERNEL_DATA); + fw_cfg_read(kernel_addr, sz); + + fw_cfg_select(FW_CFG_INITRD_SIZE); + sz = fw_cfg_readl_le(); + if (!sz) + panic(); + + fw_cfg_select(FW_CFG_INITRD_ADDR); + mb = (struct mb_info *) fw_cfg_readl_le(); + fw_cfg_select(FW_CFG_INITRD_DATA); + fw_cfg_read(mb, sz); + + mb->mem_lower = 639; + mb->mem_upper = (lowmem - 1048576) >> 10; + + mb->mmap_length = 0; + for (i = 0; i < e820->nr_map; i++) { + mbmem = (struct mb_mmap_entry *) (mb->mmap_addr + mb->mmap_length); + mbmem->size = sizeof(e820->map[i]); + mbmem->base_addr = e820->map[i].addr; + mbmem->length = e820->map[i].size; + mbmem->type = e820->map[i].type; + mb->mmap_length += sizeof(*mbmem); + } + + fw_cfg_select(FW_CFG_KERNEL_ENTRY); + kernel_entry = (void *) fw_cfg_readl_le(); + asm volatile("jmp *%2" : : "a" (0x2badb002), "b"(mb), "c"(kernel_entry)); + panic(); +} + void boot_from_fwcfg(void) { struct linuxboot_args args; @@ -79,7 +130,7 @@ void boot_from_fwcfg(void) fw_cfg_read(args.header, sizeof(args.header)); if (!parse_bzimage(&args)) - return; + boot_multiboot_from_fw_cfg(); /* SETUP_DATA already selected */ if (args.setup_size > sizeof(args.header)) diff --git a/include/e820.h b/include/e820.h index 085df6a..60483e8 100644 --- a/include/e820.h +++ b/include/e820.h @@ -29,6 +29,8 @@ struct e820map { struct e820entry map[]; }; +extern struct e820map *e820; + #define ISA_START_ADDRESS 0xa0000 #define ISA_END_ADDRESS 0x100000 diff --git a/include/multiboot.h b/include/multiboot.h new file mode 100644 index 0000000..d3d4528 --- /dev/null +++ b/include/multiboot.h @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2013 Kevin Wolf <kwolf@redhat.com> + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef MULTIBOOT_H +#define MULTIBOOT_H + +#include <stdint.h> + +struct mb_info { + uint32_t flags; + uint32_t mem_lower; + uint32_t mem_upper; + uint32_t boot_device; + uint32_t cmdline; + uint32_t mods_count; + uint32_t mods_addr; + char syms[16]; + uint32_t mmap_length; + uint32_t mmap_addr; + uint32_t drives_length; + uint32_t drives_addr; + uint32_t config_table; + uint32_t boot_loader_name; + uint32_t apm_table; + uint32_t vbe_control_info; + uint32_t vbe_mode_info; + uint16_t vbe_mode; + uint16_t vbe_interface_seg; + uint16_t vbe_interface_off; + uint16_t vbe_interface_len; +} __attribute__((packed)); + +struct mb_module { + uint32_t mod_start; + uint32_t mod_end; + uint32_t string; + uint32_t reserved; +} __attribute__((packed)); + +struct mb_mmap_entry { + uint32_t size; + uint64_t base_addr; + uint64_t length; + uint32_t type; +} __attribute__((packed)); + +#endif diff --git a/linuxboot.c b/linuxboot.c index 0e7fe37..65b5936 100644 --- a/linuxboot.c +++ b/linuxboot.c @@ -40,8 +40,9 @@ bool parse_bzimage(struct linuxboot_args *args) if (ldl_p(header+0x202) == 0x53726448) protocol = lduw_p(header+0x206); else { - // if (parse_multiboot(&args)) return; - protocol = 0; + /* assume multiboot. TODO: scan for header */ + return false; + // protocol = 0; } if (protocol < 0x200 || !(header[0x211] & 0x01)) { @@ -29,13 +29,13 @@ static void setup_idt(void) /* Top of memory below 4GB. */ uint32_t lowmem; +struct e820map *e820; static void extract_e820(void) { int id = fw_cfg_file_id("etc/e820"); uint32_t size; int nr_map; - struct e820map *e820; int i; if (id == -1) |