📄 pcibr_error.c
字号:
*/ if ((err_status & BRIDGE_ISR_INVLD_ADDR) && ((((uint64_t) bridge->b_wid_err_upper << 32) | (bridge->b_wid_err_lower)) == (BRIDGE_INT_RST_STAT & 0xff0))) { pcibr_llp_control_war_cnt++; err_status &= ~BRIDGE_ISR_INVLD_ADDR; } bridge_errors_to_dump |= BRIDGE_ISR_PCIBUS_PIOERR; /* Dump/Log Bridge error interrupt info */ if (err_status & bridge_errors_to_dump) { printk("BRIDGE ERR_STATUS 0x%lx\n", err_status); pcibr_error_dump(pcibr_soft); } /* PIC BRINGUP WAR (PV# 867308): * Make BRIDGE_ISR_LLP_REC_SNERR & BRIDGE_ISR_LLP_REC_CBERR fatal errors * so we know we've hit the problem defined in PV 867308 that we believe * has only been seen in simulation */ if (IS_PIC_SOFT(pcibr_soft) && PCIBR_WAR_ENABLED(PV867308, pcibr_soft) && (err_status & (BRIDGE_ISR_LLP_REC_SNERR | BRIDGE_ISR_LLP_REC_CBERR))) { printk("BRIDGE ERR_STATUS 0x%lx\n", err_status); pcibr_error_dump(pcibr_soft); panic("PCI Bridge Error interrupt killed the system"); } if (err_status & BRIDGE_ISR_ERROR_FATAL) { panic("PCI Bridge Error interrupt killed the system"); /*NOTREACHED */ } /* * We can't return without re-enabling the interrupt, since * it would cause problems for devices like IOC3 (Lost * interrupts ?.). So, just cleanup the interrupt, and * use saved values later.. * * PIC doesn't require groups of interrupts to be cleared... */ if (IS_PIC_SOFT(pcibr_soft)) { bridge->p_int_rst_stat_64 = (picreg_t)(int_status | BRIDGE_IRR_MULTI_CLR); } else { bridge->b_int_rst_stat = (bridgereg_t)pcibr_errintr_group(int_status); } /* PIC BRINGUP WAR (PV# 856155): * On a PCI_X_ARB_ERR error interrupt clear the DEV_BROKE bits from * the b_arb register to re-enable the device. */ if (IS_PIC_SOFT(pcibr_soft) && (err_status & PIC_ISR_PCIX_ARB_ERR) && PCIBR_WAR_ENABLED(PV856155, pcibr_soft)) { bridge->b_arb |= (0xf << 20); } /* Zero out bserr_intstat field */ pcibr_soft->bs_errinfo.bserr_intstat = 0;}voidpcibr_error_cleanup(pcibr_soft_t pcibr_soft, int error_code){ bridge_t *bridge = pcibr_soft->bs_base; ASSERT(error_code & IOECODE_PIO); error_code = error_code; if (IS_PIC_SOFT(pcibr_soft)) { bridge->p_int_rst_stat_64 = BRIDGE_IRR_PCI_GRP_CLR | PIC_PCIX_GRP_CLR | BRIDGE_IRR_MULTI_CLR; } else { bridge->b_int_rst_stat = BRIDGE_IRR_PCI_GRP_CLR | BRIDGE_IRR_MULTI_CLR; } (void) bridge->b_wid_tflush; /* flushbus */}/*ARGSUSED */voidpcibr_device_disable(pcibr_soft_t pcibr_soft, int devnum){ /* * XXX * Device failed to handle error. Take steps to * disable this device ? HOW TO DO IT ? * * If there are any Read response buffers associated * with this device, it's time to get them back!! * * We can disassociate any interrupt level associated * with this device, and disable that interrupt level * * For now it's just a place holder */}/* * pcibr_pioerror * Handle PIO error that happened at the bridge pointed by pcibr_soft. * * Queries the Bus interface attached to see if the device driver * mapping the device-number that caused error can handle the * situation. If so, it will clean up any error, and return * indicating the error was handled. If the device driver is unable * to handle the error, it expects the bus-interface to disable that * device, and takes any steps needed here to take away any resources * associated with this device. *//* BEM_ADD_IOE doesn't dump the whole ioerror, it just * decodes the PCI specific portions -- we count on our * callers to dump the raw IOE data. */#define BEM_ADD_IOE(ioe) \ do { \ if (IOERROR_FIELDVALID(ioe, busspace)) { \ iopaddr_t spc; \ iopaddr_t win; \ short widdev; \ iopaddr_t busaddr; \ \ IOERROR_GETVALUE(spc, ioe, busspace); \ win = spc - PCIIO_SPACE_WIN(0); \ IOERROR_GETVALUE(busaddr, ioe, busaddr); \ IOERROR_GETVALUE(widdev, ioe, widgetdev); \ \ switch (spc) { \ case PCIIO_SPACE_CFG: \ printk("\tPCI Slot %d Func %d CFG space Offset 0x%lx\n",\ pciio_widgetdev_slot_get(widdev), \ pciio_widgetdev_func_get(widdev), \ busaddr); \ break; \ case PCIIO_SPACE_IO: \ printk("\tPCI I/O space Offset 0x%lx\n", busaddr); \ break; \ case PCIIO_SPACE_MEM: \ case PCIIO_SPACE_MEM32: \ case PCIIO_SPACE_MEM64: \ printk("\tPCI MEM space Offset 0x%lx\n", busaddr); \ break; \ default: \ if (win < 6) { \ printk("\tPCI Slot %d Func %d Window %ld Offset 0x%lx\n",\ pciio_widgetdev_slot_get(widdev), \ pciio_widgetdev_func_get(widdev), \ win, \ busaddr); \ } \ break; \ } \ } \ } while (0)/*ARGSUSED */intpcibr_pioerror( pcibr_soft_t pcibr_soft, int error_code, ioerror_mode_t mode, ioerror_t *ioe){ int retval = IOERROR_HANDLED; vertex_hdl_t pcibr_vhdl = pcibr_soft->bs_vhdl;#if defined(FORCE_ERRORS) bridge_t *bridge = pcibr_soft->bs_base;#endif iopaddr_t bad_xaddr; pciio_space_t raw_space; /* raw PCI space */ iopaddr_t raw_paddr; /* raw PCI address */ pciio_space_t space; /* final PCI space */ pciio_slot_t slot; /* final PCI slot, if appropriate */ pciio_function_t func; /* final PCI func, if appropriate */ iopaddr_t offset; /* final PCI offset */ int cs, cw, cf; pciio_space_t wx; iopaddr_t wb; size_t ws; iopaddr_t wl; /* * We expect to have an "xtalkaddr" coming in, * and need to construct the slot/space/offset. */ IOERROR_GETVALUE(bad_xaddr, ioe, xtalkaddr); PCIBR_DEBUG_ALWAYS((PCIBR_DEBUG_ERROR_HDLR, pcibr_soft->bs_conn, "pcibr_pioerror: pcibr_soft=0x%x, bad_xaddr=0x%x\n", pcibr_soft, bad_xaddr)); slot = PCIIO_SLOT_NONE; func = PCIIO_FUNC_NONE; raw_space = PCIIO_SPACE_NONE; raw_paddr = 0; if ((bad_xaddr >= PCIBR_BUS_TYPE0_CFG_DEV0(pcibr_soft)) && (bad_xaddr < PCIBR_TYPE1_CFG(pcibr_soft))) { raw_paddr = bad_xaddr - PCIBR_BUS_TYPE0_CFG_DEV0(pcibr_soft); slot = raw_paddr / BRIDGE_TYPE0_CFG_SLOT_OFF; raw_paddr = raw_paddr % BRIDGE_TYPE0_CFG_SLOT_OFF; raw_space = PCIIO_SPACE_CFG; } if ((bad_xaddr >= PCIBR_TYPE1_CFG(pcibr_soft)) && (bad_xaddr < (PCIBR_TYPE1_CFG(pcibr_soft) + 0x1000))) { /* Type 1 config space: * slot and function numbers not known. * Perhaps we can read them back? */ raw_paddr = bad_xaddr - PCIBR_TYPE1_CFG(pcibr_soft); raw_space = PCIIO_SPACE_CFG; } if ((bad_xaddr >= PCIBR_BRIDGE_DEVIO0(pcibr_soft)) && (bad_xaddr < PCIBR_BRIDGE_DEVIO(pcibr_soft, BRIDGE_DEV_CNT))) { int x; raw_paddr = bad_xaddr - PCIBR_BRIDGE_DEVIO0(pcibr_soft); x = raw_paddr / BRIDGE_DEVIO_OFF; raw_paddr %= BRIDGE_DEVIO_OFF; /* first two devio windows are double-sized */ if ((x == 1) || (x == 3)) raw_paddr += BRIDGE_DEVIO_OFF; if (x > 0) x--; if (x > 1) x--; /* x is which devio reg; no guarantee * PCI slot x will be responding. * still need to figure out who decodes * space/offset on the bus. */ raw_space = pcibr_soft->bs_slot[x].bss_devio.bssd_space; if (raw_space == PCIIO_SPACE_NONE) { /* Someone got an error because they * accessed the PCI bus via a DevIO(x) * window that pcibr has not yet assigned * to any specific PCI address. It is * quite possible that the Device(x) * register has been changed since they * made their access, but we will give it * our best decode shot. */ raw_space = pcibr_soft->bs_slot[x].bss_device & BRIDGE_DEV_DEV_IO_MEM ? PCIIO_SPACE_MEM : PCIIO_SPACE_IO; raw_paddr += (pcibr_soft->bs_slot[x].bss_device & BRIDGE_DEV_OFF_MASK) << BRIDGE_DEV_OFF_ADDR_SHFT; } else raw_paddr += pcibr_soft->bs_slot[x].bss_devio.bssd_base; } if ((bad_xaddr >= BRIDGE_PCI_MEM32_BASE) && (bad_xaddr <= BRIDGE_PCI_MEM32_LIMIT)) { raw_space = PCIIO_SPACE_MEM32; raw_paddr = bad_xaddr - BRIDGE_PCI_MEM32_BASE; } if ((bad_xaddr >= BRIDGE_PCI_MEM64_BASE) && (bad_xaddr <= BRIDGE_PCI_MEM64_LIMIT)) { raw_space = PCIIO_SPACE_MEM64; raw_paddr = bad_xaddr - BRIDGE_PCI_MEM64_BASE; } if ((bad_xaddr >= BRIDGE_PCI_IO_BASE) && (bad_xaddr <= BRIDGE_PCI_IO_LIMIT)) { raw_space = PCIIO_SPACE_IO; raw_paddr = bad_xaddr - BRIDGE_PCI_IO_BASE; } space = raw_space; offset = raw_paddr; if ((slot == PCIIO_SLOT_NONE) && (space != PCIIO_SPACE_NONE)) { /* we've got a space/offset but not which * PCI slot decodes it. Check through our * notions of which devices decode where. * * Yes, this "duplicates" some logic in * pcibr_addr_toslot; the difference is, * this code knows which space we are in, * and can really really tell what is * going on (no guessing). */ for (cs = pcibr_soft->bs_min_slot; (cs < PCIBR_NUM_SLOTS(pcibr_soft)) && (slot == PCIIO_SLOT_NONE); cs++) { int nf = pcibr_soft->bs_slot[cs].bss_ninfo; pcibr_info_h pcibr_infoh = pcibr_soft->bs_slot[cs].bss_infos; for (cf = 0; (cf < nf) && (slot == PCIIO_SLOT_NONE); cf++) { pcibr_info_t pcibr_info = pcibr_infoh[cf]; if (!pcibr_info) continue; for (cw = 0; (cw < 6) && (slot == PCIIO_SLOT_NONE); ++cw) { if (((wx = pcibr_info->f_window[cw].w_space) != PCIIO_SPACE_NONE) && ((wb = pcibr_info->f_window[cw].w_base) != 0) && ((ws = pcibr_info->f_window[cw].w_size) != 0) && ((wl = wb + ws) > wb) && ((wb <= offset) && (wl > offset))) { /* MEM, MEM32 and MEM64 need to * compare as equal ... */ if ((wx == space) || (((wx == PCIIO_SPACE_MEM) || (wx == PCIIO_SPACE_MEM32) || (wx == PCIIO_SPACE_MEM64)) && ((space == PCIIO_SPACE_MEM) || (space == PCIIO_SPACE_MEM32) || (space == PCIIO_SPACE_MEM64)))) { slot = cs; func = cf; space = PCIIO_SPACE_WIN(cw); offset -= wb; } /* endif window space match */ } /* endif window valid and addr match */ } /* next window unless slot set */ } /* next func unless slot set */ } /* next slot unless slot set */ /* XXX- if slot is still -1, no PCI devices are * decoding here using their standard PCI BASE * registers. This would be a really good place * to cross-coordinate with the pciio PCI * address space allocation routines, to find * out if this address is "allocated" by any of * our subsidiary devices. */ } /* Scan all piomap records on this PCI bus to update * the TimeOut Counters on all matching maps. If we * don't already know the slot number, take it from * the first matching piomap. Note that we have to * compare maps against raw_space and raw_paddr * since space and offset could already be * window-relative. * * There is a chance that one CPU could update * through this path, and another CPU could also * update due to an interrupt. Closing this hole * would only result in the possibility of some * errors never getting logged at all, and since the * use for bp_toc is as a logical test rather than a * strict count, the excess counts are not a * problem. */ for (cs = pcibr_soft->bs_min_slot; cs < PCIBR_NUM_SLOTS(pcibr_soft); ++cs) { int nf = pcibr_soft->bs_slot[cs].bss_ninfo; pcibr_info_h pcibr_infoh = pcibr_soft->bs_slot[cs].bss_infos; for (cf = 0; cf < nf; cf++) { pcibr_info_t pcibr_info = pcibr_infoh[cf]; pcibr_piomap_t map; if (!pcibr_info) continue; for (map = pcibr_info->f_piomap; map != NULL; map = map->bp_next) { wx = map->bp_space; wb = map->bp_pciaddr; ws = map->bp_mapsz; cw = wx - PCIIO_SPACE_WIN(0); if (cw < 6) { wb += pcibr_soft->bs_slot[cs].bss_window[cw].bssw_base; wx = pcibr_soft->bs_slot[cs].bss_window[cw].bssw_space; } if (wx == PCIIO_SPACE_ROM) { wb += pcibr_info->f_rbase; wx = PCIIO_SPACE_MEM; } if ((wx == PCIIO_SPACE_MEM32) || (wx == PCIIO_SPACE_MEM64)) wx = PCIIO_SPACE_MEM; wl = wb + ws; if ((wx == raw_space) && (raw_paddr >= wb) && (raw_paddr < wl)) { atomic_inc(&map->bp_toc[0]); if (slot == PCIIO_SLOT_NONE) { slot = cs; space = map->bp_space; if (cw < 6) offset -= pcibr_soft->bs_slot[cs].bss_window[cw].bssw_base; } } } } } PCIBR_DEBUG_ALWAYS((PCIBR_DEBUG_ERROR_HDLR, pcibr_soft->bs_conn, "pcibr_pioerror: offset=0x%x, slot=0x%x, func=0x%x\n", offset, slot, func)); if (space != PCIIO_SPACE_NONE) { if (slot != PCIIO_SLOT_NONE) { if (func != PCIIO_FUNC_NONE) { IOERROR_SETVALUE(ioe, widgetdev, pciio_widgetdev_create(slot,func)); } else { IOERROR_SETVALUE(ioe, widgetdev, pciio_widgetdev_create(slot,0)); } } IOERROR_SETVALUE(ioe, busspace, space); IOERROR_SETVALUE(ioe, busaddr, offset); } if (mode == MODE_DEVPROBE) { /* * During probing, we don't really care what the * error is. Clean up the error in Bridge, notify * subsidiary devices, and return success. */ pcibr_error_cleanup(pcibr_soft, error_code); /* if appropriate, give the error handler for this slot * a shot at this probe access as well. */ return (slot == PCIIO_SLOT_NONE) ? IOERROR_HANDLED : pciio_error_handler(pcibr_vhdl, error_code, mode, ioe); } /* * If we don't know what "PCI SPACE" the access * was targeting, we may have problems at the * Bridge itself. Don't touch any bridge registers, * and do complain loudly. */ if (space == PCIIO_SPACE_NONE) { printk("XIO Bus Error at %s\n" "\taccess to XIO bus offset 0x%lx\n" "\tdoes not correspond to any PCI address\n", pcibr_soft->bs_name, bad_xaddr); /* caller will dump contents of ioe struct */ return IOERROR_XTALKLEVEL; } /* * Actual PCI Error handling situation. * Typically happens when a user level process accesses * PCI space, and it causes some error. * * Due to PCI Bridge implementation, we get two indication * for a read error: an interrupt and a Bus error. * We like to handle read error in the bus error context. * But the interrupt comes and goes before bus error
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -