[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Qemu-devel] [PATCH 31/61] pci: pcie host and mmcfg support.
From: |
Isaku Yamahata |
Subject: |
[Qemu-devel] [PATCH 31/61] pci: pcie host and mmcfg support. |
Date: |
Wed, 30 Sep 2009 19:18:07 +0900 |
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 <address@hidden>
---
hw/hw.h | 12 +++
hw/pci.c | 266 +++++++++++++++++++++++++++++++++++++++++++++++++++------
hw/pci.h | 36 +++++++-
hw/pci_host.h | 22 +++++
4 files changed, 304 insertions(+), 32 deletions(-)
diff --git a/hw/hw.h b/hw/hw.h
index cf266b3..478b0b2 100644
--- a/hw/hw.h
+++ b/hw/hw.h
@@ -428,6 +428,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)) \
+}
+
/* _f : field name
_f_n : num of elements field_name
_n : num of elements
diff --git a/hw/pci.c b/hw/pci.c
index d1745ab..29bca12 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"
@@ -141,9 +142,12 @@ 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[size];
+ uint8_t *config;
int i;
+ assert(size == pcie_config_size(s));
+ config = qemu_malloc(size * sizeof(config[0]));
+
qemu_get_buffer(f, config, size);
for (i = 0; i < size; ++i)
if ((config[i] ^ s->config[i]) & s->cmask[i] & ~s->wmask[i])
@@ -152,6 +156,7 @@ static int get_pci_config_device(QEMUFile *f, void *pv,
size_t size)
pci_update_mappings(s);
+ qemu_free(config);
return 0;
}
@@ -159,6 +164,7 @@ static int get_pci_config_device(QEMUFile *f, void *pv,
size_t size)
static void put_pci_config_device(QEMUFile *f, const void *pv, size_t size)
{
const uint8_t *v = pv;
+ assert(size == pcie_config_size(container_of(pv, PCIDevice, config)));
qemu_put_buffer(f, v, size);
}
@@ -168,6 +174,17 @@ static VMStateInfo vmstate_info_pci_config = {
.put = put_pci_config_device,
};
+#define VMSTATE_PCI_CONFIG(_field, _state, _version, _info, _type, \
+ _size) { \
+ .name = (stringify(_field)), \
+ .version_id = (_version), \
+ .size = (_size), \
+ .info = &(_info), \
+ .flags = VMS_SINGLE | VMS_POINTER, \
+ .offset = offsetof(_state, _field) \
+ + type_check(_type,typeof_field(_state, _field)) \
+}
+
const VMStateDescription vmstate_pci_device = {
.name = "PCIDevice",
.version_id = 2,
@@ -175,21 +192,46 @@ 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_PCI_CONFIG(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_PCI_CONFIG(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 const VMStateDescription *pci_get_vmstate(PCIDevice *s)
+{
+ if (pci_is_pcie(s)) {
+ return &vmstate_pcie_device;
+ }
+
+ return &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)
@@ -298,14 +340,31 @@ static void pci_init_cmask(PCIDevice *dev)
static void pci_init_wmask(PCIDevice *dev)
{
int i;
+ uint32_t config_size = pcie_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_init(PCIDevice *pci_dev)
+{
+ int config_size = pcie_config_size(pci_dev);
+#define PCI_CONFIG_ALLOC(d, member, size) \
+ do { \
+ (d)->member = \
+ (typeof((d)->member))qemu_mallocz(sizeof((d)->member[0]) * \
+ size); \
+ } while (0)
+ PCI_CONFIG_ALLOC(pci_dev, config, config_size);
+ PCI_CONFIG_ALLOC(pci_dev, cmask, config_size);
+ PCI_CONFIG_ALLOC(pci_dev, wmask, config_size);
+ PCI_CONFIG_ALLOC(pci_dev, used, config_size);
+}
+
/* -1 for devfn means auto assign */
static PCIDevice *do_pci_register_device(PCIDevice *pci_dev, PCIBus *bus,
const char *name, int devfn,
@@ -326,6 +385,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_init(pci_dev);
pci_set_default_subsystem_id(pci_dev);
pci_init_cmask(pci_dev);
pci_init_wmask(pci_dev);
@@ -534,40 +594,48 @@ static void pci_update_mappings(PCIDevice *d)
}
}
+static uint8_t pcie_config_get_byte(PCIDevice *d, uint32_t addr)
+{
+ uint8_t *conf = &d->config[addr];
+ if (conf != NULL)
+ return *conf;
+ return 0;
+}
+
+static uint32_t pcie_config_get(PCIDevice *d, uint32_t addr, int len)
+{
+ int i;
+ union {
+ uint8_t val8[4];
+ uint32_t val32;
+ } v = { .val32 = 0 };
+
+ for (i = 0; i < len; i++) {
+ v.val8[i] = pcie_config_get_byte(d, addr + i);
+ }
+
+ return le32_to_cpu(v.val32);
+}
+
uint32_t pci_default_read_config(PCIDevice *d,
uint32_t address, int len)
{
- uint32_t val;
+ uint32_t config_size = pcie_config_size(d);
- switch(len) {
- default:
- case 4:
- if (address <= 0xfc) {
- val = le32_to_cpu(*(uint32_t *)(d->config + address));
- break;
- }
- /* fall through */
- case 2:
- if (address <= 0xfe) {
- val = le16_to_cpu(*(uint16_t *)(d->config + address));
- break;
- }
- /* fall through */
- case 1:
- val = d->config[address];
- break;
- }
- return val;
+ assert(len == 1 || len == 2 || len == 4);
+ len = MIN(len, config_size - MIN(config_size, address));
+ return pcie_config_get(d, address, len);
}
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 = pcie_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);
}
@@ -663,6 +731,147 @@ uint32_t pci_data_read(void *opaque, uint32_t addr, int
len)
pci_addr_to_dev(s, addr, &pci_dev, &config_addr);
return pci_dev_data_read(pci_dev, config_addr, len);
}
+
+static void pcie_mmcfg_addr_to_dev(PCIBus *s, uint32_t mmcfg_addr,
+ PCIDevice **pci_dev, uint32_t *conf_addr)
+{
+ int bus_num;
+ unsigned int dev;
+ uint8_t func;
+
+#define PCIE_MASK(val, hi_bit, low_bit) \
+ (((val) & (((1ULL << (hi_bit)) - 1))) >> (low_bit))
+#define PCIE_VAL(VAL, val) \
+ PCIE_MASK((val), PCIE_MMCFG_ ## VAL ## _HI, PCIE_MMCFG_ ## VAL ## _LOW)
+#define PCIE_MMCFG_BUS_HI 28
+#define PCIE_MMCFG_BUS_LOW 20
+#define PCIE_MMCFG_DEV_HI 19
+#define PCIE_MMCFG_DEV_LOW 15
+#define PCIE_MMCFG_FUNC_HI 14
+#define PCIE_MMCFG_FUNC_LOW 12
+#define PCIE_MMCFG_CONFADDR_HI 11
+#define PCIE_MMCFG_CONFADDR_LOW 0
+#define PCIE_MMCFG_BUS(addr) PCIE_VAL(BUS, (addr))
+#define PCIE_MMCFG_DEV(addr) PCIE_VAL(DEV, (addr))
+#define PCIE_MMCFG_FUNC(addr) PCIE_VAL(FUNC, (addr))
+#define PCIE_MMCFG_CONFADDR(addr) PCIE_VAL(CONFADDR, (addr))
+
+ bus_num = PCIE_MMCFG_BUS(mmcfg_addr);
+ dev = PCIE_MMCFG_DEV(mmcfg_addr);
+ func = PCIE_MMCFG_FUNC(mmcfg_addr);
+ *conf_addr = PCIE_MMCFG_CONFADDR(mmcfg_addr);
+
+ *pci_dev = pci_bdf_to_dev(s, bus_num, PCI_DEVFN(dev, func));
+}
+
+void pcie_data_write(void *opaque, uint32_t addr, uint32_t val, int len)
+{
+ PCIBus *s = opaque;
+ PCIDevice *pci_dev;
+ uint32_t config_addr;
+
+#if 0
+ PCI_DPRINTF("%s: addr=%08"PRIx32" val=%08"PRIx32" len=%d\n",
+ __func__, addr, val, len);
+#endif
+ pcie_mmcfg_addr_to_dev(s, addr, &pci_dev, &config_addr);
+ pci_dev_data_write(pci_dev, config_addr, val, len);
+}
+
+uint32_t pcie_data_read(void *opaque, uint32_t addr, int len)
+{
+ PCIBus *s = opaque;
+ PCIDevice *pci_dev;
+ uint32_t config_addr;
+
+ pcie_mmcfg_addr_to_dev(s, addr, &pci_dev, &config_addr);
+ return pci_dev_data_read(pci_dev, config_addr, len);
+}
+
+#define DEFINE_PCIE_HOST_DATA_READ(len) \
+ static uint32_t pcie_host_data_read_ ## len ( \
+ void *opaque, target_phys_addr_t addr) \
+ { \
+ PCIExpressHost *e = (PCIExpressHost *)opaque; \
+ return pcie_data_read(e->pci.bus, \
+ addr - e->base_addr, (len)); \
+ }
+
+#define DEFINE_PCIE_HOST_DATA_WRITE(len) \
+ static void pcie_host_data_write_ ## len ( \
+ void *opaque, target_phys_addr_t addr, uint32_t value) \
+ { \
+ PCIExpressHost *e = (PCIExpressHost *)opaque; \
+ pcie_data_write(e->pci.bus, \
+ addr - e->base_addr, value, (len)); \
+ }
+
+#define DEFINE_PCIE_HOST_DATA_MMIO(len) \
+ DEFINE_PCIE_HOST_DATA_READ(len) \
+ DEFINE_PCIE_HOST_DATA_WRITE(len)
+
+DEFINE_PCIE_HOST_DATA_MMIO(1)
+DEFINE_PCIE_HOST_DATA_MMIO(2)
+DEFINE_PCIE_HOST_DATA_MMIO(4)
+
+#define DEFINE_PCIE_MEMORY_FUNCS(Type, type) \
+ static CPU ## Type ## MemoryFunc *pcie_host_data_ ## type [] = \
+ { \
+ &pcie_host_data_ ## type ## _1, \
+ &pcie_host_data_ ## type ## _2, \
+ &pcie_host_data_ ## type ## _4, \
+ };
+
+DEFINE_PCIE_MEMORY_FUNCS(Read, read)
+DEFINE_PCIE_MEMORY_FUNCS(Write, write)
+
+int pcie_host_init(PCIExpressHost *e,
+ CPUReadMemoryFunc **mmcfg_read,
+ CPUWriteMemoryFunc **mmcfg_write)
+{
+ e->base_addr = PCIE_BASE_ADDR_INVALID;
+
+ if (mmcfg_read == NULL)
+ mmcfg_read = pcie_host_data_read;
+ if (mmcfg_write == NULL)
+ mmcfg_write = pcie_host_data_write;
+ e->mmio_index = cpu_register_io_memory(mmcfg_read, 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_INVALID) {
+ cpu_register_physical_memory(e->base_addr, e->size, IO_MEM_UNASSIGNED);
+ }
+}
+
+void pcie_host_mmcfg_map(PCIExpressHost *e,
+ target_phys_addr_t addr, uint32_t size)
+{
+ assert((size & (size - 1)) == 0); /* power of 2 */
+ assert(size >= (1ULL << PCIE_MMCFG_BUS_LOW));
+ assert(size <= (1ULL << PCIE_MMCFG_BUS_HI));
+
+ 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);
+ }
+}
+
/***********************************************************/
/* generic PCI irq support */
@@ -1001,9 +1210,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 = pcie_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 b814342..460a2b5 100644
--- a/hw/pci.h
+++ b/hw/pci.h
@@ -173,20 +173,26 @@ enum {
QEMU_PCI_CAP_MSIX = 0x1,
};
+/* Size of the standart PCIe config space: 4KB */
+#define PCIE_CONFIG_SPACE_SIZE 0x1000
+#define PCIE_EXT_CONFIG_SPACE_SIZE \
+ (PCIE_CONFIG_SPACE_SIZE - PCI_CONFIG_SPACE_SIZE)
+
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;
@@ -268,6 +274,8 @@ PCIDevice *pci_nic_init(NICInfo *nd, const char
*default_model,
const char *default_devaddr);
void pci_data_write(void *opaque, uint32_t addr, uint32_t val, int len);
uint32_t pci_data_read(void *opaque, uint32_t addr, int len);
+void pcie_data_write(void *opaque, uint32_t addr, uint32_t val, int len);
+uint32_t pcie_data_read(void *opaque, uint32_t addr, int len);
int pci_bus_num(PCIBus *s);
void pci_for_each_device(int bus_num, void (*fn)(PCIDevice *d));
PCIBus *pci_find_bus(int bus_num);
@@ -340,6 +348,9 @@ typedef struct {
pci_qdev_initfn init;
PCIConfigReadFunc *config_read;
PCIConfigWriteFunc *config_write;
+
+ /* pcie stuff */
+ int pcie;
} PCIDeviceInfo;
void pci_qdev_register(PCIDeviceInfo *info);
@@ -349,6 +360,23 @@ PCIDevice *pci_create(const char *name, const char
*devaddr);
PCIDevice *pci_create_noinit(PCIBus *bus, int devfn, const char *name);
PCIDevice *pci_create_simple(PCIBus *bus, int devfn, const char *name);
+static inline int pci_is_pcie(PCIDevice *d)
+{
+ /*
+ * At the moment, all the pci devices aren't qdevfied. So
+ * d->qdev.info might be NULL.
+ * Given that pcie device emulator hasn't exist, we conclude that
+ * such a device isn't pcie.
+ */
+ return d->qdev.info != NULL &&
+ container_of(d->qdev.info, PCIDeviceInfo, qdev)->pcie;
+}
+
+static inline uint32_t pcie_config_size(PCIDevice *d)
+{
+ return pci_is_pcie(d)? PCIE_CONFIG_SPACE_SIZE: PCI_CONFIG_SPACE_SIZE;
+}
+
/* lsi53c895a.c */
#define LSI_MAX_DEVS 7
diff --git a/hw/pci_host.h b/hw/pci_host.h
index 9f272a7..6d5ae22 100644
--- a/hw/pci_host.h
+++ b/hw/pci_host.h
@@ -36,4 +36,26 @@ typedef struct {
PCIBus *bus;
} PCIHostState;
+typedef struct {
+ PCIHostState pci;
+
+ /* express part */
+ target_phys_addr_t base_addr;
+#define PCIE_BASE_ADDR_INVALID ((target_phys_addr_t)-1ULL)
+ target_phys_addr_t size;
+ int bus_num_order;
+ int mmio_index;
+} PCIExpressHost;
+
+int pcie_host_init(PCIExpressHost *e,
+ CPUReadMemoryFunc **mmcfg_read,
+ CPUWriteMemoryFunc **mmcfg_write);
+
+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 /* PCI_HOST_H */
--
1.6.0.2
- [Qemu-devel] [PATCH 00/61] Q35 chip set and stuff., Isaku Yamahata, 2009/09/30
- [Qemu-devel] [PATCH 07/61] pc, i440fx: Make smm enable/disable function i440fx independent., Isaku Yamahata, 2009/09/30
- [Qemu-devel] [PATCH 05/61] acpi_piix4: remove unused variable in get_pmsts()., Isaku Yamahata, 2009/09/30
- [Qemu-devel] [PATCH 32/61] pci: helper functions to access PCIDevice::config, Isaku Yamahata, 2009/09/30
- [Qemu-devel] [PATCH 50/61] pci hotadd, acpi_piix4: remove global variables., Isaku Yamahata, 2009/09/30
- [Qemu-devel] [PATCH 27/61] pci: clean up of pci_update_mappings(), Isaku Yamahata, 2009/09/30
- [Qemu-devel] [PATCH 06/61] pc: fix file stream leak in multiboot loader., Isaku Yamahata, 2009/09/30
- [Qemu-devel] [PATCH 20/61] pci: fix PCI_DPRINTF() wrt variadic macro., Isaku Yamahata, 2009/09/30
- [Qemu-devel] [PATCH 19/61] pc_piix: initialize ioapic before use., Isaku Yamahata, 2009/09/30
- [Qemu-devel] [PATCH 31/61] pci: pcie host and mmcfg support.,
Isaku Yamahata <=
- [Qemu-devel] [PATCH 02/61] acpi: split out apm register emulation from acpi.c, Isaku Yamahata, 2009/09/30
- [Qemu-devel] [PATCH 26/61] pci: 64bit bar support., Isaku Yamahata, 2009/09/30
- [Qemu-devel] [PATCH 04/61] acpi: split acpi.c into the common part and the piix4 part., Isaku Yamahata, 2009/09/30
- [Qemu-devel] [PATCH 28/61] pci: factor out while(bus) bus->next loop logic into pci_find_bus_from()., Isaku Yamahata, 2009/09/30
- [Qemu-devel] [PATCH 21/61] pci: introduce constant PCI_NUM_PINS for the number of interrupt pins, 4., Isaku Yamahata, 2009/09/30
- [Qemu-devel] [PATCH 17/61] pc: split out pci device init from pc_init1() into pc_pci_device_init(), Isaku Yamahata, 2009/09/30
- [Qemu-devel] [PATCH 15/61] pc: split out vga initialization from pc_init1() into pc_vga_init()., Isaku Yamahata, 2009/09/30
- [Qemu-devel] [PATCH 09/61] pc: remove a global variable, floppy_controller., Isaku Yamahata, 2009/09/30
- [Qemu-devel] [PATCH 59/61] ioapic: make irr accept more than 32 pins., Isaku Yamahata, 2009/09/30
- [Qemu-devel] [PATCH 16/61] pc: split out basic device init from pc_init1() into pc_basic_device_init(), Isaku Yamahata, 2009/09/30