📄 pcibr_error.c
字号:
* could make much progress. (NOTE: interrupd does * come in _after_ bus error processing starts. But it's * completed by the time bus error code reaches PCI PIO * error handling. * Similarly write error results in just an interrupt, * and error handling has to be done at interrupt level. * There is no way to distinguish at interrupt time, if an * error interrupt is due to read/write error.. */ /* We know the xtalk addr, the raw PCI bus space, * the raw PCI bus address, the decoded PCI bus * space, the offset within that space, and the * decoded PCI slot (which may be "PCIIO_SLOT_NONE" if no slot * is known to be involved). */ /* * Hand the error off to the handler registered * for the slot that should have decoded the error, * or to generic PCI handling (if pciio decides that * such is appropriate). */ retval = pciio_error_handler(pcibr_vhdl, error_code, mode, ioe); if (retval != IOERROR_HANDLED) { /* Generate a generic message for IOERROR_UNHANDLED * since the subsidiary handlers were silent, and * did no recovery. */ if (retval == IOERROR_UNHANDLED) { retval = IOERROR_PANIC; /* we may or may not want to print some of this, * depending on debug level and which error code. */ printk(KERN_ALERT "PIO Error on PCI Bus %s", pcibr_soft->bs_name); /* this decodes part of the ioe; our caller * will dump the raw details in DEBUG and * kdebug kernels. */ BEM_ADD_IOE(ioe); }#if defined(FORCE_ERRORS) if (0) { /* * Dump raw data from Bridge/PCI layer. */ BEM_ADD_STR("Raw info from Bridge/PCI layer:\n"); if (IS_PIC_SOFT(pcibr_soft)) { if (bridge->p_int_status_64 & (picreg_t)BRIDGE_ISR_PCIBUS_PIOERR) pcibr_error_dump(pcibr_soft); } else { if (bridge->b_int_status & (bridgereg_t)BRIDGE_ISR_PCIBUS_PIOERR) pcibr_error_dump(pcibr_soft); } BEM_ADD_SPC(raw_space); BEM_ADD_VAR(raw_paddr); if (IOERROR_FIELDVALID(ioe, widgetdev)) { short widdev; IOERROR_GETVALUE(widdev, ioe, widgetdev); slot = pciio_widgetdev_slot_get(widdev); func = pciio_widgetdev_func_get(widdev); if (slot < PCIBR_NUM_SLOTS(pcibr_soft)) { bridgereg_t device = bridge->b_device[slot].reg; BEM_ADD_VAR(slot); BEM_ADD_VAR(func); BEM_ADD_REG(device); } } }#endif /* FORCE_ERRORS */ /* * Since error could not be handled at lower level, * error data logged has not been cleared. * Clean up errors, and * re-enable bridge to interrupt on error conditions. * NOTE: Wheather we get the interrupt on PCI_ABORT or not is * dependent on INT_ENABLE register. This write just makes sure * that if the interrupt was enabled, we do get the interrupt. * * CAUTION: Resetting bit BRIDGE_IRR_PCI_GRP_CLR, acknowledges * a group of interrupts. If while handling this error, * some other error has occurred, that would be * implicitly cleared by this write. * Need a way to ensure we don't inadvertently clear some * other errors. */ if (IOERROR_FIELDVALID(ioe, widgetdev)) { short widdev; IOERROR_GETVALUE(widdev, ioe, widgetdev); pcibr_device_disable(pcibr_soft, pciio_widgetdev_slot_get(widdev)); } if (mode == MODE_DEVUSERERROR) pcibr_error_cleanup(pcibr_soft, error_code); } return retval;}/* * bridge_dmaerror * Some error was identified in a DMA transaction. * This routine will identify the <device, address> that caused the error, * and try to invoke the appropriate bus service to handle this. */intpcibr_dmard_error( pcibr_soft_t pcibr_soft, int error_code, ioerror_mode_t mode, ioerror_t *ioe){ vertex_hdl_t pcibr_vhdl = pcibr_soft->bs_vhdl; bridge_t *bridge = pcibr_soft->bs_base; bridgereg_t bus_lowaddr, bus_uppraddr; int retval = 0; int bufnum; /* * In case of DMA errors, bridge should have logged the * address that caused the error. * Look up the address, in the bridge error registers, and * take appropriate action */ { short tmp; IOERROR_GETVALUE(tmp, ioe, widgetnum); ASSERT(tmp == pcibr_soft->bs_xid); } ASSERT(bridge); /* * read error log registers */ bus_lowaddr = bridge->b_wid_resp_lower; bus_uppraddr = bridge->b_wid_resp_upper; bufnum = BRIDGE_RESP_ERRUPPR_BUFNUM(bus_uppraddr); IOERROR_SETVALUE(ioe, widgetdev, pciio_widgetdev_create( BRIDGE_RESP_ERRUPPR_DEVICE(bus_uppraddr), 0)); IOERROR_SETVALUE(ioe, busaddr, (bus_lowaddr | ((iopaddr_t) (bus_uppraddr & BRIDGE_ERRUPPR_ADDRMASK) << 32))); /* * need to ensure that the xtalk address in ioe * maps to PCI error address read from bridge. * How to convert PCI address back to Xtalk address ? * (better idea: convert XTalk address to PCI address * and then do the compare!) */ retval = pciio_error_handler(pcibr_vhdl, error_code, mode, ioe); if (retval != IOERROR_HANDLED) { short tmp; IOERROR_GETVALUE(tmp, ioe, widgetdev); pcibr_device_disable(pcibr_soft, pciio_widgetdev_slot_get(tmp)); } /* * Re-enable bridge to interrupt on BRIDGE_IRR_RESP_BUF_GRP_CLR * NOTE: Wheather we get the interrupt on BRIDGE_IRR_RESP_BUF_GRP_CLR or * not is dependent on INT_ENABLE register. This write just makes sure * that if the interrupt was enabled, we do get the interrupt. */ bridge->b_int_rst_stat = BRIDGE_IRR_RESP_BUF_GRP_CLR; /* * Also, release the "bufnum" back to buffer pool that could be re-used. * This is done by "disabling" the buffer for a moment, then restoring * the original assignment. */ { reg_p regp; bridgereg_t regv; bridgereg_t mask; regp = (bufnum & 1) ? &bridge->b_odd_resp : &bridge->b_even_resp; mask = 0xF << ((bufnum >> 1) * 4); regv = *regp; *regp = regv & ~mask; *regp = regv; } return retval;}/* * pcibr_dmawr_error: * Handle a dma write error caused by a device attached to this bridge. * * ioe has the widgetnum, widgetdev, and memaddr fields updated * But we don't know the PCI address that corresponds to "memaddr" * nor do we know which device driver is generating this address. * * There is no easy way to find out the PCI address(es) that map * to a specific system memory address. Bus handling code is also * of not much help, since they don't keep track of the DMA mapping * that have been handed out. * So it's a dead-end at this time. * * If translation is available, we could invoke the error handling * interface of the device driver. *//*ARGSUSED */intpcibr_dmawr_error( pcibr_soft_t pcibr_soft, int error_code, ioerror_mode_t mode, ioerror_t *ioe){ vertex_hdl_t pcibr_vhdl = pcibr_soft->bs_vhdl; int retval; retval = pciio_error_handler(pcibr_vhdl, error_code, mode, ioe); if (retval != IOERROR_HANDLED) { short tmp; IOERROR_GETVALUE(tmp, ioe, widgetdev); pcibr_device_disable(pcibr_soft, pciio_widgetdev_slot_get(tmp)); } return retval;}/* * Bridge error handler. * Interface to handle all errors that involve bridge in some way. * * This normally gets called from xtalk error handler. * ioe has different set of fields set depending on the error that * was encountered. So, we have a bit field indicating which of the * fields are valid. * * NOTE: This routine could be operating in interrupt context. So, * don't try to sleep here (till interrupt threads work!!) */intpcibr_error_handler( error_handler_arg_t einfo, int error_code, ioerror_mode_t mode, ioerror_t *ioe){ pcibr_soft_t pcibr_soft; int retval = IOERROR_BADERRORCODE; pcibr_soft = (pcibr_soft_t) einfo; PCIBR_DEBUG_ALWAYS((PCIBR_DEBUG_ERROR_HDLR, pcibr_soft->bs_conn, "pcibr_error_handler: pcibr_soft=0x%x, error_code=0x%x\n", pcibr_soft, error_code));#if DEBUG && ERROR_DEBUG printk( "%s: pcibr_error_handler\n", pcibr_soft->bs_name);#endif ASSERT(pcibr_soft != NULL); if (error_code & IOECODE_PIO) retval = pcibr_pioerror(pcibr_soft, error_code, mode, ioe); if (error_code & IOECODE_DMA) { if (error_code & IOECODE_READ) { /* * DMA read error occurs when a device attached to the bridge * tries to read some data from system memory, and this * either results in a timeout or access error. * First case is indicated by the bit "XREAD_REQ_TOUT" * and second case by "RESP_XTALK_ERROR" bit in bridge error * interrupt status register. * * pcibr_error_intr_handler would get invoked first, and it has * the responsibility of calling pcibr_error_handler with * suitable parameters. */ retval = pcibr_dmard_error(pcibr_soft, error_code, MODE_DEVERROR, ioe); } if (error_code & IOECODE_WRITE) { /* * A device attached to this bridge has been generating * bad DMA writes. Find out the device attached, and * slap on it's wrist. */ retval = pcibr_dmawr_error(pcibr_soft, error_code, MODE_DEVERROR, ioe); } } return retval;}/* * PIC has 2 busses under a single widget so pcibr_attach2 registers this * wrapper function rather than pcibr_error_handler() for PIC. It's upto * this wrapper to call pcibr_error_handler() with the correct pcibr_soft * struct (ie. the pcibr_soft struct for the bus that saw the error). * * NOTE: this wrapper function is only registered for PIC ASICs and will * only be called for a PIC */intpcibr_error_handler_wrapper( error_handler_arg_t einfo, int error_code, ioerror_mode_t mode, ioerror_t *ioe){ pcibr_soft_t pcibr_soft = (pcibr_soft_t) einfo; int pio_retval = -1; int dma_retval = -1; PCIBR_DEBUG_ALWAYS((PCIBR_DEBUG_ERROR_HDLR, pcibr_soft->bs_conn, "pcibr_error_handler_wrapper: pcibr_soft=0x%x, " "error_code=0x%x\n", pcibr_soft, error_code)); /* * It is possible that both a IOECODE_PIO and a IOECODE_DMA, and both * IOECODE_READ and IOECODE_WRITE could be set in error_code so we must * process all. Since we are a wrapper for pcibr_error_handler(), and * will be calling it several times within this routine, we turn off the * error_code bits we don't want it to be processing during that call. */ /* * If the error was a result of a PIO, we tell what bus on the PIC saw * the error from the PIO address. */ if (error_code & IOECODE_PIO) { iopaddr_t bad_xaddr; /* * PIC bus0 PIO space 0x000000 - 0x7fffff or 0x40000000 - 0xbfffffff * bus1 PIO space 0x800000 - 0xffffff or 0xc0000000 - 0x13fffffff */ IOERROR_GETVALUE(bad_xaddr, ioe, xtalkaddr); if ((bad_xaddr <= 0x7fffff) || ((bad_xaddr >= 0x40000000) && (bad_xaddr <= 0xbfffffff))) { /* bus 0 saw the error */ pio_retval = pcibr_error_handler((error_handler_arg_t)pcibr_soft, (error_code & ~IOECODE_DMA), mode, ioe); } else if (((bad_xaddr >= 0x800000) && (bad_xaddr <= 0xffffff)) || ((bad_xaddr >= 0xc0000000) && (bad_xaddr <= 0x13fffffff))) { /* bus 1 saw the error */ pcibr_soft = pcibr_soft->bs_peers_soft; if (!pcibr_soft) {#if DEBUG printk(KERN_WARNING "pcibr_error_handler: " "bs_peers_soft==NULL. bad_xaddr= 0x%x mode= 0x%x\n", bad_xaddr, mode);#endif pio_retval = IOERROR_HANDLED; } else pio_retval= pcibr_error_handler((error_handler_arg_t)pcibr_soft, (error_code & ~IOECODE_DMA), mode, ioe); } else { printk(KERN_WARNING "pcibr_error_handler_wrapper(): IOECODE_PIO: " "saw an invalid pio address: 0x%lx\n", bad_xaddr); pio_retval = IOERROR_UNHANDLED; } } /* * If the error was a result of a DMA Write, we tell what bus on the PIC * saw the error by looking at tnum. */ if ((error_code & IOECODE_DMA) && (error_code & IOECODE_WRITE)) { short tmp; /* * For DMA writes [X]Bridge encodes the TNUM field of a Xtalk * packet like this: * bits value * 4:3 10b * 2:0 device number * * BUT PIC needs the bus number so it does this: * bits value * 4:3 10b * 2 busnumber * 1:0 device number * * Pull out the bus number from `tnum' and reset the `widgetdev' * since when hubiio_crb_error_handler() set `widgetdev' it had * no idea if it was a PIC or a BRIDGE ASIC so it set it based * off bits 2:0 */ IOERROR_GETVALUE(tmp, ioe, tnum); IOERROR_SETVALUE(ioe, widgetdev, (tmp & 0x3)); if ((tmp & 0x4) == 0) { /* bus 0 saw the error. */ dma_retval = pcibr_error_handler((error_handler_arg_t)pcibr_soft, (error_code & ~(IOECODE_PIO|IOECODE_READ)), mode, ioe); } else { /* bus 1 saw the error */ pcibr_soft = pcibr_soft->bs_peers_soft; dma_retval = pcibr_error_handler((error_handler_arg_t)pcibr_soft, (error_code & ~(IOECODE_PIO|IOECODE_READ)), mode, ioe); } } /* * If the error was a result of a DMA READ, XXX ??? */ if ((error_code & IOECODE_DMA) && (error_code & IOECODE_READ)) { /* * A DMA Read error will result in a BRIDGE_ISR_RESP_XTLK_ERR * or BRIDGE_ISR_BAD_XRESP_PKT bridge error interrupt which * are fatal interrupts (ie. BRIDGE_ISR_ERROR_FATAL) causing * pcibr_error_intr_handler() to panic the system. So is the * error handler even going to get called??? It appears that * the pcibr_dmard_error() attempts to clear the interrupts * so pcibr_error_intr_handler() won't see them, but there * appears to be nothing to prevent pcibr_error_intr_handler() * from running before pcibr_dmard_error() has a chance to * clear the interrupt. * * Since we'll be panicing anyways, don't bother handling the * error for now until we can fix this race condition mentioned * above. */ dma_retval = IOERROR_UNHANDLED; } /* XXX: pcibr_error_handler() should probably do the same thing, it over- * write it's return value as it processes the different "error_code"s. */ if ((pio_retval == -1) && (dma_retval == -1)) { return IOERROR_BADERRORCODE; } else if (dma_retval != IOERROR_HANDLED) { return dma_retval; } else if (pio_retval != IOERROR_HANDLED) { return pio_retval; } else { return IOERROR_HANDLED; }}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -