/* Check for an eeh failure at the given token address. * The given value has been read and it should be 1's (0xff, 0xffff or * 0xffffffff). * * Probe to determine if an error actually occurred. If not return val. * Otherwise panic. */ unsigned long eeh_check_failure(void *token, unsigned long val) { unsigned long addr; struct pci_dev *dev; struct device_node *dn; unsigned long ret, rets[2]; /* IO BAR access could get us here...or if we manually force EEH * operation on even if the hardware won't support it. */ if (!eeh_implemented || ibm_read_slot_reset_state == RTAS_UNKNOWN_SERVICE) return val; /* Finding the phys addr + pci device is quite expensive. * However, the RTAS call is MUCH slower.... :( */ addr = eeh_token_to_phys((unsigned long)token); dev = pci_find_dev_by_addr(addr); if (!dev) { printk("EEH: no pci dev found for addr=0x%lx\n", addr); return val; } dn = pci_device_to_OF_node(dev); if (!dn) { printk("EEH: no pci dn found for addr=0x%lx\n", addr); return val; } /* Access to IO BARs might get this far and still not want checking. */ if (!(dn->eeh_mode & EEH_MODE_SUPPORTED) || dn->eeh_mode & EEH_MODE_NOCHECK) return val; /* Now test for an EEH failure. This is VERY expensive. * Note that the eeh_config_addr may be a parent device * in the case of a device behind a bridge, or it may be * function zero of a multi-function device. * In any case they must share a common PHB. */ if (dn->eeh_config_addr) { ret = rtas_call(ibm_read_slot_reset_state, 3, 3, rets, dn->eeh_config_addr, BUID_HI(dn->phb->buid), BUID_LO(dn->phb->buid)); if (ret == 0 && rets[1] == 1 && rets[0] >= 2) { /* * XXX We should create a separate sysctl for this. * * Since the panic_on_oops sysctl is used to halt * the system in light of potential corruption, we * can use it here. */ if (panic_on_oops) panic("EEH: MMIO failure (%ld) on device:\n%s\n", rets[0], pci_name(dev)); else printk("EEH: MMIO failure (%ld) on device:\n%s\n", rets[0], pci_name(dev)); } } eeh_false_positives++; return val; /* good case */ }
/* Check for an eeh failure at the given token address. * The given value has been read and it should be 1's (0xff, 0xffff or * 0xffffffff). * * Probe to determine if an error actually occurred. If not return val. * Otherwise panic. */ unsigned long eeh_check_failure(void *token, unsigned long val) { unsigned long addr; struct pci_dev *dev; struct device_node *dn; unsigned long ret, rets[2]; /* IO BAR access could get us here...or if we manually force EEH * operation on even if the hardware won't support it. */ if (!eeh_implemented || ibm_read_slot_reset_state == RTAS_UNKNOWN_SERVICE) return val; /* Finding the phys addr + pci device is quite expensive. * However, the RTAS call is MUCH slower.... :( */ addr = eeh_token_to_phys((unsigned long)token); dev = pci_find_dev_by_addr(addr); if (!dev) { printk("EEH: no pci dev found for addr=0x%lx\n", addr); return val; } dn = pci_device_to_OF_node(dev); if (!dn) { printk("EEH: no pci dn found for addr=0x%lx\n", addr); return val; } /* Access to IO BARs might get this far and still not want checking. */ if (!(dn->eeh_mode & EEH_MODE_SUPPORTED) || dn->eeh_mode & EEH_MODE_NOCHECK) return val; /* Now test for an EEH failure. This is VERY expensive. * Note that the eeh_config_addr may be a parent device * in the case of a device behind a bridge, or it may be * function zero of a multi-function device. * In any case they must share a common PHB. */ if (dn->eeh_config_addr) { ret = rtas_call(ibm_read_slot_reset_state, 3, 3, rets, dn->eeh_config_addr, BUID_HI(dn->phb->buid), BUID_LO(dn->phb->buid)); if (ret == 0 && rets[1] == 1 && rets[0] >= 2) { unsigned char slot_err_buf[RTAS_ERROR_LOG_MAX]; unsigned long slot_err_ret; memset(slot_err_buf, 0, RTAS_ERROR_LOG_MAX); slot_err_ret = rtas_call(rtas_token("ibm,slot-error-detail"), 8, 1, dn->eeh_config_addr, BUID_HI(dn->phb->buid), BUID_LO(dn->phb->buid), NULL, 0, __pa(slot_err_buf), RTAS_ERROR_LOG_MAX, 2 /* Permanent Error */); if (slot_err_ret == 0) log_error(slot_err_buf, ERR_TYPE_RTAS_LOG, 1 /* Fatal */); panic("EEH: MMIO failure (%ld) on device:\n %s %s\n", rets[0], dev->slot_name, dev->name); } } eeh_false_positives++; return val; /* good case */ }