接上篇:linux内核启动过程分析(一)
三、内核启动过程
1.汇编阶段
同样可以从内核的编译链接文件(arch/arm/kernel/head.S)中得到内核代码的入口地址"stext"。
OUTPUT_ARCH(arm) ENTRY(stext) jiffies = jiffies_64; SECTIONS { /DISCARD/ : { *(.ARM.exidx.exit.text) *(.ARM.extab.exit.text) *(.exitcall.exit) *(.discard) *(.discard.*) }
内核入口stext(archarmkernelhead.S)
.arm __HEAD ENTRY(stext) ARM_BE8(setend be ) @ ensure we are in BE8 mode THUMB( adr r9, BSYM(1f) ) @ Kernel is always entered in ARM. THUMB( bx r9 ) @ If this is a Thumb-2 kernel, THUMB( .thumb ) @ switch to Thumb now. THUMB(1: ) #ifdef CONFIG_ARM_VIRT_EXT bl __hyp_stub_install #endif @ ensure svc mode and all interrupts masked safe_svcmode_maskall r9 mrc p15, 0, r9, c0, c0 @ get processor id bl __lookup_processor_type @ r5=procinfo r9=cpuid movs r10, r5 @ invalid processor (r5=0)? THUMB( it eq ) @ force fixup-able long branch encoding beq __error_p @ yes, error 'p' #ifdef CONFIG_ARM_LPAE mrc p15, 0, r3, c0, c1, 4 @ read ID_MMFR0 and r3, r3, #0xf @ extract VMSA support cmp r3, #5 @ long-descriptor translation table format? THUMB( it lo ) @ force fixup-able long branch encoding blo __error_lpae @ only classic page table format #endif #ifndef CONFIG_XIP_KERNEL adr r3, 2f ldmia r3, {r4, r8} sub r4, r3, r4 @ (PHYS_OFFSET - PAGE_OFFSET) add r8, r8, r4 @ PHYS_OFFSET #else ldr r8, =PLAT_PHYS_OFFSET @ always constant in this case #endif bl __vet_atags #ifdef CONFIG_SMP_ON_UP bl __fixup_smp #endif #ifdef CONFIG_ARM_PATCH_PHYS_VIRT bl __fixup_pv_table #endif bl __create_page_tables ldr r13, =__mmap_switched @ address to jump to after @ mmu has been enabled adr lr, BSYM(1f) @ return (PIC) address mov r8, r4 @ set TTBR1 to swapper_pg_dir ldr r12, [r10, #PROCINFO_INITFUNC] add r12, r12, r10 ret r12 1: b __enable_mmu ENDPROC(stext)
__lookup_processor_type分析:
为了更好的理解__lookup_processor_type,在这之前需要先了解一下__lookup_processor_type_data。代码中可以看到__lookup_processor_type的第一条指令中就用到了__lookup_processor_type_data这个地址。__lookup_processor_type_data被定义在archarmkernelhead-common.S中。代码如下:
.align 2 .type __lookup_processor_type_data, %object __lookup_processor_type_data: .long . .long __proc_info_begin .long __proc_info_end .size __lookup_processor_type_data, . - __lookup_processor_type_data
这段代码可以认为是定义了一个__lookup_processor_type_data的结构,然后这个结构中的前三个元素被赋值成,当前的地址(用于虚拟地址和物理地址的转化)、__proc_info_begin的地址、__proc_info_end的地址。其中__proc_info_begin和__proc_info_end在archarmkernelvmlinux.lds链接文件中作为(.proc.info.init)段的开始和结束地址被定义。(.proc.info.init)段中存放了所有支持的cpu。对于ARM架构的CPU,表示支持的CPU的源码定义在在arch/arm/mm/目录下。
内存结构入下图:
archarmincludeasmprocinfo.h中proc_info_list结构定义
struct proc_info_list { unsigned int cpu_val; unsigned int cpu_mask; unsigned long __cpu_mm_mmu_flags; unsigned long __cpu_io_mmu_flags; unsigned long __cpu_flush; const char *arch_name; const char *elf_name; unsigned int elf_hwcap; const char *cpu_name; struct processor *proc; struct cpu_tlb_fns *tlb; struct cpu_user_fns *user; struct cpu_cache_fns *cache; };
如下代码是ARMv7架构CPU的proc_info_list结构,被定义在archarmmmproc-v7.S中
.section ".proc.info.init", #alloc .macro __v7_proc name, initfunc, mm_mmuflags = 0, io_mmuflags = 0, hwcaps = 0, proc_fns = v7_processor_functions ALT_SMP(.long PMD_TYPE_SECT | PMD_SECT_AP_WRITE | PMD_SECT_AP_READ | PMD_SECT_AF | PMD_FLAGS_SMP | mm_mmuflags) ALT_UP(.long PMD_TYPE_SECT | PMD_SECT_AP_WRITE | PMD_SECT_AP_READ | PMD_SECT_AF | PMD_FLAGS_UP | mm_mmuflags) .long PMD_TYPE_SECT | PMD_SECT_AP_WRITE | PMD_SECT_AP_READ | PMD_SECT_AF | io_mmuflags initfn initfunc, name .long cpu_arch_name .long cpu_elf_name .long HWCAP_SWP | HWCAP_HALF | HWCAP_THUMB | HWCAP_FAST_MULT | HWCAP_EDSP | HWCAP_TLS | hwcaps .long cpu_v7_name .long proc_fns .long v7wbi_tlb_fns .long v6_user_fns .long v7_cache_fns .endm
__lookup_processor_type函数
archarmkernelhead-common.S
__lookup_processor_type: adr r3, __lookup_processor_type_data ldmia r3, {r4 - r6} sub r3, r3, r4 @ get offset between virt&phys add r5, r5, r3 @ convert virt addresses to add r6, r6, r3 @ physical address space 1: ldmia r5, {r3, r4} @ value, mask and r4, r4, r9 @ mask wanted bits teq r3, r4 beq 2f add r5, r5, #PROC_INFO_SZ @ sizeof(proc_info_list) cmp r5, r6 blo 1b mov r5, #0 @ unknown processor 2: ret lr ENDPROC(__lookup_processor_type) .align 2 .type __lookup_processor_type_data, %object __lookup_processor_type_data: .long . .long __proc_info_begin .long __proc_info_end .size __lookup_processor_type_data, . - __lookup_processor_type_data
__enable_mmu函数分析
archarmkernelhead.S
__enable_mmu: #if defined(CONFIG_ALIGNMENT_TRAP) && __LINUX_ARM_ARCH__ < 6 orr r0, r0, #CR_A #else bic r0, r0, #CR_A #endif #ifdef CONFIG_CPU_DCACHE_DISABLE bic r0, r0, #CR_C #endif #ifdef CONFIG_CPU_BPREDICT_DISABLE bic r0, r0, #CR_Z #endif #ifdef CONFIG_CPU_ICACHE_DISABLE bic r0, r0, #CR_I #endif #ifndef CONFIG_ARM_LPAE mov r5, #(domain_val(DOMAIN_USER, DOMAIN_MANAGER) | domain_val(DOMAIN_KERNEL, DOMAIN_MANAGER) | domain_val(DOMAIN_TABLE, DOMAIN_MANAGER) | domain_val(DOMAIN_IO, DOMAIN_CLIENT)) mcr p15, 0, r5, c3, c0, 0 @ load domain access register mcr p15, 0, r4, c2, c0, 0 @ load page table pointer #endif b __turn_mmu_on ENDPROC(__enable_mmu) .align 5 .pushsection .idmap.text, "ax" ENTRY(__turn_mmu_on) mov r0, r0 instr_sync mcr p15, 0, r0, c1, c0, 0 @ write control reg mrc p15, 0, r3, c0, c0, 0 @ read id reg instr_sync mov r3, r3 mov r3, r13 ret r3 __turn_mmu_on_end: ENDPROC(__turn_mmu_on)
__mmap_switched分析
archarmkernelhead-common.S
__INIT __mmap_switched: adr r3, __mmap_switched_data ldmia r3!, {r4, r5, r6, r7} cmp r4, r5 @ Copy data segment if needed 1: cmpne r5, r6 ldrne fp, [r4], #4 strne fp, [r5], #4 bne 1b mov fp, #0 @ Clear BSS (and zero fp) 1: cmp r6, r7 strcc fp, [r6],#4 bcc 1b ARM( ldmia r3, {r4, r5, r6, r7, sp}) THUMB( ldmia r3, {r4, r5, r6, r7} ) THUMB( ldr sp, [r3, #16] ) str r9, [r4] @ Save processor ID str r1, [r5] @ Save machine type str r2, [r6] @ Save atags pointer cmp r7, #0 strne r0, [r7] @ Save control register values b start_kernel ENDPROC(__mmap_switched)
到这内核启动汇编阶段结束,下面将进入C代码阶段运行。
接下一篇:linux内核启动过程分析(三)
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)