From c8e829b7bf6e1c84af8b4b13ee7fce2959c63e0e Mon Sep 17 00:00:00 2001 From: Greg Bellows Date: Mon, 15 Dec 2014 17:09:47 -0600 Subject: target-arm: Add arm_boot_info secure_boot control Adds the secure_boot boolean field to the arm_boot_info descriptor. This fields is used to indicate whether Linux should boot into secure or non-secure state if the ARM EL3 feature is enabled. The default is to leave the CPU in an unaltered reset state. On EL3 enabled systems, the reset state is secure and can be overridden by setting the added field to false. Signed-off-by: Greg Bellows Reviewed-by: Peter Maydell Message-id: 1418684992-8996-11-git-send-email-greg.bellows@linaro.org Signed-off-by: Peter Maydell --- include/hw/arm/arm.h | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'include/hw') diff --git a/include/hw/arm/arm.h b/include/hw/arm/arm.h index cefc9e6988..e5a5d8c328 100644 --- a/include/hw/arm/arm.h +++ b/include/hw/arm/arm.h @@ -37,6 +37,10 @@ struct arm_boot_info { hwaddr gic_cpu_if_addr; int nb_cpus; int board_id; + /* ARM machines that support the ARM Security Extensions use this field to + * control whether Linux is booted as secure(true) or non-secure(false). + */ + bool secure_boot; int (*atag_board)(const struct arm_boot_info *info, void *p); /* multicore boards that use the default secondary core boot functions * can ignore these two function calls. If the default functions won't -- cgit v1.2.3-55-g7522 From 5712db6ae5101db645f71edc393368cd59bfd314 Mon Sep 17 00:00:00 2001 From: Laszlo Ersek Date: Mon, 22 Dec 2014 13:11:35 +0100 Subject: fw_cfg: hard separation between the MMIO and I/O port mappings We are going to introduce a wide data register for fw_cfg, but only for the MMIO mapped device. The wide data register will also require the tightening of endiannesses. However we don't want to touch the I/O port mapped fw_cfg device at all. Currently QEMU provides a single fw_cfg device type that can handle both I/O port and MMIO mapping. This flexibility is not actually exploited by any board in the tree, but it renders restricting the above changes to MMIO very hard. Therefore, let's derive two classes from TYPE_FW_CFG: TYPE_FW_CFG_IO and TYPE_FW_CFG_MEM. TYPE_FW_CFG_IO incorporates the base I/O port and the related combined MemoryRegion. (NB: all boards in the tree that use the I/O port mapped flavor opt for the combined mapping; that is, when the data port overlays the high address byte of the selector port. Therefore we can drop the capability to map those I/O ports separately.) TYPE_FW_CFG_MEM incorporates the base addresses for the MMIO selector and data registers, and their respective MemoryRegions. The "realize" and "props" class members are specific to each new derived class, and become unused for the base class. The base class retains the "reset" member and the "vmsd" member, because the reset functionality and the set of migrated data are not specific to the mapping. The new functions fw_cfg_init_io() and fw_cfg_init_mem() expose the possible mappings in separation. For now fw_cfg_init() is retained as a compatibility shim that enforces the above assumptions. Signed-off-by: Laszlo Ersek Signed-off-by: Paolo Bonzini Message-id: 1419250305-31062-2-git-send-email-pbonzini@redhat.com Signed-off-by: Peter Maydell --- hw/nvram/fw_cfg.c | 177 ++++++++++++++++++++++++++++++++-------------- include/hw/nvram/fw_cfg.h | 2 + include/qemu/typedefs.h | 2 + 3 files changed, 126 insertions(+), 55 deletions(-) (limited to 'include/hw') diff --git a/hw/nvram/fw_cfg.c b/hw/nvram/fw_cfg.c index c4b78ed36c..ab7bfffae2 100644 --- a/hw/nvram/fw_cfg.c +++ b/hw/nvram/fw_cfg.c @@ -32,10 +32,16 @@ #define FW_CFG_SIZE 2 #define FW_CFG_DATA_SIZE 1 -#define TYPE_FW_CFG "fw_cfg" #define FW_CFG_NAME "fw_cfg" #define FW_CFG_PATH "/machine/" FW_CFG_NAME -#define FW_CFG(obj) OBJECT_CHECK(FWCfgState, (obj), TYPE_FW_CFG) + +#define TYPE_FW_CFG "fw_cfg" +#define TYPE_FW_CFG_IO "fw_cfg_io" +#define TYPE_FW_CFG_MEM "fw_cfg_mem" + +#define FW_CFG(obj) OBJECT_CHECK(FWCfgState, (obj), TYPE_FW_CFG) +#define FW_CFG_IO(obj) OBJECT_CHECK(FWCfgIoState, (obj), TYPE_FW_CFG_IO) +#define FW_CFG_MEM(obj) OBJECT_CHECK(FWCfgMemState, (obj), TYPE_FW_CFG_MEM) typedef struct FWCfgEntry { uint32_t len; @@ -50,8 +56,6 @@ struct FWCfgState { SysBusDevice parent_obj; /*< public >*/ - MemoryRegion ctl_iomem, data_iomem, comb_iomem; - uint32_t ctl_iobase, data_iobase; FWCfgEntry entries[2][FW_CFG_MAX_ENTRY]; FWCfgFiles *files; uint16_t cur_entry; @@ -59,6 +63,23 @@ struct FWCfgState { Notifier machine_ready; }; +struct FWCfgIoState { + /*< private >*/ + FWCfgState parent_obj; + /*< public >*/ + + MemoryRegion comb_iomem; + uint32_t iobase; +}; + +struct FWCfgMemState { + /*< private >*/ + FWCfgState parent_obj; + /*< public >*/ + + MemoryRegion ctl_iomem, data_iomem; +}; + #define JPG_FILE 0 #define BMP_FILE 1 @@ -560,19 +581,11 @@ static void fw_cfg_machine_ready(struct Notifier *n, void *data) qemu_register_reset(fw_cfg_machine_reset, s); } -FWCfgState *fw_cfg_init(uint32_t ctl_port, uint32_t data_port, - hwaddr ctl_addr, hwaddr data_addr) -{ - DeviceState *dev; - SysBusDevice *d; - FWCfgState *s; - dev = qdev_create(NULL, TYPE_FW_CFG); - qdev_prop_set_uint32(dev, "ctl_iobase", ctl_port); - qdev_prop_set_uint32(dev, "data_iobase", data_port); - d = SYS_BUS_DEVICE(dev); - s = FW_CFG(dev); +static void fw_cfg_init1(DeviceState *dev) +{ + FWCfgState *s = FW_CFG(dev); assert(!object_resolve_path(FW_CFG_PATH, NULL)); @@ -580,12 +593,6 @@ FWCfgState *fw_cfg_init(uint32_t ctl_port, uint32_t data_port, qdev_init_nofail(dev); - if (ctl_addr) { - sysbus_mmio_map(d, 0, ctl_addr); - } - if (data_addr) { - sysbus_mmio_map(d, 1, data_addr); - } fw_cfg_add_bytes(s, FW_CFG_SIGNATURE, (char *)"QEMU", 4); fw_cfg_add_bytes(s, FW_CFG_UUID, qemu_uuid, 16); fw_cfg_add_i16(s, FW_CFG_NOGRAPHIC, (uint16_t)(display_type == DT_NOGRAPHIC)); @@ -596,48 +603,48 @@ FWCfgState *fw_cfg_init(uint32_t ctl_port, uint32_t data_port, s->machine_ready.notify = fw_cfg_machine_ready; qemu_add_machine_init_done_notifier(&s->machine_ready); - - return s; } -static void fw_cfg_initfn(Object *obj) +FWCfgState *fw_cfg_init_io(uint32_t iobase) { - SysBusDevice *sbd = SYS_BUS_DEVICE(obj); - FWCfgState *s = FW_CFG(obj); + DeviceState *dev; - memory_region_init_io(&s->ctl_iomem, OBJECT(s), &fw_cfg_ctl_mem_ops, s, - "fwcfg.ctl", FW_CFG_SIZE); - sysbus_init_mmio(sbd, &s->ctl_iomem); - memory_region_init_io(&s->data_iomem, OBJECT(s), &fw_cfg_data_mem_ops, s, - "fwcfg.data", FW_CFG_DATA_SIZE); - sysbus_init_mmio(sbd, &s->data_iomem); - /* In case ctl and data overlap: */ - memory_region_init_io(&s->comb_iomem, OBJECT(s), &fw_cfg_comb_mem_ops, s, - "fwcfg", FW_CFG_SIZE); + dev = qdev_create(NULL, TYPE_FW_CFG_IO); + qdev_prop_set_uint32(dev, "iobase", iobase); + fw_cfg_init1(dev); + + return FW_CFG(dev); } -static void fw_cfg_realize(DeviceState *dev, Error **errp) +FWCfgState *fw_cfg_init_mem(hwaddr ctl_addr, hwaddr data_addr) { - FWCfgState *s = FW_CFG(dev); - SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + DeviceState *dev; + SysBusDevice *sbd; - if (s->ctl_iobase + 1 == s->data_iobase) { - sysbus_add_io(sbd, s->ctl_iobase, &s->comb_iomem); - } else { - if (s->ctl_iobase) { - sysbus_add_io(sbd, s->ctl_iobase, &s->ctl_iomem); - } - if (s->data_iobase) { - sysbus_add_io(sbd, s->data_iobase, &s->data_iomem); - } + dev = qdev_create(NULL, TYPE_FW_CFG_MEM); + fw_cfg_init1(dev); + + sbd = SYS_BUS_DEVICE(dev); + sysbus_mmio_map(sbd, 0, ctl_addr); + sysbus_mmio_map(sbd, 1, data_addr); + + return FW_CFG(dev); +} + + +FWCfgState *fw_cfg_init(uint32_t ctl_port, uint32_t data_port, + hwaddr crl_addr, hwaddr data_addr) +{ + if (ctl_port + 1 == data_port && crl_addr == 0 && data_addr == 0) { + return fw_cfg_init_io(ctl_port); } + if (ctl_port == 0 && data_port == 0 && crl_addr != 0 && data_addr != 0) { + return fw_cfg_init_mem(crl_addr, data_addr); + } + assert(false); + return NULL; } -static Property fw_cfg_properties[] = { - DEFINE_PROP_UINT32("ctl_iobase", FWCfgState, ctl_iobase, -1), - DEFINE_PROP_UINT32("data_iobase", FWCfgState, data_iobase, -1), - DEFINE_PROP_END_OF_LIST(), -}; FWCfgState *fw_cfg_find(void) { @@ -648,23 +655,83 @@ static void fw_cfg_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - dc->realize = fw_cfg_realize; dc->reset = fw_cfg_reset; dc->vmsd = &vmstate_fw_cfg; - dc->props = fw_cfg_properties; } static const TypeInfo fw_cfg_info = { .name = TYPE_FW_CFG, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(FWCfgState), - .instance_init = fw_cfg_initfn, .class_init = fw_cfg_class_init, }; + +static Property fw_cfg_io_properties[] = { + DEFINE_PROP_UINT32("iobase", FWCfgIoState, iobase, -1), + DEFINE_PROP_END_OF_LIST(), +}; + +static void fw_cfg_io_realize(DeviceState *dev, Error **errp) +{ + FWCfgIoState *s = FW_CFG_IO(dev); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + + memory_region_init_io(&s->comb_iomem, OBJECT(s), &fw_cfg_comb_mem_ops, + FW_CFG(s), "fwcfg", FW_CFG_SIZE); + sysbus_add_io(sbd, s->iobase, &s->comb_iomem); +} + +static void fw_cfg_io_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = fw_cfg_io_realize; + dc->props = fw_cfg_io_properties; +} + +static const TypeInfo fw_cfg_io_info = { + .name = TYPE_FW_CFG_IO, + .parent = TYPE_FW_CFG, + .instance_size = sizeof(FWCfgIoState), + .class_init = fw_cfg_io_class_init, +}; + + +static void fw_cfg_mem_realize(DeviceState *dev, Error **errp) +{ + FWCfgMemState *s = FW_CFG_MEM(dev); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + + memory_region_init_io(&s->ctl_iomem, OBJECT(s), &fw_cfg_ctl_mem_ops, + FW_CFG(s), "fwcfg.ctl", FW_CFG_SIZE); + sysbus_init_mmio(sbd, &s->ctl_iomem); + + memory_region_init_io(&s->data_iomem, OBJECT(s), &fw_cfg_data_mem_ops, + FW_CFG(s), "fwcfg.data", FW_CFG_DATA_SIZE); + sysbus_init_mmio(sbd, &s->data_iomem); +} + +static void fw_cfg_mem_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = fw_cfg_mem_realize; +} + +static const TypeInfo fw_cfg_mem_info = { + .name = TYPE_FW_CFG_MEM, + .parent = TYPE_FW_CFG, + .instance_size = sizeof(FWCfgMemState), + .class_init = fw_cfg_mem_class_init, +}; + + static void fw_cfg_register_types(void) { type_register_static(&fw_cfg_info); + type_register_static(&fw_cfg_io_info); + type_register_static(&fw_cfg_mem_info); } type_init(fw_cfg_register_types) diff --git a/include/hw/nvram/fw_cfg.h b/include/hw/nvram/fw_cfg.h index 56e1ed7122..fcc88ea59b 100644 --- a/include/hw/nvram/fw_cfg.h +++ b/include/hw/nvram/fw_cfg.h @@ -80,6 +80,8 @@ void *fw_cfg_modify_file(FWCfgState *s, const char *filename, void *data, size_t len); FWCfgState *fw_cfg_init(uint32_t ctl_port, uint32_t data_port, hwaddr crl_addr, hwaddr data_addr); +FWCfgState *fw_cfg_init_io(uint32_t iobase); +FWCfgState *fw_cfg_init_mem(hwaddr ctl_addr, hwaddr data_addr); FWCfgState *fw_cfg_find(void); diff --git a/include/qemu/typedefs.h b/include/qemu/typedefs.h index 57ff47f284..f2bbaaf86a 100644 --- a/include/qemu/typedefs.h +++ b/include/qemu/typedefs.h @@ -22,6 +22,8 @@ typedef struct DisplayState DisplayState; typedef struct DisplaySurface DisplaySurface; typedef struct DriveInfo DriveInfo; typedef struct EventNotifier EventNotifier; +typedef struct FWCfgIoState FWCfgIoState; +typedef struct FWCfgMemState FWCfgMemState; typedef struct FWCfgState FWCfgState; typedef struct HCIInfo HCIInfo; typedef struct I2CBus I2CBus; -- cgit v1.2.3-55-g7522 From 66708822cd3007ae1ec5104d274a861148725e7a Mon Sep 17 00:00:00 2001 From: Laszlo Ersek Date: Mon, 22 Dec 2014 13:11:36 +0100 Subject: fw_cfg: move boards to fw_cfg_init_io() / fw_cfg_init_mem() This allows us to drop the fw_cfg_init() shim and to enforce the possible mappings at compile time. Signed-off-by: Laszlo Ersek Signed-off-by: Paolo Bonzini Message-id: 1419250305-31062-3-git-send-email-pbonzini@redhat.com Signed-off-by: Peter Maydell --- hw/i386/pc.c | 4 ++-- hw/nvram/fw_cfg.c | 14 -------------- hw/ppc/mac_newworld.c | 2 +- hw/ppc/mac_oldworld.c | 2 +- hw/sparc/sun4m.c | 2 +- hw/sparc64/sun4u.c | 2 +- include/hw/nvram/fw_cfg.h | 2 -- 7 files changed, 6 insertions(+), 22 deletions(-) (limited to 'include/hw') diff --git a/hw/i386/pc.c b/hw/i386/pc.c index a3ddb5e139..e07f1fac56 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -649,7 +649,7 @@ static FWCfgState *bochs_bios_init(void) int i, j; unsigned int apic_id_limit = pc_apic_id_limit(max_cpus); - fw_cfg = fw_cfg_init(BIOS_CFG_IOPORT, BIOS_CFG_IOPORT + 1, 0, 0); + fw_cfg = fw_cfg_init_io(BIOS_CFG_IOPORT); /* FW_CFG_MAX_CPUS is a bit confusing/problematic on x86: * * SeaBIOS needs FW_CFG_MAX_CPUS for CPU hotplug, but the CPU hotplug @@ -1170,7 +1170,7 @@ FWCfgState *xen_load_linux(const char *kernel_filename, assert(kernel_filename != NULL); - fw_cfg = fw_cfg_init(BIOS_CFG_IOPORT, BIOS_CFG_IOPORT + 1, 0, 0); + fw_cfg = fw_cfg_init_io(BIOS_CFG_IOPORT); rom_set_fw(fw_cfg); load_linux(fw_cfg, kernel_filename, initrd_filename, diff --git a/hw/nvram/fw_cfg.c b/hw/nvram/fw_cfg.c index ab7bfffae2..c48bc6e650 100644 --- a/hw/nvram/fw_cfg.c +++ b/hw/nvram/fw_cfg.c @@ -632,20 +632,6 @@ FWCfgState *fw_cfg_init_mem(hwaddr ctl_addr, hwaddr data_addr) } -FWCfgState *fw_cfg_init(uint32_t ctl_port, uint32_t data_port, - hwaddr crl_addr, hwaddr data_addr) -{ - if (ctl_port + 1 == data_port && crl_addr == 0 && data_addr == 0) { - return fw_cfg_init_io(ctl_port); - } - if (ctl_port == 0 && data_port == 0 && crl_addr != 0 && data_addr != 0) { - return fw_cfg_init_mem(crl_addr, data_addr); - } - assert(false); - return NULL; -} - - FWCfgState *fw_cfg_find(void) { return FW_CFG(object_resolve_path(FW_CFG_PATH, NULL)); diff --git a/hw/ppc/mac_newworld.c b/hw/ppc/mac_newworld.c index ee1ed8a344..b60a832c0a 100644 --- a/hw/ppc/mac_newworld.c +++ b/hw/ppc/mac_newworld.c @@ -454,7 +454,7 @@ static void ppc_core99_init(MachineState *machine) pmac_format_nvram_partition(nvr, 0x2000); /* No PCI init: the BIOS will do it */ - fw_cfg = fw_cfg_init(0, 0, CFG_ADDR, CFG_ADDR + 2); + fw_cfg = fw_cfg_init_mem(CFG_ADDR, CFG_ADDR + 2); fw_cfg_add_i16(fw_cfg, FW_CFG_MAX_CPUS, (uint16_t)max_cpus); fw_cfg_add_i32(fw_cfg, FW_CFG_ID, 1); fw_cfg_add_i64(fw_cfg, FW_CFG_RAM_SIZE, (uint64_t)ram_size); diff --git a/hw/ppc/mac_oldworld.c b/hw/ppc/mac_oldworld.c index 15109c294a..c7224d70b5 100644 --- a/hw/ppc/mac_oldworld.c +++ b/hw/ppc/mac_oldworld.c @@ -313,7 +313,7 @@ static void ppc_heathrow_init(MachineState *machine) /* No PCI init: the BIOS will do it */ - fw_cfg = fw_cfg_init(0, 0, CFG_ADDR, CFG_ADDR + 2); + fw_cfg = fw_cfg_init_mem(CFG_ADDR, CFG_ADDR + 2); fw_cfg_add_i16(fw_cfg, FW_CFG_MAX_CPUS, (uint16_t)max_cpus); fw_cfg_add_i32(fw_cfg, FW_CFG_ID, 1); fw_cfg_add_i64(fw_cfg, FW_CFG_RAM_SIZE, (uint64_t)ram_size); diff --git a/hw/sparc/sun4m.c b/hw/sparc/sun4m.c index df259ad605..ecd9dc1414 100644 --- a/hw/sparc/sun4m.c +++ b/hw/sparc/sun4m.c @@ -1084,7 +1084,7 @@ static void sun4m_hw_init(const struct sun4m_hwdef *hwdef, ecc_init(hwdef->ecc_base, slavio_irq[28], hwdef->ecc_version); - fw_cfg = fw_cfg_init(0, 0, CFG_ADDR, CFG_ADDR + 2); + fw_cfg = fw_cfg_init_mem(CFG_ADDR, CFG_ADDR + 2); fw_cfg_add_i16(fw_cfg, FW_CFG_MAX_CPUS, (uint16_t)max_cpus); fw_cfg_add_i32(fw_cfg, FW_CFG_ID, 1); fw_cfg_add_i64(fw_cfg, FW_CFG_RAM_SIZE, (uint64_t)ram_size); diff --git a/hw/sparc64/sun4u.c b/hw/sparc64/sun4u.c index acac8f9ed0..3ff5bd8871 100644 --- a/hw/sparc64/sun4u.c +++ b/hw/sparc64/sun4u.c @@ -892,7 +892,7 @@ static void sun4uv_init(MemoryRegion *address_space_mem, graphic_width, graphic_height, graphic_depth, (uint8_t *)&nd_table[0].macaddr); - fw_cfg = fw_cfg_init(BIOS_CFG_IOPORT, BIOS_CFG_IOPORT + 1, 0, 0); + fw_cfg = fw_cfg_init_io(BIOS_CFG_IOPORT); fw_cfg_add_i16(fw_cfg, FW_CFG_MAX_CPUS, (uint16_t)max_cpus); fw_cfg_add_i32(fw_cfg, FW_CFG_ID, 1); fw_cfg_add_i64(fw_cfg, FW_CFG_RAM_SIZE, (uint64_t)ram_size); diff --git a/include/hw/nvram/fw_cfg.h b/include/hw/nvram/fw_cfg.h index fcc88ea59b..a99586edd6 100644 --- a/include/hw/nvram/fw_cfg.h +++ b/include/hw/nvram/fw_cfg.h @@ -78,8 +78,6 @@ void fw_cfg_add_file_callback(FWCfgState *s, const char *filename, void *data, size_t len); void *fw_cfg_modify_file(FWCfgState *s, const char *filename, void *data, size_t len); -FWCfgState *fw_cfg_init(uint32_t ctl_port, uint32_t data_port, - hwaddr crl_addr, hwaddr data_addr); FWCfgState *fw_cfg_init_io(uint32_t iobase); FWCfgState *fw_cfg_init_mem(hwaddr ctl_addr, hwaddr data_addr); -- cgit v1.2.3-55-g7522 From 6c87e3d5967a1d731b5f591a8f0ee6c319c14ca8 Mon Sep 17 00:00:00 2001 From: Laszlo Ersek Date: Mon, 22 Dec 2014 13:11:41 +0100 Subject: fw_cfg_mem: expose the "data_width" property with fw_cfg_init_mem_wide() We rebase fw_cfg_init_mem() to the new function for compatibility with current callers. The behavior of the (big endian) multi-byte data reads is best shown with a qtest session. Here, we are reading the first six bytes of the UUID $ arm-softmmu/qemu-system-arm -M virt -machine accel=qtest \ -qtest stdio -uuid 4600cb32-38ec-4b2f-8acb-81c6ea54f2d8 >>> writew 0x9020008 0x0200 <<< OK >>> readl 0x9020000 <<< OK 0x000000004600cb32 Remember this is big endian. On big endian machines, it is stored directly as 0x46 0x00 0xcb 0x32. On a little endian machine, we have to first swap it, so that it becomes 0x32cb0046. When written to memory, it becomes 0x46 0x00 0xcb 0x32 again. Reading byte-by-byte works too, of course: >>> readb 0x9020000 <<< OK 0x0000000000000038 >>> readb 0x9020000 <<< OK 0x00000000000000ec Here only a single byte is read at a time, so they are read in order similar to the 1-byte data port that is already in PPC and SPARC machines. Signed-off-by: Laszlo Ersek Signed-off-by: Paolo Bonzini Message-id: 1419250305-31062-8-git-send-email-pbonzini@redhat.com Signed-off-by: Peter Maydell --- hw/nvram/fw_cfg.c | 12 +++++++++--- include/hw/nvram/fw_cfg.h | 2 ++ 2 files changed, 11 insertions(+), 3 deletions(-) (limited to 'include/hw') diff --git a/hw/nvram/fw_cfg.c b/hw/nvram/fw_cfg.c index 2950d6874b..fcdf821c31 100644 --- a/hw/nvram/fw_cfg.c +++ b/hw/nvram/fw_cfg.c @@ -663,14 +663,14 @@ FWCfgState *fw_cfg_init_io(uint32_t iobase) return FW_CFG(dev); } -FWCfgState *fw_cfg_init_mem(hwaddr ctl_addr, hwaddr data_addr) +FWCfgState *fw_cfg_init_mem_wide(hwaddr ctl_addr, hwaddr data_addr, + uint32_t data_width) { DeviceState *dev; SysBusDevice *sbd; dev = qdev_create(NULL, TYPE_FW_CFG_MEM); - qdev_prop_set_uint32(dev, "data_width", - fw_cfg_data_mem_ops.valid.max_access_size); + qdev_prop_set_uint32(dev, "data_width", data_width); fw_cfg_init1(dev); @@ -681,6 +681,12 @@ FWCfgState *fw_cfg_init_mem(hwaddr ctl_addr, hwaddr data_addr) return FW_CFG(dev); } +FWCfgState *fw_cfg_init_mem(hwaddr ctl_addr, hwaddr data_addr) +{ + return fw_cfg_init_mem_wide(ctl_addr, data_addr, + fw_cfg_data_mem_ops.valid.max_access_size); +} + FWCfgState *fw_cfg_find(void) { diff --git a/include/hw/nvram/fw_cfg.h b/include/hw/nvram/fw_cfg.h index a99586edd6..6d8a8ac564 100644 --- a/include/hw/nvram/fw_cfg.h +++ b/include/hw/nvram/fw_cfg.h @@ -80,6 +80,8 @@ void *fw_cfg_modify_file(FWCfgState *s, const char *filename, void *data, size_t len); FWCfgState *fw_cfg_init_io(uint32_t iobase); FWCfgState *fw_cfg_init_mem(hwaddr ctl_addr, hwaddr data_addr); +FWCfgState *fw_cfg_init_mem_wide(hwaddr ctl_addr, hwaddr data_addr, + uint32_t data_width); FWCfgState *fw_cfg_find(void); -- cgit v1.2.3-55-g7522 From 7d48a0f7217474899c5f5920b21f4cfdf4efa8d1 Mon Sep 17 00:00:00 2001 From: Laszlo Ersek Date: Mon, 22 Dec 2014 13:11:43 +0100 Subject: hw/loader: split out load_image_gzipped_buffer() In the next patch we'd like to reuse the image decompression facility without installing the output as a ROM at a specific guest-phys address. In addition, expose LOAD_IMAGE_MAX_GUNZIP_BYTES, because that's a straightforward "max_sz" argument for the new load_image_gzipped_buffer(). Signed-off-by: Laszlo Ersek Reviewed-by: Peter Maydell Signed-off-by: Paolo Bonzini Message-id: 1419250305-31062-10-git-send-email-pbonzini@redhat.com Signed-off-by: Peter Maydell --- hw/core/loader.c | 30 +++++++++++++++++++++--------- include/hw/loader.h | 9 +++++++++ 2 files changed, 30 insertions(+), 9 deletions(-) (limited to 'include/hw') diff --git a/hw/core/loader.c b/hw/core/loader.c index 7527fd3036..f2b34da240 100644 --- a/hw/core/loader.c +++ b/hw/core/loader.c @@ -614,14 +614,9 @@ int load_ramdisk(const char *filename, hwaddr addr, uint64_t max_sz) NULL, NULL); } -/* This simply prevents g_malloc in the function below from allocating - * a huge amount of memory, by placing a limit on the maximum - * uncompressed image size that load_image_gzipped will read. - */ -#define LOAD_IMAGE_MAX_GUNZIP_BYTES (256 << 20) - -/* Load a gzip-compressed kernel. */ -int load_image_gzipped(const char *filename, hwaddr addr, uint64_t max_sz) +/* Load a gzip-compressed kernel to a dynamically allocated buffer. */ +int load_image_gzipped_buffer(const char *filename, uint64_t max_sz, + uint8_t **buffer) { uint8_t *compressed_data = NULL; uint8_t *data = NULL; @@ -653,8 +648,11 @@ int load_image_gzipped(const char *filename, hwaddr addr, uint64_t max_sz) goto out; } - rom_add_blob_fixed(filename, data, bytes, addr); + /* trim to actual size and return to caller */ + *buffer = g_realloc(data, bytes); ret = bytes; + /* ownership has been transferred to caller */ + data = NULL; out: g_free(compressed_data); @@ -662,6 +660,20 @@ int load_image_gzipped(const char *filename, hwaddr addr, uint64_t max_sz) return ret; } +/* Load a gzip-compressed kernel. */ +int load_image_gzipped(const char *filename, hwaddr addr, uint64_t max_sz) +{ + int bytes; + uint8_t *data; + + bytes = load_image_gzipped_buffer(filename, max_sz, &data); + if (bytes != -1) { + rom_add_blob_fixed(filename, data, bytes, addr); + g_free(data); + } + return bytes; +} + /* * Functions for reboot-persistent memory regions. * - used for vga bios and option roms. diff --git a/include/hw/loader.h b/include/hw/loader.h index 64816395dc..899762019f 100644 --- a/include/hw/loader.h +++ b/include/hw/loader.h @@ -16,6 +16,15 @@ int load_image(const char *filename, uint8_t *addr); /* deprecated */ ssize_t load_image_size(const char *filename, void *addr, size_t size); int load_image_targphys(const char *filename, hwaddr, uint64_t max_sz); + +/* This is the limit on the maximum uncompressed image size that + * load_image_gzipped_buffer() and load_image_gzipped() will read. It prevents + * g_malloc() in those functions from allocating a huge amount of memory. + */ +#define LOAD_IMAGE_MAX_GUNZIP_BYTES (256 << 20) + +int load_image_gzipped_buffer(const char *filename, uint64_t max_sz, + uint8_t **buffer); int load_image_gzipped(const char *filename, hwaddr addr, uint64_t max_sz); #define ELF_LOAD_FAILED -1 -- cgit v1.2.3-55-g7522 From 07abe45c4814d42f3aca879d7932c5bc90d98bdf Mon Sep 17 00:00:00 2001 From: Laszlo Ersek Date: Mon, 22 Dec 2014 13:11:44 +0100 Subject: hw/arm: pass pristine kernel image to guest firmware over fw_cfg Introduce the new boolean field "arm_boot_info.firmware_loaded". When this field is set, it means that the portion of guest DRAM that the VCPU normally starts to execute, or the pflash chip that the VCPU normally starts to execute, has been populated by board-specific code with full-fledged guest firmware code, before the board calls arm_load_kernel(). Simultaneously, "arm_boot_info.firmware_loaded" guarantees that the board code has set up the global firmware config instance, for arm_load_kernel() to find with fw_cfg_find(). Guest kernel (-kernel) and guest firmware (-bios, -pflash) has always been possible to specify independently on the command line. The following cases should be considered: nr -bios -pflash -kernel description unit#0 -- ------- ------- ------- ------------------------------------------- 1 present present absent Board code rejects this case, -bios and present present present -pflash unit#0 are exclusive. Left intact by this patch. 2 absent absent present Traditional kernel loading, with qemu's minimal board firmware. Left intact by this patch. 3 absent present absent Preexistent case for booting guest firmware present absent absent loaded with -bios or -pflash. Left intact by this patch. 4 absent absent absent Preexistent case for not loading any firmware or kernel up-front. Left intact by this patch. 5 present absent present New case introduced by this patch: kernel absent present present image is passed to externally loaded firmware in unmodified form, using fw_cfg. An easy way to see that this patch doesn't interfere with existing cases is to realize that "info->firmware_loaded" is constant zero at this point. Which makes the "outer" condition unchanged, and the "inner" condition (with the fw_cfg-related code) dead. Signed-off-by: Laszlo Ersek Signed-off-by: Paolo Bonzini Message-id: 1419250305-31062-11-git-send-email-pbonzini@redhat.com Signed-off-by: Peter Maydell --- hw/arm/boot.c | 88 +++++++++++++++++++++++++++++++++++++++++++++++++--- include/hw/arm/arm.h | 5 +++ 2 files changed, 88 insertions(+), 5 deletions(-) (limited to 'include/hw') diff --git a/hw/arm/boot.c b/hw/arm/boot.c index c8d1d4e147..52ebd8be9b 100644 --- a/hw/arm/boot.c +++ b/hw/arm/boot.c @@ -488,6 +488,55 @@ static void do_cpu_reset(void *opaque) } } +/** + * load_image_to_fw_cfg() - Load an image file into an fw_cfg entry identified + * by key. + * @fw_cfg: The firmware config instance to store the data in. + * @size_key: The firmware config key to store the size of the loaded + * data under, with fw_cfg_add_i32(). + * @data_key: The firmware config key to store the loaded data under, + * with fw_cfg_add_bytes(). + * @image_name: The name of the image file to load. If it is NULL, the + * function returns without doing anything. + * @try_decompress: Whether the image should be decompressed (gunzipped) before + * adding it to fw_cfg. If decompression fails, the image is + * loaded as-is. + * + * In case of failure, the function prints an error message to stderr and the + * process exits with status 1. + */ +static void load_image_to_fw_cfg(FWCfgState *fw_cfg, uint16_t size_key, + uint16_t data_key, const char *image_name, + bool try_decompress) +{ + size_t size = -1; + uint8_t *data; + + if (image_name == NULL) { + return; + } + + if (try_decompress) { + size = load_image_gzipped_buffer(image_name, + LOAD_IMAGE_MAX_GUNZIP_BYTES, &data); + } + + if (size == (size_t)-1) { + gchar *contents; + gsize length; + + if (!g_file_get_contents(image_name, &contents, &length, NULL)) { + fprintf(stderr, "failed to load \"%s\"\n", image_name); + exit(1); + } + size = length; + data = (uint8_t *)contents; + } + + fw_cfg_add_i32(fw_cfg, size_key, size); + fw_cfg_add_bytes(fw_cfg, data_key, data, size); +} + void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info) { CPUState *cs; @@ -510,19 +559,48 @@ void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info) } /* Load the kernel. */ - if (!info->kernel_filename) { + if (!info->kernel_filename || info->firmware_loaded) { if (have_dtb(info)) { - /* If we have a device tree blob, but no kernel to supply it to, - * copy it to the base of RAM for a bootloader to pick up. + /* If we have a device tree blob, but no kernel to supply it to (or + * the kernel is supposed to be loaded by the bootloader), copy the + * DTB to the base of RAM for the bootloader to pick up. */ if (load_dtb(info->loader_start, info, 0) < 0) { exit(1); } } - /* If no kernel specified, do nothing; we will start from address 0 - * (typically a boot ROM image) in the same way as hardware. + if (info->kernel_filename) { + FWCfgState *fw_cfg; + bool try_decompressing_kernel; + + fw_cfg = fw_cfg_find(); + try_decompressing_kernel = arm_feature(&cpu->env, + ARM_FEATURE_AARCH64); + + /* Expose the kernel, the command line, and the initrd in fw_cfg. + * We don't process them here at all, it's all left to the + * firmware. + */ + load_image_to_fw_cfg(fw_cfg, + FW_CFG_KERNEL_SIZE, FW_CFG_KERNEL_DATA, + info->kernel_filename, + try_decompressing_kernel); + load_image_to_fw_cfg(fw_cfg, + FW_CFG_INITRD_SIZE, FW_CFG_INITRD_DATA, + info->initrd_filename, false); + + if (info->kernel_cmdline) { + fw_cfg_add_i32(fw_cfg, FW_CFG_CMDLINE_SIZE, + strlen(info->kernel_cmdline) + 1); + fw_cfg_add_string(fw_cfg, FW_CFG_CMDLINE_DATA, + info->kernel_cmdline); + } + } + + /* We will start from address 0 (typically a boot ROM image) in the + * same way as hardware. */ return; } diff --git a/include/hw/arm/arm.h b/include/hw/arm/arm.h index e5a5d8c328..c4bf56d44f 100644 --- a/include/hw/arm/arm.h +++ b/include/hw/arm/arm.h @@ -70,6 +70,11 @@ struct arm_boot_info { hwaddr initrd_start; hwaddr initrd_size; hwaddr entry; + + /* Boot firmware has been loaded, typically at address 0, with -bios or + * -pflash. It also implies that fw_cfg_find() will succeed. + */ + bool firmware_loaded; }; void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info); -- cgit v1.2.3-55-g7522