arm中断流程linux蜗窝科技,浅析Arm Linux中断Vector向量表建立流程

Linux混入了mmu内存管理之后,ARM的中断是怎么样的呢?和我们在裸板上的中断有没有区别?让我们从源代码入手,做一个粗略的分析:

init/main.c->start_kernel()->trap_init()

//-----------------------------------------------

1.trap_init()

//gliethttp函数位于arch/arm/kernel/traps.c

void __init trap_init(void)

{

extern void __trap_init(unsigned long);

unsigned long base = vectors_base(); //返回中断base基址0xffff0000

__trap_init(base);                   //以base为vector基址,初始化中断向量表

if (base != 0)

printk(KERN_DEBUG "Relocating machine vectors to 0x%08lx\n",

base);

#ifdef CONFIG_CPU_32

modify_domain(DOMAIN_USER, DOMAIN_CLIENT);

#endif

}

//--------------------------------------

2.vectors_base()

//gliethttp include/arch/asm-arm/proc-armv/system.h

extern unsigned long cr_alignment;

#if __LINUX_ARM_ARCH__ >= 4                //at91rm9200是ARMV4结构

#define vectors_base()    ((cr_alignment & CR_V) ? 0xffff0000 : 0)

#else

#define vectors_base()    (0)

#endif

可以看到ARMv4以下的版本,该地址固定为0;ARMv4及以上版本,ARM中断向量表的地址由CP15协处理器c1寄存器中V位(bit[13])控制,V和中断向量表的对应关系如下:

V=0    ~    0x00000000~0x0000001C

V=1    ~    0xffff0000~0xffff001C

//------------------------------------------

2.1 cr_alignment

//gliethttp arch/arm/kernel/entry-armv.S

ENTRY(stext)

mov    r12, r0

mov    r0, #F_BIT | I_BIT | MODE_SVC @ make sure svc mode

msr    cpsr_c, r0                @ and all irqs disabled

//__lookup_processor_type 查询处理器类型,[gliethttp

以后补上]返回值

//2007-07-04

//r9 = processor ID                    //读取cp15的c0寄存器

//r10 = pointer to processor structure

//下面会add pc, r10, #12,跳转到__arm920_setup

//gliethttp 在vmlinux-armv.lds.in中

//__proc_info_begin = .;

//             *(.proc.info)

// __proc_info_end = .;

//见2.2

bl    __lookup_processor_type

teq    r10, #0                   @ invalid processor?

moveq    r0, #'p'                @ yes, error 'p'

beq    __error

bl    __lookup_architecture_type

teq    r7, #0                    @ invalid architecture?

moveq    r0, #'a'                @ yes, error 'a'

beq    __error

//__create_page_tables 创建arm启动临时使用的前4M页表

bl    __create_page_tables

adr    lr, __ret                 @ return address

add    pc, r10, #12              @ initialise processor

.type    __switch_data, %object

__switch_data:    .long    __mmap_switched

.long    SYMBOL_NAME(__bss_start)

.long    SYMBOL_NAME(_end)

.long    SYMBOL_NAME(processor_id)

.long    SYMBOL_NAME(__machine_arch_type)

.long    SYMBOL_NAME(cr_alignment)

.long    SYMBOL_NAME(init_task_union)+8192

/*

* Enable the MMU. This completely changes the structure of the visible

* memory space. You will not be able to trace execution through this.

* If you have an enquiry about this, *please* check the linux-arm-kernel

* mailing list archives BEFORE sending another post to the list.

*/

.type    __ret, %function

__ret:        ldr    lr, __switch_data

mcr    p15, 0, r0, c1, c0

//将__arm920_setup中设置的r0值,置入cp15协处理器c1寄存器中

mrc    p15, 0, r0, c1, c0, 0     @ read it back.

mov    r0, r0

//填充armv4中的三级流水线:mov r0,

r0 对应一个nop,所以对应2个nop和一个mov pc,lr刚好三个"无用"操作

mov    r0, r0

mov    pc, lr

//跳转到__mmap_switched函数 gliethtttp

/*

* The following fragment of code is executed with the MMU on, and uses

* absolute addresses; this is not position independent.

*

* r0 = processor control register

* r1 = machine ID

* r9 = processor ID

*/

.align    5

__mmap_switched:

