300字范文,内容丰富有趣,生活中的好帮手!
300字范文 > Linux 内核启动过程--head.S(arch/xxx/kernel下的)

Linux 内核启动过程--head.S(arch/xxx/kernel下的)

时间:2023-01-18 05:48:42

相关推荐

Linux 内核启动过程--head.S(arch/xxx/kernel下的)

那么vmlinux的执行过程怎么分析呢?入口在哪?

还是先找链接文件。编译生成源码目录下的vmlinux的过程中链接的文件是arm/arm/kernel/vmlinux.lds文件,这个lds文件是有vmlinux.lds.S生成的。

分析下面的链接文件可知,vmlinux的入口函数定义在arch/arm/kernel/head.S中。

入口函数是stext。

41 OUTPUT_ARCH(arm)42 ENTRY(stext)43 44 #ifndef __ARMEB__45 jiffies = jiffies_64;46 #else47 jiffies = jiffies_64 + 4;48 #endif49 50 SECTIONS51 {52 /*53 * XXX: The linker does not define how output sections are54 * assigned to input sections when there are multiple statements55 * matching the same input section name. There is no documented56 * order of matching.57 *58 * unwind exit sections must be discarded before the rest of the59 * unwind sections get included.60 */61 /DISCARD/ : {62 *(.ARM.exidx.exit.text)63 *(.ARM.extab.exit.text)64 ARM_CPU_DISCARD(*(.ARM.exidx.cpuexit.text))65 ARM_CPU_DISCARD(*(.ARM.extab.cpuexit.text))66 ARM_EXIT_DISCARD(EXIT_TEXT)67 ARM_EXIT_DISCARD(EXIT_DATA)68 EXIT_CALL69 #ifndef CONFIG_HOTPLUG70 *(.ARM.exidx.devexit.text).....

首先看几个宏

#ifndef ENTRY#define ENTRY(name) \.globl name; \ALIGN; \name:#endif#endif /* LINKER_SCRIPT */#ifndef WEAK#define WEAK(name) \.weak name; \name:#endif#ifndef END#define END(name) \.size name, .-name#endif/* If symbol 'name' is treated as a subroutine (gets called, and returns)* then please use ENDPROC to mark 'name' as STT_FUNC for the benefit of* static analysis tools such as stack depth analyzer.*/#ifndef ENDPROC#define ENDPROC(name) \.type name, @function; \END(name)#endif

看看arch/arm/kernel下的head.S

.arm__HEADENTRY(stext)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: )setmode PSR_F_BIT | PSR_I_BIT | SVC_MODE, r9 @ ensure svc mode@ and irqs disabledmrc p15, 0, r9, c0, c0@ get processor idbl __lookup_processor_type@ r5=procinfo r9=cpuidmovs r10, r5 @ invalid processor (r5=0)?THUMB( it eq ) @ force fixup-able long branch encodingbeq __error_p @ yes, error 'p'ldr r8, =PHYS_OFFSET @ always constant in this case 0x8000 0000/** r1 = machine no, r2 = atags or dtb,* r8 = phys_offset, r9 = cpuid, r10 = procinfo*/bl __vet_atags //解析uBoot的tag,存放在bd->bi_boot_params中,其地址由Uboot传送.#ifdef CONFIG_SMP_ON_UPbl __fixup_smp //条件成立#endif#ifdef CONFIG_ARM_PATCH_PHYS_VIRTbl __fixup_pv_table#endifbl __create_page_tables/** The following calls CPU specific code in a position independent* manner. See arch/arm/mm/proc-*.S for details. r10 = base of* xxx_proc_info structure selected by __lookup_processor_type* above. On return, the CPU will be ready for the MMU to be* turned on, and r0 will hold the CPU control register value.*/ldr r13, =__mmap_switched @ address to jump to after@ mmu has been enabledadr lr, BSYM(1f) @ return (PIC) addressmov r8, r4 @ set TTBR1 to swapper_pg_dirARM( add pc, r10, #PROCINFO_INITFUNC )THUMB( add r12, r10, #PROCINFO_INITFUNC )THUMB( mov pc, r12 )1: b __enable_mmuENDPROC(stext)

这里面,首先

