1#ifndef ZTH_CONTEXT_ARCH_ARM_H
2#define ZTH_CONTEXT_ARCH_ARM_H
9#ifndef ZTH_CONTEXT_CONTEXT_H
10# error This file must be included by libzth/context/context.h.
18# if defined(ZTH_ARM_HAVE_MPU) && defined(ZTH_OS_BAREMETAL) && !defined(ZTH_HAVE_MMAN)
19# define ZTH_ARM_DO_STACK_GUARD
22# ifdef ZTH_ARM_DO_STACK_GUARD
23# define ZTH_ARM_STACK_GUARD_BITS 5
24# define ZTH_ARM_STACK_GUARD_SIZE (1 << ZTH_ARM_STACK_GUARD_BITS)
37template <
typename Impl>
46# ifdef ZTH_ARM_DO_STACK_GUARD
55# ifdef ZTH_ARM_DO_STACK_GUARD
57 return (
size_t)ZTH_ARM_STACK_GUARD_SIZE;
65# ifndef ZTH_ARM_USE_PSP
71# if defined(ZTH_HAVE_MMAN)
73 size +=
impl().pageSize() * 2U;
74# elif defined(ZTH_ARM_DO_STACK_GUARD)
76 size +=
impl().pageSize();
90# ifdef ZTH_ARM_DO_STACK_GUARD
93 int dummy __attribute__((unused)) = 0;
96 size_t const ps =
impl().pageSize();
115 __attribute__((always_inline))
static void*
sp() noexcept
118 asm(
"mov %0, sp;" :
"=r"(sp_reg));
122# ifdef ZTH_ARM_DO_STACK_GUARD
137 void* setGuard(
void* guard)
145# define REG_MPU_RBAR_ADDR_UNUSED ((unsigned)(((unsigned)-ZTH_ARM_STACK_GUARD_SIZE) >> 5))
147# pragma GCC diagnostic push
148# pragma GCC diagnostic ignored "-Wconversion"
150 static int stackGuardRegion() noexcept
155 static int region = 0;
160 region = reg_mpu_type().field.dregion - 1;
166 for(; region > 0; --region) {
167 rnr.field.region = region;
171 if(!rasr.field.enable)
179 rbar.field.addr = REG_MPU_RBAR_ADDR_UNUSED;
182 rasr.field.size = ZTH_ARM_STACK_GUARD_BITS - 1;
192 rasr.field.enable = 1;
196 if(!ctrl.field.enable) {
198 ctrl.field.privdefena = 1;
199 ctrl.field.enable = 1;
203 zth_dbg(context,
"Using MPU region %d as stack guard", region);
206 zth_dbg(context,
"Cannot find free MPU region for stack guard");
212 static void stackGuardImpl(
void* guard)
noexcept
217 int const region = stackGuardRegion();
222 zth_assert(((uintptr_t)guard & (ZTH_ARM_STACK_GUARD_SIZE - 1)) == 0);
224 reg_mpu_rbar rbar(0);
227 rbar.field.addr = (uintptr_t)guard >> 5;
228 zth_dbg(context,
"Set MPU stack guard to %p (sp=%p)", guard,
sp());
231 rbar.field.addr = REG_MPU_RBAR_ADDR_UNUSED;
232 zth_dbg(context,
"Disabled MPU stack guard");
235 rbar.field.valid = 1;
236 rbar.field.region = region;
253 stackGuardImpl(m_guard);
264 void* prev = setGuard(p);
280# pragma GCC diagnostic pop
288# ifdef ZTH_OS_BAREMETAL
290 static size_t get_lr_offset() noexcept
292 static size_t lr_offset = 0;
307 sizeof(jmp_buf) >=
sizeof(
void*) * 4U,
308 "jmp_buf size too small to find lr offset");
310 jmp_buf test_env = {};
314 asm volatile(
"mov %0, sp;" :
"=r"(sp_));
319 uintptr_t
const* test_env_ =
reinterpret_cast<uintptr_t const*
>(test_env);
320# pragma GCC diagnostic push
321# pragma GCC diagnostic ignored "-Wsizeof-array-div"
322 size_t sp_offset =
sizeof(jmp_buf) /
sizeof(
void*) - 2U;
323# pragma GCC diagnostic pop
324 for(; sp_offset > 0 && test_env_[sp_offset] != sp_; sp_offset--)
332 lr_offset = sp_offset + 1U;
340 static void**
sp(Stack
const&
stack)
noexcept
342 int dummy __attribute__((unused)) = 0;
345 return (
void**)((uintptr_t)(
stack.
p +
stack.
size -
sizeof(
void*)) & ~(uintptr_t)7);
356 static void set_sp(jmp_buf& env,
void**
sp)
noexcept
359 uintptr_t* env_ =
reinterpret_cast<uintptr_t*
>(env);
360 env_[get_lr_offset() - 1U] = (uintptr_t)
sp;
364 static void set_pc(jmp_buf& env,
void* pc)
noexcept
367 uintptr_t* env_ =
reinterpret_cast<uintptr_t*
>(env);
368 env_[get_lr_offset()] = (uintptr_t)pc;
391# if defined(ZTH_CONTEXT_SJLJ)
392# if defined(__ARM_FP) && !defined(__SOFTFP__) && NEWLIB_VERSION < 40300
393# define ZTH_CONTEXT_FPU
398 asm volatile(
"vstm %0, {d8-d15}" : :
"r"(m_fpu_env));
403 asm volatile(
"vldm %0, {d8-d15}" : :
"r"(m_fpu_env));
407# ifdef ZTH_ARM_USE_PSP
408 inline __attribute__((always_inline))
void
415 unsigned int control;
416 asm(
"mrs %0, control\n" :
"=r"(control));
432 :
"r"(env),
"r"(control)
433 :
"r0",
"r1",
"memory");
440# ifdef ZTH_STACK_SWITCH
442 __attribute__((naked, noinline))
static void*
447 "push {r4, " REG_FP ", lr}\n"
448#ifdef __cpp_exceptions
449 ".save {r4, " REG_FP ", lr}\n"
451 "add " REG_FP ", sp, #0\n"
462#ifdef __cpp_exceptions
465 "add " REG_FP ", sp, #0\n"
476 "pop {r4, " REG_FP ", pc}\n"
481 __attribute__((naked, noinline))
static void* stack_switch_psp(
487 "push {r4, " REG_FP ", lr}\n"
488#ifdef __cpp_exceptions
489 ".save {r4, " REG_FP ", lr}\n"
491 "add " REG_FP ", sp, #0\n"
496#ifdef __cpp_exceptions
499 "add " REG_FP ", sp, #0\n"
504 "pop {r4, " REG_FP ", pc}\n"
510 void*
stack_switch(
void*
stack,
size_t size,
void* (*f)(
void*)
noexcept,
void* arg)
noexcept
518 void* res = stack_switch_msp(arg, f);
525 void*
sp = (
void*)(((uintptr_t)
stack + size) & ~(uintptr_t)7);
526# ifdef ZTH_ARM_HAVE_MPU
527 void* prevGuard =
nullptr;
531 + 2 * ZTH_ARM_STACK_GUARD_SIZE - 1)
532 & ~(ZTH_ARM_STACK_GUARD_SIZE - 1));
533 prevGuard = setGuard(guard);
538 void* res = stack_switch_psp(arg, f,
sp);
540# ifdef ZTH_ARM_HAVE_MPU
552# ifdef ZTH_ARM_HAVE_MPU
594# ifdef ZTH_ARM_DO_STACK_GUARD
597# ifdef ZTH_CONTEXT_FPU
605# if !defined(__cpp_exceptions) && defined(__NEWLIB__)
609EXTERN_C
void __aeabi_unwind_cpp_pr0()
static safe_ptr< singleton_type >::type instance() noexcept
Return the only instance of T within this thread.
The class that manages the fibers within this thread.
void contextSwitchDisable() noexcept
void contextSwitchEnable(bool enable=true) noexcept
Impl & impl() noexcept
Return Impl this.
void stackAlign(Stack &stack) noexcept
constexpr ContextArch(ContextAttr const &attr) noexcept
static size_t pageSize() noexcept
size_t calcStackSize(size_t size) noexcept
static void * sp() noexcept
Base class of the Context.
static void stack_push(void **&sp, void *p) noexcept
Push data into the stack.
void stackGuardDeinit() noexcept
Release the guards around the memory.
static size_t pageSize() noexcept
Get system's page size.
Impl & impl() noexcept
Return Impl this.
void context_pop_regs() noexcept
Post-sjlj context restoring.
Stack const & stack() const noexcept
Return the stack address.
void * stack_switch(void *stack, size_t size, void *(*f)(void *) noexcept, void *arg) noexcept
int stackGuardInit() noexcept
Initialize guards around the stack memory.
void stackGuard() noexcept
Configure the guard for the current fiber.
static void set_sp(jmp_buf &env, void **sp) noexcept
Set the stack pointer in a jmp_buf.
void context_prepare_jmp(Impl &to, jmp_buf &env) noexcept
Pre-sjlj jump.
static void set_pc(jmp_buf &env, void *sp) noexcept
Set the program counter in a jmp_buf.
ContextAttr & attr() noexcept
Return the context attributes, requested by the user.
static void context_trampoline_from_jmp_buf() noexcept
Entry point to jump to from a (sig)jmp_buf.
Stack const & stackUsable() const noexcept
Return the start of the actual usable stack.
void stackAlign(Stack &stack) noexcept
Compute and modify the stack alignment and size, within the allocated space.
void context_push_regs() noexcept
Pre-sjlj context saving.
void zth_abort(char const *fmt,...)
Aborts the process after printing the given printf() formatted message.
constexpr auto guard(T &&g, char const *name=nullptr)
Create a guard from a function.
#define ZTH_REG_DEFINE(T, name, addr, fields...)
Define a hardware reference helper class, with bitfields.
#define zth_dbg(group, fmt, a...)
Debug printf()-like function.
static bool const Debug
This is a debug build when set to true.
static bool const EnableThreads
Add (Worker) thread support when true.
static bool const EnableStackGuard
When true, enable stack guards.
#define zth_assert(expr)
assert(), but better integrated in Zth.
#define likely(expr)
Marks the given expression to likely be evaluated to true.
#define unlikely(expr)
Marks the given expression to likely be evaluated to true.