From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mailman by lists.gnu.org with tmda-scanned (Exim 4.43) id 1Mwvfb-0004mh-DG for qemu-devel@nongnu.org; Sun, 11 Oct 2009 06:28:55 -0400 Received: from exim by lists.gnu.org with spam-scanned (Exim 4.43) id 1MwvfV-0004mF-DV for qemu-devel@nongnu.org; Sun, 11 Oct 2009 06:28:53 -0400 Received: from [199.232.76.173] (port=34437 helo=monty-python.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1MwvfV-0004m5-An for qemu-devel@nongnu.org; Sun, 11 Oct 2009 06:28:49 -0400 Received: from mx1.redhat.com ([209.132.183.28]:33197) by monty-python.gnu.org with esmtp (Exim 4.60) (envelope-from ) id 1MwvfU-0007RD-7f for qemu-devel@nongnu.org; Sun, 11 Oct 2009 06:28:49 -0400 Date: Sun, 11 Oct 2009 12:26:36 +0200 From: "Michael S. Tsirkin" Message-ID: <20091011102636.GA15567@redhat.com> References: <1255069742-15724-1-git-send-email-yamahata@valinux.co.jp> <1255069742-15724-24-git-send-email-yamahata@valinux.co.jp> MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline In-Reply-To: <1255069742-15724-24-git-send-email-yamahata@valinux.co.jp> Subject: [Qemu-devel] Re: [PATCH V5 23/29] pci: pcie host and mmcfg support. List-Id: qemu-devel.nongnu.org List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: Isaku Yamahata Cc: qemu-devel@nongnu.org On Fri, Oct 09, 2009 at 03:28:56PM +0900, Isaku Yamahata wrote: > This patch adds common routines for pcie host bridge and pcie mmcfg. > This will be used by q35 based chipset emulation. > > Signed-off-by: Isaku Yamahata > --- > Makefile.target | 2 +- > hw/hw.h | 12 ++++ > hw/pci.c | 86 ++++++++++++++++++++++++----- > hw/pci.h | 27 ++++++++- > hw/pcie_host.c | 166 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ > hw/pcie_host.h | 53 ++++++++++++++++++ > 6 files changed, 327 insertions(+), 19 deletions(-) > create mode 100644 hw/pcie_host.c > create mode 100644 hw/pcie_host.h > > diff --git a/Makefile.target b/Makefile.target > index cc555cb..ced7f65 100644 > --- a/Makefile.target > +++ b/Makefile.target > @@ -154,7 +154,7 @@ endif #CONFIG_BSD_USER > # System emulator target > ifdef CONFIG_SOFTMMU > > -obj-y = vl.o monitor.o pci.o pci_host.o machine.o gdbstub.o > +obj-y = vl.o monitor.o pci.o pci_host.o pcie_host.o machine.o gdbstub.o > # virtio has to be here due to weird dependency between PCI and virtio-net. > # need to fix this properly > obj-y += virtio-blk.o virtio-balloon.o virtio-net.o virtio-console.o virtio-pci.o > diff --git a/hw/hw.h b/hw/hw.h > index e867af4..fba3167 100644 > --- a/hw/hw.h > +++ b/hw/hw.h > @@ -494,6 +494,18 @@ extern const VMStateDescription vmstate_pci_device; > + type_check(PCIDevice,typeof_field(_state, _field)) \ > } > > +extern const VMStateDescription vmstate_pcie_device; > + > +#define VMSTATE_PCIE_DEVICE(_field, _state) { \ > + .name = (stringify(_field)), \ > + .version_id = 2, \ > + .size = sizeof(PCIDevice), \ > + .vmsd = &vmstate_pcie_device, \ > + .flags = VMS_STRUCT, \ > + .offset = offsetof(_state, _field) \ > + + type_check(PCIDevice,typeof_field(_state, _field)) \ > +} > + > extern const VMStateDescription vmstate_i2c_slave; > > #define VMSTATE_I2C_SLAVE(_field, _state) { \ > diff --git a/hw/pci.c b/hw/pci.c > index 99b420f..4436e12 100644 > --- a/hw/pci.c > +++ b/hw/pci.c > @@ -23,6 +23,7 @@ > */ > #include "hw.h" > #include "pci.h" > +#include "pci_host.h" > #include "monitor.h" > #include "net.h" > #include "sysemu.h" > @@ -241,18 +242,24 @@ int pci_bus_num(PCIBus *s) > static int get_pci_config_device(QEMUFile *f, void *pv, size_t size) > { > PCIDevice *s = container_of(pv, PCIDevice, config); > - uint8_t config[PCI_CONFIG_SPACE_SIZE]; > + uint8_t *config; > int i; > > - assert(size == sizeof config); > - qemu_get_buffer(f, config, sizeof config); > - for (i = 0; i < sizeof config; ++i) > - if ((config[i] ^ s->config[i]) & s->cmask[i] & ~s->wmask[i]) > + assert(size == pci_config_size(s)); > + config = qemu_malloc(size); > + > + qemu_get_buffer(f, config, size); > + for (i = 0; i < size; ++i) { > + if ((config[i] ^ s->config[i]) & s->cmask[i] & ~s->wmask[i]) { > + qemu_free(config); > return -EINVAL; > - memcpy(s->config, config, sizeof config); > + } > + } > + memcpy(s->config, config, size); > > pci_update_mappings(s); > > + qemu_free(config); > return 0; > } > > @@ -260,6 +267,7 @@ static int get_pci_config_device(QEMUFile *f, void *pv, size_t size) > static void put_pci_config_device(QEMUFile *f, void *pv, size_t size) > { > const uint8_t *v = pv; > + assert(size == pci_config_size(container_of(pv, PCIDevice, config))); > qemu_put_buffer(f, v, size); > } > > @@ -276,21 +284,42 @@ const VMStateDescription vmstate_pci_device = { > .minimum_version_id_old = 1, > .fields = (VMStateField []) { > VMSTATE_INT32_LE(version_id, PCIDevice), > - VMSTATE_SINGLE(config, PCIDevice, 0, vmstate_info_pci_config, > - typeof_field(PCIDevice,config)), > + VMSTATE_ARRAY_POINTER(config, PCIDevice, 0, vmstate_info_pci_config, > + typeof_field(PCIDevice, config), > + PCI_CONFIG_SPACE_SIZE), > + VMSTATE_INT32_ARRAY_V(irq_state, PCIDevice, PCI_NUM_PINS, 2), > + VMSTATE_END_OF_LIST() > + } > +}; > + > +const VMStateDescription vmstate_pcie_device = { > + .name = "PCIDevice", > + .version_id = 2, > + .minimum_version_id = 1, > + .minimum_version_id_old = 1, > + .fields = (VMStateField []) { > + VMSTATE_INT32_LE(version_id, PCIDevice), > + VMSTATE_ARRAY_POINTER(config, PCIDevice, 0, vmstate_info_pci_config, > + typeof_field(PCIDevice, config), > + PCIE_CONFIG_SPACE_SIZE), > VMSTATE_INT32_ARRAY_V(irq_state, PCIDevice, PCI_NUM_PINS, 2), > VMSTATE_END_OF_LIST() > } > }; > > +static inline const VMStateDescription *pci_get_vmstate(PCIDevice *s) > +{ > + return pci_is_express(s) ? &vmstate_pcie_device : &vmstate_pci_device; > +} > + > void pci_device_save(PCIDevice *s, QEMUFile *f) > { > - vmstate_save_state(f, &vmstate_pci_device, s); > + vmstate_save_state(f, pci_get_vmstate(s), s); > } > > int pci_device_load(PCIDevice *s, QEMUFile *f) > { > - return vmstate_load_state(f, &vmstate_pci_device, s, s->version_id); > + return vmstate_load_state(f, pci_get_vmstate(s), s, s->version_id); > } > > static int pci_set_default_subsystem_id(PCIDevice *pci_dev) > @@ -399,14 +428,34 @@ static void pci_init_cmask(PCIDevice *dev) > static void pci_init_wmask(PCIDevice *dev) > { > int i; > + int config_size = pci_config_size(dev); > + > dev->wmask[PCI_CACHE_LINE_SIZE] = 0xff; > dev->wmask[PCI_INTERRUPT_LINE] = 0xff; > dev->wmask[PCI_COMMAND] = PCI_COMMAND_IO | PCI_COMMAND_MEMORY > | PCI_COMMAND_MASTER; > - for (i = PCI_CONFIG_HEADER_SIZE; i < PCI_CONFIG_SPACE_SIZE; ++i) > + for (i = PCI_CONFIG_HEADER_SIZE; i < config_size; ++i) > dev->wmask[i] = 0xff; > } > > +static void pci_config_alloc(PCIDevice *pci_dev) > +{ > + int config_size = pci_config_size(pci_dev); > + > + pci_dev->config = qemu_mallocz(config_size); > + pci_dev->cmask = qemu_mallocz(config_size); > + pci_dev->wmask = qemu_mallocz(config_size); > + pci_dev->used = qemu_mallocz(config_size); > +} > + > +static void pci_config_free(PCIDevice *pci_dev) > +{ > + qemu_free(pci_dev->config); > + qemu_free(pci_dev->cmask); > + qemu_free(pci_dev->wmask); > + qemu_free(pci_dev->used); > +} > + > /* -1 for devfn means auto assign */ > static PCIDevice *do_pci_register_device(PCIDevice *pci_dev, PCIBus *bus, > const char *name, int devfn, > @@ -427,6 +476,7 @@ static PCIDevice *do_pci_register_device(PCIDevice *pci_dev, PCIBus *bus, > pci_dev->devfn = devfn; > pstrcpy(pci_dev->name, sizeof(pci_dev->name), name); > memset(pci_dev->irq_state, 0, sizeof(pci_dev->irq_state)); > + pci_config_alloc(pci_dev); > pci_set_default_subsystem_id(pci_dev); > pci_init_cmask(pci_dev); > pci_init_wmask(pci_dev); > @@ -494,6 +544,7 @@ static int pci_unregister_device(DeviceState *dev) > > qemu_free_irqs(pci_dev->irq); > pci_dev->bus->devices[pci_dev->devfn] = NULL; > + pci_config_free(pci_dev); > return 0; > } > > @@ -632,7 +683,7 @@ uint32_t pci_default_read_config(PCIDevice *d, > { > uint32_t val = 0; > assert(len == 1 || len == 2 || len == 4); > - len = MIN(len, PCI_CONFIG_SPACE_SIZE - address); > + len = MIN(len, pci_config_size(d) - address); > memcpy(&val, d->config + address, len); > return le32_to_cpu(val); > } > @@ -641,10 +692,11 @@ void pci_default_write_config(PCIDevice *d, uint32_t addr, uint32_t val, int l) > { > uint8_t orig[PCI_CONFIG_SPACE_SIZE]; > int i; > + uint32_t config_size = pci_config_size(d); > > /* not efficient, but simple */ > memcpy(orig, d->config, PCI_CONFIG_SPACE_SIZE); > - for(i = 0; i < l && addr < PCI_CONFIG_SPACE_SIZE; val >>= 8, ++i, ++addr) { > + for(i = 0; i < l && addr < config_size; val >>= 8, ++i, ++addr) { > uint8_t wmask = d->wmask[addr]; > d->config[addr] = (d->config[addr] & ~wmask) | (val & wmask); > } > @@ -1015,6 +1067,11 @@ static int pci_qdev_init(DeviceState *qdev, DeviceInfo *base) > PCIBus *bus; > int devfn, rc; > > + /* initialize cap_present for pci_is_express() and pci_config_size() */ > + if (info->is_express) { > + pci_dev->cap_present |= QEMU_PCI_CAP_EXPRESS; > + } > + > bus = FROM_QBUS(PCIBus, qdev_get_parent_bus(qdev)); > devfn = pci_dev->devfn; > pci_dev = do_pci_register_device(pci_dev, bus, base->name, devfn, > @@ -1071,9 +1128,10 @@ PCIDevice *pci_create_simple(PCIBus *bus, int devfn, const char *name) > > static int pci_find_space(PCIDevice *pdev, uint8_t size) > { > + int config_size = pci_config_size(pdev); > int offset = PCI_CONFIG_HEADER_SIZE; > int i; > - for (i = PCI_CONFIG_HEADER_SIZE; i < PCI_CONFIG_SPACE_SIZE; ++i) > + for (i = PCI_CONFIG_HEADER_SIZE; i < config_size; ++i) > if (pdev->used[i]) > offset = i + 1; > else if (i - offset + 1 == size) > diff --git a/hw/pci.h b/hw/pci.h > index 5b8f5cf..bfa29c8 100644 > --- a/hw/pci.h > +++ b/hw/pci.h > @@ -166,28 +166,31 @@ typedef struct PCIIORegion { > #define PCI_CONFIG_HEADER_SIZE 0x40 > /* Size of the standard PCI config space */ > #define PCI_CONFIG_SPACE_SIZE 0x100 > +/* Size of the standart PCIe config space: 4KB */ > +#define PCIE_CONFIG_SPACE_SIZE 0x1000 > > #define PCI_NUM_PINS 4 /* A-D */ > > /* Bits in cap_present field. */ > enum { > QEMU_PCI_CAP_MSIX = 0x1, > + QEMU_PCI_CAP_EXPRESS = 0x2, > }; > > struct PCIDevice { > DeviceState qdev; > /* PCI config space */ > - uint8_t config[PCI_CONFIG_SPACE_SIZE]; > + uint8_t *config; > > /* Used to enable config checks on load. Note that writeable bits are > * never checked even if set in cmask. */ > - uint8_t cmask[PCI_CONFIG_SPACE_SIZE]; > + uint8_t *cmask; > > /* Used to implement R/W bytes */ > - uint8_t wmask[PCI_CONFIG_SPACE_SIZE]; > + uint8_t *wmask; > > /* Used to allocate config space for capabilities. */ > - uint8_t used[PCI_CONFIG_SPACE_SIZE]; > + uint8_t *used; > > /* the following fields are read only */ > PCIBus *bus; > @@ -361,6 +364,12 @@ typedef struct { > PCIUnregisterFunc *exit; > PCIConfigReadFunc *config_read; > PCIConfigWriteFunc *config_write; > + > + /* pcie stuff */ > + int is_express; /* is this device pci express? > + * initialization code needs to know this before > + * each specific device initialization. > + */ > } PCIDeviceInfo; > > void pci_qdev_register(PCIDeviceInfo *info); > @@ -369,6 +378,16 @@ void pci_qdev_register_many(PCIDeviceInfo *info); > PCIDevice *pci_create(PCIBus *bus, int devfn, const char *name); > PCIDevice *pci_create_simple(PCIBus *bus, int devfn, const char *name); > > +static inline int pci_is_express(PCIDevice *d) > +{ > + return d->cap_present & QEMU_PCI_CAP_EXPRESS; > +} > + > +static inline uint32_t pci_config_size(PCIDevice *d) > +{ > + return pci_is_express(d) ? PCIE_CONFIG_SPACE_SIZE : PCI_CONFIG_SPACE_SIZE; > +} > + > /* lsi53c895a.c */ > #define LSI_MAX_DEVS 7 > > diff --git a/hw/pcie_host.c b/hw/pcie_host.c > new file mode 100644 > index 0000000..134d348 > --- /dev/null > +++ b/hw/pcie_host.c > @@ -0,0 +1,166 @@ > +/* > + * pcie_host.c > + * utility functions for pci express host bridge. > + * > + * Copyright (c) 2009 Isaku Yamahata > + * VA Linux Systems Japan K.K. > + * > + * This program is free software; you can redistribute it and/or modify > + * it under the terms of the GNU General Public License as published by > + * the Free Software Foundation; either version 2 of the License, or > + * (at your option) any later version. > + > + * This program is distributed in the hope that it will be useful, > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > + * GNU General Public License for more details. > + > + * You should have received a copy of the GNU General Public License along > + * with this program; if not, write to the Free Software Foundation, Inc., > + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. > + */ > + > +#include "hw.h" > +#include "pci.h" > +#include "pcie_host.h" > + > +/* > + * PCI express mmcfig address > + * bit 20 - 28: bus number > + * bit 15 - 19: device number > + * bit 12 - 14: function number > + * bit 0 - 11: offset in configuration space of a given device > + */ > +#define PCIE_MMCFG_SIZE_MAX (1ULL << 28) > +#define PCIE_MMCFG_SIZE_MIN (1ULL << 20) > +#define PCIE_MMCFG_BUS_BIT 20 > +#define PCIE_MMCFG_BUS_MASK 0x1ff > +#define PCIE_MMCFG_DEVFN_BIT 12 > +#define PCIE_MMCFG_DEVFN_MASK 0xff > +#define PCIE_MMCFG_CONFOFFSET_MASK 0xfff > +#define PCIE_MMCFG_BUS(addr) (((addr) >> PCIE_MMCFG_BUS_BIT) & \ > + PCIE_MMCFG_BUS_MASK) > +#define PCIE_MMCFG_DEVFN(addr) (((addr) >> PCIE_MMCFG_DEVFN_BIT) & \ > + PCIE_MMCFG_DEVFN_MASK) > +#define PCIE_MMCFG_CONFOFFSET(addr) ((addr) & PCIE_MMCFG_CONFOFFSET_MASK) > + > + > +/* a helper function to get a PCIDevice for a given mmconfig address */ > +static inline PCIDevice *pcie_mmcfg_addr_to_dev(PCIBus *s, uint32_t mmcfg_addr) > +{ > + return pci_find_device(s, PCIE_MMCFG_BUS(mmcfg_addr), > + PCI_SLOT(PCIE_MMCFG_DEVFN(mmcfg_addr)), > + PCI_FUNC(PCIE_MMCFG_DEVFN(mmcfg_addr))); > +} > + > +static void pcie_mmcfg_data_write(PCIBus *s, > + uint32_t mmcfg_addr, uint32_t val, int len) > +{ > + pci_dev_write_config(pcie_mmcfg_addr_to_dev(s, mmcfg_addr), > + PCIE_MMCFG_CONFOFFSET(mmcfg_addr), > + val, len); > +} > + > +static uint32_t pcie_mmcfg_data_read(PCIBus *s, > + uint32_t mmcfg_addr, int len) > +{ > + return pci_dev_read_config(pcie_mmcfg_addr_to_dev(s, mmcfg_addr), > + PCIE_MMCFG_CONFOFFSET(mmcfg_addr), > + len); > +} > + > +static void pcie_mmcfg_data_writeb(void *opaque, > + target_phys_addr_t addr, uint32_t value) > +{ > + PCIExpressHost *e = opaque; > + pcie_mmcfg_data_write(e->pci.bus, addr - e->base_addr, value, 1); > +} > + > +static void pcie_mmcfg_data_writew(void *opaque, > + target_phys_addr_t addr, uint32_t value) > +{ > + PCIExpressHost *e = opaque; > + pcie_mmcfg_data_write(e->pci.bus, addr - e->base_addr, value, 2); > +} > + > +static void pcie_mmcfg_data_writel(void *opaque, > + target_phys_addr_t addr, uint32_t value) > +{ > + PCIExpressHost *e = opaque; > + pcie_mmcfg_data_write(e->pci.bus, addr - e->base_addr, value, 4); > +} > + > +static uint32_t pcie_mmcfg_data_readb(void *opaque, target_phys_addr_t addr) > +{ > + PCIExpressHost *e = opaque; > + return pcie_mmcfg_data_read(e->pci.bus, addr - e->base_addr, 1); > +} > + > +static uint32_t pcie_mmcfg_data_readw(void *opaque, target_phys_addr_t addr) > +{ > + PCIExpressHost *e = opaque; > + return pcie_mmcfg_data_read(e->pci.bus, addr - e->base_addr, 2); > +} > + > +static uint32_t pcie_mmcfg_data_readl(void *opaque, target_phys_addr_t addr) > +{ > + PCIExpressHost *e = opaque; > + return pcie_mmcfg_data_read(e->pci.bus, addr - e->base_addr, 4); > +} > + > + > +static CPUWriteMemoryFunc * const pcie_mmcfg_write[] = > +{ > + pcie_mmcfg_data_writeb, > + pcie_mmcfg_data_writew, > + pcie_mmcfg_data_writel, > +}; > + > +static CPUReadMemoryFunc * const pcie_mmcfg_read[] = > +{ > + pcie_mmcfg_data_readb, > + pcie_mmcfg_data_readw, > + pcie_mmcfg_data_readl, > +}; > + > +int pcie_host_init(PCIExpressHost *e) > +{ > + e->base_addr = PCIE_BASE_ADDR_UNMAPPED; > + e->mmio_index = > + cpu_register_io_memory(pcie_mmcfg_read, pcie_mmcfg_write, e); > + if (e->mmio_index < 0) { > + return -1; > + } > + > + return 0; > +} > + > +void pcie_host_mmcfg_unmap(PCIExpressHost *e) > +{ > + if (e->base_addr != PCIE_BASE_ADDR_UNMAPPED) { > + cpu_register_physical_memory(e->base_addr, e->size, IO_MEM_UNASSIGNED); > + e->base_addr = PCIE_BASE_ADDR_UNMAPPED; > + } > +} > + > +void pcie_host_mmcfg_map(PCIExpressHost *e, > + target_phys_addr_t addr, uint32_t size) > +{ > + assert(!(size & (size - 1))); /* power of 2 */ > + assert(size >= PCIE_MMCFG_SIZE_MIN); > + assert(size <= PCIE_MMCFG_SIZE_MAX); > + > + e->base_addr = addr; > + e->size = size; > + cpu_register_physical_memory(e->base_addr, e->size, e->mmio_index); > +} > + > +void pcie_host_mmcfg_update(PCIExpressHost *e, > + int enable, > + target_phys_addr_t addr, uint32_t size) > +{ > + pcie_host_mmcfg_unmap(e); > + if (enable) { > + pcie_host_mmcfg_map(e, addr, size); > + } > +} > diff --git a/hw/pcie_host.h b/hw/pcie_host.h > new file mode 100644 > index 0000000..f4116b3 > --- /dev/null > +++ b/hw/pcie_host.h > @@ -0,0 +1,53 @@ > +/* > + * pcie_host.h > + * > + * Copyright (c) 2009 Isaku Yamahata > + * VA Linux Systems Japan K.K. > + * > + * This program is free software; you can redistribute it and/or modify > + * it under the terms of the GNU General Public License as published by > + * the Free Software Foundation; either version 2 of the License, or > + * (at your option) any later version. > + > + * This program is distributed in the hope that it will be useful, > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > + * GNU General Public License for more details. > + > + * You should have received a copy of the GNU General Public License along > + * with this program; if not, write to the Free Software Foundation, Inc., > + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. > + */ > + > +#ifndef PCIE_HOST_H > +#define PCIE_HOST_H > + > +#include "pci_host.h" > + > +typedef struct { > + PCIHostState pci; > + > + /* express part */ > + > + /* base address of MMCONFIG area is mapped. > + * PCIE_BASE_ADDR_UNMAPPED when it isn't mapped. > + */ > + target_phys_addr_t base_addr; > +#define PCIE_BASE_ADDR_UNMAPPED ((target_phys_addr_t)-1ULL) can we use the PCI macro? Or is that only in .c (if the later, it's not worth it to put it in .h). > + > + /* the size of MMCONFIG area. It's host bridge dependent */ > + target_phys_addr_t size; > + > + /* result of cpu_resiger_io_memory() to map MMCONFIG area */ typo > + int mmio_index; > +} PCIExpressHost; > + > +int pcie_host_init(PCIExpressHost *e); > +void pcie_host_mmcfg_unmap(PCIExpressHost *e); > +void pcie_host_mmcfg_map(PCIExpressHost *e, > + target_phys_addr_t addr, uint32_t size); > +void pcie_host_mmcfg_update(PCIExpressHost *e, > + int enable, > + target_phys_addr_t addr, uint32_t size); > + > +#endif /* PCIE_HOST_H */ > -- > 1.6.0.2