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 以后补上<浅析head-armv.S>]返回值 //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地址的跳转 |
from: http://www.xker.com/page/e2007/0705/27472.html