ARMLINUX的中断系统.docx

上传人:b****7 文档编号:23749391 上传时间:2023-05-20 格式:DOCX 页数:26 大小:28.03KB
下载 相关 举报
ARMLINUX的中断系统.docx_第1页
第1页 / 共26页
ARMLINUX的中断系统.docx_第2页
第2页 / 共26页
ARMLINUX的中断系统.docx_第3页
第3页 / 共26页
ARMLINUX的中断系统.docx_第4页
第4页 / 共26页
ARMLINUX的中断系统.docx_第5页
第5页 / 共26页
点击查看更多>>
下载资源
资源描述

ARMLINUX的中断系统.docx

《ARMLINUX的中断系统.docx》由会员分享,可在线阅读,更多相关《ARMLINUX的中断系统.docx(26页珍藏版)》请在冰豆网上搜索。

ARMLINUX的中断系统.docx

ARMLINUX的中断系统

1,entry-armv.S

.macro irq_handler

1:

get_irqnr_and_baser0,r6,r5,lr

movner1,sp

routinecalledwithr0=irqnumber,r1=structpt_regs*

adrnelr,1b 返回到1处,asm_do_IRQ返回后将再次查询发生的中断

bneasm_do_IRQ kernel的中断处理函数

...

.endm

2,entry-macro.S

下面这个宏查询ISPR(IRQ待定中断服务寄存器,当有需要处理的中断时,这个寄存器的相应位会置位,任意时刻,最多一个位会置位),计算出的中断号放在irqnr指定的寄存器中;这个宏在不同的ARM芯片上是不一样的,下面是s3c44b0x的定义

.macro get_irqnr_and_base, irqnr,irqstat,base,tmp

ldr\base,=S3C44B0X_I_ISPR 装入中断服务寄存器地址 

ldr\base,[\base] 装入中断服务寄存器值

mov\irqnr,#0

2222:

 

tst\base,#1 测试中断位

bne1111f OK,找到

add\irqnr,\irqnr,#1 中断号加1

mov\base,\base,lsr#1 中断服务寄存器值右移1位,比较下一位

cmp\irqnr,#NR_IRQS 比较一下,是不是超出了总的中断号

bcc2222b 还没找到,继续

1111:

 

.endm

3,下面这个宏是所有中断服务过程进入时的前序,主要是在当前堆栈上分配一个pt_regs结构,把r0-r15以及cpsr等保存到这个结构中,在进入irq_handler时,sp指向pt_regs底端

.macro svc_entry

subsp,sp,#S_FRAME_SIZE sizeof(pt_regs),分配一个pt_regs结构

stmibsp,{r1-r12} 保存r1-r12

ldmiar0,{r1-r3} r0指向何处?

这个地方的容是什么?

参见4节

(1)

(2)处

addr5,sp,#S_SP S_SP为sp寄存器在pt_regs中的偏移

movr4,#-1  ORIG_r0=-1?

addr0,sp,#S_FRAME_SIZE r0为中断前的堆栈指针,将成为pt_regs中的sp的值

strr1,[sp] savethe"real"r0copied fromtheexceptionstack

movr1,lr

Wearenowreadytofillintheremainingblanksonthestack:

r0-sp_svc

r1-lr_svc

r2-lr_,alreadyfixedupforcorrectreturn/restart

r3-spsr_

r4-orig_r0(seept_regsdefinitioninptrace.h)

stmiar5,{r0-r4}

.endm

4,

.macro vector_stub,name,mode,correction=0

.align5

vector_\name:

.if \correction

sublr,lr,#\correction 修正返回地址,如果必要的话

.endif

Saver0,lr_ (parentPC)andspsr_

(parentCPSR)

stmiasp,{r0,lr} saver0,lr 

