转自:https://blog.csdn.net/zgtzqzg2020/article/details/105294193
一、ARMv7 Cortex-A系列处理器寄存器组介绍及其功能介绍
1. ARMv7 Cortex-A处理器一般共有37寄存器,其中包括:
(1) 31个通用寄存器,包括PC(程序计数器)在内,都是32位的寄存器。
(2) 6个状态寄存器,都是32位的寄存器。
2. ARMv7 Cortex-A系列处理器的模式
ARMv7 Cortex-A系列处理器共有7种处理器模式分别是:用户模式(User)、快速中断模式(FIQ)、普通中断模式(IRQ)、管理模式(Supervisor SVC)、数据访问中止模式(Abort)、未定义指令中止模式(Undefined)、系统模式(System)。在每一种处理器模式中有一组相应的寄存器。在任意一种寄存器模式下,可见的寄存器包括15个通用寄存器(R0~R14)、程序计数器(PC)、一个或者两个状态寄存器(CPSR、SPSR)。在所有寄存器中,有些是各个模式共用同一个物理寄存器,有些寄存器是各个模式自己拥有独立的物理寄存器。各种模式下的寄存器组如下入所示。
其中R0~R3主要用于子程序间传递参数,R4~R11主要用于保存局部变量, 但在Thumb程序中,通常智能使用R4~R7来保存局部变量;R12(Intra-Procedure-call scratch register,详细介绍参见"Procedure Call Standard for the ARM Architecture",)用作子程序间的scratch 寄存器,即IP;R13通常用作栈指针,即SP;R14寄存器又被称为连接寄存器,即LR,用于保存子程序以及中断的返回地址;R15用作程序计数器(PC),由于ARM采用流水线机制,PC的值当前正在指令地址加8个字节,即PC指向当前指令的下两条指令地址。CPSR和SPSR都是程序状态寄存器,其中SPSR是用来保存中断前的CPSR中的值,一边在中断返回后恢复处理器状态。
3. CPSR寄存器详解
所有处理器模式下都可访问当前程序状态寄存器CPSR。CPSR中包含条件码标志、中断禁止位、当前处理器模式以及其他状态和控制信息。在每种异常模式下都有一个对应的程序状态寄存器SPSR。当异常出现时,SPSR用于保存CPSR的状态,以便异常返回后恢复异常发生时的工作状态。
(1)条件码标志
程序状态寄存器CPSR的最高4位N、Z、C、V是条件码标志。ARM的大多数指令可条件执行,即通过检测这些条件码标志来决定程序指令如何执行。
各个标志的含义如下:
N: 在结果有符号的二进制补码的情况下,如果结果为负数,则N=1;如果为非负数,则N=0.
Z:如果结果为0,则Z=1;如果结果为非零,则Z=0;
C:其设置分一下几种情况:
(1)对于加法指令(包括比较指令CMN),如果产生进位,则C=1;否则C=0。
(2)对于减法指令(包括比较指令CMP),如果产生借位,则C=0;否则C=1。
(3)对于有移位操作的非法指令,C为移位操作中最后移出位的值。
(4) 对于其他指令,C通常不变。
V:对于加减法指令,在操作数和结果是有符号的整数时,如果发生溢出,则V=1;如果无溢出发生,则V=0;对于其他指令,V通常不发生变化。
(2)控制位的作用在上图中可以看出来。
4. CPSR与CPSR_c的区别
CPSR有4个8位区域:标志域(F)、状态域(S)、扩展域(X)、控制域(C).
C控制域屏蔽字节(CPSR[7:0])
X扩展域屏蔽字节(CPSR[15:8])
S状态域屏蔽字节(CPSR[23:16])
F标志域屏蔽字节(CPSR[31:24])
常用于MRS或MSR指令,用于CPSR的值转移到寄存器或把寄存器的内容加载到CPSR中。如:
MSR CPSR_c , #0xd3
二、Linux ARMv7 Cortex-A系列处理器中断向量表处理和代码分析
1. ARMv7 Cortex-A系列处理器打开关闭irq中断
ARMv7 Cortex-A系列处理器打开关闭irq中断是通过改变CPSR寄存器的bit7位完成的。
开启和关闭当前处理器的本地中断,会产生中断信号,但不处理 。
local_irq_disable()关闭中断指令:cpsid i;
local_irq_enable()开启中断指令:cpsie i;
关闭和开启中断,不会产生中断信号。
disable_irq/enable_irq
2. linux系统为了实现异常处理引入了栈帧的概念
-
// arch/arm/include/uapi/asm/ptrace.h
-
/*
-
* This struct defines the way the registers are stored on the
-
* stack during a system call. Note that sizeof(struct pt_regs)
-
* has to be a multiple of 8.
-
*/
-
#ifndef __KERNEL__
-
struct
pt_regs {
-
long uregs[
18];
-
};
-
#endif /* __KERNEL__ */
-
-
#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]
-
-
-
//arch/arm/kernel/asm-offsets.c
-
DEFINE(S_R0,
offsetof(
struct pt_regs, ARM_r0));
-
DEFINE(S_R1,
offsetof(
struct pt_regs, ARM_r1));
-
DEFINE(S_R2,
offsetof(
struct pt_regs, ARM_r2));
-
DEFINE(S_R3,
offsetof(
struct pt_regs, ARM_r3));
-
DEFINE(S_R4,
offsetof(
struct pt_regs, ARM_r4));
-
DEFINE(S_R5,
offsetof(
struct pt_regs, ARM_r5));
-
DEFINE(S_R6,
offsetof(
struct pt_regs, ARM_r6));
-
DEFINE(S_R7,
offsetof(
struct pt_regs, ARM_r7));
-
DEFINE(S_R8,
offsetof(
struct pt_regs, ARM_r8));
-
DEFINE(S_R9,
offsetof(
struct pt_regs, ARM_r9));
-
DEFINE(S_R10,
offsetof(
struct pt_regs, ARM_r10));
-
DEFINE(S_FP,
offsetof(
struct pt_regs, ARM_fp));
-
DEFINE(S_IP,
offsetof(
struct pt_regs, ARM_ip));
-
DEFINE(S_SP,
offsetof(
struct pt_regs, ARM_sp));
-
DEFINE(S_LR,
offsetof(
struct pt_regs, ARM_lr));
-
DEFINE(S_PC,
offsetof(
struct pt_regs, ARM_pc));
-
DEFINE(S_PSR,
offsetof(
struct pt_regs, ARM_cpsr));
-
DEFINE(S_OLD_R0,
offsetof(
struct pt_regs, ARM_ORIG_r0));
-
DEFINE(S_FRAME_SIZE,
sizeof(
struct pt_regs));
-
3. ARM异常处理类型和模式
ARM的各种异常类型和每种异常类型处于的处理器模式,如下表所示:
4. ARM中断处理汇编代码分析(基于LINUX 4.4.49内核分析)
4.1 arm中断处理总入口
-
//arch/arm/kernel/entry-armv.S
-
/*
-
*注释:
-
* 1)ARM架构异常处理向量表起始地址__vectors_start(定义在arch/arm/kernel/vmlinux.lds).
-
* 2)ARM架构定义7种异常包括中断、系统调用、缺页异常等,发生异常时处理器会跳转到相应入口。
-
* 3)异常向量表的起始位置由CP15协处理器的控制寄存器C1的bit13决定:
-
* v=0,Normal exception vectors, base address 0x00000000. Software can remap this
-
* base address using the VBAR(CP15 C12寄存器);
-
* v=1,High exception vectors, base address 0xFFFF0000-0xFFFF001C. This base address
-
* is never remapped.
-
* The primary input VINITHI defines the reset value of the V bit.
-
* VINITHI: Controls the location of the exception vectors at reset:
-
* 0 = starts exception vectors at address 0x00000000
-
* 1 = starts exception vectors at address 0xFFFF0000.
-
* This pin is only sampled during reset of the processor
-
*/
-
.section .vectors,
"ax", %progbits
-
__vectors_start:
-
W(b)
vector_rst
-
W
(b) vector_und
-
/*
-
*系统调用入口点:
-
* __vectors_start + 0x1000 = __stubs_start(由arch/arm/kernel/vmlinux.lds链接脚本可知)
-
* 此时PC指向系统调用异常的处理入口:vector_swi用户态通过swi指令产生软中断。因为vector_swi系统
-
* 调用异常代码在(arch/arm/kernel/entry-common.S),其入口地址与异常向量相隔较远,使用b指令无
-
* 法跳转过去。b指令只能相对当前PC跳转 +/-32M范围)。
-
*/
-
W
(ldr) pc, __vectors_start + 0x1000
-
W
(b) vector_pabt
//取指令异常
-
W
(b) vector_dabt
//数据异常--缺页异常
-
W
(b) vector_addrexcptn
-
W
(b) vector_irq
//irq中断异常
-
W
(b) vector_fiq
4.2 以vector_irq为例进行深入分析
vector_irq是通过vector_stub宏定义的,vector_stub宏定义尤为关键,ARM任何异常都是通过将r0,lr,spsr保存到异常模式的栈中(每种异常模式都有自己的栈,栈的初始化在cpu_init,见下面分析), vector_stub通过vector_\name实现其功能。
-
//arch/arm/kernel/entry-armv.S
-
/*
-
*注释:
-
*当irq发生时,硬件自动完成如下操作:
-
*1. arm在irq模式下有自己的lr寄存器lr_irq、spsr_irq、sp_irq.
-
* r14_irq = lr_irq = address pf next instruction to be executed+4;
-
*2. spsr_irq = cpsr,保存了处理器当前的状态,中断屏蔽位以及各种条件标志位,保存后cpsr会切换到
-
* irq模式。
-
*3. cpsr[4 :0] = 0b10010,设置arm为irq模式
-
*4. cpsr[5] = 0,arm状态执行
-
*5. cpsr[7] = 1,禁止irq
-
*6. pc = 0xffff0018(High exception vectors,取决于CP15协处理器的C1寄存器的配置,参看上面的分
-
* 析), 将pc值设置成异常中断的中断向量地址,即vectot_irq.
-
*/
-
/*
-
* Interrupt dispatcher
-
*/
-
vector_stub irq, IRQ_MODE,
4
-
-
.
long __irq_usr @
0 (USR_26 / USR_32) 从用户态下进入的irq,执行__irq_usr代码
-
.
long __irq_invalid @
1 (FIQ_26 / FIQ_32)
-
.
long __irq_invalid @
2 (IRQ_26 / IRQ_32)
-
.
long __irq_svc @
3 (SVC_26 / SVC_32) 从内核态下进入的irq,执行__irq_svc代码
-
.
long __irq_invalid @
4
-
.
long __irq_invalid @
5
-
.
long __irq_invalid @
6
-
.
long __irq_invalid @
7
-
.
long __irq_invalid @
8
-
.
long __irq_invalid @
9
-
.
long __irq_invalid @ a
-
.
long __irq_invalid @ b
-
.
long __irq_invalid @ c
-
.
long __irq_invalid @ d
-
.
long __irq_invalid @ e
-
.
long __irq_invalid @ f
sp在不同的模式下有不同寄存器,在cpu_init中进行初始化。
-
/* arch/arm/kernel/setup.c
-
* cpu_init - initialise one CPU.
-
*
-
* cpu_init sets up the per-CPU stacks.
-
*/
-
void notrace cpu_init(void)
-
{
-
#ifndef CONFIG_CPU_V7M
-
unsigned
int cpu =
smp_processor_id();
-
struct
stack *stk = &stacks[cpu];
-
-
if (cpu >= NR_CPUS) {
-
pr_crit(
"CPU%u: bad primary CPU number\n", cpu);
-
BUG();
-
}
-
/*
-
* This only works on resume and secondary cores. For booting on the
-
* boot cpu, smp_prepare_boot_cpu is called after percpu area setup.
-
*/
-
set_my_cpu_offset(
per_cpu_offset(cpu));
-
cpu_proc_init();
-
-
/*
-
* Define the placement constraint for the inline asm directive below.
-
* In Thumb-2, msr with an immediate value is not allowed.
-
*/
-
#ifdef CONFIG_THUMB2_KERNEL //此宏为定义
-
#define PLC "r"
-
#else
-
#define PLC "I" //表示是立即数,其他定义可以查看GCC ARM C语言嵌入汇编语法
-
#endif
-
/*
-
* setup stacks for re-entrant exception handlers
-
* 修改几种模式下的sp指向struct stack结构体类型变量stacks中定义的各个变量,每种模式下的栈为3个字
-
*/
-
__asm__ (
-
"msr cpsr_c, %1\n\t"
//msr cpsr_c,(PSR_F_BIT|PSR_I_BIT|IRQ_MODE)切换为irq模式
-
"add r14, %0, %2\n\t"
//r14 = r +offset(struct stack, irq[0])
-
"mov sp, r14\n\t"
-
"msr cpsr_c, %3\n\t"
//msr cpsr_c,(PSR_F_BIT|PSR_I_BIT|ABT_MODE)切换为abt模式
-
"add r14, %0, %4\n\t"
//r14 = r +offset(struct stack, abt[0])
-
"mov sp, r14\n\t"
-
"msr cpsr_c, %5\n\t"
//msr cpsr_c,(PSR_F_BIT|PSR_I_BIT|UND_MODE)切换为und模式
-
"add r14, %0, %6\n\t"
//r14 = r +offset(struct stack, und[0])
-
"mov sp, r14\n\t"
-
"msr cpsr_c, %7\n\t"
//msr cpsr_c,(PSR_F_BIT|PSR_I_BIT|FIQ_MODE)切换为fiq模式
-
"add r14, %0, %8\n\t"
//r14 = r +offset(struct stack, fiq[0])
-
"mov sp, r14\n\t"
-
"msr cpsr_c, %9"
//msr cpsr_c,(PSR_F_BIT|PSR_I_BIT|FIQ_MODE)切换为svc模式
-
:
-
:
"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 | FIQ_MODE),
-
"I" (
offsetof(
struct stack, fiq[
0])),
-
PLC (PSR_F_BIT | PSR_I_BIT | SVC_MODE)
-
:
"r14");
-
#endif
-
}
-
-
-
//arch/arm/kernel/setup.c
-
struct
stack {
-
u32 irq[
3];
-
u32 abt[
3];
-
u32 und[
3];
-
u32 fiq[
3];
-
} ____cacheline_aligned;
-
#ifndef CONFIG_CPU_V7M
-
static
struct
stack stacks[NR_CPUS];
-
#endif
-
-
-
//arch/arm/include/uapi/asm/ptrace.h
-
#define USR26_MODE 0x00000000
-
#define FIQ26_MODE 0x00000001
-
#define IRQ26_MODE 0x00000002
-
#define SVC26_MODE 0x00000003
-
#define USR_MODE 0x00000010
-
#define FIQ_MODE 0x00000011
-
#define IRQ_MODE 0x00000012
-
#define SVC_MODE 0x00000013
-
#define ABT_MODE 0x00000017
-
#define UND_MODE 0x0000001b
-
#define SYSTEM_MODE 0x0000001f
-
#define MODE32_BIT 0x00000010
-
#define MODE_MASK 0x0000001f
-
#define PSR_T_BIT 0x00000020
-
#define PSR_F_BIT 0x00000040
-
#define PSR_I_BIT 0x00000080
-
#define PSR_A_BIT 0x00000100
-
#define PSR_E_BIT 0x00000200
-
#define PSR_J_BIT 0x01000000
-
#define PSR_Q_BIT 0x08000000
-
#define PSR_V_BIT 0x10000000
-
#define PSR_C_BIT 0x20000000
-
#define PSR_Z_BIT 0x40000000
-
#define PSR_N_BIT 0x80000000
vector_stub宏定义的分析如下:
-
//arch/arm/kernel/entry-armv.S
-
/*
-
*注释:
-
*1. 该接口负责保存异常发生前一时刻cpu寄存器到异常模式的栈中,保存r0,lr,spsr寄存器的值到
-
* sp_dabt或sp_irq上。
-
*2. 此时的sp是异常状态下的sp,这个栈只有12byte大小,在cpu_init()中初始化。
-
*3. arm在irq/svc/abort几种模式下sp是不能共用的。
-
*4. 此时lr中保存的实际上是异常的返回地址,异常发生,切换到svc模式后,会将lr保存到svc模式栈中
-
* (pt_reg->pc),最后从异常返回时再将pt_reg->pc加载如arm寄存器pc中,实现异常返回。本函数只是
-
* 其中一个步骤,即为将异常发生时刻lr保存到svc模式栈中(pt_reg->pc)做准备。
-
*5. spsr是异常发生那一刻(即进入异常模式前是什么模式)的cpsr状态,如内核态下发生中断,则spsr是
-
* svc模式下10011,如用户态下发生中断,则spsr是user模式10000。
-
*6. 此时cpu正处于异常状态(如中断),此时cpsr为10010。
-
*7. 要进行真正的异常处理,需要退出异常模式进入svc模式。
-
*/
-
/*
-
* Vector stubs.
-
*
-
* This code is copied to 0xffff1000 so we can use branches in the
-
* vectors, rather than ldr's. Note that this code must not exceed
-
* a page size.
-
*
-
* Common stub entry macro:
-
* Enter in IRQ mode, spsr = SVC/USR CPSR, lr = SVC/USR PC
-
* * SP points to a minimal amount of processor-private memory, the address
-
* of which is copied into r0 for the mode specific abort handler.
-
*/
-
.macro vector_stub, name, mode, correction=
0
-
.align
5
//强制对齐32字节对齐
-
vector_\name:
-
.
if \correction
-
/*
-
*需要调整返回值,对应irq异常将lr减去4,因为异常发生时,arm将pc地址+4赋值给了lr。
-
*/
-
sub lr, lr, #\correction
-
.endif
-
-
@
-
@ Save r0,
lr_<exception> (parent PC)
and spsr_<exception>
-
@ (parent CPSR)
-
@
-
/*
-
*1. spsr中保存异常发生时刻的cpsr。
-
*2. 此时的栈sp是异常时(irq mode或abt mode)的栈sp和svc mode里的栈sp不同。
-
*3. save r0,lr;将r0和lr保存到异常模式的栈上[sp] = r0;[sp+4] = lr_irq;
-
* stmia sp,{r0, lr}没有sp!,因此sp不变。
-
*4. r0也要入栈,r0用作传递参数(异常状态下的sp)。
-
*/
-
stmia sp, {r0, lr} @ save r0, lr
-
mrs lr, spsr
//得到异常发生时所处模式得信息
-
/*
-
*将spsr保存到异常模式的栈上[sp+8]=spsr_irq=lr
-
*/
-
str lr, [sp, #
8] @ save spsr
-
-
@
-
@ Prepare
for SVC32 mode. IRQs remain disabled.
-
@
-
/*
-
*cpsr中保存的是异常模式:如irq 10010;dabt 10111
-
*/
-
mrs r0, cpsr
-
/*
-
*1. dabt处理时,r0=r0^(0x17^0x13)=r0^0x4,bit3取反之后10011变为svc模式;
-
*2. irq处理时:r0=10010=r0^(0x12^0x13)=r0^0x1=10011变为svc模式
-
*/
-
eor r0, r0, #(\mode ^ SVC_MODE | PSR_ISETSTATE)
-
msr spsr_cxsf, r0
-
-
@
-
@ the branch table must immediately follow
this code
-
@
-
and lr, lr, #
0x0f
//用户态(user mode)lr=0;内核态(svn mode)lr=3;
-
THUMB( adr r0,
1f )
-
THUMB( ldr lr, [r0, lr, lsl #
2] )
-
-
/*
-
*r0=sp;
-
*注意:
-
*1. 此时r0中保存了异常状态下sp栈地址,这个栈上保存了r0,lr(异常返回地址),spsr(异常发生时,cpu
-
* 的状态,当然异常返回时需要恢复该状态)
-
*2. 之后的函数会把r0中保存的异常模式的sp上信息,加载到svc模式下的sp栈上。异常处理返回时再将svc
-
* mode 的栈加载到arm寄存器上。
-
*/
-
mov r0, sp
-
-
/*
-
*lr中保存发生异常时arm的cpsr状态到spsr
-
*1. usr模式发生异常则lr=10000&0x0f;lr=pc+lr<<2 pc+0时执行 __irq_usr;
-
*2. svc模式发生异常则lr=10011&0x0f;lr=pc+lr<<2 pc+12时执行 __irq_svc
-
*/
-
ARM( ldr lr, [pc, lr, lsl #
2] )
-
-
/* movs中s表示把spsr恢复给cpsr,上面可知spsr保存的是svc模式,不过此时中断还是关闭的
-
* 异常处理一定要进入svc模式原因:
-
*(1)异常处理一定要PL1特权级。
-
*(2)使能嵌套中断。
-
* 如果一个中断模式(例如用户态发生中断,arm从usr进入irq模式)中重新允许中断,
-
* 且这个中断模式中使用了bl指令,bl会把pc放到lr_irq中,这个地址会被当前模式下产生的中断破坏
-
* 这种情况下中断无法返回。所以为了避免这种情况,中断处理过程应该切换到svc模式,bl指令可以把
-
* pc(即子程序返回地址)保存到lr_svc.
-
*/
-
movs pc, lr @ branch to handler in SVC mode
-
ENDPROC(vector_\name)
vector_irq完整代码 (vector_stub irq, IRQ_MODE, 4)
-
//arch/arm/kernel/entry-armv.S
-
vector_irq:
-
sub lr, lr,
4
-
stmia sp, {r0, lr} @ save r0, lr
-
mrs lr, spsr
-
str lr, [sp, #
8] @ save spsr
-
mrs r0, cpsr
//读取现在cpsr寄存器,
-
eor r0, r0, #
1
//r0的第0位翻转,在cpsr的模式M定义 usr:0b10000; svc:0b10011;
-
msr spsr_cxsf, r0
//r0寄存器写入spsr
-
/*
-
*异常发生时cpsr被保存到irq模式下的spsr中,在mrs lr, spsr中将spsr保存到lr中,
-
*因为linux用户态处于usr模式,内核态处于svc模式,这两种模式在cpsr模式控制域M中只有最低两位不同,
-
*得到低四位,就可以判断进入异常前处于usr模式还是svc模式,从判断是执行下面的__irq_usr还是
-
*__irq_svc
-
*/
-
and lr, lr, #
0x0f
-
-
-
mov r0, sp
-
ldr lr, [pc, lr, lsl #
2]
-
movs pc, lr @ branch to handler in SVC mode
//发生条状,进入svc模式
-
-
.
long __irq_usr @
0 (USR_26 / USR_32) 从用户态下进入的irq,执行__irq_usr代码
-
.
long __irq_invalid @
1 (FIQ_26 / FIQ_32)
-
.
long __irq_invalid @
2 (IRQ_26 / IRQ_32)
-
.
long __irq_svc @
3 (SVC_26 / SVC_32) 从内核态下进入的irq,执行__irq_svc代码
-
.
long __irq_invalid @
4 .
long __irq_invalid @
5
-
.
long __irq_invalid @
6
-
.
long __irq_invalid @
7
-
.
long __irq_invalid @
8
-
.
long __irq_invalid @
9
-
.
long __irq_invalid @ a
-
.
long __irq_invalid @ b
-
.
long __irq_invalid @ c
-
.
long __irq_invalid @ d
-
.
long __irq_invalid @ e
-
.
long __irq_invalid @ f
从用户态进入irq中断执行的是irq_usr代码
-
//arch/arm/kernel/entry-armv.S
-
//此时arm处于svc模式执行下面代码
-
__irq_usr:
-
usr_entry
-
kuser_cmpxchg_check
-
irq_handler
//irq处理函数
-
get_thread_info tsk
-
mov why, #
0
-
b ret_to_user_from_irq
//中断处理完成返回
-
UNWIND(.fnend )
-
ENDPROC(__irq_usr)
/*
-
* User mode handlers
-
*
-
* EABI note: sp_svc is always 64-bit aligned here, so should S_FRAME_SIZE
-
*/
-
#if defined(CONFIG_AEABI) && (__LINUX_ARM_ARCH__ >= 5) && (S_FRAME_SIZE & 7)
-
#error "sizeof(struct pt_regs) must be a multiple of 8"
-
#endif
-
-
.macro usr_entry, trace=
1, uaccess=
1
-
UNWIND(.fnstart )
-
UNWIND(.cantunwind ) @ don
't unwind the user space
-
/*
-
*arch/arm/kernel/asm-offsets.c中定义DEFINE(S_FRAME_SIZE,sizeof(struct pt_regs))
-
*S_FRAME_SIZE=72
-
*/
-
sub sp, sp, #S_FRAME_SIZE
-
-
/*
-
*注释:
-
*stmib r0!,{r1,r2} 将r1,r2的值保存到r0指向的存储单元中(r0自动加4)
-
*将r1-r12全部入栈,因为没有!号所以入完栈后sp不变化,因为r1-r12在所有模式下是一组,
-
*此时r1-r12里的值还是进入irq中断时的值,将这些值保存到svc模式下的栈中
-
*/
-
ARM( stmib sp, {r1 - r12} )
-
THUMB( stmia sp, {r0 - r12} )
-
/*
-
*注释:(cortex_a8_r3p2.pdf Cortex™-A8 Technical Reference Manual)
-
*To access the Control Register, read or write CP15 with:
-
*MRC p15, 0, <Rd>, c1, c0, 0 ; Read Control Registe
-
*/
-
ATRAP( mrc p15,
0, r7, c1, c0,
0)
-
-
/*
-
*注释:
-
*.LCcralign:
-
* .word cr_alignment
-
*/
-
ATRAP( ldr r8, .LCcralign)
-
/*
-
*注释:
-
*ldmia r0!,{r1,r2} 将r0指向的单元中的数据读出到r1,r2中(r0自动加4)
-
*因r0为irq模式时sp的值,所以就是将irq模式下sp栈的内容保存到r3、r4、r5中,其中的内容irq异常发生
-
*时r0、lr、cpsr。
-
*/
-
ldmia r0, {r3 - r5}
-
add r0, sp, #S_PC @ here
for interlock avoidance
-
mov r6, #
-1 @
""
""
""
""
-
-
str r3, [sp] @ save the
"real" r0 copied
-
@ from the exception stack
-
-
ATRAP( ldr r8, [r8, #
0])
-
@
-
@ We are now ready to fill in the remaining blanks on the stack:
-
@
-
@ r4 - lr_<exception>, already fixed up
for correct
return/restart
-
@ r5 - spsr_<exception>
-
@ r6 -
orig_r0 (see pt_regs definition in ptrace.h)
-
@
-
@ Also, separately save sp_usr
and lr_usr
-
@
-
stmia r0, {r4 - r6}
-
ARM( stmdb r0, {sp, lr}^ )
-
THUMB( store_user_sp_lr r0, r1, S_SP - S_PC )
-
-
.
if \uaccess
-
uaccess_disable ip
-
.endif
-
-
@ Enable the alignment trap
while in kernel mode
-
ATRAP( teq r8, r7)
-
ATRAP( mcrne p15,
0, r8, c1, c0,
0)
-
@
-
@ Clear FP to mark the first stack frame
-
@
-
zero_fp
-
.
if \trace
-
#ifdef CONFIG_TRACE_IRQFLAGS
-
bl trace_hardirqs_off
-
#endif
-
ct_user_exit save =
0
-
.endif
-
.endm
-
-
.macro kuser_cmpxchg_check
-
#if !defined(CONFIG_CPU_32v6K) && defined(CONFIG_KUSER_HELPERS)
-
#ifndef CONFIG_MMU
-
#warning "NPTL on non MMU needs fixing"
-
#else
-
@ Make sure our user space atomic helper is restarted
-
@
if it was interrupted in a critical region. Here we
-
@ perform a quick test
inline since it should be
false
-
@
99.9999% of the time. The rest is done out of line.
-
cmp r4, #TASK_SIZE
-
blhs kuser_cmpxchg64_fixup
-
#endif
-
#endif
-
.endm
irq_handler 宏为中断处理的关键部分,从这里会进入C语言编写的代码,进入linux对中断的通用处理框架里。
-
//arch/arm/kernel/entry-armv.S
-
/*
-
*注释:
-
*宏CONFIG_MULTI_IRQ_HANDLER在.config中有定义,会将handle_arch_irq里的值赋值给pc去执行。
-
*给handle_arch_irq请看后面的C语言阶段分析
-
*/
-
/*
-
* Interrupt handling.
-
*/
-
.macro irq_handler
-
#ifdef CONFIG_MULTI_IRQ_HANDLER
-
ldr r1, =handle_arch_irq
-
mov r0, sp
-
badr lr,
9997f
-
ldr pc, [r1]
//进入C语言阶段的中断处理
-
#else
-
arch_irq_handler_default
-
#endif
-
9997:
-
.endm
-
-
-
//arch/arm/kernel/entry-armv.S
-
#ifdef CONFIG_MULTI_IRQ_HANDLER
-
.globl handle_arch_irq
-
handle_arch_irq:
-
.space
4
-
#endif
以上分析是linux对于armv架构中断通用处理的代码,该部分代码跟arm架构紧密相关,此部分汇编代码在中断发生时执行。
下篇文章分析Linux系统在启动阶段对中断向量表的搬移。
【作者】陈金
【出处】https://www.cnblogs.com/yifeichongtian2021/
【博客园】https://www.cnblogs.com/yifeichongtian2021/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利.