zoukankan      html  css  js  c++  java
  • s3c2440裸机-异常中断(三. swi软中断)

    #swi(软中断) 我们知道arm有7中工作模式,除了usr模式,其他6种都是特权模式。我们知道usr模式无法修改CPSR直接进入其他特权模式,但linux应用程序一般运行在usr模式,既然usr模式权限非常低,是无法直接访问硬件寄存器的,那么它是如何访问硬件的呢?
    linux应用程序是通过系统调用,从而进入内核态,运行驱动程序来访问的硬件,那么系统调用又是如何实现的呢,就是通过软中断swi指令来进入svc模式,进入到svc模式后当然就能访问硬件啦。
    

    所以我们的应用程序在usr模式想访问硬件,必须切换模式,怎么切换?

    有以下两种方式:

    1.发生异常或中断(被动的)

    2.swi + 某个值(主动的)

    现在介绍如何进入软中断swi

    我们知道cpu一上电会跳到0地址(reset复位)执行代码,此时CPU处于svc模式,2440异常向量表如下图所示:

    为了验证usr模式能够主动的通过swi软中断指令来进入svc模式, 我们先将模式切换到usr模式,那么这个时候就不能访问硬件了,也不能直接修改cpsr直接进入其他模式。

    从上图我们设置CPSR让M4-M0处在10000,这样就进入了usr模式, 然后我们修改。修改start.s如下:

    	.global _start
    
        _start:
            b reset  
                ldr pc, und_addr 
                ldr pc, swi_addr
                ...
            und_addr:
                .word do_und
            swi_addr:
                .word do_swi
    
    	reset:
    		/*
    		看门狗
    		时钟
    		set SP
    		sdram_init
    		重定位
    		bl uart0_init
    		*/
    
    
    		/*先进入usr模式*/
    		mrs r0, cpsr      /* 读出cpsr 读到r0 */
    		/*使用bic命令 bitclean 把低4位清零*/
    		bic r0, r0, #0xf  /* 修改M4-M0为0b10000, 进入usr模式 */
    		msr cpsr, r0	 /* 写入cpsr */
    		
    		/* 设置usr模式下的栈sp_usr */
    		ldr sp, =0x33f00000
    		swi 0x123  /* 执行此命令, 触发SWI异常, 进入0x8执行 */
    		ldr pc, =main  /* 绝对跳转, 跳到SDRAM */
    
        halt:
        	b halt
    

    那么当执行到swi 0x123,就会触发SWI异常, 进入0x8的向量去执行,调用do_swi,我们参考上一节do_und实现我们的软中断服务程序do_swi。

    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}  
    
    /* 处理swi异常 */
    mrs r0, cpsr
    ldr r1, =swi_string /*这里r0, r1只是为了给printException传参*/
    bl printException
    
    /* 恢复现场 */
    ldmia sp!, {r0-r12, pc}^  /* ^会把spsr的值恢复到cpsr里 */
    
    swi_string:
    	.string "swi exception"
    

    完整的代码如下:

    展开代码
    
    
    .global _start
            b reset  
            ldr pc, und_addr 
            ldr pc, swi_addr
            ...
        und_addr:
            .word do_und
        swi_addr:
            .word do_swi
    
    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}  
    
    /* 处理swi异常 */
    mrs r0, cpsr
    ldr r1, =swi_string /*这里r0, r1只是为了给printException传参*/
    bl printException
    
    /* 恢复现场 */
    ldmia sp!, {r0-r12, pc}^  /* ^会把spsr的值恢复到cpsr里 */
    
    swi_string:
    	.string "swi exception"
    
    .align 4
    
    reset:
    	/*
    	看门狗
    	时钟
    	set SP
    	sdram_init
    	重定位
    	bl uart0_init
    	*/
    
    	/*先进入usr模式*/
    	mrs r0, cpsr      /* 读出cpsr 读到r0 */
    	/*使用bic命令 bitclean 把低4位清零*/
    	bic r0, r0, #0xf  /* 修改M4-M0为0b10000, 进入usr模式 */
    	msr cpsr, r0	 /* 写入cpsr */
    	
    	/* 设置usr模式下的栈sp_usr */
    	ldr sp, =0x33f00000
    	swi 0x123  /* 执行此命令, 触发SWI异常, 进入0x8执行 */
    	ldr pc, =main  /* 绝对跳转, 跳到SDRAM */
    
    halt:
    	b halt
    

    测试结果如下:

    打印出了软中断异常的字符串和svc模式。

    如何打印出swi软中断号
    如何才能知道swi的值呢?

    我们要读出swi 0x123指令,我们知道当执行完swi 0x123指令以后,会发生swi异常,lr_svc = PC + offset.
    从下图看出offset是4.

    我们知道lr_svc保存着被中断模式的下一条指令的地址,那么我们把lr寄存器的地址减去4就是当前pc的值,即为swi 0x123这条指令的地址。

    do_swi代码修改如下:

    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}  
    

    我们要把lr拿出来保存,因为bl printException会破坏lr,那么把lr保存在哪个个寄存器比较好呢?

    我们知道当调用‘bl printException’可能会修改某些寄存器,但是又会恢复这些寄存器,那么得知道它会保护哪些些寄存器。
    我们来看下ATPCS规则:

    在子程序中,使用R4~R11来保存局部变量,子程序进入时必须保存这些寄存器的值,在返回前必须恢复这些寄存器的值。所以对于 r4 ~ r11在C函数里会保存这几个寄存器,执行完C函数再把它释放掉并且恢复原来的值。我们把lr 保存在r4寄存器里,r4寄存器不会被C语言破坏。

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

    当执行完‘swi 0x123’指令后,会发生一次异常,那个异常模式里的lr寄存器会保存下一条指令的地址(即'ldr pc, =main'),我们把lr寄存器的地址减去4就是'swi 0x123'这条指令的地址。

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

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

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

    void printSWIVal(unsigned int *pSWI)
    {
    	puts("SWI val = ");
    	printHEx(*pSWI & ~0xff000000); //高8位忽略掉  
    	puts("
    
    ");
    }
    

    测试结果如下所示:

  • 相关阅读:
    cocos2d-x 2.2 study ------------------------ 双击事件(转)
    cocos2d-x打包Android
    cocos2d-x在win7下的android交叉编译环境
    Cocos2d-x之CCImage深入分析
    Eclipse开发C/C++之使用技巧小结
    TortoiseSVN安装使用
    cocos2d-x 2.2 study ------------------------ CCCallFunC家族
    cocos2d-x改底层之动态改变UIListView中的某项在链表中的位置
    汇编语言,以10进制的方式显示数字
    JVM
  • 原文地址:https://www.cnblogs.com/fuzidage/p/12155982.html
Copyright © 2011-2022 走看看