首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

windowsCE异常和中断服务程序初探 02

windowsCE异常和中断服务程序初探 02

同时不管异常发生在ARM 还是Thumb 状态下,处理器都将自动进入ARM 状态。并且中断使能会自动被关闭。在这个时候由于部分通用寄存器是不同模式公用的,所以还需要保存这些将会被破坏的寄存器,待到处理完成的时候恢复这些寄存器被中断前的状态。另外在进入异常模式后,lr的值不一定就是我们所需恢复执行的位置,该位置受到异常类型和流水线误差的影响。在SWI模式下,LR就是返回值。在IRQ和FIQ中LR=LR-4,DataAbort下LR=LR-8;具体原因我们就不讨论了,有兴趣可以参看<基于arm 的嵌入式程序开发要点>一文。下面分别对这些服务程序进行分析。

2-1.undef exception服务程序

undef exception在执行到过非法的指令时产生,通常来模拟一些处理器不支持的功能,如浮点运算。简单说一下undef exception的过程:当当前指令为一条处理器不支持的指令时,处理器会自动动将该指令送交各协处理器(如MMU、FPU)处理,如果这些协处理器都无法识别这条指令的时候,就产生该异常。下面开始看相应的代码。
        NESTED_ENTRY    UndefException
        sub     lr, lr, #4                      ; (lr) = address of undefined instruction
        stmdb   sp, {r0-r3, lr}
        mov     r1, #ID_UNDEF_INSTR
        b       CommonHandler
        ENTRY_END UndefException


上面就是undef Exception的服务程序的入口处(已经将不参与编译和Thumb模式下的代码去掉),通过lr-=4计算出触发异常前的指令地址,同时保存r0-r3和lr入undef_exception stack用于最后恢复现场和取得异常指令本身,随后进入分发程序CommonHandler.CommonHandler是一个公共的异常服务程序,它通过不同的传入参数来进行处理,在这里mov r1,#ID_UNDEF_INSTR就是指定异常模式为undef Exception.



2-2.swi服务程序

      按在arm处理器的设计意图,系统软件的系统调用(SystemCalls)都是通过SWI指令完成。SWI相当于一个中断指令,不同的是SWI不是由外部中断源产生的,同时对应于SWI的异常向量位于0xc的位置或0xffff 000c的位置。也就是说当执行一个swi指令后,当前程序流中断,并转入0xc或0xffff000c执行,同时将CPSR_mode(当前程序状态寄存器)复制入SPSR_svc,转入SVC模式运行(使用特权模式的寄存器组)。也就是说系统通过执行SWI引发系统swi异常后切换入特权模式,系统调用功能号由swi xx后的xx决定,在运行完指定功能的代码后返回异常时的地址并恢复用户模式。我们看看,Wince中这部分代码是如何实现的。
        DCD     SWIHandler                      ; SVC<<--------------------------SWI入口点。

     LEAF_ENTRY SWIHandler
  IF {FALSE}               
  ...
  ENDIF
        movs    pc, lr
        ENTRY_END SWIHandler
        上面IF {FALSE}到ENDIF之间的代码在编译的时候是得不到编译的(事实上这部分代码是用于开发中调试使用的,针对特殊的硬件平台,一般与我们使用的硬件平台无关。所以下面摘抄的代码都不将不参与编译的内容写入),因此SWI服务程序就是一句话。movs    pc, lr也就是直接回到SWI的地方,同时将SPSR_svc恢复到CPSR_mode中。这个过程中并没有进行在系统态执行特定系统指令序的工作,而仅仅是简单的返回,所以这不是系统调用,系统调用还需要根据调用号的不同运行指定的核心态代码。也就是说Wince的系统调用不是通过SWI来完成的,而是通过其他的异常处理手段达成的。




2-3 中断服务程序

IRQ(大概是最熟悉的异常方式了)在外部中断源在需要向处理器请求服务时发生,比如:时钟、外围器件FIFO上/下溢出、按键等等。IRQHandler就是中断的处理句柄,下面我们来具体看看。