1.setmode PSR_F_BIT | PSR_I_BIT | SVC_MODE, r9 @ ensure svc mode

@ and irqs disabled

确保arm工作在SVC模式且IRQ、FIRQ都已经关闭了。

2、通过协处理器的操作得到processorID

“mrc p15, 0, r9, c0, c0”@ get processor id

然后通过__lookup_processor_type查看一下当前的内核是否支持这个ID。

__lookup_processor_type:adr r3, __lookup_processor_type_dataldmia r3, {r4 - r6}sub r3, r3, r4@ get offset between virt&physadd r5, r5, r3@ convert virt addresses toadd r6, r6, r3@ physical address space1: ldmia r5, {r3, r4} @ value, maskand r4, r4, r9@ mask wanted bitsteq r3, r4beq 2fadd r5, r5, #PROC_INFO_SZ @ sizeof(proc_info_list)cmp r5, r6blo 1bmov r5, #0 @ unknown processor2: mov pc, 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_dataasm-offsets.c:124: DEFINE(PROC_INFO_SZ, sizeof(struct proc_info_list));在kbuild.h中定义个这个宏#define DEFINE(sym, val) \asm volatile("\n->" #sym " %0 " #val : : "i" (val)) /*这个汇编没看懂,我的内核源码还没有经过编译,可能编译后会生成#define PROC_INFO_SZ sizeof(struct proc_info_list)) 之类的吧。 *///在proinfo.h中定义了结构体struct proc_info_list {unsigned int cpu_val;unsigned int cpu_mask;unsigned long __cpu_mm_mmu_flags; /* used by head.S */unsigned long __cpu_io_mmu_flags; /* used by head.S */unsigned long __cpu_flush; /* used by head.S */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;};//在vmlinux.lds文件中定义了__proc_info_begin和__proc_info_end这两地址之间定义了当前的内核能支持的proc_info。可以用grep '*proc.info.init*' -nR搜索一下,看看有哪些processor编译进了内核。VMLINUX_SYMBOL(__proc_info_begin) = .;\15 *(.proc.info.init)\16 VMLINUX_SYMBOL(__proc_info_end) = .;

在__lookup_processor_type中修正__proc_info_begin和__proc_info_end的位置,使能与当前的运行地址匹配。然后从__proc_info_begin开始遍历,直到匹配成功将procinfo的首地址保存到r5返回,如果到了__proc_info_end还没有匹配成功,则将r5赋值为0返回。关于 proc_info_init段的定义一般都在对应的汇编文件中。

这里一般不会出错,r8记录了PHYS_OFFSET,我这里是0x8000_0000

然后执行__vet_atags,解析uboot传进来的参数(r2寄存器指定的参数)

__vet_atags:tst r2, #0x3 @ aligned?bne 1fldr r5, [r2, #0] //uboot的bd->bi_boot_params的size,在uboot中已经被填充为5,存在DDRBASE+0x100即0x80000100处.#ifdef CONFIG_OF_FLATTREEldr r6, =OF_DT_MAGIC @ is it a DTB?cmp r5, r6beq 2f#endifcmp r5, #ATAG_CORE_SIZE @ is first tag ATAG_CORE? //20>>2 0x14>>2 =5cmpne r5, #ATAG_CORE_SIZE_EMPTYbne 1fldr r5, [r2, #4] //取tag /* struct tag_header { u32 size; u32 tag;};*/ldr r6, =ATAG_CORE //Uboot的setup_start_tag中 params->hdr.tag = ATAG_CORE;cmp r5, r6bne 1f2: mov pc, lr @ atag/dtb pointer is ok1: mov r2, #0mov pc, lrENDPROC(__vet_atags)

首先判断一下r2的内容是不是4字节对齐。我们这里是0x8000_0000+0x100

显然是4字节对齐的。

下面就是解析atags的大小内容了。检查size,检查是不是以ATAG_CORE开始的,如果是则成功返回。

我们这里定义了CONFIG_SMP_ON_UP,还会执行__fixup_smp

我们这里也是成立的

