blob: a8c1b59ba6bfe1746d38c5dbd639f8cbc8fccf54 [file] [log] [blame]
// Copyright Microsoft and CHERIoT Contributors.
// SPDX-License-Identifier: MIT
#include <compartment.h>
#include <stdint.h>
/**
* The helper functions need to expose an unmangled name because the compiler
* inserts calls to them. Declare them using the asm label extension.
*/
#define DECLARE_ATOMIC_LIBCALL(name, ret, ...) \
[[cheri::interrupt_state(disabled)]] __cheri_libcall ret name( \
__VA_ARGS__) asm(#name);
/**
* Macro that defines a library function to implement an atomic load for the
* specified size, using the specified type.
*
* Ideally the compiler should know that a system with a single core can do a
* single load for most cases of this, rather than needing a cross-library
* call, so eventually this will go away.
*/
#define DEFINE_ATOMIC_LOAD(size, type) \
DECLARE_ATOMIC_LIBCALL(__atomic_load_##size, type, const type *, int); \
type __atomic_load_##size(const type *ptr, int) \
{ \
static_assert(sizeof(type) == size, "Invalid type for size"); \
return *ptr; \
}
/**
* Macro that defines a library function to implement an atomic store for the
* specified size, using the specified type.
*
* Ideally the compiler should know that a system with a single core can do a
* single store for most cases of this, rather than needing a cross-library
* call, so eventually this will go away.
*/
#define DEFINE_ATOMIC_STORE(size, type) \
DECLARE_ATOMIC_LIBCALL(__atomic_store_##size, void, type *, type, int); \
void __atomic_store_##size(type *ptr, type value, int) \
{ \
static_assert(sizeof(type) == size, "Invalid type for size"); \
*ptr = value; \
}
/**
* Macro that defines a library function to implement an atomic
* fetch-and-something for the specified size, using the specified type. The
* name is the name of the operation and `op` is the C++ operator that
* implements it.
*/
#define DEFINE_ATOMIC_FETCH_OP(size, type, name, op) \
DECLARE_ATOMIC_LIBCALL( \
__atomic_fetch_##name##_##size, type, type *, type, int); \
type __atomic_fetch_##name##_##size(type *ptr, type value, int) \
{ \
static_assert(sizeof(type) == size, "Invalid type for size"); \
type tmp = *ptr; \
*ptr = tmp op value; \
return tmp; \
}
/**
* Helper macro that defines all of the cases of atomic operations.
*/
#define DEFINE_ATOMIC_FETCH_OPS(size, type) \
DEFINE_ATOMIC_FETCH_OP(size, type, add, +) \
DEFINE_ATOMIC_FETCH_OP(size, type, sub, -) \
DEFINE_ATOMIC_FETCH_OP(size, type, and, &) \
DEFINE_ATOMIC_FETCH_OP(size, type, or, |) \
DEFINE_ATOMIC_FETCH_OP(size, type, xor, ^)
/**
* Macro that defines a library function to implement an atomic exchange for the
* specified size, using the specified type.
*/
#define DEFINE_ATOMIC_EXCHANGE(size, type) \
DECLARE_ATOMIC_LIBCALL(__atomic_exchange_##size, type, type *, type, int); \
type __atomic_exchange_##size(type *ptr, type value, int) \
{ \
static_assert(sizeof(type) == size, "Invalid type for size"); \
type tmp = *ptr; \
*ptr = value; \
return tmp; \
}
/**
* Macro that defines a library function to implement an atomic compare and
* exchange for the specified size, using the specified type.
*/
#define DEFINE_ATOMIC_COMPARE_EXCHANGE(size, type) \
DECLARE_ATOMIC_LIBCALL( \
__atomic_compare_exchange_##size, int, type *, type *, type, int, int); \
int __atomic_compare_exchange_##size( \
type *ptr, type *expected, type desired, int, int) \
{ \
static_assert(sizeof(type) == size, "Invalid type for size"); \
if (*ptr == *expected) \
{ \
*ptr = desired; \
return 1; \
} \
*expected = *ptr; \
return 0; \
}
#define DEFINE_ALL_ATOMIC_OPS(size, type) \
DEFINE_ATOMIC_LOAD(size, type) \
DEFINE_ATOMIC_STORE(size, type) \
DEFINE_ATOMIC_EXCHANGE(size, type) \
DEFINE_ATOMIC_COMPARE_EXCHANGE(size, type) \
DEFINE_ATOMIC_FETCH_OPS(size, type)