Force machine mode on interrupt exit for context switches

The first attempt to solve illegal instruction expections was made in commit
b875108cd0
 It seemed as this "fixed" the issue, but merely added delays in the code
 which prevented the error to appear in lucky circumstances. Interesting that this code worked in the first place.

Root cause for the expections where write attempts to mstatus in
user privilege mode which raised the illegal instruction exception which
is in spec with the risc-v privileged isa and documented in the
bumbleebee core architecture manual by nucleisys. The solution is
to never enter user mode by forceing mcause.mpp to 0x3
before calling mret when exiting the interrupt handler
for context switching.
This commit is contained in:
Stefan Kerkmann 2021-04-17 19:34:27 +02:00
parent c1dfb65aa0
commit 9a64f5c17c
1 changed files with 39 additions and 44 deletions

View File

@ -64,20 +64,6 @@
csrs CSR_MSTATUS, MSTATUS_MIE csrs CSR_MSTATUS, MSTATUS_MIE
.endm .endm
# Clear previous machine interrupt enable bit in mstatus (mstatus.mpie).
# On machine return (mret) mstatus.mie is assigned this value.
# Clearing this bit disables interrupts when leaving interrupt processing mode.
.macro DISABLE_MPIE
li a0, MSTATUS_MPIE
csrc CSR_MSTATUS, a0
.endm
# Set previous machine interrupt enable bit in mstatus (mstatus.mpie).
.macro ENABLE_MPIE
li a0, MSTATUS_MPIE
csrs CSR_MSTATUS, a0
.endm
# -------------------------------------------------------------------------- # --------------------------------------------------------------------------
# Interrupt context save macro. Saves all caller save registers # Interrupt context save macro. Saves all caller save registers
# and status csr registers on the stack. # and status csr registers on the stack.
@ -174,6 +160,31 @@
mret mret
.option pop .option pop
# --------------------------------------------------------------------------
# Start a thread by invoking its work function.
#
# Threads execution starts here, the code leaves the system critical zone
# and then jumps into the thread function passed in register S0. The
# register S1 contains the thread parameter. The function chThdExit() is
# called on thread function return.
# --------------------------------------------------------------------------
.globl _port_thread_start
_port_thread_start:
#if CH_DBG_SYSTEM_STATE_CHECK
jal ra, _dbg_check_unlock
#endif
#if CH_DBG_STATISTICS
jal ra, _stats_stop_measure_crit_thd
#endif
ENABLE_MIE
mv a0, s1
jalr ra, s0
li a0, 0 # MSG_OK
jal ra, chThdExit
_zombies:
j _zombies
# -------------------------------------------------------------------------- # --------------------------------------------------------------------------
# Performs a context switch between two threads. # Performs a context switch between two threads.
# a0 = ntp, a1 = otp # a0 = ntp, a1 = otp
@ -233,31 +244,6 @@ _port_switch:
ret ret
.option pop .option pop
# --------------------------------------------------------------------------
# Start a thread by invoking its work function.
#
# Threads execution starts here, the code leaves the system critical zone
# and then jumps into the thread function passed in register S0. The
# register S1 contains the thread parameter. The function chThdExit() is
# called on thread function return.
# --------------------------------------------------------------------------
.globl _port_thread_start
_port_thread_start:
#if CH_DBG_SYSTEM_STATE_CHECK
jal ra, _dbg_check_unlock
#endif
#if CH_DBG_STATISTICS
jal ra, _stats_stop_measure_crit_thd
#endif
ENABLE_MIE
mv a0, s1
jalr ra, s0
li a0, 0 # MSG_OK
jal ra, chThdExit
_zombies:
j _zombies
# -------------------------------------------------------------------------- # --------------------------------------------------------------------------
# IRQ entry point # IRQ entry point
# -------------------------------------------------------------------------- # --------------------------------------------------------------------------
@ -285,8 +271,20 @@ _irq_handler:
la a0, _port_switch_from_isr la a0, _port_switch_from_isr
csrw mepc, a0 csrw mepc, a0
# Interrupt handling and context restoring is handled differently in nucleisys cores.
# mstatus.mpie and mstatus.mpp are mirror fields of mcause.mpie and mcause.mpp.
# Therefore we directly set the bits in mcause and not mstatus.
# See https://doc.nucleisys.com/nuclei_spec/isa/core_csr.html#mcause
# Context switch is a critical section, so disable interrupts on return. # Context switch is a critical section, so disable interrupts on return.
DISABLE_MPIE # Clear mcause.mpie.
li a0, 0x8000000
csrc mcause, a0
# Set previous privelege mode to machine mode to enforce it on return.
# Set mcause.mpp to 0x3 (== machine mode).
li a0, 0x30000000
csrs mcause, a0
mret mret
@ -323,15 +321,12 @@ _port_switch_from_isr:
jal ra, _stats_stop_measure_crit_thd jal ra, _stats_stop_measure_crit_thd
#endif #endif
# Enable interrupts after leaving the interrupt handler
ENABLE_MPIE
.globl _port_exit_from_isr .globl _port_exit_from_isr
_port_exit_from_isr: _port_exit_from_isr:
# Restore caller registers and csr registers from the thread stack # Restore caller registers and csr registers from the thread stack
RESTORE_CONTEXT RESTORE_CONTEXT
# Leave machine mode and return to address stored in mepc # Leave interrupt handling and return to address stored in mepc.
mret mret
.option pop .option pop