#ifdef CONFIG_SMP_ON_UP__INIT__fixup_smp:// r9 0x414fc091and r3, r9, #0x000f0000 @ architecture version 0x000f0000teq r3, #0x000f0000 @ CPU ID supported?bne __fixup_smp_on_up @ no, assume UPbic r3, r9, #0x00ff0000 //r3=0x4100c091bic r3, r3, #0x0000000f @ mask 0xff00fff0 r3=0x4100c090mov r4, #0x41000000orr r4, r4, #0x0000b000 //r4=0x4100b000orr r4, r4, #0x00000020 @ val 0x4100b020 //r4=0x4100b020teq r3, r4@ ARM 11MPCore?moveq pc, lr@ yes, assume SMPmrc p15, 0, r0, c0, c0, 5 @ read MPIDR //MPIDR is 0x80000000and r0, r0, #0xc0000000 @ multiprocessing extensions andteq r0, #0x80000000 @ not part of a uniprocessor system?moveq pc, lr@ yes, assume SMP/*3535的MPIDR是0x80000000 条件成立,成功返回*/

我们这里也定义了CONFIG_ARM_PATCH_PHYS_VIRT,会执行__fixup_pv_table

__fixup_pv_table:adr r0, 1fldmia r0, {r3-r5, r7}sub r3, r0, r3 @ PHYS_OFFSET - PAGE_OFFSETadd r4, r4, r3 @ adjust table start addressadd r5, r5, r3 @ adjust table end addressadd r7, r7, r3 @ adjust __pv_phys_offset addressstr r8, [r7] @ save computed PHYS_OFFSET to __pv_phys_offsetmov r6, r3, lsr #24 @ constant for add/sub instructionsteq r3, r6, lsl #24 @ must be 16MiB alignedTHUMB( it ne@ cross section branch )bne __errorstr r6, [r7, #4] @ save to __pv_offsetb __fixup_a_pv_tableENDPROC(__fixup_pv_table).align1: //r0.long . //r3.long __pv_table_begin //r4 .long __pv_table_end//r52: .long __pv_phys_offset //r7.text__fixup_a_pv_table:

主要是修正__fixup_pv_table,其内容在链接脚本中定义.

__pv_table_begin = .;

167 *(.pv_table)

168 __pv_table_end = .;

然后执行__create_page_tables,设置TLB。

这个过程有点复杂,设计到页表基地址的计算,页表项内容,还有MMU设置。

__create_page_tables:pgtbl r4, r8 @ page table address/*.macro pgtbl, rd, phys55 add \rd, \phys, #TEXT_OFFSET - PG_DIR_SIZE56 .endm47 #define PG_DIR_SIZE 0x400048 #define PMD_ORDER 2在arch/arm/boot/compressed#中有-DTEXT_OFFSET=0x00008000pgtbl r4, r8:add r4,0x8000_0000 0x4000r4=0x8000_0x4000,0x4000 2的14次方是 16K*//** Clear the swapper page table .将 在0x80008000之下的16K的内存清空.*/mov r0, r4 //0x8000_0x4000mov r3, #0add r6, r0, #PG_DIR_SIZE //加16k r6=0x8000_0x8000,1: str r3, [r0], #4str r3, [r0], #4str r3, [r0], #4str r3, [r0], #4teq r0, r6bne 1bldr r7, [r10, #PROCINFO_MM_MMUFLAGS] @ mm_mmuflagsadr r0, __turn_mmu_on_locldmia r0, {r3, r5, r6}sub r0, r0, r3@ virt->phys offsetadd r5, r5, r0@ phys __turn_mmu_onadd r6, r6, r0@ phys __turn_mmu_on_endmov r5, r5, lsr #SECTION_SHIFT//物理地址右移了20位,可以看出段地址下标mov r6, r6, lsr #SECTION_SHIFT //物理地址右移了20位cmp r5, r6addlo r5, r5, #1 @ next sectionblo 1b1: orr r3, r7, r5, lsl #SECTION_SHIFT @ flags + kernel basestr r3, [r4, r5, lsl #PMD_ORDER] @ identity mapping //PMD_ORDER =2 __turn_mmu_on_loc:.long ..long __turn_mmu_on.long __turn_mmu_on_end/* c0008108 <__turn_mmu_on_loc>:c0008108: c0008108 .word 0xc0008108c000810c: c0408440 .word 0xc0408440c0008110: c0408460 .word 0xc0408460* 可以看出__turn_mmu_on到__turn_mmu_end之间只有0x20字节,地址间距1M范围内。/

