| // 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 *); |