(1)(为什么向上长呢?

mrslr,spsr

strlr,[sp,#8] savespsr

PrepareforSVC32mode.IRQsremaindisabled.

mrsr0,cpsr

eorr0,r0,#(\mode^SVC_MODE)

msrspsr_cxsf,r0

thebranchtablemustimmediatelyfollowthiscode

andlr,lr,#0x0f 这里时,lr的值是spsr的值,因此,这个指令之后lr的值为spsr[0-3],即,系统模式的低4位

movr0,sp 

(2)

ldrlr,[pc,lr,lsl#2] lr=pc+lr<<2,usr:

pc,fiq:

pc+4,irq:

pc+8,svc:

pc+12,abt:

pc+28,und:

pc+44,system:

pc+60

movspc,lr branchtohandlerinSVCmode

.endm

对于irq中断,会这样调用上面这个宏:

vector_stubirq,IRQ_MODE,4

5,中断处理过程总体结构,当中断发生后控制先转移到4,然后跳转到__irq_svc

__irq_svc:

svc_entry

irq_handler

ldrr0,[sp,#S_PSR] irqsarealreadydisabled,S_PSR为cpsr在pt_regs中的偏移;sp指向在svc_entry中分配的pt_regs结构

msrspsr_cxsf,r0 恢复spsr

ldmiasp,{r0-pc}^ loadr0-pc,cpsr

...

.global__stub_start

__stub_start:

vector_stubirq,IRQ_MODE,4

.long__irq_usr

.long__irq_invalid

.long__irq_invalid

.long__irq_svc

.long__irq_invalid

...

以下与irq中断无关,但是是arm整个异常处理结构的一部分,因此列示在这里

vector_stubdabt,ABT_MODE,8

...

vector_stubpabt,ABT_MODE,4

...

vector_stubund,UND_MODE

...

vector_fiq:

...

vector_addrexcptn:

...

.LCvswi:

...

.globl__stubs_end

__stubs_end:

...

下面这些才是最初的入口点,__vector_start和__vector_end之间的代码会被移动到CONFIG_VECTORS_BASE开始的地方,例如0xc000000

.globl__vectors_start

__vectors_start:

swiSYS_ERROR0 Reset

bvector_und+stubs_offset Undefinedinstruction

ldrpc,.LCvswi+stubs_offset swiinstruction

bvector_pabt+stubs_offset PrefetchAbort

bvector_dabt+stubs_offset DataAbort

bvector_addrexcptn+stubs_offset ARMreserved

b vector_irq +stubs_offset IRQ

bvector_fiq+stubs_offset FIQ

.globl__vectors_end

__vectors_end:

...

附录1,arm体系下pt_regs结构

struct pt_regs{

longuregs[18];

};

uregs[0]-uregs[17]分别对应,r0-r15,cpsr,ORIG_r0

附录1,irq中断时堆栈的变化

--------

spsr

--------

lr,中断返回地址,修正后的

--------

r0

--------<-进入irq_svc之前,sp的值,也是r0的值

pt_regs

---------<-进入svc_entry后,sp的值

浅析armlinux2.4.19中断irq分发例程的派发流程之根基

文章来源:

应用程序运行在user模式,对应arm的cpsr&15的值为0,而核代码运行在svc模式,对应arm的cpsr&15的值为3,

所以,如果应用程序在运行期间,即usr模式下,arm发生了irq中断,那么中断处理代码得知后会调用__irq_usr分发处理例程处理irq中断,如果在系统调用syscall之类使程序运行在核空间执行核程序的时候,即svc模式下,arm发生了irq中断,那么中断处理代码得知后会调用__irq_svc分发处理例程处理irq中断,中断程序是怎么识别svc和usr模式,进而派发相应的__irq_usr和__irq_svc分发例程的呢,来看看代码部分:

//1.arch\arm\kernel\entry-armv.S

...

.LCvswi:

    .word    vector_swi

.LCsirq:

    .word    __temp_irq

.LCsund:

    .word    __temp_und

.LCsabt:

    .word    __temp_abt

__stubs_end:

        .equ    __real_stubs_start, .LCvectors + 0x200

.LCvectors:

    swi    SYS_ERROR0

        b    __real_stubs_start + (vector_undefinstr - __stubs_start)

        ldr    pc, __real_stubs_start + (.LCvswi - __stubs_start)

        b    __real_stubs_start + (vector_prefetch - __stubs_start)

        b    __real_stubs_start + (vector_data - __stubs_start)

        b    __real_stubs_start + (vector_addrexcptn - __stubs_start)

//irq中断发生后,cpu捕获中断,跳转到这里执行vector_IRQ处理程序[gliethttp_20071225]

//中断处理代码位于0xFFFF0000地址之后的空间中,

//为了更透彻的理解,最好先看看另3篇文章

//《浅析arm-linux中断vector向量表的建立流程》

//《浅析armlinux-setup_arch()->create_mapping()函数5-2-2》

//《浅析arm-linux系统调用的流程》

//文章1:

//文章2:

//文章3:

//为了更好理解上面的这几篇文章,最好先看看《浅析armlinux2.4.19启动程序[head-armv.s文件]》

//文章4:

        b    __real_stubs_start + (vector_IRQ - __stubs_start)

        b    __real_stubs_start + (vector_FIQ - __stubs_start)

...

//2.arch\arm\kernel\entry-armv.S

...

vector_IRQ:

    

        savemodespecificregisters

        

        ldr    r13, .LCsirq    //取出LCsirq变量的地址,用来存放lr

        sub    lr, lr, #4        //lr-4,这是arm必须的

        str    lr, [r13]        //将计算之后的返回地址lr存入LCsirq变量

//读取此次irq中断发生时,cpu所处模式spsr,即,中断发生时cpu正在用户空间运行用户应用程序,

//还是在核空间svc模式下运行核代码,lr&15的数值为arm进入irq之前cpu运行的模式值

//如果lr&15=0表示,此次irq中断发生时,cpu正运行用户空间的用户程序,

//如果lr&15=3表示,此次irq中断发生时,cpu正运行核空间的核代码[gliethttp_20071225]

        mrs    lr, spsr

        str    lr, [r13, #4]

        mrs    r13, cpsr

        bic    r13, r13, #MODE_MASK

        orr    r13, r13, #I_BIT | MODE_SVC

//切换到svc模式,因为当前cpsr为irq模式,所以可以修改cpsr,

//注意在usr模式下cpsr的数值,即使使用了msrspsr_c,r13赋值语句,

//因为usr模式不允许修改cpsr,所以r13的数值并不能被真正赋值给spsr,

//执行完赋值语句之后,spsr的数值不会发生任何改变,仍然是原来的值[gliethttp_20071225]

        msr    spsr_c, r13    

        and    lr, lr, #15

//lr&15的值为发生irq之前cpu所在的空间,

//0:

在用户空间发生了irq中断

//3:

在核空间发生了irq中断

//lr<<2=lr*4也就是pc+0和pc+12

//分别对应LCtab_irq的.word__irq_usr域和.word__irq_svc域

        ldr    lr, [pc, lr, lsl #2]

//跳转到__irq_usr或者__irq_svc处理此次irq中断[gliethttp_20071225]

        movs    pc, lr

.LCtab_irq:

 .word    __irq_usr            0 (USR_26 / USR_32)//用户空间发生irq中断

        .word    __irq_invalid            1 (FIQ_26 / FIQ_32)

        .word    __irq_invalid            2 (IRQ_26 / IRQ_32)

        .word    __irq_svc                3 (SVC_26 / SVC_32)//核空间发生irq中断

        .word    __irq_invalid            4 

        .word    __irq_invalid            5

        .word    __irq_invalid            6

        .word    __irq_invalid            7

        .word    __irq_invalid            8

        .word    __irq_invalid            9

        .word    __irq_invalid            a

        .word    __irq_invalid            b

        .word    __irq_invalid            c

        .word    __irq_invalid            d

        .word    __irq_invalid            e

        .word    __irq_invalid            f

...

.LCvswi:

    .word    vector_swi

.LCsirq:

    .word    __temp_irq

.LCsund:

    .word    __temp_und

.LCsabt:

    .word    __temp_abt

ARMLinux中断分析

ARM体系结构中,把复位、中断、快速中断等都看作‘异常’,当这些‘异常’发生时,CPU会到固定地址处去找指令,他们对应的地址如下:

地址

异常类型

进入时的工作模式

0x00000000

Reset

Supervisor

0x00000004

Und

Undefined

0x00000008

Softinterupt

Supervisor

0x0000000c

 Abort(prefetch) 

Abort

0x00000010

Abort(data)

Abort

0x00000014

Reserved 

Reserved

0x00000018

IRQ

IRQ

0x0000001c

FIQ

FIQ

  首先要明确的一点就是,无论存地址空间是如何映射的,以上这些地址都不会变,比如当有快速中断发生时,ARM将铁定到0X0000001C这个地址处取指令。

这也是BOOTLOADER把操作系统引导以后,存必须重映射的原因!

否则操作系统不能真正接管整套系统!

 

  

       LINUX启动以后要初始化这些区域,初始化代码在main.c中的start_kernel()中,具体是调用函数trap_ini()来实现的。

如下面所示(具体可参照entry-armv.S):

.LCvectors:

swiSYS_ERROR0 

b__real_stubs_start+(vector_undefinstr-__stubs_start) 

ldrpc,__real_stubs_start+(.LCvswi-__stubs_start) 

b__real_stubs_start+(vector_prefetch-__stubs_start) 

b__real_stubs_start+(vector_data-__stubs_start) 

b__real_stubs_start+(vector_addrexcptn-__stubs_start) 

b__real_stubs_start+(vector_IRQ-__stubs_start) 

b__real_stubs_start+(vector_FIQ-__stubs_start)

ENTRY(__trap_init) 

stmfdsp!

{r4-r6,lr} 

adrr1,.LCvectorssetupthevectors 

ldmiar1,{r1,r2,r3,r4,r5,r6,ip,lr} 

stmiar0,{r1,r2,r3,r4,r5,r6,ip,lr} 

addr2,r0,#0x200 

adrr0,__stubs_startcopystubsto0x200 

adrr1,__stubs_end 

1:

ldrr3,[r0],#4 

strr3,[r2],#4 

cmpr0,r1 

blt1b 

LOADREGS(fd,sp!

{r4-r6,pc})

  以上可以看出这个函数初始化了中断向量,实际上把相应的跳转指令拷贝到了对应的地址。

 

  

       当发生中断时,不管是从用户模式还是管理模式调用的,最终都要调用do_IRQ():

__irq_usr:

subsp,sp,#S_FRAME_SIZE 

stmiasp,{r0-r12}saver0-r12 

ldrr4,.LCirq 

addr8,sp,#S_PC 

ldmiar4,{r5-r7}getsavedPC,SPSR 

stmiar8,{r5-r7}savepc,psr,old_r0 

stmdbr8,{sp,lr}^ 

alignment_trapr4,r7,__temp_irq 

zero_fp 

1:

get_irqnr_and_baser0,r6,r5,lr 

movner1,sp 

adrsvcne,lr,1b 

 

routinecalledwithr0=irqnumber,r1=structpt_regs* 

 

bnedo_IRQ调用do_IRQ来实现具体的中断处理 

movwhy,#0 

get_current_tasktsk 

bret_to_user

  对于以上代码,在很多文章中都有过分析,这里不再赘述。

  Linux每个中断通过一个结构irqdesc来描述,各中断的信息都在这个结构中得以体现:

structirqdesc{ 

unsignedintnomask:

1;/*IRQdoesnotmaskinIRQ*/ 

unsignedintenabled:

1;/*IRQiscurrentlyenabled*/ 

unsignedinttriggered:

1;/*IRQhasoccurred*/ 

unsignedintprobing:

1;/*IRQinuseforaprobe*/ 

unsignedintprobe_ok:

1;/*IRQcanbeusedforprobe*/ 

unsignedintvalid:

1;/*IRQclaimable*/ 

unsignedintnoautoenable:

1;/*don'tautomaticallyenableIRQ*/ 

unsignedintunused:

25; 

void(*mask_ack)(unsignedintirq);/*MaskandacknowledgeIRQ*/ 

void(*mask)(unsignedintirq);/*MaskIRQ*/ 

void(*unma

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 幼儿教育 > 幼儿读物

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1