现在的位置: 首页 > 综合 > 正文

ARM Linux外部中断处理过程

2013年01月24日 ⁄ 综合 ⁄ 共 6407字 ⁄ 字号 评论关闭

ARM Linux外部中断处理过程

http://www.linuxforum.net/forum/showflat.php?Cat=&Board=linuxK&Number=652682&page=12&view=collapsed&sb=7&o=all&fpart=1&vc=1

作者:muxiaowei 整理:Nathan.Yu

 

最近在学习arm linux的整套外部中断的处理过程,在网上汇总了一些资料,整个过程差不多都了解到了。如果没有这些资料我真是没信心从汇编开始读代码,感谢 奔腾年代的jimmy.lee linux论坛的bx_bird

在下面的的注释中有一些我读代码时遇到的问题,要是大家知道是怎么回事,希望多多回复。

 

=============================================

一.ARM linux的中断向量表初始化分析

ARM linux内核启动时,通过start_kernel()->trap_init()的调用关系,初始化内核的中断异常向量表.

 

* arch/arm/kernel/traps.c */

void __init trap_init(void)

{

extern void __trap_init(unsigned long);

unsigned long base = vectors_base();

__trap_init(base);

if (base != 0)

oopsprintk(KERN_DEBUG "Relocating machine vectors to 0x%08lx/n", base);

#ifdef CONFIG_CPU_32

modify_domain(DOMAIN_USER, DOMAIN_CLIENT);

#endif

}

vectors_base是一个宏,它的作用是获取ARM异常向量的地址,该宏在include/arch/asm-arm/proc-armv/system.h中定义:

 

extern unsigned long cr_no_alignment; /* defined in entry-armv.S */

extern unsigned long cr_alignment; /* defined in entry-armv.S */

#if __LINUX_ARM_ARCH__ >= 4

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

#else

#define vectors_base() (0)

#endif

  对于ARMv4以下的版本,这个地址固定为0ARMv4及其以上的版本,ARM异常向量表的地址受协处理器CP15c1寄存器(control register)V(bit[13])的控制,如果V=1,则异常向量表的地址为0x00000000~0x0000001C;如果V=0,则为:0xffff0000~0xffff001C。(详情请参考ARM Architecture Reference Manual)

  下面分析一下cr_alginment的值是在哪确定的,我们在arch/arm/kernel/entry-armv.S找到cr_alignment的定义:

 

.globl SYMBOL_NAME(cr_alignment)

.globl SYMBOL_NAME(cr_no_alignment)

SYMBOL_NAME(cr_alignment):

.space 4

SYMBOL_NAME(cr_no_alignment):

.space 4

 

  分析过head-armv.S文件的朋友都会知道,head-armv.S是非压缩内核的入口:

 

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

.type stext, #function

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

bl __lookup_processor_type

teq r10, #0 @ invalid processor?

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

11 beq __error

12 bl __lookup_architecture_type

13 teq r7, #0 @ invalid architecture?

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

15 beq __error

16 bl __create_page_tables

17 adr lr, __ret @ return address

18 add pc, r10, #12 @ initialise processor

19 @ (return control reg)

20

21 .type __switch_data, %object

22__switch_data: .long __mmap_switched

23 .long SYMBOL_NAME(__bss_start)

24 .long SYMBOL_NAME(_end)

25 .long SYMBOL_NAME(processor_id)

26 .long SYMBOL_NAME(__machine_arch_type)

27 .long SYMBOL_NAME(cr_alignment)

28 .long SYMBOL_NAME(init_task_union)+8192

29

30 .type __ret, %function

31__ret: ldr lr, __switch_data

32 mcr p15, 0, r0, c1, c0

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

34 mov r0, r0

35 mov r0, r0

36 mov pc, lr