adr    r3, __switch_data + 4

ldmia    r3, {r4, r5, r6, r7, r8, sp}@ r2 = compat

//2007-07-04 gliethttp

//r4    ~    __bss_start

//r5    ~    _end

//r6    ~    processor_id

//r7    ~    __machine_arch_type

//r8    ~    cr_alignment

//sp    ~    (init_task_union)+8192

//以下几步操作对processor_id,__machine_arch_type,cr_alignment赋值gliethttp

mov    fp, #0                                @ Clear BSS (and zero fp)

1:      cmp    r4, r5                                //bss区清0

strcc    fp, [r4],#4

bcc    1b

str    r9, [r6]                              @ Save processor ID

str    r1, [r7]                              @ Save machine type

#ifdef CONFIG_ALIGNMENT_TRAP

orr    r0, r0, #2                            @ ...........A.

#endif

bic    r2, r0, #2                            @ Clear 'A' bit

//r2存放 禁用TRAP队列故障 后的r0值

//r8->cr_alignment,cr_no_alignment

//所以stmia    r8, {r0, r2}后,cr_alignment = r0,cr_no_alignment = r2

stmia    r8, {r0, r2}           @ Save control register values

b    SYMBOL_NAME(start_kernel)        //进入内核C程序

//--------------------------------------

2.2 __arm920_proc_info

//gliethttp arch/arm/mm/proc-arm920.S

.section ".proc.info", #alloc, #execinstr

.type    __arm920_proc_info,#object

__arm920_proc_info:

//该地址存储到r10中

.long    0x41009200

.long    0xff00fff0

.long    0x00000c1e                               @ mmuflags

b    __arm920_setup

//add pc, r10, #12 gliethttp将使cpu执行b __arm920_setup跳转指令

.long    cpu_arch_name

.long    cpu_elf_name

.long    HWCAP_SWP | HWCAP_HALF | HWCAP_THUMB

.long    cpu_arm920_info

.long    arm920_processor_functions

.size    __arm920_proc_info, . - __arm920_proc_info

//----------------------------------------

2.3 __arm920_setup

.section ".text.init", #alloc, #execinstr

__arm920_setup:

mov    r0, #0

mcr    p15, 0, r0, c7, c7        @ invalidate I,D caches on v4

mcr    p15, 0, r0, c7, c10, 4    @ drain write buffer on v4

mcr    p15, 0, r0, c8, c7        @ invalidate I,D TLBs on v4

mcr    p15, 0, r4, c2, c0        @ load page table pointer

mov    r0, #0x1f                 @ Domains 0, 1 = client

mcr    p15, 0, r0, c3, c0        @ load domain access register

mrc    p15, 0, r0, c1, c0        @ get control register v4

/*

* Clear out 'unwanted' bits (then put them in if we need them)

*/

//gliethttp r0单元存放了cp15协处理器c1寄存器的值,如下代码对该值进行加工

@ VI ZFRS BLDP WCAM

bic    r0, r0, #0x0e00                           //清0 bit[9..11]

bic    r0, r0, #0x0002                           //清0 bit[1]

bic    r0, r0, #0x000c

bic    r0, r0, #0x1000                @ ...0 000. .... 000.

/*

* Turn on what we want

*/

orr    r0, r0, #0x0031                           //bit0=1 使能mmu

orr    r0, r0, #0x2100                @ ..1. ...1 ..11 ...1

//bit13=1 中断向量表基址为0xFFFF0000

#ifndef CONFIG_CPU_DCACHE_DISABLE

orr    r0, r0, #0x0004                @ .... .... .... .1..

#endif

#ifndef CONFIG_CPU_ICACHE_DISABLE

orr    r0, r0, #0x1000                @ ...1 .... .... ....

#endif

mov    pc, lr

小结:通过以上的源码分析,我们可以清楚的看到vectors_base()返回的中断向量基址值为0xFFFF0000,接下来我们继续分析下面的源码:

3.__trap_ini()

//gliethttp函数位于arch/arm/kernel/entry-armv.S

.align    5

__stubs_start:

/*

* Interrupt dispatcher

* Enter in IRQ mode, spsr = SVC/USR CPSR, lr = SVC/USR PC

*/

vector_IRQ:    @

@ save mode specific registers

@

ldr    r13, .LCsirq

