Skip to content

Commit

Permalink
pci: pcie host and mmcfg support.
Browse files Browse the repository at this point in the history
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 <[email protected]>
Signed-off-by: Anthony Liguori <[email protected]>
  • Loading branch information
Isaku Yamahata authored and Anthony Liguori committed Nov 9, 2009
1 parent 9cae69b commit a9f4994
Show file tree
Hide file tree
Showing 6 changed files with 349 additions and 19 deletions.
2 changes: 1 addition & 1 deletion Makefile.target
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ endif #CONFIG_BSD_USER
# System emulator target
ifdef CONFIG_SOFTMMU

obj-y = vl.o async.o monitor.o pci.o pci_host.o machine.o gdbstub.o
obj-y = vl.o async.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
Expand Down
11 changes: 11 additions & 0 deletions hw/hw.h
Original file line number Diff line number Diff line change
Expand Up @@ -512,6 +512,17 @@ extern const VMStateDescription vmstate_pci_device;
.offset = vmstate_offset_value(_state, _field, PCIDevice), \
}

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 = vmstate_offset_value(_state, _field, PCIDevice), \
}

extern const VMStateDescription vmstate_i2c_slave;

#define VMSTATE_I2C_SLAVE(_field, _state) { \
Expand Down
86 changes: 72 additions & 14 deletions hw/pci.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
*/
#include "hw.h"
#include "pci.h"
#include "pci_host.h"
#include "monitor.h"
#include "net.h"
#include "sysemu.h"
Expand Down Expand Up @@ -248,25 +249,32 @@ static uint8_t pci_sub_bus(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;
}

/* just put buffer */
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);
}

Expand All @@ -283,21 +291,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_BUFFER_UNSAFE_INFO(config, PCIDevice, 0,
vmstate_info_pci_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_BUFFER_UNSAFE_INFO(config, PCIDevice, 0,
vmstate_info_pci_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)
Expand Down Expand Up @@ -406,14 +435,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;
pci_set_word(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,
Expand All @@ -434,6 +483,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);
Expand Down Expand Up @@ -501,6 +551,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;
}

Expand Down Expand Up @@ -641,7 +692,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);
}
Expand All @@ -650,10 +701,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);
}
Expand Down Expand Up @@ -1001,6 +1053,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,
Expand Down Expand Up @@ -1057,9 +1114,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)
Expand Down
27 changes: 23 additions & 4 deletions hw/pci.h
Original file line number Diff line number Diff line change
Expand Up @@ -163,28 +163,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;
Expand Down Expand Up @@ -354,6 +357,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);
Expand All @@ -362,6 +371,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

Expand Down
Loading

0 comments on commit a9f4994

Please sign in to comment.