diff --git a/arch/arm/core/cortex_m/scb.c b/arch/arm/core/cortex_m/scb.c index 957c66dcc9f50..147675c515813 100644 --- a/arch/arm/core/cortex_m/scb.c +++ b/arch/arm/core/cortex_m/scb.c @@ -1,5 +1,6 @@ /* * Copyright (c) 2013-2014 Wind River Systems, Inc. + * Copyright (c) 2025 STMicroelectronics * * SPDX-License-Identifier: Apache-2.0 */ @@ -22,6 +23,21 @@ #include #include #include +#include + +/* For historical reasons, in Cortex-M family, CMSIS code calls System Handler Priority + * register SHP or SHPR. This code defines the name of the register + * according to the specific Cortex-M variant. + */ +#if defined(CONFIG_CPU_CORTEX_M0) || \ + defined(CONFIG_CPU_CORTEX_M0PLUS) || \ + defined(CONFIG_CPU_CORTEX_M1) || \ + defined(CONFIG_CPU_CORTEX_M3) || \ + defined(CONFIG_CPU_CORTEX_M4) +#define SHPR_FIELD_NAME SHP +#else +#define SHPR_FIELD_NAME SHPR +#endif #if defined(CONFIG_CPU_HAS_NXP_SYSMPU) #include @@ -151,3 +167,98 @@ void z_arm_init_arch_hw_at_boot(void) barrier_isync_fence_full(); } #endif /* CONFIG_INIT_ARCH_HW_AT_BOOT */ + +/** + * @brief Save essential SCB registers into a provided context structure. + * + * This function reads the current values of critical System Control Block (SCB) + * registers that are safe to backup, and stores them into the `context` structure. + * Access to SCB registers requires atomicity and consistency, so calling code + * should guarantee that interrupts are disabled. + * + * @param context Pointer to an `scb_context` structure where the register + * values will be stored. Must not be NULL. + */ +void z_arm_save_scb_context(struct scb_context *context) +{ + __ASSERT_NO_MSG(context != NULL); + +#if defined(CONFIG_CPU_CORTEX_M_HAS_VTOR) + context->vtor = SCB->VTOR; +#endif + context->aircr = SCB->AIRCR; + context->scr = SCB->SCR; + context->ccr = SCB->CCR; + + /* Backup System Handler Priority Registers */ + volatile uint32_t *shpr = (volatile uint32_t *) SCB->SHPR_FIELD_NAME; + + for (int i = 0; i < SHPR_SIZE_W; i++) { + context->shpr[i] = shpr[i]; + } + + context->shcsr = SCB->SHCSR; +#if defined(CPACR_PRESENT) + context->cpacr = SCB->CPACR; +#endif /* CPACR_PRESENT */ +} + +/** + * @brief Restores essential SCB registers from a provided context structure. + * + * This function writes the values from the `context` structure back to the + * respective System Control Block (SCB) registers. Access to SCB registers + * requires atomicity and consistency, so calling code should guarantee that + * interrupts are disabled. + * + * @warning The ICSR register is NOT restored directly due to its volatile nature + * and presence of read-only status bits and write-only clear/set bits. + * Direct restoration can lead to undefined behavior or corrupt interrupt state. + * If specific ICSR bits need to be managed as part of a context, + * a separate, highly controlled mechanism should be implemented. + * + * @param context Pointer to a `scb_context` structure containing the + * register values to be restored. Must not be NULL. + */ +void z_arm_restore_scb_context(const struct scb_context *context) +{ + __ASSERT_NO_MSG(context != NULL); + +#if defined(CONFIG_CPU_CORTEX_M_HAS_VTOR) + /* Restore Vector Table Offset Register first if it was modified. */ + SCB->VTOR = context->vtor; +#endif + /* Restore AIRCR: Must write the VECTKEY (0x05FA) along with the desired bits. + * Ensure only the relevant modifiable bits are restored. + */ + SCB->AIRCR = (context->aircr & ~SCB_AIRCR_VECTKEY_Msk) | + (0x05FAUL << SCB_AIRCR_VECTKEY_Pos); + + SCB->SCR = context->scr; + SCB->CCR = context->ccr; + + /* Restore System Handler Priority Registers */ + volatile uint32_t *shpr = (volatile uint32_t *) SCB->SHPR_FIELD_NAME; + + for (int i = 0; i < SHPR_SIZE_W; i++) { + shpr[i] = context->shpr[i]; + } + + /* Restore SHCSR */ + SCB->SHCSR = context->shcsr; + +#if defined(CPACR_PRESENT) + /* Restore CPACR */ + SCB->CPACR = context->cpacr; +#endif /* CPACR_PRESENT */ + + /** + * Ensure that updates to the SCB are visible by executing a DSB followed by ISB. + * This sequence is recommended in the M-profile Architecture Reference Manuals: + * - ARMv6: DDI0419 Issue E - §B2.5 "Barrier support for system correctness" + * - ARMv7: DDI0403 Issue E.e - §A3.7.3 "Memory barriers" (at end of section) + * - ARMv8: DDI0553 Version B.Y - §B7.2.16 "Synchronization requirements [...]" + */ + __DSB(); + __ISB(); +} diff --git a/include/zephyr/arch/arm/cortex_m/scb.h b/include/zephyr/arch/arm/cortex_m/scb.h new file mode 100644 index 0000000000000..096a5ce7fe12a --- /dev/null +++ b/include/zephyr/arch/arm/cortex_m/scb.h @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2025 STMicroelectronics + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file + * @brief System control block context helpers for Cortex-M CPUs + * + * System control block context helpers for backup and restore + */ + +#ifndef ARM_CORTEX_M_SCB_H_ +#define ARM_CORTEX_M_SCB_H_ + +#include +#include + +/* Define macros for CPU-conditional features */ +#if defined(CONFIG_CPU_CORTEX_M0) || \ + defined(CONFIG_CPU_CORTEX_M0PLUS) || \ + defined(CONFIG_CPU_CORTEX_M1) || \ + defined(CONFIG_CPU_CORTEX_M23) +#define SHPR_SIZE_W 2 +#else +#define SHPR_SIZE_W 3 +#define CPACR_PRESENT 1 +#endif + +/** + * @brief Structure to store essential, mutable SCB register values for backup/restore. + * + * This structure explicitly lists the SCB registers that are safe and meaningful + * to backup and restore for common system state management. It avoids volatile, + * read-only, or write-only status bits that should not be directly restored. + */ +struct scb_context { +#if defined(CONFIG_CPU_CORTEX_M_HAS_VTOR) + uint32_t vtor; /*!< offset 0x08, Vector Table Offset Register */ +#endif + uint32_t aircr; /*!< offset 0x0C, Application Interrupt and Reset Control Register + * (only modifiable bits) + */ + uint32_t scr; /*!< offset 0x10, System Control Register */ + uint32_t ccr; /*!< offset 0x14, Configuration Control Register */ + uint32_t shpr[SHPR_SIZE_W]; /*!< offset 0x18 or 0x1C, System Handler Priority Registers */ + uint32_t shcsr; /*!< offset 0x24, System Handler Control and State Register */ +#if defined(CPACR_PRESENT) + uint32_t cpacr; /*!< offset 0x88, Coprocessor Access Control Register */ +#endif /* CPACR_PRESENT */ +}; + +/** + * @name SCB Register Backup/Restore Functions + * @brief Functions for saving and restoring mutable SCB register state. + * @{ + */ + +/** + * @brief Save essential SCB registers into a provided context structure. + * + * This function reads the current values of critical System Control Block (SCB) + * registers that are safe to backup and stores them into the `context` structure. + * + * @param context Pointer to an `scb_context` structure where the register + * values will be stored. Must not be NULL. + */ +void z_arm_save_scb_context(struct scb_context *context); + +/** + * @brief Restores essential SCB registers from a provided context structure. + * + * This function writes the values from the `context` structure back to the + * respective System Control Block (SCB) registers. + * + * @warning Extreme caution is advised when restoring SCB registers. Only + * mutable registers are restored. Specifically, the ICSR register + * is NOT restored directly due to its volatile nature and read-only/ + * write-only bits. + * + * @param context Pointer to a `scb_context` structure containing the + * register values to be restored. Must not be NULL. + */ +void z_arm_restore_scb_context(const struct scb_context *context); + +/** @} */ + +#endif /* ARM_CORTEX_M_SCB_H_ */