mirror of
https://github.com/qemu/qemu.git
synced 2024-11-24 03:13:44 +08:00
Merge remote branch 'mst/for_anthony' into staging
This commit is contained in:
commit
4f25ac5f42
7
Makefile
7
Makefile
@ -1,6 +1,6 @@
|
||||
# Makefile for QEMU.
|
||||
|
||||
GENERATED_HEADERS = config-host.h trace.h
|
||||
GENERATED_HEADERS = config-host.h trace.h qemu-options.def
|
||||
|
||||
ifneq ($(wildcard config-host.mak),)
|
||||
# Put the all: rule here so that config-host.mak can contain dependencies.
|
||||
@ -71,6 +71,8 @@ build-all: $(DOCS) $(TOOLS) recurse-all
|
||||
|
||||
config-host.h: config-host.h-timestamp
|
||||
config-host.h-timestamp: config-host.mak
|
||||
qemu-options.def: qemu-options.hx
|
||||
$(call quiet-command,sh $(SRC_PATH)/hxtool -h < $< > $@," GEN $@")
|
||||
|
||||
SUBDIR_RULES=$(patsubst %,subdir-%, $(TARGET_DIRS))
|
||||
|
||||
@ -150,6 +152,7 @@ check-qjson: check-qjson.o qfloat.o qint.o qdict.o qstring.o qlist.o qbool.o qjs
|
||||
clean:
|
||||
# avoid old build problems by removing potentially incorrect old files
|
||||
rm -f config.mak op-i386.h opc-i386.h gen-op-i386.h op-arm.h opc-arm.h gen-op-arm.h
|
||||
rm -f qemu-options.def
|
||||
rm -f *.o *.d *.a $(TOOLS) TAGS cscope.* *.pod *~ */*~
|
||||
rm -f slirp/*.o slirp/*.d audio/*.o audio/*.d block/*.o block/*.d net/*.o net/*.d fsdev/*.o fsdev/*.d ui/*.o ui/*.d
|
||||
rm -f qemu-img-cmds.h
|
||||
@ -157,11 +160,11 @@ clean:
|
||||
$(MAKE) -C tests clean
|
||||
for d in $(ALL_SUBDIRS) libhw32 libhw64 libuser libdis libdis-user; do \
|
||||
if test -d $$d; then $(MAKE) -C $$d $@ || exit 1; fi; \
|
||||
rm -f $$d/qemu-options.def; \
|
||||
done
|
||||
|
||||
distclean: clean
|
||||
rm -f config-host.mak config-host.h* config-host.ld $(DOCS) qemu-options.texi qemu-img-cmds.texi qemu-monitor.texi
|
||||
rm -f qemu-options.def
|
||||
rm -f config-all-devices.mak
|
||||
rm -f roms/seabios/config.mak roms/vgabios/config.mak
|
||||
rm -f qemu-doc.info qemu-doc.aux qemu-doc.cp qemu-doc.dvi qemu-doc.fn qemu-doc.info qemu-doc.ky qemu-doc.log qemu-doc.pdf qemu-doc.pg qemu-doc.toc qemu-doc.tp qemu-doc.vr
|
||||
|
@ -159,7 +159,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
|
||||
@ -206,7 +207,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
|
||||
@ -293,10 +295,3 @@ vl.o: QEMU_CFLAGS+=$(GPROF_CFLAGS)
|
||||
|
||||
vl.o: QEMU_CFLAGS+=$(SDL_CFLAGS)
|
||||
|
||||
vl.o: qemu-options.def
|
||||
os-posix.o: qemu-options.def
|
||||
os-win32.o: qemu-options.def
|
||||
|
||||
qemu-options.def: $(SRC_PATH)/qemu-options.hx
|
||||
$(call quiet-command,sh $(SRC_PATH)/hxtool -h < $< > $@," GEN $(TARGET_DIR)$@")
|
||||
|
||||
|
@ -390,6 +390,9 @@ int ram_load(QEMUFile *f, void *opaque, int version_id)
|
||||
host = qemu_get_ram_ptr(addr);
|
||||
else
|
||||
host = host_from_stream_offset(f, addr, flags);
|
||||
if (!host) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ch = qemu_get_byte(f);
|
||||
memset(host, ch, TARGET_PAGE_SIZE);
|
||||
|
@ -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) {
|
||||
|
54
hw/apb_pci.c
54
hw/apb_pci.c
@ -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)
|
||||
|
46
hw/dec_pci.c
46
hw/dec_pci.c
@ -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)
|
||||
|
@ -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
186
hw/ioh3420.c
Normal 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
10
hw/ioh3420.h
Normal 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
347
hw/msi.c
Normal 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
41
hw/msi.h
Normal 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 */
|
@ -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
322
hw/pci.c
@ -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
105
hw/pci.h
@ -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
266
hw/pci_bridge.c
Normal 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
66
hw/pci_bridge.h
Normal 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:
|
||||
*/
|
@ -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
47
hw/pci_internals.h
Normal 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
541
hw/pcie.c
Normal 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 Port’s 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
120
hw/pcie.h
Normal 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
116
hw/pcie_port.c
Normal 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
51
hw/pcie_port.h
Normal 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
154
hw/pcie_regs.h
Normal 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 */
|
11
hw/virtio.c
11
hw/virtio.c
@ -681,6 +681,7 @@ int virtio_load(VirtIODevice *vdev, QEMUFile *f)
|
||||
uint32_t features;
|
||||
uint32_t supported_features =
|
||||
vdev->binding->get_features(vdev->binding_opaque);
|
||||
uint16_t num_heads;
|
||||
|
||||
if (vdev->binding->load_config) {
|
||||
ret = vdev->binding->load_config(vdev->binding_opaque, f);
|
||||
@ -713,6 +714,16 @@ int virtio_load(VirtIODevice *vdev, QEMUFile *f)
|
||||
if (vdev->vq[i].pa) {
|
||||
virtqueue_init(&vdev->vq[i]);
|
||||
}
|
||||
num_heads = vring_avail_idx(&vdev->vq[i]) - vdev->vq[i].last_avail_idx;
|
||||
/* Check it isn't doing very strange things with descriptor numbers. */
|
||||
if (num_heads > vdev->vq[i].vring.num) {
|
||||
fprintf(stderr, "VQ %d size 0x%x Guest index 0x%x "
|
||||
"inconsistent with Host index 0x%x: delta 0x%x\n",
|
||||
i, vdev->vq[i].vring.num,
|
||||
vring_avail_idx(&vdev->vq[i]),
|
||||
vdev->vq[i].last_avail_idx, num_heads);
|
||||
return -1;
|
||||
}
|
||||
if (vdev->binding->load_queue) {
|
||||
ret = vdev->binding->load_queue(vdev->binding_opaque, i, f);
|
||||
if (ret)
|
||||
|
188
hw/xio3130_downstream.c
Normal file
188
hw/xio3130_downstream.c
Normal 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
11
hw/xio3130_downstream.h
Normal 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
174
hw/xio3130_upstream.c
Normal 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
10
hw/xio3130_upstream.h
Normal 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 */
|
16
net.c
16
net.c
@ -774,19 +774,25 @@ int qemu_find_nic_model(NICInfo *nd, const char * const *models,
|
||||
|
||||
int net_handle_fd_param(Monitor *mon, const char *param)
|
||||
{
|
||||
if (!qemu_isdigit(param[0])) {
|
||||
int fd;
|
||||
int fd;
|
||||
|
||||
if (!qemu_isdigit(param[0]) && mon) {
|
||||
|
||||
fd = monitor_get_fd(mon, param);
|
||||
if (fd == -1) {
|
||||
error_report("No file descriptor named %s found", param);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return fd;
|
||||
} else {
|
||||
return strtol(param, NULL, 0);
|
||||
char *endptr = NULL;
|
||||
|
||||
fd = strtol(param, &endptr, 10);
|
||||
if (*endptr || (fd == 0 && param == endptr)) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
static int net_init_nic(QemuOpts *opts,
|
||||
|
@ -237,6 +237,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;
|
||||
|
Loading…
Reference in New Issue
Block a user