STM32
直播中

王锦霞

8年用户 1035经验值
私信 关注
[问答]

求大佬分享S3C2440裸机und异常模示程序

求大佬分享S3C2440裸机und异常模示程序

回帖(1)

张亮

2021-11-30 14:33:39
1.异常向量表

我们先看一下芯片手册里面的异常向量表,






2.代码流程
我们在重定位的第10个程序的基础上进行修改。


我们要在start.S的前面增加跳转到相应异常向量表的代码,并且要增加代码,当发生未定义指令异常时候跳转到该代码块进行保存现场、处理未定义异常以及恢复现场的工作,然后在下面故意写一条不能被识别的指令。


2.1增加异常向量表代码
首先在start.S的前面增加相应的跳转指令,当发生异常时会根据这里的跳转指令跳转到相应的地方,代码如下。


_start:
        b reset  /* vector 0 : reset */
        b do_und /* vector 4 : und */
2.2设置栈
进入异常模式之后,使用的异常模式下的栈,所以我们要在处理异常之前设置一下异常模式的栈,我们随便找一块没有用到的内存就可以了,这里我们让他指向64MSDRAM的最高地址0x34000000。


        /* sp_und未设置, 先设置它 */
        ldr sp, =0x34000000
2.3 保存现场
由于r0-r12都有可能被修改,因为我们要把r0-r12都保存一下,另外当发生异常时,当前被中断的地址回报存在LR寄存器里面,所以我们也要把LR寄存器保存一下,这里stmdb只是stm decrease before,也就是先减后存。


        /* 在und异常处理函数中有可能会修改r0-r12, 所以先保存 */
        /* lr是异常处理完后的返回地址, 也要保存 */
        stmdb sp!, {r0-r12, lr}  
2.4 处理异常
这里处理未定义异常的的工作我们通过编写一个C函数实现,我们新建一个exception.c,在里面编写我们的处理函数,exception.c内容如下:


#include "uart.h"

void printException(unsigned int cpsr, char *str)
{
        puts("Exception! cpsr = ");
        printHex(cpsr);
        puts(" ");
        puts(str);
        puts("nr");
}

这个函数由两个参数,所以我们在start.S中调用这个函数时要传给他两个参数,在汇编代码中定义字符串可以使用.ascii和.string,他们两个的区别在于,用.ascii定义的字符串变量不会再末尾自动加上结束符.


    /* 处理und异常 */
        mrs r0, cpsr
        ldr r1, =und_string
        bl printException
       
        /* 恢复现场 */
        ldmia sp!, {r0-r12, pc}^  /* ^会把spsr的值恢复到cpsr里 */
       
und_string:
        .string "undefined instruction exception"
2.5 恢复现场
把r0-r12的值从栈中读出来,并且并原来保存在LR寄存器中的值赋值给PC,ldmia是指ldm increase after,也就是 先读后加。


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


2.6 故意加入一条未定义指令

我们看一下2440的芯片手册,找到ARM指令集,只要是下表中的指令都是合法的指令。我们需要定义一条下表中不支持的非法指令,或者直接用0xdeadc0de表示一条未定义指令。





        /* 故意加入一条未定义指令 */und_code:        .word 0xdeadc0de  /* 未定义指令 */
3.代码
3.1start.S
下面我们看一下修改后的完整的start.S。



.text
.global _start

_start:
        b reset  /* vector 0 : reset */
        b do_und /* vector 4 : und */

