中断处理函数_【嵌入式Linux基础】3.异常与中断

撑腰会儿:异常与中断介绍​zhuanlan.zhihu.com

未定义的指令处理(UNDEFINED)

简单地说,就是CPU或协处理器不认识这条指令,执行这样的指令时就会产生“未定义指令异常”。如果CPU核尝试使用操作码执行一条指令(在ARM体系结构规范中描述UNDEFINED),或者执行了协处理器指令但没有协处理器将其识别为可以执行的指令,则会导致未定义的指令异常。

在某些系统中,代码可能包含用于协处理器(例如VFP协处理器)的指令,但是系统中不存在相应的VFP硬件。另外,VFP硬件有可能无法处理特定指令,而是想调用软件来对其进行仿真。或者,可能会禁用VFP硬件,采用异常处理,以便可以启用它,然后重新执行指令。

使用未定义的指令,可以实现一些仿真器。比如在你的芯片中,它并未支持某条硬件除法指令,但是你还可以在代码中使用它。当CPU执行这条指令时会发生异常,在异常处理函数中,你用软件来实现该指令的功能。对于不是特别设置的未定义指令,在异常处理函数中不能处理它时,通常做法是记录适当的调试信息,并杀死对应的应用程序。在某些情况下,未定义指令异常的另一个用途是实现用户断点:调试器去修改代码,替换断点位置的指令为一条未定义指令。

代码分析

通过在代码段里里插入一个未定义指令(0xeeadc0de),从而产生未定义指令异常。在未定义异常指令异常的处理函数里,调用printException函数,打印出当前的CPSR值,和产生异常的原因。

通过指令,设置好异常向量的基地址:

mcr p15, 0, r0, c12, c0, 0

在异常向量表里,通过指令跳转到Undefined_Handler标签处

ldr pc, =Undefined_Handler

在Undefined_Handler里将r0-r12和lr保存在und模式的栈上,然后调用printException 打印当前的CPSR值,并打印一个字符串。最后将r0-r12从栈上恢复,lr从栈上弹出到PC,并同时将SPSR恢复到CPSR,从而返回去执行出现未定义异常指令的下一条指令。

start.s

.text
.global  _start, _vector_table
_start:
_vector_table:
	ldr 	pc, =Reset_Handler			 /* Reset				   */
	ldr 	pc, =Undefined_Handler		 /* Undefined instructions */
	//b Reset_Handler
	//b Undefined_Handler
	b halt//b SVC_Handler//ldr 	pc, =SVC_Handler			 /* Supervisor Call 	   */
	b halt//ldr 	pc, =PrefAbort_Handler		 /* Prefetch abort		   */
	b halt//ldr 	pc, =DataAbort_Handler		 /* Data abort			   */
	.word	0							 /* RESERVED			   */
	b halt//ldr 	pc, =IRQ_Handler			 /* IRQ interrupt		   */
	b halt//ldr 	pc, =FIQ_Handler			 /* FIQ interrupt		   */

.align 2
Undefined_Handler:
	/* 执行到这里之前:
	 * 1. lr_und保存有被中断模式中的下一条即将执行的指令的地址
	 * 2. SPSR_und保存有被中断模式的CPSR
	 * 3. CPSR中的M4-M0被设置为11011, 进入到und模式
	 * 4. 跳到0x4的地方执行程序 
	 */

	/* 在und异常处理函数中有可能会修改r0-r12, 所以先保存 */
	/* lr是异常处理完后的返回地址, 也要保存 */
	stmdb sp!, {r0-r12, lr}  
	
	/* 保存现场 */
	/* 处理und异常 */
	mrs r0, cpsr
	ldr r1, =und_string
	bl printException
	
	/* 恢复现场 */
	ldmia sp!, {r0-r12, pc}^  /* ^会把spsr的值恢复到cpsr里 */
	
und_string:
	.string "undefined instruction exception"

