u-boot一旦引导到内核,操作权限就会交给内核,那么所有的事情就和u-boot没有关系了,除了u-boot传递给内核的参数。
基地址+100 u-boot给内核的参数
基地址+4000 映射列表
基地址+8000 内核
内核的执行流程:
整个内核的入口:根据lds文件和编译到内核的源码文件共同决定。
arch/arm/kernel/head.s
safe_svcmode_maskall r9 确保处在SVC模式
mrc p15, 0, r9, c0, c0 读取cpu id cpu id在协处理器内部
bl __lookup_processor_type 判断内核是否支持本cpu
arch/arm/kernel/head-common.S
__lookup_processor_type:
adr r3, __lookup_processor_type_data 基于pc
154 ldmia r3, {r4 - r6}
155 sub r3, r3, r4 @ get offset between virt&phys 判断实际运行地址和规划地址的差值,为了找cpuid在内存上
156 add r5, r5, r3 @ convert virt addresses to
157 add r6, r6, r3 @ physical address space
1: ldmia r5, {r3, r4} @ value, mask
159 and r4, r4, r9 @ mask wanted bits
160 teq r3, r4
161 beq 2f
162 add r5, r5, #PROC_INFO_SZ (8) @ sizeof(proc_info_list)
163 cmp r5, r6
164 blo 1b
165 mov r5, #0 @ unknown processor
166 2: mov pc, lr
__lookup_processor_type_data
174 __lookup_processor_type_data:
175 .long . 利用一条语句,在不同地方(实际运行地址、连接规划地址)的不同,来计算实际运行地址和规划地址的差值
176 .long __proc_info_begin
177 .long __proc_info_end
178 .size __lookup_processor_type_data, . - __lookup_processor_type_data
bl __vet_atags 用来检测参数。
128 bl __create_page_tables 穿件映射列表,映射列表是(虚拟地址和物理地址之间的转换表)
ldr r13, =__mmap_switched 为跳到下一个阶段做准备
adr lr, BSYM(1f)
1: b __enable_mmu 打开mmu
b __turn_mmu_on
ENTRY(__turn_mmu_on)
464 mov r0, r0
465 instr_sync
466 mcr p15, 0, r0, c1, c0, 0 @ write control reg
467 mrc p15, 0, r3, c0, c0, 0 @ read id reg
468 instr_sync
469 mov r3, r3
470 mov r3, r13(=__mmap_switched )
471 mov pc, r3
pc ==__mmap_switched
472 __turn_mmu_on_end:
__mmap_switched :内核的第二阶段
mov fp, #0 @ Clear BSS (and zero fp)
91 1: cmp r6, r7
92 strcc fp, [r6],#4
93 bcc 1b
94
95 ARM( ldmia r3, {r4, r5, r6, r7, sp}) 设置堆栈
473 ENDPROC(__turn_mmu_on)
跳转到c语言:
stmneia r7, {r0, r4} @ Save control register values
104 b start_kernel
init/main.c
start_kernel()
setup_arch(&command_line); 会解析u-boot传递的参数。
mdesc = setup_machine_fdt(__atags_pointer) 解析设备树
mdesc = setup_machine_tags(__atags_pointer, __machine_arch_type); 检测本内核是或否支持板子
command_line :制定根文件系统
rest_init();
382 kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND);
if (execute_command) {判断bootargs里面有init=“?”,如果有则执行,如果没有则去下面的路径上运行
865 ret = run_init_process(execute_command);
866 if (!ret)
867 return 0;
868 pr_err("Failed to execute %s (error %d). Attempting defaults...\n",
869 execute_command, ret);
870 }
871 if (!try_to_run_init_process("/sbin/init") ||
872 !try_to_run_init_process("/etc/init") ||
873 !try_to_run_init_process("/bin/init") ||
874 !try_to_run_init_process("/bin/sh"))
init进程的调用流程:
init/main.c
start_kernel()-----》 rest_init();----》kernel_init();---》》》
if (execute_command) {判断bootargs里面有init=“?”,如果有则执行,如果没有则去下面的路径上运行
865 ret = run_init_process(execute_command);
866 if (!ret)
867 return 0;
868 pr_err("Failed to execute %s (error %d). Attempting defaults...\n",
869 execute_command, ret);
870 }
871 if (!try_to_run_init_process("/sbin/init") ||
872 !try_to_run_init_process("/etc/init") ||
873 !try_to_run_init_process("/bin/init") ||
874 !try_to_run_init_process("/bin/sh"))
printk内核打印函数:可以分等级
内核调试:内核的错误有两种
panic错误:内核运行到不能再运行的地方出错,是内核设计者提前规定的一些错误点。现阶段主要造成的原因是,参数错误
oops错误:内核使用非法指针,会将错误的pc报出,可以根据addr2line来查找错误点。
根文件系统:第一个被挂载的文件系统。
sys(文件系统) 用来显示所有的设备文件
proc(文件系统) 又来显示所有的内核信息
tmp 基于内存的文件夹,主要用来进行记录日志
dev 基于内存的文件夹,用来管理所有的设备节点