sim-card
/
qemu
Archived
10
0
Fork 0

Merge branch 'pci' into for_anthony

This commit is contained in:
Michael S. Tsirkin 2010-10-27 19:07:10 +02:00
commit b907b69dd7
26 changed files with 2606 additions and 276 deletions

View File

@ -152,7 +152,8 @@ user-obj-y += cutils.o cache-utils.o
hw-obj-y =
hw-obj-y += vl.o loader.o
hw-obj-y += virtio.o virtio-console.o
hw-obj-y += fw_cfg.o pci.o pci_host.o pcie_host.o
hw-obj-y += fw_cfg.o pci.o pci_host.o pcie_host.o pci_bridge.o
hw-obj-y += ioh3420.o xio3130_upstream.o xio3130_downstream.o
hw-obj-y += watchdog.o
hw-obj-$(CONFIG_ISA_MMIO) += isa_mmio.o
hw-obj-$(CONFIG_ECC) += ecc.o
@ -199,7 +200,8 @@ hw-obj-$(CONFIG_PIIX4) += piix4.o
# PCI watchdog devices
hw-obj-y += wdt_i6300esb.o
hw-obj-y += msix.o
hw-obj-y += pcie.o pcie_port.o
hw-obj-y += msix.o msi.o
# PCI network cards
hw-obj-y += ne2000.o

View File

@ -621,6 +621,9 @@ static int piix4_device_hotplug(DeviceState *qdev, PCIDevice *dev, int state)
PIIX4PMState *s = DO_UPCAST(PIIX4PMState, dev,
DO_UPCAST(PCIDevice, qdev, qdev));
if (!dev->qdev.hotplugged)
return 0;
s->pci0_status.up = 0;
s->pci0_status.down = 0;
if (state) {

View File

@ -29,6 +29,8 @@
#include "sysbus.h"
#include "pci.h"
#include "pci_host.h"
#include "pci_bridge.h"
#include "pci_internals.h"
#include "rwhandler.h"
#include "apb_pci.h"
#include "sysemu.h"
@ -293,9 +295,17 @@ static void pci_apb_set_irq(void *opaque, int irq_num, int level)
}
}
static void apb_pci_bridge_init(PCIBus *b)
static int apb_pci_bridge_initfn(PCIDevice *dev)
{
PCIDevice *dev = pci_bridge_get_device(b);
int rc;
rc = pci_bridge_initfn(dev);
if (rc < 0) {
return rc;
}
pci_config_set_vendor_id(dev->config, PCI_VENDOR_ID_SUN);
pci_config_set_device_id(dev->config, PCI_DEVICE_ID_SUN_SIMBA);
/*
* command register:
@ -312,6 +322,7 @@ static void apb_pci_bridge_init(PCIBus *b)
PCI_STATUS_FAST_BACK | PCI_STATUS_66MHZ |
PCI_STATUS_DEVSEL_MEDIUM);
pci_set_byte(dev->config + PCI_REVISION_ID, 0x11);
return 0;
}
PCIBus *pci_apb_init(target_phys_addr_t special_base,
@ -322,6 +333,8 @@ PCIBus *pci_apb_init(target_phys_addr_t special_base,
SysBusDevice *s;
APBState *d;
unsigned int i;
PCIDevice *pci_dev;
PCIBridge *br;
/* Ultrasparc PBM main bus */
dev = qdev_create(NULL, "pbm");
@ -347,17 +360,21 @@ PCIBus *pci_apb_init(target_phys_addr_t special_base,
pci_create_simple(d->bus, 0, "pbm");
/* APB secondary busses */
*bus2 = pci_bridge_init(d->bus, PCI_DEVFN(1, 0), true,
PCI_VENDOR_ID_SUN, PCI_DEVICE_ID_SUN_SIMBA,
pci_apb_map_irq,
"Advanced PCI Bus secondary bridge 1");
apb_pci_bridge_init(*bus2);
pci_dev = pci_create_multifunction(d->bus, PCI_DEVFN(1, 0), true,
"pbm-bridge");
br = DO_UPCAST(PCIBridge, dev, pci_dev);
pci_bridge_map_irq(br, "Advanced PCI Bus secondary bridge 1",
pci_apb_map_irq);
qdev_init_nofail(&pci_dev->qdev);
*bus2 = pci_bridge_get_sec_bus(br);
*bus3 = pci_bridge_init(d->bus, PCI_DEVFN(1, 1), true,
PCI_VENDOR_ID_SUN, PCI_DEVICE_ID_SUN_SIMBA,
pci_apb_map_irq,
"Advanced PCI Bus secondary bridge 2");
apb_pci_bridge_init(*bus3);
pci_dev = pci_create_multifunction(d->bus, PCI_DEVFN(1, 1), true,
"pbm-bridge");
br = DO_UPCAST(PCIBridge, dev, pci_dev);
pci_bridge_map_irq(br, "Advanced PCI Bus secondary bridge 2",
pci_apb_map_irq);
qdev_init_nofail(&pci_dev->qdev);
*bus3 = pci_bridge_get_sec_bus(br);
return d->bus;
}
@ -440,10 +457,23 @@ static SysBusDeviceInfo pbm_host_info = {
.qdev.reset = pci_pbm_reset,
.init = pci_pbm_init_device,
};
static PCIDeviceInfo pbm_pci_bridge_info = {
.qdev.name = "pbm-bridge",
.qdev.size = sizeof(PCIBridge),
.qdev.vmsd = &vmstate_pci_device,
.qdev.reset = pci_bridge_reset,
.init = apb_pci_bridge_initfn,
.exit = pci_bridge_exitfn,
.config_write = pci_bridge_write_config,
.is_bridge = 1,
};
static void pbm_register_devices(void)
{
sysbus_register_withprop(&pbm_host_info);
pci_qdev_register(&pbm_pci_host_info);
pci_qdev_register(&pbm_pci_bridge_info);
}
device_init(pbm_register_devices)

View File

@ -27,6 +27,8 @@
#include "sysbus.h"
#include "pci.h"
#include "pci_host.h"
#include "pci_bridge.h"
#include "pci_internals.h"
/* debug DEC */
//#define DEBUG_DEC
@ -48,18 +50,43 @@ static int dec_map_irq(PCIDevice *pci_dev, int irq_num)
return irq_num;
}
static int dec_21154_initfn(PCIDevice *dev)
{
int rc;
rc = pci_bridge_initfn(dev);
if (rc < 0) {
return rc;
}
pci_config_set_vendor_id(dev->config, PCI_VENDOR_ID_DEC);
pci_config_set_device_id(dev->config, PCI_DEVICE_ID_DEC_21154);
return 0;
}
static PCIDeviceInfo dec_21154_pci_bridge_info = {
.qdev.name = "dec-21154-p2p-bridge",
.qdev.desc = "DEC 21154 PCI-PCI bridge",
.qdev.size = sizeof(PCIBridge),
.qdev.vmsd = &vmstate_pci_device,
.qdev.reset = pci_bridge_reset,
.init = dec_21154_initfn,
.exit = pci_bridge_exitfn,
.config_write = pci_bridge_write_config,
.is_bridge = 1,
};
PCIBus *pci_dec_21154_init(PCIBus *parent_bus, int devfn)
{
DeviceState *dev;
PCIBus *ret;
PCIDevice *dev;
PCIBridge *br;
dev = qdev_create(NULL, "dec-21154");
qdev_init_nofail(dev);
ret = pci_bridge_init(parent_bus, devfn, false,
PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_21154,
dec_map_irq, "DEC 21154 PCI-PCI bridge");
return ret;
dev = pci_create_multifunction(parent_bus, devfn, false,
"dec-21154-p2p-bridge");
br = DO_UPCAST(PCIBridge, dev, dev);
pci_bridge_map_irq(br, "DEC 21154 PCI-PCI bridge", dec_map_irq);
qdev_init_nofail(&dev->qdev);
return pci_bridge_get_sec_bus(br);
}
static int pci_dec_21154_init_device(SysBusDevice *dev)
@ -98,6 +125,7 @@ static void dec_register_devices(void)
sysbus_register_dev("dec-21154", sizeof(DECState),
pci_dec_21154_init_device);
pci_qdev_register(&dec_21154_pci_host_info);
pci_qdev_register(&dec_21154_pci_bridge_info);
}
device_init(dec_register_devices)

View File