.align 2
Reset_Handler:
	/* Reset SCTlr Settings */
	mrc 	p15, 0, r0, c1, c0, 0	  /* read SCTRL, Read CP15 System Control register		*/
	bic 	r0,  r0, #(0x1 << 13)	  /* Clear V bit 13 to use normal exception vectors  	*/
	bic 	r0,  r0, #(0x1 << 12)	  /* Clear I bit 12 to disable I Cache					*/
	bic 	r0,  r0, #(0x1 <<  2)	  /* Clear C bit  2 to disable D Cache					*/
	bic 	r0,  r0, #(0x1 << 2)	  /* Clear A bit  1 to disable strict alignment 		*/
	bic 	r0,  r0, #(0x1 << 11)	  /* Clear Z bit 11 to disable branch prediction		*/
	bic 	r0,  r0, #0x1			  /* Clear M bit  0 to disable MMU						*/
	mcr 	p15, 0, r0, c1, c0, 0	  /* write SCTRL, Write to CP15 System Control register	*/

    cps     #0x1B                /* Enter undef mode                */
    ldr     sp, =0x80300000     /* Set up undef mode stack      */

    cps     #0x13                /* Enter Supervisor mode         */
    ldr     sp, =0x80200000     /* Set up Supervisor Mode stack  */

	ldr r0, =_vector_table
	mcr p15, 0, r0, c12, c0, 0  /* set VBAR, Vector Base Address Register*/
	//mrc p15, 0, r0, c12, c0, 0  //read VBAR

	bl clean_bss
	
	bl system_init

und_code:
	.word 0xeeadc0de  /* undefine instruction */
	//.word 0x00000000

	bl main

halt:
	b  halt

clean_bss:
	/* 清除BSS段 */
	ldr r1, =__bss_start
	ldr r2, =__bss_end
	mov r3, #0
clean:
	cmp r1, r2
	strlt r3, [r1]
	add r1, r1, #4
	blt clean
	
	mov pc, lr


Main函数中加入以下函数段:

void printException(unsigned int cpsr, char *str)
{
 printf("Exception! cpsr is 0x%xrn", cpsr);
 printf("%srn", str);
}

中断处理函数_【嵌入式Linux基础】3.异常与中断_第1张图片

中断处理函数_【嵌入式Linux基础】3.异常与中断_第2张图片

中断处理函数_【嵌入式Linux基础】3.异常与中断_第3张图片

中断处理函数_【嵌入式Linux基础】3.异常与中断_第4张图片

SVC异常处理

简单地说就是执行SVC这条汇编指令时就会触发这个异常,CPU就会跳转过执行SVC异常向量的代码。

supervisor call(SVC)通常在用户模式下使用,这使得用户模式的代码能够访问OS功能。例如,如果用户代码想要访问系统的特权部分(例如执行文件I/O),则通常将使用SVC指令执行此操作。在Linux中对文件的open/read/write等应用层的系统函数,它的本质都是执行SVC指令,从而进入Linux内核中预设的SVC异常处理函数,在内核里操作文件。

可以使用寄存器或者操作码中某个字段将参数传递给SVC处理程序。发生异常时,异常处理程序必须确定内核是处于ARM还是Thumb状态。特别是SVC处理程序,必须读取指令集状态。通过检查SPSR T位完成的。该位设置为Thumb状态,清除为ARM状态。

ARM和Thumb指令集都具有SVC指令。从Thumb状态调用SVC时,必须考虑以下因素:

•指令地址位于LR-2,而不是LR-4;

•指令本身是16位的,因此需要半字加载;

•SVC编号为8位而不是ARM状态下的24位。

在ARM9等比较老的芯片里,这个异常是SWI异常,对应的指令是SWI。

代码分析

通过执行“swi 123”指令,触发SVC异常,程序跳转到异常向量表偏移0x8的地方执行,在异常向量表里通过如下指令跳转到SVC_Handler标签处执行。

ldr pc, =SVC_Handler

在SVC_Handler里将r0-r12和lr保存在SVC模式的栈上,然后将lr的值移动到R4,调用

printException函数打印出当前的CPSR值,和产生异常的原因。将R4减去4,赋值给R0,这是swi指令所在的地址,然后调用printSWIVal函数打印出swi指令的参数。最后将r0-r12 从栈上恢复,lr从栈上弹出到PC,并同时将SPSR恢复到CPSR,从而返回去执行swi指令的下一条指令。

start.s

.text
.global  _start, _vector_table
_start:
_vector_table:
	ldr 	pc, =Reset_Handler			 /* Reset				   */
	ldr 	pc, =Undefined_Handler		 /* Undefined instructions */
	ldr 	pc, =SVC_Handler			 /* Supervisor Call 	   */
	b halt//ldr 	pc, =PrefAbort_Handler		 /* Prefetch abort		   */
	b halt//ldr 	pc, =DataAbort_Handler		 /* Data abort			   */
	.word	0							 /* RESERVED			   */
	b halt//ldr 	pc, =IRQ_Handler			 /* IRQ interrupt		   */
	b halt//ldr 	pc, =FIQ_Handler			 /* FIQ interrupt		   */