这里我们关心的是从17行开始,17code处将lr放置为__ret标号处的相对地址,以便将来某处返回时跳转到31行继续运行18,对于我所分析的pxa270平台,它将是跳转到arch/arm/mm/proc-xscale.S中执行__xscale_setup函数,(在s3c2410平台中,它跳转到arch/arm/mm/proc-arm920.S,在

type __arm920_proc_info,#object

__arm920_proc_info:

.long 0x41009200

.long 0xff00fff0

.long 0x00000c1e @ mmuflags

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

可以知道add pc, r10, #12 的#12意思是跳过3个指令,执行b _arm920_setup

arm920_setup设置完协处理器和返回寄存器r0之后,跳回到__ret:(31)

__xscale_setup中会读取CP15control register(c1)的值到r1寄存器,并在r1寄存器中设置相应的标志位(其中包括设置V位=1),但在__xscale_setup中,r1寄存器并不立即写回到Cp15control register中,而是在返回后的某个地方,接下来会慢慢分析到。__xscale_setup调用move pc, lr指令返回跳转到31行。

  31行,在lr寄存器中放置__switch_data中的数据__mmap_switched,在36行程序会跳转到__mmap_switched处。

  3233行,把r0寄存器中的值写回到cp15control register(c1),再读出来放在r0中。

  

  接下来再来看一下跳转到__mmap_switched处的代码:

40 _mmap_switched:

41 adr r3, __switch_data + 4

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

43 @ sp = stack pointer

44

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

46 1: cmp r4, r5

47 strcc fp, [r4],#4

48 bcc 1b

49

50 str r9, [r6] @ Save processor ID

51 str r1, [r7] @ Save machine type

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

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

54 b SYMBOL_NAME(start_kernel)

41~42行的结果是:r4=__bss_startr5=__end,...,r8=cr_alignment,..,这里r8保存的是cr_alignment变量的地址.

  到了53行,由于之前r0保存的是cp15control register(c1)的值,这里把r0的值写入r8指向的地址,即cr_alignment=r0.到此为止,我们就看清楚了cr_alignment的赋值过程。

  

  让我们回到trap_init()函数,经过上面的分析,我们知道vectors_base返回0xffff0000。函数__trap_init由汇编代码编写,在arch/arm/kernel/entry-arm.S

    .align 5

__stubs_start:

vector_IRQ:

     ...

vector_data:

    ....

vector_prefetch:

     ...

vector_undefinstr:

     ...

vector_FIQ: disable_fiq

     subs pc, lr, #4

vector_addrexcptn:

     b vector_addrexcptn

    ...

__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 sp!, {r4 - r6, lr} /* 压栈,保存数据*/

    /* 复制异常向量表(.LCvectors起始的8个地址)到r0指向的地址(异常向量地址),r0就是__trap_init(base)函数调用时传递的参数,不明白的请参考ATPCS*/(传递参数顺次利用r0r1r2r3

    adr r1, .LCvectors @ set up the vectors

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

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

 

/* 在异常向量地址后的0x200偏移处,放置散转代码,即__stubs_start~__stubs_end之间的各个异常处理代码*/

     add r2, r0, #0x200

     adr r0, __stubs_start @ copy stubs to 0x200

     adr r1, __stubs_end

1: ldr r3, [r0], #4

     str r3, [r2], #4

     cmp r0, r1

blt 1b

LOADREGS(fd, sp!, {r4 - r6, pc}) /*出栈,恢复数据,函数__trap_init返回*/

__trap_init函数填充后的向量表如下:

虚拟地址 异常 处理代码

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)

 

   当有异常发生时,处理器会跳转到对应的0xffff0000起始的向量处取指令,然后,通过b指令散转到异常处理代码.因为ARMb指令是相对跳转,而且只有+/-32MB的寻址范围,所以把__stubs_start~__stubs_end之间的异常处理代码复制到了0xffff0200起始处.这里可直接用b指令跳转过去,这样比使用绝对跳转(ldr)效率高。

二.ARM Linux中断处理过程分析(1)

在我的上一篇文章(ARM linux的中断向量表初始化分析)中已经分析了ARM Linux中断向量表是如何建立的,在这篇文章中,我将分析一下Linux内核的ARM体系下,中断处理是如何响应的一个过程。

ARM体系架构下,定义了7种异常,每一种异常都有自己的入口地址,即异常向量表,当异常发生时,处理器会自动跳转到相应的入口处执行。对于ARMv4及其以上的版本,异常向量表的起始位置由协处理器15cp15)的控制寄存器(c1)里的V(bit13)有关,当V=0时,异常向量表的起始位置在0x00000000,而当V=1时,异常向量表就起始于0xffff0000位置。在上一篇文章中,我们已经分析知道异常向量表放置于0xffff0000起始位置,而IRQ中断处理入口地址为:0xffff0018,所以当发生一IRQ中断异常时,处理器会自动跳转到0xffff0018这个虚拟地址上。

0xffff0018这个虚拟地址上是一条跳转指令:

b __real_stubs_start + (vector_IRQ - __stubs_start)

 

所以对于IRQ的处理就是从vector_IRQ标号处开始的。在linux2.4.19内核中相应代码如下:

__stubs_start:

/*

* Interrupt dispatcher

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

*/说明其实linux只用到了armsvcusr模式,其他的几个模式都没怎么用。

1 vector_IRQ: @

2 @ save mode specific registers

抱歉!评论已关闭.