| // Copyright Microsoft and CHERIoT Contributors. | |
| // SPDX-License-Identifier: MIT | |
| #pragma once | |
| /** | |
| * This file describes the interfaces for compartments to wait for interrupts. | |
| * Interrupts are exposed to compartments as futexes that contain the count of | |
| * the number of times that an interrupt has fired. This count can wrap but | |
| * that should not be visible in practice (if will be observable only if one | |
| * thread handles a specific interrupt 2^32 times in between another thread | |
| * finishing handling that interrupt and waiting again). | |
| * | |
| * The flow for waiting for an interrupt involves the following steps: | |
| * | |
| * 1. Request the futex word for a particular from the scheduler. | |
| * 2. Wait on the futex. | |
| * 3. Read the value of the futex word. | |
| * 4. Handle whatever the interrupt was raised for. | |
| * 5. Mark the interrupt as completed. | |
| * 6. Loop from step 2, using the value read from step 3. | |
| * | |
| * If the interrupt fires between steps 5 and 6 then the futex word will not | |
| * match the value read in step 3 and the futex wait will return immediately. | |
| * | |
| * The first time that step 2 is reached, the expected value for the futex | |
| * should be 0. This ensures that you acknowledge any interrupt that happened | |
| * in between the system starting and your registering interest in the | |
| * interrupt. | |
| * | |
| * Note that step 2 may use a multiwaiter, rather than a single futex_wait | |
| * call, if you wish to wait for one of multiple event sources. | |
| * | |
| * Both step 1 and 5 require an authorising capability, as described below. | |
| */ | |
| #include <compartment.h> | |
| #include <stdbool.h> | |
| /** | |
| * The names of interrupts. This is populated from the interrupts array in the | |
| * board configuration JSON and allows code to refer to interrupt numbers by | |
| * symbolic values. | |
| */ | |
| enum InterruptName : uint16_t | |
| { | |
| #ifdef CHERIOT_INTERRUPT_NAMES | |
| CHERIOT_INTERRUPT_NAMES | |
| #endif | |
| }; | |
| /** | |
| * Structure for authorising access to a specific interrupt. | |
| */ | |
| struct InterruptCapabilityState | |
| { | |
| /** | |
| * The interrupt number that this refers to. | |
| */ | |
| enum InterruptName interruptNumber; | |
| /** | |
| * Does this authorise accessing the futex for monitoring the interrupt? | |
| */ | |
| bool mayWait; | |
| /** | |
| * Does this authorise acknowledging the interrupt? | |
| */ | |
| bool mayComplete; | |
| }; | |
| /** | |
| * Helper macro to forward declare an interrupt capability. | |
| */ | |
| #define DECLARE_INTERRUPT_CAPABILITY(name) \ | |
| DECLARE_STATIC_SEALED_VALUE( \ | |
| struct InterruptCapabilityState, sched, InterruptKey, name); | |
| /** | |
| * Helper macro to define an interrupt capability. The three arguments after | |
| * the name are the interrupt number and two boolean values indicating whether | |
| * it may be used with `interrupt_futex_get` and with `interrupt_complete`, | |
| * respectively. | |
| */ | |
| #define DEFINE_INTERRUPT_CAPABILITY(name, number, mayWait, mayComplete) \ | |
| DEFINE_STATIC_SEALED_VALUE(struct InterruptCapabilityState, \ | |
| sched, \ | |
| InterruptKey, \ | |
| name, \ | |
| number, \ | |
| mayWait, \ | |
| mayComplete); | |
| /** | |
| * Helper macro to define an interrupt capability without a separate | |
| * declaration. The arguments are the same as those for | |
| * `DEFINE_INTERRUPT_CAPABILITY`. | |
| */ | |
| #define DECLARE_AND_DEFINE_INTERRUPT_CAPABILITY( \ | |
| name, number, mayWait, mayComplete) \ | |
| DECLARE_INTERRUPT_CAPABILITY(name); \ | |
| DEFINE_INTERRUPT_CAPABILITY(name, number, mayWait, mayComplete) | |
| struct SObjStruct; | |
| /** | |
| * Request the futex associated with an interrupt. The argument is a sealed | |
| * capability to an `InterruptCapabilityState` structure that must have | |
| * `mayWait` flag set to true. This is sealed with the `InterruptKey` type | |
| * exposed from the scheduler compartment. | |
| * | |
| * Returns `nullptr` on failure. | |
| */ | |
| __cheri_compartment("sched") const uint32_t *interrupt_futex_get( | |
| struct SObjStruct *); | |
| /** | |
| * Acknowledge the end of handling an interrupt. The argument is a sealed | |
| * capability to an `InterruptCapabilityState` structure that must have | |
| * `mayWait` flag set to true. This is sealed with the `InterruptKey` type | |
| * exposed from the scheduler compartment. | |
| * | |
| * Returns 0 on success or `-EPERM` if the argument does not authorise this | |
| * operation. | |
| */ | |
| __cheri_compartment("sched") int interrupt_complete(struct SObjStruct *); |