| /* |
| * Copyright (c) 2023 Arm Limited (or its affiliates). All rights reserved. |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| /** |
| * @file |
| * @brief Thread context switching for ARM Cortex-A and Cortex-R (AArch32) |
| * |
| * This module implements the routines necessary for thread context switching |
| * on ARM Cortex-A and Cortex-R CPUs. |
| */ |
| |
| #include <zephyr/toolchain.h> |
| #include <zephyr/linker/sections.h> |
| #include <zephyr/arch/cpu.h> |
| #include <offsets_short.h> |
| #include <zephyr/kernel.h> |
| #include "macro_priv.inc" |
| |
| _ASM_FILE_PROLOGUE |
| |
| GTEXT(z_arm_svc) |
| GTEXT(z_arm_context_switch) |
| GTEXT(z_do_kernel_oops) |
| GTEXT(z_arm_do_syscall) |
| |
| /* |
| * Routine to handle context switches |
| * |
| * This function is directly called either by _isr_wrapper() in case of |
| * preemption, or arch_switch() in case of cooperative switching. |
| * |
| * void z_arm_context_switch(struct k_thread *new, struct k_thread *old); |
| */ |
| SECTION_FUNC(TEXT, z_arm_context_switch) |
| |
| ldr r2, =_thread_offset_to_callee_saved |
| add r2, r1, r2 |
| |
| stm r2, {r4-r11, sp, lr} |
| |
| /* save current thread's exception depth */ |
| get_cpu r2 |
| ldrb r3, [r2, #_cpu_offset_to_exc_depth] |
| strb r3, [r1, #_thread_offset_to_exception_depth] |
| |
| /* retrieve next thread's exception depth */ |
| ldrb r3, [r0, #_thread_offset_to_exception_depth] |
| strb r3, [r2, #_cpu_offset_to_exc_depth] |
| |
| /* save old thread into switch handle which is required by |
| * z_sched_switch_spin(). |
| * |
| * Note that this step must be done after all relevant state is |
| * saved. |
| */ |
| dsb |
| str r1, [r1, #___thread_t_switch_handle_OFFSET] |
| |
| #if defined(CONFIG_THREAD_LOCAL_STORAGE) |
| /* Grab the TLS pointer */ |
| ldr r3, [r0, #_thread_offset_to_tls] |
| |
| /* Store TLS pointer in the "Process ID" register. |
| * This register is used as a base pointer to all |
| * thread variables with offsets added by toolchain. |
| */ |
| mcr 15, 0, r3, c13, c0, 2 |
| #endif |
| |
| ldr r2, =_thread_offset_to_callee_saved |
| add r2, r0, r2 |
| ldm r2, {r4-r11, sp, lr} |
| |
| #if defined (CONFIG_ARM_MPU) |
| /* Re-program dynamic memory map */ |
| push {r0, lr} |
| bl z_arm_configure_dynamic_mpu_regions |
| pop {r0, lr} |
| #endif |
| |
| #ifdef CONFIG_INSTRUMENT_THREAD_SWITCHING |
| push {lr} |
| bl z_thread_mark_switched_in |
| pop {lr} |
| #endif |
| |
| bx lr |
| |
| /** |
| * |
| * @brief Service call handler |
| * |
| * The service call (svc) is used in the following occasions: |
| * - Cooperative context switching |
| * - IRQ offloading |
| * - Kernel run-time exceptions |
| * |
| */ |
| SECTION_FUNC(TEXT, z_arm_svc) |
| z_arm_cortex_ar_enter_exc |
| |
| /* Get SVC number */ |
| cps #MODE_SVC |
| mrs r0, spsr |
| tst r0, #0x20 |
| ldreq r1, [lr, #-4] |
| biceq r1, #0xff000000 |
| beq demux |
| |
| ldr r1, [lr, #-2] |
| and r1, #0xff |
| |
| /* |
| * grab service call number: |
| * TODO 0: context switch |
| * 1: irq_offload (if configured) |
| * 2: kernel panic or oops (software generated fatal exception) |
| * TODO 3: system calls for memory protection |
| */ |
| demux: |
| cps #MODE_SYS |
| |
| cmp r1, #_SVC_CALL_RUNTIME_EXCEPT |
| beq _oops |
| |
| #ifdef CONFIG_IRQ_OFFLOAD |
| cmp r1, #_SVC_CALL_IRQ_OFFLOAD |
| beq offload |
| b inv |
| offload: |
| get_cpu r2 |
| ldr r3, [r2, #___cpu_t_nested_OFFSET] |
| add r3, r3, #1 |
| str r3, [r2, #___cpu_t_nested_OFFSET] |
| |
| /* If not nested: switch to IRQ stack and save current sp on it. */ |
| cmp r3, #1 |
| bhi 1f |
| mov r0, sp |
| cps #MODE_IRQ |
| push {r0} |
| |
| 1: |
| blx z_irq_do_offload |
| b z_arm_cortex_ar_irq_done |
| #endif |
| b inv |
| |
| _oops: |
| /* |
| * Pass the exception frame to z_do_kernel_oops. r0 contains the |
| * exception reason. |
| */ |
| mov r0, sp |
| bl z_do_kernel_oops |
| |
| inv: |
| mov r0, #0 /* K_ERR_CPU_EXCEPTION */ |
| mov r1, sp |
| bl z_arm_fatal_error |
| |
| /* Return here only in case of recoverable error */ |
| b z_arm_cortex_ar_exit_exc |