[RISC-V] Linux kernel - local_irq_enable() and sstatus.sie (Enable local interrupt)
The Linux kernel provides API functions to disable or enable local interrupts:
Code Analysis: preempt_schedule_irq() Function
Now, let's analyze part of the preempt_schedule_irq() function:.
asmlinkage __visible void __sched preempt_schedule_irq(void)
{
enum ctx_state prev_state;
/* Catch callers which need to be fixed */
BUG_ON(preempt_count() || !irqs_disabled());
prev_state = exception_enter();
do {
preempt_disable();
local_irq_enable();
__schedule(SM_PREEMPT);
local_irq_disable();
sched_preempt_enable_no_resched();
} while (need_resched());
exception_exit(prev_state);
}
In this code, the local_irq_enable() function is called to enable local interrupts while executing the schedule() function. After schedule() finishes, local_irq_disable() is called to disable local interrupts again.
Disassembly Analysis
Now, let’s compile this code using a RISC-V-based compiler and analyze the disassembled output:
SP:FFFFFFFF80D8E526|preempt_schedule_irq: lw a5,0x8(tp) ; a5,8(tp)
SP:FFFFFFFF80D8E52A| c.bnez a5,0xFFFFFFFF80D8E55E
[...]
SP:FFFFFFFF80D8E53C| csrsi sstatus,0x2 /// <<--
SP:FFFFFFFF80D8E540| c.li a0,0x1
SP:FFFFFFFF80D8E542| auipc ra,0xFFFFF ; ra,1048575
SP:FFFFFFFF80D8E546| jalr ra,0x516(ra) ; ra,1302(ra) ; __schedule
SP:FFFFFFFF80D8E54A| csrci sstatus,0x2 /// <<--
From this output, we can see that the csrsi sstatus,0x2 and csrci sstatus,0x2 instructions are used. These instructions set (1) or clear (0) the second bit of the sstatus register, respectively.
Internal Implementation of local_irq_enable()
The local_irq_enable() function is architecture-dependent. Let’s analyze its implementation step by step.
local_irq_enable() function is declared using the following macro:
// include/linux/irqflags.h
#define local_irq_enable() do { raw_local_irq_enable(); } while (0)
This function is replaced by raw_local_irq_enable() during compliatin. Let's analyze raw_local_irq_enable() function:
// include/linux/irqflags.h
#define raw_local_irq_enable() arch_local_irq_enable()
This function is then replaced by arch_local_irq_enable().
The Linux kernel often uses macros to ensure that these kinds of macro function, even through it is executing for different architectures.
Let's look into arch_local_irq_enable() function:
Recommended by LinkedIn
// arch/riscv/include/asm/irqflags.h
static inline void arch_local_irq_enable(void)
{
csr_set(CSR_STATUS, SR_IE);
}
This function is implemented specifically for the RISC-V architecture, if we refer to the source path. This inline type function sets the SR_IE bit in the sstatus register to enable interrupts.
Let's review the code that represents SR_IE and SR_SIE:
// arch/riscv/include/asm/csr.h
#define SR_IE SR_SIE
[...]
#define SR_SIE _AC(0x00000002, UL) /* Supervisor Interrupt Enable */
Here, SR_IE is defined as 0x2. This matches the instruction csrsi sstatus,0x2, which sets the second bit of the sstatus register.
Summary of Code Analysis:
The local_irq_enable() function enables local interrupts.
In the RISC-V architecture, the "csrsi sstatus,0x2" instruction is actual code for local_irq_enable() function. This instruction sets the second bit of the sstatus register to 1.
Hardware Debugging: Checking the sstatus Register
No matter how we try to analyze instruction in RISC-V, it is hard to identify the bit flag inside sstatus for "csrsi sstatus,0x2" instruction. We have powerful hardware debugging - TRACE32 that shows a set of CSR at RISC-V architecture level.
Let's take a look at the following screen shot:
What we can see above is that sstatus register value changes to 0x2 after running "csrsi sstatus,0x2". As a result, the local interrupt is enabled. The reason is - if we set sstatus.sie to 1, the local interrupt is enabled.
Let's take a look at the another screen shot:
What we can see above is that sstatus register value changes to 0x0 after running csrci sstatus,0x2. As a result, the local interrupt is disabled. The reason is - if we set sstatus.sie to 0, the local interrupt is disabled.
Insight:
Through debugging, we have gained a clear understanding of how the local_irq_enable() function works and how the sstatus register plays a role in enabling and disabling local interrupts. What is analyze helps us understand the interaction between software and hardware when managing interrupts.
Linux Kernel| OS Internals | Virtualization | Embedded Systems | Secure Boot | ARM/x86 | eBPF | Device Drivers
1moAustin Kim Great post about what and how mechanisms regarding the local interrupts in the linux kernel. It would be awesome if you could also add, why we need to enable/disable local interrupts and the use cases 🙂