-
Notifications
You must be signed in to change notification settings - Fork 8k
Description
Is your enhancement proposal related to a problem? Please describe.
All enabled HARTs should be able to handle the IRQ. #65922 is supposed to enable the IRQ on all HARTs, however the patch assumed that the first HART of a SoC has 1 context, whereas the other HARTs have 2 contexts each, i.e.:
HART | CONTEXT(s) |
---|---|
0 | M |
1 | M,S |
2 | M,S |
N | M,S |
zephyr/drivers/interrupt_controller/intc_plic.c
Lines 112 to 134 in 5b15751
static inline uint32_t get_first_context(uint32_t hartid) | |
{ | |
return hartid == 0 ? 0 : (hartid * 2) - 1; | |
} | |
static inline mem_addr_t get_context_en_addr(const struct device *dev, uint32_t cpu_num) | |
{ | |
const struct plic_config *config = dev->config; | |
uint32_t hartid; | |
/* | |
* We want to return the irq_en address for the context of given hart. | |
* If hartid is 0, we return the devices irq_en property, job done. If it is | |
* greater than zero, we assume that there are two context's associated with | |
* each hart: M mode enable, followed by S mode enable. We return the M mode | |
* enable address. | |
*/ | |
#if CONFIG_SMP | |
hartid = _kernel.cpus[cpu_num].arch.hartid; | |
#else | |
hartid = arch_proc_id(); | |
#endif | |
return config->irq_en + get_first_context(hartid) * CONTEXT_ENABLE_SIZE; | |
} |
This means that for SoC that doesn't fit into that assumption, the implementation will not work, i.e. QEMU RISC-V SoCs, which has 2 contexts per HART:
zephyr/dts/riscv/qemu/virt-riscv.dtsi
Lines 164 to 182 in 5b15751
plic: interrupt-controller@c000000 { | |
riscv,max-priority = <7>; | |
riscv,ndev = < 1024 >; | |
reg = <0x0c000000 0x04000000>; | |
interrupts-extended = < | |
&hlic0 0x0b &hlic0 0x09 | |
&hlic1 0x0b &hlic1 0x09 | |
&hlic2 0x0b &hlic2 0x09 | |
&hlic3 0x0b &hlic3 0x09 | |
&hlic4 0x0b &hlic4 0x09 | |
&hlic5 0x0b &hlic5 0x09 | |
&hlic6 0x0b &hlic6 0x09 | |
&hlic7 0x0b &hlic7 0x09 | |
>; | |
interrupt-controller; | |
compatible = "sifive,plic-1.0.0"; | |
#address-cells = < 0x00 >; | |
#interrupt-cells = < 0x02 >; | |
}; |
HART | CONTEXT(s) |
---|---|
0 | M(11),S(9) |
1 | M(11),S(9) |
2 | M(11),S(9) |
N | M(11),S(9) |
The current PLIC driver will enable the interrupt on context 0, 1, 3, .., (hartid * 2 - 1) of the QEMU SoC, which corresponds to:
HART | CONTEXT(s) |
---|---|
0 | M |
1 | S |
2 | S |
N | S |
as a result, machine external interrupts, like UART, only works on hartid=0
This can be tested by building the hello_world
with SHELL
enabled (to test the UART interrupt), but boot it on hartid=1
instead of 0
:
west build -b qemu_riscv64 -p auto -t run zephyr/samples/hello_world -- \
-DCONFIG_SHELL=y \
-DCONFIG_RV_BOOT_HART=1 \
-DCONFIG_MP_MAX_NUM_CPUS=2
Since UART IRQ isn't enabled on the context M of HART 1, there will be no prompt on the terminal, and typing in the terminal will have no response from the device:
*** Booting Zephyr OS build v3.7.0-2380-g5b15751d0bcb ***
Hello World! qemu_riscv64/qemu_virt_riscv64
The workaround for QEMU RISC-V SoCs is simple:
--- a/drivers/interrupt_controller/intc_plic.c
+++ b/drivers/interrupt_controller/intc_plic.c
@@ -111,7 +111,7 @@ static inline uint32_t get_plic_enabled_size(const struct device *dev)
static inline uint32_t get_first_context(uint32_t hartid)
{
- return hartid == 0 ? 0 : (hartid * 2) - 1;
+ return hartid * 2;
}
static inline mem_addr_t get_context_en_addr(const struct device *dev, uint32_t cpu_num)
Run the same build command again, the terminal will be responsive this time around:
*** Booting Zephyr OS build v3.7.0-2380-g5b15751d0bcb ***
Hello World! qemu_riscv64/qemu_virt_riscv64
uart:~$ blahblahblah
However, this workaround only works on SoC where each hart has 2 contexts.
Describe the solution you'd like
Update the implementation so that the interrupt will work regardless of the mapping of the contexts.
Describe alternatives you've considered
Not fixing it.