mirror of https://github.com/xqemu/xqemu.git
Merge remote branch 'mst/for_anthony' into staging
This commit is contained in:
commit
a7bd621d7a
|
@ -169,9 +169,7 @@ hw-obj-y =
|
||||||
hw-obj-y += vl.o loader.o
|
hw-obj-y += vl.o loader.o
|
||||||
hw-obj-$(CONFIG_VIRTIO) += virtio.o virtio-console.o
|
hw-obj-$(CONFIG_VIRTIO) += virtio.o virtio-console.o
|
||||||
hw-obj-y += fw_cfg.o
|
hw-obj-y += fw_cfg.o
|
||||||
# FIXME: Core PCI code and its direct dependencies are required by the
|
hw-obj-$(CONFIG_PCI) += pci.o pci_bridge.o
|
||||||
# QMP query-pci command.
|
|
||||||
hw-obj-y += pci.o pci_bridge.o
|
|
||||||
hw-obj-$(CONFIG_PCI) += msix.o msi.o
|
hw-obj-$(CONFIG_PCI) += msix.o msi.o
|
||||||
hw-obj-$(CONFIG_PCI) += pci_host.o pcie_host.o
|
hw-obj-$(CONFIG_PCI) += pci_host.o pcie_host.o
|
||||||
hw-obj-$(CONFIG_PCI) += ioh3420.o xio3130_upstream.o xio3130_downstream.o
|
hw-obj-$(CONFIG_PCI) += ioh3420.o xio3130_upstream.o xio3130_downstream.o
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
# -*- Mode: makefile -*-
|
# -*- Mode: makefile -*-
|
||||||
|
|
||||||
GENERATED_HEADERS = config-target.h
|
GENERATED_HEADERS = config-target.h
|
||||||
|
CONFIG_NO_PCI = $(if $(subst n,,$(CONFIG_PCI)),n,y)
|
||||||
CONFIG_NO_KVM = $(if $(subst n,,$(CONFIG_KVM)),n,y)
|
CONFIG_NO_KVM = $(if $(subst n,,$(CONFIG_KVM)),n,y)
|
||||||
|
|
||||||
include ../config-host.mak
|
include ../config-host.mak
|
||||||
|
@ -188,6 +189,7 @@ ifdef CONFIG_SOFTMMU
|
||||||
obj-y = arch_init.o cpus.o monitor.o machine.o gdbstub.o balloon.o
|
obj-y = arch_init.o cpus.o monitor.o machine.o gdbstub.o balloon.o
|
||||||
# virtio has to be here due to weird dependency between PCI and virtio-net.
|
# virtio has to be here due to weird dependency between PCI and virtio-net.
|
||||||
# need to fix this properly
|
# need to fix this properly
|
||||||
|
obj-$(CONFIG_NO_PCI) += pci-stub.o
|
||||||
obj-$(CONFIG_VIRTIO) += virtio-blk.o virtio-balloon.o virtio-net.o virtio-serial-bus.o
|
obj-$(CONFIG_VIRTIO) += virtio-blk.o virtio-balloon.o virtio-net.o virtio-serial-bus.o
|
||||||
obj-$(CONFIG_VIRTIO_PCI) += virtio-pci.o
|
obj-$(CONFIG_VIRTIO_PCI) += virtio-pci.o
|
||||||
obj-y += vhost_net.o
|
obj-y += vhost_net.o
|
||||||
|
|
|
@ -870,6 +870,31 @@ STEXI
|
||||||
@item pci_del
|
@item pci_del
|
||||||
@findex pci_del
|
@findex pci_del
|
||||||
Hot remove PCI device.
|
Hot remove PCI device.
|
||||||
|
ETEXI
|
||||||
|
|
||||||
|
{
|
||||||
|
.name = "pcie_aer_inject_error",
|
||||||
|
.args_type = "advisory_non_fatal:-a,correctable:-c,"
|
||||||
|
"id:s,error_status:s,"
|
||||||
|
"header0:i?,header1:i?,header2:i?,header3:i?,"
|
||||||
|
"prefix0:i?,prefix1:i?,prefix2:i?,prefix3:i?",
|
||||||
|
.params = "[-a] [-c] id "
|
||||||
|
"<error_status> [<tlp header> [<tlp header prefix>]]",
|
||||||
|
.help = "inject pcie aer error\n\t\t\t"
|
||||||
|
" -a for advisory non fatal error\n\t\t\t"
|
||||||
|
" -c for correctable error\n\t\t\t"
|
||||||
|
"<id> = qdev device id\n\t\t\t"
|
||||||
|
"<error_status> = error string or 32bit\n\t\t\t"
|
||||||
|
"<tlb header> = 32bit x 4\n\t\t\t"
|
||||||
|
"<tlb header prefix> = 32bit x 4",
|
||||||
|
.user_print = pcie_aer_inject_error_print,
|
||||||
|
.mhandler.cmd_new = do_pcie_aer_inejct_error,
|
||||||
|
},
|
||||||
|
|
||||||
|
STEXI
|
||||||
|
@item pcie_aer_inject_error
|
||||||
|
@findex pcie_aer_inject_error
|
||||||
|
Inject PCIe AER error
|
||||||
ETEXI
|
ETEXI
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|
12
hw/pc_piix.c
12
hw/pc_piix.c
|
@ -217,14 +217,6 @@ static QEMUMachine pc_machine = {
|
||||||
.desc = "Standard PC",
|
.desc = "Standard PC",
|
||||||
.init = pc_init_pci,
|
.init = pc_init_pci,
|
||||||
.max_cpus = 255,
|
.max_cpus = 255,
|
||||||
.compat_props = (GlobalProperty[]) {
|
|
||||||
{
|
|
||||||
.driver = "PCI",
|
|
||||||
.property = "command_serr_enable",
|
|
||||||
.value = "off",
|
|
||||||
},
|
|
||||||
{ /* end of list */ }
|
|
||||||
},
|
|
||||||
.is_default = 1,
|
.is_default = 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -246,6 +238,10 @@ static QEMUMachine pc_machine_v0_13 = {
|
||||||
.driver = "vmware-svga",
|
.driver = "vmware-svga",
|
||||||
.property = "rombar",
|
.property = "rombar",
|
||||||
.value = stringify(0),
|
.value = stringify(0),
|
||||||
|
},{
|
||||||
|
.driver = "PCI",
|
||||||
|
.property = "command_serr_enable",
|
||||||
|
.value = "off",
|
||||||
},
|
},
|
||||||
{ /* end of list */ }
|
{ /* end of list */ }
|
||||||
},
|
},
|
||||||
|
|
|
@ -0,0 +1,50 @@
|
||||||
|
/*
|
||||||
|
* PCI stubs for plathome that doesn't support pci bus.
|
||||||
|
*
|
||||||
|
* 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 "monitor.h"
|
||||||
|
#include "pci.h"
|
||||||
|
|
||||||
|
static void pci_error_message(Monitor *mon)
|
||||||
|
{
|
||||||
|
monitor_printf(mon, "PCI devices not supported\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void do_pci_info(Monitor *mon, QObject **ret_data)
|
||||||
|
{
|
||||||
|
pci_error_message(mon);
|
||||||
|
}
|
||||||
|
|
||||||
|
void do_pci_info_print(Monitor *mon, const QObject *data)
|
||||||
|
{
|
||||||
|
pci_error_message(mon);
|
||||||
|
}
|
||||||
|
|
||||||
|
int do_pcie_aer_inejct_error(Monitor *mon,
|
||||||
|
const QDict *qdict, QObject **ret_data)
|
||||||
|
{
|
||||||
|
pci_error_message(mon);
|
||||||
|
return -ENOSYS;
|
||||||
|
}
|
||||||
|
|
||||||
|
void pcie_aer_inject_error_print(Monitor *mon, const QObject *data)
|
||||||
|
{
|
||||||
|
pci_error_message(mon);
|
||||||
|
}
|
82
hw/pci.c
82
hw/pci.c
|
@ -137,7 +137,11 @@ static void pci_update_irq_status(PCIDevice *dev)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void pci_device_reset(PCIDevice *dev)
|
/*
|
||||||
|
* This function is called on #RST and FLR.
|
||||||
|
* FLR if PCI_EXP_DEVCTL_BCR_FLR is set
|
||||||
|
*/
|
||||||
|
void pci_device_reset(PCIDevice *dev)
|
||||||
{
|
{
|
||||||
int r;
|
int r;
|
||||||
/* TODO: call the below unconditionally once all pci devices
|
/* TODO: call the below unconditionally once all pci devices
|
||||||
|
@ -2010,13 +2014,77 @@ static char *pcibus_get_fw_dev_path(DeviceState *dev)
|
||||||
|
|
||||||
static char *pcibus_get_dev_path(DeviceState *dev)
|
static char *pcibus_get_dev_path(DeviceState *dev)
|
||||||
{
|
{
|
||||||
PCIDevice *d = (PCIDevice *)dev;
|
PCIDevice *d = container_of(dev, PCIDevice, qdev);
|
||||||
char path[16];
|
PCIDevice *t;
|
||||||
|
int slot_depth;
|
||||||
|
/* Path format: Domain:00:Slot.Function:Slot.Function....:Slot.Function.
|
||||||
|
* 00 is added here to make this format compatible with
|
||||||
|
* domain:Bus:Slot.Func for systems without nested PCI bridges.
|
||||||
|
* Slot.Function list specifies the slot and function numbers for all
|
||||||
|
* devices on the path from root to the specific device. */
|
||||||
|
int domain_len = strlen("DDDD:00");
|
||||||
|
int slot_len = strlen(":SS.F");
|
||||||
|
int path_len;
|
||||||
|
char *path, *p;
|
||||||
|
|
||||||
snprintf(path, sizeof(path), "%04x:%02x:%02x.%x",
|
/* Calculate # of slots on path between device and root. */;
|
||||||
pci_find_domain(d->bus), d->config[PCI_SECONDARY_BUS],
|
slot_depth = 0;
|
||||||
PCI_SLOT(d->devfn), PCI_FUNC(d->devfn));
|
for (t = d; t; t = t->bus->parent_dev) {
|
||||||
|
++slot_depth;
|
||||||
|
}
|
||||||
|
|
||||||
return strdup(path);
|
path_len = domain_len + slot_len * slot_depth;
|
||||||
|
|
||||||
|
/* Allocate memory, fill in the terminating null byte. */
|
||||||
|
path = malloc(path_len + 1 /* For '\0' */);
|
||||||
|
path[path_len] = '\0';
|
||||||
|
|
||||||
|
/* First field is the domain. */
|
||||||
|
snprintf(path, domain_len, "%04x:00", pci_find_domain(d->bus));
|
||||||
|
|
||||||
|
/* Fill in slot numbers. We walk up from device to root, so need to print
|
||||||
|
* them in the reverse order, last to first. */
|
||||||
|
p = path + path_len;
|
||||||
|
for (t = d; t; t = t->bus->parent_dev) {
|
||||||
|
p -= slot_len;
|
||||||
|
snprintf(p, slot_len, ":%02x.%x", PCI_SLOT(t->devfn), PCI_FUNC(d->devfn));
|
||||||
|
}
|
||||||
|
|
||||||
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int pci_qdev_find_recursive(PCIBus *bus,
|
||||||
|
const char *id, PCIDevice **pdev)
|
||||||
|
{
|
||||||
|
DeviceState *qdev = qdev_find_recursive(&bus->qbus, id);
|
||||||
|
if (!qdev) {
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* roughly check if given qdev is pci device */
|
||||||
|
if (qdev->info->init == &pci_qdev_init &&
|
||||||
|
qdev->parent_bus->info == &pci_bus_info) {
|
||||||
|
*pdev = DO_UPCAST(PCIDevice, qdev, qdev);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int pci_qdev_find_device(const char *id, PCIDevice **pdev)
|
||||||
|
{
|
||||||
|
struct PCIHostBus *host;
|
||||||
|
int rc = -ENODEV;
|
||||||
|
|
||||||
|
QLIST_FOREACH(host, &host_buses, next) {
|
||||||
|
int tmp = pci_qdev_find_recursive(host->bus, id, pdev);
|
||||||
|
if (!tmp) {
|
||||||
|
rc = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (tmp != -ENODEV) {
|
||||||
|
rc = tmp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
2
hw/pci.h
2
hw/pci.h
|
@ -237,6 +237,7 @@ void pci_bus_hotplug(PCIBus *bus, pci_hotplug_fn hotplug, DeviceState *dev);
|
||||||
PCIBus *pci_register_bus(DeviceState *parent, const char *name,
|
PCIBus *pci_register_bus(DeviceState *parent, const char *name,
|
||||||
pci_set_irq_fn set_irq, pci_map_irq_fn map_irq,
|
pci_set_irq_fn set_irq, pci_map_irq_fn map_irq,
|
||||||
void *irq_opaque, int devfn_min, int nirq);
|
void *irq_opaque, int devfn_min, int nirq);
|
||||||
|
void pci_device_reset(PCIDevice *dev);
|
||||||
void pci_bus_reset(PCIBus *bus);
|
void pci_bus_reset(PCIBus *bus);
|
||||||
|
|
||||||
void pci_bus_set_mem_base(PCIBus *bus, target_phys_addr_t base);
|
void pci_bus_set_mem_base(PCIBus *bus, target_phys_addr_t base);
|
||||||
|
@ -251,6 +252,7 @@ PCIBus *pci_find_root_bus(int domain);
|
||||||
int pci_find_domain(const PCIBus *bus);
|
int pci_find_domain(const PCIBus *bus);
|
||||||
PCIBus *pci_find_bus(PCIBus *bus, int bus_num);
|
PCIBus *pci_find_bus(PCIBus *bus, int bus_num);
|
||||||
PCIDevice *pci_find_device(PCIBus *bus, int bus_num, int slot, int function);
|
PCIDevice *pci_find_device(PCIBus *bus, int bus_num, int slot, int function);
|
||||||
|
int pci_qdev_find_device(const char *id, PCIDevice **pdev);
|
||||||
PCIBus *pci_get_bus_devfn(int *devfnp, const char *devaddr);
|
PCIBus *pci_get_bus_devfn(int *devfnp, const char *devaddr);
|
||||||
|
|
||||||
int pci_parse_devaddr(const char *addr, int *domp, int *busp,
|
int pci_parse_devaddr(const char *addr, int *domp, int *busp,
|
||||||
|
|
11
hw/pcie.c
11
hw/pcie.c
|
@ -380,10 +380,6 @@ void pcie_cap_root_reset(PCIDevice *dev)
|
||||||
pci_set_word(dev->config + dev->exp.exp_cap + PCI_EXP_RTCTL, 0);
|
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) */
|
/* function level reset(FLR) */
|
||||||
void pcie_cap_flr_init(PCIDevice *dev)
|
void pcie_cap_flr_init(PCIDevice *dev)
|
||||||
{
|
{
|
||||||
|
@ -403,8 +399,11 @@ void pcie_cap_flr_write_config(PCIDevice *dev,
|
||||||
uint32_t addr, uint32_t val, int len)
|
uint32_t addr, uint32_t val, int len)
|
||||||
{
|
{
|
||||||
uint8_t *devctl = dev->config + dev->exp.exp_cap + PCI_EXP_DEVCTL;
|
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)) {
|
if (pci_get_word(devctl) & PCI_EXP_DEVCTL_BCR_FLR) {
|
||||||
/* TODO: implement FLR */
|
/* Clear PCI_EXP_DEVCTL_BCR_FLR after invoking the reset handler
|
||||||
|
so the handler can detect FLR by looking at this bit. */
|
||||||
|
pci_device_reset(dev);
|
||||||
|
pci_word_test_and_clear_mask(devctl, PCI_EXP_DEVCTL_BCR_FLR);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -63,8 +63,6 @@ struct PCIExpressDevice {
|
||||||
/* Offset of express capability in config space */
|
/* Offset of express capability in config space */
|
||||||
uint8_t exp_cap;
|
uint8_t exp_cap;
|
||||||
|
|
||||||
/* TODO FLR */
|
|
||||||
|
|
||||||
/* SLOT */
|
/* SLOT */
|
||||||
unsigned int hpev_intx; /* INTx for hot plug event (0-3:INT[A-D]#)
|
unsigned int hpev_intx; /* INTx for hot plug event (0-3:INT[A-D]#)
|
||||||
* default is 0 = INTA#
|
* default is 0 = INTA#
|
||||||
|
|
223
hw/pcie_aer.c
223
hw/pcie_aer.c
|
@ -19,6 +19,8 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "sysemu.h"
|
#include "sysemu.h"
|
||||||
|
#include "qemu-objects.h"
|
||||||
|
#include "monitor.h"
|
||||||
#include "pci_bridge.h"
|
#include "pci_bridge.h"
|
||||||
#include "pcie.h"
|
#include "pcie.h"
|
||||||
#include "msix.h"
|
#include "msix.h"
|
||||||
|
@ -806,3 +808,224 @@ const VMStateDescription vmstate_pcie_aer_log = {
|
||||||
VMSTATE_END_OF_LIST()
|
VMSTATE_END_OF_LIST()
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
void pcie_aer_inject_error_print(Monitor *mon, const QObject *data)
|
||||||
|
{
|
||||||
|
QDict *qdict;
|
||||||
|
int devfn;
|
||||||
|
assert(qobject_type(data) == QTYPE_QDICT);
|
||||||
|
qdict = qobject_to_qdict(data);
|
||||||
|
|
||||||
|
devfn = (int)qdict_get_int(qdict, "devfn");
|
||||||
|
monitor_printf(mon, "OK id: %s domain: %x, bus: %x devfn: %x.%x\n",
|
||||||
|
qdict_get_str(qdict, "id"),
|
||||||
|
(int) qdict_get_int(qdict, "domain"),
|
||||||
|
(int) qdict_get_int(qdict, "bus"),
|
||||||
|
PCI_SLOT(devfn), PCI_FUNC(devfn));
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct PCIEAERErrorName {
|
||||||
|
const char *name;
|
||||||
|
uint32_t val;
|
||||||
|
bool correctable;
|
||||||
|
} PCIEAERErrorName;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* AER error name -> value convertion table
|
||||||
|
* This naming scheme is same to linux aer-injection tool.
|
||||||
|
*/
|
||||||
|
static const struct PCIEAERErrorName pcie_aer_error_list[] = {
|
||||||
|
{
|
||||||
|
.name = "TRAIN",
|
||||||
|
.val = PCI_ERR_UNC_TRAIN,
|
||||||
|
.correctable = false,
|
||||||
|
}, {
|
||||||
|
.name = "DLP",
|
||||||
|
.val = PCI_ERR_UNC_DLP,
|
||||||
|
.correctable = false,
|
||||||
|
}, {
|
||||||
|
.name = "SDN",
|
||||||
|
.val = PCI_ERR_UNC_SDN,
|
||||||
|
.correctable = false,
|
||||||
|
}, {
|
||||||
|
.name = "POISON_TLP",
|
||||||
|
.val = PCI_ERR_UNC_POISON_TLP,
|
||||||
|
.correctable = false,
|
||||||
|
}, {
|
||||||
|
.name = "FCP",
|
||||||
|
.val = PCI_ERR_UNC_FCP,
|
||||||
|
.correctable = false,
|
||||||
|
}, {
|
||||||
|
.name = "COMP_TIME",
|
||||||
|
.val = PCI_ERR_UNC_COMP_TIME,
|
||||||
|
.correctable = false,
|
||||||
|
}, {
|
||||||
|
.name = "COMP_ABORT",
|
||||||
|
.val = PCI_ERR_UNC_COMP_ABORT,
|
||||||
|
.correctable = false,
|
||||||
|
}, {
|
||||||
|
.name = "UNX_COMP",
|
||||||
|
.val = PCI_ERR_UNC_UNX_COMP,
|
||||||
|
.correctable = false,
|
||||||
|
}, {
|
||||||
|
.name = "RX_OVER",
|
||||||
|
.val = PCI_ERR_UNC_RX_OVER,
|
||||||
|
.correctable = false,
|
||||||
|
}, {
|
||||||
|
.name = "MALF_TLP",
|
||||||
|
.val = PCI_ERR_UNC_MALF_TLP,
|
||||||
|
.correctable = false,
|
||||||
|
}, {
|
||||||
|
.name = "ECRC",
|
||||||
|
.val = PCI_ERR_UNC_ECRC,
|
||||||
|
.correctable = false,
|
||||||
|
}, {
|
||||||
|
.name = "UNSUP",
|
||||||
|
.val = PCI_ERR_UNC_UNSUP,
|
||||||
|
.correctable = false,
|
||||||
|
}, {
|
||||||
|
.name = "ACSV",
|
||||||
|
.val = PCI_ERR_UNC_ACSV,
|
||||||
|
.correctable = false,
|
||||||
|
}, {
|
||||||
|
.name = "INTN",
|
||||||
|
.val = PCI_ERR_UNC_INTN,
|
||||||
|
.correctable = false,
|
||||||
|
}, {
|
||||||
|
.name = "MCBTLP",
|
||||||
|
.val = PCI_ERR_UNC_MCBTLP,
|
||||||
|
.correctable = false,
|
||||||
|
}, {
|
||||||
|
.name = "ATOP_EBLOCKED",
|
||||||
|
.val = PCI_ERR_UNC_ATOP_EBLOCKED,
|
||||||
|
.correctable = false,
|
||||||
|
}, {
|
||||||
|
.name = "TLP_PRF_BLOCKED",
|
||||||
|
.val = PCI_ERR_UNC_TLP_PRF_BLOCKED,
|
||||||
|
.correctable = false,
|
||||||
|
}, {
|
||||||
|
.name = "RCVR",
|
||||||
|
.val = PCI_ERR_COR_RCVR,
|
||||||
|
.correctable = true,
|
||||||
|
}, {
|
||||||
|
.name = "BAD_TLP",
|
||||||
|
.val = PCI_ERR_COR_BAD_TLP,
|
||||||
|
.correctable = true,
|
||||||
|
}, {
|
||||||
|
.name = "BAD_DLLP",
|
||||||
|
.val = PCI_ERR_COR_BAD_DLLP,
|
||||||
|
.correctable = true,
|
||||||
|
}, {
|
||||||
|
.name = "REP_ROLL",
|
||||||
|
.val = PCI_ERR_COR_REP_ROLL,
|
||||||
|
.correctable = true,
|
||||||
|
}, {
|
||||||
|
.name = "REP_TIMER",
|
||||||
|
.val = PCI_ERR_COR_REP_TIMER,
|
||||||
|
.correctable = true,
|
||||||
|
}, {
|
||||||
|
.name = "ADV_NONFATAL",
|
||||||
|
.val = PCI_ERR_COR_ADV_NONFATAL,
|
||||||
|
.correctable = true,
|
||||||
|
}, {
|
||||||
|
.name = "INTERNAL",
|
||||||
|
.val = PCI_ERR_COR_INTERNAL,
|
||||||
|
.correctable = true,
|
||||||
|
}, {
|
||||||
|
.name = "HL_OVERFLOW",
|
||||||
|
.val = PCI_ERR_COR_HL_OVERFLOW,
|
||||||
|
.correctable = true,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static int pcie_aer_parse_error_string(const char *error_name,
|
||||||
|
uint32_t *status, bool *correctable)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < ARRAY_SIZE(pcie_aer_error_list); i++) {
|
||||||
|
const PCIEAERErrorName *e = &pcie_aer_error_list[i];
|
||||||
|
if (strcmp(error_name, e->name)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
*status = e->val;
|
||||||
|
*correctable = e->correctable;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int do_pcie_aer_inejct_error(Monitor *mon,
|
||||||
|
const QDict *qdict, QObject **ret_data)
|
||||||
|
{
|
||||||
|
const char *id = qdict_get_str(qdict, "id");
|
||||||
|
const char *error_name;
|
||||||
|
uint32_t error_status;
|
||||||
|
bool correctable;
|
||||||
|
PCIDevice *dev;
|
||||||
|
PCIEAERErr err;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = pci_qdev_find_device(id, &dev);
|
||||||
|
if (ret < 0) {
|
||||||
|
monitor_printf(mon,
|
||||||
|
"id or pci device path is invalid or device not "
|
||||||
|
"found. %s\n", id);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
if (!pci_is_express(dev)) {
|
||||||
|
monitor_printf(mon, "the device doesn't support pci express. %s\n",
|
||||||
|
id);
|
||||||
|
return -ENOSYS;
|
||||||
|
}
|
||||||
|
|
||||||
|
error_name = qdict_get_str(qdict, "error_status");
|
||||||
|
if (pcie_aer_parse_error_string(error_name, &error_status, &correctable)) {
|
||||||
|
char *e = NULL;
|
||||||
|
error_status = strtoul(error_name, &e, 0);
|
||||||
|
correctable = !!qdict_get_int(qdict, "correctable");
|
||||||
|
if (!e || *e != '\0') {
|
||||||
|
monitor_printf(mon, "invalid error status value. \"%s\"",
|
||||||
|
error_name);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
err.source_id = (pci_bus_num(dev->bus) << 8) | dev->devfn;
|
||||||
|
|
||||||
|
err.flags = 0;
|
||||||
|
if (correctable) {
|
||||||
|
err.flags |= PCIE_AER_ERR_IS_CORRECTABLE;
|
||||||
|
}
|
||||||
|
if (qdict_get_int(qdict, "advisory_non_fatal")) {
|
||||||
|
err.flags |= PCIE_AER_ERR_MAYBE_ADVISORY;
|
||||||
|
}
|
||||||
|
if (qdict_haskey(qdict, "header0")) {
|
||||||
|
err.flags |= PCIE_AER_ERR_HEADER_VALID;
|
||||||
|
}
|
||||||
|
if (qdict_haskey(qdict, "prefix0")) {
|
||||||
|
err.flags |= PCIE_AER_ERR_TLP_PREFIX_PRESENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
err.header[0] = qdict_get_try_int(qdict, "header0", 0);
|
||||||
|
err.header[1] = qdict_get_try_int(qdict, "header1", 0);
|
||||||
|
err.header[2] = qdict_get_try_int(qdict, "header2", 0);
|
||||||
|
err.header[3] = qdict_get_try_int(qdict, "header3", 0);
|
||||||
|
|
||||||
|
err.prefix[0] = qdict_get_try_int(qdict, "prefix0", 0);
|
||||||
|
err.prefix[1] = qdict_get_try_int(qdict, "prefix1", 0);
|
||||||
|
err.prefix[2] = qdict_get_try_int(qdict, "prefix2", 0);
|
||||||
|
err.prefix[3] = qdict_get_try_int(qdict, "prefix3", 0);
|
||||||
|
|
||||||
|
ret = pcie_aer_inject_error(dev, &err);
|
||||||
|
*ret_data = qobject_from_jsonf("{'id': %s, "
|
||||||
|
"'domain': %d, 'bus': %d, 'devfn': %d, "
|
||||||
|
"'ret': %d}",
|
||||||
|
id,
|
||||||
|
pci_find_domain(dev->bus),
|
||||||
|
pci_bus_num(dev->bus), dev->devfn,
|
||||||
|
ret);
|
||||||
|
assert(*ret_data);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
13
hw/qdev.c
13
hw/qdev.c
|
@ -328,8 +328,9 @@ void qdev_reset_all(DeviceState *dev)
|
||||||
qdev_walk_children(dev, qdev_reset_one, qbus_reset_one, NULL);
|
qdev_walk_children(dev, qdev_reset_one, qbus_reset_one, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
void qbus_reset_all(BusState *bus)
|
void qbus_reset_all_fn(void *opaque)
|
||||||
{
|
{
|
||||||
|
BusState *bus = opaque;
|
||||||
qbus_walk_children(bus, qdev_reset_one, qbus_reset_one, NULL);
|
qbus_walk_children(bus, qdev_reset_one, qbus_reset_one, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -547,7 +548,7 @@ static BusState *qbus_find_recursive(BusState *bus, const char *name,
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static DeviceState *qdev_find_recursive(BusState *bus, const char *id)
|
DeviceState *qdev_find_recursive(BusState *bus, const char *id)
|
||||||
{
|
{
|
||||||
DeviceState *dev, *ret;
|
DeviceState *dev, *ret;
|
||||||
BusState *child;
|
BusState *child;
|
||||||
|
@ -754,8 +755,11 @@ void qbus_create_inplace(BusState *bus, BusInfo *info,
|
||||||
if (parent) {
|
if (parent) {
|
||||||
QLIST_INSERT_HEAD(&parent->child_bus, bus, sibling);
|
QLIST_INSERT_HEAD(&parent->child_bus, bus, sibling);
|
||||||
parent->num_child_bus++;
|
parent->num_child_bus++;
|
||||||
|
} else if (bus != main_system_bus) {
|
||||||
|
/* TODO: once all bus devices are qdevified,
|
||||||
|
only reset handler for main_system_bus should be registered here. */
|
||||||
|
qemu_register_reset(qbus_reset_all_fn, bus);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
BusState *qbus_create(BusInfo *info, DeviceState *parent, const char *name)
|
BusState *qbus_create(BusInfo *info, DeviceState *parent, const char *name)
|
||||||
|
@ -778,6 +782,9 @@ void qbus_free(BusState *bus)
|
||||||
if (bus->parent) {
|
if (bus->parent) {
|
||||||
QLIST_REMOVE(bus, sibling);
|
QLIST_REMOVE(bus, sibling);
|
||||||
bus->parent->num_child_bus--;
|
bus->parent->num_child_bus--;
|
||||||
|
} else {
|
||||||
|
assert(bus != main_system_bus); /* main_system_bus is never freed */
|
||||||
|
qemu_unregister_reset(qbus_reset_all_fn, bus);
|
||||||
}
|
}
|
||||||
qemu_free((void*)bus->name);
|
qemu_free((void*)bus->name);
|
||||||
if (bus->qdev_allocated) {
|
if (bus->qdev_allocated) {
|
||||||
|
|
|
@ -183,6 +183,8 @@ BusState *qdev_get_parent_bus(DeviceState *dev);
|
||||||
|
|
||||||
/*** BUS API. ***/
|
/*** BUS API. ***/
|
||||||
|
|
||||||
|
DeviceState *qdev_find_recursive(BusState *bus, const char *id);
|
||||||
|
|
||||||
/* Returns 0 to walk children, > 0 to skip walk, < 0 to terminate walk. */
|
/* Returns 0 to walk children, > 0 to skip walk, < 0 to terminate walk. */
|
||||||
typedef int (qbus_walkerfn)(BusState *bus, void *opaque);
|
typedef int (qbus_walkerfn)(BusState *bus, void *opaque);
|
||||||
typedef int (qdev_walkerfn)(DeviceState *dev, void *opaque);
|
typedef int (qdev_walkerfn)(DeviceState *dev, void *opaque);
|
||||||
|
@ -198,7 +200,8 @@ int qbus_walk_children(BusState *bus, qdev_walkerfn *devfn,
|
||||||
int qdev_walk_children(DeviceState *dev, qdev_walkerfn *devfn,
|
int qdev_walk_children(DeviceState *dev, qdev_walkerfn *devfn,
|
||||||
qbus_walkerfn *busfn, void *opaque);
|
qbus_walkerfn *busfn, void *opaque);
|
||||||
void qdev_reset_all(DeviceState *dev);
|
void qdev_reset_all(DeviceState *dev);
|
||||||
void qbus_reset_all(BusState *bus);
|
void qbus_reset_all_fn(void *opaque);
|
||||||
|
|
||||||
void qbus_free(BusState *bus);
|
void qbus_free(BusState *bus);
|
||||||
|
|
||||||
#define FROM_QBUS(type, dev) DO_UPCAST(type, qbus, dev)
|
#define FROM_QBUS(type, dev) DO_UPCAST(type, qbus, dev)
|
||||||
|
|
|
@ -89,7 +89,7 @@ static int xio3130_downstream_initfn(PCIDevice *d)
|
||||||
if (rc < 0) {
|
if (rc < 0) {
|
||||||
goto err_msi;
|
goto err_msi;
|
||||||
}
|
}
|
||||||
pcie_cap_flr_init(d); /* TODO: implement FLR */
|
pcie_cap_flr_init(d);
|
||||||
pcie_cap_deverr_init(d);
|
pcie_cap_deverr_init(d);
|
||||||
pcie_cap_slot_init(d, s->slot);
|
pcie_cap_slot_init(d, s->slot);
|
||||||
pcie_chassis_create(s->chassis);
|
pcie_chassis_create(s->chassis);
|
||||||
|
|
|
@ -85,10 +85,7 @@ static int xio3130_upstream_initfn(PCIDevice *d)
|
||||||
if (rc < 0) {
|
if (rc < 0) {
|
||||||
goto err_msi;
|
goto err_msi;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* TODO: implement FLR */
|
|
||||||
pcie_cap_flr_init(d);
|
pcie_cap_flr_init(d);
|
||||||
|
|
||||||
pcie_cap_deverr_init(d);
|
pcie_cap_deverr_init(d);
|
||||||
rc = pcie_aer_init(d, XIO3130_AER_OFFSET);
|
rc = pcie_aer_init(d, XIO3130_AER_OFFSET);
|
||||||
if (rc < 0) {
|
if (rc < 0) {
|
||||||
|
|
5
sysemu.h
5
sysemu.h
|
@ -158,6 +158,11 @@ void pci_device_hot_add(Monitor *mon, const QDict *qdict);
|
||||||
void drive_hot_add(Monitor *mon, const QDict *qdict);
|
void drive_hot_add(Monitor *mon, const QDict *qdict);
|
||||||
void do_pci_device_hot_remove(Monitor *mon, const QDict *qdict);
|
void do_pci_device_hot_remove(Monitor *mon, const QDict *qdict);
|
||||||
|
|
||||||
|
/* pcie aer error injection */
|
||||||
|
void pcie_aer_inject_error_print(Monitor *mon, const QObject *data);
|
||||||
|
int do_pcie_aer_inejct_error(Monitor *mon,
|
||||||
|
const QDict *qdict, QObject **ret_data);
|
||||||
|
|
||||||
/* serial ports */
|
/* serial ports */
|
||||||
|
|
||||||
#define MAX_SERIAL_PORTS 4
|
#define MAX_SERIAL_PORTS 4
|
||||||
|
|
4
vl.c
4
vl.c
|
@ -3092,7 +3092,9 @@ int main(int argc, char **argv, char **envp)
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
qemu_register_reset((void *)qbus_reset_all, sysbus_get_default());
|
/* TODO: once all bus devices are qdevified, this should be done
|
||||||
|
* when bus is created by qdev.c */
|
||||||
|
qemu_register_reset(qbus_reset_all_fn, sysbus_get_default());
|
||||||
qemu_run_machine_init_done_notifiers();
|
qemu_run_machine_init_done_notifiers();
|
||||||
|
|
||||||
qemu_system_reset();
|
qemu_system_reset();
|
||||||
|
|
Loading…
Reference in New Issue