do_und:
        /* 执行到这里之前:
         * 1. lr保存有被中断模式中的下一条即将执行的指令的地址
         * 2. SPSR保存有被中断模式的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"


reset:
        /* 关闭看门狗 */
        ldr r0, =0x53000000
        ldr r1, =0
        str r1, [r0]

        /* 设置MPLL, FCLK : HCLK : PCLK = 400m : 100m : 50m */
        /* LOCKTIME(0x4C000000) = 0xFFFFFFFF */
        ldr r0, =0x4C000000
        ldr r1, =0xFFFFFFFF
        str r1, [r0]

        /* CLKDIVN(0x4C000014) = 0X5, tFCLK:tHCLK:tPCLK = 1:4:8  */
        ldr r0, =0x4C000014
        ldr r1, =0x5
        str r1, [r0]

        /* 设置CPU工作于异步模式 */
        mrc p15,0,r0,c1,c0,0
        orr r0,r0,#0xc0000000   //R1_nF:OR:R1_iA
        mcr p15,0,r0,c1,c0,0

        /* 设置MPLLCON(0x4C000004) = (92<<12)|(1<<4)|(1<<0)
         *  m = MDIV+8 = 92+8=100
         *  p = PDIV+2 = 1+2 = 3
         *  s = SDIV = 1
         *  FCLK = 2*m*Fin/(p*2^s) = 2*100*12/(3*2^1)=400M
         */
        ldr r0, =0x4C000004
        ldr r1, =(92<<12)|(1<<4)|(1<<0)
        str r1, [r0]
        /* 一旦设置PLL, 就会锁定lock time直到PLL输出稳定
         * 然后CPU工作于新的频率FCLK
         */
       
       
        /* 设置内存: sp 栈 */
        /* 分辨是nor/nand启动
         * 写0到0地址, 再读出来
         * 如果得到0, 表示0地址上的内容被修改了, 它对应ram, 这就是nand启动
         * 否则就是nor启动
         */
        mov r1, #0
        ldr r0, [r1] /* 读出原来的值备份 */
        str r1, [r1] /* 0->[0] */
        ldr r2, [r1] /* r2=[0] */
        cmp r1, r2   /* r1==r2? 如果相等表示是NAND启动 */
        ldr sp, =0x40000000+4096 /* 先假设是nor启动 */
        moveq sp, #4096  /* nand启动 */
        streq r0, [r1]   /* 恢复原来的值 */
        bl sdram_init
        //bl sdram_init2         /* 用到有初始值的数组, 不是位置无关码 */
        /* 重定位text, rodata, data段整个程序 */
        bl copy2sdram
        /* 清除BSS段 */
        bl clean_bss
        bl uart0_init
        bl print1
        /* 故意加入一条未定义指令 */
und_code:
        .word 0xdeadc0de  /* 未定义指令 */
        bl print2
        //bl main  /* 使用BL命令相对跳转, 程序仍然在NOR/sram执行 */
        ldr pc, =main  /* 绝对跳转, 跳到SDRAM */
halt:
        b halt
       
3.2 exception.c

#include "uart.h"

void printException(unsigned int cpsr, char *str)
{
        puts("Exception! cpsr = ");
        printHex(cpsr);
        puts(" ");
        puts(str);
        puts("nr");
}

4.优化
我们把前面异常向量表的跳转指令改为绝对跳转,这样保证如果是nandflash,并且代码大于4K,那么我们后面的bl printException也不会出错,


_start:
        b reset  /* vector 0 : reset */
        ldr pc, und_addr /* vector 4 : und */
这样修改之后还有一个问题,我们看一下反汇编,





它去300000c0这个地方去取值,





然后c0这个地方存在的30000008,30000008就是下面这条指令的地址。





其实就是do_und指令的地址保存到内存中,然后ldr pc, und_addr会去内存中找到这个值,然后付给pc, 那么他去内存的那个地方找这个值呢,一般由编译器决定,通常会放在汇编文件的最后面,我们这个start.S非常小,如果这个start.S非常大,那么他有可能会去4K之外的地方取值,所以我们能不能指定让他去前面的内存去读这个值呢,因为我们担心这个汇编文件很大的话,有可能超过4K,如果你的板子是nand启动,那么在4K之外就没有办法读到值。修改如下:


_start:
        b reset  /* vector 0 : reset */
        ldr pc, und_addr /* vector 4 : und */

