aboutsummaryrefslogtreecommitdiff
path: root/hw/phb4.c
diff options
context:
space:
mode:
authorRussell Currey <ruscur@russell.cc>2017-06-09 16:06:06 +1000
committerStewart Smith <stewart@linux.vnet.ibm.com>2017-06-15 13:37:37 +1000
commitfb267a0120ff8dab4f5cff2bc4ddd72091f94dec (patch)
treedce5251365a0d7293369f63d9f141ade22142c23 /hw/phb4.c
parentb2da4f4e6c0a3ea312376c44afb16bb9321145aa (diff)
downloadskiboot-fb267a0120ff8dab4f5cff2bc4ddd72091f94dec.zip
skiboot-fb267a0120ff8dab4f5cff2bc4ddd72091f94dec.tar.gz
skiboot-fb267a0120ff8dab4f5cff2bc4ddd72091f94dec.tar.bz2
phb4: Error injection for config space
Implement CFG (config space) error injection. This works the same as PHB3. MMIO and DMA error injection require a rewrite, so they're unsupported for now. While it's not feature complete, this at least provides an easy way to inject an error that will trigger EEH. Signed-off-by: Russell Currey <ruscur@russell.cc> Signed-off-by: Stewart Smith <stewart@linux.vnet.ibm.com>
Diffstat (limited to 'hw/phb4.c')
-rw-r--r--hw/phb4.c182
1 files changed, 181 insertions, 1 deletions
diff --git a/hw/phb4.c b/hw/phb4.c
index 3a54e03..7bcbc93 100644
--- a/hw/phb4.c
+++ b/hw/phb4.c
@@ -2511,11 +2511,191 @@ static int64_t phb4_eeh_next_error(struct phb *phb,
return OPAL_SUCCESS;
}
+static int64_t phb4_err_inject_finalize(struct phb4 *phb, uint64_t addr,
+ uint64_t mask, uint64_t ctrl,
+ bool is_write)
+{
+ if (is_write)
+ ctrl |= PHB_PAPR_ERR_INJ_CTL_WR;
+ else
+ ctrl |= PHB_PAPR_ERR_INJ_CTL_RD;
+
+ out_be64(phb->regs + PHB_PAPR_ERR_INJ_ADDR, addr);
+ out_be64(phb->regs + PHB_PAPR_ERR_INJ_MASK, mask);
+ out_be64(phb->regs + PHB_PAPR_ERR_INJ_CTL, ctrl);
+
+ return OPAL_SUCCESS;
+}
+
+static int64_t phb4_err_inject_mem32(struct phb4 *phb, uint64_t pe_number,
+ uint64_t addr, uint64_t mask,
+ bool is_write)
+{
+ return OPAL_UNSUPPORTED;
+}
+
+static int64_t phb4_err_inject_mem64(struct phb4 *phb, uint64_t pe_number,
+ uint64_t addr, uint64_t mask,
+ bool is_write)
+{
+ return OPAL_UNSUPPORTED;
+}
+
+static int64_t phb4_err_inject_cfg(struct phb4 *phb, uint64_t pe_number,
+ uint64_t addr, uint64_t mask,
+ bool is_write)
+{
+ uint64_t a, m, prefer, ctrl;
+ int bdfn;
+ bool is_bus_pe = false;
+
+ a = 0xffffull;
+ prefer = 0xffffull;
+ m = PHB_PAPR_ERR_INJ_MASK_CFG_ALL;
+ ctrl = PHB_PAPR_ERR_INJ_CTL_CFG;
+
+ for (bdfn = 0; bdfn < RTT_TABLE_ENTRIES; bdfn++) {
+ if (phb->rte_cache[bdfn] != pe_number)
+ continue;
+
+ /* The PE can be associated with PCI bus or device */
+ is_bus_pe = false;
+ if ((bdfn + 8) < RTT_TABLE_ENTRIES &&
+ phb->rte_cache[bdfn + 8] == pe_number)
+ is_bus_pe = true;
+
+ /* Figure out the PCI config address */
+ if (prefer == 0xffffull) {
+ if (is_bus_pe) {
+ m = PHB_PAPR_ERR_INJ_MASK_CFG;
+ prefer = SETFIELD(m, 0x0ull, (bdfn >> 8));
+ } else {
+ m = PHB_PAPR_ERR_INJ_MASK_CFG_ALL;
+ prefer = SETFIELD(m, 0x0ull, bdfn);
+ }
+ }
+
+ /* Check the input address is valid or not */
+ if (!is_bus_pe &&
+ GETFIELD(PHB_PAPR_ERR_INJ_MASK_CFG_ALL, addr) == bdfn) {
+ a = addr;
+ break;
+ }
+
+ if (is_bus_pe &&
+ GETFIELD(PHB_PAPR_ERR_INJ_MASK_CFG, addr) == (bdfn >> 8)) {
+ a = addr;
+ break;
+ }
+ }
+
+ /* Invalid PE number */
+ if (prefer == 0xffffull)
+ return OPAL_PARAMETER;
+
+ /* Specified address is out of range */
+ if (a == 0xffffull)
+ a = prefer;
+ else
+ m = mask;
+
+ return phb4_err_inject_finalize(phb, a, m, ctrl, is_write);
+}
+
+static int64_t phb4_err_inject_dma(struct phb4 *phb, uint64_t pe_number,
+ uint64_t addr, uint64_t mask,
+ bool is_write, bool is_64bits)
+{
+ return OPAL_UNSUPPORTED;
+}
+
+static int64_t phb4_err_inject_dma32(struct phb4 *phb, uint64_t pe_number,
+ uint64_t addr, uint64_t mask,
+ bool is_write)
+{
+ return phb4_err_inject_dma(phb, pe_number, addr, mask, is_write, false);
+}
+
+static int64_t phb4_err_inject_dma64(struct phb4 *phb, uint64_t pe_number,
+ uint64_t addr, uint64_t mask,
+ bool is_write)
+{
+ return phb4_err_inject_dma(phb, pe_number, addr, mask, is_write, true);
+}
+
+
static int64_t phb4_err_inject(struct phb *phb, uint64_t pe_number,
uint32_t type, uint32_t func,
uint64_t addr, uint64_t mask)
{
- return OPAL_UNSUPPORTED;
+ struct phb4 *p = phb_to_phb4(phb);
+ int64_t (*handler)(struct phb4 *p, uint64_t pe_number,
+ uint64_t addr, uint64_t mask, bool is_write);
+ bool is_write;
+
+ /* How could we get here without valid RTT? */
+ if (!p->tbl_rtt)
+ return OPAL_HARDWARE;
+
+ /* We can't inject error to the reserved PE */
+ if (pe_number == PHB4_RESERVED_PE_NUM(p) || pe_number >= p->num_pes)
+ return OPAL_PARAMETER;
+
+ /* Clear leftover from last time */
+ out_be64(p->regs + PHB_PAPR_ERR_INJ_CTL, 0x0ul);
+
+ switch (func) {
+ case OPAL_ERR_INJECT_FUNC_IOA_LD_MEM_ADDR:
+ case OPAL_ERR_INJECT_FUNC_IOA_LD_MEM_DATA:
+ is_write = false;
+ if (type == OPAL_ERR_INJECT_TYPE_IOA_BUS_ERR64)
+ handler = phb4_err_inject_mem64;
+ else
+ handler = phb4_err_inject_mem32;
+ break;
+ case OPAL_ERR_INJECT_FUNC_IOA_ST_MEM_ADDR:
+ case OPAL_ERR_INJECT_FUNC_IOA_ST_MEM_DATA:
+ is_write = true;
+ if (type == OPAL_ERR_INJECT_TYPE_IOA_BUS_ERR64)
+ handler = phb4_err_inject_mem64;
+ else
+ handler = phb4_err_inject_mem32;
+ break;
+ case OPAL_ERR_INJECT_FUNC_IOA_LD_CFG_ADDR:
+ case OPAL_ERR_INJECT_FUNC_IOA_LD_CFG_DATA:
+ is_write = false;
+ handler = phb4_err_inject_cfg;
+ break;
+ case OPAL_ERR_INJECT_FUNC_IOA_ST_CFG_ADDR:
+ case OPAL_ERR_INJECT_FUNC_IOA_ST_CFG_DATA:
+ is_write = true;
+ handler = phb4_err_inject_cfg;
+ break;
+ case OPAL_ERR_INJECT_FUNC_IOA_DMA_RD_ADDR:
+ case OPAL_ERR_INJECT_FUNC_IOA_DMA_RD_DATA:
+ case OPAL_ERR_INJECT_FUNC_IOA_DMA_RD_MASTER:
+ case OPAL_ERR_INJECT_FUNC_IOA_DMA_RD_TARGET:
+ is_write = false;
+ if (type == OPAL_ERR_INJECT_TYPE_IOA_BUS_ERR64)
+ handler = phb4_err_inject_dma64;
+ else
+ handler = phb4_err_inject_dma32;
+ break;
+ case OPAL_ERR_INJECT_FUNC_IOA_DMA_WR_ADDR:
+ case OPAL_ERR_INJECT_FUNC_IOA_DMA_WR_DATA:
+ case OPAL_ERR_INJECT_FUNC_IOA_DMA_WR_MASTER:
+ case OPAL_ERR_INJECT_FUNC_IOA_DMA_WR_TARGET:
+ is_write = true;
+ if (type == OPAL_ERR_INJECT_TYPE_IOA_BUS_ERR64)
+ handler = phb4_err_inject_dma64;
+ else
+ handler = phb4_err_inject_dma32;
+ break;
+ default:
+ return OPAL_PARAMETER;
+ }
+
+ return handler(p, pe_number, addr, mask, is_write);
}
static int64_t phb4_get_diag_data(struct phb *phb,