1 #ifndef ZTH_CONTEXT_ARCH_ARM_H
2 #define ZTH_CONTEXT_ARCH_ARM_H
12 #ifndef ZTH_CONTEXT_CONTEXT_H
13 # error This file must be included by libzth/context/context.h.
21 # if defined(ZTH_ARM_HAVE_MPU) && defined(ZTH_OS_BAREMETAL) && !defined(ZTH_HAVE_MMAN)
22 # define ZTH_ARM_DO_STACK_GUARD
25 # ifdef ZTH_ARM_DO_STACK_GUARD
26 # define ZTH_ARM_STACK_GUARD_BITS 5
27 # define ZTH_ARM_STACK_GUARD_SIZE (1 << ZTH_ARM_STACK_GUARD_BITS)
40 template <
typename Impl>
50 # ifdef ZTH_ARM_DO_STACK_GUARD
58 # ifdef ZTH_ARM_DO_STACK_GUARD
60 return (
size_t)ZTH_ARM_STACK_GUARD_SIZE;
67 # ifndef ZTH_ARM_USE_PSP
73 # if defined(ZTH_HAVE_MMAN)
75 size +=
impl().pageSize() * 2u;
76 # elif defined(ZTH_ARM_DO_STACK_GUARD)
78 size +=
impl().pageSize();
91 # ifdef ZTH_ARM_DO_STACK_GUARD
94 int dummy __attribute__((unused)) = 0;
97 size_t const ps =
impl().pageSize();
116 __attribute__((always_inline))
static void*
sp() noexcept
119 asm(
"mov %0, sp;" :
"=r"(sp_reg));
123 # ifdef ZTH_ARM_DO_STACK_GUARD
138 void* setGuard(
void*
guard)
146 # define REG_MPU_RBAR_ADDR_UNUSED \
147 ((unsigned)(((unsigned)-ZTH_ARM_STACK_GUARD_SIZE) >> 5))
149 # pragma GCC diagnostic push
150 # pragma GCC diagnostic ignored "-Wconversion"
152 static int stackGuardRegion() noexcept
157 static int region = 0;
162 region = reg_mpu_type().field.dregion - 1;
168 for(; region > 0; --region) {
169 rnr.field.region = region;
173 if(!rasr.field.enable)
181 rbar.field.addr = REG_MPU_RBAR_ADDR_UNUSED;
184 rasr.field.size = ZTH_ARM_STACK_GUARD_BITS - 1;
194 rasr.field.enable = 1;
198 if(!ctrl.field.enable) {
200 ctrl.field.privdefena = 1;
201 ctrl.field.enable = 1;
205 zth_dbg(context,
"Using MPU region %d as stack guard", region);
208 zth_dbg(context,
"Cannot find free MPU region for stack guard");
214 static void stackGuardImpl(
void*
guard) noexcept
219 int const region = stackGuardRegion();
226 reg_mpu_rbar rbar(0);
229 rbar.field.addr = (uintptr_t)
guard >> 5;
230 zth_dbg(context,
"Set MPU stack guard to %p (sp=%p)",
guard,
sp());
233 rbar.field.addr = REG_MPU_RBAR_ADDR_UNUSED;
234 zth_dbg(context,
"Disabled MPU stack guard");
237 rbar.field.valid = 1;
238 rbar.field.region = region;
255 stackGuardImpl(m_guard);
266 void* prev = setGuard(p);
282 # pragma GCC diagnostic pop
290 # ifdef ZTH_OS_BAREMETAL
292 static size_t get_lr_offset(jmp_buf
const& env) noexcept
294 static size_t lr_offset = 0;
306 lr_offset =
sizeof(env) /
sizeof(env[0]) - 1;
307 for(; lr_offset > 1 && !env[lr_offset]; lr_offset--)
320 static void**
sp(Stack
const&
stack) noexcept
322 int dummy __attribute__((unused)) = 0;
325 return (
void**)((uintptr_t)(
stack.
p +
stack.
size -
sizeof(
void*)) & ~(uintptr_t)7);
334 static void set_sp(jmp_buf& env,
void**
sp) noexcept
336 env[get_lr_offset(env) - 1u] = (intptr_t)
sp;
339 static void set_pc(jmp_buf& env,
void* pc) noexcept
341 env[get_lr_offset(env)] = (intptr_t)pc;
352 #ifndef __cpp_exceptions
362 #ifndef __cpp_exceptions
369 # if defined(ZTH_CONTEXT_SJLJ)
370 # if defined(__ARM_FP) && !defined(__SOFTFP__)
371 # define ZTH_CONTEXT_FPU
376 asm volatile(
"vstm %0, {d8-d15}" : :
"r"(m_fpu_env));
381 asm volatile(
"vldm %0, {d8-d15}" : :
"r"(m_fpu_env));
385 # ifdef ZTH_ARM_USE_PSP
386 inline __attribute__((always_inline))
void
393 unsigned int control;
394 asm(
"mrs %0, control\n" :
"=r"(control));
410 :
"r"(env),
"r"(control)
411 :
"r0",
"r1",
"memory");
418 # ifdef ZTH_STACK_SWITCH
420 __attribute__((naked, noinline))
static void*
425 #ifndef __cpp_exceptions
428 "push {r4, " REG_FP ", lr}\n"
429 ".save {r4, " REG_FP ", lr}\n"
430 "add " REG_FP ", sp, #0\n"
442 "add " REG_FP ", sp, #0\n"
453 "pop {r4, " REG_FP ", pc}\n"
454 #ifndef __cpp_exceptions
461 __attribute__((naked, noinline))
static void* stack_switch_psp(
467 #ifndef __cpp_exceptions
470 "push {r4, " REG_FP ", lr}\n"
471 ".save {r4, " REG_FP ", lr}\n"
472 "add " REG_FP ", sp, #0\n"
478 "add " REG_FP ", sp, #0\n"
483 "pop {r4, " REG_FP ", pc}\n"
484 #ifndef __cpp_exceptions
492 void*
stack_switch(
void*
stack,
size_t size,
void* (*f)(
void*) noexcept,
void* arg) noexcept
500 void* res = stack_switch_msp(arg, f);
507 void*
sp = (
void*)(((uintptr_t)
stack + size) & ~(uintptr_t)7);
508 # ifdef ZTH_ARM_HAVE_MPU
509 void* prevGuard =
nullptr;
513 (
void*)(((uintptr_t)
stack + 2 * ZTH_ARM_STACK_GUARD_SIZE
515 & ~(ZTH_ARM_STACK_GUARD_SIZE - 1));
516 prevGuard = setGuard(
guard);
521 void* res = stack_switch_psp(arg, f,
sp);
523 # ifdef ZTH_ARM_HAVE_MPU
535 # ifdef ZTH_ARM_HAVE_MPU
577 # ifdef ZTH_ARM_DO_STACK_GUARD
580 # ifdef ZTH_CONTEXT_FPU
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
static void * sp() noexcept
void stackAlign(Stack &stack) noexcept
constexpr ContextArch(ContextAttr const &attr) noexcept
Impl & impl() noexcept
Return Impl this.
static size_t pageSize() noexcept
size_t calcStackSize(size_t size) 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.
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.
Impl & impl() noexcept
Return Impl this.
static void context_trampoline_from_jmp_buf()
Entry point to jump to from a (sig)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.
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.