#include "hw.h"
 #include "disas.h"
+#include "monitor.h"
 #include "sysemu.h"
 #include "uboot_image.h"
 #include "loader.h"
     return size;
 }
 
-/* return the amount read, just like fread.  0 may mean error or eof */
-int fread_targphys(target_phys_addr_t dst_addr, size_t nbytes, FILE *f)
-{
-    uint8_t buf[4096];
-    target_phys_addr_t dst_begin = dst_addr;
-    size_t want, did;
-
-    while (nbytes) {
-       want = nbytes > sizeof(buf) ? sizeof(buf) : nbytes;
-       did = fread(buf, 1, want, f);
-
-       cpu_physical_memory_write_rom(dst_addr, buf, did);
-       dst_addr += did;
-       nbytes -= did;
-       if (did != want)
-           break;
-    }
-    return dst_addr - dst_begin;
-}
-
-/* returns 0 on error, 1 if ok */
-int fread_targphys_ok(target_phys_addr_t dst_addr, size_t nbytes, FILE *f)
-{
-    return fread_targphys(dst_addr, nbytes, f) == nbytes;
-}
-
 /* read()-like version */
-int read_targphys(int fd, target_phys_addr_t dst_addr, size_t nbytes)
+int read_targphys(const char *name,
+                  int fd, target_phys_addr_t dst_addr, size_t nbytes)
 {
-    uint8_t buf[4096];
-    target_phys_addr_t dst_begin = dst_addr;
-    size_t want, did;
-
-    while (nbytes) {
-       want = nbytes > sizeof(buf) ? sizeof(buf) : nbytes;
-       did = read(fd, buf, want);
-       if (did != want) break;
-
-       cpu_physical_memory_write_rom(dst_addr, buf, did);
-       dst_addr += did;
-       nbytes -= did;
-    }
-    return dst_addr - dst_begin;
+    uint8_t *buf;
+    size_t did;
+
+    buf = qemu_malloc(nbytes);
+    did = read(fd, buf, nbytes);
+    if (did > 0)
+        rom_add_blob_fixed("read", buf, did, dst_addr);
+    qemu_free(buf);
+    return did;
 }
 
 /* return the size or -1 if error */
 int load_image_targphys(const char *filename,
                        target_phys_addr_t addr, int max_sz)
 {
-    FILE *f;
-    size_t got;
-
-    f = fopen(filename, "rb");
-    if (!f) return -1;
-
-    got = fread_targphys(addr, max_sz, f);
-    if (ferror(f)) { fclose(f); return -1; }
-    fclose(f);
+    int size;
 
-    return got;
+    size = get_image_size(filename);
+    if (size > 0)
+        rom_add_file_fixed(filename, addr);
+    return size;
 }
 
 void pstrcpy_targphys(target_phys_addr_t dest, int buf_size,
         if (e.a_text + e.a_data > max_sz)
             goto fail;
        lseek(fd, N_TXTOFF(e), SEEK_SET);
-       size = read_targphys(fd, addr, e.a_text + e.a_data);
+       size = read_targphys(filename, fd, addr, e.a_text + e.a_data);
        if (size < 0)
            goto fail;
        break;
         if (N_DATADDR(e, target_page_size) + e.a_data > max_sz)
             goto fail;
        lseek(fd, N_TXTOFF(e), SEEK_SET);
-       size = read_targphys(fd, addr, e.a_text);
+       size = read_targphys(filename, fd, addr, e.a_text);
        if (size < 0)
            goto fail;
-        ret = read_targphys(fd, addr + N_DATADDR(e, target_page_size),
+        ret = read_targphys(filename, fd, addr + N_DATADDR(e, target_page_size),
                             e.a_data);
        if (ret < 0)
            goto fail;
 
     lseek(fd, 0, SEEK_SET);
     if (e_ident[EI_CLASS] == ELFCLASS64) {
-        ret = load_elf64(fd, address_offset, must_swab, pentry,
+        ret = load_elf64(filename, fd, address_offset, must_swab, pentry,
                          lowaddr, highaddr, elf_machine, clear_lsb);
     } else {
-        ret = load_elf32(fd, address_offset, must_swab, pentry,
+        ret = load_elf32(filename, fd, address_offset, must_swab, pentry,
                          lowaddr, highaddr, elf_machine, clear_lsb);
     }
 
         hdr->ih_size = bytes;
     }
 
-    cpu_physical_memory_write_rom(hdr->ih_load, data, hdr->ih_size);
+    rom_add_blob_fixed(filename, data, hdr->ih_size, hdr->ih_load);
 
     if (loadaddr)
         *loadaddr = hdr->ih_load;
     close(fd);
     return ret;
 }
+
+/*
+ * Functions for reboot-persistent memory regions.
+ *  - used for vga bios and option roms.
+ *  - also linux kernel (-kernel / -initrd).
+ */
+
+typedef struct Rom Rom;
+
+struct Rom {
+    char *name;
+    char *path;
+    size_t romsize;
+    uint8_t *data;
+    int align;
+    int isrom;
+
+    target_phys_addr_t min;
+    target_phys_addr_t max;
+    target_phys_addr_t addr;
+    QTAILQ_ENTRY(Rom) next;
+};
+
+static QTAILQ_HEAD(, Rom) roms = QTAILQ_HEAD_INITIALIZER(roms);
+
+static void rom_insert(Rom *rom)
+{
+    Rom *item;
+
+    /* list is ordered by load address */
+    QTAILQ_FOREACH(item, &roms, next) {
+        if (rom->min >= item->min)
+            continue;
+        QTAILQ_INSERT_BEFORE(item, rom, next);
+        return;
+    }
+    QTAILQ_INSERT_TAIL(&roms, rom, next);
+}
+
+int rom_add_file(const char *file,
+                 target_phys_addr_t min, target_phys_addr_t max, int align)
+{
+    Rom *rom;
+    int rc, fd = -1;
+
+    rom = qemu_mallocz(sizeof(*rom));
+    rom->name = qemu_strdup(file);
+    rom->path = qemu_find_file(QEMU_FILE_TYPE_BIOS, rom->name);
+    if (rom->path == NULL) {
+        fprintf(stderr, "Could not find option rom '%s'\n", rom->name);
+        goto err;
+    }
+
+    fd = open(rom->path, O_RDONLY);
+    if (fd == -1) {
+        fprintf(stderr, "Could not open option rom '%s': %s\n",
+                rom->path, strerror(errno));
+        goto err;
+    }
+
+    rom->align   = align;
+    rom->min     = min;
+    rom->max     = max;
+    rom->romsize = lseek(fd, 0, SEEK_END);
+    rom->data    = qemu_mallocz(rom->romsize);
+    lseek(fd, 0, SEEK_SET);
+    rc = read(fd, rom->data, rom->romsize);
+    if (rc != rom->romsize) {
+        fprintf(stderr, "rom: file %-20s: read error: rc=%d (expected %zd)\n",
+                rom->name, rc, rom->romsize);
+        goto err;
+    }
+    close(fd);
+    rom_insert(rom);
+    return 0;
+
+err:
+    if (fd != -1)
+        close(fd);
+    qemu_free(rom->data);
+    qemu_free(rom->path);
+    qemu_free(rom->name);
+    qemu_free(rom);
+    return -1;
+}
+
+int rom_add_blob(const char *name, const void *blob, size_t len,
+                 target_phys_addr_t min, target_phys_addr_t max, int align)
+{
+    Rom *rom;
+
+    rom = qemu_mallocz(sizeof(*rom));
+    rom->name    = qemu_strdup(name);
+    rom->align   = align;
+    rom->min     = min;
+    rom->max     = max;
+    rom->romsize = len;
+    rom->data    = qemu_mallocz(rom->romsize);
+    memcpy(rom->data, blob, len);
+    rom_insert(rom);
+    return 0;
+}
+
+static void rom_reset(void *unused)
+{
+    Rom *rom;
+
+    QTAILQ_FOREACH(rom, &roms, next) {
+        if (rom->data == NULL)
+            continue;
+        cpu_physical_memory_write_rom(rom->addr, rom->data, rom->romsize);
+        if (rom->isrom) {
+            /* rom needs to be written only once */
+            qemu_free(rom->data);
+            rom->data = NULL;
+        }
+    }
+}
+
+int rom_load_all(void)
+{
+    target_phys_addr_t addr = 0;
+    int memtype;
+    Rom *rom;
+
+    QTAILQ_FOREACH(rom, &roms, next) {
+        if (addr < rom->min)
+            addr = rom->min;
+        if (rom->max) {
+            /* load address range */
+            if (rom->align) {
+                addr += (rom->align-1);
+                addr &= ~(rom->align-1);
+            }
+            if (addr + rom->romsize > rom->max) {
+                fprintf(stderr, "rom: out of memory (rom %s, "
+                        "addr 0x" TARGET_FMT_plx
+                        ", size 0x%zx, max 0x" TARGET_FMT_plx ")\n",
+                        rom->name, addr, rom->romsize, rom->max);
+                return -1;
+            }
+        } else {
+            /* fixed address requested */
+            if (addr != rom->min) {
+                fprintf(stderr, "rom: requested regions overlap "
+                        "(rom %s. free=0x" TARGET_FMT_plx
+                        ", addr=0x" TARGET_FMT_plx ")\n",
+                        rom->name, addr, rom->min);
+                return -1;
+            }
+        }
+        rom->addr = addr;
+        addr += rom->romsize;
+        memtype = cpu_get_physical_page_desc(rom->addr) & (3 << IO_MEM_SHIFT);
+        if (memtype == IO_MEM_ROM)
+            rom->isrom = 1;
+    }
+    qemu_register_reset(rom_reset, NULL);
+    rom_reset(NULL);
+    return 0;
+}
+
+void do_info_roms(Monitor *mon)
+{
+    Rom *rom;
+
+    QTAILQ_FOREACH(rom, &roms, next) {
+        monitor_printf(mon, "addr=" TARGET_FMT_plx
+                       " size=0x%06zx mem=%s name=\"%s\" \n",
+                       rom->addr, rom->romsize,
+                       rom->isrom ? "rom" : "ram",
+                       rom->name);
+    }
+}
 
 static PITState *pit;
 static PCII440FXState *i440fx_state;
 
-typedef struct rom_reset_data {
-    uint8_t *data;
-    target_phys_addr_t addr;
-    unsigned size;
-} RomResetData;
-
-static void option_rom_reset(void *_rrd)
-{
-    RomResetData *rrd = _rrd;
-
-    cpu_physical_memory_write_rom(rrd->addr, rrd->data, rrd->size);
-}
-
-static void option_rom_setup_reset(target_phys_addr_t addr, unsigned size)
-{
-    RomResetData *rrd = qemu_malloc(sizeof *rrd);
-
-    rrd->data = qemu_malloc(size);
-    cpu_physical_memory_read(addr, rrd->data, size);
-    rrd->addr = addr;
-    rrd->size = size;
-    qemu_register_reset(option_rom_reset, rrd);
-}
-
 typedef struct isa_irq_state {
     qemu_irq *i8259;
     qemu_irq *ioapic;
 
 /* Generate an initial boot sector which sets state and jump to
    a specified vector */
-static void generate_bootsect(target_phys_addr_t option_rom,
-                              uint32_t gpr[8], uint16_t segs[6], uint16_t ip)
+static void generate_bootsect(uint32_t gpr[8], uint16_t segs[6], uint16_t ip)
 {
     uint8_t rom[512], *p, *reloc;
     uint8_t sum;
         sum += rom[i];
     rom[sizeof(rom) - 1] = -sum;
 
-    cpu_physical_memory_write_rom(option_rom, rom, sizeof(rom));
-    option_rom_setup_reset(option_rom, sizeof (rom));
+    rom_add_blob("linux-bootsect", rom, sizeof(rom),
+                 PC_ROM_MIN_OPTION, PC_ROM_MAX, PC_ROM_ALIGN);
 }
 
 static long get_file_size(FILE *f)
                           const char *kernel_cmdline,
                           uint8_t *header)
 {
-    int i, t, is_multiboot = 0;
+    int i, is_multiboot = 0;
     uint32_t flags = 0;
     uint32_t mh_entry_addr;
     uint32_t mh_load_addr;
     uint32_t mb_kernel_size;
     uint32_t mmap_addr = MULTIBOOT_STRUCT_ADDR;
     uint32_t mb_bootinfo = MULTIBOOT_STRUCT_ADDR + 0x500;
-    uint32_t mb_cmdline = mb_bootinfo + 0x200;
     uint32_t mb_mod_end;
+    uint8_t bootinfo[0x500];
+    uint32_t cmdline = 0x200;
 
     /* Ok, let's see if it is a multiboot image.
        The header is 12x32bit long, so the latest entry may be 8192 - 48. */
 #ifdef DEBUG_MULTIBOOT
     fprintf(stderr, "qemu: I believe we found a multiboot image!\n");
 #endif
+    memset(bootinfo, 0, sizeof(bootinfo));
 
     if (flags & 0x00000004) { /* MULTIBOOT_HEADER_HAS_VBE */
         fprintf(stderr, "qemu: multiboot knows VBE. we don't.\n");
         uint32_t mh_bss_end_addr = ldl_p(header+i+24);
 #endif
         uint32_t mb_kernel_text_offset = i - (mh_header_addr - mh_load_addr);
+        uint8_t *kernel;
 
         mh_entry_addr = ldl_p(header+i+28);
         mb_kernel_size = get_file_size(f) - mb_kernel_text_offset;
         fprintf(stderr, "multiboot: mh_load_addr = %#x\n", mh_load_addr);
         fprintf(stderr, "multiboot: mh_load_end_addr = %#x\n", mh_load_end_addr);
         fprintf(stderr, "multiboot: mh_bss_end_addr = %#x\n", mh_bss_end_addr);
-#endif
-
-        fseek(f, mb_kernel_text_offset, SEEK_SET);
-
-#ifdef DEBUG_MULTIBOOT
         fprintf(stderr, "qemu: loading multiboot kernel (%#x bytes) at %#x\n",
                 mb_kernel_size, mh_load_addr);
 #endif
 
-        if (!fread_targphys_ok(mh_load_addr, mb_kernel_size, f)) {
-            fprintf(stderr, "qemu: read error on multiboot kernel '%s' (%#x)\n",
-                    kernel_filename, mb_kernel_size);
-            exit(1);
-        }
+        kernel = qemu_malloc(mb_kernel_size);
+        fseek(f, mb_kernel_text_offset, SEEK_SET);
+        fread(kernel, 1, mb_kernel_size, f);
+        rom_add_blob_fixed(kernel_filename, kernel, mb_kernel_size,
+                           mh_load_addr);
+        qemu_free(kernel);
         fclose(f);
     }
 
     mb_mod_end = mh_load_addr + mb_kernel_size;
 
     /* load modules */
-    stl_phys(mb_bootinfo + 20, 0x0); /* mods_count */
+    stl_p(bootinfo + 20, 0x0); /* mods_count */
     if (initrd_filename) {
-        uint32_t mb_mod_info = mb_bootinfo + 0x100;
-        uint32_t mb_mod_cmdline = mb_bootinfo + 0x300;
+        uint32_t mb_mod_info = 0x100;
+        uint32_t mb_mod_cmdline = 0x300;
         uint32_t mb_mod_start = mh_load_addr;
         uint32_t mb_mod_length = mb_kernel_size;
         char *next_initrd;
                 *next_initrd = '\0';
             /* if a space comes after the module filename, treat everything
                after that as parameters */
-            cpu_physical_memory_write(mb_mod_cmdline, (uint8_t*)initrd_filename,
-                                      strlen(initrd_filename) + 1);
-            stl_phys(mb_mod_info + 8, mb_mod_cmdline); /* string */
+            pstrcpy((char*)bootinfo + mb_mod_cmdline,
+                    sizeof(bootinfo) - mb_mod_cmdline,
+                    initrd_filename);
+            stl_p(bootinfo + mb_mod_info + 8, mb_mod_cmdline); /* string */
             mb_mod_cmdline += strlen(initrd_filename) + 1;
+            if (mb_mod_cmdline > sizeof(bootinfo))
+                mb_mod_cmdline = sizeof(bootinfo);
             if ((next_space = strchr(initrd_filename, ' ')))
                 *next_space = '\0';
 #ifdef DEBUG_MULTIBOOT
             printf("multiboot loading module: %s\n", initrd_filename);
 #endif
-            f = fopen(initrd_filename, "rb");
-            if (f) {
-                mb_mod_start = (mb_mod_start + mb_mod_length + (TARGET_PAGE_SIZE - 1))
-                             & (TARGET_PAGE_MASK);
-                mb_mod_length = get_file_size(f);
-                mb_mod_end = mb_mod_start + mb_mod_length;
-
-                if (!fread_targphys_ok(mb_mod_start, mb_mod_length, f)) {
-                    fprintf(stderr, "qemu: read error on multiboot module '%s' (%#x)\n",
-                            initrd_filename, mb_mod_length);
-                    exit(1);
-                }
+            mb_mod_start = (mb_mod_start + mb_mod_length + (TARGET_PAGE_SIZE - 1))
+                         & (TARGET_PAGE_MASK);
+            mb_mod_length = get_image_size(initrd_filename);
+            if (mb_mod_length < 0) {
+                fprintf(stderr, "failed to get %s image size\n", initrd_filename);
+                exit(1);
+            }
+            mb_mod_end = mb_mod_start + mb_mod_length;
+            rom_add_file_fixed(initrd_filename, mb_mod_start);
 
-                mb_mod_count++;
-                stl_phys(mb_mod_info + 0, mb_mod_start);
-                stl_phys(mb_mod_info + 4, mb_mod_start + mb_mod_length);
+            mb_mod_count++;
+            stl_p(bootinfo + mb_mod_info + 0, mb_mod_start);
+            stl_p(bootinfo + mb_mod_info + 4, mb_mod_start + mb_mod_length);
+            stl_p(bootinfo + mb_mod_info + 12, 0x0); /* reserved */
 #ifdef DEBUG_MULTIBOOT
-                printf("mod_start: %#x\nmod_end:   %#x\n", mb_mod_start,
-                       mb_mod_start + mb_mod_length);
+            printf("mod_start: %#x\nmod_end:   %#x\n", mb_mod_start,
+                   mb_mod_start + mb_mod_length);
 #endif
-                stl_phys(mb_mod_info + 12, 0x0); /* reserved */
-            }
             initrd_filename = next_initrd+1;
             mb_mod_info += 16;
         } while (next_initrd);
-        stl_phys(mb_bootinfo + 20, mb_mod_count); /* mods_count */
-        stl_phys(mb_bootinfo + 24, mb_bootinfo + 0x100); /* mods_addr */
+        stl_p(bootinfo + 20, mb_mod_count); /* mods_count */
+        stl_p(bootinfo + 24, mb_bootinfo + 0x100); /* mods_addr */
     }
 
-    /* Make sure we're getting kernel + modules back after reset */
-    option_rom_setup_reset(mh_load_addr, mb_mod_end - mh_load_addr);
-
     /* Commandline support */
-    stl_phys(mb_bootinfo + 16, mb_cmdline);
-    t = strlen(kernel_filename);
-    cpu_physical_memory_write(mb_cmdline, (uint8_t*)kernel_filename, t);
-    mb_cmdline += t;
-    stb_phys(mb_cmdline++, ' ');
-    t = strlen(kernel_cmdline) + 1;
-    cpu_physical_memory_write(mb_cmdline, (uint8_t*)kernel_cmdline, t);
+    stl_p(bootinfo + 16, mb_bootinfo + cmdline);
+    snprintf((char*)bootinfo + cmdline, 0x100, "%s %s",
+             kernel_filename, kernel_cmdline);
 
     /* the kernel is where we want it to be now */
-
 #define MULTIBOOT_FLAGS_MEMORY (1 << 0)
 #define MULTIBOOT_FLAGS_BOOT_DEVICE (1 << 1)
 #define MULTIBOOT_FLAGS_CMDLINE (1 << 2)
 #define MULTIBOOT_FLAGS_MODULES (1 << 3)
 #define MULTIBOOT_FLAGS_MMAP (1 << 6)
-    stl_phys(mb_bootinfo, MULTIBOOT_FLAGS_MEMORY
-                        | MULTIBOOT_FLAGS_BOOT_DEVICE
-                        | MULTIBOOT_FLAGS_CMDLINE
-                        | MULTIBOOT_FLAGS_MODULES
-                        | MULTIBOOT_FLAGS_MMAP);
-    stl_phys(mb_bootinfo + 4, 640); /* mem_lower */
-    stl_phys(mb_bootinfo + 8, ram_size / 1024); /* mem_upper */
-    stl_phys(mb_bootinfo + 12, 0x8001ffff); /* XXX: use the -boot switch? */
-    stl_phys(mb_bootinfo + 48, mmap_addr); /* mmap_addr */
+    stl_p(bootinfo, MULTIBOOT_FLAGS_MEMORY
+                  | MULTIBOOT_FLAGS_BOOT_DEVICE
+                  | MULTIBOOT_FLAGS_CMDLINE
+                  | MULTIBOOT_FLAGS_MODULES
+                  | MULTIBOOT_FLAGS_MMAP);
+    stl_p(bootinfo + 4, 640); /* mem_lower */
+    stl_p(bootinfo + 8, ram_size / 1024); /* mem_upper */
+    stl_p(bootinfo + 12, 0x8001ffff); /* XXX: use the -boot switch? */
+    stl_p(bootinfo + 48, mmap_addr); /* mmap_addr */
 
 #ifdef DEBUG_MULTIBOOT
     fprintf(stderr, "multiboot: mh_entry_addr = %#x\n", mh_entry_addr);
     fw_cfg_add_i32(fw_cfg, FW_CFG_INITRD_ADDR, mb_bootinfo);
     fw_cfg_add_i32(fw_cfg, FW_CFG_INITRD_SIZE, mmap_addr);
 
-    /* Make sure we're getting the config space back after reset */
-    option_rom_setup_reset(mb_bootinfo, 0x500);
+    rom_add_blob_fixed("multiboot-info", bootinfo, sizeof(bootinfo),
+                       mb_bootinfo);
 
     option_rom[nb_option_roms] = "multiboot.bin";
     nb_option_roms++;
 }
 
 static void load_linux(void *fw_cfg,
-                       target_phys_addr_t option_rom,
                        const char *kernel_filename,
                       const char *initrd_filename,
                       const char *kernel_cmdline,
-               target_phys_addr_t max_ram_size)
+                       target_phys_addr_t max_ram_size)
 {
     uint16_t protocol;
     uint32_t gpr[8];
     uint16_t real_seg;
     int setup_size, kernel_size, initrd_size = 0, cmdline_size;
     uint32_t initrd_max;
-    uint8_t header[8192];
+    uint8_t header[8192], *setup, *kernel;
     target_phys_addr_t real_addr, prot_addr, cmdline_addr, initrd_addr = 0;
-    FILE *f, *fi;
+    FILE *f;
     char *vmode;
 
     /* Align to 16 bytes as a paranoia measure */
        initrd_max = max_ram_size-ACPI_DATA_SIZE-1;
 
     /* kernel command line */
-    pstrcpy_targphys(cmdline_addr, 4096, kernel_cmdline);
+    rom_add_blob_fixed("linux-cmdline", kernel_cmdline,
+                       strlen(kernel_cmdline)+1, cmdline_addr);
 
     if (protocol >= 0x202) {
        stl_p(header+0x228, cmdline_addr);
            exit(1);
        }
 
-       fi = fopen(initrd_filename, "rb");
-       if (!fi) {
-           fprintf(stderr, "qemu: could not load initial ram disk '%s': %s\n",
-                   initrd_filename, strerror(errno));
-           exit(1);
-       }
-
-       initrd_size = get_file_size(fi);
-       initrd_addr = (initrd_max-initrd_size) & ~4095;
-
-       if (!fread_targphys_ok(initrd_addr, initrd_size, fi)) {
-           fprintf(stderr, "qemu: read error on initial ram disk '%s': %s\n",
-                   initrd_filename, strerror(errno));
-           exit(1);
-       }
-       fclose(fi);
+       initrd_size = get_image_size(initrd_filename);
+        initrd_addr = (initrd_max-initrd_size) & ~4095;
+        rom_add_file_fixed(initrd_filename, initrd_addr);
 
        stl_p(header+0x218, initrd_addr);
        stl_p(header+0x21c, initrd_size);
     }
 
