一. Linux启动流程
看完
Linux
内核的顶层
Makefile
以后再来看
Linux
内核的大致启动流程,
Linux
内核的启
动流程要比
uboot
复杂的多,涉及到的内容也更多。
本文中,我们就大致的了解一下
Linux
内
核的启动流程。
要分析
Linux
启动流程,同样需要先编译一下
Linux
源码,因为有很多文件是需要编译才会生成的。
二. 汇编流程简介
1. 链接脚本 vmlinux.lds
首先,分析
Linux
内核的链接脚本文件
arch/arm/kernel/vmlinux.lds
,通过链接脚本可以
找到
Linux
内核的第一行程序是从哪里执行的。
vmlinux.lds
中有如下代码:
492 OUTPUT_ARCH(arm)
493 ENTRY(stext)
494 jiffies = jiffies_64;
495 SECTIONS
496 {
497 /*
498 * XXX: The linker does not define how output sections are
499 * assigned to input sections when there are multiple statements
500 * matching the same input section name. There is no documented
501 * order of matching.
502 *
503 * unwind exit sections must be discarded before the rest of the
504 * unwind sections get included.
505 */
506 /DISCARD/ : {
507 *(.ARM.exidx.exit.text)
508 *(.ARM.extab.exit.text)
509
......
645 }
第
493
行的
ENTRY
指明了了
Linux
内核入口,入口为
stext
,
stext
定义在文件 arch/arm/kernel/head.S
中 , 因 此 要 分 析
Linux
内核的启动流程,就得先从文件
arch/arm/kernel/head.S
的
stext
处开始分析。
2. Linux 内核入口 stext
stext 是 Linux 内核的入口地址,在文件 arch/arm/kernel/head.S 中有如下所示提示内容:
/*
* Kernel startup entry point.
* ---------------------------
*
* This is normally called from the decompressor code. The requirements
* are: MMU = off, D-cache = off, I-cache = dont care, r0 = 0,
* r1 = machine nr, r2 = atags or dtb pointer.
.....
*/
根据上面
的注释,
Linux
内核启动之前要求如下:
①、关闭
MMU
。
②、关闭
D-cache
。
③、
I-Cache
无所谓。
④、
r0=0
。
⑤、
r1=machine nr(
也就是机器
ID)
。
⑥、
r2=atags
或者设备树
(dtb)
首地址。
Linux
内核的入口点
stext
其实相当于内核的入口函数,
stext
函数内容如下:
80 ENTRY(stext)
......
91 @ ensure svc mode and all interrupts masked
92 safe_svcmode_maskall r9
93
94 mrc p15, 0, r9, c0, c0 @ get processor id
95 bl __lookup_processor_type @ r5=procinfo r9=cpuid
96 movs r10, r5 @ invalid processor (r5=0)?
97 THUMB( it eq ) @ force fixup-able long branch encoding
98 beq __error_p @ yes, error 'p'
99
......
107
108 #ifndef CONFIG_XIP_KERNEL
......
113 #else
114 ldr r8, =PLAT_PHYS_OFFSET @ always constant in this case
115 #endif
116
117 /*
118 * r1 = machine no, r2 = atags or dtb,
119 * r8 = phys_offset, r9 = cpuid, r10 = procinfo
120 */
121 bl __vet_atags
......
128 bl __create_page_tables
129
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. On return, the CPU will be ready for the MMU to be
135 * turned on, and r0 will hold the CPU control register value.
原子哥在线教学:www.yuanzige.com 论坛:www.openedv.com
942
I.MX6U 嵌入式 Linux 驱动开发指南
136 */
137 ldr r13, =__mmap_switched @ address to jump to after
138 @ mmu has been enabled
139 adr lr, BSYM(1f) @ return (PIC) address
140 mov r8, r4 @ set TTBR1 to swapper_pg_dir
141 ldr r12, [r10, #PROCINFO_INITFUNC]
142 add r12, r12, r10
143 ret r12
144 1: b __enable_mmu
145 ENDPROC(stext)
第
92
行,调用函数
safe_svcmode_maskall
确保
CPU
处于
SVC
模式,并且关闭了所有的中断。
safe_svcmode_maskall
定义在文件
arch/arm/include/asm/assembler.h
中。
第
94
行,读处理器
ID
,
ID
值保存在
r9
寄存器中。
第
95
行,调用函数
__lookup_processor_type
检查当前系统是否支持此
CPU
,如果支持就获取
procinfo
信息。
procinfo
是
proc_info_list
类 型 的 结 构 体 ,
proc_info_list
在文件
arch/arm/include/asm/procinfo.h
中。
Linux 内核将每种处理器都抽象为一个 proc_info_list 结构体,每种处理器都对应一个 procinfo。因此,可以通过处理器 ID 来找到对应的 procinfo 结构,__lookup_processor_type 函数找 到对应处器的 procinfo 以后会将其保存到 r5 寄存器中。
第
128
行,调用函数
__create_page_tables
创建页表。
第
137
行,将函数
__mmap_switched
的地址保存到
r13
寄存器中。
__mmap_switched
定义在文件
arch/arm/kernel/head-common.S
,
__mmap_switched
最终会调用
start_kernel
函数。
第
144
行 , 调 用
__enable_mmu
函 数 使 能
MMU
,
__enable_mmu
定 义 在 文 件 arch/arm/kernel/head.S
中。
__enable_mmu
最终会通过调用
__turn_mmu_on
来打开
MMU
,
__turn_mmu_on
最后会执行
r13
里面保存的
__mmap_switched
函数。
3. __mmap_switched 函数
__mmap_switched
函数定义在文件
arch/arm/kernel/head-common.S
中,函数代码如下:
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, r7} )
98 THUMB( ldr sp, [r3, #16] )
99 str r9, [r4] @ Save processor ID
100 str r1, [r5] @ Save machine type
101 str r2, [r6] @ Save atags pointer
102 cmp r7, #0
103 strne r0, [r7] @ Save control register values
104 b start_kernel
105 ENDPROC(__mmap_switched)
第
104
行最终调用
start_kernel
来启动
Linux
内核,
start_kernel
函数定义在文件
init/main.c 中。