sub    lr, lr, #4

str    lr, [r13]            @ save lr_IRQ

mrs    lr, spsr

str    lr, [r13, #4]        @ save spsr_IRQ

...略...

vector_addrexcptn:

b    vector_addrexcptn

/*

* We group all the following data together to optimise

* for CPUs with separate I & D caches.

*/

.align    5

.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)

b    __real_stubs_start + (vector_IRQ - __stubs_start)

b    __real_stubs_start + (vector_FIQ - __stubs_start)

ENTRY(__trap_init)

stmfd     {r4 - r6, lr}

adr    r1, .LCvectors    @ set up the vectors

//通过adr指令获得与pc地址为偏移地址数据,最后r1=pc-0x2c

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

//将中断向量表跳转数据分别转存到r1,r2,r3,r4,r5,r6,ip,lr寄存器

stmia    r0, {r1, r2, r3, r4, r5, r6, ip, lr}

//根据编译器规则r0存放了函数__trap_init(base)传入的参数值base,其值为

//0xFFFF0000

//将r1,r2,r3,r4,r5,r6,ip,lr数据顺序转储到以虚拟地址0xFFFF0000为起始地址的空间

add    r2, r0, #0x200

//r2=0xFFFF0000+0x200=0xFFFF2000

adr    r0, __stubs_start@ copy stubs to 0x200

//r0=pc相对地址=pc-0x26c

adr    r1, __stubs_end

//r1=pc相对地址=pc-0x40

1:      ldr    r3, [r0], #4

str    r3, [r2], #4

//将__stubs_start

和__stubs_end之间的中断处理代码拷贝到以虚拟地址0xFFFF2000为起始地址的顺序空间

cmp    r0, r1

blt    1b

LOADREGS(fd, {r4 - r6, pc})      这样我们来看看空间分布图:

虚拟地址     异常              处理代码

0xffff0000   reset             swi SYS_ERROR0

0xffff0004   undefined  b __real_stubs_start + (vector_undefinstr - __stubs_start)

0xffff0008   软件中断     ldr pc, __real_stubs_start + (.LCvswi - __stubs_start)

0xffff000c   取指令异常   b __real_stubs_start + (vector_prefetch - __stubs_start)

0xffff0010   数据异常     b __real_stubs_start + (vector_data - __stubs_start)

0xffff0014   reserved   b __real_stubs_start + (vector_addrexcptn - __stubs_start)

0xffff0018   irq        b __real_stubs_start + (vector_IRQ - __stubs_start)

0xffff001c   fiq        b __real_stubs_start + (vector_FIQ - __stubs_start)

...

0xffff2000   __stubs_start:    ldr    r13, .LCsirq

0xffff2004                     sub    lr, lr, #4

0xffff2008                     str    lr, [r13] @ save lr_IRQ

...

0xffff21a4                     .LCsirq:    .word    __temp_irq

0xffff21a8                     .LCsund:    .word    __temp_und

0xffff21ac                     .LCsabt:    .word    __temp_abt

如果你现在有这样一种疑惑?程序为什么编译地址是0xc0008000,将其直接拷贝到0xffff0000和0xffff2000为什么还能顺利执行,请参考我的另一篇blog《arm相对跳转到底是怎么回事》,主要原因是b指令是相对跳转指令,adr也是基于pc的前后偏移指令,当然对于ldr pc, __real_stubs_start + (.LCvswi - __stubs_start)是绝对地址赋值,所以最后pc会跳转到0xc000xxxx空间执行代码,其他的跳转如:b __real_stubs_start + (vector_IRQ - __stubs_start)都会到0xffff2xxxx相应的vector_IRQ处执行向量中断处理函数,还有一个要分析的问题是:.equ __real_stubs_start,

.LCvectors + 0x200,语句b __real_stubs_start

+ (vector_undefinstr - __stubs_start)就是跳转到LCvectors+0x200空间执行。举一个例子:

org 0x8000

reset    b    InitRest

...

InitRest:

...

那么reset标号的地址为0x8000,他的意思是在0x8000处向前跳转到InitRest,

我们也可以这样来构造跳转:

org 0x8000

reset b (0x8000+(.InitRest - .reset))

...

InitRest:

...

以上的构造语句同样实现了相对0x8000地址的跳转

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值