-    /* store the finalized header and load the rest of the kernel */
-    cpu_physical_memory_write(real_addr, header, ARRAY_SIZE(header));
-
+    /* load kernel and setup */
     setup_size = header[0x1f1];
     if (setup_size == 0)
        setup_size = 4;
-
     setup_size = (setup_size+1)*512;
-    /* Size of protected-mode code */
-    kernel_size -= (setup_size > ARRAY_SIZE(header)) ? setup_size : ARRAY_SIZE(header);
-
-    /* In case we have read too much already, copy that over */
-    if (setup_size < ARRAY_SIZE(header)) {
-        cpu_physical_memory_write(prot_addr, header + setup_size, ARRAY_SIZE(header) - setup_size);
-        prot_addr += (ARRAY_SIZE(header) - setup_size);
-        setup_size = ARRAY_SIZE(header);
-    }
+    kernel_size -= setup_size;
 
-    if (!fread_targphys_ok(real_addr + ARRAY_SIZE(header),
-                           setup_size - ARRAY_SIZE(header), f) ||
-       !fread_targphys_ok(prot_addr, kernel_size, f)) {
-       fprintf(stderr, "qemu: read error on kernel '%s'\n",
-               kernel_filename);
-       exit(1);
-    }
+    setup  = qemu_malloc(setup_size);
+    kernel = qemu_malloc(kernel_size);
+    fseek(f, 0, SEEK_SET);
+    fread(setup, 1, setup_size, f);
+    fread(kernel, 1, kernel_size, f);
     fclose(f);
