1315a1350SMichael S. Tsirkin /* 2315a1350SMichael S. Tsirkin * pcie_aer.c 3315a1350SMichael S. Tsirkin * 4315a1350SMichael S. Tsirkin * Copyright (c) 2010 Isaku Yamahata <yamahata at valinux co jp> 5315a1350SMichael S. Tsirkin * VA Linux Systems Japan K.K. 6315a1350SMichael S. Tsirkin * 7315a1350SMichael S. Tsirkin * This program is free software; you can redistribute it and/or modify 8315a1350SMichael S. Tsirkin * it under the terms of the GNU General Public License as published by 9315a1350SMichael S. Tsirkin * the Free Software Foundation; either version 2 of the License, or 10315a1350SMichael S. Tsirkin * (at your option) any later version. 11315a1350SMichael S. Tsirkin * 12315a1350SMichael S. Tsirkin * This program is distributed in the hope that it will be useful, 13315a1350SMichael S. Tsirkin * but WITHOUT ANY WARRANTY; without even the implied warranty of 14315a1350SMichael S. Tsirkin * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15315a1350SMichael S. Tsirkin * GNU General Public License for more details. 16315a1350SMichael S. Tsirkin * 17315a1350SMichael S. Tsirkin * You should have received a copy of the GNU General Public License along 18315a1350SMichael S. Tsirkin * with this program; if not, see <http://www.gnu.org/licenses/>. 19315a1350SMichael S. Tsirkin */ 20315a1350SMichael S. Tsirkin 2197d5408fSPeter Maydell #include "qemu/osdep.h" 229c17d615SPaolo Bonzini #include "sysemu/sysemu.h" 23452fcdbcSMarkus Armbruster #include "qapi/qmp/qdict.h" 24d6454270SMarkus Armbruster #include "migration/vmstate.h" 2583c9089eSPaolo Bonzini #include "monitor/monitor.h" 26c759b24fSMichael S. Tsirkin #include "hw/pci/pci_bridge.h" 27c759b24fSMichael S. Tsirkin #include "hw/pci/pcie.h" 28c759b24fSMichael S. Tsirkin #include "hw/pci/msix.h" 29c759b24fSMichael S. Tsirkin #include "hw/pci/msi.h" 3006aac7bdSMichael S. Tsirkin #include "hw/pci/pci_bus.h" 31c759b24fSMichael S. Tsirkin #include "hw/pci/pcie_regs.h" 3233848ceeSCao jin #include "qapi/error.h" 33315a1350SMichael S. Tsirkin 34315a1350SMichael S. Tsirkin //#define DEBUG_PCIE 35315a1350SMichael S. Tsirkin #ifdef DEBUG_PCIE 36315a1350SMichael S. Tsirkin # define PCIE_DPRINTF(fmt, ...) \ 37315a1350SMichael S. Tsirkin fprintf(stderr, "%s:%d " fmt, __func__, __LINE__, ## __VA_ARGS__) 38315a1350SMichael S. Tsirkin #else 39315a1350SMichael S. Tsirkin # define PCIE_DPRINTF(fmt, ...) do {} while (0) 40315a1350SMichael S. Tsirkin #endif 41315a1350SMichael S. Tsirkin #define PCIE_DEV_PRINTF(dev, fmt, ...) \ 42315a1350SMichael S. Tsirkin PCIE_DPRINTF("%s:%x "fmt, (dev)->name, (dev)->devfn, ## __VA_ARGS__) 43315a1350SMichael S. Tsirkin 44315a1350SMichael S. Tsirkin #define PCI_ERR_SRC_COR_OFFS 0 45315a1350SMichael S. Tsirkin #define PCI_ERR_SRC_UNCOR_OFFS 2 46315a1350SMichael S. Tsirkin 479edd5338SEric Blake typedef struct PCIEErrorDetails { 489edd5338SEric Blake const char *id; 499edd5338SEric Blake const char *root_bus; 509edd5338SEric Blake int bus; 519edd5338SEric Blake int devfn; 529edd5338SEric Blake } PCIEErrorDetails; 539edd5338SEric Blake 54315a1350SMichael S. Tsirkin /* From 6.2.7 Error Listing and Rules. Table 6-2, 6-3 and 6-4 */ 55315a1350SMichael S. Tsirkin static uint32_t pcie_aer_uncor_default_severity(uint32_t status) 56315a1350SMichael S. Tsirkin { 57315a1350SMichael S. Tsirkin switch (status) { 58315a1350SMichael S. Tsirkin case PCI_ERR_UNC_INTN: 59315a1350SMichael S. Tsirkin case PCI_ERR_UNC_DLP: 60315a1350SMichael S. Tsirkin case PCI_ERR_UNC_SDN: 61315a1350SMichael S. Tsirkin case PCI_ERR_UNC_RX_OVER: 62315a1350SMichael S. Tsirkin case PCI_ERR_UNC_FCP: 63315a1350SMichael S. Tsirkin case PCI_ERR_UNC_MALF_TLP: 64315a1350SMichael S. Tsirkin return PCI_ERR_ROOT_CMD_FATAL_EN; 65315a1350SMichael S. Tsirkin case PCI_ERR_UNC_POISON_TLP: 66315a1350SMichael S. Tsirkin case PCI_ERR_UNC_ECRC: 67315a1350SMichael S. Tsirkin case PCI_ERR_UNC_UNSUP: 68315a1350SMichael S. Tsirkin case PCI_ERR_UNC_COMP_TIME: 69315a1350SMichael S. Tsirkin case PCI_ERR_UNC_COMP_ABORT: 70315a1350SMichael S. Tsirkin case PCI_ERR_UNC_UNX_COMP: 71315a1350SMichael S. Tsirkin case PCI_ERR_UNC_ACSV: 72315a1350SMichael S. Tsirkin case PCI_ERR_UNC_MCBTLP: 73315a1350SMichael S. Tsirkin case PCI_ERR_UNC_ATOP_EBLOCKED: 74315a1350SMichael S. Tsirkin case PCI_ERR_UNC_TLP_PRF_BLOCKED: 75315a1350SMichael S. Tsirkin return PCI_ERR_ROOT_CMD_NONFATAL_EN; 76315a1350SMichael S. Tsirkin default: 77315a1350SMichael S. Tsirkin abort(); 78315a1350SMichael S. Tsirkin break; 79315a1350SMichael S. Tsirkin } 80315a1350SMichael S. Tsirkin return PCI_ERR_ROOT_CMD_FATAL_EN; 81315a1350SMichael S. Tsirkin } 82315a1350SMichael S. Tsirkin 83315a1350SMichael S. Tsirkin static int aer_log_add_err(PCIEAERLog *aer_log, const PCIEAERErr *err) 84315a1350SMichael S. Tsirkin { 85315a1350SMichael S. Tsirkin if (aer_log->log_num == aer_log->log_max) { 86315a1350SMichael S. Tsirkin return -1; 87315a1350SMichael S. Tsirkin } 88315a1350SMichael S. Tsirkin memcpy(&aer_log->log[aer_log->log_num], err, sizeof *err); 89315a1350SMichael S. Tsirkin aer_log->log_num++; 90315a1350SMichael S. Tsirkin return 0; 91315a1350SMichael S. Tsirkin } 92315a1350SMichael S. Tsirkin 93315a1350SMichael S. Tsirkin static void aer_log_del_err(PCIEAERLog *aer_log, PCIEAERErr *err) 94315a1350SMichael S. Tsirkin { 95315a1350SMichael S. Tsirkin assert(aer_log->log_num); 96315a1350SMichael S. Tsirkin *err = aer_log->log[0]; 97315a1350SMichael S. Tsirkin aer_log->log_num--; 98315a1350SMichael S. Tsirkin memmove(&aer_log->log[0], &aer_log->log[1], 99315a1350SMichael S. Tsirkin aer_log->log_num * sizeof *err); 100315a1350SMichael S. Tsirkin } 101315a1350SMichael S. Tsirkin 102315a1350SMichael S. Tsirkin static void aer_log_clear_all_err(PCIEAERLog *aer_log) 103315a1350SMichael S. Tsirkin { 104315a1350SMichael S. Tsirkin aer_log->log_num = 0; 105315a1350SMichael S. Tsirkin } 106315a1350SMichael S. Tsirkin 107f18c697bSDou Liyang int pcie_aer_init(PCIDevice *dev, uint8_t cap_ver, uint16_t offset, 108f18c697bSDou Liyang uint16_t size, Error **errp) 109315a1350SMichael S. Tsirkin { 110f18c697bSDou Liyang pcie_add_capability(dev, PCI_EXT_CAP_ID_ERR, cap_ver, 1118d86ada2SChen Fan offset, size); 11233848ceeSCao jin dev->exp.aer_cap = offset; 113315a1350SMichael S. Tsirkin 11433848ceeSCao jin /* clip down the value to avoid unreasonable memory usage */ 115315a1350SMichael S. Tsirkin if (dev->exp.aer_log.log_max > PCIE_AER_LOG_MAX_LIMIT) { 11633848ceeSCao jin error_setg(errp, "Invalid aer_log_max %d. The max number of aer log " 11733848ceeSCao jin "is %d", dev->exp.aer_log.log_max, PCIE_AER_LOG_MAX_LIMIT); 118315a1350SMichael S. Tsirkin return -EINVAL; 119315a1350SMichael S. Tsirkin } 120315a1350SMichael S. Tsirkin dev->exp.aer_log.log = g_malloc0(sizeof dev->exp.aer_log.log[0] * 121315a1350SMichael S. Tsirkin dev->exp.aer_log.log_max); 122315a1350SMichael S. Tsirkin 123315a1350SMichael S. Tsirkin pci_set_long(dev->w1cmask + offset + PCI_ERR_UNCOR_STATUS, 124315a1350SMichael S. Tsirkin PCI_ERR_UNC_SUPPORTED); 125315a1350SMichael S. Tsirkin 126315a1350SMichael S. Tsirkin pci_set_long(dev->config + offset + PCI_ERR_UNCOR_SEVER, 127315a1350SMichael S. Tsirkin PCI_ERR_UNC_SEVERITY_DEFAULT); 128315a1350SMichael S. Tsirkin pci_set_long(dev->wmask + offset + PCI_ERR_UNCOR_SEVER, 129315a1350SMichael S. Tsirkin PCI_ERR_UNC_SUPPORTED); 130315a1350SMichael S. Tsirkin 131315a1350SMichael S. Tsirkin pci_long_test_and_set_mask(dev->w1cmask + offset + PCI_ERR_COR_STATUS, 132310e91f7SChen Fan PCI_ERR_COR_SUPPORTED); 133315a1350SMichael S. Tsirkin 134315a1350SMichael S. Tsirkin pci_set_long(dev->config + offset + PCI_ERR_COR_MASK, 135315a1350SMichael S. Tsirkin PCI_ERR_COR_MASK_DEFAULT); 136315a1350SMichael S. Tsirkin pci_set_long(dev->wmask + offset + PCI_ERR_COR_MASK, 137315a1350SMichael S. Tsirkin PCI_ERR_COR_SUPPORTED); 138315a1350SMichael S. Tsirkin 139315a1350SMichael S. Tsirkin /* capabilities and control. multiple header logging is supported */ 140315a1350SMichael S. Tsirkin if (dev->exp.aer_log.log_max > 0) { 141315a1350SMichael S. Tsirkin pci_set_long(dev->config + offset + PCI_ERR_CAP, 142315a1350SMichael S. Tsirkin PCI_ERR_CAP_ECRC_GENC | PCI_ERR_CAP_ECRC_CHKC | 143315a1350SMichael S. Tsirkin PCI_ERR_CAP_MHRC); 144315a1350SMichael S. Tsirkin pci_set_long(dev->wmask + offset + PCI_ERR_CAP, 145315a1350SMichael S. Tsirkin PCI_ERR_CAP_ECRC_GENE | PCI_ERR_CAP_ECRC_CHKE | 146315a1350SMichael S. Tsirkin PCI_ERR_CAP_MHRE); 147315a1350SMichael S. Tsirkin } else { 148315a1350SMichael S. Tsirkin pci_set_long(dev->config + offset + PCI_ERR_CAP, 149315a1350SMichael S. Tsirkin PCI_ERR_CAP_ECRC_GENC | PCI_ERR_CAP_ECRC_CHKC); 150315a1350SMichael S. Tsirkin pci_set_long(dev->wmask + offset + PCI_ERR_CAP, 151315a1350SMichael S. Tsirkin PCI_ERR_CAP_ECRC_GENE | PCI_ERR_CAP_ECRC_CHKE); 152315a1350SMichael S. Tsirkin } 153315a1350SMichael S. Tsirkin 154315a1350SMichael S. Tsirkin switch (pcie_cap_get_type(dev)) { 155315a1350SMichael S. Tsirkin case PCI_EXP_TYPE_ROOT_PORT: 156315a1350SMichael S. Tsirkin /* this case will be set by pcie_aer_root_init() */ 157315a1350SMichael S. Tsirkin /* fallthrough */ 158315a1350SMichael S. Tsirkin case PCI_EXP_TYPE_DOWNSTREAM: 159315a1350SMichael S. Tsirkin case PCI_EXP_TYPE_UPSTREAM: 160315a1350SMichael S. Tsirkin pci_word_test_and_set_mask(dev->wmask + PCI_BRIDGE_CONTROL, 161315a1350SMichael S. Tsirkin PCI_BRIDGE_CTL_SERR); 162315a1350SMichael S. Tsirkin pci_long_test_and_set_mask(dev->w1cmask + PCI_STATUS, 163315a1350SMichael S. Tsirkin PCI_SEC_STATUS_RCV_SYSTEM_ERROR); 164315a1350SMichael S. Tsirkin break; 165315a1350SMichael S. Tsirkin default: 166315a1350SMichael S. Tsirkin /* nothing */ 167315a1350SMichael S. Tsirkin break; 168315a1350SMichael S. Tsirkin } 169315a1350SMichael S. Tsirkin return 0; 170315a1350SMichael S. Tsirkin } 171315a1350SMichael S. Tsirkin 172315a1350SMichael S. Tsirkin void pcie_aer_exit(PCIDevice *dev) 173315a1350SMichael S. Tsirkin { 174315a1350SMichael S. Tsirkin g_free(dev->exp.aer_log.log); 175315a1350SMichael S. Tsirkin } 176315a1350SMichael S. Tsirkin 177315a1350SMichael S. Tsirkin static void pcie_aer_update_uncor_status(PCIDevice *dev) 178315a1350SMichael S. Tsirkin { 179315a1350SMichael S. Tsirkin uint8_t *aer_cap = dev->config + dev->exp.aer_cap; 180315a1350SMichael S. Tsirkin PCIEAERLog *aer_log = &dev->exp.aer_log; 181315a1350SMichael S. Tsirkin 182315a1350SMichael S. Tsirkin uint16_t i; 183315a1350SMichael S. Tsirkin for (i = 0; i < aer_log->log_num; i++) { 184315a1350SMichael S. Tsirkin pci_long_test_and_set_mask(aer_cap + PCI_ERR_UNCOR_STATUS, 185315a1350SMichael S. Tsirkin dev->exp.aer_log.log[i].status); 186315a1350SMichael S. Tsirkin } 187315a1350SMichael S. Tsirkin } 188315a1350SMichael S. Tsirkin 189315a1350SMichael S. Tsirkin /* 190315a1350SMichael S. Tsirkin * return value: 191315a1350SMichael S. Tsirkin * true: error message needs to be sent up 192315a1350SMichael S. Tsirkin * false: error message is masked 193315a1350SMichael S. Tsirkin * 194315a1350SMichael S. Tsirkin * 6.2.6 Error Message Control 195315a1350SMichael S. Tsirkin * Figure 6-3 196315a1350SMichael S. Tsirkin * all pci express devices part 197315a1350SMichael S. Tsirkin */ 198315a1350SMichael S. Tsirkin static bool 199315a1350SMichael S. Tsirkin pcie_aer_msg_alldev(PCIDevice *dev, const PCIEAERMsg *msg) 200315a1350SMichael S. Tsirkin { 201315a1350SMichael S. Tsirkin if (!(pcie_aer_msg_is_uncor(msg) && 202315a1350SMichael S. Tsirkin (pci_get_word(dev->config + PCI_COMMAND) & PCI_COMMAND_SERR))) { 203315a1350SMichael S. Tsirkin return false; 204315a1350SMichael S. Tsirkin } 205315a1350SMichael S. Tsirkin 206315a1350SMichael S. Tsirkin /* Signaled System Error 207315a1350SMichael S. Tsirkin * 208315a1350SMichael S. Tsirkin * 7.5.1.1 Command register 209315a1350SMichael S. Tsirkin * Bit 8 SERR# Enable 210315a1350SMichael S. Tsirkin * 211315a1350SMichael S. Tsirkin * When Set, this bit enables reporting of Non-fatal and Fatal 212315a1350SMichael S. Tsirkin * errors detected by the Function to the Root Complex. Note that 213315a1350SMichael S. Tsirkin * errors are reported if enabled either through this bit or through 214315a1350SMichael S. Tsirkin * the PCI Express specific bits in the Device Control register (see 215315a1350SMichael S. Tsirkin * Section 7.8.4). 216315a1350SMichael S. Tsirkin */ 217315a1350SMichael S. Tsirkin pci_word_test_and_set_mask(dev->config + PCI_STATUS, 218315a1350SMichael S. Tsirkin PCI_STATUS_SIG_SYSTEM_ERROR); 219315a1350SMichael S. Tsirkin 220315a1350SMichael S. Tsirkin if (!(msg->severity & 221315a1350SMichael S. Tsirkin pci_get_word(dev->config + dev->exp.exp_cap + PCI_EXP_DEVCTL))) { 222315a1350SMichael S. Tsirkin return false; 223315a1350SMichael S. Tsirkin } 224315a1350SMichael S. Tsirkin 225315a1350SMichael S. Tsirkin /* send up error message */ 226315a1350SMichael S. Tsirkin return true; 227315a1350SMichael S. Tsirkin } 228315a1350SMichael S. Tsirkin 229315a1350SMichael S. Tsirkin /* 230315a1350SMichael S. Tsirkin * return value: 231315a1350SMichael S. Tsirkin * true: error message is sent up 232315a1350SMichael S. Tsirkin * false: error message is masked 233315a1350SMichael S. Tsirkin * 234315a1350SMichael S. Tsirkin * 6.2.6 Error Message Control 235315a1350SMichael S. Tsirkin * Figure 6-3 236315a1350SMichael S. Tsirkin * virtual pci bridge part 237315a1350SMichael S. Tsirkin */ 238315a1350SMichael S. Tsirkin static bool pcie_aer_msg_vbridge(PCIDevice *dev, const PCIEAERMsg *msg) 239315a1350SMichael S. Tsirkin { 240315a1350SMichael S. Tsirkin uint16_t bridge_control = pci_get_word(dev->config + PCI_BRIDGE_CONTROL); 241315a1350SMichael S. Tsirkin 242315a1350SMichael S. Tsirkin if (pcie_aer_msg_is_uncor(msg)) { 243315a1350SMichael S. Tsirkin /* Received System Error */ 244315a1350SMichael S. Tsirkin pci_word_test_and_set_mask(dev->config + PCI_SEC_STATUS, 245315a1350SMichael S. Tsirkin PCI_SEC_STATUS_RCV_SYSTEM_ERROR); 246315a1350SMichael S. Tsirkin } 247315a1350SMichael S. Tsirkin 248315a1350SMichael S. Tsirkin if (!(bridge_control & PCI_BRIDGE_CTL_SERR)) { 249315a1350SMichael S. Tsirkin return false; 250315a1350SMichael S. Tsirkin } 251315a1350SMichael S. Tsirkin return true; 252315a1350SMichael S. Tsirkin } 253315a1350SMichael S. Tsirkin 254315a1350SMichael S. Tsirkin void pcie_aer_root_set_vector(PCIDevice *dev, unsigned int vector) 255315a1350SMichael S. Tsirkin { 256315a1350SMichael S. Tsirkin uint8_t *aer_cap = dev->config + dev->exp.aer_cap; 257315a1350SMichael S. Tsirkin assert(vector < PCI_ERR_ROOT_IRQ_MAX); 258315a1350SMichael S. Tsirkin pci_long_test_and_clear_mask(aer_cap + PCI_ERR_ROOT_STATUS, 259315a1350SMichael S. Tsirkin PCI_ERR_ROOT_IRQ); 260315a1350SMichael S. Tsirkin pci_long_test_and_set_mask(aer_cap + PCI_ERR_ROOT_STATUS, 261315a1350SMichael S. Tsirkin vector << PCI_ERR_ROOT_IRQ_SHIFT); 262315a1350SMichael S. Tsirkin } 263315a1350SMichael S. Tsirkin 264315a1350SMichael S. Tsirkin static unsigned int pcie_aer_root_get_vector(PCIDevice *dev) 265315a1350SMichael S. Tsirkin { 266315a1350SMichael S. Tsirkin uint8_t *aer_cap = dev->config + dev->exp.aer_cap; 267315a1350SMichael S. Tsirkin uint32_t root_status = pci_get_long(aer_cap + PCI_ERR_ROOT_STATUS); 268315a1350SMichael S. Tsirkin return (root_status & PCI_ERR_ROOT_IRQ) >> PCI_ERR_ROOT_IRQ_SHIFT; 269315a1350SMichael S. Tsirkin } 270315a1350SMichael S. Tsirkin 271315a1350SMichael S. Tsirkin /* Given a status register, get corresponding bits in the command register */ 272315a1350SMichael S. Tsirkin static uint32_t pcie_aer_status_to_cmd(uint32_t status) 273315a1350SMichael S. Tsirkin { 274315a1350SMichael S. Tsirkin uint32_t cmd = 0; 275315a1350SMichael S. Tsirkin if (status & PCI_ERR_ROOT_COR_RCV) { 276315a1350SMichael S. Tsirkin cmd |= PCI_ERR_ROOT_CMD_COR_EN; 277315a1350SMichael S. Tsirkin } 278315a1350SMichael S. Tsirkin if (status & PCI_ERR_ROOT_NONFATAL_RCV) { 279315a1350SMichael S. Tsirkin cmd |= PCI_ERR_ROOT_CMD_NONFATAL_EN; 280315a1350SMichael S. Tsirkin } 281315a1350SMichael S. Tsirkin if (status & PCI_ERR_ROOT_FATAL_RCV) { 282315a1350SMichael S. Tsirkin cmd |= PCI_ERR_ROOT_CMD_FATAL_EN; 283315a1350SMichael S. Tsirkin } 284315a1350SMichael S. Tsirkin return cmd; 285315a1350SMichael S. Tsirkin } 286315a1350SMichael S. Tsirkin 287315a1350SMichael S. Tsirkin static void pcie_aer_root_notify(PCIDevice *dev) 288315a1350SMichael S. Tsirkin { 289315a1350SMichael S. Tsirkin if (msix_enabled(dev)) { 290315a1350SMichael S. Tsirkin msix_notify(dev, pcie_aer_root_get_vector(dev)); 291315a1350SMichael S. Tsirkin } else if (msi_enabled(dev)) { 292315a1350SMichael S. Tsirkin msi_notify(dev, pcie_aer_root_get_vector(dev)); 293315a1350SMichael S. Tsirkin } else { 2945a03e708SMarcel Apfelbaum pci_irq_assert(dev); 295315a1350SMichael S. Tsirkin } 296315a1350SMichael S. Tsirkin } 297315a1350SMichael S. Tsirkin 298315a1350SMichael S. Tsirkin /* 299315a1350SMichael S. Tsirkin * 6.2.6 Error Message Control 300315a1350SMichael S. Tsirkin * Figure 6-3 301315a1350SMichael S. Tsirkin * root port part 302315a1350SMichael S. Tsirkin */ 303315a1350SMichael S. Tsirkin static void pcie_aer_msg_root_port(PCIDevice *dev, const PCIEAERMsg *msg) 304315a1350SMichael S. Tsirkin { 305315a1350SMichael S. Tsirkin uint16_t cmd; 306315a1350SMichael S. Tsirkin uint8_t *aer_cap; 307315a1350SMichael S. Tsirkin uint32_t root_cmd; 308315a1350SMichael S. Tsirkin uint32_t root_status, prev_status; 309315a1350SMichael S. Tsirkin 310315a1350SMichael S. Tsirkin cmd = pci_get_word(dev->config + PCI_COMMAND); 311315a1350SMichael S. Tsirkin aer_cap = dev->config + dev->exp.aer_cap; 312315a1350SMichael S. Tsirkin root_cmd = pci_get_long(aer_cap + PCI_ERR_ROOT_COMMAND); 313315a1350SMichael S. Tsirkin prev_status = root_status = pci_get_long(aer_cap + PCI_ERR_ROOT_STATUS); 314315a1350SMichael S. Tsirkin 315315a1350SMichael S. Tsirkin if (cmd & PCI_COMMAND_SERR) { 316315a1350SMichael S. Tsirkin /* System Error. 317315a1350SMichael S. Tsirkin * 318315a1350SMichael S. Tsirkin * The way to report System Error is platform specific and 319315a1350SMichael S. Tsirkin * it isn't implemented in qemu right now. 320315a1350SMichael S. Tsirkin * So just discard the error for now. 321315a1350SMichael S. Tsirkin * OS which cares of aer would receive errors via 322315a1350SMichael S. Tsirkin * native aer mechanims, so this wouldn't matter. 323315a1350SMichael S. Tsirkin */ 324315a1350SMichael S. Tsirkin } 325315a1350SMichael S. Tsirkin 326315a1350SMichael S. Tsirkin /* Errro Message Received: Root Error Status register */ 327315a1350SMichael S. Tsirkin switch (msg->severity) { 328315a1350SMichael S. Tsirkin case PCI_ERR_ROOT_CMD_COR_EN: 329315a1350SMichael S. Tsirkin if (root_status & PCI_ERR_ROOT_COR_RCV) { 330315a1350SMichael S. Tsirkin root_status |= PCI_ERR_ROOT_MULTI_COR_RCV; 331315a1350SMichael S. Tsirkin } else { 332315a1350SMichael S. Tsirkin pci_set_word(aer_cap + PCI_ERR_ROOT_ERR_SRC + PCI_ERR_SRC_COR_OFFS, 333315a1350SMichael S. Tsirkin msg->source_id); 334315a1350SMichael S. Tsirkin } 335315a1350SMichael S. Tsirkin root_status |= PCI_ERR_ROOT_COR_RCV; 336315a1350SMichael S. Tsirkin break; 337315a1350SMichael S. Tsirkin case PCI_ERR_ROOT_CMD_NONFATAL_EN: 338315a1350SMichael S. Tsirkin root_status |= PCI_ERR_ROOT_NONFATAL_RCV; 339315a1350SMichael S. Tsirkin break; 340315a1350SMichael S. Tsirkin case PCI_ERR_ROOT_CMD_FATAL_EN: 341315a1350SMichael S. Tsirkin if (!(root_status & PCI_ERR_ROOT_UNCOR_RCV)) { 342315a1350SMichael S. Tsirkin root_status |= PCI_ERR_ROOT_FIRST_FATAL; 343315a1350SMichael S. Tsirkin } 344315a1350SMichael S. Tsirkin root_status |= PCI_ERR_ROOT_FATAL_RCV; 345315a1350SMichael S. Tsirkin break; 346315a1350SMichael S. Tsirkin default: 347315a1350SMichael S. Tsirkin abort(); 348315a1350SMichael S. Tsirkin break; 349315a1350SMichael S. Tsirkin } 350315a1350SMichael S. Tsirkin if (pcie_aer_msg_is_uncor(msg)) { 351315a1350SMichael S. Tsirkin if (root_status & PCI_ERR_ROOT_UNCOR_RCV) { 352315a1350SMichael S. Tsirkin root_status |= PCI_ERR_ROOT_MULTI_UNCOR_RCV; 353315a1350SMichael S. Tsirkin } else { 354315a1350SMichael S. Tsirkin pci_set_word(aer_cap + PCI_ERR_ROOT_ERR_SRC + 355315a1350SMichael S. Tsirkin PCI_ERR_SRC_UNCOR_OFFS, msg->source_id); 356315a1350SMichael S. Tsirkin } 357315a1350SMichael S. Tsirkin root_status |= PCI_ERR_ROOT_UNCOR_RCV; 358315a1350SMichael S. Tsirkin } 359315a1350SMichael S. Tsirkin pci_set_long(aer_cap + PCI_ERR_ROOT_STATUS, root_status); 360315a1350SMichael S. Tsirkin 361315a1350SMichael S. Tsirkin /* 6.2.4.1.2 Interrupt Generation */ 362315a1350SMichael S. Tsirkin /* All the above did was set some bits in the status register. 363315a1350SMichael S. Tsirkin * Specifically these that match message severity. 364315a1350SMichael S. Tsirkin * The below code relies on this fact. */ 365315a1350SMichael S. Tsirkin if (!(root_cmd & msg->severity) || 366315a1350SMichael S. Tsirkin (pcie_aer_status_to_cmd(prev_status) & root_cmd)) { 367315a1350SMichael S. Tsirkin /* Condition is not being set or was already true so nothing to do. */ 368315a1350SMichael S. Tsirkin return; 369315a1350SMichael S. Tsirkin } 370315a1350SMichael S. Tsirkin 371315a1350SMichael S. Tsirkin pcie_aer_root_notify(dev); 372315a1350SMichael S. Tsirkin } 373315a1350SMichael S. Tsirkin 374315a1350SMichael S. Tsirkin /* 375315a1350SMichael S. Tsirkin * 6.2.6 Error Message Control Figure 6-3 376315a1350SMichael S. Tsirkin * 377315a1350SMichael S. Tsirkin * Walk up the bus tree from the device, propagate the error message. 378315a1350SMichael S. Tsirkin */ 3798f16de18SEric Blake static void pcie_aer_msg(PCIDevice *dev, const PCIEAERMsg *msg) 380315a1350SMichael S. Tsirkin { 381315a1350SMichael S. Tsirkin uint8_t type; 382315a1350SMichael S. Tsirkin 383315a1350SMichael S. Tsirkin while (dev) { 384315a1350SMichael S. Tsirkin if (!pci_is_express(dev)) { 385315a1350SMichael S. Tsirkin /* just ignore it */ 386315a1350SMichael S. Tsirkin /* TODO: Shouldn't we set PCI_STATUS_SIG_SYSTEM_ERROR? 387315a1350SMichael S. Tsirkin * Consider e.g. a PCI bridge above a PCI Express device. */ 388315a1350SMichael S. Tsirkin return; 389315a1350SMichael S. Tsirkin } 390315a1350SMichael S. Tsirkin 391315a1350SMichael S. Tsirkin type = pcie_cap_get_type(dev); 392315a1350SMichael S. Tsirkin if ((type == PCI_EXP_TYPE_ROOT_PORT || 393315a1350SMichael S. Tsirkin type == PCI_EXP_TYPE_UPSTREAM || 394315a1350SMichael S. Tsirkin type == PCI_EXP_TYPE_DOWNSTREAM) && 395315a1350SMichael S. Tsirkin !pcie_aer_msg_vbridge(dev, msg)) { 396315a1350SMichael S. Tsirkin return; 397315a1350SMichael S. Tsirkin } 398315a1350SMichael S. Tsirkin if (!pcie_aer_msg_alldev(dev, msg)) { 399315a1350SMichael S. Tsirkin return; 400315a1350SMichael S. Tsirkin } 401315a1350SMichael S. Tsirkin if (type == PCI_EXP_TYPE_ROOT_PORT) { 402315a1350SMichael S. Tsirkin pcie_aer_msg_root_port(dev, msg); 403315a1350SMichael S. Tsirkin /* Root port can notify system itself, 404315a1350SMichael S. Tsirkin or send the error message to root complex event collector. */ 405315a1350SMichael S. Tsirkin /* 406315a1350SMichael S. Tsirkin * if root port is associated with an event collector, 407315a1350SMichael S. Tsirkin * return the root complex event collector here. 408315a1350SMichael S. Tsirkin * For now root complex event collector isn't supported. 409315a1350SMichael S. Tsirkin */ 410315a1350SMichael S. Tsirkin return; 411315a1350SMichael S. Tsirkin } 412fd56e061SDavid Gibson dev = pci_bridge_get_device(pci_get_bus(dev)); 413315a1350SMichael S. Tsirkin } 414315a1350SMichael S. Tsirkin } 415315a1350SMichael S. Tsirkin 416315a1350SMichael S. Tsirkin static void pcie_aer_update_log(PCIDevice *dev, const PCIEAERErr *err) 417315a1350SMichael S. Tsirkin { 418315a1350SMichael S. Tsirkin uint8_t *aer_cap = dev->config + dev->exp.aer_cap; 419786a4ea8SStefan Hajnoczi uint8_t first_bit = ctz32(err->status); 420315a1350SMichael S. Tsirkin uint32_t errcap = pci_get_long(aer_cap + PCI_ERR_CAP); 421315a1350SMichael S. Tsirkin int i; 422315a1350SMichael S. Tsirkin 423315a1350SMichael S. Tsirkin assert(err->status); 424315a1350SMichael S. Tsirkin assert(!(err->status & (err->status - 1))); 425315a1350SMichael S. Tsirkin 426315a1350SMichael S. Tsirkin errcap &= ~(PCI_ERR_CAP_FEP_MASK | PCI_ERR_CAP_TLP); 427315a1350SMichael S. Tsirkin errcap |= PCI_ERR_CAP_FEP(first_bit); 428315a1350SMichael S. Tsirkin 429315a1350SMichael S. Tsirkin if (err->flags & PCIE_AER_ERR_HEADER_VALID) { 430315a1350SMichael S. Tsirkin for (i = 0; i < ARRAY_SIZE(err->header); ++i) { 431315a1350SMichael S. Tsirkin /* 7.10.8 Header Log Register */ 432315a1350SMichael S. Tsirkin uint8_t *header_log = 433315a1350SMichael S. Tsirkin aer_cap + PCI_ERR_HEADER_LOG + i * sizeof err->header[0]; 4346bd194abSPeter Maydell stl_be_p(header_log, err->header[i]); 435315a1350SMichael S. Tsirkin } 436315a1350SMichael S. Tsirkin } else { 437315a1350SMichael S. Tsirkin assert(!(err->flags & PCIE_AER_ERR_TLP_PREFIX_PRESENT)); 438315a1350SMichael S. Tsirkin memset(aer_cap + PCI_ERR_HEADER_LOG, 0, PCI_ERR_HEADER_LOG_SIZE); 439315a1350SMichael S. Tsirkin } 440315a1350SMichael S. Tsirkin 441315a1350SMichael S. Tsirkin if ((err->flags & PCIE_AER_ERR_TLP_PREFIX_PRESENT) && 44277a3c1d7SChen Fan (pci_get_long(dev->config + dev->exp.exp_cap + PCI_EXP_DEVCAP2) & 443315a1350SMichael S. Tsirkin PCI_EXP_DEVCAP2_EETLPP)) { 444315a1350SMichael S. Tsirkin for (i = 0; i < ARRAY_SIZE(err->prefix); ++i) { 445315a1350SMichael S. Tsirkin /* 7.10.12 tlp prefix log register */ 446315a1350SMichael S. Tsirkin uint8_t *prefix_log = 447315a1350SMichael S. Tsirkin aer_cap + PCI_ERR_TLP_PREFIX_LOG + i * sizeof err->prefix[0]; 4486bd194abSPeter Maydell stl_be_p(prefix_log, err->prefix[i]); 449315a1350SMichael S. Tsirkin } 450315a1350SMichael S. Tsirkin errcap |= PCI_ERR_CAP_TLP; 451315a1350SMichael S. Tsirkin } else { 452315a1350SMichael S. Tsirkin memset(aer_cap + PCI_ERR_TLP_PREFIX_LOG, 0, 453315a1350SMichael S. Tsirkin PCI_ERR_TLP_PREFIX_LOG_SIZE); 454315a1350SMichael S. Tsirkin } 455315a1350SMichael S. Tsirkin pci_set_long(aer_cap + PCI_ERR_CAP, errcap); 456315a1350SMichael S. Tsirkin } 457315a1350SMichael S. Tsirkin 458315a1350SMichael S. Tsirkin static void pcie_aer_clear_log(PCIDevice *dev) 459315a1350SMichael S. Tsirkin { 460315a1350SMichael S. Tsirkin uint8_t *aer_cap = dev->config + dev->exp.aer_cap; 461315a1350SMichael S. Tsirkin 462315a1350SMichael S. Tsirkin pci_long_test_and_clear_mask(aer_cap + PCI_ERR_CAP, 463315a1350SMichael S. Tsirkin PCI_ERR_CAP_FEP_MASK | PCI_ERR_CAP_TLP); 464315a1350SMichael S. Tsirkin 465315a1350SMichael S. Tsirkin memset(aer_cap + PCI_ERR_HEADER_LOG, 0, PCI_ERR_HEADER_LOG_SIZE); 466315a1350SMichael S. Tsirkin memset(aer_cap + PCI_ERR_TLP_PREFIX_LOG, 0, PCI_ERR_TLP_PREFIX_LOG_SIZE); 467315a1350SMichael S. Tsirkin } 468315a1350SMichael S. Tsirkin 469315a1350SMichael S. Tsirkin static void pcie_aer_clear_error(PCIDevice *dev) 470315a1350SMichael S. Tsirkin { 471315a1350SMichael S. Tsirkin uint8_t *aer_cap = dev->config + dev->exp.aer_cap; 472315a1350SMichael S. Tsirkin uint32_t errcap = pci_get_long(aer_cap + PCI_ERR_CAP); 473315a1350SMichael S. Tsirkin PCIEAERLog *aer_log = &dev->exp.aer_log; 474315a1350SMichael S. Tsirkin PCIEAERErr err; 475315a1350SMichael S. Tsirkin 476315a1350SMichael S. Tsirkin if (!(errcap & PCI_ERR_CAP_MHRE) || !aer_log->log_num) { 477315a1350SMichael S. Tsirkin pcie_aer_clear_log(dev); 478315a1350SMichael S. Tsirkin return; 479315a1350SMichael S. Tsirkin } 480315a1350SMichael S. Tsirkin 481315a1350SMichael S. Tsirkin /* 482315a1350SMichael S. Tsirkin * If more errors are queued, set corresponding bits in uncorrectable 483315a1350SMichael S. Tsirkin * error status. 484315a1350SMichael S. Tsirkin * We emulate uncorrectable error status register as W1CS. 485315a1350SMichael S. Tsirkin * So set bit in uncorrectable error status here again for multiple 486315a1350SMichael S. Tsirkin * error recording support. 487315a1350SMichael S. Tsirkin * 488315a1350SMichael S. Tsirkin * 6.2.4.2 Multiple Error Handling(Advanced Error Reporting Capability) 489315a1350SMichael S. Tsirkin */ 490315a1350SMichael S. Tsirkin pcie_aer_update_uncor_status(dev); 491315a1350SMichael S. Tsirkin 492315a1350SMichael S. Tsirkin aer_log_del_err(aer_log, &err); 493315a1350SMichael S. Tsirkin pcie_aer_update_log(dev, &err); 494315a1350SMichael S. Tsirkin } 495315a1350SMichael S. Tsirkin 496315a1350SMichael S. Tsirkin static int pcie_aer_record_error(PCIDevice *dev, 497315a1350SMichael S. Tsirkin const PCIEAERErr *err) 498315a1350SMichael S. Tsirkin { 499315a1350SMichael S. Tsirkin uint8_t *aer_cap = dev->config + dev->exp.aer_cap; 500315a1350SMichael S. Tsirkin uint32_t errcap = pci_get_long(aer_cap + PCI_ERR_CAP); 501315a1350SMichael S. Tsirkin int fep = PCI_ERR_CAP_FEP(errcap); 502315a1350SMichael S. Tsirkin 503315a1350SMichael S. Tsirkin assert(err->status); 504315a1350SMichael S. Tsirkin assert(!(err->status & (err->status - 1))); 505315a1350SMichael S. Tsirkin 506315a1350SMichael S. Tsirkin if (errcap & PCI_ERR_CAP_MHRE && 507315a1350SMichael S. Tsirkin (pci_get_long(aer_cap + PCI_ERR_UNCOR_STATUS) & (1U << fep))) { 508315a1350SMichael S. Tsirkin /* Not first error. queue error */ 509315a1350SMichael S. Tsirkin if (aer_log_add_err(&dev->exp.aer_log, err) < 0) { 510315a1350SMichael S. Tsirkin /* overflow */ 511315a1350SMichael S. Tsirkin return -1; 512315a1350SMichael S. Tsirkin } 513315a1350SMichael S. Tsirkin return 0; 514315a1350SMichael S. Tsirkin } 515315a1350SMichael S. Tsirkin 516315a1350SMichael S. Tsirkin pcie_aer_update_log(dev, err); 517315a1350SMichael S. Tsirkin return 0; 518315a1350SMichael S. Tsirkin } 519315a1350SMichael S. Tsirkin 520315a1350SMichael S. Tsirkin typedef struct PCIEAERInject { 521315a1350SMichael S. Tsirkin PCIDevice *dev; 522315a1350SMichael S. Tsirkin uint8_t *aer_cap; 523315a1350SMichael S. Tsirkin const PCIEAERErr *err; 524315a1350SMichael S. Tsirkin uint16_t devctl; 525315a1350SMichael S. Tsirkin uint16_t devsta; 526315a1350SMichael S. Tsirkin uint32_t error_status; 527315a1350SMichael S. Tsirkin bool unsupported_request; 528315a1350SMichael S. Tsirkin bool log_overflow; 529315a1350SMichael S. Tsirkin PCIEAERMsg msg; 530315a1350SMichael S. Tsirkin } PCIEAERInject; 531315a1350SMichael S. Tsirkin 532315a1350SMichael S. Tsirkin static bool pcie_aer_inject_cor_error(PCIEAERInject *inj, 533315a1350SMichael S. Tsirkin uint32_t uncor_status, 534315a1350SMichael S. Tsirkin bool is_advisory_nonfatal) 535315a1350SMichael S. Tsirkin { 536315a1350SMichael S. Tsirkin PCIDevice *dev = inj->dev; 537315a1350SMichael S. Tsirkin 538315a1350SMichael S. Tsirkin inj->devsta |= PCI_EXP_DEVSTA_CED; 539315a1350SMichael S. Tsirkin if (inj->unsupported_request) { 540315a1350SMichael S. Tsirkin inj->devsta |= PCI_EXP_DEVSTA_URD; 541315a1350SMichael S. Tsirkin } 542315a1350SMichael S. Tsirkin pci_set_word(dev->config + dev->exp.exp_cap + PCI_EXP_DEVSTA, inj->devsta); 543315a1350SMichael S. Tsirkin 544315a1350SMichael S. Tsirkin if (inj->aer_cap) { 545315a1350SMichael S. Tsirkin uint32_t mask; 546315a1350SMichael S. Tsirkin pci_long_test_and_set_mask(inj->aer_cap + PCI_ERR_COR_STATUS, 547315a1350SMichael S. Tsirkin inj->error_status); 548315a1350SMichael S. Tsirkin mask = pci_get_long(inj->aer_cap + PCI_ERR_COR_MASK); 549315a1350SMichael S. Tsirkin if (mask & inj->error_status) { 550315a1350SMichael S. Tsirkin return false; 551315a1350SMichael S. Tsirkin } 552315a1350SMichael S. Tsirkin if (is_advisory_nonfatal) { 553315a1350SMichael S. Tsirkin uint32_t uncor_mask = 554315a1350SMichael S. Tsirkin pci_get_long(inj->aer_cap + PCI_ERR_UNCOR_MASK); 555315a1350SMichael S. Tsirkin if (!(uncor_mask & uncor_status)) { 556315a1350SMichael S. Tsirkin inj->log_overflow = !!pcie_aer_record_error(dev, inj->err); 557315a1350SMichael S. Tsirkin } 558315a1350SMichael S. Tsirkin pci_long_test_and_set_mask(inj->aer_cap + PCI_ERR_UNCOR_STATUS, 559315a1350SMichael S. Tsirkin uncor_status); 560315a1350SMichael S. Tsirkin } 561315a1350SMichael S. Tsirkin } 562315a1350SMichael S. Tsirkin 563315a1350SMichael S. Tsirkin if (inj->unsupported_request && !(inj->devctl & PCI_EXP_DEVCTL_URRE)) { 564315a1350SMichael S. Tsirkin return false; 565315a1350SMichael S. Tsirkin } 566315a1350SMichael S. Tsirkin if (!(inj->devctl & PCI_EXP_DEVCTL_CERE)) { 567315a1350SMichael S. Tsirkin return false; 568315a1350SMichael S. Tsirkin } 569315a1350SMichael S. Tsirkin 570315a1350SMichael S. Tsirkin inj->msg.severity = PCI_ERR_ROOT_CMD_COR_EN; 571315a1350SMichael S. Tsirkin return true; 572315a1350SMichael S. Tsirkin } 573315a1350SMichael S. Tsirkin 574315a1350SMichael S. Tsirkin static bool pcie_aer_inject_uncor_error(PCIEAERInject *inj, bool is_fatal) 575315a1350SMichael S. Tsirkin { 576315a1350SMichael S. Tsirkin PCIDevice *dev = inj->dev; 577315a1350SMichael S. Tsirkin uint16_t cmd; 578315a1350SMichael S. Tsirkin 579315a1350SMichael S. Tsirkin if (is_fatal) { 580315a1350SMichael S. Tsirkin inj->devsta |= PCI_EXP_DEVSTA_FED; 581315a1350SMichael S. Tsirkin } else { 582315a1350SMichael S. Tsirkin inj->devsta |= PCI_EXP_DEVSTA_NFED; 583315a1350SMichael S. Tsirkin } 584315a1350SMichael S. Tsirkin if (inj->unsupported_request) { 585315a1350SMichael S. Tsirkin inj->devsta |= PCI_EXP_DEVSTA_URD; 586315a1350SMichael S. Tsirkin } 587315a1350SMichael S. Tsirkin pci_set_long(dev->config + dev->exp.exp_cap + PCI_EXP_DEVSTA, inj->devsta); 588315a1350SMichael S. Tsirkin 589315a1350SMichael S. Tsirkin if (inj->aer_cap) { 590315a1350SMichael S. Tsirkin uint32_t mask = pci_get_long(inj->aer_cap + PCI_ERR_UNCOR_MASK); 591315a1350SMichael S. Tsirkin if (mask & inj->error_status) { 592315a1350SMichael S. Tsirkin pci_long_test_and_set_mask(inj->aer_cap + PCI_ERR_UNCOR_STATUS, 593315a1350SMichael S. Tsirkin inj->error_status); 594315a1350SMichael S. Tsirkin return false; 595315a1350SMichael S. Tsirkin } 596315a1350SMichael S. Tsirkin 597315a1350SMichael S. Tsirkin inj->log_overflow = !!pcie_aer_record_error(dev, inj->err); 598315a1350SMichael S. Tsirkin pci_long_test_and_set_mask(inj->aer_cap + PCI_ERR_UNCOR_STATUS, 599315a1350SMichael S. Tsirkin inj->error_status); 600315a1350SMichael S. Tsirkin } 601315a1350SMichael S. Tsirkin 602315a1350SMichael S. Tsirkin cmd = pci_get_word(dev->config + PCI_COMMAND); 603315a1350SMichael S. Tsirkin if (inj->unsupported_request && 604315a1350SMichael S. Tsirkin !(inj->devctl & PCI_EXP_DEVCTL_URRE) && !(cmd & PCI_COMMAND_SERR)) { 605315a1350SMichael S. Tsirkin return false; 606315a1350SMichael S. Tsirkin } 607315a1350SMichael S. Tsirkin if (is_fatal) { 608315a1350SMichael S. Tsirkin if (!((cmd & PCI_COMMAND_SERR) || 609315a1350SMichael S. Tsirkin (inj->devctl & PCI_EXP_DEVCTL_FERE))) { 610315a1350SMichael S. Tsirkin return false; 611315a1350SMichael S. Tsirkin } 612315a1350SMichael S. Tsirkin inj->msg.severity = PCI_ERR_ROOT_CMD_FATAL_EN; 613315a1350SMichael S. Tsirkin } else { 614315a1350SMichael S. Tsirkin if (!((cmd & PCI_COMMAND_SERR) || 615315a1350SMichael S. Tsirkin (inj->devctl & PCI_EXP_DEVCTL_NFERE))) { 616315a1350SMichael S. Tsirkin return false; 617315a1350SMichael S. Tsirkin } 618315a1350SMichael S. Tsirkin inj->msg.severity = PCI_ERR_ROOT_CMD_NONFATAL_EN; 619315a1350SMichael S. Tsirkin } 620315a1350SMichael S. Tsirkin return true; 621315a1350SMichael S. Tsirkin } 622315a1350SMichael S. Tsirkin 623315a1350SMichael S. Tsirkin /* 624315a1350SMichael S. Tsirkin * non-Function specific error must be recorded in all functions. 625315a1350SMichael S. Tsirkin * It is the responsibility of the caller of this function. 626315a1350SMichael S. Tsirkin * It is also caller's responsibility to determine which function should 627b01738c2SChen Fan * report the error. 628315a1350SMichael S. Tsirkin * 629315a1350SMichael S. Tsirkin * 6.2.4 Error Logging 630b01738c2SChen Fan * 6.2.5 Sequence of Device Error Signaling and Logging Operations 631ce394947SMichael S. Tsirkin * Figure 6-2: Flowchart Showing Sequence of Device Error Signaling and Logging 632315a1350SMichael S. Tsirkin * Operations 633315a1350SMichael S. Tsirkin */ 6348f16de18SEric Blake static int pcie_aer_inject_error(PCIDevice *dev, const PCIEAERErr *err) 635315a1350SMichael S. Tsirkin { 636315a1350SMichael S. Tsirkin uint8_t *aer_cap = NULL; 637315a1350SMichael S. Tsirkin uint16_t devctl = 0; 638315a1350SMichael S. Tsirkin uint16_t devsta = 0; 639315a1350SMichael S. Tsirkin uint32_t error_status = err->status; 640315a1350SMichael S. Tsirkin PCIEAERInject inj; 641315a1350SMichael S. Tsirkin 642315a1350SMichael S. Tsirkin if (!pci_is_express(dev)) { 643315a1350SMichael S. Tsirkin return -ENOSYS; 644315a1350SMichael S. Tsirkin } 645315a1350SMichael S. Tsirkin 646315a1350SMichael S. Tsirkin if (err->flags & PCIE_AER_ERR_IS_CORRECTABLE) { 647315a1350SMichael S. Tsirkin error_status &= PCI_ERR_COR_SUPPORTED; 648315a1350SMichael S. Tsirkin } else { 649315a1350SMichael S. Tsirkin error_status &= PCI_ERR_UNC_SUPPORTED; 650315a1350SMichael S. Tsirkin } 651315a1350SMichael S. Tsirkin 652315a1350SMichael S. Tsirkin /* invalid status bit. one and only one bit must be set */ 653315a1350SMichael S. Tsirkin if (!error_status || (error_status & (error_status - 1))) { 654315a1350SMichael S. Tsirkin return -EINVAL; 655315a1350SMichael S. Tsirkin } 656315a1350SMichael S. Tsirkin 657315a1350SMichael S. Tsirkin if (dev->exp.aer_cap) { 658315a1350SMichael S. Tsirkin uint8_t *exp_cap = dev->config + dev->exp.exp_cap; 659315a1350SMichael S. Tsirkin aer_cap = dev->config + dev->exp.aer_cap; 660315a1350SMichael S. Tsirkin devctl = pci_get_long(exp_cap + PCI_EXP_DEVCTL); 661315a1350SMichael S. Tsirkin devsta = pci_get_long(exp_cap + PCI_EXP_DEVSTA); 662315a1350SMichael S. Tsirkin } 663315a1350SMichael S. Tsirkin 664315a1350SMichael S. Tsirkin inj.dev = dev; 665315a1350SMichael S. Tsirkin inj.aer_cap = aer_cap; 666315a1350SMichael S. Tsirkin inj.err = err; 667315a1350SMichael S. Tsirkin inj.devctl = devctl; 668315a1350SMichael S. Tsirkin inj.devsta = devsta; 669315a1350SMichael S. Tsirkin inj.error_status = error_status; 670315a1350SMichael S. Tsirkin inj.unsupported_request = !(err->flags & PCIE_AER_ERR_IS_CORRECTABLE) && 671315a1350SMichael S. Tsirkin err->status == PCI_ERR_UNC_UNSUP; 672315a1350SMichael S. Tsirkin inj.log_overflow = false; 673315a1350SMichael S. Tsirkin 674315a1350SMichael S. Tsirkin if (err->flags & PCIE_AER_ERR_IS_CORRECTABLE) { 675315a1350SMichael S. Tsirkin if (!pcie_aer_inject_cor_error(&inj, 0, false)) { 676315a1350SMichael S. Tsirkin return 0; 677315a1350SMichael S. Tsirkin } 678315a1350SMichael S. Tsirkin } else { 679315a1350SMichael S. Tsirkin bool is_fatal = 680315a1350SMichael S. Tsirkin pcie_aer_uncor_default_severity(error_status) == 681315a1350SMichael S. Tsirkin PCI_ERR_ROOT_CMD_FATAL_EN; 682315a1350SMichael S. Tsirkin if (aer_cap) { 683315a1350SMichael S. Tsirkin is_fatal = 684315a1350SMichael S. Tsirkin error_status & pci_get_long(aer_cap + PCI_ERR_UNCOR_SEVER); 685315a1350SMichael S. Tsirkin } 686315a1350SMichael S. Tsirkin if (!is_fatal && (err->flags & PCIE_AER_ERR_MAYBE_ADVISORY)) { 687315a1350SMichael S. Tsirkin inj.error_status = PCI_ERR_COR_ADV_NONFATAL; 688315a1350SMichael S. Tsirkin if (!pcie_aer_inject_cor_error(&inj, error_status, true)) { 689315a1350SMichael S. Tsirkin return 0; 690315a1350SMichael S. Tsirkin } 691315a1350SMichael S. Tsirkin } else { 692315a1350SMichael S. Tsirkin if (!pcie_aer_inject_uncor_error(&inj, is_fatal)) { 693315a1350SMichael S. Tsirkin return 0; 694315a1350SMichael S. Tsirkin } 695315a1350SMichael S. Tsirkin } 696315a1350SMichael S. Tsirkin } 697315a1350SMichael S. Tsirkin 698315a1350SMichael S. Tsirkin /* send up error message */ 699315a1350SMichael S. Tsirkin inj.msg.source_id = err->source_id; 700315a1350SMichael S. Tsirkin pcie_aer_msg(dev, &inj.msg); 701315a1350SMichael S. Tsirkin 702315a1350SMichael S. Tsirkin if (inj.log_overflow) { 703315a1350SMichael S. Tsirkin PCIEAERErr header_log_overflow = { 704315a1350SMichael S. Tsirkin .status = PCI_ERR_COR_HL_OVERFLOW, 705315a1350SMichael S. Tsirkin .flags = PCIE_AER_ERR_IS_CORRECTABLE, 706315a1350SMichael S. Tsirkin }; 707315a1350SMichael S. Tsirkin int ret = pcie_aer_inject_error(dev, &header_log_overflow); 708315a1350SMichael S. Tsirkin assert(!ret); 709315a1350SMichael S. Tsirkin } 710315a1350SMichael S. Tsirkin return 0; 711315a1350SMichael S. Tsirkin } 712315a1350SMichael S. Tsirkin 713315a1350SMichael S. Tsirkin void pcie_aer_write_config(PCIDevice *dev, 714315a1350SMichael S. Tsirkin uint32_t addr, uint32_t val, int len) 715315a1350SMichael S. Tsirkin { 716315a1350SMichael S. Tsirkin uint8_t *aer_cap = dev->config + dev->exp.aer_cap; 717315a1350SMichael S. Tsirkin uint32_t errcap = pci_get_long(aer_cap + PCI_ERR_CAP); 718315a1350SMichael S. Tsirkin uint32_t first_error = 1U << PCI_ERR_CAP_FEP(errcap); 719315a1350SMichael S. Tsirkin uint32_t uncorsta = pci_get_long(aer_cap + PCI_ERR_UNCOR_STATUS); 720315a1350SMichael S. Tsirkin 721315a1350SMichael S. Tsirkin /* uncorrectable error */ 722315a1350SMichael S. Tsirkin if (!(uncorsta & first_error)) { 723315a1350SMichael S. Tsirkin /* the bit that corresponds to the first error is cleared */ 724315a1350SMichael S. Tsirkin pcie_aer_clear_error(dev); 725315a1350SMichael S. Tsirkin } else if (errcap & PCI_ERR_CAP_MHRE) { 726315a1350SMichael S. Tsirkin /* When PCI_ERR_CAP_MHRE is enabled and the first error isn't cleared 727315a1350SMichael S. Tsirkin * nothing should happen. So we have to revert the modification to 728315a1350SMichael S. Tsirkin * the register. 729315a1350SMichael S. Tsirkin */ 730315a1350SMichael S. Tsirkin pcie_aer_update_uncor_status(dev); 731315a1350SMichael S. Tsirkin } else { 732315a1350SMichael S. Tsirkin /* capability & control 733315a1350SMichael S. Tsirkin * PCI_ERR_CAP_MHRE might be cleared, so clear of header log. 734315a1350SMichael S. Tsirkin */ 735315a1350SMichael S. Tsirkin aer_log_clear_all_err(&dev->exp.aer_log); 736315a1350SMichael S. Tsirkin } 737315a1350SMichael S. Tsirkin } 738315a1350SMichael S. Tsirkin 739315a1350SMichael S. Tsirkin void pcie_aer_root_init(PCIDevice *dev) 740315a1350SMichael S. Tsirkin { 741315a1350SMichael S. Tsirkin uint16_t pos = dev->exp.aer_cap; 742315a1350SMichael S. Tsirkin 743315a1350SMichael S. Tsirkin pci_set_long(dev->wmask + pos + PCI_ERR_ROOT_COMMAND, 744315a1350SMichael S. Tsirkin PCI_ERR_ROOT_CMD_EN_MASK); 745315a1350SMichael S. Tsirkin pci_set_long(dev->w1cmask + pos + PCI_ERR_ROOT_STATUS, 746315a1350SMichael S. Tsirkin PCI_ERR_ROOT_STATUS_REPORT_MASK); 747315a1350SMichael S. Tsirkin /* PCI_ERR_ROOT_IRQ is RO but devices change it using a 748315a1350SMichael S. Tsirkin * device-specific method. 749315a1350SMichael S. Tsirkin */ 750315a1350SMichael S. Tsirkin pci_set_long(dev->cmask + pos + PCI_ERR_ROOT_STATUS, 751315a1350SMichael S. Tsirkin ~PCI_ERR_ROOT_IRQ); 752315a1350SMichael S. Tsirkin } 753315a1350SMichael S. Tsirkin 754315a1350SMichael S. Tsirkin void pcie_aer_root_reset(PCIDevice *dev) 755315a1350SMichael S. Tsirkin { 756315a1350SMichael S. Tsirkin uint8_t* aer_cap = dev->config + dev->exp.aer_cap; 757315a1350SMichael S. Tsirkin 758315a1350SMichael S. Tsirkin pci_set_long(aer_cap + PCI_ERR_ROOT_COMMAND, 0); 759315a1350SMichael S. Tsirkin 760315a1350SMichael S. Tsirkin /* 761315a1350SMichael S. Tsirkin * Advanced Error Interrupt Message Number in Root Error Status Register 762315a1350SMichael S. Tsirkin * must be updated by chip dependent code because it's chip dependent 763315a1350SMichael S. Tsirkin * which number is used. 764315a1350SMichael S. Tsirkin */ 765315a1350SMichael S. Tsirkin } 766315a1350SMichael S. Tsirkin 767315a1350SMichael S. Tsirkin void pcie_aer_root_write_config(PCIDevice *dev, 768315a1350SMichael S. Tsirkin uint32_t addr, uint32_t val, int len, 769315a1350SMichael S. Tsirkin uint32_t root_cmd_prev) 770315a1350SMichael S. Tsirkin { 771315a1350SMichael S. Tsirkin uint8_t *aer_cap = dev->config + dev->exp.aer_cap; 772315a1350SMichael S. Tsirkin uint32_t root_status = pci_get_long(aer_cap + PCI_ERR_ROOT_STATUS); 773315a1350SMichael S. Tsirkin uint32_t enabled_cmd = pcie_aer_status_to_cmd(root_status); 774315a1350SMichael S. Tsirkin uint32_t root_cmd = pci_get_long(aer_cap + PCI_ERR_ROOT_COMMAND); 775315a1350SMichael S. Tsirkin /* 6.2.4.1.2 Interrupt Generation */ 776315a1350SMichael S. Tsirkin if (!msix_enabled(dev) && !msi_enabled(dev)) { 777*20766514SFrederic Barrat if (pci_intx(dev) != -1) { 7785a03e708SMarcel Apfelbaum pci_set_irq(dev, !!(root_cmd & enabled_cmd)); 779*20766514SFrederic Barrat } 780315a1350SMichael S. Tsirkin return; 781315a1350SMichael S. Tsirkin } 782315a1350SMichael S. Tsirkin 783315a1350SMichael S. Tsirkin if ((root_cmd_prev & enabled_cmd) || !(root_cmd & enabled_cmd)) { 784315a1350SMichael S. Tsirkin /* Send MSI on transition from false to true. */ 785315a1350SMichael S. Tsirkin return; 786315a1350SMichael S. Tsirkin } 787315a1350SMichael S. Tsirkin 788315a1350SMichael S. Tsirkin pcie_aer_root_notify(dev); 789315a1350SMichael S. Tsirkin } 790315a1350SMichael S. Tsirkin 791315a1350SMichael S. Tsirkin static const VMStateDescription vmstate_pcie_aer_err = { 792315a1350SMichael S. Tsirkin .name = "PCIE_AER_ERROR", 793315a1350SMichael S. Tsirkin .version_id = 1, 794315a1350SMichael S. Tsirkin .minimum_version_id = 1, 795315a1350SMichael S. Tsirkin .fields = (VMStateField[]) { 796315a1350SMichael S. Tsirkin VMSTATE_UINT32(status, PCIEAERErr), 797315a1350SMichael S. Tsirkin VMSTATE_UINT16(source_id, PCIEAERErr), 798315a1350SMichael S. Tsirkin VMSTATE_UINT16(flags, PCIEAERErr), 799315a1350SMichael S. Tsirkin VMSTATE_UINT32_ARRAY(header, PCIEAERErr, 4), 800315a1350SMichael S. Tsirkin VMSTATE_UINT32_ARRAY(prefix, PCIEAERErr, 4), 801315a1350SMichael S. Tsirkin VMSTATE_END_OF_LIST() 802315a1350SMichael S. Tsirkin } 803315a1350SMichael S. Tsirkin }; 804315a1350SMichael S. Tsirkin 8055f691ff9SMichael S. Tsirkin static bool pcie_aer_state_log_num_valid(void *opaque, int version_id) 8065f691ff9SMichael S. Tsirkin { 8075f691ff9SMichael S. Tsirkin PCIEAERLog *s = opaque; 8085f691ff9SMichael S. Tsirkin 8095f691ff9SMichael S. Tsirkin return s->log_num <= s->log_max; 8105f691ff9SMichael S. Tsirkin } 8115f691ff9SMichael S. Tsirkin 812315a1350SMichael S. Tsirkin const VMStateDescription vmstate_pcie_aer_log = { 813315a1350SMichael S. Tsirkin .name = "PCIE_AER_ERROR_LOG", 814315a1350SMichael S. Tsirkin .version_id = 1, 815315a1350SMichael S. Tsirkin .minimum_version_id = 1, 816315a1350SMichael S. Tsirkin .fields = (VMStateField[]) { 817315a1350SMichael S. Tsirkin VMSTATE_UINT16(log_num, PCIEAERLog), 818d2164ad3SHalil Pasic VMSTATE_UINT16_EQUAL(log_max, PCIEAERLog, NULL), 8195f691ff9SMichael S. Tsirkin VMSTATE_VALIDATE("log_num <= log_max", pcie_aer_state_log_num_valid), 820315a1350SMichael S. Tsirkin VMSTATE_STRUCT_VARRAY_POINTER_UINT16(log, PCIEAERLog, log_num, 821315a1350SMichael S. Tsirkin vmstate_pcie_aer_err, PCIEAERErr), 822315a1350SMichael S. Tsirkin VMSTATE_END_OF_LIST() 823315a1350SMichael S. Tsirkin } 824315a1350SMichael S. Tsirkin }; 825315a1350SMichael S. Tsirkin 826315a1350SMichael S. Tsirkin typedef struct PCIEAERErrorName { 827315a1350SMichael S. Tsirkin const char *name; 828315a1350SMichael S. Tsirkin uint32_t val; 829315a1350SMichael S. Tsirkin bool correctable; 830315a1350SMichael S. Tsirkin } PCIEAERErrorName; 831315a1350SMichael S. Tsirkin 832315a1350SMichael S. Tsirkin /* 833315a1350SMichael S. Tsirkin * AER error name -> value conversion table 834315a1350SMichael S. Tsirkin * This naming scheme is same to linux aer-injection tool. 835315a1350SMichael S. Tsirkin */ 836315a1350SMichael S. Tsirkin static const struct PCIEAERErrorName pcie_aer_error_list[] = { 837315a1350SMichael S. Tsirkin { 838315a1350SMichael S. Tsirkin .name = "DLP", 839315a1350SMichael S. Tsirkin .val = PCI_ERR_UNC_DLP, 840315a1350SMichael S. Tsirkin .correctable = false, 841315a1350SMichael S. Tsirkin }, { 842315a1350SMichael S. Tsirkin .name = "SDN", 843315a1350SMichael S. Tsirkin .val = PCI_ERR_UNC_SDN, 844315a1350SMichael S. Tsirkin .correctable = false, 845315a1350SMichael S. Tsirkin }, { 846315a1350SMichael S. Tsirkin .name = "POISON_TLP", 847315a1350SMichael S. Tsirkin .val = PCI_ERR_UNC_POISON_TLP, 848315a1350SMichael S. Tsirkin .correctable = false, 849315a1350SMichael S. Tsirkin }, { 850315a1350SMichael S. Tsirkin .name = "FCP", 851315a1350SMichael S. Tsirkin .val = PCI_ERR_UNC_FCP, 852315a1350SMichael S. Tsirkin .correctable = false, 853315a1350SMichael S. Tsirkin }, { 854315a1350SMichael S. Tsirkin .name = "COMP_TIME", 855315a1350SMichael S. Tsirkin .val = PCI_ERR_UNC_COMP_TIME, 856315a1350SMichael S. Tsirkin .correctable = false, 857315a1350SMichael S. Tsirkin }, { 858315a1350SMichael S. Tsirkin .name = "COMP_ABORT", 859315a1350SMichael S. Tsirkin .val = PCI_ERR_UNC_COMP_ABORT, 860315a1350SMichael S. Tsirkin .correctable = false, 861315a1350SMichael S. Tsirkin }, { 862315a1350SMichael S. Tsirkin .name = "UNX_COMP", 863315a1350SMichael S. Tsirkin .val = PCI_ERR_UNC_UNX_COMP, 864315a1350SMichael S. Tsirkin .correctable = false, 865315a1350SMichael S. Tsirkin }, { 866315a1350SMichael S. Tsirkin .name = "RX_OVER", 867315a1350SMichael S. Tsirkin .val = PCI_ERR_UNC_RX_OVER, 868315a1350SMichael S. Tsirkin .correctable = false, 869315a1350SMichael S. Tsirkin }, { 870315a1350SMichael S. Tsirkin .name = "MALF_TLP", 871315a1350SMichael S. Tsirkin .val = PCI_ERR_UNC_MALF_TLP, 872315a1350SMichael S. Tsirkin .correctable = false, 873315a1350SMichael S. Tsirkin }, { 874315a1350SMichael S. Tsirkin .name = "ECRC", 875315a1350SMichael S. Tsirkin .val = PCI_ERR_UNC_ECRC, 876315a1350SMichael S. Tsirkin .correctable = false, 877315a1350SMichael S. Tsirkin }, { 878315a1350SMichael S. Tsirkin .name = "UNSUP", 879315a1350SMichael S. Tsirkin .val = PCI_ERR_UNC_UNSUP, 880315a1350SMichael S. Tsirkin .correctable = false, 881315a1350SMichael S. Tsirkin }, { 882315a1350SMichael S. Tsirkin .name = "ACSV", 883315a1350SMichael S. Tsirkin .val = PCI_ERR_UNC_ACSV, 884315a1350SMichael S. Tsirkin .correctable = false, 885315a1350SMichael S. Tsirkin }, { 886315a1350SMichael S. Tsirkin .name = "INTN", 887315a1350SMichael S. Tsirkin .val = PCI_ERR_UNC_INTN, 888315a1350SMichael S. Tsirkin .correctable = false, 889315a1350SMichael S. Tsirkin }, { 890315a1350SMichael S. Tsirkin .name = "MCBTLP", 891315a1350SMichael S. Tsirkin .val = PCI_ERR_UNC_MCBTLP, 892315a1350SMichael S. Tsirkin .correctable = false, 893315a1350SMichael S. Tsirkin }, { 894315a1350SMichael S. Tsirkin .name = "ATOP_EBLOCKED", 895315a1350SMichael S. Tsirkin .val = PCI_ERR_UNC_ATOP_EBLOCKED, 896315a1350SMichael S. Tsirkin .correctable = false, 897315a1350SMichael S. Tsirkin }, { 898315a1350SMichael S. Tsirkin .name = "TLP_PRF_BLOCKED", 899315a1350SMichael S. Tsirkin .val = PCI_ERR_UNC_TLP_PRF_BLOCKED, 900315a1350SMichael S. Tsirkin .correctable = false, 901315a1350SMichael S. Tsirkin }, { 902315a1350SMichael S. Tsirkin .name = "RCVR", 903315a1350SMichael S. Tsirkin .val = PCI_ERR_COR_RCVR, 904315a1350SMichael S. Tsirkin .correctable = true, 905315a1350SMichael S. Tsirkin }, { 906315a1350SMichael S. Tsirkin .name = "BAD_TLP", 907315a1350SMichael S. Tsirkin .val = PCI_ERR_COR_BAD_TLP, 908315a1350SMichael S. Tsirkin .correctable = true, 909315a1350SMichael S. Tsirkin }, { 910315a1350SMichael S. Tsirkin .name = "BAD_DLLP", 911315a1350SMichael S. Tsirkin .val = PCI_ERR_COR_BAD_DLLP, 912315a1350SMichael S. Tsirkin .correctable = true, 913315a1350SMichael S. Tsirkin }, { 914315a1350SMichael S. Tsirkin .name = "REP_ROLL", 915315a1350SMichael S. Tsirkin .val = PCI_ERR_COR_REP_ROLL, 916315a1350SMichael S. Tsirkin .correctable = true, 917315a1350SMichael S. Tsirkin }, { 918315a1350SMichael S. Tsirkin .name = "REP_TIMER", 919315a1350SMichael S. Tsirkin .val = PCI_ERR_COR_REP_TIMER, 920315a1350SMichael S. Tsirkin .correctable = true, 921315a1350SMichael S. Tsirkin }, { 922315a1350SMichael S. Tsirkin .name = "ADV_NONFATAL", 923315a1350SMichael S. Tsirkin .val = PCI_ERR_COR_ADV_NONFATAL, 924315a1350SMichael S. Tsirkin .correctable = true, 925315a1350SMichael S. Tsirkin }, { 926315a1350SMichael S. Tsirkin .name = "INTERNAL", 927315a1350SMichael S. Tsirkin .val = PCI_ERR_COR_INTERNAL, 928315a1350SMichael S. Tsirkin .correctable = true, 929315a1350SMichael S. Tsirkin }, { 930315a1350SMichael S. Tsirkin .name = "HL_OVERFLOW", 931315a1350SMichael S. Tsirkin .val = PCI_ERR_COR_HL_OVERFLOW, 932315a1350SMichael S. Tsirkin .correctable = true, 933315a1350SMichael S. Tsirkin }, 934315a1350SMichael S. Tsirkin }; 935315a1350SMichael S. Tsirkin 936315a1350SMichael S. Tsirkin static int pcie_aer_parse_error_string(const char *error_name, 937315a1350SMichael S. Tsirkin uint32_t *status, bool *correctable) 938315a1350SMichael S. Tsirkin { 939315a1350SMichael S. Tsirkin int i; 940315a1350SMichael S. Tsirkin 941315a1350SMichael S. Tsirkin for (i = 0; i < ARRAY_SIZE(pcie_aer_error_list); i++) { 942315a1350SMichael S. Tsirkin const PCIEAERErrorName *e = &pcie_aer_error_list[i]; 943315a1350SMichael S. Tsirkin if (strcmp(error_name, e->name)) { 944315a1350SMichael S. Tsirkin continue; 945315a1350SMichael S. Tsirkin } 946315a1350SMichael S. Tsirkin 947315a1350SMichael S. Tsirkin *status = e->val; 948315a1350SMichael S. Tsirkin *correctable = e->correctable; 949315a1350SMichael S. Tsirkin return 0; 950315a1350SMichael S. Tsirkin } 951315a1350SMichael S. Tsirkin return -EINVAL; 952315a1350SMichael S. Tsirkin } 953315a1350SMichael S. Tsirkin 9549edd5338SEric Blake /* 9559edd5338SEric Blake * Inject an error described by @qdict. 9569edd5338SEric Blake * On success, set @details to show where error was sent. 9579edd5338SEric Blake * Return negative errno if injection failed and a message was emitted. 9589edd5338SEric Blake */ 95904e00c92SMarkus Armbruster static int do_pcie_aer_inject_error(Monitor *mon, 9609edd5338SEric Blake const QDict *qdict, 9619edd5338SEric Blake PCIEErrorDetails *details) 962315a1350SMichael S. Tsirkin { 963315a1350SMichael S. Tsirkin const char *id = qdict_get_str(qdict, "id"); 964315a1350SMichael S. Tsirkin const char *error_name; 965315a1350SMichael S. Tsirkin uint32_t error_status; 966315a1350SMichael S. Tsirkin bool correctable; 967315a1350SMichael S. Tsirkin PCIDevice *dev; 968315a1350SMichael S. Tsirkin PCIEAERErr err; 969315a1350SMichael S. Tsirkin int ret; 970315a1350SMichael S. Tsirkin 971315a1350SMichael S. Tsirkin ret = pci_qdev_find_device(id, &dev); 972315a1350SMichael S. Tsirkin if (ret < 0) { 973315a1350SMichael S. Tsirkin monitor_printf(mon, 974315a1350SMichael S. Tsirkin "id or pci device path is invalid or device not " 975315a1350SMichael S. Tsirkin "found. %s\n", id); 976315a1350SMichael S. Tsirkin return ret; 977315a1350SMichael S. Tsirkin } 978315a1350SMichael S. Tsirkin if (!pci_is_express(dev)) { 979315a1350SMichael S. Tsirkin monitor_printf(mon, "the device doesn't support pci express. %s\n", 980315a1350SMichael S. Tsirkin id); 981315a1350SMichael S. Tsirkin return -ENOSYS; 982315a1350SMichael S. Tsirkin } 983315a1350SMichael S. Tsirkin 984315a1350SMichael S. Tsirkin error_name = qdict_get_str(qdict, "error_status"); 985315a1350SMichael S. Tsirkin if (pcie_aer_parse_error_string(error_name, &error_status, &correctable)) { 986315a1350SMichael S. Tsirkin char *e = NULL; 987315a1350SMichael S. Tsirkin error_status = strtoul(error_name, &e, 0); 98834acbc95SEric Blake correctable = qdict_get_try_bool(qdict, "correctable", false); 989315a1350SMichael S. Tsirkin if (!e || *e != '\0') { 990315a1350SMichael S. Tsirkin monitor_printf(mon, "invalid error status value. \"%s\"", 991315a1350SMichael S. Tsirkin error_name); 992315a1350SMichael S. Tsirkin return -EINVAL; 993315a1350SMichael S. Tsirkin } 994315a1350SMichael S. Tsirkin } 995315a1350SMichael S. Tsirkin err.status = error_status; 996a05f686fSPavel Fedin err.source_id = pci_requester_id(dev); 997315a1350SMichael S. Tsirkin 998315a1350SMichael S. Tsirkin err.flags = 0; 999315a1350SMichael S. Tsirkin if (correctable) { 1000315a1350SMichael S. Tsirkin err.flags |= PCIE_AER_ERR_IS_CORRECTABLE; 1001315a1350SMichael S. Tsirkin } 100234acbc95SEric Blake if (qdict_get_try_bool(qdict, "advisory_non_fatal", false)) { 1003315a1350SMichael S. Tsirkin err.flags |= PCIE_AER_ERR_MAYBE_ADVISORY; 1004315a1350SMichael S. Tsirkin } 1005315a1350SMichael S. Tsirkin if (qdict_haskey(qdict, "header0")) { 1006315a1350SMichael S. Tsirkin err.flags |= PCIE_AER_ERR_HEADER_VALID; 1007315a1350SMichael S. Tsirkin } 1008315a1350SMichael S. Tsirkin if (qdict_haskey(qdict, "prefix0")) { 1009315a1350SMichael S. Tsirkin err.flags |= PCIE_AER_ERR_TLP_PREFIX_PRESENT; 1010315a1350SMichael S. Tsirkin } 1011315a1350SMichael S. Tsirkin 1012315a1350SMichael S. Tsirkin err.header[0] = qdict_get_try_int(qdict, "header0", 0); 1013315a1350SMichael S. Tsirkin err.header[1] = qdict_get_try_int(qdict, "header1", 0); 1014315a1350SMichael S. Tsirkin err.header[2] = qdict_get_try_int(qdict, "header2", 0); 1015315a1350SMichael S. Tsirkin err.header[3] = qdict_get_try_int(qdict, "header3", 0); 1016315a1350SMichael S. Tsirkin 1017315a1350SMichael S. Tsirkin err.prefix[0] = qdict_get_try_int(qdict, "prefix0", 0); 1018315a1350SMichael S. Tsirkin err.prefix[1] = qdict_get_try_int(qdict, "prefix1", 0); 1019315a1350SMichael S. Tsirkin err.prefix[2] = qdict_get_try_int(qdict, "prefix2", 0); 1020315a1350SMichael S. Tsirkin err.prefix[3] = qdict_get_try_int(qdict, "prefix3", 0); 1021315a1350SMichael S. Tsirkin 1022315a1350SMichael S. Tsirkin ret = pcie_aer_inject_error(dev, &err); 10239edd5338SEric Blake if (ret < 0) { 10249edd5338SEric Blake monitor_printf(mon, "failed to inject error: %s\n", 10259edd5338SEric Blake strerror(-ret)); 10269edd5338SEric Blake return ret; 10279edd5338SEric Blake } 10289edd5338SEric Blake details->id = id; 10299edd5338SEric Blake details->root_bus = pci_root_bus_path(dev); 1030cdc57472SDavid Gibson details->bus = pci_dev_bus_num(dev); 10319edd5338SEric Blake details->devfn = dev->devfn; 1032315a1350SMichael S. Tsirkin 1033315a1350SMichael S. Tsirkin return 0; 1034315a1350SMichael S. Tsirkin } 103504e00c92SMarkus Armbruster 103604e00c92SMarkus Armbruster void hmp_pcie_aer_inject_error(Monitor *mon, const QDict *qdict) 103704e00c92SMarkus Armbruster { 10389edd5338SEric Blake PCIEErrorDetails data; 103904e00c92SMarkus Armbruster 104004e00c92SMarkus Armbruster if (do_pcie_aer_inject_error(mon, qdict, &data) < 0) { 104104e00c92SMarkus Armbruster return; 104204e00c92SMarkus Armbruster } 104304e00c92SMarkus Armbruster 104404e00c92SMarkus Armbruster monitor_printf(mon, "OK id: %s root bus: %s, bus: %x devfn: %x.%x\n", 10459edd5338SEric Blake data.id, data.root_bus, data.bus, 10469edd5338SEric Blake PCI_SLOT(data.devfn), PCI_FUNC(data.devfn)); 104704e00c92SMarkus Armbruster } 1048