Patchwork [v10,5/8] hw/pc_sysfw: Support system flash memory with pflash

login
register
mail settings
Submitter jordan.l.justen@intel.com
Date Jan. 8, 2012, 7:51 p.m.
Message ID <1326052300-1172-6-git-send-email-jordan.l.justen@intel.com>
Download mbox | patch
Permalink /patch/134926/
State New
Headers show

Comments

jordan.l.justen@intel.com - Jan. 8, 2012, 7:51 p.m.
Flash can be enabled by calling pc_system_firmware_init
with the system_flash_enabled parameter being non-zero.
If system_flash_enabled is zero, then the older qemu
rom creation method will be used.

If flash is enabled and a pflash image is found, then
it is used for the system firmware image.

If flash is enabled and a pflash image is not initially
found, then a read-only pflash device is created using
the -bios filename.

KVM cannot execute from a pflash region currently.
Therefore, when KVM is enabled, a (read-only) ram memory
region is created and filled with the contents of the
pflash drive.

Signed-off-by: Jordan Justen <jordan.l.justen@intel.com>
---
 default-configs/i386-softmmu.mak   |    1 +
 default-configs/x86_64-softmmu.mak |    1 +
 hw/pc.c                            |    2 +-
 hw/pc.h                            |    3 +-
 hw/pc_sysfw.c                      |  172 +++++++++++++++++++++++++++++++++++-
 5 files changed, 173 insertions(+), 6 deletions(-)

Patch

diff --git a/default-configs/i386-softmmu.mak b/default-configs/i386-softmmu.mak
index e67ebb3..cd407a9 100644
--- a/default-configs/i386-softmmu.mak
+++ b/default-configs/i386-softmmu.mak
@@ -22,3 +22,4 @@  CONFIG_SOUND=y
 CONFIG_HPET=y
 CONFIG_APPLESMC=y
 CONFIG_I8259=y
+CONFIG_PFLASH_CFI01=y
diff --git a/default-configs/x86_64-softmmu.mak b/default-configs/x86_64-softmmu.mak
index b75757e..47734ea 100644
--- a/default-configs/x86_64-softmmu.mak
+++ b/default-configs/x86_64-softmmu.mak
@@ -22,3 +22,4 @@  CONFIG_SOUND=y
 CONFIG_HPET=y
 CONFIG_APPLESMC=y
 CONFIG_I8259=y
+CONFIG_PFLASH_CFI01=y
diff --git a/hw/pc.c b/hw/pc.c
index 47e946a..7a7af90 100644
--- a/hw/pc.c
+++ b/hw/pc.c
@@ -999,7 +999,7 @@  void pc_memory_init(MemoryRegion *system_memory,
 
 
     /* Initialize PC system firmware */
-    pc_system_firmware_init(rom_memory);
+    pc_system_firmware_init(rom_memory, 0);
 
     option_rom_mr = g_malloc(sizeof(*option_rom_mr));
     memory_region_init_ram(option_rom_mr, "pc.rom", PC_ROM_SIZE);
diff --git a/hw/pc.h b/hw/pc.h
index 227f495..ca5e76b 100644
--- a/hw/pc.h
+++ b/hw/pc.h
@@ -247,7 +247,8 @@  static inline bool isa_ne2000_init(ISABus *bus, int base, int irq, NICInfo *nd)
 }
 
 /* pc_sysfw.c */
-void pc_system_firmware_init(MemoryRegion *rom_memory);
+void pc_system_firmware_init(MemoryRegion *rom_memory,
+                             int system_flash_enabled);
 
 /* e820 types */
 #define E820_RAM        1
diff --git a/hw/pc_sysfw.c b/hw/pc_sysfw.c
index 3121c95..57ac81c 100644
--- a/hw/pc_sysfw.c
+++ b/hw/pc_sysfw.c
@@ -23,13 +23,151 @@ 
  * THE SOFTWARE.
  */
 
+#include "hw.h"
 #include "pc.h"
+#include "hw/boards.h"
 #include "loader.h"
 #include "sysemu.h"
+#include "flash.h"
+#include "kvm.h"
 
 #define BIOS_FILENAME "bios.bin"
 
-static void pc_system_rom_init(MemoryRegion *rom_memory)
+static void pc_isa_bios_init(MemoryRegion *rom_memory,
+                             MemoryRegion *flash_mem,
+                             int ram_size)
+{
+    int isa_bios_size;
+    MemoryRegion *isa_bios;
+    uint64_t flash_size;
+    void *flash_ptr, *isa_bios_ptr;
+
+    flash_size = memory_region_size(flash_mem);
+
+    /* map the last 128KB of the BIOS in ISA space */
+    isa_bios_size = flash_size;
+    if (isa_bios_size > (128 * 1024)) {
+        isa_bios_size = 128 * 1024;
+    }
+    isa_bios = g_malloc(sizeof(*isa_bios));
+    memory_region_init_ram(isa_bios, "isa-bios", isa_bios_size);
+    vmstate_register_ram_global(isa_bios);
+    memory_region_add_subregion_overlap(rom_memory,
+                                        0x100000 - isa_bios_size,
+                                        isa_bios,
+                                        1);
+
+    /* copy ISA rom image from top of flash memory */
+    flash_ptr = memory_region_get_ram_ptr(flash_mem);
+    isa_bios_ptr = memory_region_get_ram_ptr(isa_bios);
+    memcpy(isa_bios_ptr,
+           ((uint8_t*)flash_ptr) + (flash_size - isa_bios_size),
+           isa_bios_size);
+
+    memory_region_set_readonly(isa_bios, true);
+}
+
+static void pc_fw_add_pflash_drv(void)
+{
+    QemuOpts *opts;
+    QEMUMachine *machine;
+    char *filename;
+
+    if (bios_name == NULL) {
+        bios_name = BIOS_FILENAME;
+    }
+    filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name);
+
+    opts = drive_add(IF_PFLASH, -1, filename, "readonly=on");
+    if (opts == NULL) {
+      return;
+    }
+
+    machine = find_default_machine();
+    if (machine == NULL) {
+      return;
+    }
+
+    drive_init(opts, machine->use_scsi);
+}
+
+static void pc_system_flash_init(MemoryRegion *rom_memory,
+                                 DriveInfo *pflash_drv)
+{
+    BlockDriverState *bdrv;
+    int64_t size;
+    target_phys_addr_t phys_addr;
+    int sector_bits, sector_size;
+    pflash_t *system_flash;
+    MemoryRegion *flash_mem;
+
+    bdrv = pflash_drv->bdrv;
+    size = bdrv_getlength(pflash_drv->bdrv);
+    sector_bits = 12;
+    sector_size = 1 << sector_bits;
+
+    if ((size % sector_size) != 0) {
+        fprintf(stderr,
+                "qemu: PC system firmware (pflash) must be a multiple of 0x%x\n",
+                sector_size);
+        exit(1);
+    }
+
+    phys_addr = 0x100000000ULL - size;
+    system_flash = pflash_cfi01_register(phys_addr, NULL, "system.flash", size,
+                                         bdrv, sector_size, size >> sector_bits,
+                                         1, 0x0000, 0x0000, 0x0000, 0x0000, 0);
+    flash_mem = pflash_cfi01_get_memory(system_flash);
+
+    pc_isa_bios_init(rom_memory, flash_mem, size);
+}
+
+static void pc_system_rom_init(MemoryRegion *rom_memory,
+                               DriveInfo *pflash_drv)
+{
+    BlockDriverState *bdrv;
+    int64_t size;
+    target_phys_addr_t phys_addr;
+    int sector_bits, sector_size;
+    MemoryRegion *sys_rom;
+    void *buffer;
+    int ret;
+
+    bdrv = pflash_drv->bdrv;
+    size = bdrv_getlength(pflash_drv->bdrv);
+    sector_bits = 9;
+    sector_size = 1 << sector_bits;
+
+    if ((size % sector_size) != 0) {
+        fprintf(stderr,
+                "qemu: PC system rom (pflash) must be a multiple of 0x%x\n",
+                sector_size);
+        exit(1);
+    }
+
+    phys_addr = 0x100000000ULL - size;
+    sys_rom = g_malloc(sizeof(*sys_rom));
+    memory_region_init_ram(sys_rom, "system.rom", size);
+    vmstate_register_ram_global(sys_rom);
+    buffer = memory_region_get_ram_ptr(sys_rom);
+    memory_region_add_subregion(rom_memory, phys_addr, sys_rom);
+
+    /* read the rom content */
+    ret = bdrv_read(bdrv, 0, buffer, size >> sector_bits);
+    if (ret < 0) {
+        memory_region_destroy(sys_rom);
+        g_free(sys_rom);
+        fprintf(stderr,
+                "qemu: Failed to read rom image from pflash drive\n");
+        exit(1);
+    }
+
+    memory_region_set_readonly(sys_rom, true);
+
+    pc_isa_bios_init(rom_memory, sys_rom, size);
+}
+
+static void old_pc_system_rom_init(MemoryRegion *rom_memory)
 {
     char *filename;
     MemoryRegion *bios, *isa_bios;
@@ -84,9 +222,35 @@  static void pc_system_rom_init(MemoryRegion *rom_memory)
                                 bios);
 }
 
-void pc_system_firmware_init(MemoryRegion *rom_memory)
+void pc_system_firmware_init(MemoryRegion *rom_memory,
+                             int system_flash_enabled)
 {
-    pc_system_rom_init(rom_memory);
-}
+    int flash_present;
+    DriveInfo *pflash_drv;
+
+    if (!system_flash_enabled) {
+        old_pc_system_rom_init(rom_memory);
+        return;
+    }
+
+    pflash_drv = drive_get(IF_PFLASH, 0, 0);
+    flash_present = (pflash_drv != NULL);
 
+    if (!flash_present) {
+        pc_fw_add_pflash_drv();
+        pflash_drv = drive_get(IF_PFLASH, 0, 0);
+        flash_present = (pflash_drv != NULL);
+    }
+
+    if (!flash_present) {
+        fprintf(stderr, "qemu: PC system firmware (pflash) not available\n");
+        exit(1);
+    }
+
+    if (!kvm_enabled()) {
+        pc_system_flash_init(rom_memory, pflash_drv);
+    } else {
+        pc_system_rom_init(rom_memory, pflash_drv);
+    }
+}