Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1495857
  • 博文数量: 267
  • 博客积分: 3010
  • 博客等级: 少校
  • 技术积分: 3089
  • 用 户 组: 普通用户
  • 注册时间: 2011-06-05 17:09
个人简介

尊天命,尽人事

文章分类

全部博文(267)

文章存档

2017年(6)

2015年(4)

2014年(27)

2013年(52)

2012年(59)

2011年(120)

分类: LINUX

2011-12-13 11:16:07

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

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

  1. * arch/arm/kernel/traps.c */
  2. void __init trap_init(void)
  3. {
  4.      extern void __trap_init(unsigned long);
  5.      unsigned long base = vectors_base();
  6.      __trap_init(base);
  7.      if (base != 0)
  8.           oopsprintk(KERN_DEBUG "Relocating machine vectors to 0x%08lx\n", base);
  9. #ifdef CONFIG_CPU_32
  10.      modify_domain(DOMAIN_USER, DOMAIN_CLIENT);
  11. #endif
  12. }
  13. vectors_base是一个宏,它的作用是获取ARM异常向量的地址,该宏在include/arch/asm-arm/proc-armv/system.h中定义:

  14. extern unsigned long cr_no_alignment; /* defined in entry-armv.S */
  15. extern unsigned long cr_alignment; /* defined in entry-armv.S */
  16. #if __LINUX_ARM_ARCH__ >= 4
  17. #define vectors_base() ((cr_alignment & CR_V) ? 0xffff0000 : 0)
  18. #else
  19. #define vectors_base() (0)
  20. #endif
  对于ARMv4以下的版本,这个地址固定为0;ARMv4及其以上的版本,ARM异常向量表的地址受协处理器CP15的c1寄存器(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的定义: 
  1. .globl SYMBOL_NAME(cr_alignment)
  2. .globl SYMBOL_NAME(cr_no_alignment)
  3. SYMBOL_NAME(cr_alignment):
  4.        .space 4                @连续4个字节的空间
  5. SYMBOL_NAME(cr_no_alignment):
  6.        .space 4                @连续4个字节的空间
  分析过head-armv.S文件的朋友都会知道,head-armv.S是非压缩内核的入口: 
  1. 1  .section ".text.init",#alloc,#execinstr
  2. 2  .type stext, #function
  3. 3  ENTRY(stext)
  4. 4  mov r12, r0
  5. 5
  6. 6  mov r0, #F_BIT | I_BIT | MODE_SVC @ make sure svc mode
  7. 7  msr cpsr_c, r0 @ and all irqs disabled
  8. 8  bl __lookup_processor_type
  9. 9  teq r10, #0 @ invalid processor?
  10. 10 moveq r0, #'p' @ yes, error 'p'
  11. 11 beq __error
  12. 12 bl __lookup_architecture_type
  13. 13 teq r7, #0 @ invalid architecture?
  14. 14 moveq r0, #'a' @ yes, error 'a'
  15. 15 beq __error
  16. 16 bl __create_page_tables
  17. 17 adr lr, __ret @ return address
  18. 18 add pc, r10, #12 @ initialise processor
  19. 19 @ (return control reg)
  20. 20
  21. 21 .type __switch_data, %object
  22. 22__switch_data: .long __mmap_switched
  23. 23 .long SYMBOL_NAME(__bss_start)
  24. 24 .long SYMBOL_NAME(_end)
  25. 25 .long SYMBOL_NAME(processor_id)
  26. 26 .long SYMBOL_NAME(__machine_arch_type)
  27. 27 .long SYMBOL_NAME(cr_alignment)
  28. 28 .long SYMBOL_NAME(init_task_union)+8192
  29. 29
  30. 30 .type __ret, %function
  31. 31__ret: ldr lr, __switch_data
  32. 32 mcr p15, 0, r0, c1, c0
  33. 33 mrc p15, 0, r0, c1, c0, 0 @ read it back.
  34. 34 mov r0, r0
  35. 35 mov r0, r0
  36. 36 mov pc, lr
这里我们关心的是从17行开始,17行code处将lr放置为__ret标号处的相对地址,以便将来某处返回时跳转到31行继续运行18行,对于我所分析的pxa270平台,它将是跳转到arch/arm/mm/proc-xscale.S中执行__xscale_setup函数,(在s3c2410平台中,它跳转到arch/arm/mm/proc-arm920.S,在 
  1. .type __arm920_proc_info,#object
  2. __arm920_proc_info:
  3. .long 0x41009200
  4. .long 0xff00fff0
  5. .long 0x00000c1e @ mmuflags
  6. b __arm920_setup
  7. .long cpu_arch_name
  8. .long cpu_elf_name
  9. .long HWCAP_SWP | HWCAP_HALF | HWCAP_THUMB
  10. .long cpu_arm920_info
  11. .long arm920_processor_functions
可以知道add pc, r10, #12 的#12意思是跳过3个指令,执行b _arm920_setup 
在arm920_setup设置完协处理器和返回寄存器r0之后,跳回到__ret:(31行)。 
在__xscale_setup中会读取CP15的control register(c1)的值到r1寄存器,并在r1寄存器中设置相应的标志位(其中包括设置V位=1),但在__xscale_setup中,r1寄存器并不立即写回到Cp15的control register中,而是在返回后的某个地方,接下来会慢慢分析到。__xscale_setup调用move pc, lr指令返回跳转到31行。 
  31行,在lr寄存器中放置__switch_data中的数据__mmap_switched,在36行程序会跳转到__mmap_switched处。 
  32,33行,把r0寄存器中的值写回到cp15的control register(c1)中,再读出来放在r0中。 
  接下来再来看一下跳转到__mmap_switched处的代码: 
  1. 40 _mmap_switched:
  2. 41 adr r3, __switch_data + 4
  3. 42 ldmia r3, {r4, r5, r6, r7, r8, sp}@ r2 = compat
  4. 43 @ sp = stack pointer
  5. 44
  6. 45 mov fp, #0 @ Clear BSS (and zero fp)
  7. 46 1: cmp r4, r5
  8. 47 strcc fp, [r4],#4
  9. 48 bcc 1b
  10. 49
  11. 50 str r9, [r6] @ Save processor ID
  12. 51 str r1, [r7] @ Save machine type
  13. 52 bic r2, r0, #2 @ Clear 'A' bit
  14. 53 stmia r8, {r0, r2} @ Save control register values
  15. 54 b SYMBOL_NAME(start_kernel)
41~42行的结果是:r4=__bss_start,r5=__end,...,r8=cr_alignment,..,这里r8保存的是cr_alignment变量的地址. 
  到了53行,由于之前r0保存的是cp15的control register(c1)的值,这里把r0的值写入r8指向的地址,即cr_alignment=r0.到此为止,我们就看清楚了cr_alignment的赋值过程。 
   
  让我们回到trap_init()函数,经过上面的分析,我们知道vectors_base返回0xffff0000。函数__trap_init由汇编代码编写,在arch/arm/kernel/entry-arm.S: 
  1.     .align 5
  2. __stubs_start:
  3. vector_IRQ:
  4.      ...
  5. vector_data:
  6.     ....
  7. vector_prefetch:
  8.      ...
  9. vector_undefinstr:
  10.      ...
  11. vector_FIQ: disable_fiq
  12.      subs pc, lr, #4
  13. vector_addrexcptn:
  14.      b vector_addrexcptn
  15.     ...
  16. __stubs_end:
  17.      .equ __real_stubs_start, .LCvectors + 0x200
  18. .LCvectors: swi SYS_ERROR0
  19.      b __real_stubs_start + (vector_undefinstr - __stubs_start)
  20.      ldr pc, __real_stubs_start + (.LCvswi - __stubs_start)
  21.      b __real_stubs_start + (vector_prefetch - __stubs_start)
  22.      b __real_stubs_start + (vector_data - __stubs_start)
  23.      b __real_stubs_start + (vector_addrexcptn - __stubs_start)
  24.      b __real_stubs_start + (vector_IRQ - __stubs_start)
  25.      b __real_stubs_start + (vector_FIQ - __stubs_start)
  26. ENTRY(__trap_init)
  27.     stmfd sp!, {r4 - r6, lr} /* 压栈,保存数据*/
  28.     /* 复制异常向量表(.LCvectors起始的8个地址)到r0指向的地址(异常向量地址),r0就是__trap_init(base)函数调用时传递的参数,不明白的请参考ATPCS*/(传递参数顺次利用r0,r1,r2,r3)
  29.     adr r1, .LCvectors @ set up the vectors
  30.     ldmia r1, {r1, r2, r3, r4, r5, r6, ip, lr}
  31.      stmia r0, {r1, r2, r3, r4, r5, r6, ip, lr}
  32. /* 在异常向量地址后的0x200偏移处,放置散转代码,即__stubs_start~__stubs_end之间的各个异常处理代码*/
  33.      add r2, r0, #0x200
  34.      adr r0, __stubs_start @ copy stubs to 0x200
  35.      adr r1, __stubs_end
  36. 1: ldr r3, [r0], #4
  37.      str r3, [r2], #4
  38.      cmp r0, r1
  39. blt 1b
  40. LOADREGS(fd, sp!, {r4 - r6, pc}) /*出栈,恢复数据,函数__trap_init返回*/
  41. __trap_init函数填充后的向量表如下:
  42. 虚拟地址 异常 处理代码
  43. 0xffff0000 reset swi SYS_ERROR0
  44. 0xffff0004 undefined b __real_stubs_start + (vector_undefinstr - __stubs_start)
  45. 0xffff0008 软件中断 ldr pc, __real_stubs_start + (.LCvswi - __stubs_start)
  46. 0xffff000c 取指令异常 b __real_stubs_start + (vector_prefetch - __stubs_start)
  47. 0xffff0010 数据异常 b __real_stubs_start + (vector_data - __stubs_start)
  48. 0xffff0014 reserved b __real_stubs_start + (vector_addrexcptn - __stubs_start)
  49. 0xffff0018 irq b __real_stubs_start + (vector_IRQ - __stubs_start)
  50. 0xffff001c fiq b __real_stubs_start + (vector_FIQ - __stubs_start)
   当有异常发生时,处理器会跳转到对应的0xffff0000起始的向量处取指令,然后,通过b指令散转到异常处理代码.因为ARM中b指令是相对跳转,而且只有+/-32MB的寻址范围,所以把__stubs_start~__stubs_end之间的异常处理代码复制到了0xffff0200起始处.这里可直接用b指令跳转过去,这样比使用绝对跳转(ldr)效率高。 
阅读(675) | 评论(0) | 转发(1) |
给主人留下些什么吧!~~