s3c2440 bare metal - abort (b und undefined instruction exception.)

1._und (undefined instruction exception) Introduction

We analyzed five types of exceptions before, so how to enter an undefined instruction exception, of course, read the cpu command exception occurs, there has been an instruction parsing exception.
When we first look at the cpu to resolve what kind of instruction triggers an undefined instruction exception it?

From the above arm instruction format, the instruction code belongs to the long format scribed, it belongs to an undefined instruction exception.

2. Compilation pass arguments to the function c

We know that the assembler to C language function parameters passed by r0, R1, ... to the parameter passing through the stack of the way, such as r0 = 1, r1 = 2; r0 of c is then argv0 function is called in, argv1 is r1 ..., if by then we compile the C function to pass the string it?

We can und_string declared as such by a string:

und_string:
    .string "undefined instruction exception"

Then ldr r1, = und_string, r1, so it contains the address of und_string.
We call this c function can be passed into the und_string.

3._und abnormal program example

We now define an undefined instruction pseudo-code:

.text
.global _start

_start:
    b reset  /* vector 0 : reset */ 
    b do_und /* vector 4 : und (看中断向量表)*/
    
reset:
    /*看门狗
    时钟
    sdram
    设置SP
    重定位*/
    ...
    bl print1

und_code:
    .word 0xdeadc0de; /*定义一条未定义指令*/
    /*故意以一个数据的方式引入一条未定义指令,当cpu执行到这里,读取0xdeadc0de指令码的时候,
    发现无法识别这条指令,就发生未定义指令异常,就跳转到0x4的中断向量去执行*/
    
    bl print2
    ...

We now understand in order to facilitate debugging: We abnormalities before and after adding print undefined instruction print1, print2, if an undefined instruction exception occurs, will jump to read the instructions of local 0x4, print2 also can not perform.

When the jump to the interrupt vector 0x4 found here is a jump instruction "bl do_und", then we undefined instruction service program do_und unusual print out the contents of und_string this string.

Now start writing instruction exception service program do_und, to achieve the following:

do_und:
    /* sp_und未设置, 先设置它 (由于之前一直处于管理模式,现在处在und状态)*/
    ldr sp, =0x34000000

    /* 保存现场 */
    /* 在und异常处理函数中有可能会修改r0-r12, 所以先保存 */
    /* lr是异常处理完后的返回地址, 也要保存 */
    stmdb sp!, {r0-r12, lr}  /*先减后存*/ /* 把栈中的值备份到r0-r12*/
    
    /* 处理und异常 */
    mrs r0, cpsr
    ldr r1, =und_string /*保存und_string地址*/
    bl printException
    
    /* 恢复现场 */
    ldmia sp!, {r0-r12, pc}^  /*(ldmia先读后加),把备份的值恢复到栈中,让pc=lr就可以恢复到异常前的指令地址。^会把spsr的值恢复到cpsr里 */

To analyze the following about this undefined instruction exception service routine :( In fact, comments in the code has been talk in great detail)

1. Before entering the undefined instruction exception service do_und hardware automatically do the following:

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

2.进入指令异常服务程序do_und后,我们需要保存现场,处理und异常,恢复现场,注意:由于发生了cpu模式切换,如果要用到栈,那么先要设置对应模式的栈。由于栈的地址是向下生长的,这里我就用sdram的末位地址作为栈指针,把sp_und=0x34000000。

3.在und异常服务程序中有可能会用到栈, 所以先保存现场,通过stmdb sp!, {r0-r12, lr}语句把栈中的值备份到r0-r12和lr,然后恢复现场的时候通过ldmia sp!, {r0-r12, pc}^,详见上面的注释。

4.我们看到保存现场后,我们把cpsr的值放到r0, 把und_string放到r1, 然后用bl printException调用c函数,这样我们的c函数printException就能收到汇编传过来的参数,一个是cpsr模式(r0),一个是und_string汇编传过来的字符串(r1)。我们用C函数实现printException:

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

完整的代码如下:

    .text
.global _start

_start:
    b reset  /* vector 0 : reset */ 
    
    b do_und /* vector 4 : und (看中断向量表)*/

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


    /* sp_und未设置, 先设置它 (由于之前一直处于管理模式,现在处在und状态)*/
    ldr sp, =0x34000000

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


reset:
    /* 关闭看门狗 */
    /* 时钟 */
    /* sdram */ 
    bl copy2sdram
    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

测试结果如下:

打印出print1中的字符串‘abc’后,紧接着打印printException函数中的结果,cpsr=0x600000db,那么对应的M[4:0]=11011, 对应下图为und模式。然后从und异常返回,恢复原来的模式继续执行。

上述代码改进

我们将上面的代码的und_string字符串修改一下:

...
und_string:
    .string "undef instruction"

reset:
    /* 关闭看门狗 */
    /* 时钟 */
...

编译烧录再次运行,发现没有任何打印输出,这是为什么呢?我明明只是把und_string字符串改了一下呀。

查看反汇编:

我们发现reset的地址是30000032,竟然不是4字节对齐的,我们知道arm指令集是以4字节为单位的,那么这里没有对齐,肯定无法解析指令。那么我们手工改进代码如下:

...
und_string:
    .string "undef instruction"

.align 4

reset:
    /* 关闭看门狗 */
    /* 时钟 */
...

我们再来看看反汇编,发现reset的地址是30000040,是以4字节对齐的,再次烧录运行,发现能够正常输出print1, 能够进入未定义指令异常。

Guess you like

Origin www.cnblogs.com/fuzidage/p/12114215.html