From 391fb81c167e49d3f53f1806dd5902d13b0aeecd Mon Sep 17 00:00:00 2001 From: Richard W.M. Jones Date: Feb 04 2015 15:54:41 +0000 Subject: Add UEFI support for aarch64. --- diff --git a/0001-fw_cfg-remove-superfluous-blank-line.patch b/0001-fw_cfg-remove-superfluous-blank-line.patch new file mode 100644 index 0000000..08fe377 --- /dev/null +++ b/0001-fw_cfg-remove-superfluous-blank-line.patch @@ -0,0 +1,27 @@ +From f4a88b45ad11265d39f17efdf2f54f6a48043655 Mon Sep 17 00:00:00 2001 +From: Gonglei +Date: Mon, 8 Dec 2014 19:52:41 +0800 +Subject: [PATCH 01/15] fw_cfg: remove superfluous blank line + +Signed-off-by: Gonglei +Signed-off-by: Michael Tokarev +(cherry picked from commit d504fb4cecdb7c6f3b18c4d83296e84b6e970595) +--- + hw/nvram/fw_cfg.c | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/hw/nvram/fw_cfg.c b/hw/nvram/fw_cfg.c +index a7122ee..c4b78ed 100644 +--- a/hw/nvram/fw_cfg.c ++++ b/hw/nvram/fw_cfg.c +@@ -621,7 +621,6 @@ static void fw_cfg_realize(DeviceState *dev, Error **errp) + FWCfgState *s = FW_CFG(dev); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + +- + if (s->ctl_iobase + 1 == s->data_iobase) { + sysbus_add_io(sbd, s->ctl_iobase, &s->comb_iomem); + } else { +-- +2.1.0 + diff --git a/0002-hw-arm-boot-fix-uninitialized-scalar-variable-warnin.patch b/0002-hw-arm-boot-fix-uninitialized-scalar-variable-warnin.patch new file mode 100644 index 0000000..18ae34f --- /dev/null +++ b/0002-hw-arm-boot-fix-uninitialized-scalar-variable-warnin.patch @@ -0,0 +1,45 @@ +From 366ca14a9ae8210540a7e4f6bb034b4f0d1c458d Mon Sep 17 00:00:00 2001 +From: zhanghailiang +Date: Thu, 11 Dec 2014 12:07:53 +0000 +Subject: [PATCH 02/15] hw/arm/boot: fix uninitialized scalar variable warning + reported by coverity + +Coverity reports the 'size' may be used uninitialized, but that can't happen, +because the caller has checked "if (binfo->dtb_filename || binfo->get_dtb)" +before call 'load_dtb'. + +Here we simply remove the 'if (binfo->get_dtb)' to satisfy coverity. + +Signed-off-by: zhanghailiang +Message-id: 1416826240-12368-1-git-send-email-zhang.zhanghailiang@huawei.com +Signed-off-by: Peter Maydell +(cherry picked from commit a554ecb49d0021fd8bb0fd4f2f6be807b3c8b54f) +--- + hw/arm/boot.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/hw/arm/boot.c b/hw/arm/boot.c +index 0014c34..e6a3c5b 100644 +--- a/hw/arm/boot.c ++++ b/hw/arm/boot.c +@@ -329,6 +329,8 @@ static void set_kernel_args_old(const struct arm_boot_info *info) + * Returns: the size of the device tree image on success, + * 0 if the image size exceeds the limit, + * -1 on errors. ++ * ++ * Note: Must not be called unless have_dtb(binfo) is true. + */ + static int load_dtb(hwaddr addr, const struct arm_boot_info *binfo, + hwaddr addr_limit) +@@ -352,7 +354,7 @@ static int load_dtb(hwaddr addr, const struct arm_boot_info *binfo, + goto fail; + } + g_free(filename); +- } else if (binfo->get_dtb) { ++ } else { + fdt = binfo->get_dtb(binfo, &size); + if (!fdt) { + fprintf(stderr, "Board was unable to create a dtb blob\n"); +-- +2.1.0 + diff --git a/0003-Sort-include-qemu-typedefs.h.patch b/0003-Sort-include-qemu-typedefs.h.patch new file mode 100644 index 0000000..decec8b --- /dev/null +++ b/0003-Sort-include-qemu-typedefs.h.patch @@ -0,0 +1,156 @@ +From 7957f6ee42418f6b3de6148279b10faa292b2db2 Mon Sep 17 00:00:00 2001 +From: "Dr. David Alan Gilbert" +Date: Thu, 11 Dec 2014 11:46:36 +0000 +Subject: [PATCH 03/15] Sort include/qemu/typedefs.h +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Mainly to make it less likely to conflict during merges. + +Signed-off-by: Dr. David Alan Gilbert +Reviewed-by: Alex Bennée +Signed-off-by: Michael Tokarev +(cherry picked from commit 64baadc2726ae929660dd0c61a42e8d9f3ba1828) +--- + include/qemu/typedefs.h | 105 +++++++++++++++++++++++------------------------- + 1 file changed, 50 insertions(+), 55 deletions(-) + +diff --git a/include/qemu/typedefs.h b/include/qemu/typedefs.h +index 3475177..57ff47f 100644 +--- a/include/qemu/typedefs.h ++++ b/include/qemu/typedefs.h +@@ -3,80 +3,75 @@ + + /* A load of opaque types so that device init declarations don't have to + pull in all the real definitions. */ +-typedef struct QEMUTimer QEMUTimer; +-typedef struct QEMUTimerListGroup QEMUTimerListGroup; +-typedef struct QEMUFile QEMUFile; +-typedef struct QEMUBH QEMUBH; +- +-typedef struct AioContext AioContext; +- +-typedef struct Visitor Visitor; +- + struct Monitor; +-typedef struct Monitor Monitor; +-typedef struct MigrationParams MigrationParams; +- +-typedef struct Property Property; +-typedef struct PropertyInfo PropertyInfo; +-typedef struct CompatProperty CompatProperty; +-typedef struct DeviceState DeviceState; +-typedef struct BusState BusState; +-typedef struct BusClass BusClass; + ++/* Please keep this list in alphabetical order */ ++typedef struct AdapterInfo AdapterInfo; + typedef struct AddressSpace AddressSpace; +-typedef struct MemoryRegion MemoryRegion; +-typedef struct MemoryRegionSection MemoryRegionSection; +-typedef struct MemoryListener MemoryListener; +- +-typedef struct MemoryMappingList MemoryMappingList; +- +-typedef struct QEMUMachine QEMUMachine; +-typedef struct MachineClass MachineClass; +-typedef struct MachineState MachineState; +-typedef struct NICInfo NICInfo; +-typedef struct HCIInfo HCIInfo; ++typedef struct AioContext AioContext; + typedef struct AudioState AudioState; + typedef struct BlockBackend BlockBackend; + typedef struct BlockDriverState BlockDriverState; +-typedef struct DriveInfo DriveInfo; +-typedef struct DisplayState DisplayState; ++typedef struct BusClass BusClass; ++typedef struct BusState BusState; ++typedef struct CharDriverState CharDriverState; ++typedef struct CompatProperty CompatProperty; ++typedef struct DeviceState DeviceState; + typedef struct DisplayChangeListener DisplayChangeListener; ++typedef struct DisplayState DisplayState; + typedef struct DisplaySurface DisplaySurface; +-typedef struct PixelFormat PixelFormat; +-typedef struct QemuConsole QemuConsole; +-typedef struct CharDriverState CharDriverState; +-typedef struct MACAddr MACAddr; +-typedef struct NetClientState NetClientState; ++typedef struct DriveInfo DriveInfo; ++typedef struct EventNotifier EventNotifier; ++typedef struct FWCfgState FWCfgState; ++typedef struct HCIInfo HCIInfo; + typedef struct I2CBus I2CBus; ++typedef struct I2SCodec I2SCodec; + typedef struct ISABus ISABus; + typedef struct ISADevice ISADevice; +-typedef struct SMBusDevice SMBusDevice; +-typedef struct PCIHostState PCIHostState; +-typedef struct PCIExpressHost PCIExpressHost; ++typedef struct MACAddr MACAddr; ++typedef struct MachineClass MachineClass; ++typedef struct MachineState MachineState; ++typedef struct MemoryListener MemoryListener; ++typedef struct MemoryMappingList MemoryMappingList; ++typedef struct MemoryRegion MemoryRegion; ++typedef struct MemoryRegionSection MemoryRegionSection; ++typedef struct MigrationParams MigrationParams; ++typedef struct Monitor Monitor; ++typedef struct MouseTransformInfo MouseTransformInfo; ++typedef struct MSIMessage MSIMessage; ++typedef struct NetClientState NetClientState; ++typedef struct NICInfo NICInfo; ++typedef struct PcGuestInfo PcGuestInfo; ++typedef struct PCIBridge PCIBridge; + typedef struct PCIBus PCIBus; + typedef struct PCIDevice PCIDevice; +-typedef struct PCIExpressDevice PCIExpressDevice; +-typedef struct PCIBridge PCIBridge; +-typedef struct PCIEAERMsg PCIEAERMsg; +-typedef struct PCIEAERLog PCIEAERLog; + typedef struct PCIEAERErr PCIEAERErr; ++typedef struct PCIEAERLog PCIEAERLog; ++typedef struct PCIEAERMsg PCIEAERMsg; + typedef struct PCIEPort PCIEPort; + typedef struct PCIESlot PCIESlot; +-typedef struct MSIMessage MSIMessage; +-typedef struct SerialState SerialState; ++typedef struct PCIExpressDevice PCIExpressDevice; ++typedef struct PCIExpressHost PCIExpressHost; ++typedef struct PCIHostState PCIHostState; + typedef struct PCMCIACardState PCMCIACardState; +-typedef struct MouseTransformInfo MouseTransformInfo; +-typedef struct uWireSlave uWireSlave; +-typedef struct I2SCodec I2SCodec; +-typedef struct SSIBus SSIBus; +-typedef struct EventNotifier EventNotifier; +-typedef struct VirtIODevice VirtIODevice; ++typedef struct PixelFormat PixelFormat; ++typedef struct PropertyInfo PropertyInfo; ++typedef struct Property Property; ++typedef struct QEMUBH QEMUBH; ++typedef struct QemuConsole QemuConsole; ++typedef struct QEMUFile QEMUFile; ++typedef struct QEMUMachine QEMUMachine; + typedef struct QEMUSGList QEMUSGList; + typedef struct QEMUSizedBuffer QEMUSizedBuffer; +-typedef struct SHPCDevice SHPCDevice; +-typedef struct FWCfgState FWCfgState; +-typedef struct PcGuestInfo PcGuestInfo; ++typedef struct QEMUTimerListGroup QEMUTimerListGroup; ++typedef struct QEMUTimer QEMUTimer; + typedef struct Range Range; +-typedef struct AdapterInfo AdapterInfo; ++typedef struct SerialState SerialState; ++typedef struct SHPCDevice SHPCDevice; ++typedef struct SMBusDevice SMBusDevice; ++typedef struct SSIBus SSIBus; ++typedef struct uWireSlave uWireSlave; ++typedef struct VirtIODevice VirtIODevice; ++typedef struct Visitor Visitor; + + #endif /* QEMU_TYPEDEFS_H */ +-- +2.1.0 + diff --git a/0004-fw_cfg-hard-separation-between-the-MMIO-and-I-O-port.patch b/0004-fw_cfg-hard-separation-between-the-MMIO-and-I-O-port.patch new file mode 100644 index 0000000..bf75556 --- /dev/null +++ b/0004-fw_cfg-hard-separation-between-the-MMIO-and-I-O-port.patch @@ -0,0 +1,339 @@ +From e91399538669c6480ace3eb0f63b1c99645924bc Mon Sep 17 00:00:00 2001 +From: Laszlo Ersek +Date: Mon, 22 Dec 2014 13:11:35 +0100 +Subject: [PATCH 04/15] 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 +(cherry picked from commit 5712db6ae5101db645f71edc393368cd59bfd314) +--- + hw/nvram/fw_cfg.c | 181 +++++++++++++++++++++++++++++++--------------- + include/hw/nvram/fw_cfg.h | 2 + + include/qemu/typedefs.h | 2 + + 3 files changed, 128 insertions(+), 57 deletions(-) + +diff --git a/hw/nvram/fw_cfg.c b/hw/nvram/fw_cfg.c +index c4b78ed..ab7bfff 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) ++ ++ ++static void fw_cfg_init1(DeviceState *dev) + { +- 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); ++ 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); ++} ++ ++FWCfgState *fw_cfg_init_io(uint32_t iobase) ++{ ++ DeviceState *dev; + +- return s; ++ 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_initfn(Object *obj) ++FWCfgState *fw_cfg_init_mem(hwaddr ctl_addr, hwaddr data_addr) + { +- SysBusDevice *sbd = SYS_BUS_DEVICE(obj); +- FWCfgState *s = FW_CFG(obj); +- +- 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); ++ DeviceState *dev; ++ SysBusDevice *sbd; ++ ++ 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); + } + +-static void fw_cfg_realize(DeviceState *dev, Error **errp) ++ ++FWCfgState *fw_cfg_init(uint32_t ctl_port, uint32_t data_port, ++ hwaddr crl_addr, hwaddr data_addr) + { +- FWCfgState *s = FW_CFG(dev); +- SysBusDevice *sbd = SYS_BUS_DEVICE(dev); +- +- 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); +- } ++ 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 56e1ed7..fcc88ea 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 57ff47f..f2bbaaf 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; +-- +2.1.0 + diff --git a/0005-fw_cfg-move-boards-to-fw_cfg_init_io-fw_cfg_init_mem.patch b/0005-fw_cfg-move-boards-to-fw_cfg_init_io-fw_cfg_init_mem.patch new file mode 100644 index 0000000..8e78375 --- /dev/null +++ b/0005-fw_cfg-move-boards-to-fw_cfg_init_io-fw_cfg_init_mem.patch @@ -0,0 +1,139 @@ +From 6b9b5f3220b48bab7340fa7cae6ee021280fa9f1 Mon Sep 17 00:00:00 2001 +From: Laszlo Ersek +Date: Mon, 22 Dec 2014 13:11:36 +0100 +Subject: [PATCH 05/15] 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 +(cherry picked from commit 66708822cd3007ae1ec5104d274a861148725e7a) +--- + 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(-) + +diff --git a/hw/i386/pc.c b/hw/i386/pc.c +index f31d55e..f60965f 100644 +--- a/hw/i386/pc.c ++++ b/hw/i386/pc.c +@@ -648,7 +648,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 +@@ -1169,7 +1169,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 ab7bfff..c48bc6e 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 89aee71..5dac389 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 32c21a4..41fefb7 100644 +--- a/hw/ppc/mac_oldworld.c ++++ b/hw/ppc/mac_oldworld.c +@@ -314,7 +314,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 8273199..a12d3c4 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 f42112c..49fb678 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 fcc88ea..a99586e 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); + +-- +2.1.0 + diff --git a/0006-fw_cfg_mem-max-access-size-and-region-size-are-the-s.patch b/0006-fw_cfg_mem-max-access-size-and-region-size-are-the-s.patch new file mode 100644 index 0000000..5b293b7 --- /dev/null +++ b/0006-fw_cfg_mem-max-access-size-and-region-size-are-the-s.patch @@ -0,0 +1,47 @@ +From dfcc3acd2510e5adc3dbb67c83a131bf5fb017ec Mon Sep 17 00:00:00 2001 +From: Laszlo Ersek +Date: Mon, 22 Dec 2014 13:11:37 +0100 +Subject: [PATCH 06/15] fw_cfg_mem: max access size and region size are the + same for data register + +Make it clear that the maximum access size to the MMIO data register +determines the full size of the memory region. + +Currently the max access size is 1. + +This patch doesn't change behavior. + +Signed-off-by: Laszlo Ersek +Signed-off-by: Paolo Bonzini +Message-id: 1419250305-31062-4-git-send-email-pbonzini@redhat.com +Signed-off-by: Peter Maydell +(cherry picked from commit 86099db3823fec1800225f89544014a98b227ed9) +--- + hw/nvram/fw_cfg.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/hw/nvram/fw_cfg.c b/hw/nvram/fw_cfg.c +index c48bc6e..8deb860 100644 +--- a/hw/nvram/fw_cfg.c ++++ b/hw/nvram/fw_cfg.c +@@ -31,7 +31,6 @@ + #include "qemu/config-file.h" + + #define FW_CFG_SIZE 2 +-#define FW_CFG_DATA_SIZE 1 + #define FW_CFG_NAME "fw_cfg" + #define FW_CFG_PATH "/machine/" FW_CFG_NAME + +@@ -694,7 +693,8 @@ static void fw_cfg_mem_realize(DeviceState *dev, Error **errp) + 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); ++ FW_CFG(s), "fwcfg.data", ++ fw_cfg_data_mem_ops.valid.max_access_size); + sysbus_init_mmio(sbd, &s->data_iomem); + } + +-- +2.1.0 + diff --git a/0007-fw_cfg_mem-flip-ctl_mem_ops-and-data_mem_ops-to-DEVI.patch b/0007-fw_cfg_mem-flip-ctl_mem_ops-and-data_mem_ops-to-DEVI.patch new file mode 100644 index 0000000..2ea1fbb --- /dev/null +++ b/0007-fw_cfg_mem-flip-ctl_mem_ops-and-data_mem_ops-to-DEVI.patch @@ -0,0 +1,56 @@ +From 172d29b5a320c8d656072db24e94ea21d1a5ed75 Mon Sep 17 00:00:00 2001 +From: Laszlo Ersek +Date: Mon, 22 Dec 2014 13:11:38 +0100 +Subject: [PATCH 07/15] fw_cfg_mem: flip ctl_mem_ops and data_mem_ops to + DEVICE_BIG_ENDIAN + +The standalone selector port (fw_cfg_ctl_mem_ops) is only used by big +endian guests to date (*), hence this change doesn't regress them. Paolo +and Alex have suggested / requested an explicit DEVICE_BIG_ENDIAN setting +here, for clarity. + +(*) git grep -l fw_cfg_init_mem + + hw/nvram/fw_cfg.c + hw/ppc/mac_newworld.c + hw/ppc/mac_oldworld.c + hw/sparc/sun4m.c + include/hw/nvram/fw_cfg.h + +The standalone data port (fw_cfg_data_mem_ops) has max_access_size 1 (for +now), hence changing its endianness doesn't change behavior for existing +guest code. + +Signed-off-by: Laszlo Ersek +Signed-off-by: Paolo Bonzini +Message-id: 1419250305-31062-5-git-send-email-pbonzini@redhat.com +Signed-off-by: Peter Maydell +(cherry picked from commit d789c84547abaaf82828b20998aee618b9327261) +--- + hw/nvram/fw_cfg.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/hw/nvram/fw_cfg.c b/hw/nvram/fw_cfg.c +index 8deb860..910ae14 100644 +--- a/hw/nvram/fw_cfg.c ++++ b/hw/nvram/fw_cfg.c +@@ -332,14 +332,14 @@ static bool fw_cfg_comb_valid(void *opaque, hwaddr addr, + + static const MemoryRegionOps fw_cfg_ctl_mem_ops = { + .write = fw_cfg_ctl_mem_write, +- .endianness = DEVICE_NATIVE_ENDIAN, ++ .endianness = DEVICE_BIG_ENDIAN, + .valid.accepts = fw_cfg_ctl_mem_valid, + }; + + static const MemoryRegionOps fw_cfg_data_mem_ops = { + .read = fw_cfg_data_mem_read, + .write = fw_cfg_data_mem_write, +- .endianness = DEVICE_NATIVE_ENDIAN, ++ .endianness = DEVICE_BIG_ENDIAN, + .valid = { + .min_access_size = 1, + .max_access_size = 1, +-- +2.1.0 + diff --git a/0008-exec-allows-8-byte-accesses-in-subpage_ops.patch b/0008-exec-allows-8-byte-accesses-in-subpage_ops.patch new file mode 100644 index 0000000..62e0447 --- /dev/null +++ b/0008-exec-allows-8-byte-accesses-in-subpage_ops.patch @@ -0,0 +1,73 @@ +From cdea522fe22d3996228c1b5a80b8d6e959da91aa Mon Sep 17 00:00:00 2001 +From: Paolo Bonzini +Date: Mon, 22 Dec 2014 13:11:39 +0100 +Subject: [PATCH 08/15] exec: allows 8-byte accesses in subpage_ops + +Otherwise fw_cfg accesses are split into 4-byte ones before they reach the +fw_cfg ops / handlers. + +Signed-off-by: Paolo Bonzini +Signed-off-by: Laszlo Ersek +Signed-off-by: Paolo Bonzini +Message-id: 1419250305-31062-6-git-send-email-pbonzini@redhat.com +Signed-off-by: Peter Maydell +(cherry picked from commit ff6cff7554be06e95f8d712f66cd16bd6681c746) +--- + exec.c | 13 +++++++++++-- + 1 file changed, 11 insertions(+), 2 deletions(-) + +diff --git a/exec.c b/exec.c +index 71ac104..dee9543 100644 +--- a/exec.c ++++ b/exec.c +@@ -1768,7 +1768,7 @@ static uint64_t subpage_read(void *opaque, hwaddr addr, + unsigned len) + { + subpage_t *subpage = opaque; +- uint8_t buf[4]; ++ uint8_t buf[8]; + + #if defined(DEBUG_SUBPAGE) + printf("%s: subpage %p len %u addr " TARGET_FMT_plx "\n", __func__, +@@ -1782,6 +1782,8 @@ static uint64_t subpage_read(void *opaque, hwaddr addr, + return lduw_p(buf); + case 4: + return ldl_p(buf); ++ case 8: ++ return ldq_p(buf); + default: + abort(); + } +@@ -1791,7 +1793,7 @@ static void subpage_write(void *opaque, hwaddr addr, + uint64_t value, unsigned len) + { + subpage_t *subpage = opaque; +- uint8_t buf[4]; ++ uint8_t buf[8]; + + #if defined(DEBUG_SUBPAGE) + printf("%s: subpage %p len %u addr " TARGET_FMT_plx +@@ -1808,6 +1810,9 @@ static void subpage_write(void *opaque, hwaddr addr, + case 4: + stl_p(buf, value); + break; ++ case 8: ++ stq_p(buf, value); ++ break; + default: + abort(); + } +@@ -1830,6 +1835,10 @@ static bool subpage_accepts(void *opaque, hwaddr addr, + static const MemoryRegionOps subpage_ops = { + .read = subpage_read, + .write = subpage_write, ++ .impl.min_access_size = 1, ++ .impl.max_access_size = 8, ++ .valid.min_access_size = 1, ++ .valid.max_access_size = 8, + .valid.accepts = subpage_accepts, + .endianness = DEVICE_NATIVE_ENDIAN, + }; +-- +2.1.0 + diff --git a/0009-fw_cfg_mem-introduce-the-data_width-property.patch b/0009-fw_cfg_mem-introduce-the-data_width-property.patch new file mode 100644 index 0000000..c496881 --- /dev/null +++ b/0009-fw_cfg_mem-introduce-the-data_width-property.patch @@ -0,0 +1,168 @@ +From 5e7e3b47666b777f3bf44cd91d87130f8ca1a7c1 Mon Sep 17 00:00:00 2001 +From: Laszlo Ersek +Date: Mon, 22 Dec 2014 13:11:40 +0100 +Subject: [PATCH 09/15] fw_cfg_mem: introduce the "data_width" property + +The "data_width" property is capable of changing the maximum valid access +size to the MMIO data register, and resizes the memory region similarly, +at device realization time. + +The default value of "data_memwidth" is set so that we don't yet diverge +from "fw_cfg_data_mem_ops". + +Most of the fw_cfg_mem users will stick with the default, and for them we +should continue using the statically allocated "fw_cfg_data_mem_ops". This +is beneficial for debugging because gdb can resolve pointers referencing +static objects to the names of those objects. + +Signed-off-by: Laszlo Ersek +Signed-off-by: Paolo Bonzini +Message-id: 1419250305-31062-7-git-send-email-pbonzini@redhat.com +Signed-off-by: Peter Maydell +(cherry picked from commit cfaadf0e89e7c2a47462d5f96390c9a9b4de037c) +--- + hw/nvram/fw_cfg.c | 79 +++++++++++++++++++++++++++++++++++++++++++++++++++---- + 1 file changed, 74 insertions(+), 5 deletions(-) + +diff --git a/hw/nvram/fw_cfg.c b/hw/nvram/fw_cfg.c +index 910ae14..2950d68 100644 +--- a/hw/nvram/fw_cfg.c ++++ b/hw/nvram/fw_cfg.c +@@ -77,6 +77,8 @@ struct FWCfgMemState { + /*< public >*/ + + MemoryRegion ctl_iomem, data_iomem; ++ uint32_t data_width; ++ MemoryRegionOps wide_data_ops; + }; + + #define JPG_FILE 0 +@@ -284,13 +286,58 @@ static uint8_t fw_cfg_read(FWCfgState *s) + static uint64_t fw_cfg_data_mem_read(void *opaque, hwaddr addr, + unsigned size) + { +- return fw_cfg_read(opaque); ++ FWCfgState *s = opaque; ++ uint8_t buf[8]; ++ unsigned i; ++ ++ for (i = 0; i < size; ++i) { ++ buf[i] = fw_cfg_read(s); ++ } ++ switch (size) { ++ case 1: ++ return buf[0]; ++ case 2: ++ return lduw_he_p(buf); ++ case 4: ++ return (uint32_t)ldl_he_p(buf); ++ case 8: ++ return ldq_he_p(buf); ++ } ++ abort(); + } + + static void fw_cfg_data_mem_write(void *opaque, hwaddr addr, + uint64_t value, unsigned size) + { +- fw_cfg_write(opaque, (uint8_t)value); ++ FWCfgState *s = opaque; ++ uint8_t buf[8]; ++ unsigned i; ++ ++ switch (size) { ++ case 1: ++ buf[0] = value; ++ break; ++ case 2: ++ stw_he_p(buf, value); ++ break; ++ case 4: ++ stl_he_p(buf, value); ++ break; ++ case 8: ++ stq_he_p(buf, value); ++ break; ++ default: ++ abort(); ++ } ++ for (i = 0; i < size; ++i) { ++ fw_cfg_write(s, buf[i]); ++ } ++} ++ ++static bool fw_cfg_data_mem_valid(void *opaque, hwaddr addr, ++ unsigned size, bool is_write) ++{ ++ return addr == 0; + } + + static void fw_cfg_ctl_mem_write(void *opaque, hwaddr addr, +@@ -343,6 +390,7 @@ static const MemoryRegionOps fw_cfg_data_mem_ops = { + .valid = { + .min_access_size = 1, + .max_access_size = 1, ++ .accepts = fw_cfg_data_mem_valid, + }, + }; + +@@ -621,6 +669,9 @@ FWCfgState *fw_cfg_init_mem(hwaddr ctl_addr, hwaddr data_addr) + 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); ++ + fw_cfg_init1(dev); + + sbd = SYS_BUS_DEVICE(dev); +@@ -683,18 +734,35 @@ static const TypeInfo fw_cfg_io_info = { + }; + + ++static Property fw_cfg_mem_properties[] = { ++ DEFINE_PROP_UINT32("data_width", FWCfgMemState, data_width, -1), ++ DEFINE_PROP_END_OF_LIST(), ++}; ++ + static void fw_cfg_mem_realize(DeviceState *dev, Error **errp) + { + FWCfgMemState *s = FW_CFG_MEM(dev); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); ++ const MemoryRegionOps *data_ops = &fw_cfg_data_mem_ops; + + 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_mem_ops.valid.max_access_size); ++ if (s->data_width > data_ops->valid.max_access_size) { ++ /* memberwise copy because the "old_mmio" member is const */ ++ s->wide_data_ops.read = data_ops->read; ++ s->wide_data_ops.write = data_ops->write; ++ s->wide_data_ops.endianness = data_ops->endianness; ++ s->wide_data_ops.valid = data_ops->valid; ++ s->wide_data_ops.impl = data_ops->impl; ++ ++ s->wide_data_ops.valid.max_access_size = s->data_width; ++ s->wide_data_ops.impl.max_access_size = s->data_width; ++ data_ops = &s->wide_data_ops; ++ } ++ memory_region_init_io(&s->data_iomem, OBJECT(s), data_ops, FW_CFG(s), ++ "fwcfg.data", data_ops->valid.max_access_size); + sysbus_init_mmio(sbd, &s->data_iomem); + } + +@@ -703,6 +771,7 @@ static void fw_cfg_mem_class_init(ObjectClass *klass, void *data) + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = fw_cfg_mem_realize; ++ dc->props = fw_cfg_mem_properties; + } + + static const TypeInfo fw_cfg_mem_info = { +-- +2.1.0 + diff --git a/0010-fw_cfg_mem-expose-the-data_width-property-with-fw_cf.patch b/0010-fw_cfg_mem-expose-the-data_width-property-with-fw_cf.patch new file mode 100644 index 0000000..e44a588 --- /dev/null +++ b/0010-fw_cfg_mem-expose-the-data_width-property-with-fw_cf.patch @@ -0,0 +1,99 @@ +From 870ed93bb6c35e821031bbc3a3a297d0e60c67ab Mon Sep 17 00:00:00 2001 +From: Laszlo Ersek +Date: Mon, 22 Dec 2014 13:11:41 +0100 +Subject: [PATCH 10/15] 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 +(cherry picked from commit 6c87e3d5967a1d731b5f591a8f0ee6c319c14ca8) +--- + hw/nvram/fw_cfg.c | 12 +++++++++--- + include/hw/nvram/fw_cfg.h | 2 ++ + 2 files changed, 11 insertions(+), 3 deletions(-) + +diff --git a/hw/nvram/fw_cfg.c b/hw/nvram/fw_cfg.c +index 2950d68..fcdf821 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 a99586e..6d8a8ac 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); + +-- +2.1.0 + diff --git a/0011-arm-add-fw_cfg-to-virt-board.patch b/0011-arm-add-fw_cfg-to-virt-board.patch new file mode 100644 index 0000000..99c3535 --- /dev/null +++ b/0011-arm-add-fw_cfg-to-virt-board.patch @@ -0,0 +1,89 @@ +From 892602338db279204d11212034bc9f359dc4c1c4 Mon Sep 17 00:00:00 2001 +From: Laszlo Ersek +Date: Mon, 22 Dec 2014 13:11:42 +0100 +Subject: [PATCH 11/15] arm: add fw_cfg to "virt" board + +fw_cfg already supports exposure over MMIO (used in ppc/mac_newworld.c, +ppc/mac_oldworld.c, sparc/sun4m.c); we can easily add it to the "virt" +board. + +Because MMIO access is slow on ARM KVM, we enable the guest, with +fw_cfg_init_mem_wide(), to transfer up to 8 bytes with a single access. +This has been measured to speed up transfers up to 7.5-fold, relative to +single byte data access, on both ARM KVM and x86_64 TCG. + +The MMIO register block of fw_cfg is advertized in the device tree. As +base address we pick 0x09020000, which conforms to the comment preceding +"a15memmap": it falls in the miscellaneous device I/O range 128MB..256MB, +and it is aligned at 64KB. The DTB properties follow the documentation in +the Linux source file "Documentation/devicetree/bindings/arm/fw-cfg.txt". + +fw_cfg automatically exports a number of files to the guest; for example, +"bootorder" (see fw_cfg_machine_reset()). + +Signed-off-by: Laszlo Ersek +Reviewed-by: Peter Maydell +Signed-off-by: Paolo Bonzini +Message-id: 1419250305-31062-9-git-send-email-pbonzini@redhat.com +Signed-off-by: Peter Maydell +(cherry picked from commit 578f3c7b083514b4fec0bf8fa0617934cdbdf826) +--- + hw/arm/virt.c | 21 +++++++++++++++++++++ + 1 file changed, 21 insertions(+) + +diff --git a/hw/arm/virt.c b/hw/arm/virt.c +index 314e55b..8af4aa0 100644 +--- a/hw/arm/virt.c ++++ b/hw/arm/virt.c +@@ -68,6 +68,7 @@ enum { + VIRT_UART, + VIRT_MMIO, + VIRT_RTC, ++ VIRT_FW_CFG, + }; + + typedef struct MemMapEntry { +@@ -107,6 +108,7 @@ static const MemMapEntry a15memmap[] = { + [VIRT_GIC_CPU] = { 0x08010000, 0x00010000 }, + [VIRT_UART] = { 0x09000000, 0x00001000 }, + [VIRT_RTC] = { 0x09010000, 0x00001000 }, ++ [VIRT_FW_CFG] = { 0x09020000, 0x0000000a }, + [VIRT_MMIO] = { 0x0a000000, 0x00000200 }, + /* ...repeating for a total of NUM_VIRTIO_TRANSPORTS, each of that size */ + /* 0x10000000 .. 0x40000000 reserved for PCI */ +@@ -519,6 +521,23 @@ static void create_flash(const VirtBoardInfo *vbi) + g_free(nodename); + } + ++static void create_fw_cfg(const VirtBoardInfo *vbi) ++{ ++ hwaddr base = vbi->memmap[VIRT_FW_CFG].base; ++ hwaddr size = vbi->memmap[VIRT_FW_CFG].size; ++ char *nodename; ++ ++ fw_cfg_init_mem_wide(base + 8, base, 8); ++ ++ nodename = g_strdup_printf("/fw-cfg@%" PRIx64, base); ++ qemu_fdt_add_subnode(vbi->fdt, nodename); ++ qemu_fdt_setprop_string(vbi->fdt, nodename, ++ "compatible", "qemu,fw-cfg-mmio"); ++ qemu_fdt_setprop_sized_cells(vbi->fdt, nodename, "reg", ++ 2, base, 2, size); ++ g_free(nodename); ++} ++ + static void *machvirt_dtb(const struct arm_boot_info *binfo, int *fdt_size) + { + const VirtBoardInfo *board = (const VirtBoardInfo *)binfo; +@@ -604,6 +623,8 @@ static void machvirt_init(MachineState *machine) + */ + create_virtio_devices(vbi, pic); + ++ create_fw_cfg(vbi); ++ + vbi->bootinfo.ram_size = machine->ram_size; + vbi->bootinfo.kernel_filename = machine->kernel_filename; + vbi->bootinfo.kernel_cmdline = machine->kernel_cmdline; +-- +2.1.0 + diff --git a/0012-hw-loader-split-out-load_image_gzipped_buffer.patch b/0012-hw-loader-split-out-load_image_gzipped_buffer.patch new file mode 100644 index 0000000..fd54ee3 --- /dev/null +++ b/0012-hw-loader-split-out-load_image_gzipped_buffer.patch @@ -0,0 +1,101 @@ +From 29736faa92d5e4b4242786ee583ce339263d6adb Mon Sep 17 00:00:00 2001 +From: Laszlo Ersek +Date: Mon, 22 Dec 2014 13:11:43 +0100 +Subject: [PATCH 12/15] 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 +(cherry picked from commit 7d48a0f7217474899c5f5920b21f4cfdf4efa8d1) +--- + hw/core/loader.c | 30 +++++++++++++++++++++--------- + include/hw/loader.h | 9 +++++++++ + 2 files changed, 30 insertions(+), 9 deletions(-) + +diff --git a/hw/core/loader.c b/hw/core/loader.c +index 7527fd3..f2b34da 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 6481639..8997620 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 +-- +2.1.0 + diff --git a/0013-hw-arm-pass-pristine-kernel-image-to-guest-firmware-.patch b/0013-hw-arm-pass-pristine-kernel-image-to-guest-firmware-.patch new file mode 100644 index 0000000..59fcafc --- /dev/null +++ b/0013-hw-arm-pass-pristine-kernel-image-to-guest-firmware-.patch @@ -0,0 +1,192 @@ +From a4510adeb278b6781b16a5cc61cc5b7f00612130 Mon Sep 17 00:00:00 2001 +From: Laszlo Ersek +Date: Mon, 22 Dec 2014 13:11:44 +0100 +Subject: [PATCH 13/15] 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 +(cherry picked from commit 07abe45c4814d42f3aca879d7932c5bc90d98bdf) +--- + hw/arm/boot.c | 88 +++++++++++++++++++++++++++++++++++++++++++++++++--- + include/hw/arm/arm.h | 5 +++ + 2 files changed, 88 insertions(+), 5 deletions(-) + +diff --git a/hw/arm/boot.c b/hw/arm/boot.c +index e6a3c5b..17bdaee 100644 +--- a/hw/arm/boot.c ++++ b/hw/arm/boot.c +@@ -478,6 +478,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; +@@ -500,19 +549,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 cefc9e6..dd69d66 100644 +--- a/include/hw/arm/arm.h ++++ b/include/hw/arm/arm.h +@@ -66,6 +66,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); + +-- +2.1.0 + diff --git a/0014-hw-arm-virt-enable-passing-of-EFI-stubbed-kernel-to-.patch b/0014-hw-arm-virt-enable-passing-of-EFI-stubbed-kernel-to-.patch new file mode 100644 index 0000000..7ebc7c8 --- /dev/null +++ b/0014-hw-arm-virt-enable-passing-of-EFI-stubbed-kernel-to-.patch @@ -0,0 +1,36 @@ +From 6eaafa0eea15df4439488b535f4d53216e720857 Mon Sep 17 00:00:00 2001 +From: Laszlo Ersek +Date: Mon, 22 Dec 2014 13:11:45 +0100 +Subject: [PATCH 14/15] hw/arm/virt: enable passing of EFI-stubbed kernel to + guest UEFI firmware + +The virt board already ensures mutual exclusion between -bios and -pflash +unit#0; we only need to set "bootinfo.firmware_loaded", introduced in the +previous patch, if either of those options was used to load the guest +firmware. + +Signed-off-by: Laszlo Ersek +Reviewed-by: Peter Maydell +Signed-off-by: Paolo Bonzini +Message-id: 1419250305-31062-12-git-send-email-pbonzini@redhat.com +Signed-off-by: Peter Maydell +(cherry picked from commit aa351061dbb0e3054db11c00a69395785c4186c8) +--- + hw/arm/virt.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/hw/arm/virt.c b/hw/arm/virt.c +index 8af4aa0..29fbdc1 100644 +--- a/hw/arm/virt.c ++++ b/hw/arm/virt.c +@@ -633,6 +633,7 @@ static void machvirt_init(MachineState *machine) + vbi->bootinfo.board_id = -1; + vbi->bootinfo.loader_start = vbi->memmap[VIRT_MEM].base; + vbi->bootinfo.get_dtb = machvirt_dtb; ++ vbi->bootinfo.firmware_loaded = bios_name || drive_get(IF_PFLASH, 0, 0); + arm_load_kernel(ARM_CPU(first_cpu), &vbi->bootinfo); + } + +-- +2.1.0 + diff --git a/0015-fw_cfg-fix-endianness-in-fw_cfg_data_mem_read-_write.patch b/0015-fw_cfg-fix-endianness-in-fw_cfg_data_mem_read-_write.patch new file mode 100644 index 0000000..0c92900 --- /dev/null +++ b/0015-fw_cfg-fix-endianness-in-fw_cfg_data_mem_read-_write.patch @@ -0,0 +1,176 @@ +From b627b2d476b3677e35d06bdc9fac26678fb92484 Mon Sep 17 00:00:00 2001 +From: Laszlo Ersek +Date: Fri, 16 Jan 2015 11:54:30 +0000 +Subject: [PATCH 15/15] fw_cfg: fix endianness in fw_cfg_data_mem_read() / + _write() + +(1) Let's contemplate what device endianness means, for a memory mapped +device register (independently of QEMU -- that is, on physical hardware). + +It determines the byte order that the device will put on the data bus when +the device is producing a *numerical value* for the CPU. This byte order +may differ from the CPU's own byte order, therefore when software wants to +consume the *numerical value*, it may have to swap the byte order first. + +For example, suppose we have a device that exposes in a 2-byte register +the number of sheep we have to count before falling asleep. If the value +is decimal 37 (0x0025), then a big endian register will produce [0x00, +0x25], while a little endian register will produce [0x25, 0x00]. + +If the device register is big endian, but the CPU is little endian, the +numerical value will read as 0x2500 (decimal 9472), which software has to +byte swap before use. + +However... if we ask the device about who stole our herd of sheep, and it +answers "XY", then the byte representation coming out of the register must +be [0x58, 0x59], regardless of the device register's endianness for +numeric values. And, software needs to copy these bytes into a string +field regardless of the CPU's own endianness. + +(2) QEMU's device register accessor functions work with *numerical values* +exclusively, not strings: + +The emulated register's read accessor function returns the numerical value +(eg. 37 decimal, 0x0025) as a *host-encoded* uint64_t. QEMU translates +this value for the guest to the endianness of the emulated device register +(which is recorded in MemoryRegionOps.endianness). Then guest code must +translate the numerical value from device register to guest CPU +endianness, before including it in any computation (see (1)). + +(3) However, the data register of the fw_cfg device shall transfer strings +*only* -- that is, opaque blobs. Interpretation of any given blob is +subject to further agreement -- it can be an integer in an independently +determined byte order, or a genuine string, or an array of structs of +integers (in some byte order) and fixed size strings, and so on. + +Because register emulation in QEMU is integer-preserving, not +string-preserving (see (2)), we have to jump through a few hoops. + +(3a) We defined the memory mapped fw_cfg data register as +DEVICE_BIG_ENDIAN. + +The particular choice is not really relevant -- we picked BE only for +consistency with the control register, which *does* transfer integers -- +but our choice affects how we must host-encode values from fw_cfg strings. + +(3b) Since we want the fw_cfg string "XY" to appear as the [0x58, 0x59] +array on the data register, *and* we picked DEVICE_BIG_ENDIAN, we must +compose the host (== C language) value 0x5859 in the read accessor +function. + +(3c) When the guest performs the read access, the immediate uint16_t value +will be 0x5958 (in LE guests) and 0x5859 (in BE guests). However, the +uint16_t value does not matter. The only thing that matters is the byte +pattern [0x58, 0x59], which the guest code must copy into the target +string *without* any byte-swapping. + +(4) Now I get to explain where I screwed up. :( + +When we decided for big endian *integer* representation in the MMIO data +register -- see (3a) --, I mindlessly added an indiscriminate +byte-swizzling step to the (little endian) guest firmware. + +This was a grave error -- it violates (3c) --, but I didn't realize it. I +only saw that the code I otherwise intended for fw_cfg_data_mem_read(): + + value = 0; + for (i = 0; i < size; ++i) { + value = (value << 8) | fw_cfg_read(s); + } + +didn't produce the expected result in the guest. + +In true facepalm style, instead of blaming my guest code (which violated +(3c)), I blamed my host code (which was correct). Ultimately, I coded +ldX_he_p() into fw_cfg_data_mem_read(), because that happened to work. + +Obviously (...in retrospect) that was wrong. Only because my host happened +to be LE, ldX_he_p() composed the (otherwise incorrect) host value 0x5958 +from the fw_cfg string "XY". And that happened to compensate for the bogus +indiscriminate byte-swizzling in my guest code. + +Clearly the current code leaks the host endianness through to the guest, +which is wrong. Any device should work the same regardless of host +endianness. + +The solution is to compose the host-endian representation (2) of the big +endian interpretation (3a, 3b) of the fw_cfg string, and to drop the wrong +byte-swizzling in the guest (3c). + +Brown paper bag time for me. + +Signed-off-by: Laszlo Ersek +Message-id: 1420024880-15416-1-git-send-email-lersek@redhat.com +Reviewed-by: Peter Maydell +Signed-off-by: Peter Maydell +(cherry picked from commit 36b62ae6a58f9a588fd33be9386e18a2b90103f5) +--- + hw/nvram/fw_cfg.c | 41 +++++++---------------------------------- + 1 file changed, 7 insertions(+), 34 deletions(-) + +diff --git a/hw/nvram/fw_cfg.c b/hw/nvram/fw_cfg.c +index fcdf821..78a37be 100644 +--- a/hw/nvram/fw_cfg.c ++++ b/hw/nvram/fw_cfg.c +@@ -287,51 +287,24 @@ static uint64_t fw_cfg_data_mem_read(void *opaque, hwaddr addr, + unsigned size) + { + FWCfgState *s = opaque; +- uint8_t buf[8]; ++ uint64_t value = 0; + unsigned i; + + for (i = 0; i < size; ++i) { +- buf[i] = fw_cfg_read(s); ++ value = (value << 8) | fw_cfg_read(s); + } +- switch (size) { +- case 1: +- return buf[0]; +- case 2: +- return lduw_he_p(buf); +- case 4: +- return (uint32_t)ldl_he_p(buf); +- case 8: +- return ldq_he_p(buf); +- } +- abort(); ++ return value; + } + + static void fw_cfg_data_mem_write(void *opaque, hwaddr addr, + uint64_t value, unsigned size) + { + FWCfgState *s = opaque; +- uint8_t buf[8]; +- unsigned i; ++ unsigned i = size; + +- switch (size) { +- case 1: +- buf[0] = value; +- break; +- case 2: +- stw_he_p(buf, value); +- break; +- case 4: +- stl_he_p(buf, value); +- break; +- case 8: +- stq_he_p(buf, value); +- break; +- default: +- abort(); +- } +- for (i = 0; i < size; ++i) { +- fw_cfg_write(s, buf[i]); +- } ++ do { ++ fw_cfg_write(s, value >> (8 * --i)); ++ } while (i); + } + + static bool fw_cfg_data_mem_valid(void *opaque, hwaddr addr, +-- +2.1.0 + diff --git a/qemu.spec b/qemu.spec index 5036887..0a562dd 100644 --- a/qemu.spec +++ b/qemu.spec @@ -153,7 +153,7 @@ Summary: QEMU is a FAST! processor emulator Name: qemu Version: 2.2.0 -Release: 4%{?dist} +Release: 5%{?dist} Epoch: 2 License: GPLv2+ and LGPLv2+ and BSD Group: Development/Tools @@ -193,7 +193,24 @@ Source12: bridge.conf # qemu-kvm back compat wrapper Source13: qemu-kvm.sh -Patch0001: 0001-libcacard-stop-linking-against-every-single-3rd-part.patch +Patch0000: 0001-libcacard-stop-linking-against-every-single-3rd-part.patch + +# Add UEFI support for aarch64. Upstream in qemu > 2.2.0. +Patch0001: 0001-fw_cfg-remove-superfluous-blank-line.patch +Patch0002: 0002-hw-arm-boot-fix-uninitialized-scalar-variable-warnin.patch +Patch0003: 0003-Sort-include-qemu-typedefs.h.patch +Patch0004: 0004-fw_cfg-hard-separation-between-the-MMIO-and-I-O-port.patch +Patch0005: 0005-fw_cfg-move-boards-to-fw_cfg_init_io-fw_cfg_init_mem.patch +Patch0006: 0006-fw_cfg_mem-max-access-size-and-region-size-are-the-s.patch +Patch0007: 0007-fw_cfg_mem-flip-ctl_mem_ops-and-data_mem_ops-to-DEVI.patch +Patch0008: 0008-exec-allows-8-byte-accesses-in-subpage_ops.patch +Patch0009: 0009-fw_cfg_mem-introduce-the-data_width-property.patch +Patch0010: 0010-fw_cfg_mem-expose-the-data_width-property-with-fw_cf.patch +Patch0011: 0011-arm-add-fw_cfg-to-virt-board.patch +Patch0012: 0012-hw-loader-split-out-load_image_gzipped_buffer.patch +Patch0013: 0013-hw-arm-pass-pristine-kernel-image-to-guest-firmware-.patch +Patch0014: 0014-hw-arm-virt-enable-passing-of-EFI-stubbed-kernel-to-.patch +Patch0015: 0015-fw_cfg-fix-endianness-in-fw_cfg_data_mem_read-_write.patch BuildRequires: SDL2-devel BuildRequires: zlib-devel @@ -734,7 +751,22 @@ CAC emulation development files. %prep %setup -q -n qemu-2.2.0 +%patch0000 -p1 %patch0001 -p1 +%patch0002 -p1 +%patch0003 -p1 +%patch0004 -p1 +%patch0005 -p1 +%patch0006 -p1 +%patch0007 -p1 +%patch0008 -p1 +%patch0009 -p1 +%patch0010 -p1 +%patch0011 -p1 +%patch0012 -p1 +%patch0013 -p1 +%patch0014 -p1 +%patch0015 -p1 %build %if %{with kvmonly} @@ -1504,6 +1536,9 @@ getent passwd qemu >/dev/null || \ %endif %changelog +* Wed Feb 4 2015 Richard W.M. Jones - 2:2.2.0-5 +- Add UEFI support for aarch64. + * Tue Feb 3 2015 Daniel P. Berrange - 2:2.2.0-4 - Re-enable SPICE after previous build fixes circular dep