| /* |
| * Copyright (c) 2013-2014 Wind River Systems, Inc. |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| /** |
| * @file |
| * @brief New thread creation for ARM Cortex-A, Cortex-M and Cortex-R |
| * |
| * Core thread related primitives for the ARM Cortex-A, Cortex-M and |
| * Cortex-R processor architecture. |
| */ |
| |
| #include <zephyr/kernel.h> |
| #include <ksched.h> |
| #include <zephyr/wait_q.h> |
| |
| #if (MPU_GUARD_ALIGN_AND_SIZE_FLOAT > MPU_GUARD_ALIGN_AND_SIZE) |
| #define FP_GUARD_EXTRA_SIZE (MPU_GUARD_ALIGN_AND_SIZE_FLOAT - \ |
| MPU_GUARD_ALIGN_AND_SIZE) |
| #else |
| #define FP_GUARD_EXTRA_SIZE 0 |
| #endif |
| |
| #ifndef EXC_RETURN_FTYPE |
| /* bit [4] allocate stack for floating-point context: 0=done 1=skipped */ |
| #define EXC_RETURN_FTYPE (0x00000010UL) |
| #endif |
| |
| /* Default last octet of EXC_RETURN, for threads that have not run yet. |
| * The full EXC_RETURN value will be e.g. 0xFFFFFFBC. |
| */ |
| #if defined(CONFIG_ARM_NONSECURE_FIRMWARE) |
| #define DEFAULT_EXC_RETURN 0xBC; |
| #else |
| #define DEFAULT_EXC_RETURN 0xFD; |
| #endif |
| |
| #if !defined(CONFIG_MULTITHREADING) && defined(CONFIG_CPU_CORTEX_M) |
| K_THREAD_STACK_DECLARE(z_main_stack, CONFIG_MAIN_STACK_SIZE); |
| #endif |
| |
| /* An initial context, to be "restored" by z_arm_pendsv(), is put at the other |
| * end of the stack, and thus reusable by the stack when not needed anymore. |
| * |
| * The initial context is an exception stack frame (ESF) since exiting the |
| * PendSV exception will want to pop an ESF. Interestingly, even if the lsb of |
| * an instruction address to jump to must always be set since the CPU always |
| * runs in thumb mode, the ESF expects the real address of the instruction, |
| * with the lsb *not* set (instructions are always aligned on 16 bit |
| * halfwords). Since the compiler automatically sets the lsb of function |
| * addresses, we have to unset it manually before storing it in the 'pc' field |
| * of the ESF. |
| */ |
| void arch_new_thread(struct k_thread *thread, k_thread_stack_t *stack, |
| char *stack_ptr, k_thread_entry_t entry, |
| void *p1, void *p2, void *p3) |
| { |
| struct __basic_sf *iframe; |
| |
| #ifdef CONFIG_MPU_STACK_GUARD |
| #if defined(CONFIG_USERSPACE) |
| if (z_stack_is_user_capable(stack)) { |
| /* Guard area is carved-out of the buffer instead of reserved |
| * for stacks that can host user threads |
| */ |
| thread->stack_info.start += MPU_GUARD_ALIGN_AND_SIZE; |
| thread->stack_info.size -= MPU_GUARD_ALIGN_AND_SIZE; |
| } |
| #endif /* CONFIG_USERSPACE */ |
| #if FP_GUARD_EXTRA_SIZE > 0 |
| if ((thread->base.user_options & K_FP_REGS) != 0) { |
| /* Larger guard needed due to lazy stacking of FP regs may |
| * overshoot the guard area without writing anything. We |
| * carve it out of the stack buffer as-needed instead of |
| * unconditionally reserving it. |
| */ |
| thread->stack_info.start += FP_GUARD_EXTRA_SIZE; |
| thread->stack_info.size -= FP_GUARD_EXTRA_SIZE; |
| } |
| #endif /* FP_GUARD_EXTRA_SIZE */ |
| #endif /* CONFIG_MPU_STACK_GUARD */ |
| |
| iframe = Z_STACK_PTR_TO_FRAME(struct __basic_sf, stack_ptr); |
| #if defined(CONFIG_USERSPACE) |
| if ((thread->base.user_options & K_USER) != 0) { |
| iframe->pc = (uint32_t)arch_user_mode_enter; |
| } else { |
| iframe->pc = (uint32_t)z_thread_entry; |
| } |
| #else |
| iframe->pc = (uint32_t)z_thread_entry; |
| #endif |
| |
| #if defined(CONFIG_CPU_CORTEX_M) |
| /* force ARM mode by clearing LSB of address */ |
| iframe->pc &= 0xfffffffe; |
| #endif |
| iframe->a1 = (uint32_t)entry; |
| iframe->a2 = (uint32_t)p1; |
| iframe->a3 = (uint32_t)p2; |
| iframe->a4 = (uint32_t)p3; |
| |
| #if defined(CONFIG_CPU_CORTEX_M) |
| iframe->xpsr = |
| 0x01000000UL; /* clear all, thumb bit is 1, even if RO */ |
| #else |
| iframe->xpsr = A_BIT | MODE_SYS; |
| #if defined(CONFIG_COMPILER_ISA_THUMB2) |
| iframe->xpsr |= T_BIT; |
| #endif /* CONFIG_COMPILER_ISA_THUMB2 */ |
| #endif /* CONFIG_CPU_CORTEX_M */ |
| |
| #if !defined(CONFIG_CPU_CORTEX_M) \ |
| && defined(CONFIG_FPU) && defined(CONFIG_FPU_SHARING) |
| iframe = (struct __basic_sf *) |
| ((uintptr_t)iframe - sizeof(struct __fpu_sf)); |
| memset(iframe, 0, sizeof(struct __fpu_sf)); |
| #endif |
| |
| thread->callee_saved.psp = (uint32_t)iframe; |
| thread->arch.basepri = 0; |
| |
| #if defined(CONFIG_ARM_STORE_EXC_RETURN) || defined(CONFIG_USERSPACE) |
| thread->arch.mode = 0; |
| #if defined(CONFIG_ARM_STORE_EXC_RETURN) |
| thread->arch.mode_exc_return = DEFAULT_EXC_RETURN; |
| #endif |
| #if FP_GUARD_EXTRA_SIZE > 0 |
| if ((thread->base.user_options & K_FP_REGS) != 0) { |
| thread->arch.mode |= Z_ARM_MODE_MPU_GUARD_FLOAT_Msk; |
| } |
| #endif |
| #if defined(CONFIG_USERSPACE) |
| thread->arch.priv_stack_start = 0; |
| #endif |
| #endif |
| /* |
| * initial values in all other registers/thread entries are |
| * irrelevant. |
| */ |
| } |
| |
| #if defined(CONFIG_MPU_STACK_GUARD) && defined(CONFIG_FPU) \ |
| && defined(CONFIG_FPU_SHARING) |
| |
| static inline void z_arm_thread_stack_info_adjust(struct k_thread *thread, |
| bool use_large_guard) |
| { |
| if (use_large_guard) { |
| /* Switch to use a large MPU guard if not already. */ |
| if ((thread->arch.mode & |
| Z_ARM_MODE_MPU_GUARD_FLOAT_Msk) == 0) { |
| /* Default guard size is used. Update required. */ |
| thread->arch.mode |= Z_ARM_MODE_MPU_GUARD_FLOAT_Msk; |
| #if defined(CONFIG_USERSPACE) |
| if (thread->arch.priv_stack_start) { |
| /* User thread */ |
| thread->arch.priv_stack_start += |
| FP_GUARD_EXTRA_SIZE; |
| } else |
| #endif /* CONFIG_USERSPACE */ |
| { |
| /* Privileged thread */ |
| thread->stack_info.start += |
| FP_GUARD_EXTRA_SIZE; |
| thread->stack_info.size -= |
| FP_GUARD_EXTRA_SIZE; |
| } |
| } |
| } else { |
| /* Switch to use the default MPU guard size if not already. */ |
| if ((thread->arch.mode & |
| Z_ARM_MODE_MPU_GUARD_FLOAT_Msk) != 0) { |
| /* Large guard size is used. Update required. */ |
| thread->arch.mode &= ~Z_ARM_MODE_MPU_GUARD_FLOAT_Msk; |
| #if defined(CONFIG_USERSPACE) |
| if (thread->arch.priv_stack_start) { |
| /* User thread */ |
| thread->arch.priv_stack_start -= |
| FP_GUARD_EXTRA_SIZE; |
| } else |
| #endif /* CONFIG_USERSPACE */ |
| { |
| /* Privileged thread */ |
| thread->stack_info.start -= |
| FP_GUARD_EXTRA_SIZE; |
| thread->stack_info.size += |
| FP_GUARD_EXTRA_SIZE; |
| } |
| } |
| } |
| } |
| |
| /* |
| * Adjust the MPU stack guard size together with the FPU |
| * policy and the stack_info values for the thread that is |
| * being switched in. |
| */ |
| uint32_t z_arm_mpu_stack_guard_and_fpu_adjust(struct k_thread *thread) |
| { |
| if (((thread->base.user_options & K_FP_REGS) != 0) || |
| ((thread->arch.mode_exc_return & EXC_RETURN_FTYPE) == 0)) { |
| /* The thread has been pre-tagged (at creation or later) with |
| * K_FP_REGS, i.e. it is expected to be using the FPU registers |
| * (if not already). Activate lazy stacking and program a large |
| * MPU guard to safely detect privilege thread stack overflows. |
| * |
| * OR |
| * The thread is not pre-tagged with K_FP_REGS, but it has |
| * generated an FP context. Activate lazy stacking and |
| * program a large MPU guard to detect privilege thread |
| * stack overflows. |
| */ |
| FPU->FPCCR |= FPU_FPCCR_LSPEN_Msk; |
| |
| z_arm_thread_stack_info_adjust(thread, true); |
| |
| /* Tag the thread with K_FP_REGS */ |
| thread->base.user_options |= K_FP_REGS; |
| |
| return MPU_GUARD_ALIGN_AND_SIZE_FLOAT; |
| } |
| |
| /* Thread is not pre-tagged with K_FP_REGS, and it has |
| * not been using the FPU. Since there is no active FPU |
| * context, de-activate lazy stacking and program the |
| * default MPU guard size. |
| */ |
| FPU->FPCCR &= (~FPU_FPCCR_LSPEN_Msk); |
| |
| z_arm_thread_stack_info_adjust(thread, false); |
| |
| return MPU_GUARD_ALIGN_AND_SIZE; |
| } |
| #endif |
| |
| #ifdef CONFIG_USERSPACE |
| FUNC_NORETURN void arch_user_mode_enter(k_thread_entry_t user_entry, |
| void *p1, void *p2, void *p3) |
| { |
| |
| /* Set up privileged stack before entering user mode */ |
| _current->arch.priv_stack_start = |
| (uint32_t)z_priv_stack_find(_current->stack_obj); |
| #if defined(CONFIG_MPU_STACK_GUARD) |
| #if defined(CONFIG_THREAD_STACK_INFO) |
| /* We're dropping to user mode which means the guard area is no |
| * longer used here, it instead is moved to the privilege stack |
| * to catch stack overflows there. Un-do the calculations done |
| * which accounted for memory borrowed from the thread stack. |
| */ |
| #if FP_GUARD_EXTRA_SIZE > 0 |
| if ((_current->arch.mode & Z_ARM_MODE_MPU_GUARD_FLOAT_Msk) != 0) { |
| _current->stack_info.start -= FP_GUARD_EXTRA_SIZE; |
| _current->stack_info.size += FP_GUARD_EXTRA_SIZE; |
| } |
| #endif /* FP_GUARD_EXTRA_SIZE */ |
| _current->stack_info.start -= MPU_GUARD_ALIGN_AND_SIZE; |
| _current->stack_info.size += MPU_GUARD_ALIGN_AND_SIZE; |
| #endif /* CONFIG_THREAD_STACK_INFO */ |
| |
| /* Stack guard area reserved at the bottom of the thread's |
| * privileged stack. Adjust the available (writable) stack |
| * buffer area accordingly. |
| */ |
| #if defined(CONFIG_FPU) && defined(CONFIG_FPU_SHARING) |
| _current->arch.priv_stack_start += |
| ((_current->arch.mode & Z_ARM_MODE_MPU_GUARD_FLOAT_Msk) != 0) ? |
| MPU_GUARD_ALIGN_AND_SIZE_FLOAT : MPU_GUARD_ALIGN_AND_SIZE; |
| #else |
| _current->arch.priv_stack_start += MPU_GUARD_ALIGN_AND_SIZE; |
| #endif /* CONFIG_FPU && CONFIG_FPU_SHARING */ |
| #endif /* CONFIG_MPU_STACK_GUARD */ |
| |
| #if defined(CONFIG_CPU_AARCH32_CORTEX_R) |
| _current->arch.priv_stack_end = |
| _current->arch.priv_stack_start + CONFIG_PRIVILEGED_STACK_SIZE; |
| #endif |
| |
| z_arm_userspace_enter(user_entry, p1, p2, p3, |
| (uint32_t)_current->stack_info.start, |
| _current->stack_info.size - |
| _current->stack_info.delta); |
| CODE_UNREACHABLE; |
| } |
| |
| #endif |
| |
| #if defined(CONFIG_BUILTIN_STACK_GUARD) |
| /* |
| * @brief Configure ARM built-in stack guard |
| * |
| * This function configures per thread stack guards by reprogramming |
| * the built-in Process Stack Pointer Limit Register (PSPLIM). |
| * The functionality is meant to be used during context switch. |
| * |
| * @param thread thread info data structure. |
| */ |
| void configure_builtin_stack_guard(struct k_thread *thread) |
| { |
| #if defined(CONFIG_USERSPACE) |
| if ((thread->arch.mode & CONTROL_nPRIV_Msk) != 0) { |
| /* Only configure stack limit for threads in privileged mode |
| * (i.e supervisor threads or user threads doing system call). |
| * User threads executing in user mode do not require a stack |
| * limit protection. |
| */ |
| __set_PSPLIM(0); |
| return; |
| } |
| /* Only configure PSPLIM to guard the privileged stack area, if |
| * the thread is currently using it, otherwise guard the default |
| * thread stack. Note that the conditional check relies on the |
| * thread privileged stack being allocated in higher memory area |
| * than the default thread stack (ensured by design). |
| */ |
| uint32_t guard_start = |
| ((thread->arch.priv_stack_start) && |
| (__get_PSP() >= thread->arch.priv_stack_start)) ? |
| (uint32_t)thread->arch.priv_stack_start : |
| (uint32_t)thread->stack_obj; |
| |
| __ASSERT(thread->stack_info.start == ((uint32_t)thread->stack_obj), |
| "stack_info.start does not point to the start of the" |
| "thread allocated area."); |
| #else |
| uint32_t guard_start = thread->stack_info.start; |
| #endif |
| #if defined(CONFIG_CPU_CORTEX_M_HAS_SPLIM) |
| __set_PSPLIM(guard_start); |
| #else |
| #error "Built-in PSP limit checks not supported by HW" |
| #endif |
| } |
| #endif /* CONFIG_BUILTIN_STACK_GUARD */ |
| |
| #if defined(CONFIG_MPU_STACK_GUARD) || defined(CONFIG_USERSPACE) |
| |
| #define IS_MPU_GUARD_VIOLATION(guard_start, guard_len, fault_addr, stack_ptr) \ |
| ((fault_addr != -EINVAL) ? \ |
| ((fault_addr >= guard_start) && \ |
| (fault_addr < (guard_start + guard_len)) && \ |
| (stack_ptr < (guard_start + guard_len))) \ |
| : \ |
| (stack_ptr < (guard_start + guard_len))) |
| |
| /** |
| * @brief Assess occurrence of current thread's stack corruption |
| * |
| * This function performs an assessment whether a memory fault (on a |
| * given memory address) is the result of stack memory corruption of |
| * the current thread. |
| * |
| * Thread stack corruption for supervisor threads or user threads in |
| * privilege mode (when User Space is supported) is reported upon an |
| * attempt to access the stack guard area (if MPU Stack Guard feature |
| * is supported). Additionally the current PSP (process stack pointer) |
| * must be pointing inside or below the guard area. |
| * |
| * Thread stack corruption for user threads in user mode is reported, |
| * if the current PSP is pointing below the start of the current |
| * thread's stack. |
| * |
| * Notes: |
| * - we assume a fully descending stack, |
| * - we assume a stacking error has occurred, |
| * - the function shall be called when handling MemManage and Bus fault, |
| * and only if a Stacking error has been reported. |
| * |
| * If stack corruption is detected, the function returns the lowest |
| * allowed address where the Stack Pointer can safely point to, to |
| * prevent from errors when un-stacking the corrupted stack frame |
| * upon exception return. |
| * |
| * @param fault_addr memory address on which memory access violation |
| * has been reported. It can be invalid (-EINVAL), |
| * if only Stacking error has been reported. |
| * @param psp current address the PSP points to |
| * |
| * @return The lowest allowed stack frame pointer, if error is a |
| * thread stack corruption, otherwise return 0. |
| */ |
| uint32_t z_check_thread_stack_fail(const uint32_t fault_addr, const uint32_t psp) |
| { |
| #if defined(CONFIG_MULTITHREADING) |
| const struct k_thread *thread = _current; |
| |
| if (thread == NULL) { |
| return 0; |
| } |
| #endif |
| |
| #if (defined(CONFIG_FPU) && defined(CONFIG_FPU_SHARING)) && \ |
| defined(CONFIG_MPU_STACK_GUARD) |
| uint32_t guard_len = |
| ((_current->arch.mode & Z_ARM_MODE_MPU_GUARD_FLOAT_Msk) != 0) ? |
| MPU_GUARD_ALIGN_AND_SIZE_FLOAT : MPU_GUARD_ALIGN_AND_SIZE; |
| #else |
| /* If MPU_STACK_GUARD is not enabled, the guard length is |
| * effectively zero. Stack overflows may be detected only |
| * for user threads in nPRIV mode. |
| */ |
| uint32_t guard_len = MPU_GUARD_ALIGN_AND_SIZE; |
| #endif /* CONFIG_FPU && CONFIG_FPU_SHARING */ |
| |
| #if defined(CONFIG_USERSPACE) |
| if (thread->arch.priv_stack_start) { |
| /* User thread */ |
| if (z_arm_thread_is_in_user_mode() == false) { |
| /* User thread in privilege mode */ |
| if (IS_MPU_GUARD_VIOLATION( |
| thread->arch.priv_stack_start - guard_len, |
| guard_len, |
| fault_addr, psp)) { |
| /* Thread's privilege stack corruption */ |
| return thread->arch.priv_stack_start; |
| } |
| } else { |
| if (psp < (uint32_t)thread->stack_obj) { |
| /* Thread's user stack corruption */ |
| return (uint32_t)thread->stack_obj; |
| } |
| } |
| } else { |
| /* Supervisor thread */ |
| if (IS_MPU_GUARD_VIOLATION(thread->stack_info.start - |
| guard_len, |
| guard_len, |
| fault_addr, psp)) { |
| /* Supervisor thread stack corruption */ |
| return thread->stack_info.start; |
| } |
| } |
| #else /* CONFIG_USERSPACE */ |
| #if defined(CONFIG_MULTITHREADING) |
| if (IS_MPU_GUARD_VIOLATION(thread->stack_info.start - guard_len, |
| guard_len, |
| fault_addr, psp)) { |
| /* Thread stack corruption */ |
| return thread->stack_info.start; |
| } |
| #else |
| if (IS_MPU_GUARD_VIOLATION((uint32_t)z_main_stack, |
| guard_len, |
| fault_addr, psp)) { |
| /* Thread stack corruption */ |
| return (uint32_t)Z_THREAD_STACK_BUFFER(z_main_stack); |
| } |
| #endif |
| #endif /* CONFIG_USERSPACE */ |
| |
| return 0; |
| } |
| #endif /* CONFIG_MPU_STACK_GUARD || CONFIG_USERSPACE */ |
| |
| #if defined(CONFIG_FPU) && defined(CONFIG_FPU_SHARING) |
| int arch_float_disable(struct k_thread *thread) |
| { |
| if (thread != _current) { |
| return -EINVAL; |
| } |
| |
| if (arch_is_in_isr()) { |
| return -EINVAL; |
| } |
| |
| /* Disable all floating point capabilities for the thread */ |
| |
| /* K_FP_REG flag is used in SWAP and stack check fail. Locking |
| * interrupts here prevents a possible context-switch or MPU |
| * fault to take an outdated thread user_options flag into |
| * account. |
| */ |
| int key = arch_irq_lock(); |
| |
| thread->base.user_options &= ~K_FP_REGS; |
| |
| #if defined(CONFIG_CPU_CORTEX_M) |
| __set_CONTROL(__get_CONTROL() & (~CONTROL_FPCA_Msk)); |
| #else |
| __set_FPEXC(0); |
| #endif |
| |
| /* No need to add an ISB barrier after setting the CONTROL |
| * register; arch_irq_unlock() already adds one. |
| */ |
| |
| arch_irq_unlock(key); |
| |
| return 0; |
| } |
| |
| int arch_float_enable(struct k_thread *thread, unsigned int options) |
| { |
| /* This is not supported in Cortex-M */ |
| return -ENOTSUP; |
| } |
| #endif /* CONFIG_FPU && CONFIG_FPU_SHARING */ |
| |
| /* Internal function for Cortex-M initialization, |
| * applicable to either case of running Zephyr |
| * with or without multi-threading support. |
| */ |
| static void z_arm_prepare_switch_to_main(void) |
| { |
| #if defined(CONFIG_FPU) |
| /* Initialize the Floating Point Status and Control Register when in |
| * Unshared FP Registers mode (In Shared FP Registers mode, FPSCR is |
| * initialized at thread creation for threads that make use of the FP). |
| */ |
| #if defined(CONFIG_ARMV8_1_M_MAINLINE) |
| /* |
| * For ARMv8.1-M with FPU, the FPSCR[18:16] LTPSIZE field must be set |
| * to 0b100 for "Tail predication not applied" as it's reset value |
| */ |
| __set_FPSCR(4 << FPU_FPDSCR_LTPSIZE_Pos); |
| #else |
| __set_FPSCR(0); |
| #endif |
| #if defined(CONFIG_CPU_CORTEX_M) && defined(CONFIG_FPU_SHARING) |
| /* In Sharing mode clearing FPSCR may set the CONTROL.FPCA flag. */ |
| __set_CONTROL(__get_CONTROL() & (~(CONTROL_FPCA_Msk))); |
| __ISB(); |
| #endif /* CONFIG_FPU_SHARING */ |
| #endif /* CONFIG_FPU */ |
| } |
| |
| void arch_switch_to_main_thread(struct k_thread *main_thread, char *stack_ptr, |
| k_thread_entry_t _main) |
| { |
| z_arm_prepare_switch_to_main(); |
| |
| _current = main_thread; |
| |
| #if defined(CONFIG_THREAD_LOCAL_STORAGE) && defined(CONFIG_CPU_CORTEX_M) |
| /* On Cortex-M, TLS uses a global variable as pointer to |
| * the thread local storage area. So this needs to point |
| * to the main thread's TLS area before switching to any |
| * thread for the first time, as the pointer is only set |
| * during context switching. |
| */ |
| extern uintptr_t z_arm_tls_ptr; |
| |
| z_arm_tls_ptr = main_thread->tls; |
| #endif |
| |
| #ifdef CONFIG_INSTRUMENT_THREAD_SWITCHING |
| z_thread_mark_switched_in(); |
| #endif |
| |
| /* the ready queue cache already contains the main thread */ |
| |
| #if defined(CONFIG_MPU_STACK_GUARD) || defined(CONFIG_USERSPACE) |
| /* |
| * If stack protection is enabled, make sure to set it |
| * before jumping to thread entry function |
| */ |
| z_arm_configure_dynamic_mpu_regions(main_thread); |
| #endif |
| |
| #if defined(CONFIG_BUILTIN_STACK_GUARD) |
| /* Set PSPLIM register for built-in stack guarding of main thread. */ |
| #if defined(CONFIG_CPU_CORTEX_M_HAS_SPLIM) |
| __set_PSPLIM(main_thread->stack_info.start); |
| #else |
| #error "Built-in PSP limit checks not supported by HW" |
| #endif |
| #endif /* CONFIG_BUILTIN_STACK_GUARD */ |
| |
| /* |
| * Set PSP to the highest address of the main stack |
| * before enabling interrupts and jumping to main. |
| */ |
| __asm__ volatile ( |
| "mov r0, %0\n\t" /* Store _main in R0 */ |
| #if defined(CONFIG_CPU_CORTEX_M) |
| "msr PSP, %1\n\t" /* __set_PSP(stack_ptr) */ |
| #endif |
| |
| "movs r1, #0\n\t" |
| #if defined(CONFIG_ARMV6_M_ARMV8_M_BASELINE) \ |
| || defined(CONFIG_ARMV7_R) \ |
| || defined(CONFIG_AARCH32_ARMV8_R) \ |
| || defined(CONFIG_ARMV7_A) |
| "cpsie i\n\t" /* __enable_irq() */ |
| #elif defined(CONFIG_ARMV7_M_ARMV8_M_MAINLINE) |
| "cpsie if\n\t" /* __enable_irq(); __enable_fault_irq() */ |
| "msr BASEPRI, r1\n\t" /* __set_BASEPRI(0) */ |
| #else |
| #error Unknown ARM architecture |
| #endif /* CONFIG_ARMV6_M_ARMV8_M_BASELINE */ |
| "isb\n\t" |
| "movs r2, #0\n\t" |
| "movs r3, #0\n\t" |
| "bl z_thread_entry\n\t" /* z_thread_entry(_main, 0, 0, 0); */ |
| : |
| : "r" (_main), "r" (stack_ptr) |
| : "r0" /* not to be overwritten by msr PSP, %1 */ |
| ); |
| |
| CODE_UNREACHABLE; |
| } |
| |
| #if !defined(CONFIG_MULTITHREADING) && defined(CONFIG_CPU_CORTEX_M) |
| |
| FUNC_NORETURN void z_arm_switch_to_main_no_multithreading( |
| k_thread_entry_t main_entry, void *p1, void *p2, void *p3) |
| { |
| z_arm_prepare_switch_to_main(); |
| |
| /* Set PSP to the highest address of the main stack. */ |
| char *psp = Z_THREAD_STACK_BUFFER(z_main_stack) + |
| K_THREAD_STACK_SIZEOF(z_main_stack); |
| |
| #if defined(CONFIG_BUILTIN_STACK_GUARD) |
| char *psplim = (Z_THREAD_STACK_BUFFER(z_main_stack)); |
| /* Clear PSPLIM before setting it to guard the main stack area. */ |
| __set_PSPLIM(0); |
| #endif |
| |
| /* Store all required input in registers, to be accessible |
| * after stack pointer change. The function is not going |
| * to return, so callee-saved registers do not need to be |
| * stacked. |
| */ |
| register void *p1_inreg __asm__("r0") = p1; |
| register void *p2_inreg __asm__("r1") = p2; |
| register void *p3_inreg __asm__("r2") = p3; |
| |
| __asm__ volatile ( |
| #ifdef CONFIG_BUILTIN_STACK_GUARD |
| "msr PSPLIM, %[_psplim]\n\t" |
| #endif |
| "msr PSP, %[_psp]\n\t" /* __set_PSP(psp) */ |
| #if defined(CONFIG_ARMV6_M_ARMV8_M_BASELINE) |
| "cpsie i\n\t" /* enable_irq() */ |
| #elif defined(CONFIG_ARMV7_M_ARMV8_M_MAINLINE) |
| "cpsie if\n\t" /* __enable_irq(); __enable_fault_irq() */ |
| "mov r3, #0\n\t" |
| "msr BASEPRI, r3\n\t" /* __set_BASEPRI(0) */ |
| #endif |
| "isb\n\t" |
| "blx %[_main_entry]\n\t" /* main_entry(p1, p2, p3) */ |
| #if defined(CONFIG_ARMV6_M_ARMV8_M_BASELINE) |
| "cpsid i\n\t" /* disable_irq() */ |
| #elif defined(CONFIG_ARMV7_M_ARMV8_M_MAINLINE) |
| "msr BASEPRI, %[basepri]\n\t"/* __set_BASEPRI(_EXC_IRQ_DEFAULT_PRIO) */ |
| "isb\n\t" |
| #endif |
| "loop: b loop\n\t" /* while (true); */ |
| : |
| : "r" (p1_inreg), "r" (p2_inreg), "r" (p3_inreg), |
| [_psp]"r" (psp), [_main_entry]"r" (main_entry) |
| #if defined(CONFIG_ARMV7_M_ARMV8_M_MAINLINE) |
| , [basepri] "r" (_EXC_IRQ_DEFAULT_PRIO) |
| #endif |
| #ifdef CONFIG_BUILTIN_STACK_GUARD |
| , [_psplim]"r" (psplim) |
| #endif |
| : |
| ); |
| |
| CODE_UNREACHABLE; /* LCOV_EXCL_LINE */ |
| } |
| #endif /* !CONFIG_MULTITHREADING && CONFIG_CPU_CORTEX_M */ |