arch_interrupt.c
2026/7/20大约 4 分钟附录源码附录
arch_interrupt.c
路径: arch\arm\armv7m\arch_interrupt.c
功能: ARM Cortex-M 架构中断控制函数实现。提供中断开关、中断状态查询、异常上下文判断以及中断栈溢出检测等底层功能。同时支持 ARMCC(ARM Compiler)和 GCC/Clang 两种编译器内联汇编实现。
核心函数:
| 函数 | 说明 |
|---|---|
os_irq_lock | 关全局中断并返回之前的中断状态(PRIMASK 值),用于临界区保护 |
os_irq_unlock | 恢复之前的中断状态(写入 PRIMASK),用于退出临界区 |
os_irq_disable | 无条件关全局中断(CPSID I) |
os_irq_enable | 无条件开全局中断(CPSIE I) |
os_is_irq_active | 查询当前是否在中断上下文中(读取 IPSR,非零表示在中断中) |
os_is_irq_disabled | 查询全局中断是否已关闭(读取 PRIMASK) |
os_irq_num | 获取当前中断编号(读取 IPSR) |
os_is_fault_active | 判断当前是否在异常上下文(IPSR 值在 [3, 6] 范围内,即 HardFault/MemManage/BusFault/UsageFault) |
os_interrupt_stack_check | 中断栈溢出检查(检查栈起始 16 字节的 $ 签名) |
os_interrupt_stack_init | 中断栈初始化(用 $ 填充栈空间,并注册为 OS_INIT_LEVEL_PRE_KERNEL_1 级别的初始化回调) |
sh_show_interrupt_stack_info | Shell 命令 show_intstk:显示中断栈起止地址、大小和最大使用率 |
关键设计点:
- 双编译器支持: 通过
#if defined(__CC_ARM)和#elif defined(__GNUC__) || defined(__CLANG_ARM)分别实现 ARMCC 内联汇编和 GCC/Clang 内联汇编 - 中断锁机制:
os_irq_lock返回 PRIMASK 原始值,os_irq_unlock恢复它,确保嵌套关中断的正确性 - 异常上下文判定:
os_is_fault_active通过 IPSR 范围 [3, 6] 判断是否为 HardFault / MemManage / BusFault / UsageFault,用于异常处理中的安全操作 - 中断栈溢出检测: 通过
$(0x24242424)填充中断栈起始 16 字节,os_interrupt_stack_check遍历检查签名是否完整,若被覆盖则断言失败 - 多编译器栈信息获取:
os_interrupt_stack_init分别用CSTACK_BLOCK_START/END(ARMCC/Clang)、__section_begin/end(IAR)、&CSTACK_BLOCK_START/END(GCC)获取主栈起止地址
/**
***********************************************************************************************************************
* Copyright (c) 2020, China Mobile Communications Group Co.,Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* @file arch_interrupt.c
*
* @brief This file provides interrupt related functions under the ARMv7-M architecture.
*
* @revision
* Date Author Notes
* 2021-01-12 OneOS Team First version.
***********************************************************************************************************************
*/
#include <oneos_config.h>
#include <os_types.h>
#include <os_stddef.h>
#ifdef OS_USING_INTERRUPT_STACK_OVERFLOW_CHECK
#include <os_assert.h>
#include <os_errno.h>
#include <arch_exception.h>
#include <shell.h>
static uint32_t gs_main_stack_start_addr;
static uint32_t gs_main_stack_end_addr;
extern void os_main_stack_init(uint8_t *addr);
#endif /* OS_USING_INTERRUPT_STACK_OVERFLOW_CHECK */
#if defined(__CC_ARM)
__asm os_ubase_t os_irq_lock(void)
{
MRS R0, PRIMASK
CPSID I
BX LR
}
__asm void os_irq_unlock(os_ubase_t irq_save)
{
MSR PRIMASK, R0
BX LR
}
__asm void os_irq_disable(void)
{
CPSID I
BX LR
}
__asm void os_irq_enable(void)
{
CPSIE I
BX LR
}
__asm os_bool_t os_is_irq_active(void)
{
MRS R0, IPSR
CBZ R0, in_task_context
MOV R0, #1
in_task_context
BX LR
}
__asm os_bool_t os_is_irq_disabled(void)
{
MRS R0, PRIMASK
BX LR
}
__asm uint32_t os_irq_num(void)
{
MRS R0, IPSR
BX LR
}
/**
***********************************************************************************************************************
* @brief Determine whether the current context is an exception context.
*
* @detail All exception vector numbers are as follows: Hard Fault:3 MemManage Fault:4 Bus Fault:5
* Usage Fault:6.Therefore, in the exception context, the range of interrupt vector number is [3,6].
*
* @param None.
*
* @return Return 1 in exception context, otherwise return 0.
* @retval 1 In exception context.
* @retval 0 In other context.
***********************************************************************************************************************
*/
__asm os_bool_t os_is_fault_active(void)
{
MRS R0, IPSR
MOV R1, #3
MOV R2, #6
CMP R0, R1
BLT noactive
CMP R0, R2
BGT noactive
MOV R0, #1
BX LR
noactive
MOV R0, #0
BX LR
}
#elif defined(__GNUC__) || defined(__CLANG_ARM)
os_ubase_t os_irq_lock(void)
{
os_ubase_t primask;
__asm__ __volatile__(
"MRS %0, PRIMASK\n"
"CPSID I"
: "=r"(primask)
:
: "memory");
return primask;
}
void os_irq_unlock(os_ubase_t irq_save)
{
__asm__ __volatile__(
"MSR PRIMASK, %0"
:
: "r"(irq_save)
: "memory");
return;
}
void os_irq_disable(void)
{
__asm__ __volatile__(
"CPSID I"
:
:
:);
return;
}
void os_irq_enable(void)
{
__asm__ __volatile__(
"CPSIE I"
:
:
:);
return;
}
os_bool_t os_is_irq_active(void)
{
os_bool_t active;
__asm__ __volatile__(
" MRS %0, IPSR\n"
" CBZ %0, in_task_context\n"
" MOV %0, #1\n"
"in_task_context:"
: "=r"(active)
:
: "memory");
return active;
}
os_bool_t os_is_irq_disabled(void)
{
os_bool_t disabled;
__asm__ __volatile__(
" MRS %0, PRIMASK\n"
: "=r"(disabled)
:
: "memory");
return disabled;
}
uint32_t os_irq_num(void)
{
uint32_t irq_num;
__asm__ __volatile__(
" MRS %0, IPSR\n"
: "=r"(irq_num)
:
:);
return irq_num;
}
/**
***********************************************************************************************************************
* @brief Determine whether the current context is an exception context.
*
* @detail All exception vector numbers are as follows: Hard Fault:3 MemManage Fault:4 Bus Fault:5
* Usage Fault:6.Therefore, in the exception context, the range of interrupt vector number is [3,6].
*
* @param None.
*
* @return Return 1 in exception context, otherwise return 0.
* @retval 1 In exception context.
* @retval 0 In other context.
***********************************************************************************************************************
*/
os_bool_t os_is_fault_active(void)
{
os_bool_t active;
os_bool_t fault_low;
os_bool_t fault_high;
__asm__ __volatile__(
" MRS %0, IPSR\n"
" MOV %1, #3\n"
" MOV %2, #6\n"
" CMP %0, %1\n"
" BLT noactive\n"
" CMP %0, %2\n"
" BGT noactive\n"
" MOV %0, #1\n"
" B end\n"
"noactive: \n"
" MOV %0, #0\n"
"end: \n"
: "=r"(active),"=r"(fault_low),"=r"(fault_high)
:
: "cc","memory");
return active;
}
#endif
#ifdef OS_USING_INTERRUPT_STACK_OVERFLOW_CHECK
/**
***********************************************************************************************************************
* @brief Check whether the interrupt stack overflows.
*
* @detail Traverse the values of the first 16 bytes of the interrupt stack. If the value is not equal to
* the value of the character $,it is judged that the interrupt stack overflows.
*
* @param None.
*
* @return None.
***********************************************************************************************************************
*/
void os_interrupt_stack_check(void)
{
int i;
uint32_t *addr;
addr = (uint32_t *)gs_main_stack_start_addr;
for (i = 0; i < 4; i++)
{
if (*addr == 0x24242424)
{
addr++;
}
else
{
os_kprintf("ERROR: Interrupt stack overflow!\r\n");
OS_ASSERT(0);
}
}
}
/**
***********************************************************************************************************************
* @brief Interrupt stack initialization.
*
* @detail The stack is initialized to the character $,range: stack_begin ~ current_msp - 4.
*
* @param None.
*
* @return OS_SUCCESS.
***********************************************************************************************************************
*/
os_err_t os_interrupt_stack_init(void)
{
os_base_t level;
uint8_t *addr;
#if defined(__CC_ARM) || defined(__CLANG_ARM)
gs_main_stack_start_addr = (uint32_t)&CSTACK_BLOCK_START(CSTACK_BLOCK_NAME);
gs_main_stack_end_addr = (uint32_t)&CSTACK_BLOCK_END(CSTACK_BLOCK_NAME);
#elif defined(__ICCARM__)
gs_main_stack_start_addr = (uint32_t)__section_begin(CSTACK_BLOCK_NAME);
gs_main_stack_end_addr = (uint32_t)__section_end(CSTACK_BLOCK_NAME);
#elif defined(__GNUC__)
gs_main_stack_start_addr = (uint32_t)(&CSTACK_BLOCK_START);
gs_main_stack_end_addr = (uint32_t)(&CSTACK_BLOCK_END);
#endif
if (gs_main_stack_start_addr >= gs_main_stack_end_addr)
{
os_kprintf("ERROR: Unable to get the main stack information!\r\n");
OS_ASSERT(0);
}
addr = (uint8_t *)gs_main_stack_start_addr;
level = os_irq_lock();
os_main_stack_init(addr);
os_irq_unlock(level);
return OS_SUCCESS;
}
OS_INIT_CALL(os_interrupt_stack_init, OS_INIT_LEVEL_PRE_KERNEL_1, OS_INIT_SUBLEVEL_HIGH);
/**
***********************************************************************************************************************
* @brief Interrupt stack information display.
*
* @detail The interrupt stack information includes:
* interrupt stack start and end address, interrupt stack size,
* and interrupt stack maximum utilization rate.
*
* @param None.
*
* @return OS_SUCCESS.
***********************************************************************************************************************
*/
os_err_t sh_show_interrupt_stack_info(void)
{
uint32_t stack_size;
uint32_t max_used;
stack_size = gs_main_stack_end_addr - gs_main_stack_start_addr;
max_used = os_hw_stack_max_used((void *)gs_main_stack_start_addr, stack_size);
os_kprintf("stack_begin stack_end stack_size max_used\r\n");
os_kprintf("----------- ----------- ---------- --------\r\n");
os_kprintf("0x%p 0x%p %-10u %3u%%\r\n",
gs_main_stack_start_addr,
gs_main_stack_end_addr,
stack_size,
(max_used * 100) / stack_size
);
return OS_SUCCESS;
}
SH_CMD_EXPORT(show_intstk, sh_show_interrupt_stack_info, "Show interrupt stack information");
#endif