ARM 中断状态和SVC状态的堆栈切换 (异常)【转】
转自:http://blog.csdn.net/edwardlulinux/article/details/9261393
版权声明:本文为博主原创文章,未经博主允许不得转载。
ARM 中断状态和SVC状态的堆栈切换 (异常)
基础知识:
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 / STMIA Increment After (先操作,后增加)
LDMIB / STMIB Increment Before(先增加,后操作)
LDMDA / STMDA Decrement After (先操作,后递减)
LDMDB / STMDB Decrement Before(先递减,后操作)
•STMFD (Push) 块存储- Full Descending stack [STMDB]
•LDMFD (Pop) 块装载- Full Descending 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 @ Clear'A' 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以及其他模式的初始化:
/*
* setup stacks for re-entrant exceptionhandlers
*/
__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");
函数:cpu_init()文件:setup.c
通过msr设置了cpsr寄存器。然后通过mov指令把具体的参数地址写入sp寄存器。
其中offsetof(struct stack, irq[0])这个表达式表示的是偏移量。既是在结构体中的偏移量。
其实在这个函数中初始化的irq堆栈只有4 bytes x 3。这么小的堆栈空间是否可以满足中端的需求。答案是:可以。在中端进入的函数中其实并没有完全使用irq模式下sp_irq指向的堆栈空间。在进入函数中马上有利用了msr指令进行了模式切换,切换到了svc模式。并且放弃了irq的模式。从中端返回也是从svc模式返回,而非irq模式。
代码:
vector_\name:
.if \correction
sub lr, lr, #\correction
.endif
@
@ Save r0, lr_<exception>(parent PC) and spsr_<exception>
@ (parent CPSR)
@
@ 注释 1:
stmia sp, {r0, lr} @ save r0,lr
mrs lr, spsr
str lr, [sp, #8] @ save spsr
@
@ Prepare for SVC32 mode. IRQs remain disabled.
@
mrs r0, cpsr
eor r0, r0, #(\mode ^ SVC_MODE) 进入SVC模式
msr spsr_cxsf, r0
@
@ the branch table mustimmediately follow this code
@
and lr, lr, #0x0f
mov r0, sp
ldr lr, [pc, lr, lsl #2]
@注释 2:
movs pc, lr @ branch tohandler in SVC mode
参照ARM的参考
ENDPROC(vector_\name)
注释 1 :
保存irq模式下的sp和lr指针到前面初始的sp_irq中。记住只有4 bytes x 3大小的空间。在后面的代码中还会看到str lr, [sp, #8]保存了最后一个参数到sp_irq的空间中。这里要注意:stmia sp, {r0, lr}这条指令。没有使用“!”号。这样一来尽管指令执行后sp指针指向的地址不会自加。因此在正式切换到SVC模式之前sp_irq所指向的地址并没有变化。这样再次进入中断模式时候,sp_irq不需要调整,可以重复使用。
注释 2:
参照ARM 的芯片设计手册可以发现,离开异常,从异常情况返回以后会自动把spsr的内同拷贝到cpsr中。所以在执行BL Lr指令之前使用的堆栈其实并位切换。
这样一来尽管是中断的模式进入系统,但是由中断模式切换至SVC模式。在SVC模式中完成了中断的后续相应和操作。
文章只是做了学习笔记已被后用,把这些思路罗列出来也给自己以后再回忆查找提供方便。
如果文章中有什么不对的地方,还请高手指正。
谢谢
edwardlu
ARM 中断状态和SVC状态的堆栈切换 (异常)【转】的更多相关文章
- ARM状态和THUMB状态
ARM处理器的工作状态 在ARM的体系结构中,可以工作在三种不同的状态,一是ARM状态,二是Thumb状态及Thumb-2状态,三是调试状态. <嵌入式系统开发与应用教程(第2版)>上介绍 ...
- linux-2.6.26内核中ARM中断实现详解(转)
转载:http://www.cnblogs.com/leaven/archive/2010/08/06/1794293.html 更多文档参见:http://pan.baidu.com/s/1dDvJ ...
- arm中断体系结构
ARM处理器中有7种类型的异常,按优先级从高到低的排列如下: 复位异常(Reset). 数据异常(Data Abort). 快速中断异常(FIQ) ...
- 在Linux下用netstat查看网络状态、端口状态
在Linux下用netstat查看网络状态.端口状态 在linux一般使用netstat 来查看系统端口使用情况步. netstat命令是一个监控TCP/IP网络的非常有用的工具,它可以显示路由表.实 ...
- 可能是讲解ARM中断和中断嵌套最通俗易懂的文章
几天前一个学生问我ARM中断嵌套的问题,我才发现原来在我心中理所当然的事对学生来说理解实属不易. ARM有七种模式,我们这里只讨论SVC.IRQ和FIQ模式. 我们可以假设ARM核心有两根中断引脚 ...
- 线程安全,有状态,无状态的对象<转>
进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位.线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位.另外 ...
- java 无状态和有状态区别
诸位Java程序员,想必大家对SimpleDateFormat并不陌生.不过,你是否知道,SimpleDateFormat不是线程安全的(thread safe).这意味着,下面的代码是错误的: ...
- IPv6系列-彻底弄明白有状态与无状态配置IPv6地址
深入研究自动分配IPv6地址的Stateless(无状态)与Stateful(有状态)方式 小慢哥的原创文章,欢迎转载 目录 ▪ 一. Link-Local Address的生成方式 ▪ 二. Glo ...
- ARM中断深入分析几点
ARM中断深入分析几点 1.程序发生中断后,是如何跳转到中断程序里面的? 2.执行完中断后,如何返回到原来被打断的地方接着执行呢? 3.ARM处理器的流水线结构对中断返回地址的计算有什么影响? 4.A ...
随机推荐
- Leetcode 674.最长递增序列
最长递增序列 给定一个未经排序的整数数组,找到最长且连续的的递增序列. 示例 1: 输入: [1,3,5,4,7] 输出: 3 解释: 最长连续递增序列是 [1,3,5], 长度为3. 尽管 [1,3 ...
- nopcommerce商城系统--开发者常遇问题清单
原址:http://www.nopcommerce.com/docs/74/frequently-asked-development-questions.aspx 以下是开发者常见问题的清单.也介绍了 ...
- java正则表达式 3 -- 查找
用正则表达式执行查找命令,则需要用正则对象,其规则和执行顺序如下: 指定为字符串的正则表达式必须首先被便以为此类的实例.然后,可将得到的正则对象匹配任意的字符串用于创建Mather对象,执行匹配所涉及 ...
- Windows IRP
IRP(I/O Request Packet),是由IO manager发起的对device的IO请求. 当用户调用系统API,如createFile类似的函数,其实是会交给IO manager来做相 ...
- numEdit
说明: 利用tedit扩展的数字编辑框,允许设置正负.小数点等(The digital edit box using tedit extended, allowing the set of posi ...
- js表单验证工具包
常用的js表单验证方法大全 /* 非空校验 : isNull() 是否是数字: isNumber(field) trim函数: trim() lTrim() rTrim() 校验字符串是否为空: ch ...
- 【bzoj3052】[wc2013]糖果公园 带修改树上莫队
题目描述 给出一棵n个点的树,每个点有一个点权,点权范围为1~m.支持两种操作:(1)修改一个点的点权 (2)对于一条路径,求$\sum\limits_{i=1}^m\sum\limits_{j=1} ...
- Charles 抓包发现自动跳转为https 问题梳理
今天遇到个有点意思的问题.特此记录. 业务场景: 做了一个页面,但是对外是挂载在京东主站上.如:www.jd.com/yifu/123456.html. 现场情况: 在本地/测试环境/预发环境中,每次 ...
- BZOJ1607 [Usaco2008 Dec]Patting Heads 轻拍牛头 【筛法】
题目 今天是贝茜的生日,为了庆祝自己的生日,贝茜邀你来玩一个游戏. 贝茜让N(1≤N≤100000)头奶牛坐成一个圈.除了1号与N号奶牛外,i号奶牛与i-l号和i+l号奶牛相邻.N号奶牛与1号奶牛相邻 ...
- Generator的基本用法
Generator函数是一个状态机,封装了多个内部状态.执行一个Generator,会返回一个迭代器对象,通过迭代器对象,可以遍历Generator函数内部的每个状态.因此,Generator函数可以 ...