.align 2
Undefined_Handler:
	/* 执行到这里之前:
	 * 1. lr_und保存有被中断模式中的下一条即将执行的指令的地址
	 * 2. SPSR_und保存有被中断模式的CPSR
	 * 3. CPSR中的M4-M0被设置为11011, 进入到und模式
	 * 4. 跳到0x4的地方执行程序 
	 */

	/* 在und异常处理函数中有可能会修改r0-r12, 所以先保存 */
	/* lr是异常处理完后的返回地址, 也要保存 */
	stmdb sp!, {r0-r12, lr}  
	
	/* 保存现场 */
	/* 处理und异常 */
	mrs r0, cpsr
	ldr r1, =und_string
	bl printException
	
	/* 恢复现场 */
	ldmia sp!, {r0-r12, pc}^  /* ^会把spsr的值恢复到cpsr里 */
	
und_string:
	.string "undefined instruction exception"

.align 2
SVC_Handler:
	/* 执行到这里之前:
	 * 1. lr_svc保存有被中断模式中的下一条即将执行的指令的地址
	 * 2. SPSR_svc保存有被中断模式的CPSR
	 * 3. CPSR中的M4-M0被设置为10011, 进入到svc模式
	 * 4. 跳到0x08的地方执行程序 
	 */

	/* 保存现场 */
	/* 在swi异常处理函数中有可能会修改r0-r12, 所以先保存 */
	/* lr是异常处理完后的返回地址, 也要保存 */
	stmdb sp!, {r0-r12, lr}  

	mov r4, lr
	
	/* 处理swi异常 */
	mrs r0, cpsr
	ldr r1, =swi_string
	bl printException

	sub r0, r4, #4
	bl printSWIVal
	
	/* 恢复现场 */
	ldmia sp!, {r0-r12, pc}^  /* ^会把spsr的值恢复到cpsr里 */
	
swi_string:
	.string "swi exception"

.align 2
Reset_Handler:
	/* Reset SCTlr Settings */
	mrc 	p15, 0, r0, c1, c0, 0	  /* read SCTRL, Read CP15 System Control register		*/
	bic 	r0,  r0, #(0x1 << 13)	  /* Clear V bit 13 to use normal exception vectors  	*/
	bic 	r0,  r0, #(0x1 << 12)	  /* Clear I bit 12 to disable I Cache					*/
	bic 	r0,  r0, #(0x1 <<  2)	  /* Clear C bit  2 to disable D Cache					*/
	bic 	r0,  r0, #(0x1 << 2)	  /* Clear A bit  1 to disable strict alignment 		*/
	bic 	r0,  r0, #(0x1 << 11)	  /* Clear Z bit 11 to disable branch prediction		*/
	bic 	r0,  r0, #0x1			  /* Clear M bit  0 to disable MMU						*/
	mcr 	p15, 0, r0, c1, c0, 0	  /* write SCTRL, Write to CP15 System Control register	*/

    cps     #0x1B                /* Enter undef mode                */
    ldr     sp, =0x80300000     /* Set up undef mode stack      */

    cps     #0x13                /* Enter Supervisor mode         */
    ldr     sp, =0x80200000     /* Set up Supervisor Mode stack  */

	ldr r0, =_vector_table
	mcr p15, 0, r0, c12, c0, 0  /* set VBAR, Vector Base Address Register*/
	//mrc p15, 0, r0, c12, c0, 0  //read VBAR

	bl clean_bss
	
	bl system_init

und_code:
	.word 0xeeadc0de  /* undefine instruction */
	//.word 0xFFFFFFFF

swi_code:
	swi 0x123  /* 执行此命令, 触发SWI异常, 进入0x8执行 */

	bl main

halt:
	b  halt

clean_bss:
	/* 清除BSS段 */
	ldr r1, =__bss_start
	ldr r2, =__bss_end
	mov r3, #0
clean:
	cmp r1, r2
	strlt r3, [r1]
	add r1, r1, #4
	blt clean
	
	mov pc, lr


Main函数中加入以下函数段:

void printException(unsigned int cpsr, char *str)
{
 printf("Exception! cpsr is 0x%xrn", cpsr);
 printf("%srn", str);
}
 
void printSWIVal(unsigned int *pSWI)
{
 printf("SWI val = 0x%xrn", *pSWI & ~0xff000000);
}

中断处理函数_【嵌入式Linux基础】3.异常与中断_第5张图片

你可能感兴趣的:(中断处理函数)