blob: 7419c6094ed264f567f2238a91111075498768ce [file] [log] [blame] [edit]
// 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 *);