diff options
Diffstat (limited to 'lib/ultralib/src/os/setintmask.s')
| -rw-r--r-- | lib/ultralib/src/os/setintmask.s | 100 |
1 files changed, 69 insertions, 31 deletions
diff --git a/lib/ultralib/src/os/setintmask.s b/lib/ultralib/src/os/setintmask.s index f19ed17..b9f93b0 100644 --- a/lib/ultralib/src/os/setintmask.s +++ b/lib/ultralib/src/os/setintmask.s @@ -10,47 +10,85 @@ .text .set noreorder -LEAF(osSetIntMask) - mfc0 ta0, C0_SR +/** + * OSIntMask osSetIntMask(OSIntMask); + * + * Sets the interrupt enable mask for the current thread. External interrupts + * originating either in the CPU or the RCP may be "masked out" so that they + * are not handled. This is sometimes important for critical code sections + * that must not be interrupted. + * Interrupts that are not enabled in the global interrupt mask __OSGlobalIntMask + * cannot be set here. The global interrupt mask is OS-internal and is not + * expected to change during runtime. + * The returned value is the previous interrupt enable mask so that it can be + * restored later. + * + * @bug Some usage of the global interrupt mask is broken both in here and in the + * exception handler routines. + * While a thread is running, the C0_SR interrupt enable bits contain the + * interrupt enable bits for the current thread masked by the global + * interrupt mask. There is an attempt to recover only the original interrupt + * enable bits belonging to the thread itself using the operation + * (SR | ~__OSGlobalIntMask). + * However, this does not work as intended and can cause interrupts to end + * up enabled when not intended to be. The same issue is present for the + * RCP interrupt enable bits in MI_INTR_MASK_REG. + * This does not cause issues in practice as __OSGlobalIntMask is almost always + * OS_IM_ALL, so the operation is usually simply (SR | 0). + */ +LEAF(osSetIntMask) + /* Extract interrupt enable bits from current SR */ + mfc0 ta0, C0_SR - andi v0, ta0, OS_IM_CPU + andi v0, ta0, OS_IM_CPU - la t0, __OSGlobalIntMask - lw t3, 0(t0) - - xor t0, t3,-1 - andi t0, t0,(SR_IMASK) - or v0, v0,t0 + /* Get value of __OSGlobalIntMask */ + la t0, __OSGlobalIntMask + lw t3, 0(t0) - lw t2, PHYS_TO_K1(MI_INTR_MASK_REG) - beqz t2, 1f - srl t1, t3,0x10 + /* Bitwise-OR in the disabled CPU bits of __OSGlobalIntMask */ + xor t0, t3, ~0 + andi t0, t0, SR_IMASK + or v0, v0, t0 - xor t1, t1,-1 - andi t1, t1, MI_INTR_MASK - or t2, t2,t1 + /* Fetch MI_INTR_MASK_REG */ + lw t2, PHYS_TO_K1(MI_INTR_MASK_REG) + /* If there are RCP interrupts masked */ + beqz t2, 1f + srl t1, t3, 0x10 + + /* Bitwise-OR in the disabled RCP bits of __OSGlobalIntMask */ + xor t1, t1, ~0 + andi t1, t1, (RCP_IMASK >> RCP_IMASKSHIFT) + or t2, t2, t1 1: - sll t2, t2,0x10 - or v0, v0,t2 + /* Shift the RCP bits to not conflict with the CPU bits */ + sll t2, t2, RCP_IMASKSHIFT + /* OR the CPU and RCP bits together */ + or v0, v0, t2 - and t0, a0, MI_INTR_MASK<<0x10 - and t0, t0,t3 - srl t0, t0,0xf - lhu t2, __osRcpImTable(t0) - sw t2, PHYS_TO_K1(MI_INTR_MASK_REG) - - andi t0, a0, OS_IM_CPU - andi t1, t3, SR_IMASK - and t0, t0,t1 + /* Extract RCP interrupt enable bits from requested mask and mask with __OSGlobalIntMask */ + and t0, a0, RCP_IMASK + and t0, t0, t3 + /* Convert to a value for MI_INTR_MASK_REG and set it */ + srl t0, t0, (RCP_IMASKSHIFT - 1) + lhu t2, __osRcpImTable(t0) + sw t2, PHYS_TO_K1(MI_INTR_MASK_REG) - and ta0, ta0, ~SR_IMASK - or ta0, ta0,t0 + /* Extract CPU interrupt enable bits from requested mask and mask with __OSGlobalIntMask */ + andi t0, a0, OS_IM_CPU + andi t1, t3, SR_IMASK + and t0, t0, t1 - mtc0 ta0, C0_SR - nop + and ta0, ta0, ~SR_IMASK + /* Bitwise OR in the remaining bits of SR and set new SR */ + or ta0, ta0, t0 + + mtc0 ta0, C0_SR nop - jr ra nop + jr ra + nop END(osSetIntMask) .rdata |
