* [Qemu-devel] QEMU fw_cfg DMA interface @ 2015-10-01 12:14 Marc Marí 2015-10-01 12:16 ` [Qemu-devel] [PATCH v4 0/7] " Marc Marí 2015-10-01 16:03 ` [Qemu-devel] QEMU " Eric Blake 0 siblings, 2 replies; 60+ messages in thread From: Marc Marí @ 2015-10-01 12:14 UTC (permalink / raw) To: linux-kernel, qemu-devel, seabios Cc: Mark Rutland, Rob Herring, Drew, Arnd Bergmann, devicetree, Stefan Hajnoczi, Alexander Graf, Kevin O'Connor, Gerd Hoffmann, Marc Marí, Laszlo Implementation of the FW CFG DMA interface. When running a Linux guest on top of QEMU, using the -kernel options, this is the timing improvement for x86: QEMU commit b2312c6 and SeaBIOS commit 423542e QEMU startup time: .080 BIOS startup time: .060 Kernel setup time: .586 Total time: .726 QEMU with this patch series and SeaBIOS with this patch series QEMU startup time: .080 BIOS startup time: .039 Kernel setup time: .005 Total time: .126 QEMU startup time is the time between the start and the first kvm_entry. BIOS startup time is the time between the first kvm_entry and the start of function do_boot, in SeaBIOS. Kernel setup time is the time between the start of the function do_boot in SeaBIOS and the jump to the Linux kernel. As you can see, both the BIOS (because of ACPI tables and other configurations) and the Linux kernel boot (because of the copy to memory) are greatly improved with this new interface. Also, this new interface is an addon to the old interface. Both interfaces are compatible and interchangeable. Changes from v1: - Take into account order of fields in the FWCfgDmaAccess structure - Check and change endianness of FWCfgDmaAccess fields - Change order of fields in the FWCfgDmaAccess structure - Add FW_CFG_DMA_CTL_SKIP feature for control field - Split FW_CFG_SIZE in QEMU - Make FW_CFG_ID a bitmap of features - Add 64 bit address support for the transfer. Trigger when writing the low address, and address is 0 by default and at the end of each transfer. - Align ports and addresses. - Preserve old fw_cfg_comb_valid behaviour in QEMU - Update documentation to reflect all these changes Changes from v2: - Make IOports fw_cfg DMA region a different IO region. - Reuse everything for MMIO and IOport DMA regions - Make transfer status only based on control field - Use DMA helpers instead of direct map/unmap - Change ARM fw_cfg DMA address space - Change Linux boot process to match linuxboot.S - Add select capabilities in the FWCfgDmaAccess struct - Update documentation to reflect all these changes Changes from v3: - Set properly fw_cfg DMA fields in ARM - Set fw_cfg DMA boot process properly (by Laszlo Ersek) - Add signature to fw_cfg DMA address field (by Kevin O'Connor) - Minor nitpicks ^ permalink raw reply [flat|nested] 60+ messages in thread
* [Qemu-devel] [PATCH v4 0/7] fw_cfg DMA interface 2015-10-01 12:14 [Qemu-devel] QEMU fw_cfg DMA interface Marc Marí @ 2015-10-01 12:16 ` Marc Marí 2015-10-01 12:16 ` [Qemu-devel] [PATCH v4 1/7] fw_cfg: document fw_cfg_modify_iXX() update functions Marc Marí ` (7 more replies) 2015-10-01 16:03 ` [Qemu-devel] QEMU " Eric Blake 1 sibling, 8 replies; 60+ messages in thread From: Marc Marí @ 2015-10-01 12:16 UTC (permalink / raw) To: qemu-devel Cc: Drew, Stefan Hajnoczi, Gabriel L. Somlo, Kevin O'Connor, Gerd Hoffmann, Marc Marí, Laszlo Implement host-side of the FW CFG DMA interface both for x86 and ARM. Based on Gerd Hoffman's initial implementation. Gabriel L. Somlo (1): fw_cfg: document fw_cfg_modify_iXX() update functions Kevin O'Connor (1): fw_cfg: Define a static signature to be returned on DMA port reads Marc Marí (5): fw_cfg DMA interface documentation Implement fw_cfg DMA interface Enable fw_cfg DMA interface for ARM Enable fw_cfg DMA interface for x86 Make the kernel image in the fw_cfg DMA interface bootable docs/specs/fw_cfg.txt | 80 +++++++++++++++- hw/arm/virt.c | 8 +- hw/i386/pc.c | 12 ++- hw/nvram/fw_cfg.c | 240 +++++++++++++++++++++++++++++++++++++++++++--- include/hw/nvram/fw_cfg.h | 16 +++- 5 files changed, 329 insertions(+), 27 deletions(-) -- 2.4.3 ^ permalink raw reply [flat|nested] 60+ messages in thread
* [Qemu-devel] [PATCH v4 1/7] fw_cfg: document fw_cfg_modify_iXX() update functions 2015-10-01 12:16 ` [Qemu-devel] [PATCH v4 0/7] " Marc Marí @ 2015-10-01 12:16 ` Marc Marí 2015-10-01 12:16 ` [Qemu-devel] [PATCH v4 2/7] fw_cfg DMA interface documentation Marc Marí ` (6 subsequent siblings) 7 siblings, 0 replies; 60+ messages in thread From: Marc Marí @ 2015-10-01 12:16 UTC (permalink / raw) To: qemu-devel Cc: Drew, Stefan Hajnoczi, Gabriel L. Somlo, Kevin O'Connor, Gerd Hoffmann, Laszlo From: "Gabriel L. Somlo" <somlo@cmu.edu> Document the behavior of fw_cfg_modify_iXX() for leak-less updating of integer-type blobs. Currently only fw_cfg_modify_i16() is coded, but 32- and 64-bit versions may be added later if necessary.. Signed-off-by: Gabriel Somlo <somlo@cmu.edu> Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> Reviewed-by: Laszlo Ersek <lersek@redhat.com> --- docs/specs/fw_cfg.txt | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/docs/specs/fw_cfg.txt b/docs/specs/fw_cfg.txt index 74351dd..5bc7b96 100644 --- a/docs/specs/fw_cfg.txt +++ b/docs/specs/fw_cfg.txt @@ -159,6 +159,17 @@ will convert a 16-, 32-, or 64-bit integer to little-endian, then add a dynamically allocated copy of the appropriately sized item to fw_cfg under the given selector key value. +== fw_cfg_modify_iXX() == + +Modify the value of an XX-bit item (where XX may be 16, 32, or 64). +Similarly to the corresponding fw_cfg_add_iXX() function set, convert +a 16-, 32-, or 64-bit integer to little endian, create a dynamically +allocated copy of the required size, and replace the existing item at +the given selector key value with the newly allocated one. The previous +item, assumed to have been allocated during an earlier call to +fw_cfg_add_iXX() or fw_cfg_modify_iXX() (of the same width XX), is freed +before the function returns. + == fw_cfg_add_file() == Given a filename (i.e., fw_cfg item name), starting pointer, and size, -- 2.4.3 ^ permalink raw reply related [flat|nested] 60+ messages in thread
* [Qemu-devel] [PATCH v4 2/7] fw_cfg DMA interface documentation 2015-10-01 12:16 ` [Qemu-devel] [PATCH v4 0/7] " Marc Marí 2015-10-01 12:16 ` [Qemu-devel] [PATCH v4 1/7] fw_cfg: document fw_cfg_modify_iXX() update functions Marc Marí @ 2015-10-01 12:16 ` Marc Marí 2015-10-01 14:41 ` Laszlo Ersek 2015-10-01 12:16 ` [Qemu-devel] [PATCH v4 3/7] Implement fw_cfg DMA interface Marc Marí ` (5 subsequent siblings) 7 siblings, 1 reply; 60+ messages in thread From: Marc Marí @ 2015-10-01 12:16 UTC (permalink / raw) To: qemu-devel Cc: Drew, Stefan Hajnoczi, Gabriel L. Somlo, Kevin O'Connor, Gerd Hoffmann, Marc Marí, Laszlo Add fw_cfg DMA interface specification in the documentation. Based on Gerd Hoffman's initial implementation. Signed-off-by: Marc Marí <markmb@redhat.com> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> --- docs/specs/fw_cfg.txt | 65 +++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 61 insertions(+), 4 deletions(-) diff --git a/docs/specs/fw_cfg.txt b/docs/specs/fw_cfg.txt index 5bc7b96..2d6b2da 100644 --- a/docs/specs/fw_cfg.txt +++ b/docs/specs/fw_cfg.txt @@ -76,6 +76,13 @@ increasing address order, similar to memcpy(). Selector Register IOport: 0x510 Data Register IOport: 0x511 +DMA Address IOport: 0x514 + +=== ARM Register Locations === + +Selector Register address: Base + 8 (2 bytes) +Data Register address: Base + 0 (8 bytes) +DMA Address address: Base + 16 (8 bytes) == Firmware Configuration Items == @@ -86,11 +93,12 @@ by selecting the "signature" item using key 0x0000 (FW_CFG_SIGNATURE), and reading four bytes from the data register. If the fw_cfg device is present, the four bytes read will contain the characters "QEMU". -=== Revision (Key 0x0001, FW_CFG_ID) === +=== Revision / feature bitmap (Key 0x0001, FW_CFG_ID) === -A 32-bit little-endian unsigned int, this item is used as an interface -revision number, and is currently set to 1 by QEMU when fw_cfg is -initialized. +A 32-bit little-endian unsigned int, this item is used to check for enabled +features. + - Bit 0: traditional interface. Always set. + - Bit 1: DMA interface. === File Directory (Key 0x0019, FW_CFG_FILE_DIR) === @@ -132,6 +140,55 @@ Selector Reg. Range Usage In practice, the number of allowed firmware configuration items is given by the value of FW_CFG_MAX_ENTRY (see fw_cfg.h). += Guest-side DMA Interface = + +If bit 1 of the feature bitmap is set, the DMA interface is present. This does +not replace the existing fw_cfg interface, it is an add-on. This interface +can be used through the 64-bit wide address register. + +The address register is in big-endian format. The value for the register is 0 +at startup and after an operation. A write to the lower half triggers an +operation. This means that operations with 32-bit addresses can be triggered +with just one write, whereas operations with 64-bit addresses can be +triggered with one 64-bit write or two 32-bit writes, starting with the +higher part. + +In this register, the physical address of a FWCfgDmaAccess structure in RAM +should be written. This is the format of the FWCfgDmaAccess structure: + +typedef struct FWCfgDmaAccess { + uint32_t control; + uint32_t length; + uint64_t address; +} FWCfgDmaAccess; + +The fields of the structure are in big endian mode, and the field at the lowest +address is the "control" field. + +The "control" field has the following bits: + - Bit 0: Error + - Bit 1: Read + - Bit 2: Skip + - Bit 3: Select. The upper 16 bits are the selected index. + +When an operation is triggered, if the "control" field has bit 3 set, the +upper 16 bits are interpreted as an index of a firmware configuration item. +This has the same effect as writing the selector register. + +If the "control" field has bit 1 set, a read operation will be performed. +"length" bytes for the current selector and offset will be copied into the +physical RAM address specified by the "address" field. + +If the "control" field has bit 2 set (and not bit 1), a skip operation will be +performed. The offset for the current selector will be advanced "length" bytes. + +To check the result, read the "control" field: + error bit set -> something went wrong. + all bits cleared -> transfer finished successfully. + otherwise -> transfer still in progress (doesn't happen + today due to implementation not being async, + but may in the future). + = Host-side API = The following functions are available to the QEMU programmer for adding -- 2.4.3 ^ permalink raw reply related [flat|nested] 60+ messages in thread
* Re: [Qemu-devel] [PATCH v4 2/7] fw_cfg DMA interface documentation 2015-10-01 12:16 ` [Qemu-devel] [PATCH v4 2/7] fw_cfg DMA interface documentation Marc Marí @ 2015-10-01 14:41 ` Laszlo Ersek 0 siblings, 0 replies; 60+ messages in thread From: Laszlo Ersek @ 2015-10-01 14:41 UTC (permalink / raw) To: Marc Marí, qemu-devel Cc: Gabriel L. Somlo, Stefan Hajnoczi, Drew, Kevin O'Connor, Gerd Hoffmann On 10/01/15 14:16, Marc Marí wrote: > Add fw_cfg DMA interface specification in the documentation. > > Based on Gerd Hoffman's initial implementation. > > Signed-off-by: Marc Marí <markmb@redhat.com> > Reviewed-by: Peter Maydell <peter.maydell@linaro.org> > --- > docs/specs/fw_cfg.txt | 65 +++++++++++++++++++++++++++++++++++++++++++++++---- > 1 file changed, 61 insertions(+), 4 deletions(-) > > diff --git a/docs/specs/fw_cfg.txt b/docs/specs/fw_cfg.txt > index 5bc7b96..2d6b2da 100644 > --- a/docs/specs/fw_cfg.txt > +++ b/docs/specs/fw_cfg.txt > @@ -76,6 +76,13 @@ increasing address order, similar to memcpy(). > > Selector Register IOport: 0x510 > Data Register IOport: 0x511 > +DMA Address IOport: 0x514 > + > +=== ARM Register Locations === > + > +Selector Register address: Base + 8 (2 bytes) > +Data Register address: Base + 0 (8 bytes) > +DMA Address address: Base + 16 (8 bytes) > > == Firmware Configuration Items == > > @@ -86,11 +93,12 @@ by selecting the "signature" item using key 0x0000 (FW_CFG_SIGNATURE), > and reading four bytes from the data register. If the fw_cfg device is > present, the four bytes read will contain the characters "QEMU". > > -=== Revision (Key 0x0001, FW_CFG_ID) === > +=== Revision / feature bitmap (Key 0x0001, FW_CFG_ID) === > > -A 32-bit little-endian unsigned int, this item is used as an interface > -revision number, and is currently set to 1 by QEMU when fw_cfg is > -initialized. > +A 32-bit little-endian unsigned int, this item is used to check for enabled > +features. > + - Bit 0: traditional interface. Always set. > + - Bit 1: DMA interface. > > === File Directory (Key 0x0019, FW_CFG_FILE_DIR) === > > @@ -132,6 +140,55 @@ Selector Reg. Range Usage > In practice, the number of allowed firmware configuration items is given > by the value of FW_CFG_MAX_ENTRY (see fw_cfg.h). > > += Guest-side DMA Interface = > + > +If bit 1 of the feature bitmap is set, the DMA interface is present. This does > +not replace the existing fw_cfg interface, it is an add-on. This interface > +can be used through the 64-bit wide address register. > + > +The address register is in big-endian format. The value for the register is 0 > +at startup and after an operation. A write to the lower half triggers an suggest "least significant half (at offset 4)" in place of "lower half" > +operation. This means that operations with 32-bit addresses can be triggered > +with just one write, whereas operations with 64-bit addresses can be > +triggered with one 64-bit write or two 32-bit writes, starting with the > +higher part. suggest "most significant half (at offset 0)" in place of "higher part". With those changes: Reviewed-by: Laszlo Ersek <lersek@redhat.com> Thanks! Laszlo > + > +In this register, the physical address of a FWCfgDmaAccess structure in RAM > +should be written. This is the format of the FWCfgDmaAccess structure: > + > +typedef struct FWCfgDmaAccess { > + uint32_t control; > + uint32_t length; > + uint64_t address; > +} FWCfgDmaAccess; > + > +The fields of the structure are in big endian mode, and the field at the lowest > +address is the "control" field. > + > +The "control" field has the following bits: > + - Bit 0: Error > + - Bit 1: Read > + - Bit 2: Skip > + - Bit 3: Select. The upper 16 bits are the selected index. > + > +When an operation is triggered, if the "control" field has bit 3 set, the > +upper 16 bits are interpreted as an index of a firmware configuration item. > +This has the same effect as writing the selector register. > + > +If the "control" field has bit 1 set, a read operation will be performed. > +"length" bytes for the current selector and offset will be copied into the > +physical RAM address specified by the "address" field. > + > +If the "control" field has bit 2 set (and not bit 1), a skip operation will be > +performed. The offset for the current selector will be advanced "length" bytes. > + > +To check the result, read the "control" field: > + error bit set -> something went wrong. > + all bits cleared -> transfer finished successfully. > + otherwise -> transfer still in progress (doesn't happen > + today due to implementation not being async, > + but may in the future). > + > = Host-side API = > > The following functions are available to the QEMU programmer for adding > ^ permalink raw reply [flat|nested] 60+ messages in thread
* [Qemu-devel] [PATCH v4 3/7] Implement fw_cfg DMA interface 2015-10-01 12:16 ` [Qemu-devel] [PATCH v4 0/7] " Marc Marí 2015-10-01 12:16 ` [Qemu-devel] [PATCH v4 1/7] fw_cfg: document fw_cfg_modify_iXX() update functions Marc Marí 2015-10-01 12:16 ` [Qemu-devel] [PATCH v4 2/7] fw_cfg DMA interface documentation Marc Marí @ 2015-10-01 12:16 ` Marc Marí 2015-10-01 14:36 ` Laszlo Ersek 2015-10-06 14:44 ` Stefan Hajnoczi 2015-10-01 12:16 ` [Qemu-devel] [PATCH v4 4/7] Enable fw_cfg DMA interface for ARM Marc Marí ` (4 subsequent siblings) 7 siblings, 2 replies; 60+ messages in thread From: Marc Marí @ 2015-10-01 12:16 UTC (permalink / raw) To: qemu-devel Cc: Drew, Stefan Hajnoczi, Gabriel L. Somlo, Kevin O'Connor, Gerd Hoffmann, Marc Marí, Laszlo Based on the specifications on docs/specs/fw_cfg.txt This interface is an addon. The old interface can still be used as usual. Based on Gerd Hoffman's initial implementation. Signed-off-by: Marc Marí <markmb@redhat.com> --- hw/arm/virt.c | 2 +- hw/nvram/fw_cfg.c | 231 +++++++++++++++++++++++++++++++++++++++++++--- include/hw/nvram/fw_cfg.h | 16 +++- 3 files changed, 233 insertions(+), 16 deletions(-) diff --git a/hw/arm/virt.c b/hw/arm/virt.c index d25d6cf..7ae984f 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -683,7 +683,7 @@ static void create_fw_cfg(const VirtBoardInfo *vbi) hwaddr size = vbi->memmap[VIRT_FW_CFG].size; char *nodename; - fw_cfg_init_mem_wide(base + 8, base, 8); + fw_cfg_init_mem_wide(base + 8, base, 8, 0, NULL); nodename = g_strdup_printf("/fw-cfg@%" PRIx64, base); qemu_fdt_add_subnode(vbi->fdt, nodename); diff --git a/hw/nvram/fw_cfg.c b/hw/nvram/fw_cfg.c index 658f8c4..59933b3 100644 --- a/hw/nvram/fw_cfg.c +++ b/hw/nvram/fw_cfg.c @@ -23,6 +23,7 @@ */ #include "hw/hw.h" #include "sysemu/sysemu.h" +#include "sysemu/dma.h" #include "hw/isa/isa.h" #include "hw/nvram/fw_cfg.h" #include "hw/sysbus.h" @@ -30,7 +31,7 @@ #include "qemu/error-report.h" #include "qemu/config-file.h" -#define FW_CFG_SIZE 2 +#define FW_CFG_CTL_SIZE 2 #define FW_CFG_NAME "fw_cfg" #define FW_CFG_PATH "/machine/" FW_CFG_NAME @@ -42,6 +43,16 @@ #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) +/* FW_CFG_VERSION bits */ +#define FW_CFG_VERSION 0x01 +#define FW_CFG_VERSION_DMA 0x02 + +/* FW_CFG_DMA_CONTROL bits */ +#define FW_CFG_DMA_CTL_ERROR 0x01 +#define FW_CFG_DMA_CTL_READ 0x02 +#define FW_CFG_DMA_CTL_SKIP 0x04 +#define FW_CFG_DMA_CTL_SELECT 0x08 + typedef struct FWCfgEntry { uint32_t len; uint8_t *data; @@ -59,6 +70,10 @@ struct FWCfgState { uint16_t cur_entry; uint32_t cur_offset; Notifier machine_ready; + + bool dma_enabled; + AddressSpace *dma_as; + dma_addr_t dma_addr; }; struct FWCfgIoState { @@ -66,8 +81,8 @@ struct FWCfgIoState { FWCfgState parent_obj; /*< public >*/ - MemoryRegion comb_iomem; - uint32_t iobase; + MemoryRegion comb_iomem, dma_iomem; + uint32_t iobase, dma_iobase; }; struct FWCfgMemState { @@ -75,7 +90,7 @@ struct FWCfgMemState { FWCfgState parent_obj; /*< public >*/ - MemoryRegion ctl_iomem, data_iomem; + MemoryRegion ctl_iomem, data_iomem, dma_iomem; uint32_t data_width; MemoryRegionOps wide_data_ops; }; @@ -292,6 +307,119 @@ static void fw_cfg_data_mem_write(void *opaque, hwaddr addr, } while (i); } +static void fw_cfg_dma_transfer(FWCfgState *s) +{ + dma_addr_t len; + FWCfgDmaAccess dma; + int arch; + FWCfgEntry *e; + int read; + dma_addr_t dma_addr; + + /* Reset the address before the next access */ + dma_addr = s->dma_addr; + s->dma_addr = 0; + + dma.address = ldq_be_dma(s->dma_as, + dma_addr + offsetof(FWCfgDmaAccess, address)); + dma.length = ldl_be_dma(s->dma_as, + dma_addr + offsetof(FWCfgDmaAccess, length)); + dma.control = ldl_be_dma(s->dma_as, + dma_addr + offsetof(FWCfgDmaAccess, control)); + + if (dma.control & FW_CFG_DMA_CTL_SELECT) { + fw_cfg_select(s, dma.control >> 16); + } + + arch = !!(s->cur_entry & FW_CFG_ARCH_LOCAL); + e = &s->entries[arch][s->cur_entry & FW_CFG_ENTRY_MASK]; + + if (dma.control & FW_CFG_DMA_CTL_READ) { + read = 1; + } else if (dma.control & FW_CFG_DMA_CTL_SKIP) { + read = 0; + } else { + dma.length = 0; + } + + dma.control = 0; + + while (dma.length > 0 && !(dma.control & FW_CFG_DMA_CTL_ERROR)) { + if (s->cur_entry == FW_CFG_INVALID || !e->data || + s->cur_offset >= e->len) { + len = dma.length; + + /* If the access is not a read access, it will be a skip access, + * tested before. + */ + if (read) { + if (dma_memory_set(s->dma_as, dma.address, 0, len)) { + dma.control |= FW_CFG_DMA_CTL_ERROR; + } + } + + } else { + if (dma.length <= (e->len - s->cur_offset)) { + len = dma.length; + } else { + len = (e->len - s->cur_offset); + } + + if (e->read_callback) { + e->read_callback(e->callback_opaque, s->cur_offset); + } + + /* If the access is not a read access, it will be a skip access, + * tested before. + */ + if (read) { + if (dma_memory_write(s->dma_as, dma.address, + &e->data[s->cur_offset], len)) { + dma.control |= FW_CFG_DMA_CTL_ERROR; + } + } + + s->cur_offset += len; + } + + dma.address += len; + dma.length -= len; + + } + + stl_be_dma(s->dma_as, dma_addr + offsetof(FWCfgDmaAccess, control), + dma.control); + + trace_fw_cfg_read(s, 0); +} + +static void fw_cfg_dma_mem_write(void *opaque, hwaddr addr, + uint64_t value, unsigned size) +{ + FWCfgState *s = opaque; + + if (size == 4) { + if (addr == 0) { + /* FWCfgDmaAccess high address */ + s->dma_addr = value << 32; + } else if (addr == 4) { + /* FWCfgDmaAccess low address */ + s->dma_addr |= value; + fw_cfg_dma_transfer(s); + } + } else if (size == 8 && addr == 0) { + s->dma_addr = value; + fw_cfg_dma_transfer(s); + } +} + +static bool fw_cfg_dma_mem_valid(void *opaque, hwaddr addr, + unsigned size, bool is_write) +{ + return is_write && ((size == 4 && (addr == 0 || addr == 4)) || + (size == 8 && addr == 0)); +} + static bool fw_cfg_data_mem_valid(void *opaque, hwaddr addr, unsigned size, bool is_write) { @@ -359,6 +487,12 @@ static const MemoryRegionOps fw_cfg_comb_mem_ops = { .valid.accepts = fw_cfg_comb_valid, }; +static const MemoryRegionOps fw_cfg_dma_mem_ops = { + .write = fw_cfg_dma_mem_write, + .endianness = DEVICE_BIG_ENDIAN, + .valid.accepts = fw_cfg_dma_mem_valid, +}; + static void fw_cfg_reset(DeviceState *d) { FWCfgState *s = FW_CFG(d); @@ -399,6 +533,22 @@ static bool is_version_1(void *opaque, int version_id) return version_id == 1; } +static bool fw_cfg_dma_enabled(void *opaque) +{ + FWCfgState *s = opaque; + + return s->dma_enabled; +} + +static VMStateDescription vmstate_fw_cfg_dma = { + .name = "fw_cfg/dma", + .needed = fw_cfg_dma_enabled, + .fields = (VMStateField[]) { + VMSTATE_UINT64(dma_addr, FWCfgState), + VMSTATE_END_OF_LIST() + }, +}; + static const VMStateDescription vmstate_fw_cfg = { .name = "fw_cfg", .version_id = 2, @@ -408,6 +558,10 @@ static const VMStateDescription vmstate_fw_cfg = { VMSTATE_UINT16_HACK(cur_offset, FWCfgState, is_version_1), VMSTATE_UINT32_V(cur_offset, FWCfgState, 2), VMSTATE_END_OF_LIST() + }, + .subsections = (const VMStateDescription*[]) { + &vmstate_fw_cfg_dma, + NULL, } }; @@ -593,7 +747,6 @@ static void fw_cfg_init1(DeviceState *dev) qdev_init_nofail(dev); fw_cfg_add_bytes(s, FW_CFG_SIGNATURE, (char *)"QEMU", 4); - fw_cfg_add_i32(s, FW_CFG_ID, 1); 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)); fw_cfg_add_i16(s, FW_CFG_NB_CPUS, (uint16_t)smp_cpus); @@ -605,25 +758,52 @@ static void fw_cfg_init1(DeviceState *dev) qemu_add_machine_init_done_notifier(&s->machine_ready); } -FWCfgState *fw_cfg_init_io(uint32_t iobase) +FWCfgState *fw_cfg_init_io_dma(uint32_t iobase, uint32_t dma_iobase, + AddressSpace *dma_as) { DeviceState *dev; + FWCfgState *s; + uint32_t version = FW_CFG_VERSION; dev = qdev_create(NULL, TYPE_FW_CFG_IO); qdev_prop_set_uint32(dev, "iobase", iobase); + qdev_prop_set_uint32(dev, "dma_iobase", dma_iobase); + fw_cfg_init1(dev); + s = FW_CFG(dev); + + if (dma_as) { + /* 64 bits for the address field */ + s->dma_as = dma_as; + s->dma_enabled = true; + s->dma_addr = 0; + + version |= FW_CFG_VERSION_DMA; + } - return FW_CFG(dev); + fw_cfg_add_i32(s, FW_CFG_ID, version); + + return s; } -FWCfgState *fw_cfg_init_mem_wide(hwaddr ctl_addr, hwaddr data_addr, - uint32_t data_width) +FWCfgState *fw_cfg_init_io(uint32_t iobase) +{ + return fw_cfg_init_io_dma(iobase, 0, NULL); +} + +FWCfgState *fw_cfg_init_mem_wide(hwaddr ctl_addr, + hwaddr data_addr, uint32_t data_width, + hwaddr dma_addr, AddressSpace *dma_as) { DeviceState *dev; SysBusDevice *sbd; + FWCfgState *s; + uint32_t version = FW_CFG_VERSION; + bool dma_enabled = dma_addr && dma_as; dev = qdev_create(NULL, TYPE_FW_CFG_MEM); qdev_prop_set_uint32(dev, "data_width", data_width); + qdev_prop_set_bit(dev, "dma_enabled", dma_enabled); fw_cfg_init1(dev); @@ -631,13 +811,25 @@ FWCfgState *fw_cfg_init_mem_wide(hwaddr ctl_addr, hwaddr data_addr, sysbus_mmio_map(sbd, 0, ctl_addr); sysbus_mmio_map(sbd, 1, data_addr); - return FW_CFG(dev); + s = FW_CFG(dev); + + if (dma_enabled) { + s->dma_as = dma_as; + s->dma_addr = 0; + sysbus_mmio_map(sbd, 2, dma_addr); + version |= FW_CFG_VERSION_DMA; + } + + fw_cfg_add_i32(s, FW_CFG_ID, version); + + return s; } 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); + fw_cfg_data_mem_ops.valid.max_access_size, + 0, NULL); } @@ -664,6 +856,7 @@ static const TypeInfo fw_cfg_info = { static Property fw_cfg_io_properties[] = { DEFINE_PROP_UINT32("iobase", FWCfgIoState, iobase, -1), + DEFINE_PROP_UINT32("dma_iobase", FWCfgIoState, dma_iobase, -1), DEFINE_PROP_END_OF_LIST(), }; @@ -673,8 +866,12 @@ static void fw_cfg_io_realize(DeviceState *dev, Error **errp) 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); + FW_CFG(s), "fwcfg", FW_CFG_CTL_SIZE); sysbus_add_io(sbd, s->iobase, &s->comb_iomem); + + memory_region_init_io(&s->dma_iomem, OBJECT(s), &fw_cfg_dma_mem_ops, + FW_CFG(s), "fwcfg.dma", sizeof(dma_addr_t)); + sysbus_add_io(sbd, s->dma_iobase, &s->dma_iomem); } static void fw_cfg_io_class_init(ObjectClass *klass, void *data) @@ -695,6 +892,8 @@ static const TypeInfo fw_cfg_io_info = { static Property fw_cfg_mem_properties[] = { DEFINE_PROP_UINT32("data_width", FWCfgMemState, data_width, -1), + DEFINE_PROP_BOOL("dma_enabled", FWCfgMemState, parent_obj.dma_enabled, + false), DEFINE_PROP_END_OF_LIST(), }; @@ -705,7 +904,7 @@ static void fw_cfg_mem_realize(DeviceState *dev, Error **errp) 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); + FW_CFG(s), "fwcfg.ctl", FW_CFG_CTL_SIZE); sysbus_init_mmio(sbd, &s->ctl_iomem); if (s->data_width > data_ops->valid.max_access_size) { @@ -723,6 +922,12 @@ static void fw_cfg_mem_realize(DeviceState *dev, Error **errp) 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); + + if (FW_CFG(s)->dma_enabled) { + memory_region_init_io(&s->dma_iomem, OBJECT(s), &fw_cfg_dma_mem_ops, + FW_CFG(s), "fwcfg.dma", sizeof(dma_addr_t)); + sysbus_init_mmio(sbd, &s->dma_iomem); + } } static void fw_cfg_mem_class_init(ObjectClass *klass, void *data) diff --git a/include/hw/nvram/fw_cfg.h b/include/hw/nvram/fw_cfg.h index e60d3ca..ee0cd8a 100644 --- a/include/hw/nvram/fw_cfg.h +++ b/include/hw/nvram/fw_cfg.h @@ -61,6 +61,15 @@ typedef struct FWCfgFiles { FWCfgFile f[]; } FWCfgFiles; +/* Control as first field allows for different structures selected by this + * field, which might be useful in the future + */ +typedef struct FWCfgDmaAccess { + uint32_t control; + uint32_t length; + uint64_t address; +} QEMU_PACKED FWCfgDmaAccess; + typedef void (*FWCfgCallback)(void *opaque, uint8_t *data); typedef void (*FWCfgReadCallback)(void *opaque, uint32_t offset); @@ -77,10 +86,13 @@ 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_io_dma(uint32_t iobase, uint32_t dma_iobase, + AddressSpace *dma_as); 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_init_mem_wide(hwaddr ctl_addr, + hwaddr data_addr, uint32_t data_width, + hwaddr dma_addr, AddressSpace *dma_as); FWCfgState *fw_cfg_find(void); -- 2.4.3 ^ permalink raw reply related [flat|nested] 60+ messages in thread
* Re: [Qemu-devel] [PATCH v4 3/7] Implement fw_cfg DMA interface 2015-10-01 12:16 ` [Qemu-devel] [PATCH v4 3/7] Implement fw_cfg DMA interface Marc Marí @ 2015-10-01 14:36 ` Laszlo Ersek 2015-10-01 15:52 ` Marc Marí 2015-10-01 17:18 ` Peter Maydell 2015-10-06 14:44 ` Stefan Hajnoczi 1 sibling, 2 replies; 60+ messages in thread From: Laszlo Ersek @ 2015-10-01 14:36 UTC (permalink / raw) To: Marc Marí, qemu-devel Cc: Gabriel L. Somlo, Stefan Hajnoczi, Drew, Kevin O'Connor, Gerd Hoffmann This looks good to me. Thanks for addressing my v3 request. I have some new remarks here. I feel *really* bad for not finding them earlier. (If you get tired of working on this series, I could pick it up and try to shepherd it further.) On 10/01/15 14:16, Marc Marí wrote: > Based on the specifications on docs/specs/fw_cfg.txt > > This interface is an addon. The old interface can still be used as usual. > > Based on Gerd Hoffman's initial implementation. > > Signed-off-by: Marc Marí <markmb@redhat.com> > --- > hw/arm/virt.c | 2 +- > hw/nvram/fw_cfg.c | 231 +++++++++++++++++++++++++++++++++++++++++++--- > include/hw/nvram/fw_cfg.h | 16 +++- > 3 files changed, 233 insertions(+), 16 deletions(-) > > diff --git a/hw/arm/virt.c b/hw/arm/virt.c > index d25d6cf..7ae984f 100644 > --- a/hw/arm/virt.c > +++ b/hw/arm/virt.c > @@ -683,7 +683,7 @@ static void create_fw_cfg(const VirtBoardInfo *vbi) > hwaddr size = vbi->memmap[VIRT_FW_CFG].size; > char *nodename; > > - fw_cfg_init_mem_wide(base + 8, base, 8); > + fw_cfg_init_mem_wide(base + 8, base, 8, 0, NULL); > > nodename = g_strdup_printf("/fw-cfg@%" PRIx64, base); > qemu_fdt_add_subnode(vbi->fdt, nodename); > diff --git a/hw/nvram/fw_cfg.c b/hw/nvram/fw_cfg.c > index 658f8c4..59933b3 100644 > --- a/hw/nvram/fw_cfg.c > +++ b/hw/nvram/fw_cfg.c > @@ -23,6 +23,7 @@ > */ > #include "hw/hw.h" > #include "sysemu/sysemu.h" > +#include "sysemu/dma.h" > #include "hw/isa/isa.h" > #include "hw/nvram/fw_cfg.h" > #include "hw/sysbus.h" > @@ -30,7 +31,7 @@ > #include "qemu/error-report.h" > #include "qemu/config-file.h" > > -#define FW_CFG_SIZE 2 > +#define FW_CFG_CTL_SIZE 2 > #define FW_CFG_NAME "fw_cfg" > #define FW_CFG_PATH "/machine/" FW_CFG_NAME > > @@ -42,6 +43,16 @@ > #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) > > +/* FW_CFG_VERSION bits */ > +#define FW_CFG_VERSION 0x01 > +#define FW_CFG_VERSION_DMA 0x02 > + > +/* FW_CFG_DMA_CONTROL bits */ > +#define FW_CFG_DMA_CTL_ERROR 0x01 > +#define FW_CFG_DMA_CTL_READ 0x02 > +#define FW_CFG_DMA_CTL_SKIP 0x04 > +#define FW_CFG_DMA_CTL_SELECT 0x08 > + > typedef struct FWCfgEntry { > uint32_t len; > uint8_t *data; > @@ -59,6 +70,10 @@ struct FWCfgState { > uint16_t cur_entry; > uint32_t cur_offset; > Notifier machine_ready; > + > + bool dma_enabled; > + AddressSpace *dma_as; > + dma_addr_t dma_addr; > }; > > struct FWCfgIoState { > @@ -66,8 +81,8 @@ struct FWCfgIoState { > FWCfgState parent_obj; > /*< public >*/ > > - MemoryRegion comb_iomem; > - uint32_t iobase; > + MemoryRegion comb_iomem, dma_iomem; > + uint32_t iobase, dma_iobase; > }; > > struct FWCfgMemState { > @@ -75,7 +90,7 @@ struct FWCfgMemState { > FWCfgState parent_obj; > /*< public >*/ > > - MemoryRegion ctl_iomem, data_iomem; > + MemoryRegion ctl_iomem, data_iomem, dma_iomem; > uint32_t data_width; > MemoryRegionOps wide_data_ops; > }; (1) I *think* the new "dma_iomem" field, of type MemoryRegion, could be moved up to the parent struct FWCfgEntry, from both FWCfgMemState and FWCfgIoState. (And the references in the rest of the code could be updated.) ( Independently, some loud thinking, mostly for myself: I've always been surprised by the difference between (a) FWCfgIoState *carrying* "dma_iobase" as a field -- and a property! --, and (b) FWCfgMemState *not* carrying the same as a field -- nor as a property. I think I finally understand this difference now. It is all rooted in the difference between the internal APIs sysbus_add_io() and sysbus_init_mmio(). Both of these are called from the device realize functions, but the first (sysbus_add_io()) wants the IO port address at once, whereas the second (sysbus_init_mmio()) doesn't want the address -- the actual mapping (sysbus_mmio_map()) is delayed to board code; the device code doesn't want to be aware of it. And this ripples to the top. Because sysbus_add_io() wants the IO port address, we must pass that address to the device realize function. And for that, we need a device property -- "dma_iobase". This is not new, it just follows the example of the preexistent "iobase" field / property. Whereas, in the sysbus_init_mmio() case, we can keep the MMIO address private to the board code; the realize function need not know the address. However, the realize function does need to know the *fact* that we're going to do DMA. Given that we must maintain this fact (in "FWCfgState.dma_enabled") anyway, for other -- e.g. migration subsection -- purposes as well, it makes sense to expose that same field of the parent struct as a property, so we can set it in the memory mapped case *before* the realize function looks at it. I feel better now, thanks for listening. ) Then, > @@ -292,6 +307,119 @@ static void fw_cfg_data_mem_write(void *opaque, hwaddr addr, > } while (i); > } > > +static void fw_cfg_dma_transfer(FWCfgState *s) > +{ > + dma_addr_t len; > + FWCfgDmaAccess dma; > + int arch; > + FWCfgEntry *e; > + int read; > + dma_addr_t dma_addr; > + > + /* Reset the address before the next access */ > + dma_addr = s->dma_addr; > + s->dma_addr = 0; > + > + dma.address = ldq_be_dma(s->dma_as, > + dma_addr + offsetof(FWCfgDmaAccess, address)); > + dma.length = ldl_be_dma(s->dma_as, > + dma_addr + offsetof(FWCfgDmaAccess, length)); > + dma.control = ldl_be_dma(s->dma_as, > + dma_addr + offsetof(FWCfgDmaAccess, control)); > + > + if (dma.control & FW_CFG_DMA_CTL_SELECT) { > + fw_cfg_select(s, dma.control >> 16); > + } > + > + arch = !!(s->cur_entry & FW_CFG_ARCH_LOCAL); > + e = &s->entries[arch][s->cur_entry & FW_CFG_ENTRY_MASK]; > + > + if (dma.control & FW_CFG_DMA_CTL_READ) { > + read = 1; > + } else if (dma.control & FW_CFG_DMA_CTL_SKIP) { > + read = 0; > + } else { > + dma.length = 0; I can see you addressed Kevin's comment here. > + } > + > + dma.control = 0; > + > + while (dma.length > 0 && !(dma.control & FW_CFG_DMA_CTL_ERROR)) { > + if (s->cur_entry == FW_CFG_INVALID || !e->data || > + s->cur_offset >= e->len) { > + len = dma.length; > + > + /* If the access is not a read access, it will be a skip access, > + * tested before. > + */ > + if (read) { > + if (dma_memory_set(s->dma_as, dma.address, 0, len)) { > + dma.control |= FW_CFG_DMA_CTL_ERROR; > + } > + } > + > + } else { > + if (dma.length <= (e->len - s->cur_offset)) { > + len = dma.length; > + } else { > + len = (e->len - s->cur_offset); > + } > + > + if (e->read_callback) { > + e->read_callback(e->callback_opaque, s->cur_offset); > + } > + > + /* If the access is not a read access, it will be a skip access, > + * tested before. > + */ > + if (read) { > + if (dma_memory_write(s->dma_as, dma.address, > + &e->data[s->cur_offset], len)) { > + dma.control |= FW_CFG_DMA_CTL_ERROR; > + } > + } > + > + s->cur_offset += len; > + } > + > + dma.address += len; > + dma.length -= len; > + > + } > + > + stl_be_dma(s->dma_as, dma_addr + offsetof(FWCfgDmaAccess, control), > + dma.control); > + > + trace_fw_cfg_read(s, 0); > +} Seems OK to me. > + > +static void fw_cfg_dma_mem_write(void *opaque, hwaddr addr, > + uint64_t value, unsigned size) > +{ > + FWCfgState *s = opaque; > + > + if (size == 4) { > + if (addr == 0) { > + /* FWCfgDmaAccess high address */ > + s->dma_addr = value << 32; > + } else if (addr == 4) { > + /* FWCfgDmaAccess low address */ > + s->dma_addr |= value; > + fw_cfg_dma_transfer(s); > + } > + } else if (size == 8 && addr == 0) { > + s->dma_addr = value; > + fw_cfg_dma_transfer(s); > + } > +} Seems to match the zeroing of s->dma_addr in fw_cfg_dma_transfer(). Good. > + > +static bool fw_cfg_dma_mem_valid(void *opaque, hwaddr addr, > + unsigned size, bool is_write) > +{ > + return is_write && ((size == 4 && (addr == 0 || addr == 4)) || > + (size == 8 && addr == 0)); > +} > + > static bool fw_cfg_data_mem_valid(void *opaque, hwaddr addr, > unsigned size, bool is_write) > { > @@ -359,6 +487,12 @@ static const MemoryRegionOps fw_cfg_comb_mem_ops = { > .valid.accepts = fw_cfg_comb_valid, > }; > > +static const MemoryRegionOps fw_cfg_dma_mem_ops = { > + .write = fw_cfg_dma_mem_write, > + .endianness = DEVICE_BIG_ENDIAN, > + .valid.accepts = fw_cfg_dma_mem_valid, > +}; (2) Okay. This is somewhat important, and *completely* non-intuitive, unfortunately. Without setting *both* .valid.max_access_size = 8, .impl.max_access_size = 8, here, the memory subsystem will split up all 8-byte wide accesses (from the guest side) to two 4-byte wide calls to fw_cfg_dma_mem_write()). Those calls do satisfy the ordering logic in fw_cfg_dma_mem_write(), but nonetheless, the lack of the above setting makes the following code in fw_cfg_dma_mem_write() dead: > + } else if (size == 8 && addr == 0) { > + s->dma_addr = value; > + fw_cfg_dma_transfer(s); > + } (I verified this claim with gdb on aarch64.) So, please initialize both of the above fields to 8. > + > static void fw_cfg_reset(DeviceState *d) > { > FWCfgState *s = FW_CFG(d); > @@ -399,6 +533,22 @@ static bool is_version_1(void *opaque, int version_id) > return version_id == 1; > } > > +static bool fw_cfg_dma_enabled(void *opaque) > +{ > + FWCfgState *s = opaque; > + > + return s->dma_enabled; > +} > + > +static VMStateDescription vmstate_fw_cfg_dma = { > + .name = "fw_cfg/dma", > + .needed = fw_cfg_dma_enabled, > + .fields = (VMStateField[]) { > + VMSTATE_UINT64(dma_addr, FWCfgState), > + VMSTATE_END_OF_LIST() > + }, > +}; Looks good to me. All fields that come from the command line (ie. management layer) need not / must not be part of the migration stream. And all data that is programmed by the guest, must. Here, "dma_addr" is the only such item. Okay. > + > static const VMStateDescription vmstate_fw_cfg = { > .name = "fw_cfg", > .version_id = 2, > @@ -408,6 +558,10 @@ static const VMStateDescription vmstate_fw_cfg = { > VMSTATE_UINT16_HACK(cur_offset, FWCfgState, is_version_1), > VMSTATE_UINT32_V(cur_offset, FWCfgState, 2), > VMSTATE_END_OF_LIST() > + }, > + .subsections = (const VMStateDescription*[]) { > + &vmstate_fw_cfg_dma, > + NULL, > } > }; > > @@ -593,7 +747,6 @@ static void fw_cfg_init1(DeviceState *dev) > qdev_init_nofail(dev); > > fw_cfg_add_bytes(s, FW_CFG_SIGNATURE, (char *)"QEMU", 4); > - fw_cfg_add_i32(s, FW_CFG_ID, 1); > 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)); > fw_cfg_add_i16(s, FW_CFG_NB_CPUS, (uint16_t)smp_cpus); This is called from fw_cfg_init_io() and fw_cfg_init_mem_wide(). The former is renamed to fw_cfg_init_io_dma() -- and gets a wrapper under the original name --, and sets FW_CFG_ID expliticly. The latter sets FW_CFG_ID expliticly. Okay. > @@ -605,25 +758,52 @@ static void fw_cfg_init1(DeviceState *dev) > qemu_add_machine_init_done_notifier(&s->machine_ready); > } > > -FWCfgState *fw_cfg_init_io(uint32_t iobase) > +FWCfgState *fw_cfg_init_io_dma(uint32_t iobase, uint32_t dma_iobase, > + AddressSpace *dma_as) > { > DeviceState *dev; > + FWCfgState *s; > + uint32_t version = FW_CFG_VERSION; > > dev = qdev_create(NULL, TYPE_FW_CFG_IO); > qdev_prop_set_uint32(dev, "iobase", iobase); > + qdev_prop_set_uint32(dev, "dma_iobase", dma_iobase); > + > fw_cfg_init1(dev); > + s = FW_CFG(dev); > + > + if (dma_as) { > + /* 64 bits for the address field */ > + s->dma_as = dma_as; > + s->dma_enabled = true; > + s->dma_addr = 0; > + > + version |= FW_CFG_VERSION_DMA; > + } > > - return FW_CFG(dev); > + fw_cfg_add_i32(s, FW_CFG_ID, version); > + > + return s; > } > > -FWCfgState *fw_cfg_init_mem_wide(hwaddr ctl_addr, hwaddr data_addr, > - uint32_t data_width) > +FWCfgState *fw_cfg_init_io(uint32_t iobase) > +{ > + return fw_cfg_init_io_dma(iobase, 0, NULL); > +} > + > +FWCfgState *fw_cfg_init_mem_wide(hwaddr ctl_addr, > + hwaddr data_addr, uint32_t data_width, > + hwaddr dma_addr, AddressSpace *dma_as) > { > DeviceState *dev; > SysBusDevice *sbd; > + FWCfgState *s; > + uint32_t version = FW_CFG_VERSION; > + bool dma_enabled = dma_addr && dma_as; > > dev = qdev_create(NULL, TYPE_FW_CFG_MEM); > qdev_prop_set_uint32(dev, "data_width", data_width); > + qdev_prop_set_bit(dev, "dma_enabled", dma_enabled); > > fw_cfg_init1(dev); > > @@ -631,13 +811,25 @@ FWCfgState *fw_cfg_init_mem_wide(hwaddr ctl_addr, hwaddr data_addr, > sysbus_mmio_map(sbd, 0, ctl_addr); > sysbus_mmio_map(sbd, 1, data_addr); > > - return FW_CFG(dev); > + s = FW_CFG(dev); > + > + if (dma_enabled) { > + s->dma_as = dma_as; > + s->dma_addr = 0; > + sysbus_mmio_map(sbd, 2, dma_addr); > + version |= FW_CFG_VERSION_DMA; > + } > + > + fw_cfg_add_i32(s, FW_CFG_ID, version); > + > + return s; > } > > 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); > + fw_cfg_data_mem_ops.valid.max_access_size, > + 0, NULL); > } > > > @@ -664,6 +856,7 @@ static const TypeInfo fw_cfg_info = { > > static Property fw_cfg_io_properties[] = { > DEFINE_PROP_UINT32("iobase", FWCfgIoState, iobase, -1), > + DEFINE_PROP_UINT32("dma_iobase", FWCfgIoState, dma_iobase, -1), > DEFINE_PROP_END_OF_LIST(), > }; > > @@ -673,8 +866,12 @@ static void fw_cfg_io_realize(DeviceState *dev, Error **errp) > 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); > + FW_CFG(s), "fwcfg", FW_CFG_CTL_SIZE); > sysbus_add_io(sbd, s->iobase, &s->comb_iomem); > + > + memory_region_init_io(&s->dma_iomem, OBJECT(s), &fw_cfg_dma_mem_ops, > + FW_CFG(s), "fwcfg.dma", sizeof(dma_addr_t)); > + sysbus_add_io(sbd, s->dma_iobase, &s->dma_iomem); > } (3) Hmmmm. I think this should be made conditional. sysbus_add_io() maps the region into IO port space immediately. Callers of fw_cfg_init_io() should *not* reach sysbus_add_io(); it makes no sense to map the DMA addr register at IO port 0. (And then you can omit memory_region_init_io() as well, if dma_iobase is zero.) The rest of the code looks fine to me. Again, I apologize for sucking this much at timely reviews lately. If you fix (2) and (3) above -- optionally: (1) as well --, then you'll have my R-b. If you've lost your patience, I can pick up this series. :) Thank you Laszlo > > static void fw_cfg_io_class_init(ObjectClass *klass, void *data) > @@ -695,6 +892,8 @@ static const TypeInfo fw_cfg_io_info = { > > static Property fw_cfg_mem_properties[] = { > DEFINE_PROP_UINT32("data_width", FWCfgMemState, data_width, -1), > + DEFINE_PROP_BOOL("dma_enabled", FWCfgMemState, parent_obj.dma_enabled, > + false), > DEFINE_PROP_END_OF_LIST(), > }; > > @@ -705,7 +904,7 @@ static void fw_cfg_mem_realize(DeviceState *dev, Error **errp) > 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); > + FW_CFG(s), "fwcfg.ctl", FW_CFG_CTL_SIZE); > sysbus_init_mmio(sbd, &s->ctl_iomem); > > if (s->data_width > data_ops->valid.max_access_size) { > @@ -723,6 +922,12 @@ static void fw_cfg_mem_realize(DeviceState *dev, Error **errp) > 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); > + > + if (FW_CFG(s)->dma_enabled) { > + memory_region_init_io(&s->dma_iomem, OBJECT(s), &fw_cfg_dma_mem_ops, > + FW_CFG(s), "fwcfg.dma", sizeof(dma_addr_t)); > + sysbus_init_mmio(sbd, &s->dma_iomem); > + } > } > > static void fw_cfg_mem_class_init(ObjectClass *klass, void *data) > diff --git a/include/hw/nvram/fw_cfg.h b/include/hw/nvram/fw_cfg.h > index e60d3ca..ee0cd8a 100644 > --- a/include/hw/nvram/fw_cfg.h > +++ b/include/hw/nvram/fw_cfg.h > @@ -61,6 +61,15 @@ typedef struct FWCfgFiles { > FWCfgFile f[]; > } FWCfgFiles; > > +/* Control as first field allows for different structures selected by this > + * field, which might be useful in the future > + */ > +typedef struct FWCfgDmaAccess { > + uint32_t control; > + uint32_t length; > + uint64_t address; > +} QEMU_PACKED FWCfgDmaAccess; > + > typedef void (*FWCfgCallback)(void *opaque, uint8_t *data); > typedef void (*FWCfgReadCallback)(void *opaque, uint32_t offset); > > @@ -77,10 +86,13 @@ 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_io_dma(uint32_t iobase, uint32_t dma_iobase, > + AddressSpace *dma_as); > 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_init_mem_wide(hwaddr ctl_addr, > + hwaddr data_addr, uint32_t data_width, > + hwaddr dma_addr, AddressSpace *dma_as); > > FWCfgState *fw_cfg_find(void); > > ^ permalink raw reply [flat|nested] 60+ messages in thread
* Re: [Qemu-devel] [PATCH v4 3/7] Implement fw_cfg DMA interface 2015-10-01 14:36 ` Laszlo Ersek @ 2015-10-01 15:52 ` Marc Marí 2015-10-01 17:18 ` Peter Maydell 1 sibling, 0 replies; 60+ messages in thread From: Marc Marí @ 2015-10-01 15:52 UTC (permalink / raw) To: Laszlo Ersek Cc: Drew, Stefan Hajnoczi, Gabriel L. Somlo, qemu-devel, Kevin O'Connor, Gerd Hoffmann On Thu, 1 Oct 2015 16:36:07 +0200 Laszlo Ersek <lersek@redhat.com> wrote: > This looks good to me. Thanks for addressing my v3 request. > > I have some new remarks here. I feel *really* bad for not finding them > earlier. (If you get tired of working on this series, I could pick it > up and try to shepherd it further.) I'll continue with it, don't worry. Thanks for your comments. Marc > > On 10/01/15 14:16, Marc Marí wrote: > > Based on the specifications on docs/specs/fw_cfg.txt > > > > This interface is an addon. The old interface can still be used as > > usual. > > > > Based on Gerd Hoffman's initial implementation. > > > > Signed-off-by: Marc Marí <markmb@redhat.com> > > --- > > hw/arm/virt.c | 2 +- > > hw/nvram/fw_cfg.c | 231 > > +++++++++++++++++++++++++++++++++++++++++++--- > > include/hw/nvram/fw_cfg.h | 16 +++- 3 files changed, 233 > > insertions(+), 16 deletions(-) > > > > diff --git a/hw/arm/virt.c b/hw/arm/virt.c > > index d25d6cf..7ae984f 100644 > > --- a/hw/arm/virt.c > > +++ b/hw/arm/virt.c > > @@ -683,7 +683,7 @@ static void create_fw_cfg(const VirtBoardInfo > > *vbi) hwaddr size = vbi->memmap[VIRT_FW_CFG].size; > > char *nodename; > > > > - fw_cfg_init_mem_wide(base + 8, base, 8); > > + fw_cfg_init_mem_wide(base + 8, base, 8, 0, NULL); > > > > nodename = g_strdup_printf("/fw-cfg@%" PRIx64, base); > > qemu_fdt_add_subnode(vbi->fdt, nodename); > > diff --git a/hw/nvram/fw_cfg.c b/hw/nvram/fw_cfg.c > > index 658f8c4..59933b3 100644 > > --- a/hw/nvram/fw_cfg.c > > +++ b/hw/nvram/fw_cfg.c > > @@ -23,6 +23,7 @@ > > */ > > #include "hw/hw.h" > > #include "sysemu/sysemu.h" > > +#include "sysemu/dma.h" > > #include "hw/isa/isa.h" > > #include "hw/nvram/fw_cfg.h" > > #include "hw/sysbus.h" > > @@ -30,7 +31,7 @@ > > #include "qemu/error-report.h" > > #include "qemu/config-file.h" > > > > -#define FW_CFG_SIZE 2 > > +#define FW_CFG_CTL_SIZE 2 > > #define FW_CFG_NAME "fw_cfg" > > #define FW_CFG_PATH "/machine/" FW_CFG_NAME > > > > @@ -42,6 +43,16 @@ > > #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) > > +/* FW_CFG_VERSION bits */ > > +#define FW_CFG_VERSION 0x01 > > +#define FW_CFG_VERSION_DMA 0x02 > > + > > +/* FW_CFG_DMA_CONTROL bits */ > > +#define FW_CFG_DMA_CTL_ERROR 0x01 > > +#define FW_CFG_DMA_CTL_READ 0x02 > > +#define FW_CFG_DMA_CTL_SKIP 0x04 > > +#define FW_CFG_DMA_CTL_SELECT 0x08 > > + > > typedef struct FWCfgEntry { > > uint32_t len; > > uint8_t *data; > > @@ -59,6 +70,10 @@ struct FWCfgState { > > uint16_t cur_entry; > > uint32_t cur_offset; > > Notifier machine_ready; > > + > > + bool dma_enabled; > > + AddressSpace *dma_as; > > + dma_addr_t dma_addr; > > }; > > > > struct FWCfgIoState { > > @@ -66,8 +81,8 @@ struct FWCfgIoState { > > FWCfgState parent_obj; > > /*< public >*/ > > > > - MemoryRegion comb_iomem; > > - uint32_t iobase; > > + MemoryRegion comb_iomem, dma_iomem; > > + uint32_t iobase, dma_iobase; > > }; > > > > struct FWCfgMemState { > > @@ -75,7 +90,7 @@ struct FWCfgMemState { > > FWCfgState parent_obj; > > /*< public >*/ > > > > - MemoryRegion ctl_iomem, data_iomem; > > + MemoryRegion ctl_iomem, data_iomem, dma_iomem; > > uint32_t data_width; > > MemoryRegionOps wide_data_ops; > > }; > > (1) I *think* the new "dma_iomem" field, of type MemoryRegion, could > be moved up to the parent struct FWCfgEntry, from both FWCfgMemState > and FWCfgIoState. (And the references in the rest of the code could > be updated.) > > ( > > Independently, some loud thinking, mostly for myself: I've always been > surprised by the difference between (a) FWCfgIoState *carrying* > "dma_iobase" as a field -- and a property! --, and (b) FWCfgMemState > *not* carrying the same as a field -- nor as a property. > > I think I finally understand this difference now. It is all rooted in > the difference between the internal APIs sysbus_add_io() and > sysbus_init_mmio(). Both of these are called from the device realize > functions, but the first (sysbus_add_io()) wants the IO port address > at once, whereas the second (sysbus_init_mmio()) doesn't want the > address -- the actual mapping (sysbus_mmio_map()) is delayed to board > code; the device code doesn't want to be aware of it. > > And this ripples to the top. Because sysbus_add_io() wants the IO port > address, we must pass that address to the device realize function. And > for that, we need a device property -- "dma_iobase". This is not new, > it just follows the example of the preexistent "iobase" field / > property. > > Whereas, in the sysbus_init_mmio() case, we can keep the MMIO address > private to the board code; the realize function need not know the > address. However, the realize function does need to know the *fact* > that we're going to do DMA. Given that we must maintain this fact (in > "FWCfgState.dma_enabled") anyway, for other -- e.g. migration > subsection -- purposes as well, it makes sense to expose that same > field of the parent struct as a property, so we can set it in the > memory mapped case *before* the realize function looks at it. > > I feel better now, thanks for listening. > > ) > > Then, > > > @@ -292,6 +307,119 @@ static void fw_cfg_data_mem_write(void > > *opaque, hwaddr addr, } while (i); > > } > > > > +static void fw_cfg_dma_transfer(FWCfgState *s) > > +{ > > + dma_addr_t len; > > + FWCfgDmaAccess dma; > > + int arch; > > + FWCfgEntry *e; > > + int read; > > + dma_addr_t dma_addr; > > + > > + /* Reset the address before the next access */ > > + dma_addr = s->dma_addr; > > + s->dma_addr = 0; > > + > > + dma.address = ldq_be_dma(s->dma_as, > > + dma_addr + offsetof(FWCfgDmaAccess, > > address)); > > + dma.length = ldl_be_dma(s->dma_as, > > + dma_addr + offsetof(FWCfgDmaAccess, > > length)); > > + dma.control = ldl_be_dma(s->dma_as, > > + dma_addr + offsetof(FWCfgDmaAccess, > > control)); + > > + if (dma.control & FW_CFG_DMA_CTL_SELECT) { > > + fw_cfg_select(s, dma.control >> 16); > > + } > > + > > + arch = !!(s->cur_entry & FW_CFG_ARCH_LOCAL); > > + e = &s->entries[arch][s->cur_entry & FW_CFG_ENTRY_MASK]; > > + > > + if (dma.control & FW_CFG_DMA_CTL_READ) { > > + read = 1; > > + } else if (dma.control & FW_CFG_DMA_CTL_SKIP) { > > + read = 0; > > + } else { > > + dma.length = 0; > > I can see you addressed Kevin's comment here. > > > > + } > > + > > + dma.control = 0; > > + > > + while (dma.length > 0 && !(dma.control & > > FW_CFG_DMA_CTL_ERROR)) { > > + if (s->cur_entry == FW_CFG_INVALID || !e->data || > > + s->cur_offset >= e->len) { > > + len = dma.length; > > + > > + /* If the access is not a read access, it will be a > > skip access, > > + * tested before. > > + */ > > + if (read) { > > + if (dma_memory_set(s->dma_as, dma.address, 0, > > len)) { > > + dma.control |= FW_CFG_DMA_CTL_ERROR; > > + } > > + } > > + > > + } else { > > + if (dma.length <= (e->len - s->cur_offset)) { > > + len = dma.length; > > + } else { > > + len = (e->len - s->cur_offset); > > + } > > + > > + if (e->read_callback) { > > + e->read_callback(e->callback_opaque, > > s->cur_offset); > > + } > > + > > + /* If the access is not a read access, it will be a > > skip access, > > + * tested before. > > + */ > > + if (read) { > > + if (dma_memory_write(s->dma_as, dma.address, > > + &e->data[s->cur_offset], len)) > > { > > + dma.control |= FW_CFG_DMA_CTL_ERROR; > > + } > > + } > > + > > + s->cur_offset += len; > > + } > > + > > + dma.address += len; > > + dma.length -= len; > > + > > + } > > + > > + stl_be_dma(s->dma_as, dma_addr + offsetof(FWCfgDmaAccess, > > control), > > + dma.control); > > + > > + trace_fw_cfg_read(s, 0); > > +} > > Seems OK to me. > > > + > > +static void fw_cfg_dma_mem_write(void *opaque, hwaddr addr, > > + uint64_t value, unsigned size) > > +{ > > + FWCfgState *s = opaque; > > + > > + if (size == 4) { > > + if (addr == 0) { > > + /* FWCfgDmaAccess high address */ > > + s->dma_addr = value << 32; > > + } else if (addr == 4) { > > + /* FWCfgDmaAccess low address */ > > + s->dma_addr |= value; > > + fw_cfg_dma_transfer(s); > > + } > > + } else if (size == 8 && addr == 0) { > > + s->dma_addr = value; > > + fw_cfg_dma_transfer(s); > > + } > > +} > > Seems to match the zeroing of s->dma_addr in fw_cfg_dma_transfer(). > Good. > > > + > > +static bool fw_cfg_dma_mem_valid(void *opaque, hwaddr addr, > > + unsigned size, bool is_write) > > +{ > > + return is_write && ((size == 4 && (addr == 0 || addr == 4)) || > > + (size == 8 && addr == 0)); > > +} > > + > > static bool fw_cfg_data_mem_valid(void *opaque, hwaddr addr, > > unsigned size, bool is_write) > > { > > @@ -359,6 +487,12 @@ static const MemoryRegionOps > > fw_cfg_comb_mem_ops = { .valid.accepts = fw_cfg_comb_valid, > > }; > > > > +static const MemoryRegionOps fw_cfg_dma_mem_ops = { > > + .write = fw_cfg_dma_mem_write, > > + .endianness = DEVICE_BIG_ENDIAN, > > + .valid.accepts = fw_cfg_dma_mem_valid, > > +}; > > (2) Okay. This is somewhat important, and *completely* non-intuitive, > unfortunately. > > Without setting *both* > > .valid.max_access_size = 8, > .impl.max_access_size = 8, > > here, the memory subsystem will split up all 8-byte wide accesses > (from the guest side) to two 4-byte wide calls to > fw_cfg_dma_mem_write()). > > Those calls do satisfy the ordering logic in fw_cfg_dma_mem_write(), > but nonetheless, the lack of the above setting makes the following > code in fw_cfg_dma_mem_write() dead: > > > + } else if (size == 8 && addr == 0) { > > + s->dma_addr = value; > > + fw_cfg_dma_transfer(s); > > + } > > (I verified this claim with gdb on aarch64.) > > So, please initialize both of the above fields to 8. > > > + > > static void fw_cfg_reset(DeviceState *d) > > { > > FWCfgState *s = FW_CFG(d); > > @@ -399,6 +533,22 @@ static bool is_version_1(void *opaque, int > > version_id) return version_id == 1; > > } > > > > +static bool fw_cfg_dma_enabled(void *opaque) > > +{ > > + FWCfgState *s = opaque; > > + > > + return s->dma_enabled; > > +} > > + > > +static VMStateDescription vmstate_fw_cfg_dma = { > > + .name = "fw_cfg/dma", > > + .needed = fw_cfg_dma_enabled, > > + .fields = (VMStateField[]) { > > + VMSTATE_UINT64(dma_addr, FWCfgState), > > + VMSTATE_END_OF_LIST() > > + }, > > +}; > > Looks good to me. All fields that come from the command line (ie. > management layer) need not / must not be part of the migration stream. > And all data that is programmed by the guest, must. Here, "dma_addr" > is the only such item. Okay. > > > + > > static const VMStateDescription vmstate_fw_cfg = { > > .name = "fw_cfg", > > .version_id = 2, > > @@ -408,6 +558,10 @@ static const VMStateDescription vmstate_fw_cfg > > = { VMSTATE_UINT16_HACK(cur_offset, FWCfgState, is_version_1), > > VMSTATE_UINT32_V(cur_offset, FWCfgState, 2), > > VMSTATE_END_OF_LIST() > > + }, > > + .subsections = (const VMStateDescription*[]) { > > + &vmstate_fw_cfg_dma, > > + NULL, > > } > > }; > > > > @@ -593,7 +747,6 @@ static void fw_cfg_init1(DeviceState *dev) > > qdev_init_nofail(dev); > > > > fw_cfg_add_bytes(s, FW_CFG_SIGNATURE, (char *)"QEMU", 4); > > - fw_cfg_add_i32(s, FW_CFG_ID, 1); > > 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)); fw_cfg_add_i16(s, FW_CFG_NB_CPUS, > > (uint16_t)smp_cpus); > > This is called from fw_cfg_init_io() and fw_cfg_init_mem_wide(). > > The former is renamed to fw_cfg_init_io_dma() -- and gets a wrapper > under the original name --, and sets FW_CFG_ID expliticly. > > The latter sets FW_CFG_ID expliticly. > > Okay. > > > > @@ -605,25 +758,52 @@ static void fw_cfg_init1(DeviceState *dev) > > qemu_add_machine_init_done_notifier(&s->machine_ready); > > } > > > > -FWCfgState *fw_cfg_init_io(uint32_t iobase) > > +FWCfgState *fw_cfg_init_io_dma(uint32_t iobase, uint32_t > > dma_iobase, > > + AddressSpace *dma_as) > > { > > DeviceState *dev; > > + FWCfgState *s; > > + uint32_t version = FW_CFG_VERSION; > > > > dev = qdev_create(NULL, TYPE_FW_CFG_IO); > > qdev_prop_set_uint32(dev, "iobase", iobase); > > + qdev_prop_set_uint32(dev, "dma_iobase", dma_iobase); > > + > > fw_cfg_init1(dev); > > + s = FW_CFG(dev); > > + > > + if (dma_as) { > > + /* 64 bits for the address field */ > > + s->dma_as = dma_as; > > + s->dma_enabled = true; > > + s->dma_addr = 0; > > + > > + version |= FW_CFG_VERSION_DMA; > > + } > > > > - return FW_CFG(dev); > > + fw_cfg_add_i32(s, FW_CFG_ID, version); > > + > > + return s; > > } > > > > -FWCfgState *fw_cfg_init_mem_wide(hwaddr ctl_addr, hwaddr data_addr, > > - uint32_t data_width) > > +FWCfgState *fw_cfg_init_io(uint32_t iobase) > > +{ > > + return fw_cfg_init_io_dma(iobase, 0, NULL); > > +} > > + > > +FWCfgState *fw_cfg_init_mem_wide(hwaddr ctl_addr, > > + hwaddr data_addr, uint32_t > > data_width, > > + hwaddr dma_addr, AddressSpace > > *dma_as) { > > DeviceState *dev; > > SysBusDevice *sbd; > > + FWCfgState *s; > > + uint32_t version = FW_CFG_VERSION; > > + bool dma_enabled = dma_addr && dma_as; > > > > dev = qdev_create(NULL, TYPE_FW_CFG_MEM); > > qdev_prop_set_uint32(dev, "data_width", data_width); > > + qdev_prop_set_bit(dev, "dma_enabled", dma_enabled); > > > > fw_cfg_init1(dev); > > > > @@ -631,13 +811,25 @@ FWCfgState *fw_cfg_init_mem_wide(hwaddr > > ctl_addr, hwaddr data_addr, sysbus_mmio_map(sbd, 0, ctl_addr); > > sysbus_mmio_map(sbd, 1, data_addr); > > > > - return FW_CFG(dev); > > + s = FW_CFG(dev); > > + > > + if (dma_enabled) { > > + s->dma_as = dma_as; > > + s->dma_addr = 0; > > + sysbus_mmio_map(sbd, 2, dma_addr); > > + version |= FW_CFG_VERSION_DMA; > > + } > > + > > + fw_cfg_add_i32(s, FW_CFG_ID, version); > > + > > + return s; > > } > > > > 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); > > + > > fw_cfg_data_mem_ops.valid.max_access_size, > > + 0, NULL); > > } > > > > > > @@ -664,6 +856,7 @@ static const TypeInfo fw_cfg_info = { > > > > static Property fw_cfg_io_properties[] = { > > DEFINE_PROP_UINT32("iobase", FWCfgIoState, iobase, -1), > > + DEFINE_PROP_UINT32("dma_iobase", FWCfgIoState, dma_iobase, -1), > > DEFINE_PROP_END_OF_LIST(), > > }; > > > > @@ -673,8 +866,12 @@ static void fw_cfg_io_realize(DeviceState > > *dev, Error **errp) 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); > > + FW_CFG(s), "fwcfg", FW_CFG_CTL_SIZE); > > sysbus_add_io(sbd, s->iobase, &s->comb_iomem); > > + > > + memory_region_init_io(&s->dma_iomem, OBJECT(s), > > &fw_cfg_dma_mem_ops, > > + FW_CFG(s), "fwcfg.dma", > > sizeof(dma_addr_t)); > > + sysbus_add_io(sbd, s->dma_iobase, &s->dma_iomem); > > } > > (3) Hmmmm. I think this should be made conditional. sysbus_add_io() > maps the region into IO port space immediately. Callers of > fw_cfg_init_io() should *not* reach sysbus_add_io(); it makes no > sense to map the DMA addr register at IO port 0. > > (And then you can omit memory_region_init_io() as well, if dma_iobase > is zero.) > > The rest of the code looks fine to me. > > Again, I apologize for sucking this much at timely reviews lately. If > you fix (2) and (3) above -- optionally: (1) as well --, then you'll > have my R-b. > > If you've lost your patience, I can pick up this series. :) > > Thank you > Laszlo > > > > > > static void fw_cfg_io_class_init(ObjectClass *klass, void *data) > > @@ -695,6 +892,8 @@ static const TypeInfo fw_cfg_io_info = { > > > > static Property fw_cfg_mem_properties[] = { > > DEFINE_PROP_UINT32("data_width", FWCfgMemState, data_width, > > -1), > > + DEFINE_PROP_BOOL("dma_enabled", FWCfgMemState, > > parent_obj.dma_enabled, > > + false), > > DEFINE_PROP_END_OF_LIST(), > > }; > > > > @@ -705,7 +904,7 @@ static void fw_cfg_mem_realize(DeviceState > > *dev, Error **errp) 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); > > + FW_CFG(s), "fwcfg.ctl", FW_CFG_CTL_SIZE); > > sysbus_init_mmio(sbd, &s->ctl_iomem); > > > > if (s->data_width > data_ops->valid.max_access_size) { > > @@ -723,6 +922,12 @@ static void fw_cfg_mem_realize(DeviceState > > *dev, Error **errp) 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); + > > + if (FW_CFG(s)->dma_enabled) { > > + memory_region_init_io(&s->dma_iomem, OBJECT(s), > > &fw_cfg_dma_mem_ops, > > + FW_CFG(s), "fwcfg.dma", > > sizeof(dma_addr_t)); > > + sysbus_init_mmio(sbd, &s->dma_iomem); > > + } > > } > > > > static void fw_cfg_mem_class_init(ObjectClass *klass, void *data) > > diff --git a/include/hw/nvram/fw_cfg.h b/include/hw/nvram/fw_cfg.h > > index e60d3ca..ee0cd8a 100644 > > --- a/include/hw/nvram/fw_cfg.h > > +++ b/include/hw/nvram/fw_cfg.h > > @@ -61,6 +61,15 @@ typedef struct FWCfgFiles { > > FWCfgFile f[]; > > } FWCfgFiles; > > > > +/* Control as first field allows for different structures selected > > by this > > + * field, which might be useful in the future > > + */ > > +typedef struct FWCfgDmaAccess { > > + uint32_t control; > > + uint32_t length; > > + uint64_t address; > > +} QEMU_PACKED FWCfgDmaAccess; > > + > > typedef void (*FWCfgCallback)(void *opaque, uint8_t *data); > > typedef void (*FWCfgReadCallback)(void *opaque, uint32_t offset); > > > > @@ -77,10 +86,13 @@ 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_io_dma(uint32_t iobase, uint32_t > > dma_iobase, > > + AddressSpace *dma_as); > > 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_init_mem_wide(hwaddr ctl_addr, > > + hwaddr data_addr, uint32_t > > data_width, > > + hwaddr dma_addr, AddressSpace > > *dma_as); > > FWCfgState *fw_cfg_find(void); > > > > > ^ permalink raw reply [flat|nested] 60+ messages in thread
* Re: [Qemu-devel] [PATCH v4 3/7] Implement fw_cfg DMA interface 2015-10-01 14:36 ` Laszlo Ersek 2015-10-01 15:52 ` Marc Marí @ 2015-10-01 17:18 ` Peter Maydell 2015-10-01 19:20 ` Laszlo Ersek 1 sibling, 1 reply; 60+ messages in thread From: Peter Maydell @ 2015-10-01 17:18 UTC (permalink / raw) To: Laszlo Ersek Cc: Drew, Stefan Hajnoczi, Gabriel L. Somlo, QEMU Developers, Kevin O'Connor, Gerd Hoffmann, Marc Marí On 1 October 2015 at 15:36, Laszlo Ersek <lersek@redhat.com> wrote: > I think I finally understand this difference now. It is all rooted in > the difference between the internal APIs sysbus_add_io() and > sysbus_init_mmio(). Both of these are called from the device realize > functions, but the first (sysbus_add_io()) wants the IO port address at > once, whereas the second (sysbus_init_mmio()) doesn't want the address > -- the actual mapping (sysbus_mmio_map()) is delayed to board code; the > device code doesn't want to be aware of it. Yes. The sysbus_add_io() API is firmly wedded to the x86 I/O port concept and to the idea that devices are at fixed I/O port addresses which don't depend on what board they're in (because the few non-x86 systems that set up some kind of "IO port" abstraction are generally doing it to look more x86-like). That said, sysbus_add_io() is one of those odd functions which we use half a dozen times in the whole codebase and which leaves me wondering if it ought to be refactored to work differently (eg split into "declare IO ports" and "map IO ports into IO space" like the mmio functions)... thanks -- PMM ^ permalink raw reply [flat|nested] 60+ messages in thread
* Re: [Qemu-devel] [PATCH v4 3/7] Implement fw_cfg DMA interface 2015-10-01 17:18 ` Peter Maydell @ 2015-10-01 19:20 ` Laszlo Ersek 0 siblings, 0 replies; 60+ messages in thread From: Laszlo Ersek @ 2015-10-01 19:20 UTC (permalink / raw) To: Peter Maydell Cc: Drew, Stefan Hajnoczi, Gabriel L. Somlo, QEMU Developers, Kevin O'Connor, Gerd Hoffmann, Marc Marí On 10/01/15 19:18, Peter Maydell wrote: > On 1 October 2015 at 15:36, Laszlo Ersek <lersek@redhat.com> wrote: >> I think I finally understand this difference now. It is all rooted in >> the difference between the internal APIs sysbus_add_io() and >> sysbus_init_mmio(). Both of these are called from the device realize >> functions, but the first (sysbus_add_io()) wants the IO port address at >> once, whereas the second (sysbus_init_mmio()) doesn't want the address >> -- the actual mapping (sysbus_mmio_map()) is delayed to board code; the >> device code doesn't want to be aware of it. > > Yes. The sysbus_add_io() API is firmly wedded to the x86 I/O port > concept and to the idea that devices are at fixed I/O port addresses > which don't depend on what board they're in (because the few > non-x86 systems that set up some kind of "IO port" abstraction > are generally doing it to look more x86-like). > > That said, sysbus_add_io() is one of those odd functions which > we use half a dozen times in the whole codebase and which leaves > me wondering if it ought to be refactored to work differently > (eg split into "declare IO ports" and "map IO ports into IO space" > like the mmio functions)... I had the same idea looming in the back of my mind, but I wouldn't know how to attack the refactoring (plus I wouldn't want to delay Marc's work with it -- after all the function is not being introduced by this series), so I didn't bring it up. :P Thanks! Laszlo ^ permalink raw reply [flat|nested] 60+ messages in thread
* Re: [Qemu-devel] [PATCH v4 3/7] Implement fw_cfg DMA interface 2015-10-01 12:16 ` [Qemu-devel] [PATCH v4 3/7] Implement fw_cfg DMA interface Marc Marí 2015-10-01 14:36 ` Laszlo Ersek @ 2015-10-06 14:44 ` Stefan Hajnoczi 2015-10-06 14:53 ` Peter Maydell 2015-10-06 14:54 ` Marc Marí 1 sibling, 2 replies; 60+ messages in thread From: Stefan Hajnoczi @ 2015-10-06 14:44 UTC (permalink / raw) To: Marc Marí Cc: Drew, Gabriel L. Somlo, qemu-devel, Kevin O'Connor, Gerd Hoffmann, Laszlo On Thu, Oct 01, 2015 at 02:16:55PM +0200, Marc Marí wrote: > @@ -292,6 +307,119 @@ static void fw_cfg_data_mem_write(void *opaque, hwaddr addr, > } while (i); > } > > +static void fw_cfg_dma_transfer(FWCfgState *s) > +{ > + dma_addr_t len; > + FWCfgDmaAccess dma; > + int arch; > + FWCfgEntry *e; > + int read; > + dma_addr_t dma_addr; > + > + /* Reset the address before the next access */ > + dma_addr = s->dma_addr; > + s->dma_addr = 0; > + > + dma.address = ldq_be_dma(s->dma_as, > + dma_addr + offsetof(FWCfgDmaAccess, address)); > + dma.length = ldl_be_dma(s->dma_as, > + dma_addr + offsetof(FWCfgDmaAccess, length)); > + dma.control = ldl_be_dma(s->dma_as, > + dma_addr + offsetof(FWCfgDmaAccess, control)); ldq_be_dma() doesn't report errors. If dma_addr is invalid the return value could be anything. Memory corruption inside the guest is possible if the address/length/control values happen to cause a memory read operation! Instead, please use: if (dma_memory_read(s->dma_as, dma_addr, &dma, sizeof(dma))) { stl_be_dma(s->dma_as, dma_addr + offsetof(FWCfgDmaAccess, control), FW_CFG_DMA_CTL_ERROR); return; } dma.address = be64_to_cpu(dma.address); dma.length = be32_to_cpu(dma.length); dma.control = be32_to_cpu(dma.control); > + > + if (dma.control & FW_CFG_DMA_CTL_SELECT) { > + fw_cfg_select(s, dma.control >> 16); > + } > + > + arch = !!(s->cur_entry & FW_CFG_ARCH_LOCAL); > + e = &s->entries[arch][s->cur_entry & FW_CFG_ENTRY_MASK]; > + > + if (dma.control & FW_CFG_DMA_CTL_READ) { > + read = 1; > + } else if (dma.control & FW_CFG_DMA_CTL_SKIP) { > + read = 0; > + } else { > + dma.length = 0; > + } > + > + dma.control = 0; > + > + while (dma.length > 0 && !(dma.control & FW_CFG_DMA_CTL_ERROR)) { > + if (s->cur_entry == FW_CFG_INVALID || !e->data || > + s->cur_offset >= e->len) { > + len = dma.length; > + > + /* If the access is not a read access, it will be a skip access, > + * tested before. > + */ > + if (read) { > + if (dma_memory_set(s->dma_as, dma.address, 0, len)) { > + dma.control |= FW_CFG_DMA_CTL_ERROR; > + } > + } > + > + } else { > + if (dma.length <= (e->len - s->cur_offset)) { > + len = dma.length; > + } else { > + len = (e->len - s->cur_offset); > + } > + > + if (e->read_callback) { > + e->read_callback(e->callback_opaque, s->cur_offset); > + } > + > + /* If the access is not a read access, it will be a skip access, > + * tested before. > + */ > + if (read) { > + if (dma_memory_write(s->dma_as, dma.address, > + &e->data[s->cur_offset], len)) { > + dma.control |= FW_CFG_DMA_CTL_ERROR; > + } > + } > + > + s->cur_offset += len; > + } > + > + dma.address += len; > + dma.length -= len; I thought these fields are written back to guest memory? For example, so the guest knows how many bytes were read before the error occurred. ^ permalink raw reply [flat|nested] 60+ messages in thread
* Re: [Qemu-devel] [PATCH v4 3/7] Implement fw_cfg DMA interface 2015-10-06 14:44 ` Stefan Hajnoczi @ 2015-10-06 14:53 ` Peter Maydell 2015-10-08 9:07 ` Stefan Hajnoczi 2015-10-06 14:54 ` Marc Marí 1 sibling, 1 reply; 60+ messages in thread From: Peter Maydell @ 2015-10-06 14:53 UTC (permalink / raw) To: Stefan Hajnoczi Cc: Drew, Gabriel L. Somlo, QEMU Developers, Kevin O'Connor, Gerd Hoffmann, Marc Marí, Laszlo On 6 October 2015 at 15:44, Stefan Hajnoczi <stefanha@gmail.com> wrote: > On Thu, Oct 01, 2015 at 02:16:55PM +0200, Marc Marí wrote: >> @@ -292,6 +307,119 @@ static void fw_cfg_data_mem_write(void *opaque, hwaddr addr, >> } while (i); >> } >> >> +static void fw_cfg_dma_transfer(FWCfgState *s) >> +{ >> + dma_addr_t len; >> + FWCfgDmaAccess dma; >> + int arch; >> + FWCfgEntry *e; >> + int read; >> + dma_addr_t dma_addr; >> + >> + /* Reset the address before the next access */ >> + dma_addr = s->dma_addr; >> + s->dma_addr = 0; >> + >> + dma.address = ldq_be_dma(s->dma_as, >> + dma_addr + offsetof(FWCfgDmaAccess, address)); >> + dma.length = ldl_be_dma(s->dma_as, >> + dma_addr + offsetof(FWCfgDmaAccess, length)); >> + dma.control = ldl_be_dma(s->dma_as, >> + dma_addr + offsetof(FWCfgDmaAccess, control)); > > ldq_be_dma() doesn't report errors. If dma_addr is invalid the return > value could be anything. Memory corruption inside the guest is possible > if the address/length/control values happen to cause a memory read > operation! We discussed this in a previous revision. IMHO if the guest has passed us a bogus dma_addr it should expect memory corruption. We only need to be sure we don't allow a VM escape. > Instead, please use: > > if (dma_memory_read(s->dma_as, dma_addr, &dma, sizeof(dma))) { > stl_be_dma(s->dma_as, dma_addr + offsetof(FWCfgDmaAccess, control), > FW_CFG_DMA_CTL_ERROR); If the guest handed us a bad dma_addr then this write will also be bogus and could corrupt the guest's memory. thanks -- PMM ^ permalink raw reply [flat|nested] 60+ messages in thread
* Re: [Qemu-devel] [PATCH v4 3/7] Implement fw_cfg DMA interface 2015-10-06 14:53 ` Peter Maydell @ 2015-10-08 9:07 ` Stefan Hajnoczi 2015-10-08 10:01 ` Marc Marí 0 siblings, 1 reply; 60+ messages in thread From: Stefan Hajnoczi @ 2015-10-08 9:07 UTC (permalink / raw) To: Peter Maydell Cc: Drew, Gabriel L. Somlo, QEMU Developers, Kevin O'Connor, Gerd Hoffmann, Marc Marí, Laszlo On Tue, Oct 06, 2015 at 03:53:33PM +0100, Peter Maydell wrote: > On 6 October 2015 at 15:44, Stefan Hajnoczi <stefanha@gmail.com> wrote: > > On Thu, Oct 01, 2015 at 02:16:55PM +0200, Marc Marí wrote: > >> @@ -292,6 +307,119 @@ static void fw_cfg_data_mem_write(void *opaque, hwaddr addr, > >> } while (i); > >> } > >> > >> +static void fw_cfg_dma_transfer(FWCfgState *s) > >> +{ > >> + dma_addr_t len; > >> + FWCfgDmaAccess dma; > >> + int arch; > >> + FWCfgEntry *e; > >> + int read; > >> + dma_addr_t dma_addr; > >> + > >> + /* Reset the address before the next access */ > >> + dma_addr = s->dma_addr; > >> + s->dma_addr = 0; > >> + > >> + dma.address = ldq_be_dma(s->dma_as, > >> + dma_addr + offsetof(FWCfgDmaAccess, address)); > >> + dma.length = ldl_be_dma(s->dma_as, > >> + dma_addr + offsetof(FWCfgDmaAccess, length)); > >> + dma.control = ldl_be_dma(s->dma_as, > >> + dma_addr + offsetof(FWCfgDmaAccess, control)); > > > > ldq_be_dma() doesn't report errors. If dma_addr is invalid the return > > value could be anything. Memory corruption inside the guest is possible > > if the address/length/control values happen to cause a memory read > > operation! > > We discussed this in a previous revision. IMHO if the guest has > passed us a bogus dma_addr it should expect memory corruption. > We only need to be sure we don't allow a VM escape. Even if the guest-visible behavior doesn't matter, Valgrind won't like this. ldq_be_dma() reads from uninitialized stack memory: #define DEFINE_LDST_DMA(_lname, _sname, _bits, _end) \ static inline uint##_bits##_t ld##_lname##_##_end##_dma(AddressSpace *as, \ dma_addr_t addr) \ { \ uint##_bits##_t val; \ dma_memory_read(as, addr, &val, (_bits) / 8); \ return _end##_bits##_to_cpu(val); \ } Bad QEMU, bad userspace process :). I think we really need to check the error and at least return early. > > Instead, please use: > > > > if (dma_memory_read(s->dma_as, dma_addr, &dma, sizeof(dma))) { > > stl_be_dma(s->dma_as, dma_addr + offsetof(FWCfgDmaAccess, control), > > FW_CFG_DMA_CTL_ERROR); > > If the guest handed us a bad dma_addr then this write will also > be bogus and could corrupt the guest's memory. That's fine because it's not a random address - it's the address that the guest gave us. Stefan ^ permalink raw reply [flat|nested] 60+ messages in thread
* Re: [Qemu-devel] [PATCH v4 3/7] Implement fw_cfg DMA interface 2015-10-08 9:07 ` Stefan Hajnoczi @ 2015-10-08 10:01 ` Marc Marí 0 siblings, 0 replies; 60+ messages in thread From: Marc Marí @ 2015-10-08 10:01 UTC (permalink / raw) To: Stefan Hajnoczi Cc: Peter Maydell, Drew, Gabriel L. Somlo, QEMU Developers, Kevin O'Connor, Gerd Hoffmann, Laszlo On Thu, 8 Oct 2015 10:07:34 +0100 Stefan Hajnoczi <stefanha@gmail.com> wrote: > On Tue, Oct 06, 2015 at 03:53:33PM +0100, Peter Maydell wrote: > > On 6 October 2015 at 15:44, Stefan Hajnoczi <stefanha@gmail.com> > > wrote: > > > On Thu, Oct 01, 2015 at 02:16:55PM +0200, Marc Marí wrote: > > >> @@ -292,6 +307,119 @@ static void fw_cfg_data_mem_write(void > > >> *opaque, hwaddr addr, } while (i); > > >> } > > >> > > >> +static void fw_cfg_dma_transfer(FWCfgState *s) > > >> +{ > > >> + dma_addr_t len; > > >> + FWCfgDmaAccess dma; > > >> + int arch; > > >> + FWCfgEntry *e; > > >> + int read; > > >> + dma_addr_t dma_addr; > > >> + > > >> + /* Reset the address before the next access */ > > >> + dma_addr = s->dma_addr; > > >> + s->dma_addr = 0; > > >> + > > >> + dma.address = ldq_be_dma(s->dma_as, > > >> + dma_addr + offsetof(FWCfgDmaAccess, > > >> address)); > > >> + dma.length = ldl_be_dma(s->dma_as, > > >> + dma_addr + offsetof(FWCfgDmaAccess, > > >> length)); > > >> + dma.control = ldl_be_dma(s->dma_as, > > >> + dma_addr + offsetof(FWCfgDmaAccess, > > >> control)); > > > > > > ldq_be_dma() doesn't report errors. If dma_addr is invalid the > > > return value could be anything. Memory corruption inside the > > > guest is possible if the address/length/control values happen to > > > cause a memory read operation! > > > > We discussed this in a previous revision. IMHO if the guest has > > passed us a bogus dma_addr it should expect memory corruption. > > We only need to be sure we don't allow a VM escape. > > Even if the guest-visible behavior doesn't matter, Valgrind won't like > this. ldq_be_dma() reads from uninitialized stack memory: > > #define DEFINE_LDST_DMA(_lname, _sname, _bits, _end) \ > static inline uint##_bits##_t > ld##_lname##_##_end##_dma(AddressSpace *as, \ dma_addr_t addr) \ > { \ > uint##_bits##_t > val; \ dma_memory_read(as, > addr, &val, (_bits) / 8); \ return > _end##_bits##_to_cpu(val); \ } > > Bad QEMU, bad userspace process :). > > I think we really need to check the error and at least return early. It doesn't hurt to check the error. I'll add it. Thanks Marc > > > Instead, please use: > > > > > > if (dma_memory_read(s->dma_as, dma_addr, &dma, sizeof(dma))) { > > > stl_be_dma(s->dma_as, dma_addr + offsetof(FWCfgDmaAccess, > > > control), FW_CFG_DMA_CTL_ERROR); > > > > If the guest handed us a bad dma_addr then this write will also > > be bogus and could corrupt the guest's memory. > > That's fine because it's not a random address - it's the address that > the guest gave us. > > Stefan ^ permalink raw reply [flat|nested] 60+ messages in thread
* Re: [Qemu-devel] [PATCH v4 3/7] Implement fw_cfg DMA interface 2015-10-06 14:44 ` Stefan Hajnoczi 2015-10-06 14:53 ` Peter Maydell @ 2015-10-06 14:54 ` Marc Marí 1 sibling, 0 replies; 60+ messages in thread From: Marc Marí @ 2015-10-06 14:54 UTC (permalink / raw) To: Stefan Hajnoczi Cc: Drew, Gabriel L. Somlo, qemu-devel, Kevin O'Connor, Gerd Hoffmann, Laszlo On Tue, 6 Oct 2015 15:44:53 +0100 Stefan Hajnoczi <stefanha@gmail.com> wrote: > On Thu, Oct 01, 2015 at 02:16:55PM +0200, Marc Marí wrote: > > @@ -292,6 +307,119 @@ static void fw_cfg_data_mem_write(void > > *opaque, hwaddr addr, } while (i); > > } > > > > +static void fw_cfg_dma_transfer(FWCfgState *s) > > +{ > > + dma_addr_t len; > > + FWCfgDmaAccess dma; > > + int arch; > > + FWCfgEntry *e; > > + int read; > > + dma_addr_t dma_addr; > > + > > + /* Reset the address before the next access */ > > + dma_addr = s->dma_addr; > > + s->dma_addr = 0; > > + > > + dma.address = ldq_be_dma(s->dma_as, > > + dma_addr + offsetof(FWCfgDmaAccess, > > address)); > > + dma.length = ldl_be_dma(s->dma_as, > > + dma_addr + offsetof(FWCfgDmaAccess, > > length)); > > + dma.control = ldl_be_dma(s->dma_as, > > + dma_addr + offsetof(FWCfgDmaAccess, > > control)); > > ldq_be_dma() doesn't report errors. If dma_addr is invalid the return > value could be anything. Memory corruption inside the guest is > possible if the address/length/control values happen to cause a > memory read operation! > > Instead, please use: > > if (dma_memory_read(s->dma_as, dma_addr, &dma, sizeof(dma))) { > stl_be_dma(s->dma_as, dma_addr + offsetof(FWCfgDmaAccess, > control), FW_CFG_DMA_CTL_ERROR); > return; > } > > dma.address = be64_to_cpu(dma.address); > dma.length = be32_to_cpu(dma.length); > dma.control = be32_to_cpu(dma.control); > > > + > > + if (dma.control & FW_CFG_DMA_CTL_SELECT) { > > + fw_cfg_select(s, dma.control >> 16); > > + } > > + > > + arch = !!(s->cur_entry & FW_CFG_ARCH_LOCAL); > > + e = &s->entries[arch][s->cur_entry & FW_CFG_ENTRY_MASK]; > > + > > + if (dma.control & FW_CFG_DMA_CTL_READ) { > > + read = 1; > > + } else if (dma.control & FW_CFG_DMA_CTL_SKIP) { > > + read = 0; > > + } else { > > + dma.length = 0; > > + } > > + > > + dma.control = 0; > > + > > + while (dma.length > 0 && !(dma.control & > > FW_CFG_DMA_CTL_ERROR)) { > > + if (s->cur_entry == FW_CFG_INVALID || !e->data || > > + s->cur_offset >= e->len) { > > + len = dma.length; > > + > > + /* If the access is not a read access, it will be a > > skip access, > > + * tested before. > > + */ > > + if (read) { > > + if (dma_memory_set(s->dma_as, dma.address, 0, > > len)) { > > + dma.control |= FW_CFG_DMA_CTL_ERROR; > > + } > > + } > > + > > + } else { > > + if (dma.length <= (e->len - s->cur_offset)) { > > + len = dma.length; > > + } else { > > + len = (e->len - s->cur_offset); > > + } > > + > > + if (e->read_callback) { > > + e->read_callback(e->callback_opaque, > > s->cur_offset); > > + } > > + > > + /* If the access is not a read access, it will be a > > skip access, > > + * tested before. > > + */ > > + if (read) { > > + if (dma_memory_write(s->dma_as, dma.address, > > + &e->data[s->cur_offset], len)) > > { > > + dma.control |= FW_CFG_DMA_CTL_ERROR; > > + } > > + } > > + > > + s->cur_offset += len; > > + } > > + > > + dma.address += len; > > + dma.length -= len; > > I thought these fields are written back to guest memory? For example, > so the guest knows how many bytes were read before the error occurred. This was proposed here: http://lists.gnu.org/archive/html/qemu-devel/2015-08/msg04001.html I also don't see much benefit into knowing how many bytes were read. If the guest is trying to read an invalid entry or past the end of that entry, the memory will be filled with zeros. The only moment when an error would be reported is when there's some problem in the mapping. And this problem is bad enough to just abort the whole operation. Regards Marc ^ permalink raw reply [flat|nested] 60+ messages in thread
* [Qemu-devel] [PATCH v4 4/7] Enable fw_cfg DMA interface for ARM 2015-10-01 12:16 ` [Qemu-devel] [PATCH v4 0/7] " Marc Marí ` (2 preceding siblings ...) 2015-10-01 12:16 ` [Qemu-devel] [PATCH v4 3/7] Implement fw_cfg DMA interface Marc Marí @ 2015-10-01 12:16 ` Marc Marí 2015-10-01 14:42 ` Laszlo Ersek 2015-10-01 12:16 ` [Qemu-devel] [PATCH v4 5/7] Enable fw_cfg DMA interface for x86 Marc Marí ` (3 subsequent siblings) 7 siblings, 1 reply; 60+ messages in thread From: Marc Marí @ 2015-10-01 12:16 UTC (permalink / raw) To: qemu-devel Cc: Drew, Stefan Hajnoczi, Gabriel L. Somlo, Kevin O'Connor, Gerd Hoffmann, Marc Marí, Laszlo Enable the fw_cfg DMA interface for the ARM virt machine. Based on Gerd Hoffman's initial implementation. Signed-off-by: Marc Marí <markmb@redhat.com> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> --- hw/arm/virt.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/hw/arm/virt.c b/hw/arm/virt.c index 7ae984f..0a39087 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -119,7 +119,7 @@ static const MemMapEntry a15memmap[] = { [VIRT_GIC_REDIST] = { 0x080A0000, 0x00F60000 }, [VIRT_UART] = { 0x09000000, 0x00001000 }, [VIRT_RTC] = { 0x09010000, 0x00001000 }, - [VIRT_FW_CFG] = { 0x09020000, 0x0000000a }, + [VIRT_FW_CFG] = { 0x09020000, 0x00000018 }, [VIRT_MMIO] = { 0x0a000000, 0x00000200 }, /* ...repeating for a total of NUM_VIRTIO_TRANSPORTS, each of that size */ [VIRT_PLATFORM_BUS] = { 0x0c000000, 0x02000000 }, @@ -677,13 +677,13 @@ static void create_flash(const VirtBoardInfo *vbi) g_free(nodename); } -static void create_fw_cfg(const VirtBoardInfo *vbi) +static void create_fw_cfg(const VirtBoardInfo *vbi, AddressSpace *as) { 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, 0, NULL); + fw_cfg_init_mem_wide(base + 8, base, 8, base + 16, as); nodename = g_strdup_printf("/fw-cfg@%" PRIx64, base); qemu_fdt_add_subnode(vbi->fdt, nodename); @@ -1026,7 +1026,7 @@ static void machvirt_init(MachineState *machine) */ create_virtio_devices(vbi, pic); - create_fw_cfg(vbi); + create_fw_cfg(vbi, &address_space_memory); rom_set_fw(fw_cfg_find()); guest_info->smp_cpus = smp_cpus; -- 2.4.3 ^ permalink raw reply related [flat|nested] 60+ messages in thread
* Re: [Qemu-devel] [PATCH v4 4/7] Enable fw_cfg DMA interface for ARM 2015-10-01 12:16 ` [Qemu-devel] [PATCH v4 4/7] Enable fw_cfg DMA interface for ARM Marc Marí @ 2015-10-01 14:42 ` Laszlo Ersek 0 siblings, 0 replies; 60+ messages in thread From: Laszlo Ersek @ 2015-10-01 14:42 UTC (permalink / raw) To: Marc Marí, qemu-devel Cc: Gabriel L. Somlo, Stefan Hajnoczi, Drew, Kevin O'Connor, Gerd Hoffmann On 10/01/15 14:16, Marc Marí wrote: > Enable the fw_cfg DMA interface for the ARM virt machine. > > Based on Gerd Hoffman's initial implementation. > > Signed-off-by: Marc Marí <markmb@redhat.com> > Reviewed-by: Peter Maydell <peter.maydell@linaro.org> > --- > hw/arm/virt.c | 8 ++++---- > 1 file changed, 4 insertions(+), 4 deletions(-) > > diff --git a/hw/arm/virt.c b/hw/arm/virt.c > index 7ae984f..0a39087 100644 > --- a/hw/arm/virt.c > +++ b/hw/arm/virt.c > @@ -119,7 +119,7 @@ static const MemMapEntry a15memmap[] = { > [VIRT_GIC_REDIST] = { 0x080A0000, 0x00F60000 }, > [VIRT_UART] = { 0x09000000, 0x00001000 }, > [VIRT_RTC] = { 0x09010000, 0x00001000 }, > - [VIRT_FW_CFG] = { 0x09020000, 0x0000000a }, > + [VIRT_FW_CFG] = { 0x09020000, 0x00000018 }, > [VIRT_MMIO] = { 0x0a000000, 0x00000200 }, > /* ...repeating for a total of NUM_VIRTIO_TRANSPORTS, each of that size */ > [VIRT_PLATFORM_BUS] = { 0x0c000000, 0x02000000 }, > @@ -677,13 +677,13 @@ static void create_flash(const VirtBoardInfo *vbi) > g_free(nodename); > } > > -static void create_fw_cfg(const VirtBoardInfo *vbi) > +static void create_fw_cfg(const VirtBoardInfo *vbi, AddressSpace *as) > { > 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, 0, NULL); > + fw_cfg_init_mem_wide(base + 8, base, 8, base + 16, as); > > nodename = g_strdup_printf("/fw-cfg@%" PRIx64, base); > qemu_fdt_add_subnode(vbi->fdt, nodename); > @@ -1026,7 +1026,7 @@ static void machvirt_init(MachineState *machine) > */ > create_virtio_devices(vbi, pic); > > - create_fw_cfg(vbi); > + create_fw_cfg(vbi, &address_space_memory); > rom_set_fw(fw_cfg_find()); > > guest_info->smp_cpus = smp_cpus; > Reviewed-by: Laszlo Ersek <lersek@redhat.com> ^ permalink raw reply [flat|nested] 60+ messages in thread
* [Qemu-devel] [PATCH v4 5/7] Enable fw_cfg DMA interface for x86 2015-10-01 12:16 ` [Qemu-devel] [PATCH v4 0/7] " Marc Marí ` (3 preceding siblings ...) 2015-10-01 12:16 ` [Qemu-devel] [PATCH v4 4/7] Enable fw_cfg DMA interface for ARM Marc Marí @ 2015-10-01 12:16 ` Marc Marí 2015-10-01 14:48 ` Laszlo Ersek 2015-10-01 12:16 ` [Qemu-devel] [PATCH v4 6/7] Make the kernel image in the fw_cfg DMA interface bootable Marc Marí ` (2 subsequent siblings) 7 siblings, 1 reply; 60+ messages in thread From: Marc Marí @ 2015-10-01 12:16 UTC (permalink / raw) To: qemu-devel Cc: Drew, Stefan Hajnoczi, Gabriel L. Somlo, Kevin O'Connor, Gerd Hoffmann, Marc Marí, Laszlo Enable the fw_cfg DMA interface for all the x86 platforms. Based on Gerd Hoffman's initial implementation. Signed-off-by: Marc Marí <markmb@redhat.com> --- hw/i386/pc.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 461c128..81d93b4 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -753,14 +753,15 @@ static void pc_build_smbios(FWCfgState *fw_cfg) } } -static FWCfgState *bochs_bios_init(void) +static FWCfgState *bochs_bios_init(AddressSpace *as) { FWCfgState *fw_cfg; uint64_t *numa_fw_cfg; int i, j; unsigned int apic_id_limit = pc_apic_id_limit(max_cpus); - fw_cfg = fw_cfg_init_io(BIOS_CFG_IOPORT); + fw_cfg = fw_cfg_init_io_dma(BIOS_CFG_IOPORT, BIOS_CFG_IOPORT + 4, as); + /* FW_CFG_MAX_CPUS is a bit confusing/problematic on x86: * * SeaBIOS needs FW_CFG_MAX_CPUS for CPU hotplug, but the CPU hotplug @@ -1407,7 +1408,8 @@ FWCfgState *pc_memory_init(PCMachineState *pcms, option_rom_mr, 1); - fw_cfg = bochs_bios_init(); + fw_cfg = bochs_bios_init(&address_space_memory); + rom_set_fw(fw_cfg); if (guest_info->has_reserved_memory && pcms->hotplug_memory.base) { -- 2.4.3 ^ permalink raw reply related [flat|nested] 60+ messages in thread
* Re: [Qemu-devel] [PATCH v4 5/7] Enable fw_cfg DMA interface for x86 2015-10-01 12:16 ` [Qemu-devel] [PATCH v4 5/7] Enable fw_cfg DMA interface for x86 Marc Marí @ 2015-10-01 14:48 ` Laszlo Ersek 0 siblings, 0 replies; 60+ messages in thread From: Laszlo Ersek @ 2015-10-01 14:48 UTC (permalink / raw) To: Marc Marí, qemu-devel Cc: Gabriel L. Somlo, Stefan Hajnoczi, Drew, Kevin O'Connor, Gerd Hoffmann On 10/01/15 14:16, Marc Marí wrote: > Enable the fw_cfg DMA interface for all the x86 platforms. > > Based on Gerd Hoffman's initial implementation. > > Signed-off-by: Marc Marí <markmb@redhat.com> > --- > hw/i386/pc.c | 8 +++++--- > 1 file changed, 5 insertions(+), 3 deletions(-) > > diff --git a/hw/i386/pc.c b/hw/i386/pc.c > index 461c128..81d93b4 100644 > --- a/hw/i386/pc.c > +++ b/hw/i386/pc.c > @@ -753,14 +753,15 @@ static void pc_build_smbios(FWCfgState *fw_cfg) > } > } > > -static FWCfgState *bochs_bios_init(void) > +static FWCfgState *bochs_bios_init(AddressSpace *as) > { > FWCfgState *fw_cfg; > uint64_t *numa_fw_cfg; > int i, j; > unsigned int apic_id_limit = pc_apic_id_limit(max_cpus); > > - fw_cfg = fw_cfg_init_io(BIOS_CFG_IOPORT); > + fw_cfg = fw_cfg_init_io_dma(BIOS_CFG_IOPORT, BIOS_CFG_IOPORT + 4, as); > + > /* FW_CFG_MAX_CPUS is a bit confusing/problematic on x86: > * > * SeaBIOS needs FW_CFG_MAX_CPUS for CPU hotplug, but the CPU hotplug > @@ -1407,7 +1408,8 @@ FWCfgState *pc_memory_init(PCMachineState *pcms, > option_rom_mr, > 1); > > - fw_cfg = bochs_bios_init(); > + fw_cfg = bochs_bios_init(&address_space_memory); > + > rom_set_fw(fw_cfg); > > if (guest_info->has_reserved_memory && pcms->hotplug_memory.base) { > Looks like this matches the agreement between Gerd and Peter. http://thread.gmane.org/gmane.comp.emulators.qemu/363030/focus=363058 Reviewed-by: Laszlo Ersek <lersek@redhat.com> ^ permalink raw reply [flat|nested] 60+ messages in thread
* [Qemu-devel] [PATCH v4 6/7] Make the kernel image in the fw_cfg DMA interface bootable 2015-10-01 12:16 ` [Qemu-devel] [PATCH v4 0/7] " Marc Marí ` (4 preceding siblings ...) 2015-10-01 12:16 ` [Qemu-devel] [PATCH v4 5/7] Enable fw_cfg DMA interface for x86 Marc Marí @ 2015-10-01 12:16 ` Marc Marí 2015-10-01 15:25 ` Laszlo Ersek 2015-10-01 12:16 ` [Qemu-devel] [PATCH v4 7/7] fw_cfg: Define a static signature to be returned on DMA port reads Marc Marí 2015-10-01 13:19 ` [Qemu-devel] [PATCH v4 0/7] fw_cfg DMA interface Kevin O'Connor 7 siblings, 1 reply; 60+ messages in thread From: Marc Marí @ 2015-10-01 12:16 UTC (permalink / raw) To: qemu-devel Cc: Drew, Stefan Hajnoczi, Gabriel L. Somlo, Kevin O'Connor, Gerd Hoffmann, Marc Marí, Laszlo Add an entry to the bootorder file with name "vmlinux". Give this entry more priority than the romfile. Signed-off-by: Marc Marí <markmb@redhat.com> --- hw/i386/pc.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 81d93b4..c4c51f7 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -1012,8 +1012,10 @@ static void load_linux(PCMachineState *pcms, fw_cfg_add_bytes(fw_cfg, FW_CFG_SETUP_DATA, setup, setup_size); option_rom[nb_option_roms].name = "linuxboot.bin"; - option_rom[nb_option_roms].bootindex = 0; + option_rom[nb_option_roms].bootindex = 1; nb_option_roms++; + + add_boot_device_path(0, NULL, "vmlinux"); } #define NE2000_NB_MAX 6 -- 2.4.3 ^ permalink raw reply related [flat|nested] 60+ messages in thread
* Re: [Qemu-devel] [PATCH v4 6/7] Make the kernel image in the fw_cfg DMA interface bootable 2015-10-01 12:16 ` [Qemu-devel] [PATCH v4 6/7] Make the kernel image in the fw_cfg DMA interface bootable Marc Marí @ 2015-10-01 15:25 ` Laszlo Ersek 2015-10-01 16:02 ` Kevin O'Connor 2015-10-02 8:09 ` Gerd Hoffmann 0 siblings, 2 replies; 60+ messages in thread From: Laszlo Ersek @ 2015-10-01 15:25 UTC (permalink / raw) To: Marc Marí, qemu-devel Cc: Gabriel L. Somlo, Stefan Hajnoczi, Drew, Kevin O'Connor, Gerd Hoffmann On 10/01/15 14:16, Marc Marí wrote: > Add an entry to the bootorder file with name "vmlinux". > Give this entry more priority than the romfile. > > Signed-off-by: Marc Marí <markmb@redhat.com> > --- > hw/i386/pc.c | 4 +++- > 1 file changed, 3 insertions(+), 1 deletion(-) > > diff --git a/hw/i386/pc.c b/hw/i386/pc.c > index 81d93b4..c4c51f7 100644 > --- a/hw/i386/pc.c > +++ b/hw/i386/pc.c > @@ -1012,8 +1012,10 @@ static void load_linux(PCMachineState *pcms, > fw_cfg_add_bytes(fw_cfg, FW_CFG_SETUP_DATA, setup, setup_size); > > option_rom[nb_option_roms].name = "linuxboot.bin"; > - option_rom[nb_option_roms].bootindex = 0; > + option_rom[nb_option_roms].bootindex = 1; > nb_option_roms++; > + > + add_boot_device_path(0, NULL, "vmlinux"); > } > > #define NE2000_NB_MAX 6 > Where does this idea come from? This will yet again break the invariant that the bootorder fw_cfg file is a list of OpenFirmware device paths. (The other annoying offender being "HALT", which caused me huge grief in the OVMF OpenFirmware devpath parser parser, when libvirt decided that "-boot strict=on" would become default.) OVMF (and AAVMF) have been able to boot kernels directly from fw_cfg for quite some time now, without the above change. They look at the fw_cfg key 0x0008 (FW_CFG_KERNEL_SIZE). Direct kernel boot is being requested iff the (little endian encoded) uint32 value is nonzero. In QEMU, this role of FW_CFG_KERNEL_SIZE is true for: - arm_load_kernel_notify() [hw/arm/boot.c], relied upon by AAVMF, - load_multiboot() [hw/i386/multiboot.c] and load_linux() [hw/i386/pc.c], relied upon by OVMF, - "hw/ppc/mac_newworld.c", "hw/ppc/mac_oldworld.c", "hw/sparc/sun4m.c", and "hw/sparc64/sun4u.c", relied upon by whatever boot firmware they have. Why is this necessary for SeaBIOS? ... I can see the function bootprio_find_vmlinux(), in SeaBIOS patch [PATCH v4 2/2] Boot Linux using QEMU fw_cfg DMA interface Given that direct kernel boot is always expected to take priority over anything else (which is ensured by this QEMU patch too), can bootprio_find_vmlinux() in SeaBIOS just look at the same fw_cfg key (0x0008)? I checked the QEMU_CFG_* macros in "src/fw/paravirt.c", and I think when SeaBIOS boots an fw_cfg kernel *now*, it doesn't do it with its own implementation; it probably launches the "linuxboot.bin" oprom (from QEMU -- "pc-bios/optionrom/linuxboot.S"). I vaguely recall that this assembly code has been deemed unwieldy for implementing the DMA interface (and I fully agree), which is why the above-referenced SeaBIOS patch adds the capability to SeaBIOS itself. I agree with that too. But, instead of messing up the "bootorder" fw_cfg file, can bootprio_find_vmlinux() look at the non-nullity of the QEMU_CFG_KERNEL_SIZE key? Such as: - read four bytes from under the fw_cfg selector QEMU_CFG_KERNEL_SIZE (0x0008), - if it is zero, return -1 --> no kernel boot requested, - if it is nonzero, return 0 --> which means "top priority". In other words, I agree with: > - option_rom[nb_option_roms].bootindex = 0; > + option_rom[nb_option_roms].bootindex = 1; in this patch, but I disagree with: > + add_boot_device_path(0, NULL, "vmlinux"); Thank you Laszlo ^ permalink raw reply [flat|nested] 60+ messages in thread
* Re: [Qemu-devel] [PATCH v4 6/7] Make the kernel image in the fw_cfg DMA interface bootable 2015-10-01 15:25 ` Laszlo Ersek @ 2015-10-01 16:02 ` Kevin O'Connor 2015-10-01 16:10 ` Laszlo Ersek ` (2 more replies) 2015-10-02 8:09 ` Gerd Hoffmann 1 sibling, 3 replies; 60+ messages in thread From: Kevin O'Connor @ 2015-10-01 16:02 UTC (permalink / raw) To: Laszlo Ersek Cc: Drew, Stefan Hajnoczi, Gabriel L. Somlo, qemu-devel, Gerd Hoffmann, Marc Marí On Thu, Oct 01, 2015 at 05:25:11PM +0200, Laszlo Ersek wrote: > On 10/01/15 14:16, Marc Marí wrote: > > Add an entry to the bootorder file with name "vmlinux". > > Give this entry more priority than the romfile. > > > > Signed-off-by: Marc Marí <markmb@redhat.com> > > --- > > hw/i386/pc.c | 4 +++- > > 1 file changed, 3 insertions(+), 1 deletion(-) > > > > diff --git a/hw/i386/pc.c b/hw/i386/pc.c > > index 81d93b4..c4c51f7 100644 > > --- a/hw/i386/pc.c > > +++ b/hw/i386/pc.c > > @@ -1012,8 +1012,10 @@ static void load_linux(PCMachineState *pcms, > > fw_cfg_add_bytes(fw_cfg, FW_CFG_SETUP_DATA, setup, setup_size); > > > > option_rom[nb_option_roms].name = "linuxboot.bin"; > > - option_rom[nb_option_roms].bootindex = 0; > > + option_rom[nb_option_roms].bootindex = 1; > > nb_option_roms++; > > + > > + add_boot_device_path(0, NULL, "vmlinux"); > > } > > > > #define NE2000_NB_MAX 6 > > > > Where does this idea come from? > > This will yet again break the invariant that the bootorder fw_cfg file > is a list of OpenFirmware device paths. I believe it came from a discussion between myself and Marc, because I did not like the way Marc's original SeaBIOS patches overloaded the meaning of "genroms/linuxboot.bin" in the bootorder file. [...] > Given that direct kernel boot is always expected to take priority over > anything else (which is ensured by this QEMU patch too), can > bootprio_find_vmlinux() in SeaBIOS just look at the same fw_cfg key > (0x0008)? That's fine with me. Marc - I think qemu_vmlinux_setup() in SeaBIOS with the following would work: void qemu_vmlinux_setup(void) { u32 kernel_size; qemu_cfg_read_entry(&kernel_size, QEMU_CFG_KERNEL_SIZE, sizeof(kernel_size)); if (kernel_size) boot_add_qemu_vmlinux("QEMU Kernel image", 0); } Marc, if you're okay with the above, you don't have to keep respinning patches - I can fix it up upon commit. -Kevin ^ permalink raw reply [flat|nested] 60+ messages in thread
* Re: [Qemu-devel] [PATCH v4 6/7] Make the kernel image in the fw_cfg DMA interface bootable 2015-10-01 16:02 ` Kevin O'Connor @ 2015-10-01 16:10 ` Laszlo Ersek 2015-10-01 18:15 ` Marc Marí 2015-10-02 8:16 ` Gerd Hoffmann 2 siblings, 0 replies; 60+ messages in thread From: Laszlo Ersek @ 2015-10-01 16:10 UTC (permalink / raw) To: Kevin O'Connor Cc: Drew, Stefan Hajnoczi, Gabriel L. Somlo, qemu-devel, Gerd Hoffmann, Marc Marí On 10/01/15 18:02, Kevin O'Connor wrote: > On Thu, Oct 01, 2015 at 05:25:11PM +0200, Laszlo Ersek wrote: >> On 10/01/15 14:16, Marc Marí wrote: >>> Add an entry to the bootorder file with name "vmlinux". >>> Give this entry more priority than the romfile. >>> >>> Signed-off-by: Marc Marí <markmb@redhat.com> >>> --- >>> hw/i386/pc.c | 4 +++- >>> 1 file changed, 3 insertions(+), 1 deletion(-) >>> >>> diff --git a/hw/i386/pc.c b/hw/i386/pc.c >>> index 81d93b4..c4c51f7 100644 >>> --- a/hw/i386/pc.c >>> +++ b/hw/i386/pc.c >>> @@ -1012,8 +1012,10 @@ static void load_linux(PCMachineState *pcms, >>> fw_cfg_add_bytes(fw_cfg, FW_CFG_SETUP_DATA, setup, setup_size); >>> >>> option_rom[nb_option_roms].name = "linuxboot.bin"; >>> - option_rom[nb_option_roms].bootindex = 0; >>> + option_rom[nb_option_roms].bootindex = 1; >>> nb_option_roms++; >>> + >>> + add_boot_device_path(0, NULL, "vmlinux"); >>> } >>> >>> #define NE2000_NB_MAX 6 >>> >> >> Where does this idea come from? >> >> This will yet again break the invariant that the bootorder fw_cfg file >> is a list of OpenFirmware device paths. > > I believe it came from a discussion between myself and Marc, because I > did not like the way Marc's original SeaBIOS patches overloaded the > meaning of "genroms/linuxboot.bin" in the bootorder file. > > [...] >> Given that direct kernel boot is always expected to take priority over >> anything else (which is ensured by this QEMU patch too), can >> bootprio_find_vmlinux() in SeaBIOS just look at the same fw_cfg key >> (0x0008)? > > That's fine with me. Marc - I think qemu_vmlinux_setup() in SeaBIOS > with the following would work: > > void qemu_vmlinux_setup(void) > { > u32 kernel_size; > qemu_cfg_read_entry(&kernel_size, QEMU_CFG_KERNEL_SIZE, sizeof(kernel_size)); > if (kernel_size) > boot_add_qemu_vmlinux("QEMU Kernel image", 0); > } Thank you, Kevin; that would be great! Laszlo > Marc, if you're okay with the above, you don't have to keep respinning > patches - I can fix it up upon commit. > > -Kevin > ^ permalink raw reply [flat|nested] 60+ messages in thread
* Re: [Qemu-devel] [PATCH v4 6/7] Make the kernel image in the fw_cfg DMA interface bootable 2015-10-01 16:02 ` Kevin O'Connor 2015-10-01 16:10 ` Laszlo Ersek @ 2015-10-01 18:15 ` Marc Marí 2015-10-02 8:16 ` Gerd Hoffmann 2 siblings, 0 replies; 60+ messages in thread From: Marc Marí @ 2015-10-01 18:15 UTC (permalink / raw) To: Kevin O'Connor Cc: Drew, Stefan Hajnoczi, Gabriel L. Somlo, qemu-devel, Gerd Hoffmann, Laszlo Ersek On Thu, 1 Oct 2015 12:02:42 -0400 "Kevin O'Connor" <kevin@koconnor.net> wrote: > On Thu, Oct 01, 2015 at 05:25:11PM +0200, Laszlo Ersek wrote: > > On 10/01/15 14:16, Marc Marí wrote: > > > Add an entry to the bootorder file with name "vmlinux". > > > Give this entry more priority than the romfile. > > > > > > Signed-off-by: Marc Marí <markmb@redhat.com> > > > --- > > > hw/i386/pc.c | 4 +++- > > > 1 file changed, 3 insertions(+), 1 deletion(-) > > > > > > diff --git a/hw/i386/pc.c b/hw/i386/pc.c > > > index 81d93b4..c4c51f7 100644 > > > --- a/hw/i386/pc.c > > > +++ b/hw/i386/pc.c > > > @@ -1012,8 +1012,10 @@ static void load_linux(PCMachineState > > > *pcms, fw_cfg_add_bytes(fw_cfg, FW_CFG_SETUP_DATA, setup, > > > setup_size); > > > option_rom[nb_option_roms].name = "linuxboot.bin"; > > > - option_rom[nb_option_roms].bootindex = 0; > > > + option_rom[nb_option_roms].bootindex = 1; > > > nb_option_roms++; > > > + > > > + add_boot_device_path(0, NULL, "vmlinux"); > > > } > > > > > > #define NE2000_NB_MAX 6 > > > > > > > Where does this idea come from? > > > > This will yet again break the invariant that the bootorder fw_cfg > > file is a list of OpenFirmware device paths. > > I believe it came from a discussion between myself and Marc, because I > did not like the way Marc's original SeaBIOS patches overloaded the > meaning of "genroms/linuxboot.bin" in the bootorder file. I didn't like it either. > [...] > > Given that direct kernel boot is always expected to take priority > > over anything else (which is ensured by this QEMU patch too), can > > bootprio_find_vmlinux() in SeaBIOS just look at the same fw_cfg key > > (0x0008)? > > That's fine with me. Marc - I think qemu_vmlinux_setup() in SeaBIOS > with the following would work: > > void qemu_vmlinux_setup(void) > { > u32 kernel_size; > qemu_cfg_read_entry(&kernel_size, QEMU_CFG_KERNEL_SIZE, > sizeof(kernel_size)); if (kernel_size) > boot_add_qemu_vmlinux("QEMU Kernel image", 0); > } > > Marc, if you're okay with the above, you don't have to keep respinning > patches - I can fix it up upon commit. I'm ok with it Thanks Marc ^ permalink raw reply [flat|nested] 60+ messages in thread
* Re: [Qemu-devel] [PATCH v4 6/7] Make the kernel image in the fw_cfg DMA interface bootable 2015-10-01 16:02 ` Kevin O'Connor 2015-10-01 16:10 ` Laszlo Ersek 2015-10-01 18:15 ` Marc Marí @ 2015-10-02 8:16 ` Gerd Hoffmann 2015-10-02 8:24 ` Marc Marí 2015-10-02 13:38 ` Kevin O'Connor 2 siblings, 2 replies; 60+ messages in thread From: Gerd Hoffmann @ 2015-10-02 8:16 UTC (permalink / raw) To: Kevin O'Connor Cc: Drew, Stefan Hajnoczi, Gabriel L. Somlo, qemu-devel, Marc Marí, Laszlo Ersek Hi, > That's fine with me. Marc - I think qemu_vmlinux_setup() in SeaBIOS > with the following would work: > > void qemu_vmlinux_setup(void) > { > u32 kernel_size; > qemu_cfg_read_entry(&kernel_size, QEMU_CFG_KERNEL_SIZE, sizeof(kernel_size)); > if (kernel_size) > boot_add_qemu_vmlinux("QEMU Kernel image", 0); > } It isn't that simple. We also have support for multiboot kernels (using multiboot.bin option rom). So when doing this you need to be prepared to find a multiboot kernel in fw_cfg. cheers, Gerd ^ permalink raw reply [flat|nested] 60+ messages in thread
* Re: [Qemu-devel] [PATCH v4 6/7] Make the kernel image in the fw_cfg DMA interface bootable 2015-10-02 8:16 ` Gerd Hoffmann @ 2015-10-02 8:24 ` Marc Marí 2015-10-02 9:01 ` Gerd Hoffmann 2015-10-02 13:38 ` Kevin O'Connor 1 sibling, 1 reply; 60+ messages in thread From: Marc Marí @ 2015-10-02 8:24 UTC (permalink / raw) To: Gerd Hoffmann Cc: Drew, Stefan Hajnoczi, Gabriel L. Somlo, qemu-devel, Kevin O'Connor, Laszlo Ersek On Fri, 02 Oct 2015 10:16:26 +0200 Gerd Hoffmann <kraxel@redhat.com> wrote: > Hi, > > > That's fine with me. Marc - I think qemu_vmlinux_setup() in SeaBIOS > > with the following would work: > > > > void qemu_vmlinux_setup(void) > > { > > u32 kernel_size; > > qemu_cfg_read_entry(&kernel_size, QEMU_CFG_KERNEL_SIZE, > > sizeof(kernel_size)); if (kernel_size) > > boot_add_qemu_vmlinux("QEMU Kernel image", 0); > > } > > It isn't that simple. We also have support for multiboot kernels > (using multiboot.bin option rom). So when doing this you need to be > prepared to find a multiboot kernel in fw_cfg. > > cheers, > Gerd A solution that I can see is adding DMA boot capabilities to the linuxboot.S optionrom. I was trying to avoid this, but it looks like not doing so creates lots of problems. It may be better than adding a "nice" shortcut somewhere in QEMU or SeaBIOS. Who uses this optionrom (and will benefit from this change)? Thanks Marc ^ permalink raw reply [flat|nested] 60+ messages in thread
* Re: [Qemu-devel] [PATCH v4 6/7] Make the kernel image in the fw_cfg DMA interface bootable 2015-10-02 8:24 ` Marc Marí @ 2015-10-02 9:01 ` Gerd Hoffmann 2015-10-02 11:47 ` Laszlo Ersek 0 siblings, 1 reply; 60+ messages in thread From: Gerd Hoffmann @ 2015-10-02 9:01 UTC (permalink / raw) To: Marc Marí Cc: Drew, Stefan Hajnoczi, Gabriel L. Somlo, qemu-devel, Kevin O'Connor, Laszlo Ersek Hi, > A solution that I can see is adding DMA boot capabilities to the > linuxboot.S optionrom. I was trying to avoid this, but it looks like > not doing so creates lots of problems. It may be better than adding a > "nice" shortcut somewhere in QEMU or SeaBIOS. > > Who uses this optionrom (and will benefit from this change)? Any -kernel boot on x86 will use either linuxboot.bin or multiboot.bin. >From a compatibility point of view implementing it in the option rom certainly has its advantages. And as the option roms are shipped with and loaded by qemu you don't even have to do runtime detection if that simplifies things. We can just build two versions of the roms and qemu can use the one or the other depending on whenever the machine type in question has fw_cfg dma enabled or not. cheers, Gerd ^ permalink raw reply [flat|nested] 60+ messages in thread
* Re: [Qemu-devel] [PATCH v4 6/7] Make the kernel image in the fw_cfg DMA interface bootable 2015-10-02 9:01 ` Gerd Hoffmann @ 2015-10-02 11:47 ` Laszlo Ersek 2015-10-02 12:07 ` Gerd Hoffmann 0 siblings, 1 reply; 60+ messages in thread From: Laszlo Ersek @ 2015-10-02 11:47 UTC (permalink / raw) To: Gerd Hoffmann, Marc Marí Cc: Stefan Hajnoczi, Drew, Kevin O'Connor, Gabriel L. Somlo, qemu-devel On 10/02/15 11:01, Gerd Hoffmann wrote: > Hi, > >> A solution that I can see is adding DMA boot capabilities to the >> linuxboot.S optionrom. I was trying to avoid this, but it looks like >> not doing so creates lots of problems. It may be better than adding a >> "nice" shortcut somewhere in QEMU or SeaBIOS. >> >> Who uses this optionrom (and will benefit from this change)? > > Any -kernel boot on x86 will use either linuxboot.bin or multiboot.bin. (Except when your firmware is OVMF -- OVMF has its own LoadLinuxLib. So, if you decide to extend linuxboot.bin / multiboot.bin with the DMA capability, that can't regress OVMF by definition, and you certainly won't hear me complain.) Thanks Laszlo > From a compatibility point of view implementing it in the option rom > certainly has its advantages. And as the option roms are shipped with > and loaded by qemu you don't even have to do runtime detection if that > simplifies things. We can just build two versions of the roms and qemu > can use the one or the other depending on whenever the machine type in > question has fw_cfg dma enabled or not. > > cheers, > Gerd > > > > ^ permalink raw reply [flat|nested] 60+ messages in thread
* Re: [Qemu-devel] [PATCH v4 6/7] Make the kernel image in the fw_cfg DMA interface bootable 2015-10-02 11:47 ` Laszlo Ersek @ 2015-10-02 12:07 ` Gerd Hoffmann 2015-10-02 13:25 ` Laszlo Ersek 0 siblings, 1 reply; 60+ messages in thread From: Gerd Hoffmann @ 2015-10-02 12:07 UTC (permalink / raw) To: Laszlo Ersek Cc: Drew, Stefan Hajnoczi, Gabriel L. Somlo, qemu-devel, Kevin O'Connor, Marc Marí Hi, > > Any -kernel boot on x86 will use either linuxboot.bin or multiboot.bin. > > (Except when your firmware is OVMF -- OVMF has its own LoadLinuxLib. So, > if you decide to extend linuxboot.bin / multiboot.bin with the DMA > capability, that can't regress OVMF by definition, and you certainly > won't hear me complain.) What does ovmf expect btw? linux kernel with efi stub I assume? Could you also load efi apps, i.e. something like "qemu -kernel shell.efi"? cheers, Gerd ^ permalink raw reply [flat|nested] 60+ messages in thread
* Re: [Qemu-devel] [PATCH v4 6/7] Make the kernel image in the fw_cfg DMA interface bootable 2015-10-02 12:07 ` Gerd Hoffmann @ 2015-10-02 13:25 ` Laszlo Ersek 2015-10-02 13:30 ` Laszlo Ersek 2015-10-03 0:05 ` Jordan Justen 0 siblings, 2 replies; 60+ messages in thread From: Laszlo Ersek @ 2015-10-02 13:25 UTC (permalink / raw) To: Gerd Hoffmann Cc: Drew, Matt Fleming, Stefan Hajnoczi, Gabriel L. Somlo, qemu-devel, Kevin O'Connor, Jordan Justen (Intel address), Marc Marí On 10/02/15 14:07, Gerd Hoffmann wrote: > Hi, > >>> Any -kernel boot on x86 will use either linuxboot.bin or multiboot.bin. >> >> (Except when your firmware is OVMF -- OVMF has its own LoadLinuxLib. So, >> if you decide to extend linuxboot.bin / multiboot.bin with the DMA >> capability, that can't regress OVMF by definition, and you certainly >> won't hear me complain.) > > What does ovmf expect btw? linux kernel with efi stub I assume? That's a hard question for me to answer :) (The library was written / ported by Jordan, so I'm not responding from personal memory.) In Dec 2014 - Jan 2015, Matt, Paolo, Jordan & myself had a long discussion about the different ways to boot an EFI kernel (subject "the different ways to boot an EFI kernel"). Ultimately Matt wrote an article: http://www.uefidk.com/blog/linux-efi-boot-stub To distill the discussion (and I hope Matt will correct me if I'm wrong, although I'll be heavily stealing from his emails), there are three ways: (1) Legacy EFI boot where the boot loader does *everything* (2) EFI boot stub (3) EFI handover protocol In (1), the boot loader calls ExitBootServices(). "OvmfPkg/Library/LoadLinuxLib" supports this method, for directly booting a kernel from fw_cfg, on the path that it calls "Old kernels without EFI handover protocol". The problem with (1) is that "all the smarts of booting a Linux kernel on EFI platforms are in the boot loader", which includes workarounds for platform bugs, and lock-step development between kernel and boot loader. (2) From Matt: "This method makes the Linux kernel appear to be a PE/COFF executable. This allows us to perform bug workarounds early in the kernel source because it's the kernel that calls ExitBootServices(). Additional goodness is obtained by the fact that you no longer need to try as hard to keep kernel and boot loader development in sync because the boot loader does very little and all smarts are in the kernel." The general drawback here is that without a boot loader, you can "only" load the kernel image (and the EFI stub of the kernel can "only" load the initrd) from filesystems for which the firmware has support (ie. those that it exposes with EFI_SIMPLE_FILE_SYSTEM_PROTOCOL interfaces). OvmfPkg/Library/LoadLinuxLib does *not* support this method. However, ArmVirtPkg/Library/PlatformIntelBdsLib/QemuKernel.c does. (In fact, for AAVMF, this is the only method supported.) The "drawback" remark about filesystems does not apply, because the kernel, initrd and cmdline blobs are retrieved from fw_cfg, and exposed in a synthetic (memory-only, read-only) EFI_SIMPLE_FILE_SYSTEM_PROTOCOL. Then we launch the kernel with gBS->LoadImage() and gBS->StartImage(), and the kernel loads the initrd with the standard EFI_SIMPLE_FILE_SYSTEM_PROTOCOL member functions. (3) From Matt again: 'The third option, the EFI handover protocol, is a happy medium between the first two approaches. You split the "EFI boot smarts" between the boot loader and EFI boot stub, which allows you to a) load files from non-FAT file systems via the boot loader and b) leave all the EFI bug workarounds to the kernel developers because the kernel is still responsible for calling ExitBootServices().' Later Matt also mentioned that under (3) you can have extra bells and whistles (graphics etc) in your boot loader, while the kernel still gets early firmware access. OVMF supports this method too. I'll add that the "FAT file system" restriction that (3) is supposed to remedy is a bit laxer in general; even without a GRUB-like boot loader, you can load a kernel from PXE / TFTP, and generally from anything that looks like an EFI_SIMPLE_FILE_SYSTEM_PROTOCOL. So the summary is this: - OVMF can load "legacy" kernels, implementing an appropriate boot loader and calling ExitBootServices() (1) - OVMF can load EFI stubbed kernels, implementing an absolutely minimal boot loader (3) - AAVMF can load EFI stubbed kernels only (see the rationale in <https://github.com/tianocore/edk2/commit/23d04b58>), in method (2). This was written by me, and I chose this over (1) and (3) because: - (1) would have never worked / made any sense for aarch64 -- see again the commit msg I referenced, - (3) would have been overkill -- all the extras that could have been granted by an external boot loader were useless here, and the EFI stub dependency of (3) enabled the simpler, direct LoadImage() / StartImage() method. ... Paolo asked why OVMF hadn't opted for (2) as well, to which Matt replied -- if I understand correctly -- that in parallel with the kernel's facilities being developed for (3), OVMF was supposed to support / exercise those facilities too. Another question was if (2) could be enabled in / ported to OVMF -- it could be, yes, but I'm hard pressed for any reason. > Could > you also load efi apps, i.e. something like "qemu -kernel shell.efi"? I seem to remember that this has been suggested by Jordan as well. The synthetic file system + LoadImage() + StartImage(), seen in (2), would be theoretically suitable for this. However, at least three things aren't a good match now: - The way the synthetic filesystem is currently populated. One thing we need to put in there is the kernel image, to be launched from the firmware with LoadImage() + StartImage(). However, the kernel uses the filesystem too (it looks for the initrd file there), therefore we populate the fs with that file too. Such a filesystem may not a good match for other EFI executables (especially not if they expect a writeable working directory). - If you boot the kernel successfully, then StartImage() never returns. If it returns, then that's mostly considered an error, and the usual boot option processing commences, as if you had never specified "-kernel". This is probably not appropriate when your payload was the UEFI shell: exiting the shell (any shell) is completely normal for a user, and starting to process boot options right after that is probably unexpected / unintended. - When the top level kernel boot function returns (due to a genuine kernel boot error, or because "-kernel" wasn't specified, or -- fictively -- because the shell exited normally), the synthetic filesystem is torn down. This is okay for a kernel payload (because if booting it failed for the first time, there's no reason to retry it), but a user might want to reenter (and again exit) the shell any number of times. ... I think I prefer to keep the shell built into the firmware, and/or to keep it on "UefiShell.iso". I already consider "-kernel" an abuse of fw_cfg (one that at this point we can't get rid of any more); let's not make it worse. I think the *real* goal here is an easy-to-use, zero-config semihosting solution; ie. accessing a host directory tree within the guest. However, that use case is a burning problem for full-blown guest OS-es as well. Assuming we end up with a simple solution, I think I'd prefer to implement a UEFI_DRIVER that exposes the same host-side tree as an EFI_SIMPLE_FILE_SYSTEM_PROTOCOL instance to the firmware, over bastardizing fw_cfg even more. I don't yet know how dynamic host-side writes should be shown to guest firmware, but in the worst case, I could just return EFI_MEDIA_CHANGED: If the medium is changed while there are open file handles to the volume, all file handles to the volume will return EFI_MEDIA_CHANGED. To access the files on the new medium, the volume must be reopened with OpenVolume(). Thanks Laszlo ^ permalink raw reply [flat|nested] 60+ messages in thread
* Re: [Qemu-devel] [PATCH v4 6/7] Make the kernel image in the fw_cfg DMA interface bootable 2015-10-02 13:25 ` Laszlo Ersek @ 2015-10-02 13:30 ` Laszlo Ersek 2015-10-03 0:05 ` Jordan Justen 1 sibling, 0 replies; 60+ messages in thread From: Laszlo Ersek @ 2015-10-02 13:30 UTC (permalink / raw) To: Gerd Hoffmann Cc: Drew, Matt Fleming, Stefan Hajnoczi, Gabriel L. Somlo, qemu-devel, Kevin O'Connor, Jordan Justen (Intel address), Marc Marí On 10/02/15 15:25, Laszlo Ersek wrote: > In Dec 2014 - Jan 2015, Matt, Paolo, Jordan & myself had a long > discussion about the different ways to boot an EFI kernel (subject "the > different ways to boot an EFI kernel"). Ultimately Matt wrote an article: > > http://www.uefidk.com/blog/linux-efi-boot-stub Correction: that article predates our discussion. The one I wanted to link is this: http://lwn.net/Articles/632528/ Sorry Laszlo ^ permalink raw reply [flat|nested] 60+ messages in thread
* Re: [Qemu-devel] [PATCH v4 6/7] Make the kernel image in the fw_cfg DMA interface bootable 2015-10-02 13:25 ` Laszlo Ersek 2015-10-02 13:30 ` Laszlo Ersek @ 2015-10-03 0:05 ` Jordan Justen 1 sibling, 0 replies; 60+ messages in thread From: Jordan Justen @ 2015-10-03 0:05 UTC (permalink / raw) To: Laszlo Ersek, Gerd Hoffmann Cc: Drew, Matt Fleming, Stefan Hajnoczi, Gabriel L. Somlo, qemu-devel, Kevin O'Connor, Marc Marí On 2015-10-02 06:25:50, Laszlo Ersek wrote: > On 10/02/15 14:07, Gerd Hoffmann wrote: > > Hi, > > > >>> Any -kernel boot on x86 will use either linuxboot.bin or multiboot.bin. > >> > >> (Except when your firmware is OVMF -- OVMF has its own LoadLinuxLib. So, > >> if you decide to extend linuxboot.bin / multiboot.bin with the DMA > >> capability, that can't regress OVMF by definition, and you certainly > >> won't hear me complain.) > > > > What does ovmf expect btw? linux kernel with efi stub I assume? > > That's a hard question for me to answer :) (The library was written / > ported by Jordan, so I'm not responding from personal memory.) > > In Dec 2014 - Jan 2015, Matt, Paolo, Jordan & myself had a long > discussion about the different ways to boot an EFI kernel (subject "the > different ways to boot an EFI kernel"). Ultimately Matt wrote an article: > > http://www.uefidk.com/blog/linux-efi-boot-stub > > To distill the discussion (and I hope Matt will correct me if I'm wrong, > although I'll be heavily stealing from his emails), there are three ways: > > (1) Legacy EFI boot where the boot loader does *everything* > (2) EFI boot stub > (3) EFI handover protocol > > In (1), the boot loader calls ExitBootServices(). > > "OvmfPkg/Library/LoadLinuxLib" supports this method, for directly > booting a kernel from fw_cfg, on the path that it calls "Old kernels > without EFI handover protocol". > > The problem with (1) is that "all the smarts > of booting a Linux kernel on EFI platforms are in the boot loader", > which includes workarounds for platform bugs, and lock-step development > between kernel and boot loader. > > (2) From Matt: "This method makes the Linux kernel appear to be > a PE/COFF executable. This allows us to perform bug workarounds early in > the kernel source because it's the kernel that calls ExitBootServices(). > Additional goodness is obtained by the fact that you no longer need to > try as hard to keep kernel and boot loader development in sync because > the boot loader does very little and all smarts are in the kernel." > > The general drawback here is that without a boot loader, you can "only" > load the kernel image (and the EFI stub of the kernel can "only" load > the initrd) from filesystems for which the firmware has support (ie. > those that it exposes with EFI_SIMPLE_FILE_SYSTEM_PROTOCOL interfaces). > > OvmfPkg/Library/LoadLinuxLib does *not* support this method. > > However, ArmVirtPkg/Library/PlatformIntelBdsLib/QemuKernel.c does. (In > fact, for AAVMF, this is the only method supported.) The "drawback" > remark about filesystems does not apply, because the kernel, initrd and > cmdline blobs are retrieved from fw_cfg, and exposed in a synthetic > (memory-only, read-only) EFI_SIMPLE_FILE_SYSTEM_PROTOCOL. Then we launch > the kernel with gBS->LoadImage() and gBS->StartImage(), and the kernel > loads the initrd with the standard EFI_SIMPLE_FILE_SYSTEM_PROTOCOL > member functions. > > (3) From Matt again: 'The third option, the EFI handover protocol, is a > happy medium between the first two approaches. You split the "EFI boot > smarts" between the boot loader and EFI boot stub, which allows you to > a) load files from non-FAT file systems via the boot loader and b) leave > all the EFI bug workarounds to the kernel developers because the kernel > is still responsible for calling ExitBootServices().' > > Later Matt also mentioned that under (3) you can have extra bells and > whistles (graphics etc) in your boot loader, while the kernel still gets > early firmware access. > > OVMF supports this method too. > > I'll add that the "FAT file system" restriction that (3) is supposed to > remedy is a bit laxer in general; even without a GRUB-like boot loader, > you can load a kernel from PXE / TFTP, and generally from anything that > looks like an EFI_SIMPLE_FILE_SYSTEM_PROTOCOL. > > So the summary is this: > - OVMF can load "legacy" kernels, implementing an appropriate boot > loader and calling ExitBootServices() (1) > > - OVMF can load EFI stubbed kernels, implementing an absolutely minimal > boot loader (3) > > - AAVMF can load EFI stubbed kernels only (see the rationale in > <https://github.com/tianocore/edk2/commit/23d04b58>), in method (2). > This was written by me, and I chose this over (1) and (3) because: > > - (1) would have never worked / made any sense for aarch64 -- see > again the commit msg I referenced, > > - (3) would have been overkill -- all the extras that could have been > granted by an external boot loader were useless here, and the EFI > stub dependency of (3) enabled the simpler, direct LoadImage() / > StartImage() method. > > ... Paolo asked why OVMF hadn't opted for (2) as well, to which Matt > replied -- if I understand correctly -- that in parallel with the > kernel's facilities being developed for (3), OVMF was supposed to > support / exercise those facilities too. > > Another question was if (2) could be enabled in / ported to OVMF -- it > could be, yes, but I'm hard pressed for any reason. I already did this nearly 3 years ago, but then I dropped the ball. http://thread.gmane.org/gmane.comp.bios.tianocore.devel/925 I'd still like to add this to OVMF, since it enables any old EFI application to be run with qemu's -kernel parameter. For example, I was able to launch the EFI shells with it. -Jordan > > Could > > you also load efi apps, i.e. something like "qemu -kernel shell.efi"? > > I seem to remember that this has been suggested by Jordan as well. The > synthetic file system + LoadImage() + StartImage(), seen in (2), would > be theoretically suitable for this. However, at least three things > aren't a good match now: > > - The way the synthetic filesystem is currently populated. One thing we > need to put in there is the kernel image, to be launched from the > firmware with LoadImage() + StartImage(). However, the kernel uses > the filesystem too (it looks for the initrd file there), therefore we > populate the fs with that file too. Such a filesystem may not a good > match for other EFI executables (especially not if they expect a > writeable working directory). > > - If you boot the kernel successfully, then StartImage() never returns. > If it returns, then that's mostly considered an error, and the usual > boot option processing commences, as if you had never specified > "-kernel". This is probably not appropriate when your payload was the > UEFI shell: exiting the shell (any shell) is completely normal for a > user, and starting to process boot options right after that is > probably unexpected / unintended. > > - When the top level kernel boot function returns (due to a genuine > kernel boot error, or because "-kernel" wasn't specified, > or -- fictively -- because the shell exited normally), the synthetic > filesystem is torn down. This is okay for a kernel payload (because if > booting it failed for the first time, there's no reason to retry it), > but a user might want to reenter (and again exit) the shell any > number of times. > > ... I think I prefer to keep the shell built into the firmware, and/or > to keep it on "UefiShell.iso". I already consider "-kernel" an abuse of > fw_cfg (one that at this point we can't get rid of any more); let's not > make it worse. I think the *real* goal here is an easy-to-use, > zero-config semihosting solution; ie. accessing a host directory tree > within the guest. > > However, that use case is a burning problem for full-blown guest OS-es > as well. Assuming we end up with a simple solution, I think I'd prefer > to implement a UEFI_DRIVER that exposes the same host-side tree as an > EFI_SIMPLE_FILE_SYSTEM_PROTOCOL instance to the firmware, over > bastardizing fw_cfg even more. > > I don't yet know how dynamic host-side writes should be shown to guest > firmware, but in the worst case, I could just return EFI_MEDIA_CHANGED: > > If the medium is changed while there are open file handles to the > volume, all file handles to the volume will return > EFI_MEDIA_CHANGED. To access the files on the new medium, the > volume must be reopened with OpenVolume(). > > Thanks > Laszlo > ^ permalink raw reply [flat|nested] 60+ messages in thread
* Re: [Qemu-devel] [PATCH v4 6/7] Make the kernel image in the fw_cfg DMA interface bootable 2015-10-02 8:16 ` Gerd Hoffmann 2015-10-02 8:24 ` Marc Marí @ 2015-10-02 13:38 ` Kevin O'Connor 2015-10-05 9:18 ` Gerd Hoffmann 1 sibling, 1 reply; 60+ messages in thread From: Kevin O'Connor @ 2015-10-02 13:38 UTC (permalink / raw) To: Gerd Hoffmann Cc: Drew, Stefan Hajnoczi, Gabriel L. Somlo, qemu-devel, Marc Marí, Laszlo Ersek On Fri, Oct 02, 2015 at 10:16:26AM +0200, Gerd Hoffmann wrote: > Hi, > > > That's fine with me. Marc - I think qemu_vmlinux_setup() in SeaBIOS > > with the following would work: > > > > void qemu_vmlinux_setup(void) > > { > > u32 kernel_size; > > qemu_cfg_read_entry(&kernel_size, QEMU_CFG_KERNEL_SIZE, sizeof(kernel_size)); > > if (kernel_size) > > boot_add_qemu_vmlinux("QEMU Kernel image", 0); > > } > > It isn't that simple. We also have support for multiboot kernels (using > multiboot.bin option rom). So when doing this you need to be prepared > to find a multiboot kernel in fw_cfg. Is there some way to detect if it's a multiboot kernel? If so, seabios can just fall back to using multiboot.bin. -Kevin ^ permalink raw reply [flat|nested] 60+ messages in thread
* Re: [Qemu-devel] [PATCH v4 6/7] Make the kernel image in the fw_cfg DMA interface bootable 2015-10-02 13:38 ` Kevin O'Connor @ 2015-10-05 9:18 ` Gerd Hoffmann 0 siblings, 0 replies; 60+ messages in thread From: Gerd Hoffmann @ 2015-10-05 9:18 UTC (permalink / raw) To: Kevin O'Connor Cc: Drew, Stefan Hajnoczi, Gabriel L. Somlo, qemu-devel, Marc Marí, Laszlo Ersek On Fr, 2015-10-02 at 09:38 -0400, Kevin O'Connor wrote: > On Fri, Oct 02, 2015 at 10:16:26AM +0200, Gerd Hoffmann wrote: > > Hi, > > > > > That's fine with me. Marc - I think qemu_vmlinux_setup() in SeaBIOS > > > with the following would work: > > > > > > void qemu_vmlinux_setup(void) > > > { > > > u32 kernel_size; > > > qemu_cfg_read_entry(&kernel_size, QEMU_CFG_KERNEL_SIZE, sizeof(kernel_size)); > > > if (kernel_size) > > > boot_add_qemu_vmlinux("QEMU Kernel image", 0); > > > } > > > > It isn't that simple. We also have support for multiboot kernels (using > > multiboot.bin option rom). So when doing this you need to be prepared > > to find a multiboot kernel in fw_cfg. > > Is there some way to detect if it's a multiboot kernel? Yes. There is a header with magic + checksum in the first 8k, see hw/i386/multiboot.c (in qemu). Or check the option rom name in the bootorder file, it's multiboot instead of linuxboot. > If so, > seabios can just fall back to using multiboot.bin. Or add multiboot support to seabios. cheers, Gerd ^ permalink raw reply [flat|nested] 60+ messages in thread
* Re: [Qemu-devel] [PATCH v4 6/7] Make the kernel image in the fw_cfg DMA interface bootable 2015-10-01 15:25 ` Laszlo Ersek 2015-10-01 16:02 ` Kevin O'Connor @ 2015-10-02 8:09 ` Gerd Hoffmann 2015-10-02 13:40 ` Kevin O'Connor 1 sibling, 1 reply; 60+ messages in thread From: Gerd Hoffmann @ 2015-10-02 8:09 UTC (permalink / raw) To: Laszlo Ersek Cc: Drew, Stefan Hajnoczi, Gabriel L. Somlo, qemu-devel, Kevin O'Connor, Marc Marí > - read four bytes from under the fw_cfg selector QEMU_CFG_KERNEL_SIZE > (0x0008), > - if it is zero, return -1 --> no kernel boot requested, > - if it is nonzero, return 0 --> which means "top priority". > > In other words, I agree with: > > > - option_rom[nb_option_roms].bootindex = 0; > > + option_rom[nb_option_roms].bootindex = 1; Hmm. That makes the boot order undefined for "qemu -kernel foo -device virtio-blk,drive=bar,bootindex=1" when using an old seabios. I don't think this is a good idea. cheers, Gerd ^ permalink raw reply [flat|nested] 60+ messages in thread
* Re: [Qemu-devel] [PATCH v4 6/7] Make the kernel image in the fw_cfg DMA interface bootable 2015-10-02 8:09 ` Gerd Hoffmann @ 2015-10-02 13:40 ` Kevin O'Connor 2015-10-02 13:50 ` Laszlo Ersek ` (2 more replies) 0 siblings, 3 replies; 60+ messages in thread From: Kevin O'Connor @ 2015-10-02 13:40 UTC (permalink / raw) To: Gerd Hoffmann Cc: Drew, Stefan Hajnoczi, Gabriel L. Somlo, qemu-devel, Marc Marí, Laszlo Ersek On Fri, Oct 02, 2015 at 10:09:17AM +0200, Gerd Hoffmann wrote: > > - read four bytes from under the fw_cfg selector QEMU_CFG_KERNEL_SIZE > > (0x0008), > > - if it is zero, return -1 --> no kernel boot requested, > > - if it is nonzero, return 0 --> which means "top priority". > > > > In other words, I agree with: > > > > > - option_rom[nb_option_roms].bootindex = 0; > > > + option_rom[nb_option_roms].bootindex = 1; The bootindex in QEMU is not visible in the firmware, so if the rest of patch 6 is dropped then the above should be dropped as well. > Hmm. That makes the boot order undefined for "qemu -kernel foo -device > virtio-blk,drive=bar,bootindex=1" when using an old seabios. I don't > think this is a good idea. Wouldn't that make the bootorder undefined everywhere? What does it mean to use -kernel and specify a bootorder? -Kevin ^ permalink raw reply [flat|nested] 60+ messages in thread
* Re: [Qemu-devel] [PATCH v4 6/7] Make the kernel image in the fw_cfg DMA interface bootable 2015-10-02 13:40 ` Kevin O'Connor @ 2015-10-02 13:50 ` Laszlo Ersek 2015-10-02 15:24 ` Daniel P. Berrange 2015-10-05 9:26 ` Gerd Hoffmann 2 siblings, 0 replies; 60+ messages in thread From: Laszlo Ersek @ 2015-10-02 13:50 UTC (permalink / raw) To: Kevin O'Connor, Gerd Hoffmann Cc: Gabriel L. Somlo, Stefan Hajnoczi, Marc Marí, Drew, qemu-devel On 10/02/15 15:40, Kevin O'Connor wrote: > On Fri, Oct 02, 2015 at 10:09:17AM +0200, Gerd Hoffmann wrote: >>> - read four bytes from under the fw_cfg selector QEMU_CFG_KERNEL_SIZE >>> (0x0008), >>> - if it is zero, return -1 --> no kernel boot requested, >>> - if it is nonzero, return 0 --> which means "top priority". >>> >>> In other words, I agree with: >>> >>>> - option_rom[nb_option_roms].bootindex = 0; >>>> + option_rom[nb_option_roms].bootindex = 1; > > The bootindex in QEMU is not visible in the firmware, so if the rest > of patch 6 is dropped then the above should be dropped as well. > >> Hmm. That makes the boot order undefined for "qemu -kernel foo -device >> virtio-blk,drive=bar,bootindex=1" when using an old seabios. I don't >> think this is a good idea. > > Wouldn't that make the bootorder undefined everywhere? What does it > mean to use -kernel and specify a bootorder? OVMF & AAVMF look at QEMU_CFG_KERNEL_SIZE first, and process the bootorder only after. (The kernel boot can fail due to various reasons, after which it makes sense to start processing the bootorder fw_cfg file.) Thanks Laszlo ^ permalink raw reply [flat|nested] 60+ messages in thread
* Re: [Qemu-devel] [PATCH v4 6/7] Make the kernel image in the fw_cfg DMA interface bootable 2015-10-02 13:40 ` Kevin O'Connor 2015-10-02 13:50 ` Laszlo Ersek @ 2015-10-02 15:24 ` Daniel P. Berrange 2015-10-05 9:26 ` Gerd Hoffmann 2 siblings, 0 replies; 60+ messages in thread From: Daniel P. Berrange @ 2015-10-02 15:24 UTC (permalink / raw) To: Kevin O'Connor Cc: Drew, Stefan Hajnoczi, Gabriel L. Somlo, qemu-devel, Gerd Hoffmann, Marc Marí, Laszlo Ersek On Fri, Oct 02, 2015 at 09:40:41AM -0400, Kevin O'Connor wrote: > On Fri, Oct 02, 2015 at 10:09:17AM +0200, Gerd Hoffmann wrote: > > > - read four bytes from under the fw_cfg selector QEMU_CFG_KERNEL_SIZE > > > (0x0008), > > > - if it is zero, return -1 --> no kernel boot requested, > > > - if it is nonzero, return 0 --> which means "top priority". > > > > > > In other words, I agree with: > > > > > > > - option_rom[nb_option_roms].bootindex = 0; > > > > + option_rom[nb_option_roms].bootindex = 1; > > The bootindex in QEMU is not visible in the firmware, so if the rest > of patch 6 is dropped then the above should be dropped as well. > > > Hmm. That makes the boot order undefined for "qemu -kernel foo -device > > virtio-blk,drive=bar,bootindex=1" when using an old seabios. I don't > > think this is a good idea. > > Wouldn't that make the bootorder undefined everywhere? What does it > mean to use -kernel and specify a bootorder? I think it is pretty meaningless - I've always considered the use of -kernel vs -boot / bootindex= to be mutually exclusive. I think that libvirt leaves out -boot entirely if it adds -kernel. If the kernel specified via -kernel doesn't boot for some reason (corrupt image or wrong arch image or something) then I think users would reasonably expect QEMU to not boot, rather than fallback to non-kernel boot approach. Regards, Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :| ^ permalink raw reply [flat|nested] 60+ messages in thread
* Re: [Qemu-devel] [PATCH v4 6/7] Make the kernel image in the fw_cfg DMA interface bootable 2015-10-02 13:40 ` Kevin O'Connor 2015-10-02 13:50 ` Laszlo Ersek 2015-10-02 15:24 ` Daniel P. Berrange @ 2015-10-05 9:26 ` Gerd Hoffmann 2 siblings, 0 replies; 60+ messages in thread From: Gerd Hoffmann @ 2015-10-05 9:26 UTC (permalink / raw) To: Kevin O'Connor Cc: Drew, Stefan Hajnoczi, Gabriel L. Somlo, qemu-devel, Marc Marí, Laszlo Ersek On Fr, 2015-10-02 at 09:40 -0400, Kevin O'Connor wrote: > On Fri, Oct 02, 2015 at 10:09:17AM +0200, Gerd Hoffmann wrote: > > > - read four bytes from under the fw_cfg selector QEMU_CFG_KERNEL_SIZE > > > (0x0008), > > > - if it is zero, return -1 --> no kernel boot requested, > > > - if it is nonzero, return 0 --> which means "top priority". > > > > > > In other words, I agree with: > > > > > > > - option_rom[nb_option_roms].bootindex = 0; > > > > + option_rom[nb_option_roms].bootindex = 1; > > The bootindex in QEMU is not visible in the firmware, so if the rest > of patch 6 is dropped then the above should be dropped as well. > > > Hmm. That makes the boot order undefined for "qemu -kernel foo -device > > virtio-blk,drive=bar,bootindex=1" when using an old seabios. I don't > > think this is a good idea. > > Wouldn't that make the bootorder undefined everywhere? What does it > mean to use -kernel and specify a bootorder? Kernel gets priority 0, everything else what you specify (typically configs start with bootindex=1), so the kernel gets the highest priority. So unless the user uses F12 to enter the boot menu and picks something else the kernel is booted. cheers, Gerd ^ permalink raw reply [flat|nested] 60+ messages in thread
* [Qemu-devel] [PATCH v4 7/7] fw_cfg: Define a static signature to be returned on DMA port reads 2015-10-01 12:16 ` [Qemu-devel] [PATCH v4 0/7] " Marc Marí ` (5 preceding siblings ...) 2015-10-01 12:16 ` [Qemu-devel] [PATCH v4 6/7] Make the kernel image in the fw_cfg DMA interface bootable Marc Marí @ 2015-10-01 12:16 ` Marc Marí 2015-10-01 16:07 ` Laszlo Ersek 2015-10-01 13:19 ` [Qemu-devel] [PATCH v4 0/7] fw_cfg DMA interface Kevin O'Connor 7 siblings, 1 reply; 60+ messages in thread From: Marc Marí @ 2015-10-01 12:16 UTC (permalink / raw) To: qemu-devel Cc: Drew, Stefan Hajnoczi, Gabriel L. Somlo, Kevin O'Connor, Gerd Hoffmann, Laszlo From: Kevin O'Connor <kevin@koconnor.net> Return a static signature ("QEMU CFG") if the guest does a read to the DMA address io register. Signed-off-by: Kevin O'Connor <kevin@koconnor.net> --- docs/specs/fw_cfg.txt | 4 ++++ hw/nvram/fw_cfg.c | 13 +++++++++++-- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/docs/specs/fw_cfg.txt b/docs/specs/fw_cfg.txt index 2d6b2da..249b99e 100644 --- a/docs/specs/fw_cfg.txt +++ b/docs/specs/fw_cfg.txt @@ -93,6 +93,10 @@ by selecting the "signature" item using key 0x0000 (FW_CFG_SIGNATURE), and reading four bytes from the data register. If the fw_cfg device is present, the four bytes read will contain the characters "QEMU". +Additionaly, if the DMA interface is available then a read to the DMA +Address will return 0x51454d5520434647 ("QEMU CFG" in big-endian +format). + === Revision / feature bitmap (Key 0x0001, FW_CFG_ID) === A 32-bit little-endian unsigned int, this item is used to check for enabled diff --git a/hw/nvram/fw_cfg.c b/hw/nvram/fw_cfg.c index 59933b3..c6dcce4 100644 --- a/hw/nvram/fw_cfg.c +++ b/hw/nvram/fw_cfg.c @@ -53,6 +53,8 @@ #define FW_CFG_DMA_CTL_SKIP 0x04 #define FW_CFG_DMA_CTL_SELECT 0x08 +#define FW_CFG_DMA_SIGNATURE 0x51454d5520434647 /* "QEMU CFG" */ + typedef struct FWCfgEntry { uint32_t len; uint8_t *data; @@ -393,6 +395,12 @@ static void fw_cfg_dma_transfer(FWCfgState *s) trace_fw_cfg_read(s, 0); } +static uint64_t fw_cfg_dma_mem_read(void *opaque, hwaddr addr, + unsigned size) +{ + return FW_CFG_DMA_SIGNATURE >> ((8 - addr - size) * 8); +} + static void fw_cfg_dma_mem_write(void *opaque, hwaddr addr, uint64_t value, unsigned size) { @@ -416,8 +424,8 @@ static void fw_cfg_dma_mem_write(void *opaque, hwaddr addr, static bool fw_cfg_dma_mem_valid(void *opaque, hwaddr addr, unsigned size, bool is_write) { - return is_write && ((size == 4 && (addr == 0 || addr == 4)) || - (size == 8 && addr == 0)); + return !is_write || ((size == 4 && (addr == 0 || addr == 4)) || + (size == 8 && addr == 0)); } static bool fw_cfg_data_mem_valid(void *opaque, hwaddr addr, @@ -488,6 +496,7 @@ static const MemoryRegionOps fw_cfg_comb_mem_ops = { }; static const MemoryRegionOps fw_cfg_dma_mem_ops = { + .read = fw_cfg_dma_mem_read, .write = fw_cfg_dma_mem_write, .endianness = DEVICE_BIG_ENDIAN, .valid.accepts = fw_cfg_dma_mem_valid, -- 2.4.3 ^ permalink raw reply related [flat|nested] 60+ messages in thread
* Re: [Qemu-devel] [PATCH v4 7/7] fw_cfg: Define a static signature to be returned on DMA port reads 2015-10-01 12:16 ` [Qemu-devel] [PATCH v4 7/7] fw_cfg: Define a static signature to be returned on DMA port reads Marc Marí @ 2015-10-01 16:07 ` Laszlo Ersek 2015-10-01 17:02 ` Kevin O'Connor 0 siblings, 1 reply; 60+ messages in thread From: Laszlo Ersek @ 2015-10-01 16:07 UTC (permalink / raw) To: Marc Marí, qemu-devel Cc: Gabriel L. Somlo, Stefan Hajnoczi, Drew, Kevin O'Connor, Gerd Hoffmann On 10/01/15 14:16, Marc Marí wrote: > From: Kevin O'Connor <kevin@koconnor.net> > > Return a static signature ("QEMU CFG") if the guest does a read to the > DMA address io register. > > Signed-off-by: Kevin O'Connor <kevin@koconnor.net> > --- > docs/specs/fw_cfg.txt | 4 ++++ > hw/nvram/fw_cfg.c | 13 +++++++++++-- > 2 files changed, 15 insertions(+), 2 deletions(-) > > diff --git a/docs/specs/fw_cfg.txt b/docs/specs/fw_cfg.txt > index 2d6b2da..249b99e 100644 > --- a/docs/specs/fw_cfg.txt > +++ b/docs/specs/fw_cfg.txt > @@ -93,6 +93,10 @@ by selecting the "signature" item using key 0x0000 (FW_CFG_SIGNATURE), > and reading four bytes from the data register. If the fw_cfg device is > present, the four bytes read will contain the characters "QEMU". > > +Additionaly, if the DMA interface is available then a read to the DMA > +Address will return 0x51454d5520434647 ("QEMU CFG" in big-endian > +format). I suggest: Additionaly, if the DMA interface is available, then a read into the DMA Address register will return the matching substring of the "QEMU CFG" string. Characters that are at lower offsets of the substring being read will be stored at lower addresses in the guest. > + > === Revision / feature bitmap (Key 0x0001, FW_CFG_ID) === > > A 32-bit little-endian unsigned int, this item is used to check for enabled > diff --git a/hw/nvram/fw_cfg.c b/hw/nvram/fw_cfg.c > index 59933b3..c6dcce4 100644 > --- a/hw/nvram/fw_cfg.c > +++ b/hw/nvram/fw_cfg.c > @@ -53,6 +53,8 @@ > #define FW_CFG_DMA_CTL_SKIP 0x04 > #define FW_CFG_DMA_CTL_SELECT 0x08 > > +#define FW_CFG_DMA_SIGNATURE 0x51454d5520434647 /* "QEMU CFG" */ > + > typedef struct FWCfgEntry { > uint32_t len; > uint8_t *data; > @@ -393,6 +395,12 @@ static void fw_cfg_dma_transfer(FWCfgState *s) > trace_fw_cfg_read(s, 0); > } > > +static uint64_t fw_cfg_dma_mem_read(void *opaque, hwaddr addr, > + unsigned size) > +{ > + return FW_CFG_DMA_SIGNATURE >> ((8 - addr - size) * 8); > +} > + After re-reading the message of QEMU commit 36b62ae6a5, I think this is mostly correct. (I think *technically* it is fully correct, due to the truncation that is implicit in the argument passing to bswapXX(), in the adjust_endianness() function [memory.c].) However, for the human reader's sake, I'd like to see the over-significant bits masked off explicitly here, after the shift. > static void fw_cfg_dma_mem_write(void *opaque, hwaddr addr, > uint64_t value, unsigned size) > { > @@ -416,8 +424,8 @@ static void fw_cfg_dma_mem_write(void *opaque, hwaddr addr, > static bool fw_cfg_dma_mem_valid(void *opaque, hwaddr addr, > unsigned size, bool is_write) > { > - return is_write && ((size == 4 && (addr == 0 || addr == 4)) || > - (size == 8 && addr == 0)); > + return !is_write || ((size == 4 && (addr == 0 || addr == 4)) || > + (size == 8 && addr == 0)); > } This is not good enough I think; it's possible for the guest to do an "inl" at offset 7 into the register, which will cause undefined behavior in fw_cfg_dma_mem_read(). (The shift count being negative.) Thus, for reading, please enforce: (addr + size <= 8) (In an uncontrolled environment, I'd protect the addition against overflow too, but here the individual addends are safe enough.) > > static bool fw_cfg_data_mem_valid(void *opaque, hwaddr addr, > @@ -488,6 +496,7 @@ static const MemoryRegionOps fw_cfg_comb_mem_ops = { > }; > > static const MemoryRegionOps fw_cfg_dma_mem_ops = { > + .read = fw_cfg_dma_mem_read, > .write = fw_cfg_dma_mem_write, > .endianness = DEVICE_BIG_ENDIAN, > .valid.accepts = fw_cfg_dma_mem_valid, > Thanks Laszlo ^ permalink raw reply [flat|nested] 60+ messages in thread
* Re: [Qemu-devel] [PATCH v4 7/7] fw_cfg: Define a static signature to be returned on DMA port reads 2015-10-01 16:07 ` Laszlo Ersek @ 2015-10-01 17:02 ` Kevin O'Connor 2015-10-01 17:17 ` Laszlo Ersek 0 siblings, 1 reply; 60+ messages in thread From: Kevin O'Connor @ 2015-10-01 17:02 UTC (permalink / raw) To: Laszlo Ersek Cc: Drew, Stefan Hajnoczi, Gabriel L. Somlo, qemu-devel, Gerd Hoffmann, Marc Marí On Thu, Oct 01, 2015 at 06:07:06PM +0200, Laszlo Ersek wrote: > On 10/01/15 14:16, Marc Marí wrote: > > From: Kevin O'Connor <kevin@koconnor.net> > > > > Return a static signature ("QEMU CFG") if the guest does a read to the > > DMA address io register. > > > > Signed-off-by: Kevin O'Connor <kevin@koconnor.net> > > --- > > docs/specs/fw_cfg.txt | 4 ++++ > > hw/nvram/fw_cfg.c | 13 +++++++++++-- > > 2 files changed, 15 insertions(+), 2 deletions(-) > > > > diff --git a/docs/specs/fw_cfg.txt b/docs/specs/fw_cfg.txt > > index 2d6b2da..249b99e 100644 > > --- a/docs/specs/fw_cfg.txt > > +++ b/docs/specs/fw_cfg.txt > > @@ -93,6 +93,10 @@ by selecting the "signature" item using key 0x0000 (FW_CFG_SIGNATURE), > > and reading four bytes from the data register. If the fw_cfg device is > > present, the four bytes read will contain the characters "QEMU". > > > > +Additionaly, if the DMA interface is available then a read to the DMA > > +Address will return 0x51454d5520434647 ("QEMU CFG" in big-endian > > +format). > > I suggest: > > Additionaly, if the DMA interface is available, then a read into the > DMA Address register will return the matching substring of the > "QEMU CFG" string. Characters that are at lower offsets of the > substring being read will be stored at lower addresses in the guest. The interface only supports 32bit and 64bit writes, so I would suggest the document not encourage 8bit reads. The implementation happens to works with 8bit reads, but that was just a side-effect. [...] > > @@ -393,6 +395,12 @@ static void fw_cfg_dma_transfer(FWCfgState *s) > > trace_fw_cfg_read(s, 0); > > } > > > > +static uint64_t fw_cfg_dma_mem_read(void *opaque, hwaddr addr, > > + unsigned size) > > +{ > > + return FW_CFG_DMA_SIGNATURE >> ((8 - addr - size) * 8); > > +} > > + > > After re-reading the message of QEMU commit 36b62ae6a5, I think this is > mostly correct. > > (I think *technically* it is fully correct, due to the truncation that > is implicit in the argument passing to bswapXX(), in the > adjust_endianness() function [memory.c].) > > However, for the human reader's sake, I'd like to see the > over-significant bits masked off explicitly here, after the shift. The value has to be truncated, as a guest inl() or readl() can't possibly return more than 32 bits. I think the code's harder to read with the mask in place. Are there any helper macros or functions in QEMU to help with register read/writes of smaller/larger sizes than expected? Maybe the .impl.min_access_size should just be set to 4. [...] > > @@ -416,8 +424,8 @@ static void fw_cfg_dma_mem_write(void *opaque, hwaddr addr, > > static bool fw_cfg_dma_mem_valid(void *opaque, hwaddr addr, > > unsigned size, bool is_write) > > { > > - return is_write && ((size == 4 && (addr == 0 || addr == 4)) || > > - (size == 8 && addr == 0)); > > + return !is_write || ((size == 4 && (addr == 0 || addr == 4)) || > > + (size == 8 && addr == 0)); > > } > > This is not good enough I think; it's possible for the guest to do an > "inl" at offset 7 into the register, which will cause undefined behavior > in fw_cfg_dma_mem_read(). (The shift count being negative.) The function never gets called on unaligned accesses (.impl.unaligned != true), so that can't happen. -Kevin ^ permalink raw reply [flat|nested] 60+ messages in thread
* Re: [Qemu-devel] [PATCH v4 7/7] fw_cfg: Define a static signature to be returned on DMA port reads 2015-10-01 17:02 ` Kevin O'Connor @ 2015-10-01 17:17 ` Laszlo Ersek 0 siblings, 0 replies; 60+ messages in thread From: Laszlo Ersek @ 2015-10-01 17:17 UTC (permalink / raw) To: Kevin O'Connor Cc: Drew, Stefan Hajnoczi, Gabriel L. Somlo, qemu-devel, Gerd Hoffmann, Marc Marí On 10/01/15 19:02, Kevin O'Connor wrote: > On Thu, Oct 01, 2015 at 06:07:06PM +0200, Laszlo Ersek wrote: >> On 10/01/15 14:16, Marc Marí wrote: >>> From: Kevin O'Connor <kevin@koconnor.net> >>> >>> Return a static signature ("QEMU CFG") if the guest does a read to the >>> DMA address io register. >>> >>> Signed-off-by: Kevin O'Connor <kevin@koconnor.net> >>> --- >>> docs/specs/fw_cfg.txt | 4 ++++ >>> hw/nvram/fw_cfg.c | 13 +++++++++++-- >>> 2 files changed, 15 insertions(+), 2 deletions(-) >>> >>> diff --git a/docs/specs/fw_cfg.txt b/docs/specs/fw_cfg.txt >>> index 2d6b2da..249b99e 100644 >>> --- a/docs/specs/fw_cfg.txt >>> +++ b/docs/specs/fw_cfg.txt >>> @@ -93,6 +93,10 @@ by selecting the "signature" item using key 0x0000 (FW_CFG_SIGNATURE), >>> and reading four bytes from the data register. If the fw_cfg device is >>> present, the four bytes read will contain the characters "QEMU". >>> >>> +Additionaly, if the DMA interface is available then a read to the DMA >>> +Address will return 0x51454d5520434647 ("QEMU CFG" in big-endian >>> +format). >> >> I suggest: >> >> Additionaly, if the DMA interface is available, then a read into the >> DMA Address register will return the matching substring of the >> "QEMU CFG" string. Characters that are at lower offsets of the >> substring being read will be stored at lower addresses in the guest. > > The interface only supports 32bit and 64bit writes, so I would suggest > the document not encourage 8bit reads. The implementation happens to > works with 8bit reads, but that was just a side-effect. Okay, I don't insist. :) > > [...] >>> @@ -393,6 +395,12 @@ static void fw_cfg_dma_transfer(FWCfgState *s) >>> trace_fw_cfg_read(s, 0); >>> } >>> >>> +static uint64_t fw_cfg_dma_mem_read(void *opaque, hwaddr addr, >>> + unsigned size) >>> +{ >>> + return FW_CFG_DMA_SIGNATURE >> ((8 - addr - size) * 8); >>> +} >>> + >> >> After re-reading the message of QEMU commit 36b62ae6a5, I think this is >> mostly correct. >> >> (I think *technically* it is fully correct, due to the truncation that >> is implicit in the argument passing to bswapXX(), in the >> adjust_endianness() function [memory.c].) >> >> However, for the human reader's sake, I'd like to see the >> over-significant bits masked off explicitly here, after the shift. > > The value has to be truncated, as a guest inl() or readl() can't > possibly return more than 32 bits. > > I think the code's harder to read with the mask in place. Are there > any helper macros or functions in QEMU to help with register > read/writes of smaller/larger sizes than expected? Maybe the > .impl.min_access_size should just be set to 4. I'd be fine even with just a comment then. :) > > [...] >>> @@ -416,8 +424,8 @@ static void fw_cfg_dma_mem_write(void *opaque, hwaddr addr, >>> static bool fw_cfg_dma_mem_valid(void *opaque, hwaddr addr, >>> unsigned size, bool is_write) >>> { >>> - return is_write && ((size == 4 && (addr == 0 || addr == 4)) || >>> - (size == 8 && addr == 0)); >>> + return !is_write || ((size == 4 && (addr == 0 || addr == 4)) || >>> + (size == 8 && addr == 0)); >>> } >> >> This is not good enough I think; it's possible for the guest to do an >> "inl" at offset 7 into the register, which will cause undefined behavior >> in fw_cfg_dma_mem_read(). (The shift count being negative.) > > The function never gets called on unaligned accesses (.impl.unaligned > != true), so that can't happen. That may be true, but -- for me at least -- that's too much to remember (or to look up all the time). Plus, I find an explicit check more robust, should the alignment restriction be lifted at some point. In any case, I don't insist. Thanks Laszlo > > -Kevin > ^ permalink raw reply [flat|nested] 60+ messages in thread
* Re: [Qemu-devel] [PATCH v4 0/7] fw_cfg DMA interface 2015-10-01 12:16 ` [Qemu-devel] [PATCH v4 0/7] " Marc Marí ` (6 preceding siblings ...) 2015-10-01 12:16 ` [Qemu-devel] [PATCH v4 7/7] fw_cfg: Define a static signature to be returned on DMA port reads Marc Marí @ 2015-10-01 13:19 ` Kevin O'Connor 7 siblings, 0 replies; 60+ messages in thread From: Kevin O'Connor @ 2015-10-01 13:19 UTC (permalink / raw) To: Marc Marí Cc: Drew, Stefan Hajnoczi, Gabriel L. Somlo, qemu-devel, Gerd Hoffmann, Laszlo On Thu, Oct 01, 2015 at 02:16:52PM +0200, Marc Marí wrote: > Implement host-side of the FW CFG DMA interface both for x86 and ARM. Thanks for working on this. The QEMU patches look good to me. -Kevin ^ permalink raw reply [flat|nested] 60+ messages in thread
* Re: [Qemu-devel] QEMU fw_cfg DMA interface 2015-10-01 12:14 [Qemu-devel] QEMU fw_cfg DMA interface Marc Marí 2015-10-01 12:16 ` [Qemu-devel] [PATCH v4 0/7] " Marc Marí @ 2015-10-01 16:03 ` Eric Blake 2015-10-01 16:11 ` Eric Blake 2015-10-01 16:17 ` Laszlo Ersek 1 sibling, 2 replies; 60+ messages in thread From: Eric Blake @ 2015-10-01 16:03 UTC (permalink / raw) To: Marc Marí, linux-kernel, qemu-devel, seabios Cc: Mark Rutland, Rob Herring, Drew, Arnd Bergmann, devicetree, Stefan Hajnoczi, Alexander Graf, Kevin O'Connor, Gerd Hoffmann, Laszlo [-- Attachment #1: Type: text/plain, Size: 589 bytes --] [meta-comment] On 10/01/2015 06:14 AM, Marc Marí wrote: > Implementation of the FW CFG DMA interface. The subject line is missing "v4" and "0/7". Also, the cover letter is missing a diffstat. That makes it harder to see from the cover letter what the rest of the series is about. 'git format-patch/send-email --cover-letter' does what you want; you can even 'git config format.coverletter=auto' to always include a decent cover letter on any multi-patch series. -- Eric Blake eblake redhat com +1-919-301-3266 Libvirt virtualization library http://libvirt.org [-- Attachment #2: OpenPGP digital signature --] [-- Type: application/pgp-signature, Size: 604 bytes --] ^ permalink raw reply [flat|nested] 60+ messages in thread
* Re: [Qemu-devel] QEMU fw_cfg DMA interface 2015-10-01 16:03 ` [Qemu-devel] QEMU " Eric Blake @ 2015-10-01 16:11 ` Eric Blake 2015-10-01 16:19 ` Laszlo Ersek 2015-10-01 16:17 ` Laszlo Ersek 1 sibling, 1 reply; 60+ messages in thread From: Eric Blake @ 2015-10-01 16:11 UTC (permalink / raw) To: Marc Marí, linux-kernel, qemu-devel, seabios Cc: Mark Rutland, Rob Herring, Drew, Arnd Bergmann, devicetree, Stefan Hajnoczi, Alexander Graf, Kevin O'Connor, Gerd Hoffmann, Laszlo [-- Attachment #1: Type: text/plain, Size: 1104 bytes --] On 10/01/2015 10:03 AM, Eric Blake wrote: > [meta-comment] > > On 10/01/2015 06:14 AM, Marc Marí wrote: >> Implementation of the FW CFG DMA interface. > > The subject line is missing "v4" and "0/7". Also, the cover letter is > missing a diffstat. That makes it harder to see from the cover letter > what the rest of the series is about. 'git format-patch/send-email > --cover-letter' does what you want; you can even 'git config > format.coverletter=auto' to always include a decent cover letter on any > multi-patch series. Oh, I see - you sent a meta-cover letter (the one I replied to in this subthread), and then a patch series including a cover letter (the real 0/7, then 1/7 and friends in-reply-to the 0/7) as a child of the meta-cover. It's still a bit awkward for tools that expect the 0/7 as the start of the thread, and part of my confusion was caused by out-of-order mail delivery due to the nongnu.org mail server still recovering from its mail delays. -- Eric Blake eblake redhat com +1-919-301-3266 Libvirt virtualization library http://libvirt.org [-- Attachment #2: OpenPGP digital signature --] [-- Type: application/pgp-signature, Size: 604 bytes --] ^ permalink raw reply [flat|nested] 60+ messages in thread
* Re: [Qemu-devel] QEMU fw_cfg DMA interface 2015-10-01 16:11 ` Eric Blake @ 2015-10-01 16:19 ` Laszlo Ersek 0 siblings, 0 replies; 60+ messages in thread From: Laszlo Ersek @ 2015-10-01 16:19 UTC (permalink / raw) To: Eric Blake, Marc Marí, linux-kernel, qemu-devel, seabios Cc: Mark Rutland, Rob Herring, Drew, Arnd Bergmann, devicetree, Stefan Hajnoczi, Alexander Graf, Kevin O'Connor, Gerd Hoffmann On 10/01/15 18:11, Eric Blake wrote: > On 10/01/2015 10:03 AM, Eric Blake wrote: >> [meta-comment] >> >> On 10/01/2015 06:14 AM, Marc Marí wrote: >>> Implementation of the FW CFG DMA interface. >> >> The subject line is missing "v4" and "0/7". Also, the cover letter is >> missing a diffstat. That makes it harder to see from the cover letter >> what the rest of the series is about. 'git format-patch/send-email >> --cover-letter' does what you want; you can even 'git config >> format.coverletter=auto' to always include a decent cover letter on any >> multi-patch series. > > Oh, I see - you sent a meta-cover letter (the one I replied to in this > subthread), and then a patch series including a cover letter (the real > 0/7, then 1/7 and friends in-reply-to the 0/7) as a child of the > meta-cover. It's still a bit awkward for tools that expect the 0/7 as > the start of the thread, Yep, the pattern I just described doesn't consider those tools. Is that a bad problem? Maybe the pattern is not so clever then. :) (I'm allowed to say bad things about it, because I "invented" it "independently". :)) > and part of my confusion was caused by > out-of-order mail delivery due to the nongnu.org mail server still > recovering from its mail delays. > Right, those delays are not helping. Thanks Laszlo ^ permalink raw reply [flat|nested] 60+ messages in thread
* Re: [Qemu-devel] QEMU fw_cfg DMA interface 2015-10-01 16:03 ` [Qemu-devel] QEMU " Eric Blake 2015-10-01 16:11 ` Eric Blake @ 2015-10-01 16:17 ` Laszlo Ersek 2015-10-01 16:21 ` Eric Blake 1 sibling, 1 reply; 60+ messages in thread From: Laszlo Ersek @ 2015-10-01 16:17 UTC (permalink / raw) To: Eric Blake, Marc Marí, linux-kernel, qemu-devel, seabios Cc: Mark Rutland, Rob Herring, Drew, Arnd Bergmann, devicetree, Stefan Hajnoczi, Alexander Graf, Kevin O'Connor, Gerd Hoffmann On 10/01/15 18:03, Eric Blake wrote: > [meta-comment] > > On 10/01/2015 06:14 AM, Marc Marí wrote: >> Implementation of the FW CFG DMA interface. > > The subject line is missing "v4" and "0/7". Also, the cover letter is > missing a diffstat. That makes it harder to see from the cover letter > what the rest of the series is about. 'git format-patch/send-email > --cover-letter' does what you want; you can even 'git config > format.coverletter=auto' to always include a decent cover letter on any > multi-patch series. > This posting follows a little bit different pattern, one that I myself follow when posting patches for two (or more) components that must work in sync. Usually, a top-level blurb is manually cross-posted to all relevant mailing lists. Then, each separate patch series is posted only to the relevant mailing list, with its own cover letter (as usual with git), *in response* to the manually posted blurb. This has the following benefits: - in mailing list archives that organize messages into threads *across* mailing lists (like Gmane does, for example), the top-level manual blurb is a good "root" for referencing the entire posting. - The same is true for personal mailboxes, if a recipient is explicitly CC'd on all of the messages. Because the top level blurb is parent to several patch series, and those child series can all have different version numbers (due to different numbers of respinds), it is not always straightforward to assign a version number to the top blurb. Thanks Laszlo ^ permalink raw reply [flat|nested] 60+ messages in thread
* Re: [Qemu-devel] QEMU fw_cfg DMA interface 2015-10-01 16:17 ` Laszlo Ersek @ 2015-10-01 16:21 ` Eric Blake 2015-10-01 16:34 ` Laszlo Ersek 0 siblings, 1 reply; 60+ messages in thread From: Eric Blake @ 2015-10-01 16:21 UTC (permalink / raw) To: Laszlo Ersek, Marc Marí, linux-kernel, qemu-devel, seabios Cc: Mark Rutland, Rob Herring, Drew, Arnd Bergmann, devicetree, Stefan Hajnoczi, Alexander Graf, Kevin O'Connor, Gerd Hoffmann [-- Attachment #1: Type: text/plain, Size: 1463 bytes --] On 10/01/2015 10:17 AM, Laszlo Ersek wrote: > On 10/01/15 18:03, Eric Blake wrote: >> [meta-comment] >> >> On 10/01/2015 06:14 AM, Marc Marí wrote: >>> Implementation of the FW CFG DMA interface. >> >> The subject line is missing "v4" and "0/7". Also, the cover letter is >> missing a diffstat. That makes it harder to see from the cover letter >> what the rest of the series is about. 'git format-patch/send-email >> --cover-letter' does what you want; you can even 'git config >> format.coverletter=auto' to always include a decent cover letter on any >> multi-patch series. >> > > This posting follows a little bit different pattern, one that I myself > follow when posting patches for two (or more) components that must work > in sync. Ok, makes sense. Maybe the only additional suggestions would be to make it more obvious in the subject line (put the text 'cross-post' somewhere?) or have the first paragraph of the meta-cover be more explicit that there are going to be multiple sub-threads, one per project, where all subthreads must be applied to their corresponding project for the overall feature to be complete? [And maybe I should wait a few minutes for the full thread to appear in my inbox, rather than immediately replying to the first mail while the series is still incomplete due to mail delays...] -- Eric Blake eblake redhat com +1-919-301-3266 Libvirt virtualization library http://libvirt.org [-- Attachment #2: OpenPGP digital signature --] [-- Type: application/pgp-signature, Size: 604 bytes --] ^ permalink raw reply [flat|nested] 60+ messages in thread
* Re: [Qemu-devel] QEMU fw_cfg DMA interface 2015-10-01 16:21 ` Eric Blake @ 2015-10-01 16:34 ` Laszlo Ersek 0 siblings, 0 replies; 60+ messages in thread From: Laszlo Ersek @ 2015-10-01 16:34 UTC (permalink / raw) To: Eric Blake, Marc Marí, linux-kernel, qemu-devel, seabios Cc: Mark Rutland, Rob Herring, Drew, Arnd Bergmann, devicetree, Stefan Hajnoczi, Alexander Graf, Kevin O'Connor, Gerd Hoffmann On 10/01/15 18:21, Eric Blake wrote: > On 10/01/2015 10:17 AM, Laszlo Ersek wrote: >> On 10/01/15 18:03, Eric Blake wrote: >>> [meta-comment] >>> >>> On 10/01/2015 06:14 AM, Marc Marí wrote: >>>> Implementation of the FW CFG DMA interface. >>> >>> The subject line is missing "v4" and "0/7". Also, the cover letter is >>> missing a diffstat. That makes it harder to see from the cover letter >>> what the rest of the series is about. 'git format-patch/send-email >>> --cover-letter' does what you want; you can even 'git config >>> format.coverletter=auto' to always include a decent cover letter on any >>> multi-patch series. >>> >> >> This posting follows a little bit different pattern, one that I myself >> follow when posting patches for two (or more) components that must work >> in sync. > > Ok, makes sense. Maybe the only additional suggestions would be to make > it more obvious in the subject line (put the text 'cross-post' > somewhere?) or have the first paragraph of the meta-cover be more > explicit that there are going to be multiple sub-threads, one per > project, where all subthreads must be applied to their corresponding > project for the overall feature to be complete? That's a good idea. I think prefixing the main blurb's subject with [cross-post], and a "standard" first paragraph based on your above suggestion, would be helpful. > [And maybe I should wait a few minutes for the full thread to appear in > my inbox, rather than immediately replying to the first mail while the > series is still incomplete due to mail delays...] I'm not patient; it would be unfair from me to expect others to be... :) Laszlo ^ permalink raw reply [flat|nested] 60+ messages in thread
* [Qemu-devel] QEMU fw_cfg DMA interface @ 2015-09-18 8:58 Marc Marí 0 siblings, 0 replies; 60+ messages in thread From: Marc Marí @ 2015-09-18 8:58 UTC (permalink / raw) To: linux-kernel, qemu-devel, seabios Cc: Mark Rutland, Rob Herring, Drew, Arnd Bergmann, devicetree, Stefan Hajnoczi, Alexander Graf, Kevin O'Connor, Gerd Hoffmann, Marc Marí, Laszlo Implementation of the FW CFG DMA interface. When running a Linux guest on top of QEMU, using the -kernel options, this is the timing improvement for x86: QEMU commit 16a1b6e and SeaBIOS commit e4d2b8c QEMU startup time: .080 BIOS startup time: .060 Kernel setup time: .586 Total time: .726 QEMU with this patch series and SeaBIOS with this patch series QEMU startup time: .080 BIOS startup time: .039 Kernel setup time: .002 Total time: .121 QEMU startup time is the time between the start and the first kvm_entry. BIOS startup time is the time between the first kvm_entry and the start of function do_boot, in SeaBIOS. Kernel setup time is the time between the start of the function do_boot in SeaBIOS and the jump to the Linux kernel. As you can see, both the BIOS (because of ACPI tables and other configurations) and the Linux kernel boot (because of the copy to memory) are greatly improved with this new interface. Also, this new interface is an addon to the old interface. Both interfaces are compatible and interchangeable. Changes from v1: - Take into account order of fields in the FWCfgDmaAccess structure - Check and change endianness of FWCfgDmaAccess fields - Change order of fields in the FWCfgDmaAccess structure - Add FW_CFG_DMA_CTL_SKIP feature for control field - Split FW_CFG_SIZE in QEMU - Make FW_CFG_ID a bitmap of features - Add 64 bit address support for the transfer. Trigger when writing the low address, and address is 0 by default and at the end of each transfer. - Align ports and addresses. - Preserve old fw_cfg_comb_valid behaviour in QEMU - Update documentation to reflect all these changes Changes from v2: - Make IOports fw_cfg DMA region a different IO region. - Reuse everything for MMIO and IOport DMA regions - Make transfer status only based on control field - Use DMA helpers instead of direct map/unmap - Change ARM fw_cfg DMA address space - Change Linux boot process to match linuxboot.S - Add select capabilities in the FWCfgDmaAccess struct - Update documentation to reflect all these changes ^ permalink raw reply [flat|nested] 60+ messages in thread
* [Qemu-devel] QEMU fw_cfg DMA interface @ 2015-08-31 9:08 Marc Marí 0 siblings, 0 replies; 60+ messages in thread From: Marc Marí @ 2015-08-31 9:08 UTC (permalink / raw) To: linux-kernel, qemu-devel, seabios Cc: Mark Rutland, Rob Herring, Drew, Arnd Bergmann, devicetree, Stefan Hajnoczi, Alexander Graf, Kevin O'Connor, Gerd Hoffmann, Marc Marí, Laszlo Implementation of the FW CFG DMA interface. When running a Linux guest on top of QEMU, using the -kernel options, this is the timing improvement for x86: QEMU commit 090d0bf and SeaBIOS commit 2fc20dc QEMU startup time: .078 BIOS startup time: .060 Kernel setup time: .578 Total time: .716 QEMU with this patch series and SeaBIOS with this patch series QEMU startup time: .080 BIOS startup time: .039 Kernel setup time: .002 Total time: .121 QEMU startup time is the time between the start and the first kvm_entry. BIOS startup time is the time between the first kvm_entry and the start of function do_boot, in SeaBIOS. Kernel setup time is the time between the start of the function do_boot in SeaBIOS and the jump to the Linux kernel. As you can see, both the BIOS (because of ACPI tables and other configurations) and the Linux kernel boot (because of the copy to memory) are greatly improved with this new interface. Also, this new interface is an addon to the old interface. Both interfaces are compatible and interchangeable. Changes from v1: - Take into account order of fields in the FWCfgDmaAccess structure - Check and change endianness of FWCfgDmaAccess fields - Change order of fields in the FWCfgDmaAccess structure - Add FW_CFG_DMA_CTL_SKIP feature for control field - Split FW_CFG_SIZE in QEMU - Make FW_CFG_ID a bitmap of features - Add 64 bit address support for the transfer. Trigger when writing the low address, and address is 0 by default and at the end of each transfer. - Align ports and addresses. - Preserve old fw_cfg_comb_valid behaviour in QEMU - Update documentation to reflect all these changes ^ permalink raw reply [flat|nested] 60+ messages in thread
* [Qemu-devel] QEMU fw_cfg DMA interface @ 2015-08-06 11:00 Marc Marí 2015-08-06 12:27 ` Stefan Hajnoczi 0 siblings, 1 reply; 60+ messages in thread From: Marc Marí @ 2015-08-06 11:00 UTC (permalink / raw) To: linux-kernel, qemu-devel, seabios Cc: Drew, Stefan Hajnoczi, Kevin O'Connor, Gerd Hoffmann, Marc Marí, Laszlo Implementation of the FW CFG DMA interface. When running a Linux guest on top of QEMU, using the -kernel options, this is the timing improvement for x86: QEMU commit 2be4f242b50a8 and SeaBIOS commit 908a58c1d5ff QEMU startup time: .078 BIOS startup time: .060 Kernel setup time: .578 Total time: .716 QEMU with this patch series and SeaBIOS with this patch series QEMU startup time: .080 BIOS startup time: .039 Kernel setup time: .002 Total time: .121 QEMU startup time is the time between the start and the first kvm_entry. BIOS startup time is the time between the first kvm_entry and the start of function do_boot, in SeaBIOS. Kernel setup time is the time between the start of the function do_boot in SeaBIOS and the jump to the Linux kernel. As you can see, both the BIOS (because of ACPI tables and other configurations) and the Linux kernel boot (because of the copy to memory) are greatly improved with this new interface. Also, this new interface is an addon to the old interface. Both interfaces are compatible and interchangeable. ^ permalink raw reply [flat|nested] 60+ messages in thread
* Re: [Qemu-devel] QEMU fw_cfg DMA interface 2015-08-06 11:00 Marc Marí @ 2015-08-06 12:27 ` Stefan Hajnoczi 2015-08-06 12:37 ` Marc Marí 0 siblings, 1 reply; 60+ messages in thread From: Stefan Hajnoczi @ 2015-08-06 12:27 UTC (permalink / raw) To: Marc Marí Cc: Drew, seabios, linux-kernel, qemu-devel, Kevin O'Connor, Gerd Hoffmann, Laszlo On Thu, Aug 6, 2015 at 12:00 PM, Marc Marí <markmb@redhat.com> wrote: > When running a Linux guest on top of QEMU, using the -kernel options, this > is the timing improvement for x86: > > QEMU commit 2be4f242b50a8 and SeaBIOS commit 908a58c1d5ff > QEMU startup time: .078 > BIOS startup time: .060 > Kernel setup time: .578 > Total time: .716 > > QEMU with this patch series and SeaBIOS with this patch series > QEMU startup time: .080 > BIOS startup time: .039 > Kernel setup time: .002 > Total time: .121 Impressive results! Is this a fully-featured QEMU build or did you disable things? Is this the default SeaBIOS build or did you disable things? Stefan ^ permalink raw reply [flat|nested] 60+ messages in thread
* Re: [Qemu-devel] QEMU fw_cfg DMA interface 2015-08-06 12:27 ` Stefan Hajnoczi @ 2015-08-06 12:37 ` Marc Marí 2015-08-06 12:40 ` Stefan Hajnoczi 2015-08-06 15:30 ` Kevin O'Connor 0 siblings, 2 replies; 60+ messages in thread From: Marc Marí @ 2015-08-06 12:37 UTC (permalink / raw) To: Stefan Hajnoczi Cc: Drew, seabios, linux-kernel, qemu-devel, Kevin O'Connor, Gerd Hoffmann, Laszlo On Thu, 6 Aug 2015 13:27:16 +0100 Stefan Hajnoczi <stefanha@gmail.com> wrote: > On Thu, Aug 6, 2015 at 12:00 PM, Marc Marí <markmb@redhat.com> wrote: > > When running a Linux guest on top of QEMU, using the -kernel > > options, this is the timing improvement for x86: > > > > QEMU commit 2be4f242b50a8 and SeaBIOS commit 908a58c1d5ff > > QEMU startup time: .078 > > BIOS startup time: .060 > > Kernel setup time: .578 > > Total time: .716 > > > > QEMU with this patch series and SeaBIOS with this patch series > > QEMU startup time: .080 > > BIOS startup time: .039 > > Kernel setup time: .002 > > Total time: .121 > > Impressive results! > > Is this a fully-featured QEMU build or did you disable things? > > Is this the default SeaBIOS build or did you disable things? > This is the default QEMU configuration I get for my system. It's not a fully-featured QEMU, but it has a lot of things enabled. SeaBIOS has a default configuration (with debugging disabled). The QEMU configuration is: [...] tcg debug enabled no gprof enabled no sparse enabled no strip binaries yes profiler no static build no pixman system SDL support yes GTK support yes GNUTLS support yes GNUTLS hash yes GNUTLS gcrypt no GNUTLS nettle yes (2.7.1) VTE support yes curses support yes curl support yes mingw32 support no Audio drivers oss Block whitelist (rw) Block whitelist (ro) VirtFS support yes VNC support yes VNC TLS support yes VNC SASL support yes VNC JPEG support yes VNC PNG support yes xen support no brlapi support yes bluez support yes Documentation no GUEST_BASE yes PIE yes vde support yes netmap support no Linux AIO support yes ATTR/XATTR support yes Install blobs yes KVM support yes RDMA support yes TCG interpreter no fdt support yes preadv support yes fdatasync yes madvise yes posix_madvise yes sigev_thread_id yes uuid support yes libcap-ng support yes vhost-net support yes vhost-scsi support yes Trace backends nop spice support yes (0.12.7/0.12.5) rbd support yes xfsctl support no nss used yes libusb yes usb net redir yes OpenGL support no libiscsi support yes libnfs support no build guest agent yes QGA VSS support no QGA w32 disk info no seccomp support yes coroutine backend ucontext coroutine pool yes GlusterFS support yes Archipelago support no gcov gcov gcov enabled no TPM support yes libssh2 support yes TPM passthrough yes QOM debugging yes vhdx yes lzo support yes snappy support yes bzip2 support yes NUMA host support yes tcmalloc support no Marc ^ permalink raw reply [flat|nested] 60+ messages in thread
* Re: [Qemu-devel] QEMU fw_cfg DMA interface 2015-08-06 12:37 ` Marc Marí @ 2015-08-06 12:40 ` Stefan Hajnoczi 2015-08-06 15:30 ` Kevin O'Connor 1 sibling, 0 replies; 60+ messages in thread From: Stefan Hajnoczi @ 2015-08-06 12:40 UTC (permalink / raw) To: Marc Marí Cc: Drew, seabios, linux-kernel, qemu-devel, Kevin O'Connor, Gerd Hoffmann, Laszlo On Thu, Aug 6, 2015 at 1:37 PM, Marc Marí <markmb@redhat.com> wrote: > On Thu, 6 Aug 2015 13:27:16 +0100 > Stefan Hajnoczi <stefanha@gmail.com> wrote: > >> On Thu, Aug 6, 2015 at 12:00 PM, Marc Marí <markmb@redhat.com> wrote: >> > When running a Linux guest on top of QEMU, using the -kernel >> > options, this is the timing improvement for x86: >> > >> > QEMU commit 2be4f242b50a8 and SeaBIOS commit 908a58c1d5ff >> > QEMU startup time: .078 >> > BIOS startup time: .060 >> > Kernel setup time: .578 >> > Total time: .716 >> > >> > QEMU with this patch series and SeaBIOS with this patch series >> > QEMU startup time: .080 >> > BIOS startup time: .039 >> > Kernel setup time: .002 >> > Total time: .121 >> >> Impressive results! >> >> Is this a fully-featured QEMU build or did you disable things? >> >> Is this the default SeaBIOS build or did you disable things? >> > > This is the default QEMU configuration I get for my system. It's not a > fully-featured QEMU, but it has a lot of things enabled. SeaBIOS > has a default configuration (with debugging disabled). That's great. Since SeaBIOS is a default configuration, the remaining BIOS startup time is amenable to more optimizations in the future. Stefan ^ permalink raw reply [flat|nested] 60+ messages in thread
* Re: [Qemu-devel] QEMU fw_cfg DMA interface 2015-08-06 12:37 ` Marc Marí 2015-08-06 12:40 ` Stefan Hajnoczi @ 2015-08-06 15:30 ` Kevin O'Connor 2015-08-06 15:53 ` Marc Marí 1 sibling, 1 reply; 60+ messages in thread From: Kevin O'Connor @ 2015-08-06 15:30 UTC (permalink / raw) To: Marc Marí Cc: Drew, Stefan Hajnoczi, seabios, linux-kernel, qemu-devel, Gerd Hoffmann, Laszlo On Thu, Aug 06, 2015 at 02:37:45PM +0200, Marc Marí wrote: > On Thu, 6 Aug 2015 13:27:16 +0100 > Stefan Hajnoczi <stefanha@gmail.com> wrote: > > > On Thu, Aug 6, 2015 at 12:00 PM, Marc Marí <markmb@redhat.com> wrote: > > > When running a Linux guest on top of QEMU, using the -kernel > > > options, this is the timing improvement for x86: > > > > > > QEMU commit 2be4f242b50a8 and SeaBIOS commit 908a58c1d5ff > > > QEMU startup time: .078 > > > BIOS startup time: .060 > > > Kernel setup time: .578 > > > Total time: .716 > > > > > > QEMU with this patch series and SeaBIOS with this patch series > > > QEMU startup time: .080 > > > BIOS startup time: .039 > > > Kernel setup time: .002 > > > Total time: .121 > > > > Impressive results! > > > > Is this a fully-featured QEMU build or did you disable things? > > > > Is this the default SeaBIOS build or did you disable things? > > > > This is the default QEMU configuration I get for my system. It's not a > fully-featured QEMU, but it has a lot of things enabled. SeaBIOS > has a default configuration (with debugging disabled). Thanks! What qemu command-line did you use during testing? Also, do you have a quick primer on how to use the kvm trace stuff to obtain timings? -Kevin ^ permalink raw reply [flat|nested] 60+ messages in thread
* Re: [Qemu-devel] QEMU fw_cfg DMA interface 2015-08-06 15:30 ` Kevin O'Connor @ 2015-08-06 15:53 ` Marc Marí 2015-08-07 4:30 ` Kevin O'Connor 0 siblings, 1 reply; 60+ messages in thread From: Marc Marí @ 2015-08-06 15:53 UTC (permalink / raw) To: Kevin O'Connor Cc: Drew, Stefan Hajnoczi, seabios, linux-kernel, qemu-devel, Gerd Hoffmann, Laszlo [-- Attachment #1: Type: text/plain, Size: 2332 bytes --] On Thu, 6 Aug 2015 11:30:43 -0400 "Kevin O'Connor" <kevin@koconnor.net> wrote: > On Thu, Aug 06, 2015 at 02:37:45PM +0200, Marc Marí wrote: > > On Thu, 6 Aug 2015 13:27:16 +0100 > > Stefan Hajnoczi <stefanha@gmail.com> wrote: > > > > > On Thu, Aug 6, 2015 at 12:00 PM, Marc Marí <markmb@redhat.com> > > > wrote: > > > > When running a Linux guest on top of QEMU, using the -kernel > > > > options, this is the timing improvement for x86: > > > > > > > > QEMU commit 2be4f242b50a8 and SeaBIOS commit 908a58c1d5ff > > > > QEMU startup time: .078 > > > > BIOS startup time: .060 > > > > Kernel setup time: .578 > > > > Total time: .716 > > > > > > > > QEMU with this patch series and SeaBIOS with this patch series > > > > QEMU startup time: .080 > > > > BIOS startup time: .039 > > > > Kernel setup time: .002 > > > > Total time: .121 > > > > > > Impressive results! > > > > > > Is this a fully-featured QEMU build or did you disable things? > > > > > > Is this the default SeaBIOS build or did you disable things? > > > > > > > This is the default QEMU configuration I get for my system. It's > > not a fully-featured QEMU, but it has a lot of things enabled. > > SeaBIOS has a default configuration (with debugging disabled). > > Thanks! > > What qemu command-line did you use during testing? Also, do you have > a quick primer on how to use the kvm trace stuff to obtain timings? > The command line I used is: x86_64-softmmu/qemu-system-x86_64 --enable-kvm \ -kernel /boot/vmlinuz-4.0.7-300.fc22.x86_64 \ -L pc-bios/optionrom/ \ -bios roms/seabios/out/bios.bin -nographic And I used perf (and two out instructions in the BIOS) to measure the times: perf record -a -e kvm:\* -e sched:sched_process_exec And searching for sched:sched_process_exec, kvm:kvm_entry, pio_write at 0xf5 and pio_write at 0xf4. Out at 0xf5 is the one in "do_boot" function, and out at 0xf4 is the one just before the jump to the Linux kernel. I attach the script I've been using. It can be improved, but it works. It can be run like this: sudo ../../results/run_test.sh x86_64-softmmu/qemu-system-x86_64 \ --enable-kvm -kernel /boot/vmlinuz-4.0.8-300.fc22.x86_64 \ -L pc-bios/optionrom/ \ -bios roms/seabios/out/bios.bin -nographic Thanks Marc [-- Attachment #2: run_test.sh --] [-- Type: application/x-shellscript, Size: 1401 bytes --] ^ permalink raw reply [flat|nested] 60+ messages in thread
* Re: [Qemu-devel] QEMU fw_cfg DMA interface 2015-08-06 15:53 ` Marc Marí @ 2015-08-07 4:30 ` Kevin O'Connor 2015-08-17 22:08 ` Gerd Hoffmann 0 siblings, 1 reply; 60+ messages in thread From: Kevin O'Connor @ 2015-08-07 4:30 UTC (permalink / raw) To: Marc Marí Cc: Drew, Stefan Hajnoczi, seabios, qemu-devel, Gerd Hoffmann, Laszlo Trimming CC list. On Thu, Aug 06, 2015 at 05:53:26PM +0200, Marc Marí wrote: > On Thu, 6 Aug 2015 11:30:43 -0400 > "Kevin O'Connor" <kevin@koconnor.net> wrote: > > On Thu, Aug 06, 2015 at 02:37:45PM +0200, Marc Marí wrote: > > > On Thu, 6 Aug 2015 13:27:16 +0100 > > > Stefan Hajnoczi <stefanha@gmail.com> wrote: > > > > > > > On Thu, Aug 6, 2015 at 12:00 PM, Marc Marí <markmb@redhat.com> > > > > wrote: > > > > > When running a Linux guest on top of QEMU, using the -kernel > > > > > options, this is the timing improvement for x86: > > > > > > > > > > QEMU commit 2be4f242b50a8 and SeaBIOS commit 908a58c1d5ff > > > > > QEMU startup time: .078 > > > > > BIOS startup time: .060 > > > > > Kernel setup time: .578 > > > > > Total time: .716 > > > > > > > > > > QEMU with this patch series and SeaBIOS with this patch series > > > > > QEMU startup time: .080 > > > > > BIOS startup time: .039 > > > > > Kernel setup time: .002 > > > > > Total time: .121 > > > > > > > > Impressive results! > > > > > > > > Is this a fully-featured QEMU build or did you disable things? > > > > > > > > Is this the default SeaBIOS build or did you disable things? > > > > > > > > > > This is the default QEMU configuration I get for my system. It's > > > not a fully-featured QEMU, but it has a lot of things enabled. > > > SeaBIOS has a default configuration (with debugging disabled). > > > > Thanks! > > > > What qemu command-line did you use during testing? Also, do you have > > a quick primer on how to use the kvm trace stuff to obtain timings? > > > > The command line I used is: > > x86_64-softmmu/qemu-system-x86_64 --enable-kvm \ > -kernel /boot/vmlinuz-4.0.7-300.fc22.x86_64 \ > -L pc-bios/optionrom/ \ > -bios roms/seabios/out/bios.bin -nographic Thanks. One thing that immediately pops up is that even though "-nographic" is used, it looks like QEMU still emulates a VGA device and that device has an option rom that takes several milliseconds to init the display hardware. Adding "-device VGA,romfile=" to the qemu command line will further reduce the boot time by avoiding this hardware init delay. -Kevin ^ permalink raw reply [flat|nested] 60+ messages in thread
* Re: [Qemu-devel] QEMU fw_cfg DMA interface 2015-08-07 4:30 ` Kevin O'Connor @ 2015-08-17 22:08 ` Gerd Hoffmann 0 siblings, 0 replies; 60+ messages in thread From: Gerd Hoffmann @ 2015-08-17 22:08 UTC (permalink / raw) To: Kevin O'Connor Cc: Drew, Stefan Hajnoczi, seabios, qemu-devel, Marc Marí, Laszlo Hi, > > The command line I used is: > > > > x86_64-softmmu/qemu-system-x86_64 --enable-kvm \ > > -kernel /boot/vmlinuz-4.0.7-300.fc22.x86_64 \ > > -L pc-bios/optionrom/ \ > > -bios roms/seabios/out/bios.bin -nographic > > Thanks. One thing that immediately pops up is that even though > "-nographic" is used, it looks like QEMU still emulates a VGA device > and that device has an option rom that takes several milliseconds to > init the display hardware. Adding "-device VGA,romfile=" to the qemu > command line will further reduce the boot time by avoiding this > hardware init delay. Same goes for the NIC btw. cheers, Gerd ^ permalink raw reply [flat|nested] 60+ messages in thread
end of thread, other threads:[~2015-10-08 10:02 UTC | newest] Thread overview: 60+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2015-10-01 12:14 [Qemu-devel] QEMU fw_cfg DMA interface Marc Marí 2015-10-01 12:16 ` [Qemu-devel] [PATCH v4 0/7] " Marc Marí 2015-10-01 12:16 ` [Qemu-devel] [PATCH v4 1/7] fw_cfg: document fw_cfg_modify_iXX() update functions Marc Marí 2015-10-01 12:16 ` [Qemu-devel] [PATCH v4 2/7] fw_cfg DMA interface documentation Marc Marí 2015-10-01 14:41 ` Laszlo Ersek 2015-10-01 12:16 ` [Qemu-devel] [PATCH v4 3/7] Implement fw_cfg DMA interface Marc Marí 2015-10-01 14:36 ` Laszlo Ersek 2015-10-01 15:52 ` Marc Marí 2015-10-01 17:18 ` Peter Maydell 2015-10-01 19:20 ` Laszlo Ersek 2015-10-06 14:44 ` Stefan Hajnoczi 2015-10-06 14:53 ` Peter Maydell 2015-10-08 9:07 ` Stefan Hajnoczi 2015-10-08 10:01 ` Marc Marí 2015-10-06 14:54 ` Marc Marí 2015-10-01 12:16 ` [Qemu-devel] [PATCH v4 4/7] Enable fw_cfg DMA interface for ARM Marc Marí 2015-10-01 14:42 ` Laszlo Ersek 2015-10-01 12:16 ` [Qemu-devel] [PATCH v4 5/7] Enable fw_cfg DMA interface for x86 Marc Marí 2015-10-01 14:48 ` Laszlo Ersek 2015-10-01 12:16 ` [Qemu-devel] [PATCH v4 6/7] Make the kernel image in the fw_cfg DMA interface bootable Marc Marí 2015-10-01 15:25 ` Laszlo Ersek 2015-10-01 16:02 ` Kevin O'Connor 2015-10-01 16:10 ` Laszlo Ersek 2015-10-01 18:15 ` Marc Marí 2015-10-02 8:16 ` Gerd Hoffmann 2015-10-02 8:24 ` Marc Marí 2015-10-02 9:01 ` Gerd Hoffmann 2015-10-02 11:47 ` Laszlo Ersek 2015-10-02 12:07 ` Gerd Hoffmann 2015-10-02 13:25 ` Laszlo Ersek 2015-10-02 13:30 ` Laszlo Ersek 2015-10-03 0:05 ` Jordan Justen 2015-10-02 13:38 ` Kevin O'Connor 2015-10-05 9:18 ` Gerd Hoffmann 2015-10-02 8:09 ` Gerd Hoffmann 2015-10-02 13:40 ` Kevin O'Connor 2015-10-02 13:50 ` Laszlo Ersek 2015-10-02 15:24 ` Daniel P. Berrange 2015-10-05 9:26 ` Gerd Hoffmann 2015-10-01 12:16 ` [Qemu-devel] [PATCH v4 7/7] fw_cfg: Define a static signature to be returned on DMA port reads Marc Marí 2015-10-01 16:07 ` Laszlo Ersek 2015-10-01 17:02 ` Kevin O'Connor 2015-10-01 17:17 ` Laszlo Ersek 2015-10-01 13:19 ` [Qemu-devel] [PATCH v4 0/7] fw_cfg DMA interface Kevin O'Connor 2015-10-01 16:03 ` [Qemu-devel] QEMU " Eric Blake 2015-10-01 16:11 ` Eric Blake 2015-10-01 16:19 ` Laszlo Ersek 2015-10-01 16:17 ` Laszlo Ersek 2015-10-01 16:21 ` Eric Blake 2015-10-01 16:34 ` Laszlo Ersek -- strict thread matches above, loose matches on Subject: below -- 2015-09-18 8:58 Marc Marí 2015-08-31 9:08 Marc Marí 2015-08-06 11:00 Marc Marí 2015-08-06 12:27 ` Stefan Hajnoczi 2015-08-06 12:37 ` Marc Marí 2015-08-06 12:40 ` Stefan Hajnoczi 2015-08-06 15:30 ` Kevin O'Connor 2015-08-06 15:53 ` Marc Marí 2015-08-07 4:30 ` Kevin O'Connor 2015-08-17 22:08 ` Gerd Hoffmann
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).