@ -540,8 +540,8 @@ static void e100_pci_reset(EEPRO100State * s, E100PCIDeviceInfo *e100_device)
if (e100_device->power_management) {
/* Power Management Capabilities */
int cfg_offset = 0xdc;
int r = pci_add_capability_at_offset(&s->dev, PCI_CAP_ID_PM,
cfg_offset, PCI_PM_SIZEOF);
int r = pci_add_capability(&s->dev, PCI_CAP_ID_PM,
cfg_offset, PCI_PM_SIZEOF);
assert(r >= 0);
pci_set_word(pci_conf + cfg_offset + PCI_PM_PMC, 0x7e21);
#if 0 /* TODO: replace dummy code for power management emulation. */

186
hw/ioh3420.c Normal file
View File

@ -0,0 +1,186 @@
/*
* ioh3420.c
* Intel X58 north bridge IOH
* PCI Express root port device id 3420
*
* Copyright (c) 2010 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, see <http://www.gnu.org/licenses/>.
*/
#include "pci_ids.h"
#include "msi.h"
#include "pcie.h"
#include "ioh3420.h"
#define PCI_DEVICE_ID_IOH_EPORT 0x3420 /* D0:F0 express mode */
#define PCI_DEVICE_ID_IOH_REV 0x2
#define IOH_EP_SSVID_OFFSET 0x40
#define IOH_EP_SSVID_SVID PCI_VENDOR_ID_INTEL
#define IOH_EP_SSVID_SSID 0
#define IOH_EP_MSI_OFFSET 0x60
#define IOH_EP_MSI_SUPPORTED_FLAGS PCI_MSI_FLAGS_MASKBIT
#define IOH_EP_MSI_NR_VECTOR 2
#define IOH_EP_EXP_OFFSET 0x90
#define IOH_EP_AER_OFFSET 0x100
static void ioh3420_write_config(PCIDevice *d,
uint32_t address, uint32_t val, int len)
{
pci_bridge_write_config(d, address, val, len);
msi_write_config(d, address, val, len);
pcie_cap_slot_write_config(d, address, val, len);
/* TODO: AER */
}
static void ioh3420_reset(DeviceState *qdev)
{
PCIDevice *d = DO_UPCAST(PCIDevice, qdev, qdev);
msi_reset(d);
pcie_cap_root_reset(d);
pcie_cap_deverr_reset(d);
pcie_cap_slot_reset(d);
pci_bridge_reset(qdev);
pci_bridge_disable_base_limit(d);
/* TODO: AER */
}
static int ioh3420_initfn(PCIDevice *d)
{
PCIBridge* br = DO_UPCAST(PCIBridge, dev, d);
PCIEPort *p = DO_UPCAST(PCIEPort, br, br);
PCIESlot *s = DO_UPCAST(PCIESlot, port, p);
int rc;
rc = pci_bridge_initfn(d);
if (rc < 0) {
return rc;
}
d->config[PCI_REVISION_ID] = PCI_DEVICE_ID_IOH_REV;
pcie_port_init_reg(d);
pci_config_set_vendor_id(d->config, PCI_VENDOR_ID_INTEL);
pci_config_set_device_id(d->config, PCI_DEVICE_ID_IOH_EPORT);
rc = pci_bridge_ssvid_init(d, IOH_EP_SSVID_OFFSET,
IOH_EP_SSVID_SVID, IOH_EP_SSVID_SSID);
if (rc < 0) {
return rc;
}
rc = msi_init(d, IOH_EP_MSI_OFFSET, IOH_EP_MSI_NR_VECTOR,
IOH_EP_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_64BIT,
IOH_EP_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_MASKBIT);
if (rc < 0) {
return rc;
}
rc = pcie_cap_init(d, IOH_EP_EXP_OFFSET, PCI_EXP_TYPE_ROOT_PORT, p->port);
if (rc < 0) {
return rc;
}
pcie_cap_deverr_init(d);
pcie_cap_slot_init(d, s->slot);
pcie_chassis_create(s->chassis);
rc = pcie_chassis_add_slot(s);
if (rc < 0) {
return rc;
}
pcie_cap_root_init(d);
/* TODO: AER */
return 0;
}
static int ioh3420_exitfn(PCIDevice *d)
{
/* TODO: AER */
msi_uninit(d);
pcie_cap_exit(d);
return pci_bridge_exitfn(d);
}
PCIESlot *ioh3420_init(PCIBus *bus, int devfn, bool multifunction,
const char *bus_name, pci_map_irq_fn map_irq,
uint8_t port, uint8_t chassis, uint16_t slot)
{
PCIDevice *d;
PCIBridge *br;
DeviceState *qdev;
d = pci_create_multifunction(bus, devfn, multifunction, "ioh3420");
if (!d) {
return NULL;
}
br = DO_UPCAST(PCIBridge, dev, d);
qdev = &br->dev.qdev;
pci_bridge_map_irq(br, bus_name, map_irq);
qdev_prop_set_uint8(qdev, "port", port);
qdev_prop_set_uint8(qdev, "chassis", chassis);
qdev_prop_set_uint16(qdev, "slot", slot);
qdev_init_nofail(qdev);
return DO_UPCAST(PCIESlot, port, DO_UPCAST(PCIEPort, br, br));
}
static const VMStateDescription vmstate_ioh3420 = {
.name = "ioh-3240-express-root-port",
.version_id = 1,
.minimum_version_id = 1,
.minimum_version_id_old = 1,
.post_load = pcie_cap_slot_post_load,
.fields = (VMStateField[]) {
VMSTATE_PCIE_DEVICE(port.br.dev, PCIESlot),
/* TODO: AER */
VMSTATE_END_OF_LIST()
}
};
static PCIDeviceInfo ioh3420_info = {
.qdev.name = "ioh3420",
.qdev.desc = "Intel IOH device id 3420 PCIE Root Port",
.qdev.size = sizeof(PCIESlot),
.qdev.reset = ioh3420_reset,
.qdev.vmsd = &vmstate_ioh3420,
.is_express = 1,
.is_bridge = 1,
.config_write = ioh3420_write_config,
.init = ioh3420_initfn,
.exit = ioh3420_exitfn,
.qdev.props = (Property[]) {
DEFINE_PROP_UINT8("port", PCIESlot, port.port, 0),
DEFINE_PROP_UINT8("chassis", PCIESlot, chassis, 0),
DEFINE_PROP_UINT16("slot", PCIESlot, slot, 0),
/* TODO: AER */
DEFINE_PROP_END_OF_LIST(),
}
};
static void ioh3420_register(void)
{
pci_qdev_register(&ioh3420_info);
}
device_init(ioh3420_register);
/*
* Local variables:
* c-indent-level: 4
* c-basic-offset: 4
* tab-width: 8
* indent-tab-mode: nil
* End:
*/

10
hw/ioh3420.h Normal file
View File

@ -0,0 +1,10 @@
#ifndef QEMU_IOH3420_H
#define QEMU_IOH3420_H
#include "pcie_port.h"
PCIESlot *ioh3420_init(PCIBus *bus, int devfn, bool multifunction,
const char *bus_name, pci_map_irq_fn map_irq,
uint8_t port, uint8_t chassis, uint16_t slot);
#endif /* QEMU_IOH3420_H */

347
hw/msi.c Normal file
View File

@ -0,0 +1,347 @@
/*
* msi.c
*
* Copyright (c) 2010 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, see <http://www.gnu.org/licenses/>.
*/
#include "msi.h"
#include "range.h"
/* Eventually those constants should go to Linux pci_regs.h */
#define PCI_MSI_PENDING_32 0x10
#define PCI_MSI_PENDING_64 0x14
/* PCI_MSI_ADDRESS_LO */
#define PCI_MSI_ADDRESS_LO_MASK (~0x3)
/* If we get rid of cap allocator, we won't need those. */
#define PCI_MSI_32_SIZEOF 0x0a
#define PCI_MSI_64_SIZEOF 0x0e
#define PCI_MSI_32M_SIZEOF 0x14
#define PCI_MSI_64M_SIZEOF 0x18
#define PCI_MSI_VECTORS_MAX 32
/* If we get rid of cap allocator, we won't need this. */
static inline uint8_t msi_cap_sizeof(uint16_t flags)
{
switch (flags & (PCI_MSI_FLAGS_MASKBIT | PCI_MSI_FLAGS_64BIT)) {
case PCI_MSI_FLAGS_MASKBIT | PCI_MSI_FLAGS_64BIT:
return PCI_MSI_64M_SIZEOF;
case PCI_MSI_FLAGS_64BIT:
return PCI_MSI_64_SIZEOF;
case PCI_MSI_FLAGS_MASKBIT:
return PCI_MSI_32M_SIZEOF;
case 0:
return PCI_MSI_32_SIZEOF;
default:
abort();
break;
}
return 0;
}
//#define MSI_DEBUG
#ifdef MSI_DEBUG
# define MSI_DPRINTF(fmt, ...) \
fprintf(stderr, "%s:%d " fmt, __func__, __LINE__, ## __VA_ARGS__)
#else
# define MSI_DPRINTF(fmt, ...) do { } while (0)
#endif
#define MSI_DEV_PRINTF(dev, fmt, ...) \
MSI_DPRINTF("%s:%x " fmt, (dev)->name, (dev)->devfn, ## __VA_ARGS__)
static inline unsigned int msi_nr_vectors(uint16_t flags)
{
return 1U <<
((flags & PCI_MSI_FLAGS_QSIZE) >> (ffs(PCI_MSI_FLAGS_QSIZE) - 1));
}
static inline uint8_t msi_flags_off(const PCIDevice* dev)
{
return dev->msi_cap + PCI_MSI_FLAGS;
}
static inline uint8_t msi_address_lo_off(const PCIDevice* dev)
{
return dev->msi_cap + PCI_MSI_ADDRESS_LO;
}
static inline uint8_t msi_address_hi_off(const PCIDevice* dev)
{
return dev->msi_cap + PCI_MSI_ADDRESS_HI;
}
static inline uint8_t msi_data_off(const PCIDevice* dev, bool msi64bit)
{
return dev->msi_cap + (msi64bit ? PCI_MSI_DATA_64 : PCI_MSI_DATA_32);
}
static inline uint8_t msi_mask_off(const PCIDevice* dev, bool msi64bit)
{
return dev->msi_cap + (msi64bit ? PCI_MSI_MASK_64 : PCI_MSI_MASK_32);
}
static inline uint8_t msi_pending_off(const PCIDevice* dev, bool msi64bit)
{
return dev->msi_cap + (msi64bit ? PCI_MSI_PENDING_64 : PCI_MSI_PENDING_32);
}
bool msi_enabled(const PCIDevice *dev)
{
return msi_present(dev) &&
(pci_get_word(dev->config + msi_flags_off(dev)) &
PCI_MSI_FLAGS_ENABLE);
}
int msi_init(struct PCIDevice *dev, uint8_t offset,
unsigned int nr_vectors, bool msi64bit, bool msi_per_vector_mask)
{
unsigned int vectors_order;
uint16_t flags;
uint8_t cap_size;
int config_offset;
MSI_DEV_PRINTF(dev,
"init offset: 0x%"PRIx8" vector: %"PRId8
" 64bit %d mask %d\n",
offset, nr_vectors, msi64bit, msi_per_vector_mask);
assert(!(nr_vectors & (nr_vectors - 1))); /* power of 2 */
assert(nr_vectors > 0);
assert(nr_vectors <= PCI_MSI_VECTORS_MAX);
/* the nr of MSI vectors is up to 32 */
vectors_order = ffs(nr_vectors) - 1;
flags = vectors_order << (ffs(PCI_MSI_FLAGS_QMASK) - 1);
if (msi64bit) {
flags |= PCI_MSI_FLAGS_64BIT;
}
if (msi_per_vector_mask) {
flags |= PCI_MSI_FLAGS_MASKBIT;
}
cap_size = msi_cap_sizeof(flags);
config_offset = pci_add_capability(dev, PCI_CAP_ID_MSI, offset, cap_size);
if (config_offset < 0) {
return config_offset;
}
dev->msi_cap = config_offset;
dev->cap_present |= QEMU_PCI_CAP_MSI;
pci_set_word(dev->config + msi_flags_off(dev), flags);
pci_set_word(dev->wmask + msi_flags_off(dev),
PCI_MSI_FLAGS_QSIZE | PCI_MSI_FLAGS_ENABLE);
pci_set_long(dev->wmask + msi_address_lo_off(dev),
PCI_MSI_ADDRESS_LO_MASK);
if (msi64bit) {
pci_set_long(dev->wmask + msi_address_hi_off(dev), 0xffffffff);
}
pci_set_word(dev->wmask + msi_data_off(dev, msi64bit), 0xffff);
if (msi_per_vector_mask) {
/* Make mask bits 0 to nr_vectors - 1 writeable. */
pci_set_long(dev->wmask + msi_mask_off(dev, msi64bit),
0xffffffff >> (PCI_MSI_VECTORS_MAX - nr_vectors));
}
return config_offset;
}
void msi_uninit(struct PCIDevice *dev)
{
uint16_t flags = pci_get_word(dev->config + msi_flags_off(dev));
uint8_t cap_size = msi_cap_sizeof(flags);
pci_del_capability(dev, PCI_CAP_ID_MSIX, cap_size);
MSI_DEV_PRINTF(dev, "uninit\n");
}
void msi_reset(PCIDevice *dev)
{
uint16_t flags;
bool msi64bit;
flags = pci_get_word(dev->config + msi_flags_off(dev));
flags &= ~(PCI_MSI_FLAGS_QSIZE | PCI_MSI_FLAGS_ENABLE);
msi64bit = flags & PCI_MSI_FLAGS_64BIT;
pci_set_word(dev->config + msi_flags_off(dev), flags);
pci_set_long(dev->config + msi_address_lo_off(dev), 0);
if (msi64bit) {
pci_set_long(dev->config + msi_address_hi_off(dev), 0);
}
pci_set_word(dev->config + msi_data_off(dev, msi64bit), 0);
if (flags & PCI_MSI_FLAGS_MASKBIT) {
pci_set_long(dev->config + msi_mask_off(dev, msi64bit), 0);
pci_set_long(dev->config + msi_pending_off(dev, msi64bit), 0);
}
MSI_DEV_PRINTF(dev, "reset\n");
}
static bool msi_is_masked(const PCIDevice *dev, unsigned int vector)
{
uint16_t flags = pci_get_word(dev->config + msi_flags_off(dev));
uint32_t mask;
assert(vector < PCI_MSI_VECTORS_MAX);
if (!(flags & PCI_MSI_FLAGS_MASKBIT)) {
return false;
}
mask = pci_get_long(dev->config +
msi_mask_off(dev, flags & PCI_MSI_FLAGS_64BIT));
return mask & (1U << vector);
}
void msi_notify(PCIDevice *dev, unsigned int vector)
{
uint16_t flags = pci_get_word(dev->config + msi_flags_off(dev));
bool msi64bit = flags & PCI_MSI_FLAGS_64BIT;
unsigned int nr_vectors = msi_nr_vectors(flags);
uint64_t address;
uint32_t data;
assert(vector < nr_vectors);
if (msi_is_masked(dev, vector)) {
assert(flags & PCI_MSI_FLAGS_MASKBIT);
pci_long_test_and_set_mask(
dev->config + msi_pending_off(dev, msi64bit), 1U << vector);
MSI_DEV_PRINTF(dev, "pending vector 0x%x\n", vector);
return;
}
if (msi64bit) {
address = pci_get_quad(dev->config + msi_address_lo_off(dev));
} else {
address = pci_get_long(dev->config + msi_address_lo_off(dev));
}
/* upper bit 31:16 is zero */
data = pci_get_word(dev->config + msi_data_off(dev, msi64bit));
if (nr_vectors > 1) {
data &= ~(nr_vectors - 1);
data |= vector;
}
MSI_DEV_PRINTF(dev,
"notify vector 0x%x"
" address: 0x%"PRIx64" data: 0x%"PRIx32"\n",
vector, address, data);
stl_phys(address, data);
}
/* call this function after updating configs by pci_default_write_config(). */
void msi_write_config(PCIDevice *dev, uint32_t addr, uint32_t val, int len)
{
uint16_t flags = pci_get_word(dev->config + msi_flags_off(dev));
bool msi64bit = flags & PCI_MSI_FLAGS_64BIT;
bool msi_per_vector_mask = flags & PCI_MSI_FLAGS_MASKBIT;
unsigned int nr_vectors;
uint8_t log_num_vecs;
uint8_t log_max_vecs;
unsigned int vector;
uint32_t pending;
int i;
if (!ranges_overlap(addr, len, dev->msi_cap, msi_cap_sizeof(flags))) {
return;
}
#ifdef MSI_DEBUG
MSI_DEV_PRINTF(dev, "addr 0x%"PRIx32" val 0x%"PRIx32" len %d\n",
addr, val, len);
MSI_DEV_PRINTF(dev, "ctrl: 0x%"PRIx16" address: 0x%"PRIx32,
flags,
pci_get_long(dev->config + msi_address_lo_off(dev)));
if (msi64bit) {
fprintf(stderr, " address-hi: 0x%"PRIx32,
pci_get_long(dev->config + msi_address_hi_off(dev)));
}
fprintf(stderr, " data: 0x%"PRIx16,
pci_get_word(dev->config + msi_data_off(dev, msi64bit)));
if (flags & PCI_MSI_FLAGS_MASKBIT) {
fprintf(stderr, " mask 0x%"PRIx32" pending 0x%"PRIx32,
pci_get_long(dev->config + msi_mask_off(dev, msi64bit)),
pci_get_long(dev->config + msi_pending_off(dev, msi64bit)));
}
fprintf(stderr, "\n");
#endif
if (!(flags & PCI_MSI_FLAGS_ENABLE)) {
return;
}
/*
* Now MSI is enabled, clear INTx# interrupts.
* the driver is prohibited from writing enable bit to mask
* a service request. But the guest OS could do this.
* So we just discard the interrupts as moderate fallback.
*
* 6.8.3.3. Enabling Operation
* While enabled for MSI or MSI-X operation, a function is prohibited
* from using its INTx# pin (if implemented) to request
* service (MSI, MSI-X, and INTx# are mutually exclusive).
*/
for (i = 0; i < PCI_NUM_PINS; ++i) {
qemu_set_irq(dev->irq[i], 0);
}
/*
* nr_vectors might be set bigger than capable. So clamp it.
* This is not legal by spec, so we can do anything we like,
* just don't crash the host
*/
log_num_vecs =
(flags & PCI_MSI_FLAGS_QSIZE) >> (ffs(PCI_MSI_FLAGS_QSIZE) - 1);
log_max_vecs =
(flags & PCI_MSI_FLAGS_QMASK) >> (ffs(PCI_MSI_FLAGS_QMASK) - 1);
if (log_num_vecs > log_max_vecs) {
flags &= ~PCI_MSI_FLAGS_QSIZE;
flags |= log_max_vecs << (ffs(PCI_MSI_FLAGS_QSIZE) - 1);
pci_set_word(dev->config + msi_flags_off(dev), flags);
}
if (!msi_per_vector_mask) {
/* if per vector masking isn't supported,
there is no pending interrupt. */
return;
}
nr_vectors = msi_nr_vectors(flags);
/* This will discard pending interrupts, if any. */
pending = pci_get_long(dev->config + msi_pending_off(dev, msi64bit));
pending &= 0xffffffff >> (PCI_MSI_VECTORS_MAX - nr_vectors);
pci_set_long(dev->config + msi_pending_off(dev, msi64bit), pending);
/* deliver pending interrupts which are unmasked */
for (vector = 0; vector < nr_vectors; ++vector) {
if (msi_is_masked(dev, vector) || !(pending & (1U << vector))) {
continue;
}
pci_long_test_and_clear_mask(
dev->config + msi_pending_off(dev, msi64bit), 1U << vector);
msi_notify(dev, vector);
}
}
unsigned int msi_nr_vectors_allocated(const PCIDevice *dev)
{
uint16_t flags = pci_get_word(dev->config + msi_flags_off(dev));
return msi_nr_vectors(flags);
}

41
hw/msi.h Normal file
View File

@ -0,0 +1,41 @@
/*
* msi.h
*
* Copyright (c) 2010 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, see <http://www.gnu.org/licenses/>.
*/
#ifndef QEMU_MSI_H
#define QEMU_MSI_H
#include "qemu-common.h"
#include "pci.h"
bool msi_enabled(const PCIDevice *dev);
int msi_init(struct PCIDevice *dev, uint8_t offset,
unsigned int nr_vectors, bool msi64bit, bool msi_per_vector_mask);
void msi_uninit(struct PCIDevice *dev);
void msi_reset(PCIDevice *dev);
void msi_notify(PCIDevice *dev, unsigned int vector);
void msi_write_config(PCIDevice *dev, uint32_t addr, uint32_t val, int len);
unsigned int msi_nr_vectors_allocated(const PCIDevice *dev);
static inline bool msi_present(const PCIDevice *dev)
{
return dev->cap_present & QEMU_PCI_CAP_MSI;
}
#endif /* QEMU_MSI_H */

View File

@ -74,7 +74,8 @@ static int msix_add_config(struct PCIDevice *pdev, unsigned short nentries,
}
pdev->msix_bar_size = new_size;
config_offset = pci_add_capability(pdev, PCI_CAP_ID_MSIX, MSIX_CAP_LENGTH);
config_offset = pci_add_capability(pdev, PCI_CAP_ID_MSIX,
0, MSIX_CAP_LENGTH);
if (config_offset < 0)
return config_offset;
config = pdev->config + config_offset;
@ -158,6 +159,7 @@ void msix_write_config(PCIDevice *dev, uint32_t addr,
{
unsigned enable_pos = dev->msix_cap + MSIX_CONTROL_OFFSET;
int vector;
int i;
if (!range_covers_byte(addr, len, enable_pos)) {
return;
@ -167,7 +169,9 @@ void msix_write_config(PCIDevice *dev, uint32_t addr,
return;
}
qemu_set_irq(dev->irq[0], 0);
for (i = 0; i < PCI_NUM_PINS; ++i) {
qemu_set_irq(dev->irq[i], 0);
}
if (msix_function_masked(dev)) {
return;

322
hw/pci.c
View File

@ -23,6 +23,10 @@
*/
#include "hw.h"
#include "pci.h"
#include "pci_bridge.h"
#include "pci_internals.h"
#include "msix.h"
#include "msi.h"
#include "monitor.h"
#include "net.h"
#include "sysemu.h"
@ -37,31 +41,10 @@
# define PCI_DPRINTF(format, ...) do { } while (0)
#endif
struct PCIBus {
BusState qbus;
int devfn_min;
pci_set_irq_fn set_irq;
pci_map_irq_fn map_irq;
pci_hotplug_fn hotplug;
DeviceState *hotplug_qdev;
void *irq_opaque;
PCIDevice *devices[256];
PCIDevice *parent_dev;
target_phys_addr_t mem_base;
QLIST_HEAD(, PCIBus) child; /* this will be replaced by qdev later */
QLIST_ENTRY(PCIBus) sibling;/* this will be replaced by qdev later */
/* The bus IRQ state is the logical OR of the connected devices.
Keep a count of the number of devices with raised IRQs. */
int nirq;
int *irq_count;
};
static void pcibus_dev_print(Monitor *mon, DeviceState *dev, int indent);
static char *pcibus_get_dev_path(DeviceState *dev);
static struct BusInfo pci_bus_info = {
struct BusInfo pci_bus_info = {
.name = "PCI",
.size = sizeof(PCIBus),
.print_dev = pcibus_dev_print,
@ -157,9 +140,9 @@ static void pci_device_reset(PCIDevice *dev)
dev->irq_state = 0;
pci_update_irq_status(dev);
/* Clear all writeable bits */
pci_set_word(dev->config + PCI_COMMAND,
pci_get_word(dev->config + PCI_COMMAND) &
~pci_get_word(dev->wmask + PCI_COMMAND));
pci_word_test_and_clear_mask(dev->config + PCI_COMMAND,
pci_get_word(dev->wmask + PCI_COMMAND) |
pci_get_word(dev->w1cmask + PCI_COMMAND));
dev->config[PCI_CACHE_LINE_SIZE] = 0x0;
dev->config[PCI_INTERRUPT_LINE] = 0x0;
for (r = 0; r < PCI_NUM_REGIONS; ++r) {
@ -293,26 +276,6 @@ PCIBus *pci_register_bus(DeviceState *parent, const char *name,
return bus;
}
static void pci_register_secondary_bus(PCIBus *parent,
PCIBus *bus,
PCIDevice *dev,
pci_map_irq_fn map_irq,
const char *name)
{
qbus_create_inplace(&bus->qbus, &pci_bus_info, &dev->qdev, name);
bus->map_irq = map_irq;
bus->parent_dev = dev;
QLIST_INIT(&bus->child);
QLIST_INSERT_HEAD(&parent->child, bus, sibling);
}
static void pci_unregister_secondary_bus(PCIBus *bus)
{
assert(QLIST_EMPTY(&bus->child));
QLIST_REMOVE(bus, sibling);
}
int pci_bus_num(PCIBus *s)
{
if (!s->parent_dev)
@ -331,7 +294,8 @@ static int get_pci_config_device(QEMUFile *f, void *pv, size_t 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]) {
if ((config[i] ^ s->config[i]) &
s->cmask[i] & ~s->wmask[i] & ~s->w1cmask[i]) {
qemu_free(config);
return -EINVAL;
}
@ -464,15 +428,18 @@ static void pci_set_default_subsystem_id(PCIDevice *pci_dev)
}
/*
* Parse [[<domain>:]<bus>:]<slot>, return -1 on error
* Parse [[<domain>:]<bus>:]<slot>, return -1 on error if funcp == NULL
* [[<domain>:]<bus>:]<slot>.<func>, return -1 on error
*/
static int pci_parse_devaddr(const char *addr, int *domp, int *busp, unsigned *slotp)
int pci_parse_devaddr(const char *addr, int *domp, int *busp,
unsigned int *slotp, unsigned int *funcp)
{
const char *p;
char *e;
unsigned long val;
unsigned long dom = 0, bus = 0;
unsigned slot = 0;
unsigned int slot = 0;
unsigned int func = 0;
p = addr;
val = strtoul(p, &e, 16);
@ -494,11 +461,24 @@ static int pci_parse_devaddr(const char *addr, int *domp, int *busp, unsigned *s
}
}
if (dom > 0xffff || bus > 0xff || val > 0x1f)
return -1;
slot = val;
if (funcp != NULL) {
if (*e != '.')
return -1;
p = e + 1;
val = strtoul(p, &e, 16);
if (e == p)
return -1;
func = val;
}
/* if funcp == NULL func is 0 */
if (dom > 0xffff || bus > 0xff || slot > 0x1f || func > 7)
return -1;
if (*e)
return -1;
@ -509,6 +489,8 @@ static int pci_parse_devaddr(const char *addr, int *domp, int *busp, unsigned *s
*domp = dom;
*busp = bus;
*slotp = slot;
if (funcp != NULL)
*funcp = func;
return 0;
}
@ -519,7 +501,7 @@ int pci_read_devaddr(Monitor *mon, const char *addr, int *domp, int *busp,
if (!strncmp(addr, "pci_addr=", 9)) {
addr += 9;
}
if (pci_parse_devaddr(addr, domp, busp, slotp)) {
if (pci_parse_devaddr(addr, domp, busp, slotp, NULL)) {
monitor_printf(mon, "Invalid pci address\n");
return -1;
}
@ -536,7 +518,7 @@ PCIBus *pci_get_bus_devfn(int *devfnp, const char *devaddr)
return pci_find_bus(pci_find_root_bus(0), 0);
}
if (pci_parse_devaddr(devaddr, &dom, &bus, &slot) < 0) {
if (pci_parse_devaddr(devaddr, &dom, &bus, &slot, NULL) < 0) {
return NULL;
}
@ -649,6 +631,7 @@ static void pci_config_alloc(PCIDevice *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->w1cmask = qemu_mallocz(config_size);
pci_dev->used = qemu_mallocz(config_size);
}
@ -657,6 +640,7 @@ 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->w1cmask);
qemu_free(pci_dev->used);
}
@ -780,16 +764,15 @@ static int pci_unregister_device(DeviceState *dev)
}
void pci_register_bar(PCIDevice *pci_dev, int region_num,
pcibus_t size, int type,
pcibus_t size, uint8_t type,
PCIMapIORegionFunc *map_func)
{
PCIIORegion *r;
uint32_t addr;
pcibus_t wmask;
if ((unsigned int)region_num >= PCI_NUM_REGIONS)
return;
uint64_t wmask;
assert(region_num >= 0);
assert(region_num < PCI_NUM_REGIONS);
if (size & (size-1)) {
fprintf(stderr, "ERROR: PCI region size must be pow2 "
"type=0x%x, size=0x%"FMT_PCIBUS"\n", type, size);
@ -820,75 +803,6 @@ void pci_register_bar(PCIDevice *pci_dev, int region_num,
}
}
static uint32_t pci_config_get_io_base(PCIDevice *d,
uint32_t base, uint32_t base_upper16)
{
uint32_t val;
val = ((uint32_t)d->config[base] & PCI_IO_RANGE_MASK) << 8;
if (d->config[base] & PCI_IO_RANGE_TYPE_32) {
val |= (uint32_t)pci_get_word(d->config + base_upper16) << 16;
}
return val;
}
static pcibus_t pci_config_get_memory_base(PCIDevice *d, uint32_t base)
{
return ((pcibus_t)pci_get_word(d->config + base) & PCI_MEMORY_RANGE_MASK)
<< 16;
}
static pcibus_t pci_config_get_pref_base(PCIDevice *d,
uint32_t base, uint32_t upper)
{
pcibus_t tmp;
pcibus_t val;
tmp = (pcibus_t)pci_get_word(d->config + base);
val = (tmp & PCI_PREF_RANGE_MASK) << 16;
if (tmp & PCI_PREF_RANGE_TYPE_64) {
val |= (pcibus_t)pci_get_long(d->config + upper) << 32;
}
return val;
}
static pcibus_t pci_bridge_get_base(PCIDevice *bridge, uint8_t type)
{
pcibus_t base;
if (type & PCI_BASE_ADDRESS_SPACE_IO) {
base = pci_config_get_io_base(bridge,
PCI_IO_BASE, PCI_IO_BASE_UPPER16);
} else {
if (type & PCI_BASE_ADDRESS_MEM_PREFETCH) {
base = pci_config_get_pref_base(
bridge, PCI_PREF_MEMORY_BASE, PCI_PREF_BASE_UPPER32);
} else {
base = pci_config_get_memory_base(bridge, PCI_MEMORY_BASE);
}
}
return base;
}
static pcibus_t pci_bridge_get_limit(PCIDevice *bridge, uint8_t type)
{
pcibus_t limit;
if (type & PCI_BASE_ADDRESS_SPACE_IO) {
limit = pci_config_get_io_base(bridge,
PCI_IO_LIMIT, PCI_IO_LIMIT_UPPER16);
limit |= 0xfff; /* PCI bridge spec 3.2.5.6. */
} else {
if (type & PCI_BASE_ADDRESS_MEM_PREFETCH) {
limit = pci_config_get_pref_base(
bridge, PCI_PREF_MEMORY_LIMIT, PCI_PREF_LIMIT_UPPER32);
} else {
limit = pci_config_get_memory_base(bridge, PCI_MEMORY_LIMIT);
}
limit |= 0xfffff; /* PCI bridge spec 3.2.5.{1, 8}. */
}
return limit;
}
static void pci_bridge_filter(PCIDevice *d, pcibus_t *addr, pcibus_t *size,
uint8_t type)
{
@ -1089,7 +1003,10 @@ void pci_default_write_config(PCIDevice *d, uint32_t addr, uint32_t val, int l)
for (i = 0; i < l && addr + i < config_size; val >>= 8, ++i) {
uint8_t wmask = d->wmask[addr + i];
uint8_t w1cmask = d->w1cmask[addr + i];
assert(!(wmask & w1cmask));
d->config[addr + i] = (d->config[addr + i] & ~wmask) | (val & wmask);
d->config[addr + i] &= ~(val & w1cmask); /* W1C: Write 1 to Clear */
}
if (ranges_overlap(addr, l, PCI_BASE_ADDRESS_0, 24) ||
ranges_overlap(addr, l, PCI_ROM_ADDRESS, 4) ||
@ -1121,6 +1038,23 @@ static void pci_set_irq(void *opaque, int irq_num, int level)
pci_change_irq_level(pci_dev, irq_num, change);
}
bool pci_msi_enabled(PCIDevice *dev)
{
return msix_enabled(dev) || msi_enabled(dev);
}
void pci_msi_notify(PCIDevice *dev, unsigned int vector)
{
if (msix_enabled(dev)) {
msix_notify(dev, vector);
} else if (msi_enabled(dev)) {
msi_notify(dev, vector);
} else {
/* MSI/MSI-X must be enabled */
abort();
}
}
/***********************************************************/
/* monitor info on PCI */
@ -1534,20 +1468,12 @@ PCIDevice *pci_nic_init_nofail(NICInfo *nd, const char *default_model,
return res;
}
typedef struct {
PCIDevice dev;
PCIBus bus;
uint32_t vid;
uint32_t did;
} PCIBridge;
static void pci_bridge_update_mappings_fn(PCIBus *b, PCIDevice *d)
{
pci_update_mappings(d);
}
static void pci_bridge_update_mappings(PCIBus *b)
void pci_bridge_update_mappings(PCIBus *b)
{
PCIBus *child;
@ -1558,23 +1484,6 @@ static void pci_bridge_update_mappings(PCIBus *b)
}
}
static void pci_bridge_write_config(PCIDevice *d,
uint32_t address, uint32_t val, int len)
{
pci_default_write_config(d, address, val, len);
if (/* io base/limit */
ranges_overlap(address, len, PCI_IO_BASE, 2) ||
/* memory base/limit, prefetchable base/limit and
io base/limit upper 16 */
ranges_overlap(address, len, PCI_MEMORY_BASE, 20)) {
PCIBridge *s = container_of(d, PCIBridge, dev);
PCIBus *secondary_bus = &s->bus;
pci_bridge_update_mappings(secondary_bus);
}
}
PCIBus *pci_find_bus(PCIBus *bus, int bus_num)
{
PCIBus *sec;
@ -1618,54 +1527,6 @@ PCIDevice *pci_find_device(PCIBus *bus, int bus_num, int slot, int function)
return bus->devices[PCI_DEVFN(slot, function)];
}
static int pci_bridge_initfn(PCIDevice *dev)
{
PCIBridge *s = DO_UPCAST(PCIBridge, dev, dev);
pci_config_set_vendor_id(s->dev.config, s->vid);
pci_config_set_device_id(s->dev.config, s->did);
pci_set_word(dev->config + PCI_STATUS,
PCI_STATUS_66MHZ | PCI_STATUS_FAST_BACK);
pci_config_set_class(dev->config, PCI_CLASS_BRIDGE_PCI);
dev->config[PCI_HEADER_TYPE] =
(dev->config[PCI_HEADER_TYPE] & PCI_HEADER_TYPE_MULTI_FUNCTION) |
PCI_HEADER_TYPE_BRIDGE;
pci_set_word(dev->config + PCI_SEC_STATUS,
PCI_STATUS_66MHZ | PCI_STATUS_FAST_BACK);
return 0;
}
static int pci_bridge_exitfn(PCIDevice *pci_dev)
{
PCIBridge *s = DO_UPCAST(PCIBridge, dev, pci_dev);
PCIBus *bus = &s->bus;
pci_unregister_secondary_bus(bus);
return 0;
}
PCIBus *pci_bridge_init(PCIBus *bus, int devfn, bool multifunction,
uint16_t vid, uint16_t did,
pci_map_irq_fn map_irq, const char *name)
{
PCIDevice *dev;
PCIBridge *s;
dev = pci_create_multifunction(bus, devfn, multifunction, "pci-bridge");
qdev_prop_set_uint32(&dev->qdev, "vendorid", vid);
qdev_prop_set_uint32(&dev->qdev, "deviceid", did);
qdev_init_nofail(&dev->qdev);
s = DO_UPCAST(PCIBridge, dev, dev);
pci_register_secondary_bus(bus, &s->bus, &s->dev, map_irq, name);
return &s->bus;
}
PCIDevice *pci_bridge_get_device(PCIBus *bus)
{
return bus->parent_dev;
}
static int pci_qdev_init(DeviceState *qdev, DeviceInfo *base)
{
PCIDevice *pci_dev = (PCIDevice *)qdev;
@ -1696,7 +1557,8 @@ static int pci_qdev_init(DeviceState *qdev, DeviceInfo *base)
pci_dev->romfile = qemu_strdup(info->romfile);
pci_add_option_rom(pci_dev);
if (qdev->hotplugged) {
if (bus->hotplug) {
/* lower layer must check qdev->hotplugged */
rc = bus->hotplug(bus->hotplug_qdev, pci_dev, 1);
if (rc != 0) {
int r = pci_unregister_device(&pci_dev->qdev);
@ -1864,11 +1726,25 @@ static void pci_del_option_rom(PCIDevice *pdev)
pdev->rom_offset = 0;
}
/* Reserve space and add capability to the linked list in pci config space */
int pci_add_capability_at_offset(PCIDevice *pdev, uint8_t cap_id,
uint8_t offset, uint8_t size)
/*
* if !offset
* Reserve space and add capability to the linked list in pci config space
*
* if offset = 0,
* Find and reserve space and add capability to the linked list
* in pci config space */
int pci_add_capability(PCIDevice *pdev, uint8_t cap_id,
uint8_t offset, uint8_t size)
{
uint8_t *config = pdev->config + offset;
uint8_t *config;
if (!offset) {
offset = pci_find_space(pdev, size);
if (!offset) {
return -ENOSPC;
}
}
config = pdev->config + offset;
config[PCI_CAP_LIST_ID] = cap_id;
config[PCI_CAP_LIST_NEXT] = pdev->config[PCI_CAPABILITY_LIST];
pdev->config[PCI_CAPABILITY_LIST] = offset;
@ -1881,17 +1757,6 @@ int pci_add_capability_at_offset(PCIDevice *pdev, uint8_t cap_id,
return offset;
}
/* Find and reserve space and add capability to the linked list
* in pci config space */
int pci_add_capability(PCIDevice *pdev, uint8_t cap_id, uint8_t size)
{
uint8_t offset = pci_find_space(pdev, size);
if (!offset) {
return -ENOSPC;
}
return pci_add_capability_at_offset(pdev, cap_id, offset, size);
}
/* Unlink capability from the pci config space. */
void pci_del_capability(PCIDevice *pdev, uint8_t cap_id, uint8_t size)
{
@ -1901,6 +1766,7 @@ void pci_del_capability(PCIDevice *pdev, uint8_t cap_id, uint8_t size)
pdev->config[prev] = pdev->config[offset + PCI_CAP_LIST_NEXT];
/* Make capability writeable again */
memset(pdev->wmask + offset, 0xff, size);
memset(pdev->w1cmask + offset, 0, size);
/* Clear cmask as device-specific registers can't be checked */
memset(pdev->cmask + offset, 0, size);
memset(pdev->used + offset, 0, size);
@ -1971,23 +1837,3 @@ static char *pcibus_get_dev_path(DeviceState *dev)
return strdup(path);
}
static PCIDeviceInfo bridge_info = {
.qdev.name = "pci-bridge",
.qdev.size = sizeof(PCIBridge),
.init = pci_bridge_initfn,
.exit = pci_bridge_exitfn,
.config_write = pci_bridge_write_config,
.is_bridge = 1,
.qdev.props = (Property[]) {
DEFINE_PROP_HEX32("vendorid", PCIBridge, vid, 0),
DEFINE_PROP_HEX32("deviceid", PCIBridge, did, 0),
DEFINE_PROP_END_OF_LIST(),
}
};
static void pci_register_devices(void)
{
pci_qdev_register(&bridge_info);
}
device_init(pci_register_devices)

105
hw/pci.h
View File

@ -9,6 +9,8 @@
/* PCI includes legacy ISA access. */
#include "isa.h"
#include "pcie.h"
/* PCI bus */
#define PCI_DEVFN(slot, func) ((((slot) & 0x1f) << 3) | ((func) & 0x07))
@ -109,11 +111,12 @@ typedef struct PCIIORegion {
/* Bits in cap_present field. */
enum {
QEMU_PCI_CAP_MSIX = 0x1,
QEMU_PCI_CAP_EXPRESS = 0x2,
QEMU_PCI_CAP_MSI = 0x1,
QEMU_PCI_CAP_MSIX = 0x2,
QEMU_PCI_CAP_EXPRESS = 0x4,
/* multifunction capable device */
#define QEMU_PCI_CAP_MULTIFUNCTION_BITNR 2
#define QEMU_PCI_CAP_MULTIFUNCTION_BITNR 3
QEMU_PCI_CAP_MULTIFUNCTION = (1 << QEMU_PCI_CAP_MULTIFUNCTION_BITNR),
};
@ -129,6 +132,9 @@ struct PCIDevice {
/* Used to implement R/W bytes */
uint8_t *wmask;
/* Used to implement RW1C(Write 1 to Clear) bytes */
uint8_t *w1cmask;
/* Used to allocate config space for capabilities. */
uint8_t *used;
@ -168,6 +174,12 @@ struct PCIDevice {
/* Version id needed for VMState */
int32_t version_id;
/* Offset of MSI capability in config space */
uint8_t msi_cap;
/* PCI Express */
PCIExpressDevice exp;
/* Location of option rom */
char *romfile;
ram_addr_t rom_offset;
@ -180,12 +192,11 @@ PCIDevice *pci_register_device(PCIBus *bus, const char *name,
PCIConfigWriteFunc *config_write);
void pci_register_bar(PCIDevice *pci_dev, int region_num,
pcibus_t size, int type,
pcibus_t size, uint8_t type,
PCIMapIORegionFunc *map_func);
int pci_add_capability(PCIDevice *pci_dev, uint8_t cap_id, uint8_t cap_size);
int pci_add_capability_at_offset(PCIDevice *pci_dev, uint8_t cap_id,
uint8_t cap_offset, uint8_t cap_size);
int pci_add_capability(PCIDevice *pdev, uint8_t cap_id,
uint8_t offset, uint8_t size);
void pci_del_capability(PCIDevice *pci_dev, uint8_t cap_id, uint8_t cap_size);
@ -228,15 +239,17 @@ PCIBus *pci_find_bus(PCIBus *bus, int bus_num);
PCIDevice *pci_find_device(PCIBus *bus, int bus_num, int slot, int function);
PCIBus *pci_get_bus_devfn(int *devfnp, const char *devaddr);
int pci_parse_devaddr(const char *addr, int *domp, int *busp,
unsigned int *slotp, unsigned int *funcp);
int pci_read_devaddr(Monitor *mon, const char *addr, int *domp, int *busp,
unsigned *slotp);
void do_pci_info_print(Monitor *mon, const QObject *data);
void do_pci_info(Monitor *mon, QObject **ret_data);
PCIBus *pci_bridge_init(PCIBus *bus, int devfn, bool multifunction,
uint16_t vid, uint16_t did,
pci_map_irq_fn map_irq, const char *name);
PCIDevice *pci_bridge_get_device(PCIBus *bus);
void pci_bridge_update_mappings(PCIBus *b);
bool pci_msi_enabled(PCIDevice *dev);
void pci_msi_notify(PCIDevice *dev, unsigned int vector);
static inline void
pci_set_byte(uint8_t *config, uint8_t val)
@ -322,6 +335,76 @@ pci_config_set_interrupt_pin(uint8_t *pci_config, uint8_t val)
pci_set_byte(&pci_config[PCI_INTERRUPT_PIN], val);
}
/*
* helper functions to do bit mask operation on configuration space.
* Just to set bit, use test-and-set and discard returned value.
* Just to clear bit, use test-and-clear and discard returned value.
* NOTE: They aren't atomic.
*/
static inline uint8_t
pci_byte_test_and_clear_mask(uint8_t *config, uint8_t mask)
{
uint8_t val = pci_get_byte(config);
pci_set_byte(config, val & ~mask);
return val & mask;
}
static inline uint8_t
pci_byte_test_and_set_mask(uint8_t *config, uint8_t mask)
{
uint8_t val = pci_get_byte(config);
pci_set_byte(config, val | mask);
return val & mask;
}
static inline uint16_t
pci_word_test_and_clear_mask(uint8_t *config, uint16_t mask)
{
uint16_t val = pci_get_word(config);
pci_set_word(config, val & ~mask);
return val & mask;
}
static inline uint16_t
pci_word_test_and_set_mask(uint8_t *config, uint16_t mask)
{
uint16_t val = pci_get_word(config);
pci_set_word(config, val | mask);
return val & mask;
}
static inline uint32_t
pci_long_test_and_clear_mask(uint8_t *config, uint32_t mask)
{
uint32_t val = pci_get_long(config);
pci_set_long(config, val & ~mask);
return val & mask;
}
static inline uint32_t
pci_long_test_and_set_mask(uint8_t *config, uint32_t mask)
{
uint32_t val = pci_get_long(config);
pci_set_long(config, val | mask);
return val & mask;
}
static inline uint64_t
pci_quad_test_and_clear_mask(uint8_t *config, uint64_t mask)
{
uint64_t val = pci_get_quad(config);
pci_set_quad(config, val & ~mask);
return val & mask;
}
static inline uint64_t
pci_quad_test_and_set_mask(uint8_t *config, uint64_t mask)
{
uint64_t val = pci_get_quad(config);
pci_set_quad(config, val | mask);
return val & mask;
}
typedef int (*pci_qdev_initfn)(PCIDevice *dev);
typedef struct {
DeviceInfo qdev;

266
hw/pci_bridge.c Normal file
View File

@ -0,0 +1,266 @@
/*
* QEMU PCI bus manager
*
* Copyright (c) 2004 Fabrice Bellard
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to dea
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
/*
* split out from pci.c
* Copyright (c) 2010 Isaku Yamahata <yamahata at valinux co jp>
* VA Linux Systems Japan K.K.
*/
#include "pci_bridge.h"
#include "pci_internals.h"
#include "range.h"
/* PCI bridge subsystem vendor ID helper functions */
#define PCI_SSVID_SIZEOF 8
#define PCI_SSVID_SVID 4
#define PCI_SSVID_SSID 6
int pci_bridge_ssvid_init(PCIDevice *dev, uint8_t offset,
uint16_t svid, uint16_t ssid)
{
int pos;
pos = pci_add_capability(dev, PCI_CAP_ID_SSVID, offset, PCI_SSVID_SIZEOF);
if (pos < 0) {
return pos;
}
pci_set_word(dev->config + pos + PCI_SSVID_SVID, svid);
pci_set_word(dev->config + pos + PCI_SSVID_SSID, ssid);
return pos;
}
/* Accessor function to get parent bridge device from pci bus. */
PCIDevice *pci_bridge_get_device(PCIBus *bus)
{
return bus->parent_dev;
}
/* Accessor function to get secondary bus from pci-to-pci bridge device */
PCIBus *pci_bridge_get_sec_bus(PCIBridge *br)
{
return &br->sec_bus;
}
static uint32_t pci_config_get_io_base(const PCIDevice *d,
uint32_t base, uint32_t base_upper16)
{
uint32_t val;
val = ((uint32_t)d->config[base] & PCI_IO_RANGE_MASK) << 8;
if (d->config[base] & PCI_IO_RANGE_TYPE_32) {
val |= (uint32_t)pci_get_word(d->config + base_upper16) << 16;
}
return val;
}
static pcibus_t pci_config_get_memory_base(const PCIDevice *d, uint32_t base)
{
return ((pcibus_t)pci_get_word(d->config + base) & PCI_MEMORY_RANGE_MASK)
<< 16;
}
static pcibus_t pci_config_get_pref_base(const PCIDevice *d,
uint32_t base, uint32_t upper)
{
pcibus_t tmp;
pcibus_t val;
tmp = (pcibus_t)pci_get_word(d->config + base);
val = (tmp & PCI_PREF_RANGE_MASK) << 16;
if (tmp & PCI_PREF_RANGE_TYPE_64) {
val |= (pcibus_t)pci_get_long(d->config + upper) << 32;
}
return val;
}
/* accessor function to get bridge filtering base address */
pcibus_t pci_bridge_get_base(const PCIDevice *bridge, uint8_t type)
{
pcibus_t base;
if (type & PCI_BASE_ADDRESS_SPACE_IO) {
base = pci_config_get_io_base(bridge,
PCI_IO_BASE, PCI_IO_BASE_UPPER16);
} else {
if (type & PCI_BASE_ADDRESS_MEM_PREFETCH) {
base = pci_config_get_pref_base(
bridge, PCI_PREF_MEMORY_BASE, PCI_PREF_BASE_UPPER32);
} else {
base = pci_config_get_memory_base(bridge, PCI_MEMORY_BASE);
}
}
return base;
}
/* accessor funciton to get bridge filtering limit */
pcibus_t pci_bridge_get_limit(const PCIDevice *bridge, uint8_t type)
{
pcibus_t limit;
if (type & PCI_BASE_ADDRESS_SPACE_IO) {
limit = pci_config_get_io_base(bridge,
PCI_IO_LIMIT, PCI_IO_LIMIT_UPPER16);
limit |= 0xfff; /* PCI bridge spec 3.2.5.6. */
} else {
if (type & PCI_BASE_ADDRESS_MEM_PREFETCH) {
limit = pci_config_get_pref_base(
bridge, PCI_PREF_MEMORY_LIMIT, PCI_PREF_LIMIT_UPPER32);
} else {
limit = pci_config_get_memory_base(bridge, PCI_MEMORY_LIMIT);
}
limit |= 0xfffff; /* PCI bridge spec 3.2.5.{1, 8}. */
}
return limit;
}
/* default write_config function for PCI-to-PCI bridge */
void pci_bridge_write_config(PCIDevice *d,
uint32_t address, uint32_t val, int len)
{
pci_default_write_config(d, address, val, len);
if (/* io base/limit */
ranges_overlap(address, len, PCI_IO_BASE, 2) ||
/* memory base/limit, prefetchable base/limit and
io base/limit upper 16 */
ranges_overlap(address, len, PCI_MEMORY_BASE, 20)) {
PCIBridge *s = container_of(d, PCIBridge, dev);
pci_bridge_update_mappings(&s->sec_bus);
}
}
void pci_bridge_disable_base_limit(PCIDevice *dev)
{
uint8_t *conf = dev->config;
pci_byte_test_and_set_mask(conf + PCI_IO_BASE,
PCI_IO_RANGE_MASK & 0xff);
pci_byte_test_and_clear_mask(conf + PCI_IO_LIMIT,
PCI_IO_RANGE_MASK & 0xff);
pci_word_test_and_set_mask(conf + PCI_MEMORY_BASE,
PCI_MEMORY_RANGE_MASK & 0xffff);
pci_word_test_and_clear_mask(conf + PCI_MEMORY_LIMIT,
PCI_MEMORY_RANGE_MASK & 0xffff);
pci_word_test_and_set_mask(conf + PCI_PREF_MEMORY_BASE,
PCI_PREF_RANGE_MASK & 0xffff);
pci_word_test_and_clear_mask(conf + PCI_PREF_MEMORY_LIMIT,
PCI_PREF_RANGE_MASK & 0xffff);
pci_set_word(conf + PCI_PREF_BASE_UPPER32, 0);
pci_set_word(conf + PCI_PREF_LIMIT_UPPER32, 0);
}
/* reset bridge specific configuration registers */
void pci_bridge_reset_reg(PCIDevice *dev)
{
uint8_t *conf = dev->config;
conf[PCI_PRIMARY_BUS] = 0;
conf[PCI_SECONDARY_BUS] = 0;
conf[PCI_SUBORDINATE_BUS] = 0;
conf[PCI_SEC_LATENCY_TIMER] = 0;
/*
* the default values for base/limit registers aren't specified
* in the PCI-to-PCI-bridge spec. So we don't thouch them here.
* Each implementation can override it.
* typical implementation does
* zero base/limit registers or
* disable forwarding: pci_bridge_disable_base_limit()
* If disable forwarding is wanted, call pci_bridge_disable_base_limit()
* after this function.
*/
pci_byte_test_and_clear_mask(conf + PCI_IO_BASE,
PCI_IO_RANGE_MASK & 0xff);
pci_byte_test_and_clear_mask(conf + PCI_IO_LIMIT,
PCI_IO_RANGE_MASK & 0xff);
pci_word_test_and_clear_mask(conf + PCI_MEMORY_BASE,
PCI_MEMORY_RANGE_MASK & 0xffff);
pci_word_test_and_clear_mask(conf + PCI_MEMORY_LIMIT,
PCI_MEMORY_RANGE_MASK & 0xffff);
pci_word_test_and_clear_mask(conf + PCI_PREF_MEMORY_BASE,
PCI_PREF_RANGE_MASK & 0xffff);
pci_word_test_and_clear_mask(conf + PCI_PREF_MEMORY_LIMIT,
PCI_PREF_RANGE_MASK & 0xffff);
pci_set_word(conf + PCI_PREF_BASE_UPPER32, 0);
pci_set_word(conf + PCI_PREF_LIMIT_UPPER32, 0);
pci_set_word(conf + PCI_BRIDGE_CONTROL, 0);
}
/* default reset function for PCI-to-PCI bridge */
void pci_bridge_reset(DeviceState *qdev)
{
PCIDevice *dev = DO_UPCAST(PCIDevice, qdev, qdev);
pci_bridge_reset_reg(dev);
}
/* default qdev initialization function for PCI-to-PCI bridge */
int pci_bridge_initfn(PCIDevice *dev)
{
PCIBus *parent = dev->bus;
PCIBridge *br = DO_UPCAST(PCIBridge, dev, dev);
PCIBus *sec_bus = &br->sec_bus;
pci_set_word(dev->config + PCI_STATUS,
PCI_STATUS_66MHZ | PCI_STATUS_FAST_BACK);
pci_config_set_class(dev->config, PCI_CLASS_BRIDGE_PCI);
dev->config[PCI_HEADER_TYPE] =
(dev->config[PCI_HEADER_TYPE] & PCI_HEADER_TYPE_MULTI_FUNCTION) |
PCI_HEADER_TYPE_BRIDGE;
pci_set_word(dev->config + PCI_SEC_STATUS,
PCI_STATUS_66MHZ | PCI_STATUS_FAST_BACK);
qbus_create_inplace(&sec_bus->qbus, &pci_bus_info, &dev->qdev,
br->bus_name);
sec_bus->parent_dev = dev;
sec_bus->map_irq = br->map_irq;
QLIST_INIT(&sec_bus->child);
QLIST_INSERT_HEAD(&parent->child, sec_bus, sibling);
return 0;
}
/* default qdev clean up function for PCI-to-PCI bridge */
int pci_bridge_exitfn(PCIDevice *pci_dev)
{
PCIBridge *s = DO_UPCAST(PCIBridge, dev, pci_dev);
assert(QLIST_EMPTY(&s->sec_bus.child));
QLIST_REMOVE(&s->sec_bus, sibling);
/* qbus_free() is called automatically by qdev_free() */
return 0;
}
/*
* before qdev initialization(qdev_init()), this function sets bus_name and
* map_irq callback which are necessry for pci_bridge_initfn() to
* initialize bus.
*/
void pci_bridge_map_irq(PCIBridge *br, const char* bus_name,
pci_map_irq_fn map_irq)
{
br->map_irq = map_irq;
br->bus_name = bus_name;
}

66
hw/pci_bridge.h Normal file
View File

@ -0,0 +1,66 @@
/*
* QEMU PCI bridge
*
* Copyright (c) 2004 Fabrice Bellard
*
* 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, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* split out pci bus specific stuff from pci.[hc] to pci_bridge.[hc]
* Copyright (c) 2009 Isaku Yamahata <yamahata at valinux co jp>
* VA Linux Systems Japan K.K.
*
*/
#ifndef QEMU_PCI_BRIDGE_H
#define QEMU_PCI_BRIDGE_H
#include "pci.h"
int pci_bridge_ssvid_init(PCIDevice *dev, uint8_t offset,
uint16_t svid, uint16_t ssid);
PCIDevice *pci_bridge_get_device(PCIBus *bus);
PCIBus *pci_bridge_get_sec_bus(PCIBridge *br);
pcibus_t pci_bridge_get_base(const PCIDevice *bridge, uint8_t type);
pcibus_t pci_bridge_get_limit(const PCIDevice *bridge, uint8_t type);
void pci_bridge_write_config(PCIDevice *d,
uint32_t address, uint32_t val, int len);
void pci_bridge_disable_base_limit(PCIDevice *dev);
void pci_bridge_reset_reg(PCIDevice *dev);
void pci_bridge_reset(DeviceState *qdev);
int pci_bridge_initfn(PCIDevice *pci_dev);
int pci_bridge_exitfn(PCIDevice *pci_dev);
/*
* before qdev initialization(qdev_init()), this function sets bus_name and
* map_irq callback which are necessry for pci_bridge_initfn() to
* initialize bus.
*/
void pci_bridge_map_irq(PCIBridge *br, const char* bus_name,
pci_map_irq_fn map_irq);
#endif /* QEMU_PCI_BRIDGE_H */
/*
* Local variables:
* c-indent-level: 4
* c-basic-offset: 4
* tab-width: 8
* indent-tab-mode: nil
* End:
*/

View File

@ -57,6 +57,8 @@
#define PCI_VENDOR_ID_AMD 0x1022
#define PCI_DEVICE_ID_AMD_LANCE 0x2000
#define PCI_VENDOR_ID_TI 0x104c
#define PCI_VENDOR_ID_MOTOROLA 0x1057
#define PCI_DEVICE_ID_MOTOROLA_MPC106 0x0002
#define PCI_DEVICE_ID_MOTOROLA_RAVEN 0x4801

47
hw/pci_internals.h Normal file
View File

@ -0,0 +1,47 @@
#ifndef QEMU_PCI_INTERNALS_H
#define QEMU_PCI_INTERNALS_H
/*
* This header files is private to pci.c and pci_bridge.c
* So following structures are opaque to others and shouldn't be
* accessed.
*
* For pci-to-pci bridge needs to include this header file to embed
* PCIBridge in its structure or to get sizeof(PCIBridge),
* However, they shouldn't access those following members directly.
* Use accessor function in pci.h, pci_bridge.h
*/
extern struct BusInfo pci_bus_info;
struct PCIBus {
BusState qbus;
int devfn_min;
pci_set_irq_fn set_irq;
pci_map_irq_fn map_irq;
pci_hotplug_fn hotplug;
DeviceState *hotplug_qdev;
void *irq_opaque;
PCIDevice *devices[256];
PCIDevice *parent_dev;
target_phys_addr_t mem_base;
QLIST_HEAD(, PCIBus) child; /* this will be replaced by qdev later */
QLIST_ENTRY(PCIBus) sibling;/* this will be replaced by qdev later */
/* The bus IRQ state is the logical OR of the connected devices.
Keep a count of the number of devices with raised IRQs. */
int nirq;
int *irq_count;
};
struct PCIBridge {
PCIDevice dev;
/* private member */
PCIBus sec_bus;
pci_map_irq_fn map_irq;
const char *bus_name;
};
#endif /* QEMU_PCI_INTERNALS_H */

541
hw/pcie.c Normal file
View File

@ -0,0 +1,541 @@
/*
* pcie.c
*
* Copyright (c) 2010 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, see <http://www.gnu.org/licenses/>.
*/
#include "sysemu.h"
#include "range.h"
#include "pci_bridge.h"
#include "pcie.h"
#include "msix.h"
#include "msi.h"
#include "pci_internals.h"
#include "pcie_regs.h"
#include "range.h"
//#define DEBUG_PCIE
#ifdef DEBUG_PCIE
# define PCIE_DPRINTF(fmt, ...) \
fprintf(stderr, "%s:%d " fmt, __func__, __LINE__, ## __VA_ARGS__)
#else
# define PCIE_DPRINTF(fmt, ...) do {} while (0)
#endif
#define PCIE_DEV_PRINTF(dev, fmt, ...) \
PCIE_DPRINTF("%s:%x "fmt, (dev)->name, (dev)->devfn, ## __VA_ARGS__)
/***************************************************************************
* pci express capability helper functions
*/
int pcie_cap_init(PCIDevice *dev, uint8_t offset, uint8_t type, uint8_t port)
{
int pos;
uint8_t *exp_cap;
assert(pci_is_express(dev));
pos = pci_add_capability(dev, PCI_CAP_ID_EXP, offset,
PCI_EXP_VER2_SIZEOF);
if (pos < 0) {
return pos;
}
dev->exp.exp_cap = pos;
exp_cap = dev->config + pos;
/* capability register
interrupt message number defaults to 0 */
pci_set_word(exp_cap + PCI_EXP_FLAGS,
((type << PCI_EXP_FLAGS_TYPE_SHIFT) & PCI_EXP_FLAGS_TYPE) |
PCI_EXP_FLAGS_VER2);
/* device capability register
* table 7-12:
* roll based error reporting bit must be set by all
* Functions conforming to the ECN, PCI Express Base
* Specification, Revision 1.1., or subsequent PCI Express Base
* Specification revisions.
*/
pci_set_long(exp_cap + PCI_EXP_DEVCAP, PCI_EXP_DEVCAP_RBER);
pci_set_long(exp_cap + PCI_EXP_LNKCAP,
(port << PCI_EXP_LNKCAP_PN_SHIFT) |
PCI_EXP_LNKCAP_ASPMS_0S |
PCI_EXP_LNK_MLW_1 |
PCI_EXP_LNK_LS_25);
pci_set_word(exp_cap + PCI_EXP_LNKSTA,
PCI_EXP_LNK_MLW_1 | PCI_EXP_LNK_LS_25);
pci_set_long(exp_cap + PCI_EXP_DEVCAP2,
PCI_EXP_DEVCAP2_EFF | PCI_EXP_DEVCAP2_EETLPP);
pci_set_word(dev->wmask + pos, PCI_EXP_DEVCTL2_EETLPPB);
return pos;
}
void pcie_cap_exit(PCIDevice *dev)
{
pci_del_capability(dev, PCI_CAP_ID_EXP, PCI_EXP_VER2_SIZEOF);
}
uint8_t pcie_cap_get_type(const PCIDevice *dev)
{
uint32_t pos = dev->exp.exp_cap;
assert(pos > 0);
return (pci_get_word(dev->config + pos + PCI_EXP_FLAGS) &
PCI_EXP_FLAGS_TYPE) >> PCI_EXP_FLAGS_TYPE_SHIFT;
}
/* MSI/MSI-X */
/* pci express interrupt message number */
/* 7.8.2 PCI Express Capabilities Register: Interrupt Message Number */
void pcie_cap_flags_set_vector(PCIDevice *dev, uint8_t vector)
{
uint8_t *exp_cap = dev->config + dev->exp.exp_cap;
assert(vector < 32);
pci_word_test_and_clear_mask(exp_cap + PCI_EXP_FLAGS, PCI_EXP_FLAGS_IRQ);
pci_word_test_and_set_mask(exp_cap + PCI_EXP_FLAGS,
vector << PCI_EXP_FLAGS_IRQ_SHIFT);
}
uint8_t pcie_cap_flags_get_vector(PCIDevice *dev)
{
return (pci_get_word(dev->config + dev->exp.exp_cap + PCI_EXP_FLAGS) &
PCI_EXP_FLAGS_IRQ) >> PCI_EXP_FLAGS_IRQ_SHIFT;
}
void pcie_cap_deverr_init(PCIDevice *dev)
{
uint32_t pos = dev->exp.exp_cap;
pci_long_test_and_set_mask(dev->config + pos + PCI_EXP_DEVCAP,
PCI_EXP_DEVCAP_RBER);
pci_long_test_and_set_mask(dev->wmask + pos + PCI_EXP_DEVCTL,
PCI_EXP_DEVCTL_CERE | PCI_EXP_DEVCTL_NFERE |
PCI_EXP_DEVCTL_FERE | PCI_EXP_DEVCTL_URRE);
pci_long_test_and_set_mask(dev->w1cmask + pos + PCI_EXP_DEVSTA,
PCI_EXP_DEVSTA_CED | PCI_EXP_DEVSTA_NFED |
PCI_EXP_DEVSTA_URD | PCI_EXP_DEVSTA_URD);
}
void pcie_cap_deverr_reset(PCIDevice *dev)
{
uint8_t *devctl = dev->config + dev->exp.exp_cap + PCI_EXP_DEVCTL;
pci_long_test_and_clear_mask(devctl,
PCI_EXP_DEVCTL_CERE | PCI_EXP_DEVCTL_NFERE |
PCI_EXP_DEVCTL_FERE | PCI_EXP_DEVCTL_URRE);
}
static void hotplug_event_update_event_status(PCIDevice *dev)
{
uint32_t pos = dev->exp.exp_cap;
uint8_t *exp_cap = dev->config + pos;
uint16_t sltctl = pci_get_word(exp_cap + PCI_EXP_SLTCTL);
uint16_t sltsta = pci_get_word(exp_cap + PCI_EXP_SLTSTA);
dev->exp.hpev_notified = (sltctl & PCI_EXP_SLTCTL_HPIE) &&
(sltsta & sltctl & PCI_EXP_HP_EV_SUPPORTED);
}
static void hotplug_event_notify(PCIDevice *dev)
{
bool prev = dev->exp.hpev_notified;
hotplug_event_update_event_status(dev);
if (prev == dev->exp.hpev_notified) {
return;
}
/* Note: the logic above does not take into account whether interrupts
* are masked. The result is that interrupt will be sent when it is
* subsequently unmasked. This appears to be legal: Section 6.7.3.4:
* The Port may optionally send an MSI when there are hot-plug events that
* occur while interrupt generation is disabled, and interrupt generation is
* subsequently enabled. */
if (!pci_msi_enabled(dev)) {
qemu_set_irq(dev->irq[dev->exp.hpev_intx], dev->exp.hpev_notified);
} else if (dev->exp.hpev_notified) {
pci_msi_notify(dev, pcie_cap_flags_get_vector(dev));
}
}
/*
* A PCI Express Hot-Plug Event has occured, so update slot status register
* and notify OS of the event if necessary.
*
* 6.7.3 PCI Express Hot-Plug Events
* 6.7.3.4 Software Notification of Hot-Plug Events
*/
static void pcie_cap_slot_event(PCIDevice *dev, PCIExpressHotPlugEvent event)
{
/* Minor optimization: if nothing changed - no event is needed. */
if (pci_word_test_and_set_mask(dev->config + dev->exp.exp_cap +
PCI_EXP_SLTSTA, event)) {
return;
}
hotplug_event_notify(dev);
}
static int pcie_cap_slot_hotplug(DeviceState *qdev,
PCIDevice *pci_dev, int state)
{
PCIDevice *d = DO_UPCAST(PCIDevice, qdev, qdev);
uint8_t *exp_cap = d->config + d->exp.exp_cap;
uint16_t sltsta = pci_get_word(exp_cap + PCI_EXP_SLTSTA);
if (!pci_dev->qdev.hotplugged) {
assert(state); /* this case only happens at machine creation. */
pci_word_test_and_set_mask(exp_cap + PCI_EXP_SLTSTA,
PCI_EXP_SLTSTA_PDS);
return 0;
}
PCIE_DEV_PRINTF(pci_dev, "hotplug state: %d\n", state);
if (sltsta & PCI_EXP_SLTSTA_EIS) {
/* the slot is electromechanically locked.
* This error is propagated up to qdev and then to HMP/QMP.
*/
return -EBUSY;
}
/* TODO: multifunction hot-plug.
* Right now, only a device of function = 0 is allowed to be
* hot plugged/unplugged.
*/
assert(PCI_FUNC(pci_dev->devfn) == 0);
if (state) {
pci_word_test_and_set_mask(exp_cap + PCI_EXP_SLTSTA,
PCI_EXP_SLTSTA_PDS);
pcie_cap_slot_event(d, PCI_EXP_HP_EV_PDC);
} else {
qdev_free(&pci_dev->qdev);
pci_word_test_and_clear_mask(exp_cap + PCI_EXP_SLTSTA,
PCI_EXP_SLTSTA_PDS);
pcie_cap_slot_event(d, PCI_EXP_HP_EV_PDC);
}
return 0;
}
/* pci express slot for pci express root/downstream port
PCI express capability slot registers */
void pcie_cap_slot_init(PCIDevice *dev, uint16_t slot)
{
uint32_t pos = dev->exp.exp_cap;
pci_word_test_and_set_mask(dev->config + pos + PCI_EXP_FLAGS,
PCI_EXP_FLAGS_SLOT);
pci_long_test_and_clear_mask(dev->config + pos + PCI_EXP_SLTCAP,
~PCI_EXP_SLTCAP_PSN);
pci_long_test_and_set_mask(dev->config + pos + PCI_EXP_SLTCAP,
(slot << PCI_EXP_SLTCAP_PSN_SHIFT) |
PCI_EXP_SLTCAP_EIP |
PCI_EXP_SLTCAP_HPS |
PCI_EXP_SLTCAP_HPC |
PCI_EXP_SLTCAP_PIP |
PCI_EXP_SLTCAP_AIP |
PCI_EXP_SLTCAP_ABP);
pci_word_test_and_clear_mask(dev->config + pos + PCI_EXP_SLTCTL,
PCI_EXP_SLTCTL_PIC |
PCI_EXP_SLTCTL_AIC);
pci_word_test_and_set_mask(dev->config + pos + PCI_EXP_SLTCTL,
PCI_EXP_SLTCTL_PIC_OFF |
PCI_EXP_SLTCTL_AIC_OFF);
pci_word_test_and_set_mask(dev->wmask + pos + PCI_EXP_SLTCTL,
PCI_EXP_SLTCTL_PIC |
PCI_EXP_SLTCTL_AIC |
PCI_EXP_SLTCTL_HPIE |
PCI_EXP_SLTCTL_CCIE |
PCI_EXP_SLTCTL_PDCE |
PCI_EXP_SLTCTL_ABPE);
/* Although reading PCI_EXP_SLTCTL_EIC returns always 0,
* make the bit writable here in order to detect 1b is written.
* pcie_cap_slot_write_config() test-and-clear the bit, so
* this bit always returns 0 to the guest.
*/
pci_word_test_and_set_mask(dev->wmask + pos + PCI_EXP_SLTCTL,
PCI_EXP_SLTCTL_EIC);
pci_word_test_and_set_mask(dev->w1cmask + pos + PCI_EXP_SLTSTA,
PCI_EXP_HP_EV_SUPPORTED);
dev->exp.hpev_notified = false;
pci_bus_hotplug(pci_bridge_get_sec_bus(DO_UPCAST(PCIBridge, dev, dev)),
pcie_cap_slot_hotplug, &dev->qdev);
}
void pcie_cap_slot_reset(PCIDevice *dev)
{
uint8_t *exp_cap = dev->config + dev->exp.exp_cap;
PCIE_DEV_PRINTF(dev, "reset\n");
pci_word_test_and_clear_mask(exp_cap + PCI_EXP_SLTCTL,
PCI_EXP_SLTCTL_EIC |
PCI_EXP_SLTCTL_PIC |
PCI_EXP_SLTCTL_AIC |
PCI_EXP_SLTCTL_HPIE |
PCI_EXP_SLTCTL_CCIE |
PCI_EXP_SLTCTL_PDCE |
PCI_EXP_SLTCTL_ABPE);
pci_word_test_and_set_mask(exp_cap + PCI_EXP_SLTCTL,
PCI_EXP_SLTCTL_PIC_OFF |
PCI_EXP_SLTCTL_AIC_OFF);
pci_word_test_and_clear_mask(exp_cap + PCI_EXP_SLTSTA,
PCI_EXP_SLTSTA_EIS |/* on reset,
the lock is released */
PCI_EXP_SLTSTA_CC |
PCI_EXP_SLTSTA_PDC |
PCI_EXP_SLTSTA_ABP);
hotplug_event_update_event_status(dev);
}
void pcie_cap_slot_write_config(PCIDevice *dev,
uint32_t addr, uint32_t val, int len)
{
uint32_t pos = dev->exp.exp_cap;
uint8_t *exp_cap = dev->config + pos;
uint16_t sltsta = pci_get_word(exp_cap + PCI_EXP_SLTSTA);
if (!ranges_overlap(addr, len, pos + PCI_EXP_SLTCTL, 2)) {
return;
}
if (pci_word_test_and_clear_mask(exp_cap + PCI_EXP_SLTCTL,
PCI_EXP_SLTCTL_EIC)) {
sltsta ^= PCI_EXP_SLTSTA_EIS; /* toggle PCI_EXP_SLTSTA_EIS bit */
pci_set_word(exp_cap + PCI_EXP_SLTSTA, sltsta);
PCIE_DEV_PRINTF(dev, "PCI_EXP_SLTCTL_EIC: "
"sltsta -> 0x%02"PRIx16"\n",
sltsta);
}
hotplug_event_notify(dev);
/*
* 6.7.3.2 Command Completed Events
*
* Software issues a command to a hot-plug capable Downstream Port by
* issuing a write transaction that targets any portion of the Ports Slot
* Control register. A single write to the Slot Control register is
* considered to be a single command, even if the write affects more than
* one field in the Slot Control register. In response to this transaction,
* the Port must carry out the requested actions and then set the
* associated status field for the command completed event. */
/* Real hardware might take a while to complete requested command because
* physical movement would be involved like locking the electromechanical
* lock. However in our case, command is completed instantaneously above,
* so send a command completion event right now.
*/
pcie_cap_slot_event(dev, PCI_EXP_HP_EV_CCI);
}
int pcie_cap_slot_post_load(void *opaque, int version_id)
{
PCIDevice *dev = opaque;
hotplug_event_update_event_status(dev);
return 0;
}
void pcie_cap_slot_push_attention_button(PCIDevice *dev)
{
pcie_cap_slot_event(dev, PCI_EXP_HP_EV_ABP);
}
/* root control/capabilities/status. PME isn't emulated for now */
void pcie_cap_root_init(PCIDevice *dev)
{
pci_set_word(dev->wmask + dev->exp.exp_cap + PCI_EXP_RTCTL,
PCI_EXP_RTCTL_SECEE | PCI_EXP_RTCTL_SENFEE |
PCI_EXP_RTCTL_SEFEE);
}
void pcie_cap_root_reset(PCIDevice *dev)
{
pci_set_word(dev->config + dev->exp.exp_cap + PCI_EXP_RTCTL, 0);
}
/*
* TODO: implement FLR:
* Right now sets the bit which indicates FLR is supported.
*/
/* function level reset(FLR) */
void pcie_cap_flr_init(PCIDevice *dev)
{
pci_long_test_and_set_mask(dev->config + dev->exp.exp_cap + PCI_EXP_DEVCAP,
PCI_EXP_DEVCAP_FLR);
/* Although reading BCR_FLR returns always 0,
* the bit is made writable here in order to detect the 1b is written
* pcie_cap_flr_write_config() test-and-clear the bit, so
* this bit always returns 0 to the guest.
*/
pci_word_test_and_set_mask(dev->wmask + dev->exp.exp_cap + PCI_EXP_DEVCTL,
PCI_EXP_DEVCTL_BCR_FLR);
}
void pcie_cap_flr_write_config(PCIDevice *dev,
uint32_t addr, uint32_t val, int len)
{
uint8_t *devctl = dev->config + dev->exp.exp_cap + PCI_EXP_DEVCTL;
if (pci_word_test_and_clear_mask(devctl, PCI_EXP_DEVCTL_BCR_FLR)) {
/* TODO: implement FLR */
}
}
/* Alternative Routing-ID Interpretation (ARI) */
/* ari forwarding support for down stream port */
void pcie_cap_ari_init(PCIDevice *dev)
{
uint32_t pos = dev->exp.exp_cap;
pci_long_test_and_set_mask(dev->config + pos + PCI_EXP_DEVCAP2,
PCI_EXP_DEVCAP2_ARI);
pci_long_test_and_set_mask(dev->wmask + pos + PCI_EXP_DEVCTL2,
PCI_EXP_DEVCTL2_ARI);
}
void pcie_cap_ari_reset(PCIDevice *dev)
{
uint8_t *devctl2 = dev->config + dev->exp.exp_cap + PCI_EXP_DEVCTL2;
pci_long_test_and_clear_mask(devctl2, PCI_EXP_DEVCTL2_ARI);
}
bool pcie_cap_is_ari_enabled(const PCIDevice *dev)
{
if (!pci_is_express(dev)) {
return false;
}
if (!dev->exp.exp_cap) {
return false;
}
return pci_get_long(dev->config + dev->exp.exp_cap + PCI_EXP_DEVCTL2) &
PCI_EXP_DEVCTL2_ARI;
}
/**************************************************************************
* pci express extended capability allocation functions
* uint16_t ext_cap_id (16 bit)
* uint8_t cap_ver (4 bit)
* uint16_t cap_offset (12 bit)
* uint16_t ext_cap_size
*/
static uint16_t pcie_find_capability_list(PCIDevice *dev, uint16_t cap_id,
uint16_t *prev_p)
{
uint16_t prev = 0;
uint16_t next;
uint32_t header = pci_get_long(dev->config + PCI_CONFIG_SPACE_SIZE);
if (!header) {
/* no extended capability */
next = 0;
goto out;
}
for (next = PCI_CONFIG_SPACE_SIZE; next;
prev = next, next = PCI_EXT_CAP_NEXT(header)) {
assert(next >= PCI_CONFIG_SPACE_SIZE);
assert(next <= PCIE_CONFIG_SPACE_SIZE - 8);
header = pci_get_long(dev->config + next);
if (PCI_EXT_CAP_ID(header) == cap_id) {
break;
}
}
out:
if (prev_p) {
*prev_p = prev;
}
return next;
}
uint16_t pcie_find_capability(PCIDevice *dev, uint16_t cap_id)
{
return pcie_find_capability_list(dev, cap_id, NULL);
}
static void pcie_ext_cap_set_next(PCIDevice *dev, uint16_t pos, uint16_t next)
{
uint16_t header = pci_get_long(dev->config + pos);
assert(!(next & (PCI_EXT_CAP_ALIGN - 1)));
header = (header & ~PCI_EXT_CAP_NEXT_MASK) |
((next << PCI_EXT_CAP_NEXT_SHIFT) & PCI_EXT_CAP_NEXT_MASK);
pci_set_long(dev->config + pos, header);
}
/*
* caller must supply valid (offset, size) * such that the range shouldn't
* overlap with other capability or other registers.
* This function doesn't check it.
*/
void pcie_add_capability(PCIDevice *dev,
uint16_t cap_id, uint8_t cap_ver,
uint16_t offset, uint16_t size)
{
uint32_t header;
uint16_t next;
assert(offset >= PCI_CONFIG_SPACE_SIZE);
assert(offset < offset + size);
assert(offset + size < PCIE_CONFIG_SPACE_SIZE);
assert(size >= 8);
assert(pci_is_express(dev));
if (offset == PCI_CONFIG_SPACE_SIZE) {
header = pci_get_long(dev->config + offset);
next = PCI_EXT_CAP_NEXT(header);
} else {
uint16_t prev;
/* 0 is reserved cap id. use internally to find the last capability
in the linked list */
next = pcie_find_capability_list(dev, 0, &prev);
assert(prev >= PCI_CONFIG_SPACE_SIZE);
assert(next == 0);
pcie_ext_cap_set_next(dev, prev, offset);
}
pci_set_long(dev->config + offset, PCI_EXT_CAP(cap_id, cap_ver, next));
/* Make capability read-only by default */
memset(dev->wmask + offset, 0, size);
memset(dev->w1cmask + offset, 0, size);
/* Check capability by default */
memset(dev->cmask + offset, 0xFF, size);
}
/**************************************************************************
* pci express extended capability helper functions
*/
/* ARI */
void pcie_ari_init(PCIDevice *dev, uint16_t offset, uint16_t nextfn)
{
pcie_add_capability(dev, PCI_EXT_CAP_ID_ARI, PCI_ARI_VER,
offset, PCI_ARI_SIZEOF);
pci_set_long(dev->config + offset + PCI_ARI_CAP, PCI_ARI_CAP_NFN(nextfn));
}

120
hw/pcie.h Normal file
View File

@ -0,0 +1,120 @@
/*
* pcie.h
*
* Copyright (c) 2010 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, see <http://www.gnu.org/licenses/>.
*/
#ifndef QEMU_PCIE_H
#define QEMU_PCIE_H
#include "hw.h"
#include "pci_regs.h"
#include "pcie_regs.h"
typedef enum {
/* for attention and power indicator */
PCI_EXP_HP_IND_RESERVED = PCI_EXP_SLTCTL_IND_RESERVED,
PCI_EXP_HP_IND_ON = PCI_EXP_SLTCTL_IND_ON,
PCI_EXP_HP_IND_BLINK = PCI_EXP_SLTCTL_IND_BLINK,
PCI_EXP_HP_IND_OFF = PCI_EXP_SLTCTL_IND_OFF,
} PCIExpressIndicator;
typedef enum {
/* these bits must match the bits in Slot Control/Status registers.
* PCI_EXP_HP_EV_xxx = PCI_EXP_SLTCTL_xxxE = PCI_EXP_SLTSTA_xxx
*
* Not all the bits of slot control register match with the ones of
* slot status. Not some bits of slot status register is used to
* show status, not to report event occurence.
* So such bits must be masked out when checking the software
* notification condition.
*/
PCI_EXP_HP_EV_ABP = PCI_EXP_SLTCTL_ABPE,
/* attention button pressed */
PCI_EXP_HP_EV_PDC = PCI_EXP_SLTCTL_PDCE,
/* presence detect changed */
PCI_EXP_HP_EV_CCI = PCI_EXP_SLTCTL_CCIE,
/* command completed */
PCI_EXP_HP_EV_SUPPORTED = PCI_EXP_HP_EV_ABP |
PCI_EXP_HP_EV_PDC |
PCI_EXP_HP_EV_CCI,
/* supported event mask */
/* events not listed aren't supported */
} PCIExpressHotPlugEvent;
struct PCIExpressDevice {
/* Offset of express capability in config space */
uint8_t exp_cap;
/* TODO FLR */
/* SLOT */
unsigned int hpev_intx; /* INTx for hot plug event (0-3:INT[A-D]#)
* default is 0 = INTA#
* If the chip wants to use other interrupt
* line, initialize this member with the
* desired number.
* If the chip dynamically changes this member,
* also initialize it when loaded as
* appropreately.
*/
bool hpev_notified; /* Logical AND of conditions for hot plug event.
Following 6.7.3.4:
Software Notification of Hot-Plug Events, an interrupt
is sent whenever the logical and of these conditions
transitions from false to true. */
};
/* PCI express capability helper functions */
int pcie_cap_init(PCIDevice *dev, uint8_t offset, uint8_t type, uint8_t port);
void pcie_cap_exit(PCIDevice *dev);
uint8_t pcie_cap_get_type(const PCIDevice *dev);
void pcie_cap_flags_set_vector(PCIDevice *dev, uint8_t vector);
uint8_t pcie_cap_flags_get_vector(PCIDevice *dev);
void pcie_cap_deverr_init(PCIDevice *dev);
void pcie_cap_deverr_reset(PCIDevice *dev);
void pcie_cap_slot_init(PCIDevice *dev, uint16_t slot);
void pcie_cap_slot_reset(PCIDevice *dev);
void pcie_cap_slot_write_config(PCIDevice *dev,
uint32_t addr, uint32_t val, int len);
int pcie_cap_slot_post_load(void *opaque, int version_id);
void pcie_cap_slot_push_attention_button(PCIDevice *dev);
void pcie_cap_root_init(PCIDevice *dev);
void pcie_cap_root_reset(PCIDevice *dev);
void pcie_cap_flr_init(PCIDevice *dev);
void pcie_cap_flr_write_config(PCIDevice *dev,
uint32_t addr, uint32_t val, int len);
void pcie_cap_ari_init(PCIDevice *dev);
void pcie_cap_ari_reset(PCIDevice *dev);
bool pcie_cap_is_ari_enabled(const PCIDevice *dev);
/* PCI express extended capability helper functions */
uint16_t pcie_find_capability(PCIDevice *dev, uint16_t cap_id);
void pcie_add_capability(PCIDevice *dev,
uint16_t cap_id, uint8_t cap_ver,
uint16_t offset, uint16_t size);
void pcie_ari_init(PCIDevice *dev, uint16_t offset, uint16_t nextfn);
#endif /* QEMU_PCIE_H */

116
hw/pcie_port.c Normal file
View File

@ -0,0 +1,116 @@
/*
* pcie_port.c
*
* Copyright (c) 2010 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, see <http://www.gnu.org/licenses/>.
*/
#include "pcie_port.h"
void pcie_port_init_reg(PCIDevice *d)
{
/* Unlike pci bridge,
66MHz and fast back to back don't apply to pci express port. */
pci_set_word(d->config + PCI_STATUS, 0);
pci_set_word(d->config + PCI_SEC_STATUS, 0);
/* 7.5.3.5 Prefetchable Memory Base Limit
* The Prefetchable Memory Base and Prefetchable Memory Limit registers
* must indicate that 64-bit addresses are supported, as defined in
* PCI-to-PCI Bridge Architecture Specification, Revision 1.2.
*/
pci_word_test_and_set_mask(d->config + PCI_PREF_MEMORY_BASE,
PCI_PREF_RANGE_TYPE_64);
pci_word_test_and_set_mask(d->config + PCI_PREF_MEMORY_LIMIT,
PCI_PREF_RANGE_TYPE_64);
}
/**************************************************************************
* (chassis number, pcie physical slot number) -> pcie slot conversion
*/
struct PCIEChassis {
uint8_t number;
QLIST_HEAD(, PCIESlot) slots;
QLIST_ENTRY(PCIEChassis) next;
};
static QLIST_HEAD(, PCIEChassis) chassis = QLIST_HEAD_INITIALIZER(chassis);
static struct PCIEChassis *pcie_chassis_find(uint8_t chassis_number)
{
struct PCIEChassis *c;
QLIST_FOREACH(c, &chassis, next) {
if (c->number == chassis_number) {
break;
}
}
return c;
}
void pcie_chassis_create(uint8_t chassis_number)
{
struct PCIEChassis *c;
c = pcie_chassis_find(chassis_number);
if (c) {
return;
}
c = qemu_mallocz(sizeof(*c));
c->number = chassis_number;
QLIST_INIT(&c->slots);
QLIST_INSERT_HEAD(&chassis, c, next);
}
static PCIESlot *pcie_chassis_find_slot_with_chassis(struct PCIEChassis *c,
uint8_t slot)
{
PCIESlot *s;
QLIST_FOREACH(s, &c->slots, next) {
if (s->slot == slot) {
break;
}
}
return s;
}
PCIESlot *pcie_chassis_find_slot(uint8_t chassis_number, uint16_t slot)
{
struct PCIEChassis *c;
c = pcie_chassis_find(chassis_number);
if (!c) {
return NULL;
}
return pcie_chassis_find_slot_with_chassis(c, slot);
}
int pcie_chassis_add_slot(struct PCIESlot *slot)
{
struct PCIEChassis *c;
c = pcie_chassis_find(slot->chassis);
if (!c) {
return -ENODEV;
}
if (pcie_chassis_find_slot_with_chassis(c, slot->slot)) {
return -EBUSY;
}
QLIST_INSERT_HEAD(&c->slots, slot, next);
return 0;
}
void pcie_chassis_del_slot(PCIESlot *s)
{
QLIST_REMOVE(s, next);
}

51
hw/pcie_port.h Normal file
View File

@ -0,0 +1,51 @@
/*
* pcie_port.h
*
* Copyright (c) 2010 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, see <http://www.gnu.org/licenses/>.
*/
#ifndef QEMU_PCIE_PORT_H
#define QEMU_PCIE_PORT_H
#include "pci_bridge.h"
#include "pci_internals.h"
struct PCIEPort {
PCIBridge br;
/* pci express switch port */
uint8_t port;
};
void pcie_port_init_reg(PCIDevice *d);
struct PCIESlot {
PCIEPort port;
/* pci express switch port with slot */
uint8_t chassis;
uint16_t slot;
QLIST_ENTRY(PCIESlot) next;
};
void pcie_chassis_create(uint8_t chassis_number);
void pcie_main_chassis_create(void);
PCIESlot *pcie_chassis_find_slot(uint8_t chassis, uint16_t slot);
int pcie_chassis_add_slot(struct PCIESlot *slot);
void pcie_chassis_del_slot(PCIESlot *s);
#endif /* QEMU_PCIE_PORT_H */

154
hw/pcie_regs.h Normal file
View File

@ -0,0 +1,154 @@
/*
* constants for pcie configurations space from pci express spec.
*
* TODO:
* Those constants and macros should go to Linux pci_regs.h
* Once they're merged, they will go away.
*/
#ifndef QEMU_PCIE_REGS_H
#define QEMU_PCIE_REGS_H
/* express capability */
#define PCI_EXP_VER2_SIZEOF 0x3c /* express capability of ver. 2 */
#define PCI_EXT_CAP_VER_SHIFT 16
#define PCI_EXT_CAP_NEXT_SHIFT 20
#define PCI_EXT_CAP_NEXT_MASK (0xffc << PCI_EXT_CAP_NEXT_SHIFT)
#define PCI_EXT_CAP(id, ver, next) \
((id) | \
((ver) << PCI_EXT_CAP_VER_SHIFT) | \
((next) << PCI_EXT_CAP_NEXT_SHIFT))
#define PCI_EXT_CAP_ALIGN 4
#define PCI_EXT_CAP_ALIGNUP(x) \
(((x) + PCI_EXT_CAP_ALIGN - 1) & ~(PCI_EXT_CAP_ALIGN - 1))
/* PCI_EXP_FLAGS */
#define PCI_EXP_FLAGS_VER2 2 /* for now, supports only ver. 2 */
#define PCI_EXP_FLAGS_IRQ_SHIFT (ffs(PCI_EXP_FLAGS_IRQ) - 1)
#define PCI_EXP_FLAGS_TYPE_SHIFT (ffs(PCI_EXP_FLAGS_TYPE) - 1)
/* PCI_EXP_LINK{CAP, STA} */
/* link speed */
#define PCI_EXP_LNK_LS_25 1
#define PCI_EXP_LNK_MLW_SHIFT (ffs(PCI_EXP_LNKCAP_MLW) - 1)
#define PCI_EXP_LNK_MLW_1 (1 << PCI_EXP_LNK_MLW_SHIFT)
/* PCI_EXP_LINKCAP */
#define PCI_EXP_LNKCAP_ASPMS_SHIFT (ffs(PCI_EXP_LNKCAP_ASPMS) - 1)
#define PCI_EXP_LNKCAP_ASPMS_0S (1 << PCI_EXP_LNKCAP_ASPMS_SHIFT)
#define PCI_EXP_LNKCAP_PN_SHIFT (ffs(PCI_EXP_LNKCAP_PN) - 1)
#define PCI_EXP_SLTCAP_PSN_SHIFT (ffs(PCI_EXP_SLTCAP_PSN) - 1)
#define PCI_EXP_SLTCTL_IND_RESERVED 0x0
#define PCI_EXP_SLTCTL_IND_ON 0x1
#define PCI_EXP_SLTCTL_IND_BLINK 0x2
#define PCI_EXP_SLTCTL_IND_OFF 0x3
#define PCI_EXP_SLTCTL_AIC_SHIFT (ffs(PCI_EXP_SLTCTL_AIC) - 1)
#define PCI_EXP_SLTCTL_AIC_OFF \
(PCI_EXP_SLTCTL_IND_OFF << PCI_EXP_SLTCTL_AIC_SHIFT)
#define PCI_EXP_SLTCTL_PIC_SHIFT (ffs(PCI_EXP_SLTCTL_PIC) - 1)
#define PCI_EXP_SLTCTL_PIC_OFF \
(PCI_EXP_SLTCTL_IND_OFF << PCI_EXP_SLTCTL_PIC_SHIFT)
#define PCI_EXP_SLTCTL_SUPPORTED \
(PCI_EXP_SLTCTL_ABPE | \
PCI_EXP_SLTCTL_PDCE | \
PCI_EXP_SLTCTL_CCIE | \
PCI_EXP_SLTCTL_HPIE | \
PCI_EXP_SLTCTL_AIC | \
PCI_EXP_SLTCTL_PCC | \
PCI_EXP_SLTCTL_EIC)
#define PCI_EXP_DEVCAP2_EFF 0x100000
#define PCI_EXP_DEVCAP2_EETLPP 0x200000
#define PCI_EXP_DEVCTL2_EETLPPB 0x80
/* ARI */
#define PCI_ARI_VER 1
#define PCI_ARI_SIZEOF 8
/* AER */
#define PCI_ERR_VER 2
#define PCI_ERR_SIZEOF 0x48
#define PCI_ERR_UNC_SDN 0x00000020 /* surprise down */
#define PCI_ERR_UNC_ACSV 0x00200000 /* ACS Violation */
#define PCI_ERR_UNC_INTN 0x00400000 /* Internal Error */
#define PCI_ERR_UNC_MCBTLP 0x00800000 /* MC Blcoked TLP */
#define PCI_ERR_UNC_ATOP_EBLOCKED 0x01000000 /* atomic op egress blocked */
#define PCI_ERR_UNC_TLP_PRF_BLOCKED 0x02000000 /* TLP Prefix Blocked */
#define PCI_ERR_COR_ADV_NONFATAL 0x00002000 /* Advisory Non-Fatal */
#define PCI_ERR_COR_INTERNAL 0x00004000 /* Corrected Internal */
#define PCI_ERR_COR_HL_OVERFLOW 0x00008000 /* Header Long Overflow */
#define PCI_ERR_CAP_FEP_MASK 0x0000001f
#define PCI_ERR_CAP_MHRC 0x00000200
#define PCI_ERR_CAP_MHRE 0x00000400
#define PCI_ERR_CAP_TLP 0x00000800
#define PCI_ERR_TLP_PREFIX_LOG 0x38
#define PCI_SEC_STATUS_RCV_SYSTEM_ERROR 0x4000
/* aer root error command/status */
#define PCI_ERR_ROOT_CMD_EN_MASK (PCI_ERR_ROOT_CMD_COR_EN | \
PCI_ERR_ROOT_CMD_NONFATAL_EN | \
PCI_ERR_ROOT_CMD_FATAL_EN)
#define PCI_ERR_ROOT_IRQ_MAX 32
#define PCI_ERR_ROOT_IRQ 0xf8000000
#define PCI_ERR_ROOT_IRQ_SHIFT (ffs(PCI_ERR_ROOT_IRQ) - 1)
#define PCI_ERR_ROOT_STATUS_REPORT_MASK (PCI_ERR_ROOT_COR_RCV | \
PCI_ERR_ROOT_MULTI_COR_RCV | \
PCI_ERR_ROOT_UNCOR_RCV | \
PCI_ERR_ROOT_MULTI_UNCOR_RCV | \
PCI_ERR_ROOT_FIRST_FATAL | \
PCI_ERR_ROOT_NONFATAL_RCV | \
PCI_ERR_ROOT_FATAL_RCV)
#define PCI_ERR_UNC_SUPPORTED (PCI_ERR_UNC_DLP | \
PCI_ERR_UNC_SDN | \
PCI_ERR_UNC_POISON_TLP | \
PCI_ERR_UNC_FCP | \
PCI_ERR_UNC_COMP_TIME | \
PCI_ERR_UNC_COMP_ABORT | \
PCI_ERR_UNC_UNX_COMP | \
PCI_ERR_UNC_RX_OVER | \
PCI_ERR_UNC_MALF_TLP | \
PCI_ERR_UNC_ECRC | \
PCI_ERR_UNC_UNSUP | \
PCI_ERR_UNC_ACSV | \
PCI_ERR_UNC_INTN | \
PCI_ERR_UNC_MCBTLP | \
PCI_ERR_UNC_ATOP_EBLOCKED | \
PCI_ERR_UNC_TLP_PRF_BLOCKED)
#define PCI_ERR_UNC_SEVERITY_DEFAULT (PCI_ERR_UNC_DLP | \
PCI_ERR_UNC_SDN | \
PCI_ERR_UNC_FCP | \
PCI_ERR_UNC_RX_OVER | \
PCI_ERR_UNC_MALF_TLP | \
PCI_ERR_UNC_INTN)
#define PCI_ERR_COR_SUPPORTED (PCI_ERR_COR_RCVR | \
PCI_ERR_COR_BAD_TLP | \
PCI_ERR_COR_BAD_DLLP | \
PCI_ERR_COR_REP_ROLL | \
PCI_ERR_COR_REP_TIMER | \
PCI_ERR_COR_ADV_NONFATAL | \
PCI_ERR_COR_INTERNAL | \
PCI_ERR_COR_HL_OVERFLOW)
#define PCI_ERR_COR_MASK_DEFAULT (PCI_ERR_COR_ADV_NONFATAL | \
PCI_ERR_COR_INTERNAL | \
PCI_ERR_COR_HL_OVERFLOW)
#endif /* QEMU_PCIE_REGS_H */

188
hw/xio3130_downstream.c Normal file
View File

@ -0,0 +1,188 @@
/*
* x3130_downstream.c
* TI X3130 pci express downstream port switch
*
* Copyright (c) 2010 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, see <http://www.gnu.org/licenses/>.
*/
#include "pci_ids.h"
#include "msi.h"
#include "pcie.h"
#include "xio3130_downstream.h"
#define PCI_DEVICE_ID_TI_XIO3130D 0x8233 /* downstream port */
#define XIO3130_REVISION 0x1
#define XIO3130_MSI_OFFSET 0x70
#define XIO3130_MSI_SUPPORTED_FLAGS PCI_MSI_FLAGS_64BIT
#define XIO3130_MSI_NR_VECTOR 1
#define XIO3130_SSVID_OFFSET 0x80
#define XIO3130_SSVID_SVID 0
#define XIO3130_SSVID_SSID 0
#define XIO3130_EXP_OFFSET 0x90
#define XIO3130_AER_OFFSET 0x100
static void xio3130_downstream_write_config(PCIDevice *d, uint32_t address,
uint32_t val, int len)
{
pci_bridge_write_config(d, address, val, len);
pcie_cap_flr_write_config(d, address, val, len);
pcie_cap_slot_write_config(d, address, val, len);
msi_write_config(d, address, val, len);
/* TODO: AER */
}
static void xio3130_downstream_reset(DeviceState *qdev)
{
PCIDevice *d = DO_UPCAST(PCIDevice, qdev, qdev);
msi_reset(d);
pcie_cap_deverr_reset(d);
pcie_cap_slot_reset(d);
pcie_cap_ari_reset(d);
pci_bridge_reset(qdev);
}
static int xio3130_downstream_initfn(PCIDevice *d)
{
PCIBridge* br = DO_UPCAST(PCIBridge, dev, d);
PCIEPort *p = DO_UPCAST(PCIEPort, br, br);
PCIESlot *s = DO_UPCAST(PCIESlot, port, p);
int rc;
rc = pci_bridge_initfn(d);
if (rc < 0) {
return rc;
}
pcie_port_init_reg(d);
pci_config_set_vendor_id(d->config, PCI_VENDOR_ID_TI);
pci_config_set_device_id(d->config, PCI_DEVICE_ID_TI_XIO3130D);
d->config[PCI_REVISION_ID] = XIO3130_REVISION;
rc = msi_init(d, XIO3130_MSI_OFFSET, XIO3130_MSI_NR_VECTOR,
XIO3130_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_64BIT,
XIO3130_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_MASKBIT);
if (rc < 0) {
return rc;
}
rc = pci_bridge_ssvid_init(d, XIO3130_SSVID_OFFSET,
XIO3130_SSVID_SVID, XIO3130_SSVID_SSID);
if (rc < 0) {
return rc;
}
rc = pcie_cap_init(d, XIO3130_EXP_OFFSET, PCI_EXP_TYPE_DOWNSTREAM,
p->port);
if (rc < 0) {
return rc;
}
pcie_cap_flr_init(d); /* TODO: implement FLR */
pcie_cap_deverr_init(d);
pcie_cap_slot_init(d, s->slot);
pcie_chassis_create(s->chassis);
rc = pcie_chassis_add_slot(s);
if (rc < 0) {
return rc;
}
pcie_cap_ari_init(d);
/* TODO: AER */
return 0;
}
static int xio3130_downstream_exitfn(PCIDevice *d)
{
/* TODO: AER */
msi_uninit(d);
pcie_cap_exit(d);
return pci_bridge_exitfn(d);
}
PCIESlot *xio3130_downstream_init(PCIBus *bus, int devfn, bool multifunction,
const char *bus_name, pci_map_irq_fn map_irq,
uint8_t port, uint8_t chassis,
uint16_t slot)
{
PCIDevice *d;
PCIBridge *br;
DeviceState *qdev;
d = pci_create_multifunction(bus, devfn, multifunction,
"xio3130-downstream");
if (!d) {
return NULL;
}
br = DO_UPCAST(PCIBridge, dev, d);
qdev = &br->dev.qdev;
pci_bridge_map_irq(br, bus_name, map_irq);
qdev_prop_set_uint8(qdev, "port", port);
qdev_prop_set_uint8(qdev, "chassis", chassis);
qdev_prop_set_uint16(qdev, "slot", slot);
qdev_init_nofail(qdev);
return DO_UPCAST(PCIESlot, port, DO_UPCAST(PCIEPort, br, br));
}
static const VMStateDescription vmstate_xio3130_downstream = {
.name = "xio3130-express-downstream-port",
.version_id = 1,
.minimum_version_id = 1,
.minimum_version_id_old = 1,
.post_load = pcie_cap_slot_post_load,
.fields = (VMStateField[]) {
VMSTATE_PCIE_DEVICE(port.br.dev, PCIESlot),
/* TODO: AER */
VMSTATE_END_OF_LIST()
}
};
static PCIDeviceInfo xio3130_downstream_info = {
.qdev.name = "xio3130-downstream",
.qdev.desc = "TI X3130 Downstream Port of PCI Express Switch",
.qdev.size = sizeof(PCIESlot),
.qdev.reset = xio3130_downstream_reset,
.qdev.vmsd = &vmstate_xio3130_downstream,
.is_express = 1,
.is_bridge = 1,
.config_write = xio3130_downstream_write_config,
.init = xio3130_downstream_initfn,
.exit = xio3130_downstream_exitfn,
.qdev.props = (Property[]) {
DEFINE_PROP_UINT8("port", PCIESlot, port.port, 0),
DEFINE_PROP_UINT8("chassis", PCIESlot, chassis, 0),
DEFINE_PROP_UINT16("slot", PCIESlot, slot, 0),
/* TODO: AER */
DEFINE_PROP_END_OF_LIST(),
}
};
static void xio3130_downstream_register(void)
{
pci_qdev_register(&xio3130_downstream_info);
}
device_init(xio3130_downstream_register);
/*
* Local variables:
* c-indent-level: 4
* c-basic-offset: 4
* tab-width: 8
* indent-tab-mode: nil
* End:
*/

11
hw/xio3130_downstream.h Normal file
View File

@ -0,0 +1,11 @@
#ifndef QEMU_XIO3130_DOWNSTREAM_H
#define QEMU_XIO3130_DOWNSTREAM_H
#include "pcie_port.h"
PCIESlot *xio3130_downstream_init(PCIBus *bus, int devfn, bool multifunction,
const char *bus_name, pci_map_irq_fn map_irq,
uint8_t port, uint8_t chassis,
uint16_t slot);
#endif /* QEMU_XIO3130_DOWNSTREAM_H */

174
hw/xio3130_upstream.c Normal file
View File

@ -0,0 +1,174 @@
/*
* xio3130_upstream.c
* TI X3130 pci express upstream port switch
*
* Copyright (c) 2010 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, see <http://www.gnu.org/licenses/>.
*/
#include "pci_ids.h"
#include "msi.h"
#include "pcie.h"
#include "xio3130_upstream.h"
#define PCI_DEVICE_ID_TI_XIO3130U 0x8232 /* upstream port */
#define XIO3130_REVISION 0x2
#define XIO3130_MSI_OFFSET 0x70
#define XIO3130_MSI_SUPPORTED_FLAGS PCI_MSI_FLAGS_64BIT
#define XIO3130_MSI_NR_VECTOR 1
#define XIO3130_SSVID_OFFSET 0x80
#define XIO3130_SSVID_SVID 0
#define XIO3130_SSVID_SSID 0
#define XIO3130_EXP_OFFSET 0x90
#define XIO3130_AER_OFFSET 0x100
static void xio3130_upstream_write_config(PCIDevice *d, uint32_t address,
uint32_t val, int len)
{
pci_bridge_write_config(d, address, val, len);
pcie_cap_flr_write_config(d, address, val, len);
msi_write_config(d, address, val, len);
/* TODO: AER */
}
static void xio3130_upstream_reset(DeviceState *qdev)
{
PCIDevice *d = DO_UPCAST(PCIDevice, qdev, qdev);
msi_reset(d);
pci_bridge_reset(qdev);
pcie_cap_deverr_reset(d);
}
static int xio3130_upstream_initfn(PCIDevice *d)
{
PCIBridge* br = DO_UPCAST(PCIBridge, dev, d);
PCIEPort *p = DO_UPCAST(PCIEPort, br, br);
int rc;
rc = pci_bridge_initfn(d);
if (rc < 0) {
return rc;
}
pcie_port_init_reg(d);
pci_config_set_vendor_id(d->config, PCI_VENDOR_ID_TI);
pci_config_set_device_id(d->config, PCI_DEVICE_ID_TI_XIO3130U);
d->config[PCI_REVISION_ID] = XIO3130_REVISION;
rc = msi_init(d, XIO3130_MSI_OFFSET, XIO3130_MSI_NR_VECTOR,
XIO3130_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_64BIT,
XIO3130_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_MASKBIT);
if (rc < 0) {
return rc;
}
rc = pci_bridge_ssvid_init(d, XIO3130_SSVID_OFFSET,
XIO3130_SSVID_SVID, XIO3130_SSVID_SSID);
if (rc < 0) {
return rc;
}
rc = pcie_cap_init(d, XIO3130_EXP_OFFSET, PCI_EXP_TYPE_UPSTREAM,
p->port);
if (rc < 0) {
return rc;
}
/* TODO: implement FLR */
pcie_cap_flr_init(d);
pcie_cap_deverr_init(d);
/* TODO: AER */
return 0;
}
static int xio3130_upstream_exitfn(PCIDevice *d)
{
/* TODO: AER */
msi_uninit(d);
pcie_cap_exit(d);
return pci_bridge_exitfn(d);
}
PCIEPort *xio3130_upstream_init(PCIBus *bus, int devfn, bool multifunction,
const char *bus_name, pci_map_irq_fn map_irq,
uint8_t port)
{
PCIDevice *d;
PCIBridge *br;
DeviceState *qdev;
d = pci_create_multifunction(bus, devfn, multifunction, "x3130-upstream");
if (!d) {
return NULL;
}
br = DO_UPCAST(PCIBridge, dev, d);
qdev = &br->dev.qdev;
pci_bridge_map_irq(br, bus_name, map_irq);
qdev_prop_set_uint8(qdev, "port", port);
qdev_init_nofail(qdev);
return DO_UPCAST(PCIEPort, br, br);
}
static const VMStateDescription vmstate_xio3130_upstream = {
.name = "xio3130-express-upstream-port",
.version_id = 1,
.minimum_version_id = 1,
.minimum_version_id_old = 1,
.fields = (VMStateField[]) {
VMSTATE_PCIE_DEVICE(br.dev, PCIEPort),
/* TODO: AER */
VMSTATE_END_OF_LIST()
}
};
static PCIDeviceInfo xio3130_upstream_info = {
.qdev.name = "x3130-upstream",
.qdev.desc = "TI X3130 Upstream Port of PCI Express Switch",
.qdev.size = sizeof(PCIEPort),
.qdev.reset = xio3130_upstream_reset,
.qdev.vmsd = &vmstate_xio3130_upstream,
.is_express = 1,
.is_bridge = 1,
.config_write = xio3130_upstream_write_config,
.init = xio3130_upstream_initfn,
.exit = xio3130_upstream_exitfn,
.qdev.props = (Property[]) {
DEFINE_PROP_UINT8("port", PCIEPort, port, 0),
/* TODO: AER */
DEFINE_PROP_END_OF_LIST(),
}
};
static void xio3130_upstream_register(void)
{
pci_qdev_register(&xio3130_upstream_info);
}
device_init(xio3130_upstream_register);
/*
* Local variables:
* c-indent-level: 4
* c-basic-offset: 4
* tab-width: 8
* indent-tab-mode: nil
* End:
*/

10
hw/xio3130_upstream.h Normal file
View File

@ -0,0 +1,10 @@
#ifndef QEMU_XIO3130_UPSTREAM_H
#define QEMU_XIO3130_UPSTREAM_H
#include "pcie_port.h"
PCIEPort *xio3130_upstream_init(PCIBus *bus, int devfn, bool multifunction,
const char *bus_name, pci_map_irq_fn map_irq,
uint8_t port);
#endif /* QEMU_XIO3130_H */

View File

@ -228,6 +228,10 @@ typedef struct PCIHostState PCIHostState;
typedef struct PCIExpressHost PCIExpressHost;
typedef struct PCIBus PCIBus;
typedef struct PCIDevice PCIDevice;
typedef struct PCIExpressDevice PCIExpressDevice;
typedef struct PCIBridge PCIBridge;
typedef struct PCIEPort PCIEPort;
typedef struct PCIESlot PCIESlot;
typedef struct SerialState SerialState;
typedef struct IRQState *qemu_irq;
typedef struct PCMCIACardState PCMCIACardState;