📄 pcibr_error.c
字号:
} 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)) { atomicAddInt(map->bp_toc, 1); 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; } } } } } 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%x\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 * 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) {#elif !DEBUG if (kdebug) {#endif /* * Dump raw data from Bridge/PCI layer. */ BEM_ADD_STR("Raw info from Bridge/PCI layer:\n"); if (bridge->b_int_status & BRIDGE_ISR_PCIBUS_PIOERR) pcibr_error_dump(pcibr_soft); BEM_ADD_SPC(raw_space); BEM_ADD_VAR(raw_paddr); if (IOERROR_FIELDVALID(ioe, widgetdev)) { slot = pciio_widgetdev_slot_get(IOERROR_GETVALUE(ioe, widgetdev)); func = pciio_widgetdev_func_get(IOERROR_GETVALUE(ioe, widgetdev)); if (slot < 8) { bridgereg_t device = bridge->b_device[slot].reg; BEM_ADD_VAR(slot); BEM_ADD_VAR(func); BEM_ADD_REG(device); } }#if !DEBUG || defined(FORCE_ERRORS) }#endif /* * 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 occured, 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)) pcibr_device_disable(pcibr_soft, pciio_widgetdev_slot_get( IOERROR_GETVALUE(ioe, widgetdev))); 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. */#define BRIDGE_DMA_READ_ERROR (BRIDGE_ISR_RESP_XTLK_ERR|BRIDGE_ISR_XREAD_REQ_TIMEOUT)intpcibr_dmard_error( pcibr_soft_t pcibr_soft, int error_code, ioerror_mode_t mode, ioerror_t *ioe){ devfs_handle_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 */ ASSERT(IOERROR_GETVALUE(ioe, widgetnum) == 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 adress 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) pcibr_device_disable(pcibr_soft, pciio_widgetdev_slot_get( IOERROR_GETVALUE(ioe,widgetdev))); /* * 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){ devfs_handle_t pcibr_vhdl = pcibr_soft->bs_vhdl; int retval; retval = pciio_error_handler(pcibr_vhdl, error_code, mode, ioe); if (retval != IOERROR_HANDLED) { pcibr_device_disable(pcibr_soft, pciio_widgetdev_slot_get( IOERROR_GETVALUE(ioe, widgetdev))); } 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;#ifdef EHE_ENABLE devfs_handle_t xconn_vhdl,pcibr_vhdl; error_state_t e_state;#endif /* EHE_ENABLE */ pcibr_soft = (pcibr_soft_t) einfo;#ifdef EHE_ENABLE xconn_vhdl = pcibr_soft->bs_conn; pcibr_vhdl = pcibr_soft->bs_vhdl; e_state = error_state_get(xconn_vhdl); if (error_state_set(pcibr_vhdl, e_state) == ERROR_RETURN_CODE_CANNOT_SET_STATE) return(IOERROR_UNHANDLED); /* If we are in the action handling phase clean out the error state * on the xswitch. */ if (e_state == ERROR_STATE_ACTION) (void)error_state_set(xconn_vhdl, ERROR_STATE_NONE);#endif /* EHE_ENABLE */#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;}/* * Reenable a device after handling the error. * This is called by the lower layers when they wish to be reenabled * after an error. * Note that each layer would be calling the previous layer to reenable * first, before going ahead with their own re-enabling. */intpcibr_error_devenable(devfs_handle_t pconn_vhdl, int error_code){ pciio_info_t pciio_info = pciio_info_get(pconn_vhdl); pciio_slot_t pciio_slot = pciio_info_slot_get(pciio_info); pcibr_soft_t pcibr_soft = (pcibr_soft_t) pciio_info_mfast_get(pciio_info); ASSERT(error_code & IOECODE_PIO); /* If the error is not known to be a write, * we have to call devenable. * write errors are isolated to the bridge. */ if (!(error_code & IOECODE_WRITE)) { devfs_handle_t xconn_vhdl = pcibr_soft->bs_conn; int rc; rc = xtalk_error_devenable(xconn_vhdl, pciio_slot, error_code); if (rc != IOERROR_HANDLED) return rc; } pcibr_error_cleanup(pcibr_soft, error_code); return IOERROR_HANDLED;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -