1 /*
2  * Copyright 2020, Data61, CSIRO (ABN 41 687 119 230)
3  * Copyright 2021, HENSOLDT Cyber
4  *
5  * SPDX-License-Identifier: GPL-2.0-only
6  */
7 
8 #pragma once
9 
10 #include <config.h>
11 #include <types.h>
12 
13 /*
14  * The "RISC-V Instruction Set Manual Volume II: Privileged Architecture" V1.10
15  * defines a platform-level interrupt controller (PLIC), that manages global
16  * interrupt on a RISC-V platform. A PLIC takes multiple interrupt sources,
17  * usually the platform peripherals, and delivers them to different hart
18  * contexts depending on the interrupt routing configuration. A hart is a
19  * logical CPU core, a hart context is a privilege level on a given hart,
20  * usually M-Mode or S-Mode.
21  *
22  * If an interrupt is pending for a particular hart context, the PLIC will
23  * assert the hart context's external interrupt line. The hart can then issue
24  * an interrupt claim to the PLIC, in response it will receive the pending
25  * interrupt with the highest priority. In multicore systems, if an interrupt is
26  * routed to multiple harts, the first hart to claim it gets to process it and
27  * subsequent harts won't see it. Upon claiming, the PLIC will de-assert a hart
28  * context's external interrupt line, even if there are more pending interrupt.
29  * When a  hart has finished processing a claimed interrupt, it notifies the
30  * PLIC about the completion. The PLIC will assert the hart context's external
31  * interrupt line if there are more pending interrupts.
32  *
33  * The RISC-V specification is not clear how interrupt priorities affect
34  * asserting a hart context's external interrupt line. The PLIC is supposed to
35  * deassert the line upon interrupt claiming. but could asserted it again if an
36  * interrupt of a higher priority arrives, even if the current claim has no been
37  * completed. This allows interrupt nesting. However, seL4 does not support this
38  * and cannot be interrupted when running in kernel mode. To achieve this, it
39  * keeps the S-Mode hart context's external interrupts masked until leaving to
40  * user space. However, great care must be taken if interrupt priorities are not
41  * implemented in the PLIC and the interrupt claim completion does not happen
42  * within the kernel's interrupt trap handling. If completion is done when the
43  * user mode ISR ack's the interrupt, the user mode driver that implements the
44  * ISR can simply block all other platform interrupts by not doing the ack.
45  * Luckily, this will not affect the timer used for the preemptive scheduler,
46  * because RISC-V has a dedicated timer interrupt that is separate from the
47  * external interrupt.
48  *
49  *
50  * This file defines the seL4 kernel's internal API for PLIC access that must be
51  * implemented by platform specific PLIC drivers.
52  * The implementation here are
53  * used on the 'spike' platform, which is just a RISC-V ISA reference
54  * implementation that does not have a PLIC to trigger external interrupts.
55  */
56 
57 
58 /*
59  * This function is called when an interrupt is pending. It claims an interrupt
60  * from the PLIC and returns it. If no interrupt could be claimed 'irqInvalid'
61  * is returned.
62  *
63  * @return     interrupt number or irqInvalid.
64  */
65 static inline irq_t plic_get_claim(void);
66 
67 /*
68  * This function is called to complete a claim process for an interrupt.
69  *
70  * @param[in]  irq  interrupt to complete.
71  */
72 static inline void plic_complete_claim(irq_t irq);
73 
74 /*
75 * This function is called to mask (disable) or unmasks (enable) an interrupt in
76 * the PLIC.
77  *
78  * @param[in]  disable  True to mask/disable, False to unmask/enable.
79  * @param[in]  irq      interrupt to mask/unmask.
80  */
81 static inline void plic_mask_irq(bool_t disable, irq_t irq);
82 
83 
84 #ifdef HAVE_SET_TRIGGER
85 /*
86  * If HAVE_SET_TRIGGER is defined, this function is called to configure an
87  * interrupt source on the PLIC as edge or level triggered. By default all
88  * interrupts should be configures to be edge triggered.
89  *
90  * @param[in]  irq             interrupt to set trigger mode for.
91  * @param[in]  edge_triggered  True for edge triggered, False for level
92  *                             triggered.
93  */
94 static inline void plic_irq_set_trigger(irq_t irq, bool_t edge_triggered);
95 #endif /* HAVE_SET_TRIGGER */
96 
97 /*
98  * This function is called during the boot process to perform hart specific
99  * PLIC initialisation. It is called as part of the core local initialisation
100  * process and runs before plic_init_controller() is called.
101  */
102 static inline void plic_init_hart(void);
103 
104 /*
105  * This function is called during the boot process to perform platform specific
106  * PLIC initialisation. It is called as part of the platform specific
107  * initialisation pprocess and runs after the core specific plic_init_hart() was
108  * called. In SMP configurations this is called before the secondary cores are
109  * released to start their boot process.
110  */
111 static inline void plic_init_controller(void);
112