diff mbox

[V5,23/29] pci: pcie host and mmcfg support.

Message ID 1255069742-15724-24-git-send-email-yamahata@valinux.co.jp
State Under Review
Headers show

Commit Message

Isaku Yamahata Oct. 9, 2009, 6:28 a.m. UTC
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 <yamahata@valinux.co.jp>
---
 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

Comments

Michael S. Tsirkin Oct. 11, 2009, 10:26 a.m. UTC | #1
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 <yamahata@valinux.co.jp>
> ---
>  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 <yamahata at valinux co jp>
> + *                    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 <yamahata at valinux co jp>
> + *                    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
diff mbox

Patch

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 <yamahata at valinux co jp>
+ *                    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 <yamahata at valinux co jp>
+ *                    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)
+
+    /* 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 */
+    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 */