I'm running into an odd problem with the DBE exception handler.
I've got an IO device that on some error conditions causes a bus fault
on access. The driver I have that accesses this device directs
all reads/writes through a wrapper function to handle potential bus
faults (see the read version below).
If the device causes a DBE on access, do_be() looks up the EPC in the
DBE table and successfully corrects the PC to handle the fault.
This works most of the time, however on about 1 out of 100 faults the
SP register is saved and restored incorrectly. When control returns to
the faulting function SP is 304 bytes less than where it should
be and as expected things go down hill from there.
304 bytes is PT_SIZE (the amount of space for saved registers)
I suspect something is wrong with except_vec3_generic() or
handle_dbe() but the only thing that comes to mind is potential nested
interrupts/exceptions that would clobber K0/K1. The fact that SP is
off by 304 bytes seems to indicate it saved twice but only restored
CPU: SiByte BCM1250 (both A8 and B2 stepping tested)
Kernel: linux 2.6.12 (yes, I know it's old), 64bit kernel
Config: Occurs with and without SMP and with and without PREEMPT
I took a quick look to see if this area has changed between 2.6.12 and
2.6.17 and the only part I see is get_saved_sp() and that should
only effect faults from userspace. All the faults I'm getting are
from a kernel-mode driver.
I've walked through one (succesful) DBE fault from this driver using a
JTAG debugger and everything looks to run exactly as expected. I have
yet to catch a failing one with the debugger except for after the
restore is finished but that's too late.
Anyone have any thoughts on this issue?
read wrapper function is:
/* int sb_io_trap_readb(unsigned char *value, const volatile void *addrs); */
/* do the read, handle error */
8: lb t0, (a1)
9: add t0, t0, zero /* consume read */
PTR 8b, 1f
PTR 9b, 1f
* write out to the caller's pointer, if this fails it's a bug
* and we should fault as normal
sb t0, (a0)
/* all good, return success */
move v0, zero
/* fault handler, return -EIO */
1: jr ra
li v0, -EIO