汇编阶段
执行文件
linux-4.19.y\arch\arm\kernel\head.S
调用同目录下的 head-common.S
明接下来的汇编代码将被汇编为 ARM 指令集,并且 stext 被标识为程序的入口点,根据当前的处理器模式来执行一系列操作,包括确保处理器模式为 BE8 大端字节序、切换到 ARM 模式或 Thumb-2 模式
.arm // 用于指示汇编器将接下来的代码汇编为 ARM 指令集的代码。
__HEAD // 标识符,通常用于表示代码的起始点或头部
ENTRY(stext) // 标识 stext 作为程序的入口点,通常,stext 是操作系统内核的入口点,它会被调用来启动整个系统。
ARM_BE8(setend be ) @ ensure we are in BE8 mode //使用 setend 指令来确保处理器处于 BE8(大端字节序)模式。
THUMB( badr r9, 1f ) @ Kernel is always entered in ARM. // 这是一个条件编译宏,用于在 Thumb 模式下执行。它将标签 1f 的地址加载到寄存器 r9 中,标签 1: 位于代码的最后一行
THUMB( bx r9 ) @ If this is a Thumb-2 kernel, // 在 Thumb 模式下,这行代码使用 bx 指令跳转到寄存器 r9 中保存的地址,这里可能是为了将执行流切换到 ARM 模式。
THUMB( .thumb ) @ switch to Thumb now. // 这行代码用于在 Thumb 模式下切换到 Thumb-2 指令集
THUMB(1: ) // 这是标签,用于在之前的条件编译中作为跳转目标。在这个例子中,它没有实际的指令,仅用于标记位置。
获取处理器ID,检查处理器类型是否有效
mrc p15, 0, r9, c0, c0 @ get processor id
bl __lookup_processor_type @ r5=procinfo r9=cpuid //调用__lookup_processor_type函数,其中r5用于传递处理器信息,r9用于传递处理器ID
movs r10, r5 @ invalid processor (r5=0)?
THUMB( it eq ) @ force fixup-able long branch encoding
beq __error_p @ yes, error 'p' //处理器ID无效的情况下触发错误处理。
设置物理地址偏移
#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 // 如果不是 xip内核,为一个固定值,目前还未找到对该值的设置
#endif
调用一系列函数,实现验证 ATAGS 数据的有效性和创建页表(只设置使内核运行所需的最少量)
bl __vet_atags // 验证 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 //创建页表 只设置使内核运行所需的最少量
完成使能mmu的操作,最终调用init/main.c文件中的start_kernel函数
ldr r13, =__mmap_switched @ address to jump to after
// 当mmu使能之后,跳转到__mmap_switched 函数
//复制数据段、清楚BSS段、设置栈指针、保存CPU ID
@ mmu has been enabled
badr lr, 1f @ return (PIC) address
#ifdef CONFIG_ARM_LPAE
mov r5, #0 @ high TTBR0
mov r8, r4, lsr #12 @ TTBR1 is swapper_pg_dir pfn
#else
mov r8, r4 @ set TTBR1 to swapper_pg_dir
#endif
ldr r12, [r10, #PROCINFO_INITFUNC]
add r12, r12, r10
ret r12
1: b __enable_mmu // 使能 mmu,并执行start_kernel
ENDPROC(stext)
.ltorg
#ifndef CONFIG_XIP_KERNEL
2: .long .
.long PAGE_OFFSET
#endif
start_kernel 为linux启动过程中的第一个C函数。
汇编流程总结:
处理uboot传入的参数(机器ID 、启动参数)
- 判断是否支持这个CPU(架构)
- 判断是否支持这个单板(机器ID)
- 建立页表(一级页表)
- 使能MMU
- 复制数据段、清楚BSS段、设置栈指针、保存CPU ID
- 跳转到start_kernel
C语言阶段
linux-4.19.y\init\main.c
C语言阶段内核启动流程
start_kernel
setup_arch // 解析 uboot传入的启动参数
setup_command_line // 解析 uboot传入的启动参数
parse_early_param
do_early_param
// 从__setup_start 到 __setup_end ,调用early函数
unknown_bootoption
obsolete_chechsetup
// 从__setup_start 到 __setup_end ,调用非early函数
rest_init
kernel_init
prepare_namespace
mount_root // 挂载根文件系统
init_post
// 执行应用
未完待续……