arm平台开机的时候,会先跑ic内部的rom code,rom code把flash上的uboot加载到dram中,然后执行uboot。不同的ic厂商uboot实现可能不一样,有些厂商会把uboot拆分为两个部分,第一部分会先加载到ic内部的sram运行起来,然后初始化dram,再把第二部分加载到dram中运行起来。这里我们不再深入探讨,我们只分析common的部分,uboot在跳转到kernel之前,会传递一些参数过来,并且对于arm构架的平台而言,跳转到kernel前,r0寄存器设置为0,r1的值为match type,r2的值为指向参数结构体的指针,所以kernel是通过r2寄存器拿到uboot传递过来的参数。
linux-4.10/arch/arm/kernel/head.S
/*
60 * Kernel startup entry point.
61 * ---------------------------
62 *
63 * This is normally called from the decompressor code. The requirements
64 * are: MMU = off, D-cache = off, I-cache = dont care, r0 = 0,
65 * r1 = machine nr, r2 = atags or dtb pointer.
66 *
67 * This code is mostly position independent, so if you link the kernel at
68 * 0xc0008000, you call this at __pa(0xc0008000).
69 *
70 * See linux/arch/arm/tools/mach-types for the complete list of machine
71 * numbers for r1.
72 *
73 * We're trying to keep crap to a minimum; DO NOT add any machine specific
74 * crap here - that's what the boot loader (or in extreme, well justified
75 * circumstances, zImage) is for.
76 */
77 .arm
78
79 __HEAD
80 ENTRY(stext)
// ...
117 /*
118 * r1 = machine no, r2 = atags or dtb,
119 * r8 = phys_offset, r9 = cpuid, r10 = procinfo
120 */
121 bl __vet_atags
//...
130 /*
131 * The following calls CPU specific code in a position independent
132 * manner. See arch/arm/mm/proc-*.S for details. r10 = base of
133 * xxx_proc_info structure selected by __lookup_processor_type
134 * above.
135 *
136 * The processor init function will be called with:
137 * r1 - machine type
138 * r2 - boot data (atags/dt) pointer
139 * r4 - translation table base (low word)
140 * r5 - translation table base (high word, if LPAE)
141 * r8 - translation table base 1 (pfn if LPAE)
142 * r9 - cpuid
143 * r13 - virtual address for __enable_mmu -> __turn_mmu_on
144 *
145 * On return, the CPU will be ready for the MMU to be turned on,
146 * r0 will hold the CPU control register value, r1, r2, r4, and
147 * r9 will be preserved. r5 will also be preserved if LPAE.
148 */
149 ldr r13, =__mmap_switched @ address to jump to after
150 @ mmu has been enabled
151 badr lr, 1f @ return (PIC) address
152 #ifdef CONFIG_ARM_LPAE
153 mov r5, #0 @ high TTBR0
154 mov r8, r4, lsr #12 @ TTBR1 is swapper_pg_dir pfn
155 #else
156 mov r8, r4 @ set TTBR1 to swapper_pg_dir
157 #endif
158 ldr r12, [r10, #PROCINFO_INITFUNC]
159 add r12, r12, r10
160 ret r12
161 1: b __enable_mmu
162 ENDPROC(stext)
bl __vet_atags 就是把uboot传递给kernel的参数拷贝到一个位置,ldr r13, =__mmap_switched 保存后面要执行的指令
/*
436 * Setup common bits before finally enabling the MMU. Essentially
437 * this is just loading the page table pointer and domain access
438 * registers. All these registers need to be preserved by the
439 * processor setup function (or set in the case of r0)
440 *
441 * r0 = cp#15 control register
442 * r1 = machine ID
443 * r2 = atags or dtb pointer
444 * r4 = TTBR pointer (low word)
445 * r5 = TTBR pointer (high word if LPAE)
446 * r9 = processor ID
447 * r13 = *virtual* address to jump to upon completion
448 */
449 __enable_mmu:
//...
471 b __turn_mmu_on
472 ENDPROC(__enable_mmu)
__enable_mmu执行完之后,又跳转到__turn_mmu_on,我们接下来看一下__turn_mmu_on。
490 ENTRY(__turn_mmu_on)
491 mov r0, r0
492 instr_sync
493 mcr p15, 0, r0, c1, c0, 0 @ write control reg
494 mrc p15, 0, r3, c0, c0, 0 @ read id reg
495 instr_sync
496 mov r3, r3
497 mov r3, r13
498 ret r3 //子程序返回时,执行这条指令,也就是跳到 __mmap_switched
499 __turn_mmu_on_end:
500 ENDPROC(__turn_mmu_on)
__turn_mmu_on最后执行r3中的指令,r3从r13来,而r13在上面的赋值是__mmap_switched。
linux-4.10/arch/arm/kernel/head-common.S
15 #define ATAG_CORE 0x54410001
16 #define ATAG_CORE_SIZE ((2*4 + 3*4) >> 2)
17 #define ATAG_CORE_SIZE_EMPTY ((2*4) >> 2)
47 __vet_atags:
48 tst r2, #0x3 @ aligned?
49 bne 1f
50
51 ldr r5, [r2, #0]
52 #ifdef CONFIG_OF_FLATTREE
53 ldr r6, =OF_DT_MAGIC @ is it a DTB?
54 cmp r5, r6
55 beq 2f
56 #endif
57 cmp r5, #ATAG_CORE_SIZE @ is first tag ATAG_CORE?
58 cmpne r5, #ATAG_CORE_SIZE_EMPTY
59 bne 1f
60 ldr r5, [r2, #4]
61 ldr r6, =ATAG_CORE
62 cmp r5, r6
63 bne 1f
64
65 2: ret lr @ atag/dtb pointer is ok
66
67 1: mov r2, #0
68 ret lr
69 ENDPROC(__vet_atags)
在分析__mmap_switched之前,我们先看一下__vet_atags把参数拷贝到了哪里,下面这两句汇编相当于把uboot传递过来的参数拷贝到了0x54410001这个地址开始的内存中。
ldr r5, [r2, #4]
ldr r6, =ATAG_CORE
下面我们接着看__mmap_switched 汇编代码做了什么事。
/*
72 * The following fragment of code is executed with the MMU on in MMU mode,
73 * and uses absolute addresses; this is not position independent.
74 *
75 * r0 = cp#15 control register
76 * r1 = machine ID
77 * r2 = atags/dtb pointer
78 * r9 = processor ID
79 */
80 __INIT
81 __mmap_switched:
82 adr r3, __mmap_switched_data
83
84 ldmia r3!, {r4, r5, r6, r7}
85 cmp r4, r5 @ Copy data segment if needed
86 1: cmpne r5, r6
87 ldrne fp, [r4], #4
88 strne fp, [r5], #4
89 bne 1b
90
91 mov fp, #0 @ Clear BSS (and zero fp)
92 1: cmp r6, r7
93 strcc fp, [r6],#4
94 bcc 1b
95
96 ARM( ldmia r3, {r4, r5, r6, r7, sp})
97 THUMB( ldmia r3, {r4, r5, r6,