multiboot.c 13.0 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
/*
 * QEMU PC System Emulator
 *
 * Copyright (c) 2003-2004 Fabrice Bellard
 *
 * 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.
 */

P
Peter Maydell 已提交
25
#include "qemu/osdep.h"
26
#include "hw/hw.h"
P
Paolo Bonzini 已提交
27
#include "hw/nvram/fw_cfg.h"
28
#include "multiboot.h"
29
#include "hw/loader.h"
30
#include "elf.h"
31
#include "sysemu/sysemu.h"
32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57

/* Show multiboot debug output */
//#define DEBUG_MULTIBOOT

#ifdef DEBUG_MULTIBOOT
#define mb_debug(a...) fprintf(stderr, ## a)
#else
#define mb_debug(a...)
#endif

#define MULTIBOOT_STRUCT_ADDR 0x9000

#if MULTIBOOT_STRUCT_ADDR > 0xf0000
#error multiboot struct needs to fit in 16 bit real mode
#endif

enum {
    /* Multiboot info */
    MBI_FLAGS       = 0,
    MBI_MEM_LOWER   = 4,
    MBI_MEM_UPPER   = 8,
    MBI_BOOT_DEVICE = 12,
    MBI_CMDLINE     = 16,
    MBI_MODS_COUNT  = 20,
    MBI_MODS_ADDR   = 24,
    MBI_MMAP_ADDR   = 48,
58
    MBI_BOOTLOADER  = 64,
59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78

    MBI_SIZE        = 88,

    /* Multiboot modules */
    MB_MOD_START    = 0,
    MB_MOD_END      = 4,
    MB_MOD_CMDLINE  = 8,

    MB_MOD_SIZE     = 16,

    /* Region offsets */
    ADDR_E820_MAP = MULTIBOOT_STRUCT_ADDR + 0,
    ADDR_MBI      = ADDR_E820_MAP + 0x500,

    /* Multiboot flags */
    MULTIBOOT_FLAGS_MEMORY      = 1 << 0,
    MULTIBOOT_FLAGS_BOOT_DEVICE = 1 << 1,
    MULTIBOOT_FLAGS_CMDLINE     = 1 << 2,
    MULTIBOOT_FLAGS_MODULES     = 1 << 3,
    MULTIBOOT_FLAGS_MMAP        = 1 << 6,
79
    MULTIBOOT_FLAGS_BOOTLOADER  = 1 << 9,
80 81 82 83 84 85
};

typedef struct {
    /* buffer holding kernel, cmdlines and mb_infos */
    void *mb_buf;
    /* address in target */
A
Avi Kivity 已提交
86
    hwaddr mb_buf_phys;
87 88 89
    /* size of mb_buf in bytes */
    unsigned mb_buf_size;
    /* offset of mb-info's in bytes */
A
Avi Kivity 已提交
90
    hwaddr offset_mbinfo;
91
    /* offset in buffer for cmdlines in bytes */
A
Avi Kivity 已提交
92
    hwaddr offset_cmdlines;
93 94
    /* offset in buffer for bootloader name in bytes */
    hwaddr offset_bootloader;
95
    /* offset of modules in bytes */
A
Avi Kivity 已提交
96
    hwaddr offset_mods;
97 98 99 100 101 102
    /* available slots for mb modules infos */
    int mb_mods_avail;
    /* currently used slots of mb modules */
    int mb_mods_count;
} MultibootState;

103 104
const char *bootloader_name = "qemu";

105 106
static uint32_t mb_add_cmdline(MultibootState *s, const char *cmdline)
{
A
Avi Kivity 已提交
107
    hwaddr p = s->offset_cmdlines;
108
    char *b = (char *)s->mb_buf + p;
109

110 111
    get_opt_value(b, strlen(cmdline) + 1, cmdline);
    s->offset_cmdlines += strlen(b) + 1;
112 113 114
    return s->mb_buf_phys + p;
}

115 116 117 118 119 120 121 122 123 124
static uint32_t mb_add_bootloader(MultibootState *s, const char *bootloader)
{
    hwaddr p = s->offset_bootloader;
    char *b = (char *)s->mb_buf + p;

    memcpy(b, bootloader, strlen(bootloader) + 1);
    s->offset_bootloader += strlen(b) + 1;
    return s->mb_buf_phys + p;
}

