aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Qiu <qiudayu@linux.vnet.ibm.com>2014-07-02 04:02:07 -0400
committerBenjamin Herrenschmidt <benh@kernel.crashing.org>2014-07-21 14:48:45 +1000
commit9cd97494117f86b84e8b946d025226035acff70e (patch)
tree1d9de3f712b77a096b25da24486915544b618b74
parent5377c73b25c6b19c3b3d238a5bcc7d5f1348ea45 (diff)
downloadskiboot-9cd97494117f86b84e8b946d025226035acff70e.zip
skiboot-9cd97494117f86b84e8b946d025226035acff70e.tar.gz
skiboot-9cd97494117f86b84e8b946d025226035acff70e.tar.bz2
P7IOC: Support error injection
The patch implements P7IOC backend to support error injection. There are 3 registers (control, address and mask) for each P7IOC PHB to do error injection. The way to do error injection is straight-forward. For IO, MMIO and PCI-CFG error injection, we check the address is valid or not. The address will be used for error injection if that's valid. Otherwise, we have to figure one out from IODT, M32DT or PELTM. As DMA address is natually invisible to users, we just figure it out from TVT and use that for error injection. 64-bits error injection will be supported later. Signed-off-by: Mike Qiu <qiudayu@linux.vnet.ibm.com> Reviewed-by: Gavin Shan <gwshan@linux.vnet.ibm.com> Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
-rw-r--r--hw/p7ioc-phb.c233
-rw-r--r--include/p7ioc-regs.h17
2 files changed, 249 insertions, 1 deletions
diff --git a/hw/p7ioc-phb.c b/hw/p7ioc-phb.c
index 9bcda82..bea18ea 100644
--- a/hw/p7ioc-phb.c
+++ b/hw/p7ioc-phb.c
@@ -1301,6 +1301,238 @@ static int64_t p7ioc_eeh_freeze_clear(struct phb *phb, uint64_t pe_number,
return OPAL_SUCCESS;
}
+static int64_t p7ioc_err_injct(struct phb *phb, uint32_t pe_no,
+ uint32_t type, uint32_t function,
+ uint64_t address, uint64_t mask)
+{
+ struct p7ioc_phb *p = phb_to_p7ioc_phb(phb);
+ uint64_t pelt, ctl = 0;
+ uint64_t base, prefer, addr, msk;
+ int index = 0, bus_valid = 0;
+
+ /* To support 64-bits error later */
+ if (type == OpalErrinjctTypeIoaBusError64)
+ return OPAL_UNSUPPORTED;
+ /*
+ * The control register might have something left from
+ * the error injection of last time. We need clear it
+ * for consistency
+ */
+ out_be64(p->regs + PHB_PAPR_ERR_INJ_CTL, 0x0ul);
+
+ /* We shouldn't inject error to the reserved PE#127 */
+ if (pe_no > 126 ||
+ function > OpalEjtIoaDmaWriteMemTarget)
+ return OPAL_PARAMETER;
+
+ /* Looking into PELTM cache to see if the PE# is valid */
+ pelt = p->peltm_cache[pe_no];
+ if (pelt == 0x0001f80000000000)
+ return OPAL_PARAMETER;
+
+ /*
+ * HW100549: For error injection on outbound opertions, we
+ * need have read and write on DD10 chip.
+ */
+ switch (function) {
+ case OpalEjtIoaLoadMemAddr:
+ case OpalEjtIoaLoadMemData:
+ case OpalEjtIoaStoreMemAddr:
+ case OpalEjtIoaStoreMemData:
+ ctl |= PHB_PAPR_ERR_INJ_CTL_OUTB;
+ if (function == OpalEjtIoaLoadMemAddr ||
+ function == OpalEjtIoaLoadMemData)
+ ctl |= PHB_PAPR_ERR_INJ_CTL_RD;
+ else
+ ctl |= PHB_PAPR_ERR_INJ_CTL_WR;
+
+ if (p->rev == P7IOC_REV_DD10)
+ ctl |= (PHB_PAPR_ERR_INJ_CTL_RD |
+ PHB_PAPR_ERR_INJ_CTL_WR);
+
+ /*
+ * For now, we only care about M32. Looking into M32DT to see
+ * if the input address has been assigned to the PE.
+ */
+ addr = 0x0ul;
+ prefer = 0x0ul;
+ for (index = 0; index < 128; index++) {
+ if (SETFIELD(IODA_XXDT_PE, 0ull, pe_no) ==
+ p->m32d_cache[index]) {
+ base = p->m32_base + M32_PCI_START +
+ (M32_PCI_SIZE / 128) * index;
+
+ /* Update prefer address */
+ if (!prefer) {
+ prefer = GETFIELD(PHB_PAPR_ERR_INJ_MASK_MMIO, base);
+ prefer = SETFIELD(PHB_PAPR_ERR_INJ_MASK_MMIO, 0x0ul, prefer);
+ }
+
+ /* The input address matches ? */
+ if (address >= base &&
+ address < base + (M32_PCI_SIZE / 128)) {
+ addr = address;
+ break;
+ }
+ }
+ }
+
+ /* Need amend the address ? */
+ if (!addr) {
+ if (!prefer)
+ return OPAL_PARAMETER;
+
+ addr = prefer;
+ msk = PHB_PAPR_ERR_INJ_MASK_MMIO_MASK;
+ } else {
+ msk = mask;
+ }
+
+ break;
+ case OpalEjtIoaLoadIoAddr:
+ case OpalEjtIoaLoadIoData:
+ case OpalEjtIoaStoreIoAddr:
+ case OpalEjtIoaStoreIoData:
+ ctl |= PHB_PAPR_ERR_INJ_CTL_OUTB;
+ if (function == OpalEjtIoaLoadIoAddr ||
+ function == OpalEjtIoaLoadIoData)
+ ctl |= PHB_PAPR_ERR_INJ_CTL_RD;
+ else
+ ctl |= PHB_PAPR_ERR_INJ_CTL_WR;
+
+ if (p->rev == P7IOC_REV_DD10)
+ ctl |= (PHB_PAPR_ERR_INJ_CTL_RD |
+ PHB_PAPR_ERR_INJ_CTL_WR);
+
+ /*
+ * Similar to M32 case. Looking into IO domain to see
+ * if the input address is owned by the designated PE.
+ * Otherwise, we have to pick one up from IO domain.
+ */
+ addr = 0x0ul;
+ prefer = 0x0ul;
+ for (index = 0; index < 128; index++) {
+ if (SETFIELD(IODA_XXDT_PE, 0ull, pe_no) ==
+ p->iod_cache[index]) {
+ base = p->io_base + (PHB_IO_SIZE / 128) * index;
+
+ /* Update prefer address */
+ if (!prefer) {
+ prefer = GETFIELD(PHB_PAPR_ERR_INJ_MASK_IO, base);
+ prefer = SETFIELD(PHB_PAPR_ERR_INJ_MASK_IO, 0x0ul, prefer);
+ }
+
+ /* The input address matches ? */
+ if (address >= base &&
+ address < base + (PHB_IO_SIZE / 128)) {
+ addr = address;
+ break;
+ }
+ }
+ }
+
+ /* Need amend the address ? */
+ if (!addr) {
+ if (!prefer)
+ return OPAL_PARAMETER;
+
+ addr = prefer;
+ msk = PHB_PAPR_ERR_INJ_MASK_IO_MASK;
+ } else {
+ msk = mask;
+ }
+
+ break;
+ case OpalEjtIoaLoadConfigAddr:
+ case OpalEjtIoaLoadConfigData:
+ case OpalEjtIoaStoreConfigAddr:
+ case OpalEjtIoaStoreConfigData:
+ ctl |= PHB_PAPR_ERR_INJ_CTL_CFG;
+ if (function == OpalEjtIoaLoadConfigAddr ||
+ function == OpalEjtIoaLoadConfigData)
+ ctl |= PHB_PAPR_ERR_INJ_CTL_RD;
+ else
+ ctl |= PHB_PAPR_ERR_INJ_CTL_WR;
+
+ if (p->rev == P7IOC_REV_DD10)
+ ctl |= (PHB_PAPR_ERR_INJ_CTL_RD |
+ PHB_PAPR_ERR_INJ_CTL_WR);
+
+ /*
+ * Looking into PELTM to see if the PCI bus# is owned
+ * by the PE#. Otherwise, we have to figure one out.
+ */
+ bus_valid = GETFIELD(IODA_PELTM_BUS_VALID, pelt);
+ base = GETFIELD(IODA_PELTM_BUS, pelt);
+ addr = 0x0ul;
+ prefer = SETFIELD(PHB_PAPR_ERR_INJ_MASK_CFG, 0x0ul, base);
+ switch (bus_valid) {
+ case 0x0:
+ addr = address;
+ break;
+ case 0x1:
+ return OPAL_HARDWARE;
+ default:
+ if (GETFIELD(PHB_PAPR_ERR_INJ_MASK_CFG, address) >= base
+ && GETFIELD(PHB_PAPR_ERR_INJ_MASK_CFG, address)
+ < base + (0x1ul << (7 - bus_valid)))
+ addr = address;
+ break;
+ }
+
+ /* Address needs amend ? */
+ if (!addr) {
+ addr = prefer;
+ msk = PHB_PAPR_ERR_INJ_MASK_CFG_MASK;
+ } else {
+ msk = mask;
+ }
+
+ break;
+ case OpalEjtIoaDmaReadMemAddr:
+ case OpalEjtIoaDmaReadMemData:
+ case OpalEjtIoaDmaReadMemMaster:
+ case OpalEjtIoaDmaReadMemTarget:
+ case OpalEjtIoaDmaWriteMemAddr:
+ case OpalEjtIoaDmaWriteMemData:
+ case OpalEjtIoaDmaWriteMemMaster:
+ case OpalEjtIoaDmaWriteMemTarget:
+ ctl |= PHB_PAPR_ERR_INJ_CTL_INB;
+ if (function == OpalEjtIoaDmaReadMemAddr ||
+ function == OpalEjtIoaDmaReadMemData ||
+ function == OpalEjtIoaDmaReadMemMaster ||
+ function == OpalEjtIoaDmaReadMemTarget)
+ ctl |= PHB_PAPR_ERR_INJ_CTL_RD;
+ else
+ ctl |= PHB_PAPR_ERR_INJ_CTL_WR;
+
+ /* For DMA, we just pick address from TVT */
+ addr = 0x0;
+ for (index = 0; index < 128; index++) {
+ if (GETFIELD(IODA_TVT1_PE_NUM, p->tve_hi_cache[index])
+ == pe_no) {
+ addr = SETFIELD(PHB_PAPR_ERR_INJ_MASK_DMA, 0ul, index);
+ msk = PHB_PAPR_ERR_INJ_MASK_DMA_MASK;
+ break;
+ }
+ }
+
+ /* Some PE might not have DMA capability */
+ if (index >= 128)
+ return OPAL_PARAMETER;
+
+ break;
+ default:
+ return OPAL_PARAMETER;
+ }
+
+ out_be64(p->regs + PHB_PAPR_ERR_INJ_CTL, ctl);
+ out_be64(p->regs + PHB_PAPR_ERR_INJ_ADDR, addr);
+ out_be64(p->regs + PHB_PAPR_ERR_INJ_MASK, msk);
+
+ return OPAL_SUCCESS;
+}
+
static int64_t p7ioc_get_diag_data(struct phb *phb, void *diag_buffer,
uint64_t diag_buffer_len)
{
@@ -2276,6 +2508,7 @@ static const struct phb_ops p7ioc_phb_ops = {
.pci_reinit = p7ioc_pci_reinit,
.eeh_freeze_status = p7ioc_eeh_freeze_status,
.eeh_freeze_clear = p7ioc_eeh_freeze_clear,
+ .err_injct = p7ioc_err_injct,
.get_diag_data = NULL,
.get_diag_data2 = p7ioc_get_diag_data,
.next_error = p7ioc_eeh_next_error,
diff --git a/include/p7ioc-regs.h b/include/p7ioc-regs.h
index daac1eb..2cd5b75 100644
--- a/include/p7ioc-regs.h
+++ b/include/p7ioc-regs.h
@@ -164,9 +164,24 @@
#define PHB_PHB2_TCE_CAP 0x258
#define PHB_PHB2_IRQ_CAP 0x260
#define PHB_PHB2_EEH_CAP 0x268
-#define PHB_PAPR_ERR_INJ_CONTROL 0x2b0
+#define PHB_PAPR_ERR_INJ_CTL 0x2b0
+#define PHB_PAPR_ERR_INJ_CTL_INB PPC_BIT(0)
+#define PHB_PAPR_ERR_INJ_CTL_OUTB PPC_BIT(1)
+#define PHB_PAPR_ERR_INJ_CTL_STICKY PPC_BIT(2)
+#define PHB_PAPR_ERR_INJ_CTL_CFG PPC_BIT(3)
+#define PHB_PAPR_ERR_INJ_CTL_RD PPC_BIT(4)
+#define PHB_PAPR_ERR_INJ_CTL_WR PPC_BIT(5)
+#define PHB_PAPR_ERR_INJ_CTL_FREEZE PPC_BIT(6)
#define PHB_PAPR_ERR_INJ_ADDR 0x2b8
#define PHB_PAPR_ERR_INJ_MASK 0x2c0
+#define PHB_PAPR_ERR_INJ_MASK_CFG_MASK PPC_BITMASK(4,11)
+#define PHB_PAPR_ERR_INJ_MASK_CFG_LSH PPC_BITLSHIFT(11)
+#define PHB_PAPR_ERR_INJ_MASK_MMIO_MASK PPC_BITMASK(16,39) /* 16M aligned */
+#define PHB_PAPR_ERR_INJ_MASK_MMIO_LSH PPC_BITLSHIFT(39)
+#define PHB_PAPR_ERR_INJ_MASK_IO_MASK PPC_BITMASK(16,47) /* 64K aligned */
+#define PHB_PAPR_ERR_INJ_MASK_IO_LSH PPC_BITLSHIFT(47)
+#define PHB_PAPR_ERR_INJ_MASK_DMA_MASK PPC_BITMASK(60,63) /* 16 window */
+#define PHB_PAPR_ERR_INJ_MASK_DMA_LSH PPC_BITLSHIFT(63)
#define PHB_ETU_ERR_SUMMARY 0x2c8
/* UTL registers */