找到了下面内容,但是还是有些不理解什么叫可分配段,继续找。
.section 语法: .section NAME [, "FLAGS"[, @TYPE[, @ENTSIZE]]] FLAGS 常用的有以下几种: `a' 可分配段 `w' 可写段 `x' 可执行段 data section 具有的 FLAGS 通常是’wa’; text section 具有的 FLAGS 通常是’ax’。 用法: .section .text1, “wa” // 定义一个.text1段,且该段为可写的以及可分配的 请参考 《GNU linker ld》
文分析基于linux2.4.19 source,pxa 270 cpu.
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以下的版本,这个地址固定为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的定义: .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是非压缩内核的入口: 1 .section ".text.init",#alloc,#execinstr 2 .type stext, #function 3ENTRY(stext) 4 mov r12, r0 5 6 mov r0, #F_BIT | I_BIT | MODE_SVC @ make sure svc mode 7 msr cpsr_c, r0 @ and all irqs disabled 8 bl __lookup_processor_type 9 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行开始,17行code处将lr放置为__ret标号处的相对地址,以便将来某处返回时跳转到31行继续运行; 18行,对于我所分析的pxa270平台,它将是跳转到arch/arm/mm/proc-xscale.S中执行__xscale_setup函数, 在__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处的代码: 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_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: .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*/ 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指令散转到异常处理代码.因为ARM中b指令是相对跳转,而 且只有+/-32MB的寻址范围,所以把__stubs_start~__stubs_end之间的异常处理代码复制到了0xffff0200起始处.这 里可直接用b指令跳转过去,这样比使用绝对跳转(ldr)效率高。 -------------------------参考资料-------------------- 1, 刘淼,嵌入式系统接口设计与Linux驱动程序开发,北京航天航空大学出版社,2006. 2, ARM Architecture Reference Manual, ARM limited,2000. |
|
来自: ARM_Linux > 《linux 内核技术》