und_addr:
        .word do_und
同样下面的代码在重定位之后,我们就应该跳转到SDRAM去执行代码,所以增加ldr pc, =sdram


        ldr pc, =sdram
sdram:
        bl uart0_init

        bl print1
        /* 故意加入一条未定义指令 */
und_code:
        .word 0xdeadc0de  /* 未定义指令 */
        bl print2

        //bl main  /* 使用BL命令相对跳转, 程序仍然在NOR/sram执行 */
        ldr pc, =main  /* 绝对跳转, 跳到SDRAM */

halt:
        b halt
修改后的start.S如下:



.text
.global _start

_start:
        b reset  /* vector 0 : reset */
        ldr pc, und_addr /* vector 4 : und */

und_addr:
        .word do_und

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"

.align 4

reset:
        /* 关闭看门狗 */
        ldr r0, =0x53000000
        ldr r1, =0
        str r1, [r0]

        /* 设置MPLL, FCLK : HCLK : PCLK = 400m : 100m : 50m */
        /* LOCKTIME(0x4C000000) = 0xFFFFFFFF */
        ldr r0, =0x4C000000
        ldr r1, =0xFFFFFFFF
        str r1, [r0]

        /* CLKDIVN(0x4C000014) = 0X5, tFCLK:tHCLK:tPCLK = 1:4:8  */
        ldr r0, =0x4C000014
        ldr r1, =0x5
        str r1, [r0]

        /* 设置CPU工作于异步模式 */
        mrc p15,0,r0,c1,c0,0
        orr r0,r0,#0xc0000000   //R1_nF:OR:R1_iA
        mcr p15,0,r0,c1,c0,0

        /* 设置MPLLCON(0x4C000004) = (92<<12)|(1<<4)|(1<<0)
         *  m = MDIV+8 = 92+8=100
         *  p = PDIV+2 = 1+2 = 3
         *  s = SDIV = 1
         *  FCLK = 2*m*Fin/(p*2^s) = 2*100*12/(3*2^1)=400M
         */
        ldr r0, =0x4C000004
        ldr r1, =(92<<12)|(1<<4)|(1<<0)
        str r1, [r0]

        /* 一旦设置PLL, 就会锁定lock time直到PLL输出稳定
         * 然后CPU工作于新的频率FCLK
         */
       
       

        /* 设置内存: sp 栈 */
        /* 分辨是nor/nand启动
         * 写0到0地址, 再读出来
         * 如果得到0, 表示0地址上的内容被修改了, 它对应ram, 这就是nand启动
         * 否则就是nor启动
         */
        mov r1, #0
        ldr r0, [r1] /* 读出原来的值备份 */
        str r1, [r1] /* 0->[0] */
        ldr r2, [r1] /* r2=[0] */
        cmp r1, r2   /* r1==r2? 如果相等表示是NAND启动 */
        ldr sp, =0x40000000+4096 /* 先假设是nor启动 */
        moveq sp, #4096  /* nand启动 */
        streq r0, [r1]   /* 恢复原来的值 */

        bl sdram_init
        //bl sdram_init2         /* 用到有初始值的数组, 不是位置无关码 */

        /* 重定位text, rodata, data段整个程序 */
        bl copy2sdram

        /* 清除BSS段 */
        bl clean_bss


        ldr pc, =sdram
sdram:
        bl uart0_init

        bl print1
        /* 故意加入一条未定义指令 */
und_code:
        .word 0xdeadc0de  /* 未定义指令 */
        bl print2

        //bl main  /* 使用BL命令相对跳转, 程序仍然在NOR/sram执行 */
        ldr pc, =main  /* 绝对跳转, 跳到SDRAM */

halt:
        b halt
       
修改后的程序整体执行过程如下:


修改后的程序整体执行过程如下:


举报

更多回帖

发帖
×
20
完善资料,
赚取积分