aboutsummaryrefslogtreecommitdiff
path: root/hw/p7ioc-phb.c
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 /hw/p7ioc-phb.c
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>
Diffstat (limited to 'hw/p7ioc-phb.c')
-rw-r--r--hw/p7ioc-phb.c233
1 files changed, 233 insertions, 0 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,