| #pragma once |
| /** |
| * FreeRTOS semaphore compatibility layer. This maps FreeRTOS semaphore types |
| * to their CHERIoT RTOS equivalents. |
| * |
| * There is some overhead from dynamic dispatch that can be avoided if only one |
| * of the FreeRTOS semaphore types needs to be supported in a particular |
| * component. You can enable individual semaphore types by defining the |
| * following macros: |
| * |
| * - `CHERIOT_EXPOSE_FREERTOS_SEMAPHORE`: Enable counting and binary semaphores. |
| * - `CHERIOT_EXPOSE_FREERTOS_MUTEX`: Enable non-recursive, priority-inheriting |
| * mutexes. |
| * - `CHERIOT_EXPOSE_FREERTOS_RECURSIVE_MUTEX`: Enable recursive mutexes. |
| */ |
| #include "FreeRTOS.h" |
| #include <locks.h> |
| #include <stdatomic.h> |
| |
| #if !defined(CHERIOT_EXPOSE_FREERTOS_SEMAPHORE) && \ |
| !defined(CHERIOT_EXPOSE_FREERTOS_MUTEX) && \ |
| !defined(CHERIOT_EXPOSE_FREERTOS_RECURSIVE_MUTEX) |
| # error At least one out of CHERIOT_EXPOSE_FREERTOS_SEMAPHORE, \ |
| CHERIOT_EXPOSE_FREERTOS_MUTEX, or CHERIOT_EXPOSE_FREERTOS_RECURSIVE_MUTEX \ |
| must be defined. |
| #endif |
| |
| #ifdef CHERIOT_EXPOSE_FREERTOS_SEMAPHORE |
| # define CHERIOT_FREERTOS_SEMAPHORE_CASE(x) \ |
| case CheriotFreeRTOS_Semaphore: \ |
| x; \ |
| break; |
| #else |
| # define CHERIOT_FREERTOS_SEMAPHORE_CASE(x) |
| #endif |
| |
| #ifdef CHERIOT_EXPOSE_FREERTOS_MUTEX |
| # define CHERIOT_FREERTOS_MUTEX_CASE(x) \ |
| case CheriotFreeRTOS_Mutex: \ |
| x; \ |
| break; |
| #else |
| # define CHERIOT_FREERTOS_MUTEX_CASE(x) |
| #endif |
| |
| #ifdef CHERIOT_EXPOSE_FREERTOS_RECURSIVE_MUTEX |
| # define CHERIOT_FREERTOS_RECURSIVE_MUTEX_CASE(x) \ |
| case CheriotFreeRTOS_RecursiveMutex: \ |
| x; \ |
| break; |
| #else |
| # define CHERIOT_FREERTOS_RECURSIVE_MUTEX_CASE(x) |
| #endif |
| |
| /** |
| * Enumeration used to differentiate the different kinds of FreeRTOS semaphores |
| * in this compatibility layer. |
| */ |
| enum CheriotFreeRTOS_SemaphoreKind : uint8_t |
| { |
| #ifdef CHERIOT_EXPOSE_FREERTOS_SEMAPHORE |
| /** |
| * A FreeRTOS counting or binary semaphore. |
| */ |
| CheriotFreeRTOS_Semaphore, |
| #endif |
| #ifdef CHERIOT_EXPOSE_FREERTOS_MUTEX |
| /** |
| * A non-recursive, priority-inheriting mutex. |
| */ |
| CheriotFreeRTOS_Mutex, |
| #endif |
| #ifdef CHERIOT_EXPOSE_FREERTOS_RECURSIVE_MUTEX |
| /** |
| * A recursive mutex. |
| */ |
| CheriotFreeRTOS_RecursiveMutex, |
| #endif |
| }; |
| |
| /** |
| * Type used to define space for storing any of the exposed FreeRTOS semaphore |
| * types. |
| */ |
| typedef struct |
| { |
| /** |
| * The state for the various kinds of FreeRTOS semaphores. |
| */ |
| union |
| { |
| #ifdef CHERIOT_EXPOSE_FREERTOS_RECURSIVE_MUTEX |
| struct RecursiveMutexState recursiveMutex; |
| #endif |
| #ifdef CHERIOT_EXPOSE_FREERTOS_MUTEX |
| struct FlagLockState mutex; |
| #endif |
| #ifdef CHERIOT_EXPOSE_FREERTOS_SEMAPHORE |
| struct CountingSemaphoreState semaphore; |
| #endif |
| }; |
| #if (defined(CHERIOT_EXPOSE_FREERTOS_SEMAPHORE) + \ |
| defined(CHERIOT_EXPOSE_FREERTOS_MUTEX) + \ |
| defined(CHERIOT_EXPOSE_FREERTOS_RECURSIVE_MUTEX)) > 1 |
| # define CHERIOT_FREERTOS_SEMAPHORE_SWITCH(x) switch (x->kind) |
| /** |
| * The discriminator for the union above. If only one kind of semaphore is |
| * supported, this is omitted. |
| */ |
| enum CheriotFreeRTOS_SemaphoreKind kind; |
| # define CHERIOT_FREERTOS_SEMAPHORE_KIND_SET(x, y) x->kind = y |
| #else |
| # if defined(CHERIOT_EXPOSE_FREERTOS_RECURSIVE_MUTEX) |
| # define CHERIOT_FREERTOS_SEMAPHORE_SWITCH(x) \ |
| switch (CheriotFreeRTOS_RecursiveMutex) |
| # elif defined(CHERIOT_EXPOSE_FREERTOS_MUTEX) |
| # define CHERIOT_FREERTOS_SEMAPHORE_SWITCH(x) \ |
| switch (CheriotFreeRTOS_Mutex) |
| # elif defined(CHERIOT_EXPOSE_FREERTOS_SEMAPHORE) |
| # define CHERIOT_FREERTOS_SEMAPHORE_SWITCH(x) \ |
| switch (CheriotFreeRTOS_Semaphore) |
| # endif |
| |
| # define CHERIOT_FREERTOS_SEMAPHORE_KIND_SET(x, y) |
| #endif |
| } StaticSemaphore_t; |
| |
| /** |
| * Type for a handle to a counting semaphore. |
| */ |
| typedef StaticSemaphore_t *SemaphoreHandle_t; |
| |
| #ifdef CHERIOT_EXPOSE_FREERTOS_SEMAPHORE |
| /** |
| * Initialise a statically allocated semaphore. The initial value and maximum |
| * are specified with `uxInitialCount` and `uxMaxCount` respectively. |
| * |
| * Returns a pointer to the semaphore. |
| */ |
| __always_inline static inline SemaphoreHandle_t |
| xSemaphoreCreateCountingStatic(UBaseType_t uxMaxCount, |
| UBaseType_t uxInitialCount, |
| StaticSemaphore_t *pxSemaphoreBuffer) |
| { |
| pxSemaphoreBuffer->semaphore.maxCount = uxMaxCount; |
| # ifdef __cplusplus |
| pxSemaphoreBuffer->semaphore.count = uxInitialCount; |
| # else |
| // In C mode, we don't want a variable-sized atomic store here! |
| atomic_init(&pxSemaphoreBuffer->semaphore.count, uxInitialCount); |
| # endif |
| CHERIOT_FREERTOS_SEMAPHORE_KIND_SET(pxSemaphoreBuffer, |
| CheriotFreeRTOS_Semaphore); |
| return pxSemaphoreBuffer; |
| } |
| |
| # ifndef CHERIOT_NO_AMBIENT_MALLOC |
| /** |
| * Allocate a counting semaphore on the heap. The initial value and maximum are |
| * specified with `uxInitialCount` and `uxMaxCount` respectively. |
| * |
| * Returns a pointer to the semaphore on success, NULL on allocation failure. |
| */ |
| __always_inline static inline SemaphoreHandle_t |
| xSemaphoreCreateCounting(UBaseType_t uxMaxCount, UBaseType_t uxInitialCount) |
| { |
| SemaphoreHandle_t semaphore = |
| (SemaphoreHandle_t)malloc(sizeof(StaticSemaphore_t)); |
| if (semaphore != NULL) |
| { |
| xSemaphoreCreateCountingStatic(uxMaxCount, uxInitialCount, semaphore); |
| } |
| return semaphore; |
| } |
| # endif |
| |
| /** |
| * Create a heap-allocated binary semaphore. |
| * |
| * Binary semaphores are implemented as counting semaphores with a maximum count |
| * of 1. |
| */ |
| __always_inline static inline SemaphoreHandle_t xSemaphoreCreateBinary() |
| { |
| return xSemaphoreCreateCounting(1, 0); |
| } |
| |
| /** |
| * Create a statically allocated binary semaphore. |
| * |
| * Binary semaphores are implemented as counting semaphores with a maximum count |
| * of 1. |
| */ |
| __always_inline static inline SemaphoreHandle_t |
| xSemaphoreCreateBinaryStatic(StaticSemaphore_t *pxSemaphoreBuffer) |
| { |
| return xSemaphoreCreateCountingStatic(1, 0, pxSemaphoreBuffer); |
| } |
| #endif |
| |
| #ifdef CHERIOT_EXPOSE_FREERTOS_MUTEX |
| /** |
| * Create statically allocated mutex. |
| */ |
| __always_inline static inline SemaphoreHandle_t |
| xSemaphoreCreateMutexStatic(StaticSemaphore_t *pxMutexBuffer) |
| { |
| pxMutexBuffer->mutex.lockWord = 0; |
| CHERIOT_FREERTOS_SEMAPHORE_KIND_SET(pxMutexBuffer, CheriotFreeRTOS_Mutex); |
| return pxMutexBuffer; |
| } |
| |
| # ifndef CHERIOT_NO_AMBIENT_MALLOC |
| /** |
| * Create a heap-allocated mutex. |
| */ |
| __always_inline static inline SemaphoreHandle_t xSemaphoreCreateMutex() |
| { |
| SemaphoreHandle_t semaphore = |
| (SemaphoreHandle_t)malloc(sizeof(StaticSemaphore_t)); |
| if (semaphore != NULL) |
| { |
| xSemaphoreCreateMutexStatic(semaphore); |
| } |
| return semaphore; |
| } |
| # endif |
| #endif |
| |
| #ifdef CHERIOT_EXPOSE_FREERTOS_RECURSIVE_MUTEX |
| |
| /** |
| * Create a statically allocated recursive mutex. |
| */ |
| __always_inline static inline SemaphoreHandle_t |
| xSemaphoreCreateRecursiveMutexStatic(StaticSemaphore_t *pxMutexBuffer) |
| { |
| pxMutexBuffer->recursiveMutex.lock.lockWord = 0; |
| pxMutexBuffer->recursiveMutex.depth = 0; |
| CHERIOT_FREERTOS_SEMAPHORE_KIND_SET(pxMutexBuffer, |
| CheriotFreeRTOS_RecursiveMutex); |
| return pxMutexBuffer; |
| } |
| |
| # ifndef CHERIOT_NO_AMBIENT_MALLOC |
| /** |
| * Create a heap-allocated recursive mutex. |
| */ |
| __always_inline static inline SemaphoreHandle_t |
| xSemaphoreCreateRecursiveMutex(void) |
| { |
| SemaphoreHandle_t semaphore = |
| (SemaphoreHandle_t)malloc(sizeof(StaticSemaphore_t)); |
| if (semaphore != NULL) |
| { |
| xSemaphoreCreateRecursiveMutexStatic(semaphore); |
| } |
| return semaphore; |
| } |
| # endif |
| #endif |
| |
| /** |
| * Delete a heap-allocated semaphore, of any kind. |
| * |
| * Note: As on FreeRTOS, if there are waiters blocked on this semaphore then |
| * they will remain blocked until their timeout (if ever). |
| */ |
| __always_inline static inline void |
| vSemaphoreDelete(SemaphoreHandle_t xSemaphore) |
| { |
| free(xSemaphore); |
| } |
| |
| /** |
| * Get the current owner (thread ID) of a mutex. If this is a counting |
| * semaphore, or if the lock is not held, returns zero. |
| * |
| * Note: This API is inherently somewhat racy. If this returns the current |
| * thread's thread ID, then it cannot change, but any other return value may |
| * change between the time it is returned and the time the caller inspects it. |
| */ |
| __always_inline static inline TaskHandle_t |
| xSemaphoreGetMutexHolder(SemaphoreHandle_t xMutex) |
| { |
| CHERIOT_FREERTOS_SEMAPHORE_SWITCH(xMutex) |
| { |
| CHERIOT_FREERTOS_SEMAPHORE_CASE(return 0;) |
| CHERIOT_FREERTOS_RECURSIVE_MUTEX_CASE( |
| return xMutex->recursiveMutex.lock.lockWord & 0xffff;) |
| CHERIOT_FREERTOS_MUTEX_CASE(return xMutex->mutex.lockWord & 0xffff;) |
| } |
| return 0; |
| } |
| |
| /** |
| * Returns the current count of a counting semaphore, or whether a mutex or |
| * binary semaphore is available |
| * |
| * Note: This API is inherently racy. Unless run with interrupts disabled, |
| * there is no guarantee that the value returned is still valid by the time the |
| * caller inspects it. |
| */ |
| static inline UBaseType_t uxSemaphoreGetCount(SemaphoreHandle_t xSemaphore) |
| { |
| CHERIOT_FREERTOS_SEMAPHORE_SWITCH(xSemaphore) |
| { |
| CHERIOT_FREERTOS_SEMAPHORE_CASE(return xSemaphore->semaphore.count;) |
| CHERIOT_FREERTOS_RECURSIVE_MUTEX_CASE( |
| return xSemaphore->recursiveMutex.lock.lockWord != 0;) |
| CHERIOT_FREERTOS_MUTEX_CASE(return xSemaphore->mutex.lockWord != 0;) |
| } |
| return 0; |
| } |
| |
| /** |
| * A semaphore get (down) operation. If the semaphore value is zero, this can |
| * block for up to `xTicksToWait` ticks. Returns true if the semaphore was |
| * obtained, false if the timeout expired. |
| */ |
| static inline _Bool xSemaphoreTake(SemaphoreHandle_t xSemaphore, |
| TickType_t xTicksToWait) |
| { |
| Timeout t = {0, xTicksToWait}; |
| int ret = -1; |
| CHERIOT_FREERTOS_SEMAPHORE_SWITCH(xSemaphore) |
| { |
| CHERIOT_FREERTOS_SEMAPHORE_CASE( |
| ret = semaphore_get(&t, &xSemaphore->semaphore);) |
| CHERIOT_FREERTOS_MUTEX_CASE( |
| ret = flaglock_priority_inheriting_trylock(&t, &xSemaphore->mutex);) |
| // Recursive mutexes are not supposed to use this interface. |
| CHERIOT_FREERTOS_RECURSIVE_MUTEX_CASE(;); |
| } |
| return ret == 0; |
| } |
| |
| /** |
| * Take a semaphore from code that may run in an ISR. CHERIoT RTOS does not |
| * permit code to run in ISRs and so this simply calls the normal code paths. |
| * |
| * The second parameter is a pointer to a variable that will be set to true if |
| * the caller needs to yield to wake a higher-priority task. This is |
| * unconditionally false on CHERIoT RTOS because the scheduler will wake any |
| * threads that are waiting on a semaphore. It is necessary on FreeRTOS |
| * because code in ISRs cannot call into the scheduler. |
| */ |
| __always_inline static inline _Bool __attribute__((overloadable)) |
| xSemaphoreTakeFromISR(SemaphoreHandle_t xSemaphore, |
| BaseType_t *pxHigherPriorityTaskWoken) |
| { |
| *pxHigherPriorityTaskWoken = 0; |
| return xSemaphoreTake(xSemaphore, 0); |
| } |
| |
| /* |
| * Take a semaphore from code that may run in an ISR. CHERIoT RTOS does not |
| * permit code to run in ISRs and so this simply calls the normal code paths. |
| */ |
| __always_inline static inline _Bool __attribute__((overloadable)) |
| xSemaphoreTakeFromISR(SemaphoreHandle_t xSemaphore) |
| { |
| return xSemaphoreTake(xSemaphore, 0); |
| } |
| |
| /** |
| * A semaphore put (up) operation. If there are tasks blocked on the semaphore, |
| * this will unblock one of them. Otherwise, the semaphore value is incremented |
| * by one. |
| */ |
| __always_inline static inline _Bool xSemaphoreGive(SemaphoreHandle_t xSemaphore) |
| { |
| int ret = -1; |
| CHERIOT_FREERTOS_SEMAPHORE_SWITCH(xSemaphore) |
| { |
| CHERIOT_FREERTOS_SEMAPHORE_CASE( |
| ret = semaphore_put(&xSemaphore->semaphore);) |
| CHERIOT_FREERTOS_MUTEX_CASE(flaglock_unlock(&xSemaphore->mutex); |
| ret = 0;) |
| // Recursive mutexes are not supposed to use this interface. |
| CHERIOT_FREERTOS_RECURSIVE_MUTEX_CASE(;); |
| } |
| return ret == 0; |
| } |
| |
| /** |
| * Give a semaphore from code that may run in an ISR. CHERIoT RTOS does not |
| * permit code to run in ISRs and so this simply calls the normal code paths. |
| * |
| * The second parameter is a pointer to a variable that will be set to true if |
| * the caller needs to yield to wake a higher-priority task. This is |
| * unconditionally false on CHERIoT RTOS. |
| */ |
| __always_inline static inline _Bool __attribute__((overloadable)) |
| xSemaphoreGiveFromISR(SemaphoreHandle_t xSemaphore, |
| BaseType_t *pxHigherPriorityTaskWoken) |
| { |
| *pxHigherPriorityTaskWoken = 0; |
| return xSemaphoreGive(xSemaphore); |
| } |
| |
| /* |
| * Give a semaphore from code that may run in an ISR. CHERIoT RTOS does not |
| * permit code to run in ISRs and so this simply calls the normal code paths. |
| */ |
| __always_inline static inline _Bool __attribute__((overloadable)) |
| xSemaphoreGiveFromISR(SemaphoreHandle_t xSemaphore) |
| { |
| return xSemaphoreGive(xSemaphore); |
| } |
| |
| /** |
| * Try to acquire a recursive mutex. Returns true on success, false on |
| * failure. |
| */ |
| __always_inline static inline _Bool |
| xSemaphoreTakeRecursive(SemaphoreHandle_t xMutex, TickType_t xTicksToWait) |
| { |
| Timeout t = {0, xTicksToWait}; |
| int ret = -1; |
| CHERIOT_FREERTOS_SEMAPHORE_SWITCH(xMutex) |
| { |
| CHERIOT_FREERTOS_SEMAPHORE_CASE(;) |
| CHERIOT_FREERTOS_MUTEX_CASE(;) |
| CHERIOT_FREERTOS_RECURSIVE_MUTEX_CASE( |
| ret = recursivemutex_trylock(&t, &xMutex->recursiveMutex);) |
| } |
| return ret == 0; |
| } |
| |
| /** |
| * Release a recursive mutex. |
| */ |
| __always_inline static inline _Bool |
| xSemaphoreGiveRecursive(SemaphoreHandle_t xMutex) |
| { |
| int ret = -1; |
| CHERIOT_FREERTOS_SEMAPHORE_SWITCH(xMutex) |
| { |
| CHERIOT_FREERTOS_SEMAPHORE_CASE(;) |
| CHERIOT_FREERTOS_MUTEX_CASE(;) |
| CHERIOT_FREERTOS_RECURSIVE_MUTEX_CASE( |
| ret = recursivemutex_unlock(&xMutex->recursiveMutex);) |
| } |
| return ret == 0; |
| } |
| |
| /** |
| * Launder a semaphore back into the type used to initialise it. |
| */ |
| __always_inline static inline BaseType_t |
| xSemaphoreGetStaticBuffer(SemaphoreHandle_t xSemaphore, |
| StaticSemaphore_t **ppxSemaphoreBuffer) |
| { |
| *ppxSemaphoreBuffer = xSemaphore; |
| return pdTRUE; |
| } |