pcie/aer: glue aer error injection into qemu monitor
introduce pcie_aer_inject_error command. Signed-off-by: Isaku Yamahata <yamahata@valinux.co.jp> Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
This commit is contained in:
parent
f3006dd1e6
commit
2ae63bda50
@ -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
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
* with this program; if not, see <http://www.gnu.org/licenses/>.
|
* with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "sysemu.h"
|
||||||
#include "monitor.h"
|
#include "monitor.h"
|
||||||
#include "pci.h"
|
#include "pci.h"
|
||||||
|
|
||||||
@ -35,3 +36,15 @@ void do_pci_info_print(Monitor *mon, const QObject *data)
|
|||||||
{
|
{
|
||||||
pci_error_message(mon);
|
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);
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
5
sysemu.h
5
sysemu.h
@ -157,6 +157,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
|
||||||
|
Loading…
Reference in New Issue
Block a user