s3c2440裸机-异常中断3-swi软中断

发布时间:2024-07-05  

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("nr");

}


文章来源于:电子工程世界    原文链接
本站所有转载文章系出于传递更多信息之目的,且明确注明来源,不希望被转载的媒体或个人可与我们联系,我们将立即进行删除处理。

我们与500+贴片厂合作,完美满足客户的定制需求。为品牌提供定制化的推广方案、专属产品特色页,多渠道推广,SEM/SEO精准营销以及与公众号的联合推广...详细>>

利用葫芦芯平台的卓越技术服务和新产品推广能力,原厂代理能轻松打入消费物联网(IOT)、信息与通信(ICT)、汽车及新能源汽车、工业自动化及工业物联网、装备及功率电子...详细>>

充分利用其强大的电子元器件采购流量,创新性地为这些物料提供了一个全新的窗口。我们的高效数字营销技术,不仅可以助你轻松识别与连接到需求方,更能够极大地提高“闲置物料”的处理能力,通过葫芦芯平台...详细>>

我们的目标很明确:构建一个全方位的半导体产业生态系统。成为一家全球领先的半导体互联网生态公司。目前,我们已成功打造了智能汽车、智能家居、大健康医疗、机器人和材料等五大生态领域。更为重要的是...详细>>

我们深知加工与定制类服务商的价值和重要性,因此,我们倾力为您提供最顶尖的营销资源。在我们的平台上,您可以直接接触到100万的研发工程师和采购工程师,以及10万的活跃客户群体...详细>>

凭借我们强大的专业流量和尖端的互联网数字营销技术,我们承诺为原厂提供免费的产品资料推广服务。无论是最新的资讯、技术动态还是创新产品,都可以通过我们的平台迅速传达给目标客户...详细>>

我们不止于将线索转化为潜在客户。葫芦芯平台致力于形成业务闭环,从引流、宣传到最终销售,全程跟进,确保每一个potential lead都得到妥善处理,从而大幅提高转化率。不仅如此...详细>>