uboot中的中断macro宏
title: uboot中的中断macro宏
date: 2019/2/26 09:37:12
toc: true
uboot中的中断macro宏
引入
以前因为uboot的目的只是引导linux,没有去看关于中断相关的代码,这两天重新回顾看了下 Uboot中start.S源码的指令级的详尽解析
中关于uboot1.6
的分析,看了下中断章节,记录一下.原文已经更新到V1.9
,网上很多流传的是1.6
的,本文作为对齐章节的补充.这里要先明确不同状态下都有哪些独立的寄存器
内存分配
先来看下内部的sp是怎么设置的,这里的用户栈加上中断栈等为128k,是在后面代码分析得出来的
/* Set up the stack */
stack_setup:
ldr r0, _TEXT_BASE /* upper 128 KiB: relocated uboot */
sub r0, r0, #CFG_MALLOC_LEN /* malloc area */
sub r0, r0, #CFG_GBL_DATA_SIZE /* bdinfo */
#ifdef CONFIG_USE_IRQ
sub r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ)
#endif
sub sp, r0, #12 /* leave 3 words for abort-stack */
具体的内存栈
IRQ_STACK_START = _armboot_start - CFG_MALLOC_LEN - CFG_GBL_DATA_SIZE - 4;
FIQ_STACK_START = IRQ_STACK_START - CONFIG_STACKSIZE_IRQ;
流程概览
先来看下整体的流程的代码
普通中断模式
----------------------------------------------------------------------------
正常程序
↓
中断
↓
硬件切换到中断SP
↓
设置SP_irq到中断栈顶
↓
保存r0-r12,使用stmdb r8, {sp, lr}^ 保存 用户模式的sp,lr}^
直接保存 spsr 来保存用户的cpsr
str lr, [r8, #0]
↓
将中断栈sp传递给函数,调用打印
↓
恢复r0-r14(lr),这个lr是用户函数本身的返回
恢复S_PC 到现在的lr 设置pc=lr 返回用户函数 subs pc, lr, #4 sub+s表示更新cpsr
其他异常模式
----------------------------------------------------------------------------
正常程序
↓
异常
↓
切换到异常的sp寄存器,设置到用户栈的栈底
↓
保存 当前的lr=用户的pc
保存 用户的cpsr
↓
切换到系统模式
↓
切换到用户模式的sp寄存器,这里的系统模式和用户模式是公用寄存器的
计算用户栈底到r2,从栈底取出上面存的 用户的pc 和 用户的cpsr
↓
当前的sp指向当前用户栈指针
↓
保存当前的lr 当前的lr这里就是用户模式的lr
保存用户的pc cpsr
保存这个用户栈的指针-----实际也是我们保存这些寄存器 r0-r12,lr,pc,cpsr..的地址
↓
将栈sp传递给函数,调用打印
保存现场,保存用户态的寄存器到异常的栈中
/*
* use bad_save_user_regs for abort/prefetch/undef/swi ...
* use irq_save_user_regs / irq_restore_user_regs for IRQ/FIQ handling
*/
.macro bad_save_user_regs
sub sp, sp, #S_FRAME_SIZE
stmia sp, {r0 - r12} @ Calling r0-r12
ldr r2, _armboot_start
sub r2, r2, #(CONFIG_STACKSIZE+CFG_MALLOC_LEN)
sub r2, r2, #(CFG_GBL_DATA_SIZE+8) @ set base 2 words into abort stack
ldmia r2, {r2 - r3} @ get pc, cpsr
add r0, sp, #S_FRAME_SIZE @ restore sp_SVC
add r5, sp, #S_SP
mov r1, lr
stmia r5, {r0 - r3} @ save sp_SVC, lr_SVC, pc, cpsr
mov r0, sp
.endm
.macro irq_save_user_regs
sub sp, sp, #S_FRAME_SIZE
stmia sp, {r0 - r12} @ Calling r0-r12
add r8, sp, #S_PC
stmdb r8, {sp, lr}^ @ Calling SP, LR
str lr, [r8, #0] @ Save calling PC
mrs r6, spsr
str r6, [r8, #4] @ Save CPSR
str r0, [r8, #8] @ Save OLD_R0
mov r0, sp
.endm
.macro irq_restore_user_regs
ldmia sp, {r0 - lr}^ @ Calling r0 - lr
mov r0, r0
ldr lr, [sp, #S_PC] @ Get PC
add sp, sp, #S_FRAME_SIZE
subs pc, lr, #4 @ return & move spsr_svc into cpsr
.endm
.macro get_bad_stack
ldr r13, _armboot_start @ setup our mode stack
sub r13, r13, #(CONFIG_STACKSIZE+CFG_MALLOC_LEN)
sub r13, r13, #(CFG_GBL_DATA_SIZE+8) @ reserved a couple spots in abort stack
str lr, [r13] @ save caller lr / spsr
mrs lr, spsr
str lr, [r13, #4]
mov r13, #MODE_SVC @ prepare SVC-Mode
@ msr spsr_c, r13
msr spsr, r13
mov lr, pc
movs pc, lr
.endm
.macro get_irq_stack @ setup IRQ stack
ldr sp, IRQ_STACK_START
.endm
.macro get_fiq_stack @ setup FIQ stack
ldr sp, FIQ_STACK_START
.endm
具体的中断函数
/*
* exception handlers
*/
.align 5
undefined_instruction:
get_bad_stack
bad_save_user_regs
bl do_undefined_instruction
.align 5
software_interrupt:
get_bad_stack
bad_save_user_regs
bl do_software_interrupt
.align 5
prefetch_abort:
get_bad_stack
bad_save_user_regs
bl do_prefetch_abort
.align 5
data_abort:
get_bad_stack
bad_save_user_regs
bl do_data_abort
.align 5
not_used:
get_bad_stack
bad_save_user_regs
bl do_not_used
#ifdef CONFIG_USE_IRQ
.align 5
irq:
get_irq_stack
irq_save_user_regs
bl do_irq
irq_restore_user_regs
.align 5
fiq:
get_fiq_stack
/* someone ought to write a more effiction fiq_save_user_regs */
irq_save_user_regs
bl do_fiq
irq_restore_user_regs
#else
.align 5
irq:
get_bad_stack
bad_save_user_regs
bl do_irq
.align 5
fiq:
get_bad_stack
bad_save_user_regs
bl do_fiq
#endif
普通中断
先来分析普通的中断函数
.align 5 ;2^5=32字节对齐
irq:
get_irq_stack ;ldr sp, IRQ_STACK_START ;获得irq的栈
irq_save_user_regs
bl do_irq
irq_restore_user_regs
保存现场
这里使用stmdb r8, {sp, lr}^
保存用户态的寄存器
ldr sp, IRQ_STACK_START ;获得irq的栈
.macro irq_save_user_regs
sub sp, sp, #S_FRAME_SIZE
;sp 空出S_FRAME_SIZE =72大小
;这里的52个字节空间的分配如下:
@
@ IRQ stack frame.
@
#define S_FRAME_SIZE 72
#define S_OLD_R0 68
#define S_PSR 64
#define S_PC 60
#define S_LR 56
#define S_SP 52
@ 这里没有空余的空间
#define S_IP 48
#define S_FP 44
#define S_R10 40
#define S_R9 36
#define S_R8 32
#define S_R7 28
#define S_R6 24
#define S_R5 20
#define S_R4 16
#define S_R3 12
#define S_R2 8
#define S_R1 4
#define S_R0 0
#define MODE_SVC 0x13
#define I_BIT 0x80
;到这里的时候,sp=sp(顶)-S_FRAME_SIZE
stmia sp, {r0 - r12} @ Calling r0-r12
;stmia 是
;IA 每次传送后地址加1;
;IB 每次传送前地址加1;
;DA 每次传送后地址减1;
;DB 每次传送前地址减1;
;FD 满递减堆栈;
;ED 空递减堆栈;
;FA 满递增堆栈;
;EA 空递增堆栈;
;从[sp-S_FRAME_SIZE]的地址开始,存储r0~r12
;由于上面的指令 sp没有!,所以sp本身最后不变,依然是 sp=sp(顶)-S_FRAME_SIZE
add r8, sp, #S_PC
;这里将 sp值指向 S_PC 其实就是接着上面的存,这里直接指向的就是没有存过的sp
;但是本身的sp依然指向 sp=sp(顶)-S_FRAME_SIZE
stmdb r8, {sp, lr}^ @ Calling SP, LR
;这里因为有^ 所以访问的是用户模式的寄存器
;这个是 DB 每次传送前地址减1; 也就是将 sp用户模式 lr用户模式存储
; [r8-1]=sp
; [r8-2]=lr
;但是没有使用! 也就是说r8依然是S_PC,也就是保存了S_PC 也就是用户函数的pc
str lr, [r8, #0] @ Save calling PC
mrs r6, spsr
str r6, [r8, #4] @ Save CPSR
; 把spsr 存到 下一个单元
str r0, [r8, #8] @ Save OLD_R0
;把r0 存到下一个单元
mov r0, sp
; 这里的sp依然是sp=sp(顶)-S_FRAME_SIZE ,也就是我们保存现场的东西的头指针 传递个r0
; 准备给c语言传递参数
.endm
也就是说最终的地址分配是这样的
struct pt_regs {
long uregs[18];
};
#define ARM_cpsr uregs[16]
#define ARM_pc uregs[15]
#define ARM_lr uregs[14]
#define ARM_sp uregs[13]
#define ARM_ip uregs[12]
#define ARM_fp uregs[11]
#define ARM_r10 uregs[10]
#define ARM_r9 uregs[9]
#define ARM_r8 uregs[8]
#define ARM_r7 uregs[7]
#define ARM_r6 uregs[6]
#define ARM_r5 uregs[5]
#define ARM_r4 uregs[4]
#define ARM_r3 uregs[3]
#define ARM_r2 uregs[2]
#define ARM_r1 uregs[1]
#define ARM_r0 uregs[0]
#define ARM_ORIG_r0 uregs[17]
中断函数打印具体寄存器
void do_irq (struct pt_regs *pt_regs)
{
#if defined (CONFIG_USE_IRQ) && defined (CONFIG_ARCH_INTEGRATOR)
/* ASSUMED to be a timer interrupt */
/* Just clear it - count handled in */
/* integratorap.c */
*(volatile ulong *)(CFG_TIMERBASE + 0x0C) = 0;
#else
printf ("interrupt request\n");
show_regs (pt_regs);
bad_mode (); /* 死循环*/
#endif
}
//--------------------------------------------------------------
void show_regs (struct pt_regs *regs)
{
unsigned long flags;
const char *processor_modes[] = {
"USER_26", "FIQ_26", "IRQ_26", "SVC_26",
"UK4_26", "UK5_26", "UK6_26", "UK7_26",
"UK8_26", "UK9_26", "UK10_26", "UK11_26",
"UK12_26", "UK13_26", "UK14_26", "UK15_26",
"USER_32", "FIQ_32", "IRQ_32", "SVC_32",
"UK4_32", "UK5_32", "UK6_32", "ABT_32",
"UK8_32", "UK9_32", "UK10_32", "UND_32",
"UK12_32", "UK13_32", "UK14_32", "SYS_32",
};
//#define condition_codes(regs) ((regs)->ARM_cpsr & (CC_V_BIT|CC_C_BIT|CC_Z_BIT|CC_N_BIT))
flags = condition_codes (regs);
//#define PCMASK 0
//#define pc_pointer(v) ((v) & ~PCMASK)
//#define instruction_pointer(regs) (pc_pointer((regs)->ARM_pc))
printf ("pc : [<%08lx>] lr : [<%08lx>]\n"
"sp : %08lx ip : %08lx fp : %08lx\n",
instruction_pointer (regs),
regs->ARM_lr, regs->ARM_sp, regs->ARM_ip, regs->ARM_fp);
printf ("r10: %08lx r9 : %08lx r8 : %08lx\n",
regs->ARM_r10, regs->ARM_r9, regs->ARM_r8);
printf ("r7 : %08lx r6 : %08lx r5 : %08lx r4 : %08lx\n",
regs->ARM_r7, regs->ARM_r6, regs->ARM_r5, regs->ARM_r4);
printf ("r3 : %08lx r2 : %08lx r1 : %08lx r0 : %08lx\n",
regs->ARM_r3, regs->ARM_r2, regs->ARM_r1, regs->ARM_r0);
printf ("Flags: %c%c%c%c",
flags & CC_N_BIT ? 'N' : 'n',
flags & CC_Z_BIT ? 'Z' : 'z',
flags & CC_C_BIT ? 'C' : 'c', flags & CC_V_BIT ? 'V' : 'v');
printf (" IRQs %s FIQs %s Mode %s%s\n",
interrupts_enabled (regs) ? "on" : "off",
fast_interrupts_enabled (regs) ? "on" : "off",
processor_modes[processor_mode (regs)],
thumb_mode (regs) ? " (T)" : "");
}
恢复现场
这里就是恢复r0-r12
,返回到lr
执行
.macro irq_restore_user_regs
;在irq_save_user_regs 中,sp=sp(顶)-S_FRAME_SIZE
;也就是指向了保存现场区域
ldmia sp, {r0 - lr}^ @ Calling r0 - lr
mov r0, r0
ldr lr, [sp, #S_PC] @ Get PC
add sp, sp, #S_FRAME_SIZE ;这里恢复sp到顶部
subs pc, lr, #4 @ return & move spsr_svc into cpsr
; 这里返回地址是 lr-4,具体是手册确定的
;注意这里有个 sub+s 表示更新cpsr
.endm
其他的返回地址如下
BL MOV PC, R14
SWI MOVS PC, R14_svc
UDEF MOVS PC, R14_und
FIQ SUBS PC, R14_fiq, #4
IRQ SUBS PC, R14_irq, #4
PABT SUBS PC, R14_abt, #4
DABT SUBS PC, R14_abt, #8
软中断
软中断和普通中断应该只是寄存器不太一样,所以流程上是一样的
.align 5
software_interrupt:
get_bad_stack
bad_save_user_regs
bl do_software_interrupt
空间获取
这里的栈分配有点不一样从代码上看,有着模式切换,切换到用户模式后,将sp和cpsr保存到栈底
.macro get_bad_stack
;
ldr r13, _armboot_start @ setup our mode stack
sub r13, r13, #(CONFIG_STACKSIZE+CFG_MALLOC_LEN)
sub r13, r13, #(CFG_GBL_DATA_SIZE+8) @ reserved a couple spots in abort stack
; 这里感觉是一个未知的空间,假设为x 起始
str lr, [r13] @ save caller lr / spsr
; 先存lr,这个lr是用户的pc调用者
mrs lr, spsr
str lr, [r13, #4]
;再存 spsr
mov r13, #MODE_SVC @ prepare SVC-Mode
@ msr spsr_c, r13
msr spsr, r13
;设置了 spsr=MODE_SVC
mov lr, pc ;把pc值 实际是endm的值给lr
movs pc, lr ;把endm后面的值给pc 总的来说还是执行 endm 的语句,
;也就是这两句话 本身对于程序流程没有什么影响,但是能够更新spsr
;注意 接下去切换到了系统模式,sp会是用户模式的sp了
.endm
也就是我们在一段未知的空间,先存储了lr,spsr
关于这里的 mov lr, pc ,movs pc, lr
参考这里,用这两条指令可以更新cpsr的标志位。
假设
100:mov lr, pc
104:movs pc,lr
108:xxx
mov lr, pc实际的pc其实等于pc+8即lr=108,后面的movs pc,lr就是跳转到108运行。
关于这里的movs指令解释如下,在ARM指令集E004armproc.chm
任何带 S 位设置的到 R15 的 32-bit 写(MOVS、ORRS、TEQP、LDM...^) 将传送当前模式的 SPSR 到 CPSR 中。
例如,假定我们在 irq_32 模式下:
MOVS PC, R14
将复制 R14 到 PC,并接着复制 SPSR_IRQ32 到 CPSR。
这在 USR 模式下不是非常有用因为它没有 SPSR!
保存现场
这里
.macro get_bad_stack
;
ldr r13, _armboot_start @ setup our mode stack
sub r13, r13, #(CONFIG_STACKSIZE+CFG_MALLOC_LEN)
sub r13, r13, #(CFG_GBL_DATA_SIZE+8) @ reserved a couple spots in abort stack
; 这里感觉是一个未知的空间,假设为x 起始
str lr, [r13] @ save caller lr / spsr
; 先存lr
;ARM处理器相应异常时,会自动完成将当前的PC保存到LR寄存器。
;也就是说 这个lr就是返回用户的地址
mrs lr, spsr
str lr, [r13, #4]
;再存 用户的cpsr
mov r13, #MODE_SVC @ prepare SVC-Mode
@ msr spsr_c, r13
msr spsr, r13
;设置了 spsr=MODE_SVC
mov lr, pc ;把pc值 实际是endm的值给lr 也就是下面的lablexxx
movs pc, lr ;把endm后面的值给pc 总的来说还是执行 endm 的语句,
;也就是这两句话 本身对于程序流程没有什么影响,但是能够更新spsr
;注意 接下去切换到了系统模式,sp会是用户模式的sp了
.endm
------------------------------------------------------------------------------
接下去就切换到用户模式了
sp 切换到用户模式的sp
lr这里应该是切换回用户自己原来的lr了,也就是说切换模式并不会赋值lr
lablexxx
.macro bad_save_user_regs
;这里的sp是用户模式的sp了,也就是在用户区域开辟一个 S_FRAME_SIZE 的空间
sub sp, sp, #S_FRAME_SIZE
stmia sp, {r0 - r12} @ Calling r0-r12
;这里的 r0~r12 是一样的
ldr r2, _armboot_start
sub r2, r2, #(CONFIG_STACKSIZE+CFG_MALLOC_LEN)
sub r2, r2, #(CFG_GBL_DATA_SIZE+8) @ set base 2 words into abort stack
; r2指向3word
ldmia r2, {r2 - r3} @ get pc, cpsr ,
; 在上面的函数get_bad_stack ,我们在这个地方存储了`lr,spsr` 这个是用户态的 pc cpsr
;IA 每次传送后地址加1
add r0, sp, #S_FRAME_SIZE @ restore sp_SVC
;sp 回到用户原来的sp尾巴
add r5, sp, #S_SP
;r5 这个区用来存 进入异常前的用户原来的sp
mov r1, lr
;到这里为止
; r0 =sp, #S_FRAME_SIZE =进入异常前的用户原来的sp
; r1 =lr 这个lr应该就是用户模式的lr,切换回用户模式后,并没有对他操作过
; r2 =pc 这个是进入异常前的用户原来的pc断点
; r3 =cpsr 用户的cpsr
stmia r5, {r0 - r3} @ save sp_SVC, lr_SVC, pc, cpsr
mov r0, sp
;这里的sp 实际上是用户栈
.endm
附录速记
手动设置cpsr到中断模式 应该是不会跳转到中断向量表,只是模式变了
任何带 S 位设置的到 R15 的 32-bit 写(MOVS、ORRS、TEQP、LDM...^) 将传送当前模式的 SPSR 到 CPSR 中。
疑惑待解
从我画的图中可以看到,作者的意思应该是保留12个字节给bad_stack模式用,但实际代码是存在栈底的,是否是我哪里理解错了
uboot中的中断macro宏的更多相关文章
- uboot中添加FIQ中断及相关问题
本文主要说明了在uboot中添加FIQ中断时遇到的问题以及对应的解决办法. 首先交代一下项目的软硬件环境.硬件方面,使用s3c2440作为主控芯片,外接串口.网卡等设备.软件方面,主控芯片上电后运行u ...
- [Umbraco] macro(宏)在umbraco中的作用
macro在umbraco中是一个核心的应用,它是模板页中用于动态加载内容的标签(模板指令),宏可以是基于XSLT文件创建,亦可以是基于ASP.NET用户控件创建 在develop下的Macros中创 ...
- Uboot中start.S源码的指令级的详尽解析【转】
本文转载自:http://www.crifan.com/files/doc/docbook/uboot_starts_analysis/release/html/uboot_starts_analys ...
- linux-2.6.26内核中ARM中断实现详解(转)
转载:http://www.cnblogs.com/leaven/archive/2010/08/06/1794293.html 更多文档参见:http://pan.baidu.com/s/1dDvJ ...
- tiny4412 串口驱动分析一 --- u-boot中的串口驱动
作者:彭东林 邮箱:pengdonglin137@163.com 开发板:tiny4412ADK+S700 4GB Flash 主机:Wind7 64位 虚拟机:Vmware+Ubuntu12_04 ...
- VC中预处理指令与宏定义详解
刚接触到MFC编程的人往往会被MFC 向导生成的各种宏定义和预处理指令所吓倒,但是预处理和宏定义又是C语言的一个强大工具.使用它们可以进行简单的源代码控制,版本控制,预警或者完成一些特殊的功能. 一个 ...
- u-boot中nandflash初始化流程分析(转)
u-boot中nandflash初始化流程分析(转) 原文地址http://zhuairlunjj.blog.163.com/blog/static/80050945201092011249136/ ...
- C中的预编译宏定义
可以用宏判断是否为ARC环境 #if _has_feature(objc_arc) #else //MRC #endif C中的预编译宏定义 -- 作者: infobillows 来源:网络 在将一 ...
- C++ 中常见预定义宏的使用
http://blog.csdn.net/hgl868/article/details/7058906 替代字符串: #define DOWNLOAD_IMAGE_LOG /var/log/png.l ...
随机推荐
- @Resource 与 @Service注解的区别
pring中什么时候用@Resource,什么时候用@service当你需要定义某个类为一个bean,则在这个类的类名前一行使用@Service("XXX"),就相当于讲这个类定义 ...
- MyDAL - .IsExistAsync() 使用
索引: 目录索引 一.API 列表 .IsExistAsync() 用于 单表 / 多表连接 查询 二.API 单表-便捷 方法 举例 1.单表-便捷, 判断是否存在方法 var date = Dat ...
- hbase rowkey 的设计
什么是rowkey Hbase是一个分布式的.面向列的数据库,它和一般关系型数据库的最大区别是:HBase很适合于存储非结构化的数据,还有就是它基于列的而不是基于行的模式. Hbase是采用K,V存储 ...
- Storm入门(一)原理介绍
问题导读:1.hadoop有master与slave,Storm与之对应的节点是什么?2.Storm控制节点上面运行一个后台程序被称之为什么?3.Supervisor的作用是什么?4.Topology ...
- lombok使用
下载地址 链接:https://pan.baidu.com/s/19Rz7sgasVv5Gc7vw1A4whA 提取码:6bgg lombok的安装: 使用lombox是需要安装的,如果不安装,IDE ...
- (转)sizeof()和_countof()区别
先看程序: #include <iostream> using namespace std; int main(int argc, char* argv[]) { char *a = &q ...
- CentOS7.x安装kickstart
kickstart简介 kickstartkickstart是RedHat公司开源的软件,所以对CentOS兼容性最好,是一种无人值守的安装方式就是将手动安装配置的步骤,记录到文件中,然后通过kick ...
- 安装maven,并配置eclipse
平台 ubuntu 18.04 + Java 8 下载并安装Maven 下载页面:http://maven.apache.org/download.cgi 我这里使用写博客是最新的版本3.6.1,选择 ...
- Appium could not connect to server are you sure it's running appium desktop
use remote host value : 127.0.0.1 switch to Custom server to Automatic server adb kill-server adb st ...
- apache+php项目部署
先安装apache和php然后进行如下操作(以63服务器的安装路径为例) 1.查看php项目运行的报错信息 路径: cd /var/log/httpd/error_log 如果错误如下: 可以尝试 ...