上面的代码应该是将__turn_mmu_on_loc的运行地址进行映射,映射到上面地方呢?orr r3, r7, r5, lsl #SECTION_SHIFT

str r3, [r4, r5, lsl #PMD_ORDER]

其中r7位MMU_FLAG,r5为__turn_mmu_on的运行时地址(物理地址)

不知道MMU是怎么设置的,猜一下,应该是将__turn_mmu_on的物理硬质映射到了__turn_mmu_on的物理地址,即平映射。为什么平映射,现在还不清楚。

而这个页表的首地址就是r4即,大概是0x8000_4000//因为不明白MMU是怎么设置的,因此先做个假设,_str r3, [r4, r5, lsl #PMD_ORDER]中的[r4]就是页表地址,r5就是虚拟地址的段地址,即虚拟地址的高12位,r3就是物理地址。暂时这么认为。这不影响我们了解页表初始化。

/** Now setup the pagetables for our kernel direct* mapped region.* 看注释。映射kernel了*/mov r3, pcmov r3, r3, lsr #SECTION_SHIFT orr r3, r7, r3, lsl #SECTION_SHIFT //r7 MMUFLAGadd r0, r4, #(KERNEL_START & 0xff000000) >> (SECTION_SHIFT - PMD_ORDER) //KERNEL_START 0xc0008000//r0=r4+0xc000 0000>>18=0xc000>>2+0x8000_0x4000=0x8000_7000 str r3, [r0, #((KERNEL_START & 0x00f00000) >> SECTION_SHIFT) << PMD_ORDER]!//此处,r3是当前的运行地址,可以看做是物理地址。那么这句话就应该是将物理地址r3进行映射了。映射到哪了呢?应该是KERNEL_START 之后的地址了。因为不知道页表项内容是怎么计算的,所以先分析到这。ldr r6, =(KERNEL_END - 1)//#define KERNEL_END _endadd r0, r0, #1 << PMD_ORDER //r0=r0+4add r6, r4, r6, lsr #(SECTION_SHIFT - PMD_ORDER)1: cmp r0, r6add r3, r3, #1 << SECTION_SHIFT// //将该1M空间的物理起始地址存储到页表中相应虚拟地址页中 strls r3, [r0], #1 << PMD_ORDER //0x8007030开始处,存放[KERNEL_START,END]的虚拟地址,1M为单位bls 1b

上面的代码作用就是初始化页表,页表中的数据内容指示了当前运行地址映射到的虚拟地址,这个虚拟地址应该是跟我们编译时的链接地址是一致的。

继续向下看

/** Then map boot params address in r2 or the first 1MB (2MB with LPAE)* of ram if boot params address is not specified.*/mov r0, r2, lsr #SECTION_SHIFTmovs r0, r0, lsl #SECTION_SHIFTmoveq r0, r8sub r3, r0, r8add r3, r3, #PAGE_OFFSET //+ 0xc000 0100add r3, r4, r3, lsr #(SECTION_SHIFT - PMD_ORDER)orr r6, r7, r0str r6, [r3]mov pc, lr

这里终于看到了个mov pc,lr表示函数要返回了,creat_page_table终于结束了。

从注释上看应该映射boot params,即uboot传进来的参数了。但是到现在为止只是初始化了这种页表,MMU依然没有打开。

在向下看

/** The following calls CPU specific code in a position independent* manner. See arch/arm/mm/proc-*.S for details. r10 = base of* xxx_proc_info structure selected by __lookup_processor_type* above. On return, the CPU will be ready for the MMU to be* turned on, and r0 will hold the CPU control register value.*/ldr r13, =__mmap_switched @ address to jump to after@ mmu has been enabledadr lr, BSYM(1f) @ return (PIC) addressmov r8, r4 @ set TTBR1 to swapper_pg_dirARM( add pc, r10, #PROCINFO_INITFUNC )THUMB( add r12, r10, #PROCINFO_INITFUNC )THUMB( mov pc, r12 )1: b __enable_mmuENDPROC(stext)

看到这里就要分析add pc, r10, #PROCINFO_INITFUNC

PROCINFO_INITFUNC 这个宏在

Asm-offsets.c (z:\code\hi3535kernel\linux-3.4.y\arch\arm\kernel): DEFINE(PROCINFO_INITFUNC, offsetof(struct proc_info_list, __cpu_flush));

这个值可能是16

r10又是什么呢?前面分析过,r10保存了proc_info_list的首地址。

用proc_info_list的首地址+offsetof(struct proc_info_list, __cpu_flush))就是我们现在的proc_info_list中的__cpu_flush。

看到这里你会想到 unsigned long __cpu_flush; /* used by head.S */

pc=myproc_info.__cpu_flush

看来必须找到myproc_info是在哪定义的了。看看他的__cpu_flush代表的是什么。

在arch/arm下搜索

grep -nr ‘proc.info.init’ *

出来了很多

root@ubuntu:/home/work/code/hi3535kernel/linux-3.4.y/arch/arm# grep -nr 'proc.info.init' *Binary file kernel/.vmlinux.lds.S.swp matcheskernel/vmlinux.lds.S:15: *(.proc.info.init) \mm/proc-sa1100.S:247: .section ".proc.info.init", #alloc, #execinstrmm/proc-arm1022.S:449: .section ".proc.info.init", #alloc, #execinstrmm/proc-v6.S:262: .section ".proc.info.init", #alloc, #execinstrmm/proc-arm1020e.S:466: .section ".proc.info.init", #alloc, #execinstrmm/proc-arm926.S:475: .section ".proc.info.init", #alloc, #execinstrmm/proc-arm720.S:191: .section ".proc.info.init", #alloc, #execinstrmm/proc-arm922.S:427: .section ".proc.info.init", #alloc, #execinstrmm/proc-xsc3.S:501: .section ".proc.info.init", #alloc, #execinstrmm/proc-mohawk.S:393: .section ".proc.info.init", #alloc, #execinstrmm/proc-arm925.S:495: .section ".proc.info.init", #alloc, #execinstrmm/proc-arm1026.S:444: .section ".proc.info.init", #alloc, #execinstrmm/proc-xscale.S:613: .section ".proc.info.init", #alloc, #execinstrmm/proc-arm920.S:449: .section ".proc.info.init", #alloc, #execinstrmm/proc-arm7tdmi.S:81:.section ".proc.info.init", #alloc, #execinstrmm/proc-sa110.S:204: .section ".proc.info.init", #alloc, #execinstrmm/proc-arm946.S:410: .section ".proc.info.init", #alloc, #execinstrmm/proc-v7.S:301: .section ".proc.info.init", #alloc, #execinstrmm/proc-arm740.S:134: .section ".proc.info.init", #alloc, #execinstrmm/proc-feroceon.S:558: .section ".proc.info.init", #alloc, #execinstrmm/proc-arm9tdmi.S:75:.section ".proc.info.init", #alloc, #execinstrmm/proc-fa526.S:196: .section ".proc.info.init", #alloc, #execinstrmm/proc-arm6_7.S:289: .section ".proc.info.init", #alloc, #execinstrmm/proc-arm940.S:356: .section ".proc.info.init", #alloc, #execinstrmm/proc-arm1020.S:508: .section ".proc.info.init", #alloc, #execinstrroot@ubuntu:/home/work/code/hi3535kernel/linux-3.4.y/arch/arm# grep -nr 'proc.info.init' *

在这里,我们用的是armv7,分析一下mm/proc-v7.S

.section ".proc.info.init", #alloc, #execinstr/** Standard v7 proc info content*/.macro __v7_proc initfunc, mm_mmuflags = 0, io_mmuflags = 0, hwcaps = 0ALT_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_mmuflagsW(b) \initfunc.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 v7_processor_functions.long v7wbi_tlb_fns.long v6_user_fns.long v7_cache_fns.endm#ifndef CONFIG_ARM_LPAE/** ARM Ltd. Cortex A5 processor.*/.type __v7_ca5mp_proc_info, #object__v7_ca5mp_proc_info:.long 0x410fc050.long 0xff0ffff0__v7_proc __v7_ca5mp_setup.size __v7_ca5mp_proc_info, . - __v7_ca5mp_proc_info/** ARM Ltd. Cortex A9 processor.*/.type __v7_ca9mp_proc_info, #object__v7_ca9mp_proc_info:.long 0x410fc090.long 0xff0ffff0__v7_proc __v7_ca9mp_setup.size __v7_ca9mp_proc_info, . - __v7_ca9mp_proc_info#endif /* CONFIG_ARM_LPAE *//** ARM Ltd. Cortex A7 processor.*/.type __v7_ca7mp_proc_info, #object__v7_ca7mp_proc_info:.long 0x410fc070.long 0xff0ffff0__v7_proc __v7_ca7mp_setup, hwcaps = HWCAP_IDIV.size __v7_ca7mp_proc_info, . - __v7_ca7mp_proc_info/** ARM Ltd. Cortex A15 processor.*/.type __v7_ca15mp_proc_info, #object__v7_ca15mp_proc_info:.long 0x410fc0f0.long 0xff0ffff0__v7_proc __v7_ca15mp_setup, hwcaps = HWCAP_IDIV.size __v7_ca15mp_proc_info, . - __v7_ca15mp_proc_info/** Match any ARMv7 processor core.*/.type __v7_proc_info, #object__v7_proc_info:.long 0x000f0000@ Required ID value.long 0x000f0000@ Mask for ID__v7_proc __v7_setup.size __v7_proc_info, . - __v7_proc_info

看最后一个__v7_proc_info,将其展开后,你会发现

.section ".proc.info.init", #alloc, #execinstr__v7_proc_info:.long 0x000f0000 .long 0x000f0000.long \mm_mmuflags.long \__cpu_io_mmu_flags; b __v7_setup //这就是我们要查的函数.long \cpu_arch_name.long \cpu_elf_name.long \hwcaps.long cpu_v7_name.long v7_processor_functions.long v7wbi_tlb_fns.long v6_user_fns.long v7_cache_fns

因为我们用的是就是__v7_proc_info,因此可以看看__v7_setup 这个函数干的什么事儿。

__v7_setup:adr r12, __v7_setup_stack @ the local stackstmia r12, {r0-r5, r7, r9, r11, lr}bl v7_flush_dcache_allldmia r12, {r0-r5, r7, r9, r11, lr}mrc p15, 0, r0, c0, c0, 0 @ read main ID registerand r10, r0, #0xff000000 @ ARM?teq r10, #0x41000000bne 3fand r5, r0, #0x00f00000 @ variantand r6, r0, #0x0000000f @ revisionorr r6, r6, r5, lsr #20-4 @ combine variant and revisionubfx r0, r0, #4, #12 @ primary part number/* Cortex-A8 Errata */ldr r10, =0x00000c08 @ Cortex-A8 primary part numberteq r0, r10bne 2f#ifdef CONFIG_ARM_ERRATA_430973teq r5, #0x00100000 @ only present in r1p*mrceq p15, 0, r10, c1, c0, 1@ read aux control registerorreq r10, r10, #(1 << 6) @ set IBE to 1mcreq p15, 0, r10, c1, c0, 1@ write aux control register#endif#ifdef CONFIG_ARM_ERRATA_458693teq r6, #0x20 @ only present in r2p0mrceq p15, 0, r10, c1, c0, 1@ read aux control registerorreq r10, r10, #(1 << 5) @ set L1NEON to 1orreq r10, r10, #(1 << 9) @ set PLDNOP to 1mcreq p15, 0, r10, c1, c0, 1@ write aux control register#endif#ifdef CONFIG_ARM_ERRATA_460075teq r6, #0x20 @ only present in r2p0mrceq p15, 1, r10, c9, c0, 2@ read L2 cache aux ctrl registertsteq r10, #1 << 22orreq r10, r10, #(1 << 22) @ set the Write Allocate disable bitmcreq p15, 1, r10, c9, c0, 2@ write the L2 cache aux ctrl register#endifb 3f/* Cortex-A9 Errata */2: ldr r10, =0x00000c09 @ Cortex-A9 primary part numberteq r0, r10bne 3f#ifdef CONFIG_ARM_ERRATA_742230cmp r6, #0x22 @ only present up to r2p2mrcle p15, 0, r10, c15, c0, 1@ read diagnostic registerorrle r10, r10, #1 << 4 @ set bit #4mcrle p15, 0, r10, c15, c0, 1@ write diagnostic register#endif#ifdef CONFIG_ARM_ERRATA_742231teq r6, #0x20 @ present in r2p0teqne r6, #0x21 @ present in r2p1teqne r6, #0x22 @ present in r2p2mrceq p15, 0, r10, c15, c0, 1@ read diagnostic registerorreq r10, r10, #1 << 12 @ set bit #12orreq r10, r10, #1 << 22 @ set bit #22mcreq p15, 0, r10, c15, c0, 1@ write diagnostic register#endif#ifdef CONFIG_ARM_ERRATA_743622teq r5, #0x00200000 @ only present in r2p*mrceq p15, 0, r10, c15, c0, 1@ read diagnostic registerorreq r10, r10, #1 << 6 @ set bit #6mcreq p15, 0, r10, c15, c0, 1@ write diagnostic register#endif#if defined(CONFIG_ARM_ERRATA_751472) && defined(CONFIG_SMP)ALT_SMP(cmp r6, #0x30) @ present prior to r3p0ALT_UP_B(1f)mrclt p15, 0, r10, c15, c0, 1@ read diagnostic registerorrlt r10, r10, #1 << 11 @ set bit #11mcrlt p15, 0, r10, c15, c0, 1@ write diagnostic register1:#endif3: mov r10, #0mcr p15, 0, r10, c7, c5, 0@ I+BTB cache invalidatedsb#ifdef CONFIG_MMUmcr p15, 0, r10, c8, c7, 0@ invalidate I + D TLBsv7_ttb_setup r10, r4, r8, r5 @ TTBCR, TTBRx setupldr r5, =PRRR @ PRRRldr r6, =NMRR @ NMRRmcr p15, 0, r5, c10, c2, 0@ write PRRRmcr p15, 0, r6, c10, c2, 1@ write NMRR#endif#ifndef CONFIG_ARM_THUMBEEmrc p15, 0, r0, c0, c1, 0 @ read ID_PFR0 for ThumbEEand r0, r0, #(0xf << 12) @ ThumbEE enabled fieldteq r0, #(1 << 12) @ check if ThumbEE is presentbne 1fmov r5, #0mcr p14, 6, r5, c1, c0, 0 @ Initialize TEEHBR to 0mrc p14, 6, r0, c0, c0, 0 @ load TEECRorr r0, r0, #1 @ set the 1st bit in order tomcr p14, 6, r0, c0, c0, 0 @ stop userspace TEEHBR access1:#endifadr r5, v7_crvalldmia r5, {r5, r6}#ifdef CONFIG_CPU_ENDIAN_BE8orr r6, r6, #1 << 25 @ big-endian page tables#endif#ifdef CONFIG_SWP_EMULATEorrr5, r5, #(1 << 10) @ set SW bit in "clear"bicr6, r6, #(1 << 10) @ clear it in "mmuset"#endifmrc p15, 0, r0, c1, c0, 0 @ read control registerbic r0, r0, r5@ clear bits themorr r0, r0, r6@ set themTHUMB( orr r0, r0, #1 << 30 ) @ Thumb exceptionsmov pc, lr @ return to head.S:__retENDPROC(__v7_setup)

这个函数很长啊,但是有很多编译条件是我们不需要的。

看英文注释大概知道,他里面还区分A8 A9,我们用的是A9

看最后一个mov pc, lr

这就是在刚才的 ARM( add pc, r10, #PROCINFO_INITFUNC )

后返回了下一条指令。

下一条指令就是b __enable_mmu

ARM( add pc, r10, #PROCINFO_INITFUNC )THUMB( add r12, r10, #PROCINFO_INITFUNC )THUMB( mov pc, r12 )1: b __enable_mmu__enable_mmu:#if defined(CONFIG_ALIGNMENT_TRAP) && __LINUX_ARM_ARCH__ < 6orr r0, r0, #CR_A#elsebic r0, r0, #CR_A#endif#ifdef CONFIG_CPU_DCACHE_DISABLEbic r0, r0, #CR_C#endif#ifdef CONFIG_CPU_BPREDICT_DISABLEbic r0, r0, #CR_Z#endif#ifdef CONFIG_CPU_ICACHE_DISABLEbic r0, r0, #CR_I#endif#ifdef CONFIG_ARM_LPAEmov r5, #0mcrr p15, 0, r4, r5, c2@ load TTBR0#elsemov 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 registermcr p15, 0, r4, c2, c0, 0 @ load page table pointer#endifb __turn_mmu_onENDPROC(__enable_mmu).align 5.pushsection .idmap.text, "ax"ENTRY(__turn_mmu_on)mov r0, r0instr_syncmcr p15, 0, r0, c1, c0, 0 @ write control regmrc p15, 0, r3, c0, c0, 0 @ read id reginstr_syncmov r3, r3mov r3, r13mov pc, r3__turn_mmu_on_end:ENDPROC(__turn_mmu_on)

__enable_mmu的功能比较简单,就是加载 页表,并执行,在__turn_mmu_on中,最后用mov pc,r3再次跳转。

由`bl __create_page_tables

/** The following calls CPU specific code in a position independent* manner. See arch/arm/mm/proc-*.S for details. r10 = base of* xxx_proc_info structure selected by __lookup_processor_type* above. On return, the CPU will be ready for the MMU to be* turned on, and r0 will hold the CPU control register value.*/ldr r13, =__mmap_switched @ address to jump to after`

可以知道,开启MMU后又跳转到了__mmap_switched

__mmap_switched:adr r3, __mmap_switched_dataldmia r3!, {r4, r5, r6, r7}cmp r4, r5 @ Copy data segment if needed1: cmpne r5, r6ldrne fp, [r4], #4strne fp, [r5], #4bne 1bmov fp, #0 @ Clear BSS (and zero fp)1: cmp r6, r7strcc fp, [r6],#4bcc 1bARM( ldmia r3, {r4, r5, r6, r7, sp})THUMB( ldmia r3, {r4, r5, r6, r7} )THUMB( ldr sp, [r3, #16] )str r9, [r4] @ Save processor IDstr r1, [r5] @ Save machine typestr r2, [r6] @ Save atags pointerbic r4, r0, #CR_A @ Clear 'A' bitstmia r7, {r0, r4} @ Save control register valuesb start_kernelENDPROC(__mmap_switched)

原来从这个函数跳到了start_kernel。终于看到希望了,最后有个b start_kernel,C函数。

ENTRY(stext)setmode PSR_F_BIT | PSR_I_BIT | SVC_MODE, r9 @ ensure svc modemrc p15, 0, r9, c0, c0@ get processor idbl __lookup_processor_type@ r5=procinfo r9=cpuidldr r8, =PHYS_OFFSET/** r1 = machine no, r2 = atags or dtb,* r8 = phys_offset, r9 = cpuid, r10 = procinfo*/bl __vet_atags //解析uBoot的tag,存放在bd->bi_boot_params中,其地址由Uboot传送.#ifdef CONFIG_SMP_ON_UPbl __fixup_smp //条件成立#endif#ifdef CONFIG_ARM_PATCH_PHYS_VIRTbl __fixup_pv_table#endifbl __create_page_tablesldr r13, =__mmap_switchedadr lr, BSYM(1f) @ return (PIC) addressmov r8, r4 @ set TTBR1 to swapper_pg_dirARM( add pc, r10, #PROCINFO_INITFUNC )1: b __enable_mmuENDPROC(stext)

下面再回顾一下:

1.设置arm工作模式在SVC并关闭FIRQ IRQ

2.探测processor 类型

3.解析uboot传进来的atags

4.__fixup_smp __fixup_pv_table

5.__create_page_tables

6.执行由proc_info_list 的__cpu_flush;指定的函数。如__v7_setup

7.开启MMU

8.__mmap_switched

9.b start_kernel

以上只是我个人对arch/arm/kernel/head.S的理解。

有很多MMU的操作还不明白。不知道页表地址、页表项数据是怎么算出来的。上文理解是我的主观看法,如读者发现错误之处,请留言讨论一下!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。