125
static void mb_add_mod(MultibootState *s,
A
Avi Kivity 已提交
126 127
                       hwaddr start, hwaddr end,
                       hwaddr cmdline_phys)
128 129 130 131 132 133 134 135 136 137
{
    char *p;
    assert(s->mb_mods_count < s->mb_mods_avail);

    p = (char *)s->mb_buf + s->offset_mbinfo + MB_MOD_SIZE * s->mb_mods_count;

    stl_p(p + MB_MOD_START,   start);
    stl_p(p + MB_MOD_END,     end);
    stl_p(p + MB_MOD_CMDLINE, cmdline_phys);

138 139
    mb_debug("mod%02d: "TARGET_FMT_plx" - "TARGET_FMT_plx"\n",
             s->mb_mods_count, start, end);
140 141 142 143

    s->mb_mods_count++;
}

L
Laszlo Ersek 已提交
144
int load_multiboot(FWCfgState *fw_cfg,
145 146 147 148 149 150 151 152 153 154 155 156 157 158 159
                   FILE *f,
                   const char *kernel_filename,
                   const char *initrd_filename,
                   const char *kernel_cmdline,
                   int kernel_file_size,
                   uint8_t *header)
{
    int i, is_multiboot = 0;
    uint32_t flags = 0;
    uint32_t mh_entry_addr;
    uint32_t mh_load_addr;
    uint32_t mb_kernel_size;
    MultibootState mbs;
    uint8_t bootinfo[MBI_SIZE];
    uint8_t *mb_bootinfo_data;
160
    uint32_t cmdline_len;
161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191

    /* Ok, let's see if it is a multiboot image.
       The header is 12x32bit long, so the latest entry may be 8192 - 48. */
    for (i = 0; i < (8192 - 48); i += 4) {
        if (ldl_p(header+i) == 0x1BADB002) {
            uint32_t checksum = ldl_p(header+i+8);
            flags = ldl_p(header+i+4);
            checksum += flags;
            checksum += (uint32_t)0x1BADB002;
            if (!checksum) {
                is_multiboot = 1;
                break;
            }
        }
    }

    if (!is_multiboot)
        return 0; /* no multiboot */

    mb_debug("qemu: I believe we found a multiboot image!\n");
    memset(bootinfo, 0, sizeof(bootinfo));
    memset(&mbs, 0, sizeof(mbs));

    if (flags & 0x00000004) { /* MULTIBOOT_HEADER_HAS_VBE */
        fprintf(stderr, "qemu: multiboot knows VBE. we don't.\n");
    }
    if (!(flags & 0x00010000)) { /* MULTIBOOT_HEADER_HAS_ADDR */
        uint64_t elf_entry;
        uint64_t elf_low, elf_high;
        int kernel_size;
        fclose(f);
192 193 194 195 196 197

        if (((struct elf64_hdr*)header)->e_machine == EM_X86_64) {
            fprintf(stderr, "Cannot load x86-64 image, give a 32bit one.\n");
            exit(1);
        }

198
        kernel_size = load_elf(kernel_filename, NULL, NULL, &elf_entry,
199 200
                               &elf_low, &elf_high, 0, I386_ELF_MACHINE,
                               0, 0);
201 202 203 204 205 206 207 208
        if (kernel_size < 0) {
            fprintf(stderr, "Error while loading elf kernel\n");
            exit(1);
        }
        mh_load_addr = elf_low;
        mb_kernel_size = elf_high - elf_low;
        mh_entry_addr = elf_entry;

209
        mbs.mb_buf = g_malloc(mb_kernel_size);
210 211 212 213 214 215 216 217 218 219
        if (rom_copy(mbs.mb_buf, mh_load_addr, mb_kernel_size) != mb_kernel_size) {
            fprintf(stderr, "Error while fetching elf kernel from rom\n");
            exit(1);
        }

        mb_debug("qemu: loading multiboot-elf kernel (%#x bytes) with entry %#zx\n",
                  mb_kernel_size, (size_t)mh_entry_addr);
    } else {
        /* Valid if mh_flags sets MULTIBOOT_HEADER_HAS_ADDR. */
        uint32_t mh_header_addr = ldl_p(header+i+12);
220 221
        uint32_t mh_load_end_addr = ldl_p(header+i+20);
        uint32_t mh_bss_end_addr = ldl_p(header+i+24);
222 223
        mh_load_addr = ldl_p(header+i+16);
        uint32_t mb_kernel_text_offset = i - (mh_header_addr - mh_load_addr);
224
        uint32_t mb_load_size = 0;
225
        mh_entry_addr = ldl_p(header+i+28);
226 227 228 229 230 231 232 233

        if (mh_load_end_addr) {
            mb_kernel_size = mh_bss_end_addr - mh_load_addr;
            mb_load_size = mh_load_end_addr - mh_load_addr;
        } else {
            mb_kernel_size = kernel_file_size - mb_kernel_text_offset;
            mb_load_size = mb_kernel_size;
        }
234 235 236 237 238 239 240 241 242

        /* Valid if mh_flags sets MULTIBOOT_HEADER_HAS_VBE.
        uint32_t mh_mode_type = ldl_p(header+i+32);
        uint32_t mh_width = ldl_p(header+i+36);
        uint32_t mh_height = ldl_p(header+i+40);
        uint32_t mh_depth = ldl_p(header+i+44); */

        mb_debug("multiboot: mh_header_addr = %#x\n", mh_header_addr);
        mb_debug("multiboot: mh_load_addr = %#x\n", mh_load_addr);
243 244
        mb_debug("multiboot: mh_load_end_addr = %#x\n", mh_load_end_addr);
        mb_debug("multiboot: mh_bss_end_addr = %#x\n", mh_bss_end_addr);
245
        mb_debug("qemu: loading multiboot kernel (%#x bytes) at %#x\n",
246
                 mb_load_size, mh_load_addr);
247

248
        mbs.mb_buf = g_malloc(mb_kernel_size);
249
        fseek(f, mb_kernel_text_offset, SEEK_SET);
250
        if (fread(mbs.mb_buf, 1, mb_load_size, f) != mb_load_size) {
251 252 253
            fprintf(stderr, "fread() failed\n");
            exit(1);
        }
254
        memset(mbs.mb_buf + mb_load_size, 0, mb_kernel_size - mb_load_size);
255 256 257 258 259 260 261 262
        fclose(f);
    }

    mbs.mb_buf_phys = mh_load_addr;

    mbs.mb_buf_size = TARGET_PAGE_ALIGN(mb_kernel_size);
    mbs.offset_mbinfo = mbs.mb_buf_size;

263
    /* Calculate space for cmdlines, bootloader name, and mb_mods */
264 265
    cmdline_len = strlen(kernel_filename) + 1;
    cmdline_len += strlen(kernel_cmdline) + 1;
266 267
    if (initrd_filename) {
        const char *r = initrd_filename;
268
        cmdline_len += strlen(r) + 1;
269
        mbs.mb_mods_avail = 1;
270
        while (*(r = get_opt_value(NULL, 0, r))) {
271 272 273 274 275
           mbs.mb_mods_avail++;
           r++;
        }
    }

276 277 278 279
    mbs.mb_buf_size += cmdline_len;
    mbs.mb_buf_size += MB_MOD_SIZE * mbs.mb_mods_avail;
    mbs.mb_buf_size += strlen(bootloader_name) + 1;

280 281
    mbs.mb_buf_size = TARGET_PAGE_ALIGN(mbs.mb_buf_size);

282 283 284
    /* enlarge mb_buf to hold cmdlines, bootloader, mb-info structs */
    mbs.mb_buf            = g_realloc(mbs.mb_buf, mbs.mb_buf_size);
    mbs.offset_cmdlines   = mbs.offset_mbinfo + mbs.mb_mods_avail * MB_MOD_SIZE;
285
    mbs.offset_bootloader = mbs.offset_cmdlines + cmdline_len;
286 287

    if (initrd_filename) {
288
        char *next_initrd, not_last;
289 290 291 292 293

        mbs.offset_mods = mbs.mb_buf_size;

        do {
            char *next_space;
294
            int mb_mod_length;
295 296
            uint32_t offs = mbs.mb_buf_size;

297 298 299
            next_initrd = (char *)get_opt_value(NULL, 0, initrd_filename);
            not_last = *next_initrd;
            *next_initrd = '\0';
300 301
            /* if a space comes after the module filename, treat everything
               after that as parameters */
A
Avi Kivity 已提交
302
            hwaddr c = mb_add_cmdline(&mbs, initrd_filename);
303 304 305 306 307
            if ((next_space = strchr(initrd_filename, ' ')))
                *next_space = '\0';
            mb_debug("multiboot loading module: %s\n", initrd_filename);
            mb_mod_length = get_image_size(initrd_filename);
            if (mb_mod_length < 0) {
308
                fprintf(stderr, "Failed to open file '%s'\n", initrd_filename);
309 310 311 312
                exit(1);
            }

            mbs.mb_buf_size = TARGET_PAGE_ALIGN(mb_mod_length + mbs.mb_buf_size);
313
            mbs.mb_buf = g_realloc(mbs.mb_buf, mbs.mb_buf_size);
314 315 316 317 318

            load_image(initrd_filename, (unsigned char *)mbs.mb_buf + offs);
            mb_add_mod(&mbs, mbs.mb_buf_phys + offs,
                       mbs.mb_buf_phys + offs + mb_mod_length, c);

319
            mb_debug("mod_start: %p\nmod_end:   %p\n  cmdline: "TARGET_FMT_plx"\n",
320 321 322
                     (char *)mbs.mb_buf + offs,
                     (char *)mbs.mb_buf + offs + mb_mod_length, c);
            initrd_filename = next_initrd+1;
323
        } while (not_last);
324 325 326 327 328 329 330 331
    }

    /* Commandline support */
    char kcmdline[strlen(kernel_filename) + strlen(kernel_cmdline) + 2];
    snprintf(kcmdline, sizeof(kcmdline), "%s %s",
             kernel_filename, kernel_cmdline);
    stl_p(bootinfo + MBI_CMDLINE, mb_add_cmdline(&mbs, kcmdline));

332 333
    stl_p(bootinfo + MBI_BOOTLOADER, mb_add_bootloader(&mbs, bootloader_name));

334 335 336 337 338 339 340 341
    stl_p(bootinfo + MBI_MODS_ADDR,  mbs.mb_buf_phys + mbs.offset_mbinfo);
    stl_p(bootinfo + MBI_MODS_COUNT, mbs.mb_mods_count); /* mods_count */

    /* the kernel is where we want it to be now */
    stl_p(bootinfo + MBI_FLAGS, MULTIBOOT_FLAGS_MEMORY
                                | MULTIBOOT_FLAGS_BOOT_DEVICE
                                | MULTIBOOT_FLAGS_CMDLINE
                                | MULTIBOOT_FLAGS_MODULES
342 343
                                | MULTIBOOT_FLAGS_MMAP
                                | MULTIBOOT_FLAGS_BOOTLOADER);
344
    stl_p(bootinfo + MBI_BOOT_DEVICE, 0x8000ffff); /* XXX: use the -boot switch? */
345 346 347
    stl_p(bootinfo + MBI_MMAP_ADDR,   ADDR_E820_MAP);

    mb_debug("multiboot: mh_entry_addr = %#x\n", mh_entry_addr);
348 349
    mb_debug("           mb_buf_phys   = "TARGET_FMT_plx"\n", mbs.mb_buf_phys);
    mb_debug("           mod_start     = "TARGET_FMT_plx"\n", mbs.mb_buf_phys + mbs.offset_mods);
350 351 352
    mb_debug("           mb_mods_count = %d\n", mbs.mb_mods_count);

    /* save bootinfo off the stack */
353
    mb_bootinfo_data = g_malloc(sizeof(bootinfo));
354 355 356 357 358 359 360 361 362 363 364 365 366 367
    memcpy(mb_bootinfo_data, bootinfo, sizeof(bootinfo));

    /* Pass variables to option rom */
    fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_ENTRY, mh_entry_addr);
    fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_ADDR, mh_load_addr);
    fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_SIZE, mbs.mb_buf_size);
    fw_cfg_add_bytes(fw_cfg, FW_CFG_KERNEL_DATA,
                     mbs.mb_buf, mbs.mb_buf_size);

    fw_cfg_add_i32(fw_cfg, FW_CFG_INITRD_ADDR, ADDR_MBI);
    fw_cfg_add_i32(fw_cfg, FW_CFG_INITRD_SIZE, sizeof(bootinfo));
    fw_cfg_add_bytes(fw_cfg, FW_CFG_INITRD_DATA, mb_bootinfo_data,
                     sizeof(bootinfo));

G
Gleb Natapov 已提交
368 369
    option_rom[nb_option_roms].name = "multiboot.bin";
    option_rom[nb_option_roms].bootindex = 0;
370 371 372 373
    nb_option_roms++;

    return 1; /* yes, we are multiboot */
}