Linux 内核 ARM 异常堆栈初始化
Posted
技术标签:
【中文标题】Linux 内核 ARM 异常堆栈初始化【英文标题】:Linux kernel ARM exception stack init 【发布时间】:2014-05-20 16:03:38 【问题描述】:我在 Freescale i.MX6 (ARM Cortex-A9) 上使用 Linux 内核 3.0.35。在遇到内核 OOPS 后,我试图了解异常堆栈初始化。这是我目前所发现的。
在 arch/arm/kernel/setup.c 的 cpu_init() 中,我看到异常堆栈正在初始化:
struct stack
u32 irq[3];
u32 abt[3];
u32 und[3];
____cacheline_aligned;
static struct stack stacks[NR_CPUS];
void cpu_init(void)
struct stack *stk = &stacks[cpu];
...<snip>
/*
* setup stacks for re-entrant exception handlers
*/
__asm__ (
"msr cpsr_c, %1\n\t"
"add r14, %0, %2\n\t"
"mov sp, r14\n\t"
"msr cpsr_c, %3\n\t"
"add r14, %0, %4\n\t"
"mov sp, r14\n\t"
"msr cpsr_c, %5\n\t"
"add r14, %0, %6\n\t"
"mov sp, r14\n\t"
"msr cpsr_c, %7"
:
: "r" (stk),
PLC (PSR_F_BIT | PSR_I_BIT | IRQ_MODE),
"I" (offsetof(struct stack, irq[0])),
PLC (PSR_F_BIT | PSR_I_BIT | ABT_MODE),
"I" (offsetof(struct stack, abt[0])),
PLC (PSR_F_BIT | PSR_I_BIT | UND_MODE),
"I" (offsetof(struct stack, und[0])),
PLC (PSR_F_BIT | PSR_I_BIT | SVC_MODE)
: "r14");
我看到每个堆栈只有三个单词的空间。这就是 arch/arm/kernel/entry-armv.S 中的宏 vector_stub 使用它的方式。它将 R0、LR (parent PC) 和 SPSR (parent CPSR) 保存到这三个词中。然后跳转到 __irq_svc。从创建堆栈帧的宏 svc_entry 开始
.macro svc_entry, stack_hole=0
UNWIND(.fnstart )
UNWIND(.save r0 - pc )
sub sp, sp, #(S_FRAME_SIZE + \stack_hole - 4)
这也是我从 KGDB 中看到反汇编代码的方式:
Dump of assembler code for function __irq_svc:
0xc01402c0 <+0>: 44 d0 4d e2 sub sp, sp, #68 ; 0x44
0xc01402c4 <+4>: 04 00 1d e3 tst sp, #4
0xc01402c8 <+8>: 04 d0 4d 02 subeq sp, sp, #4
0xc01402cc <+12>: fe 1f 8d e8 stm sp, r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, r12
0xc01402d0 <+16>: 0e 00 90 e8 ldm r0, r1, r2, r3
0xc01402d4 <+20>: 30 50 8d e2 add r5, sp, #48 ; 0x30
0xc01402d8 <+24>: 00 40 e0 e3 mvn r4, #0
0xc01402dc <+28>: 44 00 8d e2 add r0, sp, #68 ; 0x44
0xc01402e0 <+32>: 04 00 80 02 addeq r0, r0, #4
0xc01402e4 <+36>: 04 10 2d e5 push r1 ; (str r1, [sp, #-4]!)
0xc01402e8 <+40>: 0e 10 a0 e1 mov r1, lr
0xc01402ec <+44>: 1f 00 85 e8 stm r5, r0, r1, r2, r3, r4
0xc01402f0 <+48>: ad 96 a0 e1 lsr r9, sp, #13
0xc01402f4 <+52>: 89 96 a0 e1 lsl r9, r9, #13
0xc01402f8 <+56>: 04 80 99 e5 ldr r8, [r9, #4]
0xc01402fc <+60>: 01 70 88 e2 add r7, r8, #1
0xc0140300 <+64>: 04 70 89 e5 str r7, [r9, #4]
0xc0140304 <+68>: 54 50 9f e5 ldr r5, [pc, #84] ; 0xc0140360
0xc0140308 <+72>: 00 50 95 e5 ldr r5, [r5]
0xc014030c <+76>: 0c 60 95 e5 ldr r6, [r5, #12]
0xc0140310 <+80>: 4c e0 9f e5 ldr lr, [pc, #76] ; 0xc0140364
0xc0140314 <+84>: 07 0b c6 e3 bic r0, r6, #7168 ; 0x1c00
0xc0140318 <+88>: 1d 00 50 e3 cmp r0, #29
0xc014031c <+92>: 00 00 50 31 cmpcc r0, r0
0xc0140320 <+96>: 0e 00 50 11 cmpne r0, lr
0xc0140324 <+100>: 00 00 50 21 cmpcs r0, r0
0xc0140328 <+104>: 0d 10 a0 11 movne r1, sp
0xc014032c <+108>: 28 e0 4f 12 subne lr, pc, #40 ; 0x28
0xc0140330 <+112>: 32 eb ff 1a bne 0xc013b000 <asm_do_IRQ>
0xc0140334 <+116>: 04 80 89 e5 str r8, [r9, #4]
0xc0140338 <+120>: 00 00 99 e5 ldr r0, [r9]
0xc014033c <+124>: 00 00 38 e3 teq r8, #0
0xc0140340 <+128>: 00 00 a0 13 movne r0, #0
0xc0140344 <+132>: 02 00 10 e3 tst r0, #2
0xc0140348 <+136>: 06 00 00 1b blne 0xc0140368 <svc_preempt>
0xc014034c <+140>: 40 40 9d e5 ldr r4, [sp, #64] ; 0x40
0xc0140350 <+144>: 04 f0 6f e1 msr SPSR_fsxc, r4
0xc0140354 <+148>: 1f f0 7f f5 clrex
0xc0140358 <+152>: ff ff dd e8 ldm sp, r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, r12, sp, lr, pc^
End of assembler dump.
在异常期间,SP 是存储的 R13。如果我的操作正确,则该堆栈上的该框架没有空间。这意味着我一定错过了什么。是否有其他地方初始化异常堆栈?
【问题讨论】:
【参考方案1】:tl;dr - 我们将模式切换到主管并使用该堆栈。
您错过了通过 向量表 将控制权交给 CPU 并切换模式的关键点。请参阅:entry-armv.S and __vectors_start
。 vector stubs
是最初在主向量表中的 b
ranch 之后发送控制的代码。 vector_stub
宏保存了三个项目;已更正的lr
、r0
和例外模式的spsr
(如您所述)。
您错过的一点是,在此之后所有异常都切换到SVC_MODE
,因此使用current
任务堆栈,它也具有thread_info
结构。 模式切换 在 ARM 系统级汇编器中是一个很难理解的概念。以前设置的寄存器现在完全不同了。注意msr
和cps
类型说明。事情可以在他们之后完全改变;我已经被这个困惑了几十次了。
spsr
用作vector_stub
表的索引,该表通常会跳转到__irq_svc
或__irq_usr
。只需向下滚动查看您已经找到的 entry-arm.S 的底部。
相关:Physical address of ARM-Linux vector table
【讨论】:
在这个版本中,mov r0, sp
在 movs pc, lr
之前完成。第一个给__irq_svc
一个指向保存的三个单词的指针。第二个改变模式。宏 svc_entry
使用 r0
重新加载值。由于中断被禁用,此静态存储仅使用一次。
啊,以防有人不知道。 ARM 对每种模式都有不同的堆栈。所以sp_irq
、sp_abt
、sp_und
和sp_svc
都是指向不同栈的不同寄存器。 Linux 只使用sp_svc
,除了这个问题问的很短的时间。以上是关于Linux 内核 ARM 异常堆栈初始化的主要内容,如果未能解决你的问题,请参考以下文章
Linux 内核 内存管理memblock 分配器 ⑤ ( Linux 内核中定义 memblock 分配器的位置 | ARM64体系架构下 Linux内核初始化 memblock 分配器流程 )
Linux 内核 内存管理Linux 内核内存布局 ④ ( ARM64 架构体系内存分布 | 内核启动源码 start_kernel | 内存初始化 mm_init | mem_init )
[架构之路-120]-《软考-系统架构设计师》-计算机体系结构 -2- 一文了解ARM SOC体系结构原理(CPU工作原理指令内存中断堆栈IO初始化)