Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

riscv: plic: IRQs may not be enabled in non-zero HART #78138

Closed
ycsin opened this issue Sep 9, 2024 · 0 comments · Fixed by #78140
Closed

riscv: plic: IRQs may not be enabled in non-zero HART #78138

ycsin opened this issue Sep 9, 2024 · 0 comments · Fixed by #78140
Assignees
Labels
area: Interrupt Controller area: RISCV RISCV Architecture (32-bit & 64-bit) bug The issue is a bug, or the PR is fixing a bug Enhancement Changes/Updates/Additions to existing features priority: low Low impact/importance bug

Comments

@ycsin
Copy link
Member

ycsin commented Sep 9, 2024

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

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:

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.

@ycsin ycsin added Enhancement Changes/Updates/Additions to existing features area: RISCV RISCV Architecture (32-bit & 64-bit) area: Interrupt Controller labels Sep 9, 2024
@ycsin ycsin linked a pull request Sep 9, 2024 that will close this issue
@ycsin ycsin added the bug The issue is a bug, or the PR is fixing a bug label Sep 9, 2024
@mmahadevan108 mmahadevan108 added the priority: low Low impact/importance bug label Sep 10, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area: Interrupt Controller area: RISCV RISCV Architecture (32-bit & 64-bit) bug The issue is a bug, or the PR is fixing a bug Enhancement Changes/Updates/Additions to existing features priority: low Low impact/importance bug
Projects
None yet
6 participants