jz2440裸机开发与分析:S3c2440ARM异常与中断体系详解5---_swi异常模示程序示例

本文深入探讨了ARM处理器上SWI(软件中断)指令的处理流程,包括如何从用户模式切换到特权模式,SWI异常处理函数的实现,以及如何正确地对齐异常处理函数中的字符串以确保程序正常执行。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

这节我们再来演示swi的处理流程
swi软件中断:

software interrupt

在前面的视频中我们讲过ARMCPU有7中模式(下图为7种模式),除了用户模式以外,其他6种都是特权模式,这些特权模式可以直接修改CPSR进入其他模式
usr用户模式不能修改CPSR进入其他模式
在这里插入图片描述
Linux应用程序一般运行于用户模式

APP运行于usermode,(受限模式,不可访问硬件)

APP想访问硬件,必须切换模式,怎么切换?

通过发生异常中断进入特权模式

发生异常3种模式
1、中断是一种异常
2、und也是
3、 swi + 某个值(使用软中断切换模式)
在这里插入图片描述
由上图可知道,当进入reset时,进入的svc模式,所以复位之后,我们要改变cpsr进入用户模式
现在start.S把要做的事情列出来

/*1
    /* 复位之后, cpu处于svc模式
     * 现在, 切换到usr模式
     * 设置栈
     * 跳转执行
 */

/*2 故意引入一条swi指令*/

/*3 需在_start这里放一条swi指令*/

我们先切换到usr模式下
在这里插入图片描述
在这里插入图片描述
usr模式下的 M0 ~ M4是10000

	bl clean_bss

	/* 复位之后, cpu处于svc模式
	 * 现在, 切换到usr模式
	 */
	 
	mrs r0,cpsr     /* 读出cpsr */
	bic r0,r0, #0xf /* 修改M4-M0为0b10000, 进入usr模式 */
	msr cpsr,r0     /* r0 = 0b10000 */

	/* 设置 sp_usr */
	ldr sp, =0x33f00000  /*在SDRAM种设置sp*/
	ldr pc, =SDRAM

编译运行

发现可以处理und指令

添加 swi异常,仿照未定义指令做


.text
.global _start

_start:
    b reset          /* vector 0 : reset */
    ldr pc, und_addr /* vector 4 : und */
/*1 添加swi指令*/
    ldr pc, swi_addr /* vector 8 : swi */

und_addr:
    .word do_und

/*2 仿照und未定义添加指令*/
swi_addr:
    .word do_swi

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

    /* sp_und未设置, 先设置它 */
    ldr sp, =0x34000000

    /* 在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"


/*3 复制do_und修改为swi */
do_swi:
    /* 执行到这里之前:
     * 3.1. lr_svc保存有被中断模式中的下一条即将执行的指令的地址
     * 3.2. SPSR_svc保存有被中断模式的CPSR
     * 3.3. CPSR中的M4-M0被设置为10011, 进入到svc模式
     * 3.4. 跳到0x08的地方执行程序 
     */

    /* 3.5 sp_svc未设置, 先设置它 */
    ldr sp, =0x33e00000

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

    /* 3.8 保存现场 */
    /* 3.9 处理swi异常 只是打印 */
    mrs r0, cpsr
    ldr r1, =swi_string
    bl printException

    /*3.10  恢复现场 */
    ldmia sp!, {r0-r12, pc}^  /* ^会把spsr的值恢复到cpsr里 */

/*swi处理函数*/ 
swi_string:
    .string "swi exception"

上传代码实验

烧写 发现没有执行

我们先把下面这些代码注释掉

/*3 复制do_und 修改为swi */

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

    /* 3.5 sp_svc未设置, 先设置它 */
    ldr sp, =0x33e00000

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

    /* 3.8 保存现场 */
    /* 3.9 处理swi异常 只是打印 */
    mrs r0, cpsr
    ldr r1, =swi_string
    bl printException

    /*3.10  恢复现场 */
    ldmia sp!, {r0-r12, pc}^  /* ^会把spsr的值恢复到cpsr里 */

/* swi处理函数 */   
swi_string:
    .string "swi exception"

上传编译

烧写执行 可以正常运行

循环打印
swi 0x123 /* 执行此命令, 触发SWI异常, 进入0x8执行 */

执行后继续执行
ldr pc, swi_addr /* vector 8 : swi */

表明问题出现在 do_swi:函数中

先把下面这句话注释掉

.string "swi exception"

编译烧写运行

程序可以正常运行

显然程序问题出现在.string “swi exception” 这句话,为什么加上这句话程序就无法执行,查看一下反汇编:

