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