微波EDA网,见证研发工程师的成长!
首页 > 硬件设计 > 嵌入式设计 > ARM 中断状态和SVC状态的堆栈切换 (异常)

ARM 中断状态和SVC状态的堆栈切换 (异常)

时间:11-20 来源:互联网 点击:
基础知识:

Arm的寄存器使用规则以及寻址指令:

R13 Sp 堆栈寄存器

R14 Lr 连接寄存器

R15 PC 程序计数器

寄存器寻址

LDMIA R0!,{R1-R4}

执行以后的效果

R1 <——[R0]

R2 <——[R0+4]

R3 <——[R0+8]

R4 <——[R0+12]

堆栈寻址:

STMFD入栈指令,相当于STMDB

STMFD SP!,{R2-R4} 注意这个“!”的使用,在使用和不使用的情况下会有不一样的效果,在后面的代码中具体分析。

[SP-4] <­——R4

[SP-8] <——R3

[SP-12] <——R2

LDMFD出栈指令,相当于LDMIA

LDMFD SP!,{R6-R8}

R6 <——[SP]

R7 <——[SP+4]

R8 <——[SP+8]

补充说明:

LDMIA/STMIAIncrementAfter (先操作,后增加)

LDMIB/STMIBIncrementBefore(先增加,后操作)

LDMDA/STMDADecrementAfter(先操作,后递减)

LDMDB/STMDBDecrementBefore(先递减,后操作)

•STMFD(Push)块存储-FullDescending stack [STMDB]

•LDMFD(Pop) 块装载-FullDescending stack [LDMIA]

这些使用规则以及默认的表达方法是给编译器使用。但是在开发底层语言的同时,有必要知道这个么命名的规则和使用方法。初始化代码和部分关键代码是靠汇编实现。这些代码的理解不免和汇编打交道。因此了解一下基本的汇编规则还是很有帮助。

Arm的工作模式:

Arm的工作模式以及相关寄存器设置:

1,用户模式(usr)[10000]:ARM处理器正常的程序执行状态

2,快速中断模式(fiq)[10001]:用于高速数据传输或通道处理

3,外部中断模式(irq)[10010]:用于通用的中断处理

4,管理模式(svc)[10011]:操作系统使用的保护模式

5,中止模式(abt)[10111]:当数据或指令预取终止时进入该模式,用于虚拟存

储及存储保护

6,未定义指令模式(und)[11011]:当未定义的指令执行时进入该模式,用于支持硬件

协处理器的软件仿真

7,系统模式(sys)[11111]:运行具有特权模式的操作系统任务

设置方法:

MRS R14,CPSR 读取

MSR CPSR_c, R14 写入

以上几种模式存在的意义在于不同模式下特殊的几个寄存器使用是有区别的。再svc模式下堆栈指针为sp svc中断模式下sp指针为 sp irq等。同样lr连接寄存器的内容也是有不同的含义。再不同模式切换中,lr寄存器保存的地址是由硬件完成,但是表示的是不同模式下的下一条指令,既返模式切换后的返回地址。

每一种模式对应不同的bank register。中文官方翻译不详。但是每一种模式要拥有自己独立的寄存器组。并且每一种模式使用和可见寄存器的数量也是不相同的。

模式切换过程中其实只针对spsr进行操作,而未涉及cpsr是的操作。这个过程之所以这样主要是参考ARM cortex A8的TRM。其中这样描述离开异常的情况:

Typically the return instruction is an arithmetic orlogical operation with the S bit set to

1 and rd = r15, so the core copies the SPSR back to theCPSR.

也就是说离开异常,从异常情况返回以后会自动把spsr的内同拷贝到cpsr中。所以在执行BL Lr指令之前使用的堆栈其实并位切换。

Linux中初始化:

1, Svc模式的堆栈初始化:

堆栈的概念是给C 语言编译以后的代码使用,因此从head.S一直到C语言的执行,就是start_kernel。

__mmap_switched:

@注释 1:

adr r3, __switch_data + 4

ldmia r3!, {r4, r5, r6, r7}

cmp r4, r5 @ Copy datasegment if needed

1: cmpne r5, r6

ldrne fp, [r4], #4

strne fp, [r5], #4

bne 1b

mov fp, #0 @ Clear BSS(and zero fp)

1: cmp r6, r7

strcc fp, [r6],#4

bcc 1b

@注释 2:

ldmia r3, {r4, r5, r6, r7, sp}

str r9, [r4] @ Saveprocessor ID

str r1, [r5] @ Savemachine type

str r2, [r6] @ Saveatags pointer

bic r4, r0, #CR_A @ ClearA bit

stmia r7, {r0, r4} @Save control register values

@注释 3:

b start_kernel

ENDPROC(__mmap_switched)

注释1:

__switch_data这是以个地址。Linker会安排这个地址具体的数值。打开Sysmap可以发现这个数值为:c0008123 t __switch_data

注释 2:

将r3所指的内容依次装入{r4– r6,sp},这个时候sp指针就有了具体的数值了。

注释 3:

跳转指令,指向C函数的start_kernel。这时候栈针开始起效。因为C语言编译出来的代码参数传递,调用变量保存等都使用sp指针。这个指针仅仅是给初始化代码所使用。在进程的概念中还有进程堆栈的概念。这时候的sp具体指向的是描述进程结构的结构体task_info。

2,irq以及其他模式的初始化:

__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"

Copyright © 2017-2020 微波EDA网 版权所有

网站地图

Top