30000064 <swi_string>: //这里地址是64
30000064:   20697773    rsbcs   r7, r9, r3, ror r7
30000068:   65637865    strvsb  r7, [r3, #-2149]!
3000007c:   6f697470    swivs   0x00697470
30000070:   0000006e    andeq   r0, r0, lr, rrx

30000082 <reset>: //我们使用的是ARM指令集,应该是4字节对齐,发现这里并不是,问题就在这里
30000082:   e3a00453    mov r0, #1392508928 ; 0x53000000
30000086:   e3a01000    mov r1, #0  ; 0x0
3000008a:   e5801000    str r1, [r0]
3000008e:   e3a00313    mov r0, #1275068416 ; 0x4c000000
30000092:   e3e01000    mvn r1, #0  ; 0x0

因为这个字符串长度有问题

前面und_string 那里的字符串长度刚刚好

我们不能把问题放在运气上面

添加:

/*******
以4字节对齐
*/
.align 4


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

    /* 3.5 sp_svc未设置, 先设置它 */
    ldr sp, =0x33e00000

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

    /* 3.8 保存现场 */
    /* 3.9 处理swi异常 只是打印 */
    mrs r0, cpsr
    ldr r1, =swi_string
    bl printException

    /*3.10  恢复现场 */
    ldmia sp!, {r0-r12, pc}^  /* ^会把spsr的值恢复到cpsr里 */
/*****
swi处理函数
*/  
swi_string:
    .string "swi exception"


.align 4
/**************
表明下面的标号要放在4字节对齐的地方
*/

上传代码编译运行查看反汇编:


30000068 <swi_string>:
30000068:   20697773    rsbcs   r7, r9, r3, ror r7
3000006c:   65637865    strvsb  r7, [r3, #-2149]!
30000070:   6f697470    swivs   0x00697470
30000074:   0000006e    andeq   r0, r0, lr, rrx
    ...

30000080 <reset>: //现在reset放在4自己对齐的地方
30000080:   e3a00453    mov r0, #1392508928 ; 0x53000000
30000084:   e3a01000    mov r1, #0  ; 0x0
30000088:   e5801000    str r1, [r0]
3000008c:   e3a00313    mov r0, #1275068416 ; 0x4c000000
30000090:   e3e01000    mvn r1, #0  ; 0x0
30000094:   e5801000    str r1, [r0]
30000098:   e59f0084    ldr r0, [pc, #132]  ; 30000124 <.text+0x124>
3000009c:   e3a01005    mov r1, #5  ; 0x5
300000a0:   e5801000    str r1, [r0]
300000a4:   ee110f10    mrc 15, 0, r0, cr1, cr0, {0}
300000a8:   e3700103    orr r0, r0, #-1073741824    ; 0xc0000000
300000ac:   ee010f10    mcr 15, 0, r0, cr1, cr0, {0}
300000b0:   e59f0070    ldr r0, [pc, #112]  ; 30000128 <.text+0x128>
300000b4:   e59f1070    ldr r1, [pc, #112]  ; 3000012c <.text+0x12c>
300000b8:   e5801000    str r1, [r0]
300000bc:   e3a01000    mov r1, #0  ; 0x0
300000c0:   e5910000    ldr r0, [r1]

下载烧写

程序执行完全没有问题

程序备份修改代码

swi可以根据应用程序传入的val来判断为什么调用swi指令,我们的异常处理函数能不能把这个val值读出来

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

    /* sp_svc未设置, 先设置它 */
    ldr sp, =0x33e00000

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

/* 2
我们要把lr拿出来保存
因为bl printException会破坏lr

mov rX, lr

我把lr保存在那个寄存器?

这个函数 bl printException 可能会修改某些寄存器,但是又会恢复这些寄存器,我得知道他会保护那些寄存器

我们之前讲过ATPCS规则
对于 r4 ~ r11在C函数里他都会保存这几个寄存器,如果用到的话就把他保存起来,执行完C函数再把它释放掉
我们把lr 保存在r4寄存器里,r4寄存器不会被C语言破坏
*/
    mov r4, lr

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


/*1
跳转到printSWIVal
如何才能知道swi的值呢?
我们得读出swi 0x123指令,这条指令保存在内存中,我们得找到他的内存地址
执行完0x123指令以后,会发生一次异常,那个异常模式里的lr寄存器会保存下一条指令的地址
我们把lr寄存器的地址减去4就是swi 0x123这条指令的地址
*/

/*3
我再把r4的寄存器赋给r0让后打印
我们得写出打印函数

mov r0, r4

指令地址减4才可以
swi 0x123
下一条指令bl main 减4就是指令本身
*/
    sub r0, r4, #4
    bl printSWIVal

    /* 恢复现场 */
    ldmia sp!, {r0-r12, pc}^  /* ^会把spsr的值恢复到cpsr里 */

swi_string:
.string "swi exception"

```c
在uart.c添加printSWIVal打印函数

void printSWIVal(unsigned int *pSWI)
{

    puts("SWI val = ");
    printHEx(*pSWI & ~0xff000000); //高8位忽略掉  
    puts("\n\r");

}

在这里插入图片描述
我们再来看看这个程序是怎么跳转的

/*1
发生swi异常,他是在sdram中,CPU就会跳到0x8的地方
swi 0x123  /* 执行此命令, 触发SWI异常, 进入0x8执行 */

*/

/* 2

_start:
    b reset          /* vector 0 : reset */
    ldr pc, und_addr /* vector 4 : und */
执行这条读内存指令
    ldr pc, swi_addr /* vector 8 : swi */

读到swi_addr地址跳转到sdram执行代码 do_swi那段代码
swi_addr:
    .word do_swi

*/

/* 3
这段代码被设置栈保存现场 调用处理函数恢复现场,让后就会跳到sdram执行 swi 0x123的下一条指令
do_swi:
    /* 执行到这里之前:
     * 1. lr_svc保存有被中断模式中的下一条即将执行的指令的地址
     * 2. SPSR_svc保存有被中断模式的CPSR
     * 3. CPSR中的M4-M0被设置为10011, 进入到svc模式
     * 4. 跳到0x08的地方执行程序 
     */

    /* sp_svc未设置, 先设置它 */
    ldr sp, =0x33e00000

    /* 保存现场 */
    /* 在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"
*/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值