----------------------------------------------------------------------------------     
    NESTED_ENTRY IRQHandler
        sub     lr, lr, #4                      ; fix return address
        stmfd   sp!, {r0-r3, r12, lr}       ;保存将要用到的寄存器和lr压入stack_irq
        PROLOG_END
        和上面一样,服务程序的入口处都是例行公事的计算返回位置以抵消流水线误差。再将要用到的寄存器压入STACK_IRQ,这样,准备工作就做完了。
        ; Test interlocked API status.        
        ;INTERLOCKED_START EQU USER_KPAGE+0x380
    ;INTERLOCKED_END EQU USER_KPAGE+0x400
        sub     r0, lr, #INTERLOCKED_START
        cmp     r0, #INTERLOCKED_END-INTERLOCKED_START
        bllo    CheckInterlockedRestart
        上面这部分的内容是关于互锁的检测,由于如信号量这些同步手段都必须作为原子操作进行,不允许打断。所以如果中断发生在互锁API的执行过程中,就需要专门的处理了。这些API都是放在INTERLOCKED_START和INTERLOCKED_END之间的,通过LR很容易就检查出是否是INTERLOCKEDXXX的过程中。这里并不关心互锁的实现就绕开这部分代码继续往下看,当作中断没有发生在interlock过程处理。
        ;
        ; CAREFUL! The stack frame is being altered here. It's ok since
        ; the only routine relying on this was the Interlock Check. Note that
        ; we re-push LR onto the stack so that the incoming argument area to
        ; OEMInterruptHandler will be correct.
        ;
        mrs     r1, spsr                        ; (r1) = saved status reg
        stmfd   sp!, {r1}                       ; save SPSR onto the IRQ stack   
        mov     r0,lr                           ; parameter to OEMInterruptHandler
     msr     cpsr_c, #SVC_MODE:OR:0x80       ; switch to supervisor mode w/IRQs disabled
        stmfd   sp!, {lr}                       ; save LR onto the SVC stack        
        stmfd   sp!, {r0}                       ; save IRQ LR (in R0) onto the SVC stack (param)
        ;
        ; Now we call the OEM's interrupt handler code. It is up to them to
        ; enable interrupts if they so desire. We can't do it for them since
        ; there's only on interrupt and they haven't yet defined their nesting.
        ;

        CALL    OEMInterruptHandler


        ldmfd   sp!, {r1}                       ; dummy pop (parameter)
        ldmfd   sp!, {lr}                       ; restore SVC LR from the SVC stack
        msr     cpsr_c, #IRQ_MODE:OR:0x80       ; switch back to IRQ mode w/IRQs disabled
    ; Restore the saved program status register from the stack.
        ;
        ldmfd   sp!, {r1}                       ; restore IRQ SPSR from the IRQ stack
        msr     spsr, r1                        ; (r1) = saved status reg
        ldr     lr, =KData                      ; (lr) = ptr to KDataStruct


        cmp     r0, #SYSINTR_RESCHED      ;->时间片已到,进行调度
        beq     %F10            
        ;SYSINTR_DEVICES EQU 8         ;是否设备中断,中断号是否有效
    ;SYSINTR_MAX_DEVICES EQU 32   
        sub     r0, r0, #SYSINTR_DEVICES
        cmp     r0, #SYSINTR_MAX_DEVICES
                            ;由此可以看出windowsCE的系统中断号最大支持32种从9-40.
                            ;其中第16号(24)被定义为SYSINTR_FIRMWARE
        ; If not a device request (and not SYSINTR_RESCHED)

        ldrhsb  r0, [lr, #bResched]             ; (r0) = reschedule flag
        bhs     %F20                            ; not a device request

        endEvents  EQU 0x340             ; offset 0x10*sizeof(DWORD) of aInfo
                            ;device 中断
        ldr     r2, [lr, #PendEvents]           ; (r2) = pending interrupt event mask
        mov     r1, #1
        orr     r2, r2, r1, LSL r0              ; (r2) = new pending mask
        str     r2, [lr, #PendEvents]           ; save it
    ;*PendEvents = *PendEvents|(1<<InterruptNO);
        ;
返回列表