From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from eggs.gnu.org ([2001:4830:134:3::10]:46875) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1ZWL78-0005FK-KT for qemu-devel@nongnu.org; Mon, 31 Aug 2015 05:11:00 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1ZWL76-0007SW-NH for qemu-devel@nongnu.org; Mon, 31 Aug 2015 05:10:54 -0400 Received: from mx1.redhat.com ([209.132.183.28]:41303) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1ZWL76-0007SE-DV for qemu-devel@nongnu.org; Mon, 31 Aug 2015 05:10:52 -0400 From: =?UTF-8?q?Marc=20Mar=C3=AD?= Date: Mon, 31 Aug 2015 11:10:15 +0200 Message-Id: <1441012217-8213-4-git-send-email-markmb@redhat.com> In-Reply-To: <1441012217-8213-1-git-send-email-markmb@redhat.com> References: <1441012133-8154-1-git-send-email-markmb@redhat.com> <1441012217-8213-1-git-send-email-markmb@redhat.com> MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: quoted-printable Subject: [Qemu-devel] [PATCH v2 3/5] Implement fw_cfg DMA interface List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: qemu-devel@nongnu.org Cc: Drew , Stefan Hajnoczi , Kevin O'Connor , Gerd Hoffmann , =?UTF-8?q?Marc=20Mar=C3=AD?= , 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=C3=AD --- hw/arm/virt.c | 2 +- hw/nvram/fw_cfg.c | 261 ++++++++++++++++++++++++++++++++++++++++= +++--- include/hw/nvram/fw_cfg.h | 15 ++- 3 files changed, 260 insertions(+), 18 deletions(-) diff --git a/hw/arm/virt.c b/hw/arm/virt.c index d5a8417..b88c104 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -620,7 +620,7 @@ static void create_fw_cfg(const VirtBoardInfo *vbi) hwaddr size =3D vbi->memmap[VIRT_FW_CFG].size; char *nodename; =20 - fw_cfg_init_mem_wide(base + 8, base, 8); + fw_cfg_init_mem_wide(base + 8, base, 8, 0, NULL); =20 nodename =3D 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 88481b7..7e5ba96 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,8 @@ #include "qemu/error-report.h" #include "qemu/config-file.h" =20 -#define FW_CFG_SIZE 2 +#define FW_CFG_IO_SIZE 12 /* Address aligned to 4 bytes */ +#define FW_CFG_CTL_SIZE 2 #define FW_CFG_NAME "fw_cfg" #define FW_CFG_PATH "/machine/" FW_CFG_NAME =20 @@ -42,6 +44,15 @@ #define FW_CFG_IO(obj) OBJECT_CHECK(FWCfgIoState, (obj), TYPE_FW_CFG_I= O) #define FW_CFG_MEM(obj) OBJECT_CHECK(FWCfgMemState, (obj), TYPE_FW_CFG_M= EM) =20 +/* 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 + 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; }; =20 struct FWCfgIoState { @@ -75,7 +90,7 @@ struct FWCfgMemState { FWCfgState parent_obj; /*< public >*/ =20 - MemoryRegion ctl_iomem, data_iomem; + MemoryRegion ctl_iomem, data_iomem, dma_iomem; uint32_t data_width; MemoryRegionOps wide_data_ops; }; @@ -294,6 +309,142 @@ static void fw_cfg_data_mem_write(void *opaque, hwa= ddr addr, } while (i); } =20 +static void fw_cfg_dma_transfer(FWCfgState *s) +{ + dma_addr_t len; + uint8_t *ptr; + void *addr; + FWCfgDmaAccess dma; + int arch =3D !!(s->cur_entry & FW_CFG_ARCH_LOCAL); + FWCfgEntry *e =3D &s->entries[arch][s->cur_entry & FW_CFG_ENTRY_MASK= ]; + + len =3D sizeof(dma); + addr =3D dma_memory_map(s->dma_as, s->dma_addr, &len, + DMA_DIRECTION_FROM_DEVICE); + + s->dma_addr =3D 0; + + if (!addr || !len) { + return; + } + + dma.address =3D be64_to_cpu(*(uint64_t *)(addr + + offsetof(FWCfgDmaAccess, address))); + dma.length =3D be32_to_cpu(*(uint32_t *)(addr + + offsetof(FWCfgDmaAccess, length))); + dma.control =3D be32_to_cpu(*(uint32_t *)(addr + + offsetof(FWCfgDmaAccess, control))); + + if (dma.control & FW_CFG_DMA_CTL_ERROR) { + goto out; + } + + if (!(dma.control & (FW_CFG_DMA_CTL_READ | FW_CFG_DMA_CTL_SKIP))) { + goto out; + } + + while (dma.length > 0) { + if (s->cur_entry =3D=3D FW_CFG_INVALID || !e->data || + s->cur_offset >=3D e->len) { + len =3D dma.length; + + /* If the access is not a read access, it will be a skip acc= ess, + * tested before. + */ + if (dma.control & FW_CFG_DMA_CTL_READ) { + ptr =3D dma_memory_map(s->dma_as, dma.address, &len, + DMA_DIRECTION_FROM_DEVICE); + if (!ptr || !len) { + dma.control |=3D FW_CFG_DMA_CTL_ERROR; + goto out; + } + + memset(ptr, 0, len); + + dma_memory_unmap(s->dma_as, ptr, len, + DMA_DIRECTION_FROM_DEVICE, len); + } + + } else { + if (dma.length <=3D e->len) { + len =3D dma.length; + } else { + len =3D e->len; + } + + 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 acc= ess, + * tested before. + */ + if (dma.control & FW_CFG_DMA_CTL_READ) { + ptr =3D dma_memory_map(s->dma_as, dma.address, &len, + DMA_DIRECTION_FROM_DEVICE); + if (!ptr || !len) { + dma.control |=3D FW_CFG_DMA_CTL_ERROR; + goto out; + } + + memcpy(ptr, &e->data[s->cur_offset], len); + + dma_memory_unmap(s->dma_as, ptr, len, + DMA_DIRECTION_FROM_DEVICE, len); + } + + s->cur_offset +=3D len; + + } + + dma.address +=3D len; + dma.length -=3D len; + dma.control =3D 0; + + *(uint64_t *)(addr + offsetof(FWCfgDmaAccess, address)) =3D + cpu_to_be64(dma.address)= ; + *(uint32_t *)(addr + offsetof(FWCfgDmaAccess, length)) =3D + cpu_to_be32(dma.length); + *(uint32_t *)(addr + offsetof(FWCfgDmaAccess, control)) =3D + cpu_to_be32(dma.control)= ; + } + + trace_fw_cfg_read(s, 0); + +out: + dma_memory_unmap(s->dma_as, addr, sizeof(dma), + DMA_DIRECTION_FROM_DEVICE, sizeof(dma)); + return; + +} + +static void fw_cfg_dma_mem_write(void *opaque, hwaddr addr, + uint64_t value, unsigned size) +{ + FWCfgState *s =3D opaque; + + if (size =3D=3D 4) { + if (addr =3D=3D 0) { + /* FWCfgDmaAccess high address */ + s->dma_addr =3D (0xFFFFFFFF & value) << 32; + } else if (addr =3D=3D 4) { + /* FWCfgDmaAccess low address */ + s->dma_addr |=3D value; + fw_cfg_dma_transfer(s); + } + } else if (size =3D=3D 8 && addr =3D=3D 0) { + s->dma_addr =3D 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 =3D=3D 4 && (addr =3D=3D 0 || addr =3D=3D = 4)) || + (size =3D=3D 8 && addr =3D=3D 0)); +} + static bool fw_cfg_data_mem_valid(void *opaque, hwaddr addr, unsigned size, bool is_write) { @@ -321,20 +472,35 @@ static uint64_t fw_cfg_comb_read(void *opaque, hwad= dr addr, static void fw_cfg_comb_write(void *opaque, hwaddr addr, uint64_t value, unsigned size) { - switch (size) { + FWCfgState *s; + + switch (addr) { + case 0: + fw_cfg_select(opaque, (uint16_t)value); + break; case 1: fw_cfg_write(opaque, (uint8_t)value); break; - case 2: - fw_cfg_select(opaque, (uint16_t)value); + case 4: + /* FWCfgDmaAccess low address */ + s =3D opaque; + s->dma_addr |=3D value; + fw_cfg_dma_transfer(s); break; + case 8: + /* FWCfgDmaAccess high address */ + s =3D opaque; + s->dma_addr =3D (0xFFFFFFFF & value) << 32; } } =20 static bool fw_cfg_comb_valid(void *opaque, hwaddr addr, unsigned size, bool is_write) { - return (size =3D=3D 1) || (is_write && size =3D=3D 2); + FWCfgState *s =3D opaque; + + return (size =3D=3D 1) || (is_write && size =3D=3D 2) || + (s->dma_enabled && is_write && addr >=3D 4); } =20 static const MemoryRegionOps fw_cfg_ctl_mem_ops =3D { @@ -361,6 +527,12 @@ static const MemoryRegionOps fw_cfg_comb_mem_ops =3D= { .valid.accepts =3D fw_cfg_comb_valid, }; =20 +static const MemoryRegionOps fw_cfg_dma_mem_ops =3D { + .write =3D fw_cfg_dma_mem_write, + .endianness =3D DEVICE_BIG_ENDIAN, + .valid.accepts =3D fw_cfg_dma_mem_valid, +}; + static void fw_cfg_reset(DeviceState *d) { FWCfgState *s =3D FW_CFG(d); @@ -401,6 +573,22 @@ static bool is_version_1(void *opaque, int version_i= d) return version_id =3D=3D 1; } =20 +static bool fw_cfg_dma_enabled(void *opaque) +{ + FWCfgState *s =3D opaque; + + return s->dma_enabled; +} + +static VMStateDescription vmstate_fw_cfg_dma =3D { + .name =3D "fw_cfg/dma", + .needed =3D fw_cfg_dma_enabled, + .fields =3D (VMStateField[]) { + VMSTATE_UINT64(dma_addr, FWCfgState), + VMSTATE_END_OF_LIST() + }, +}; + static const VMStateDescription vmstate_fw_cfg =3D { .name =3D "fw_cfg", .version_id =3D 2, @@ -410,6 +598,10 @@ static const VMStateDescription vmstate_fw_cfg =3D { VMSTATE_UINT16_HACK(cur_offset, FWCfgState, is_version_1), VMSTATE_UINT32_V(cur_offset, FWCfgState, 2), VMSTATE_END_OF_LIST() + }, + .subsections =3D (const VMStateDescription*[]) { + &vmstate_fw_cfg_dma, + NULL, } }; =20 @@ -595,7 +787,6 @@ static void fw_cfg_init1(DeviceState *dev) qdev_init_nofail(dev); =20 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 =3D=3D D= T_NOGRAPHIC)); fw_cfg_add_i16(s, FW_CFG_NB_CPUS, (uint16_t)smp_cpus); @@ -607,22 +798,45 @@ static void fw_cfg_init1(DeviceState *dev) qemu_add_machine_init_done_notifier(&s->machine_ready); } =20 -FWCfgState *fw_cfg_init_io(uint32_t iobase) +FWCfgState *fw_cfg_init_io_dma(uint32_t iobase, AddressSpace *dma_as) { DeviceState *dev; + FWCfgState *s; + uint32_t version =3D FW_CFG_VERSION; =20 dev =3D qdev_create(NULL, TYPE_FW_CFG_IO); qdev_prop_set_uint32(dev, "iobase", iobase); + fw_cfg_init1(dev); + s =3D FW_CFG(dev); + + if (dma_as) { + /* 64 bits for the address field */ + s->dma_as =3D dma_as; + s->dma_enabled =3D true; + s->dma_addr =3D 0; + + version |=3D FW_CFG_VERSION_DMA; + } + + fw_cfg_add_i32(s, FW_CFG_ID, version); =20 - return FW_CFG(dev); + return s; } =20 -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, 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 =3D FW_CFG_VERSION; =20 dev =3D qdev_create(NULL, TYPE_FW_CFG_MEM); qdev_prop_set_uint32(dev, "data_width", data_width); @@ -633,13 +847,26 @@ FWCfgState *fw_cfg_init_mem_wide(hwaddr ctl_addr, h= waddr data_addr, sysbus_mmio_map(sbd, 0, ctl_addr); sysbus_mmio_map(sbd, 1, data_addr); =20 - return FW_CFG(dev); + s =3D FW_CFG(dev); + + if (dma_addr && dma_as) { + s->dma_as =3D dma_as; + s->dma_enabled =3D true; + s->dma_addr =3D 0; + sysbus_mmio_map(sbd, 2, dma_addr); + version |=3D FW_CFG_VERSION_DMA; + } + + fw_cfg_add_i32(s, FW_CFG_ID, version); + + return s; } =20 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_siz= e); + fw_cfg_data_mem_ops.valid.max_access_siz= e, + 0, NULL); } =20 =20 @@ -675,7 +902,7 @@ static void fw_cfg_io_realize(DeviceState *dev, Error= **errp) SysBusDevice *sbd =3D SYS_BUS_DEVICE(dev); =20 memory_region_init_io(&s->comb_iomem, OBJECT(s), &fw_cfg_comb_mem_op= s, - FW_CFG(s), "fwcfg", FW_CFG_SIZE); + FW_CFG(s), "fwcfg", FW_CFG_IO_SIZE); sysbus_add_io(sbd, s->iobase, &s->comb_iomem); } =20 @@ -707,7 +934,7 @@ static void fw_cfg_mem_realize(DeviceState *dev, Erro= r **errp) const MemoryRegionOps *data_ops =3D &fw_cfg_data_mem_ops; =20 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); =20 if (s->data_width > data_ops->valid.max_access_size) { @@ -725,6 +952,10 @@ static void fw_cfg_mem_realize(DeviceState *dev, Err= or **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); + + 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); } =20 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..d0cbc88 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; =20 +/* Control as first field allows for different structures selected by th= is + * 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); =20 @@ -77,10 +86,12 @@ void fw_cfg_add_file_callback(FWCfgState *s, const ch= ar *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, 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); =20 FWCfgState *fw_cfg_find(void); =20 --=20 2.4.3