+    memcpy(setup, header, MIN(sizeof(header), setup_size));
+    rom_add_blob_fixed("linux-setup", setup,
+                       setup_size, real_addr);
+    rom_add_blob_fixed(kernel_filename, kernel,
+                       kernel_size, prot_addr);
+    qemu_free(setup);
+    qemu_free(kernel);
 
     /* generate bootsector to set up the initial register state */
     real_seg = real_addr >> 4;
     memset(gpr, 0, sizeof gpr);
     gpr[4] = cmdline_addr-real_addr-16;        /* SP (-16 is paranoia) */
 
-    option_rom_setup_reset(real_addr, setup_size);
-    option_rom_setup_reset(prot_addr, kernel_size);
-    option_rom_setup_reset(cmdline_addr, cmdline_size);
-    if (initrd_filename)
-        option_rom_setup_reset(initrd_addr, initrd_size);
-
-    generate_bootsect(option_rom, gpr, seg, 0);
+    generate_bootsect(gpr, seg, 0);
 }
 
 static const int ide_iobase[2] = { 0x1f0, 0x170 };
     nb_ne2k++;
 }
 
-static int load_option_rom(const char *oprom, target_phys_addr_t start,
-                           target_phys_addr_t end)
-{
-    int size;
-    char *filename;
-
-    filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, oprom);
-    if (filename) {
-        size = get_image_size(filename);
-        if (size > 0 && start + size > end) {
-            fprintf(stderr, "Not enough space to load option rom '%s'\n",
-                    oprom);
-            exit(1);
-        }
-        size = load_image_targphys(filename, start, end - start);
-        qemu_free(filename);
-    } else {
-        size = -1;
-    }
-    if (size < 0) {
-        fprintf(stderr, "Could not load option rom '%s'\n", oprom);
-        exit(1);
-    }
-    /* Round up optiom rom size to the next 2k boundary */
-    size = (size + 2047) & ~2047;
-    option_rom_setup_reset(start, size);
-    return size;
-}
-
 int cpu_is_bsp(CPUState *env)
 {
     return env->cpuid_apic_id == 0;
     int ret, linux_boot, i;
     ram_addr_t ram_addr, bios_offset, option_rom_offset;
     ram_addr_t below_4g_mem_size, above_4g_mem_size = 0;
-    int bios_size, isa_bios_size, oprom_area_size;
+    int bios_size, isa_bios_size;
     PCIBus *pci_bus;
     ISADevice *isa_dev;
     int piix3_devfn = -1;
 
 
 
-    option_rom_offset = qemu_ram_alloc(0x20000);
-    oprom_area_size = 0;
-    cpu_register_physical_memory(0xc0000, 0x20000, option_rom_offset);
+    option_rom_offset = qemu_ram_alloc(PC_ROM_SIZE);
+    cpu_register_physical_memory(PC_ROM_MIN_VGA, PC_ROM_SIZE, option_rom_offset);
 
     if (using_vga) {
-        const char *vgabios_filename;
         /* VGA BIOS load */
         if (cirrus_vga_enabled) {
-            vgabios_filename = VGABIOS_CIRRUS_FILENAME;
+            rom_add_vga(VGABIOS_CIRRUS_FILENAME);
         } else {
-            vgabios_filename = VGABIOS_FILENAME;
+            rom_add_vga(VGABIOS_FILENAME);
         }
-        oprom_area_size = load_option_rom(vgabios_filename, 0xc0000, 0xe0000);
     }
-    /* Although video roms can grow larger than 0x8000, the area between
-     * 0xc0000 - 0xc8000 is reserved for them. It means we won't be looking
-     * for any other kind of option rom inside this area */
-    if (oprom_area_size < 0x8000)
-        oprom_area_size = 0x8000;
 
     /* map all the bios at the top of memory */
     cpu_register_physical_memory((uint32_t)(-bios_size),
     fw_cfg = bochs_bios_init();
 
     if (linux_boot) {
-        load_linux(fw_cfg, 0xc0000 + oprom_area_size,
-                   kernel_filename, initrd_filename, kernel_cmdline, below_4g_mem_size);
-        oprom_area_size += 2048;
+        load_linux(fw_cfg, kernel_filename, initrd_filename, kernel_cmdline, below_4g_mem_size);
     }
 
     for (i = 0; i < nb_option_roms; i++) {
-        oprom_area_size += load_option_rom(option_rom[i], 0xc0000 + oprom_area_size,
-                                           0xe0000);
+        rom_add_option(option_rom[i]);
     }
 
     for (i = 0; i < nb_nics; i++) {
             model = "e1000";
         snprintf(nic_oprom, sizeof(nic_oprom), "pxe-%s.bin", model);
 
-        oprom_area_size += load_option_rom(nic_oprom, 0xc0000 + oprom_area_size,
-                                           0xe0000);
+        rom_add_option(nic_oprom);
     }
 
     cpu_irq = qemu_allocate_irqs(pic_irq_request, NULL, 1);