kernel 启动流程之 【head.S】 学习笔记
2017-03-29 14:48
519 查看
本文转载来源:http://blog.csdn.net/forever_2015/article/details/52885250
1、系统引导程序 【参考:kernel-3.18/Documentation/arm/Booting】
bootloader 要干的主要事情:
找到并初始化内存;
初始化和使能一个串口输出,这个对于调试很重要;
获取CPU类型,指的是具体体现结构CPU的类型,比如ARM Cortex-x系列;
初始化kernel tagged list,向kernel传递系统内存和根文件系统位置和其它信息;
加载 initramfs (基于ram的一种fs);
初始化设备树,跳转到kernel代码段入口stext;
进入kernel执行前的CPU的状态:
r0 = 0,
r1 = CPU 类型.
r2 = kernel参数list的物理地址.
irq & fiq 必须关闭.
MMU 必须关闭,这里内存地址都是物理地址.
D-cache 必须关闭,I-cache 没有要求.
2、Kernel 启动
入口程序是stext (参考 kernel-3.18/arch/arm/kernel/vmlinux.lds.S), 代码位于 kernel-3.18/arch/arm/kernel/head.S 文件中,如下代码: 【head.S 从系统引导程序拿到系统控制权,代码跟体系结构强相关,深入分析需要查阅数据手册学习ARM指令集和体系结构相关知识,这里只关注启动主线流程,不做深入分析】
/*
这里有个关键地方就是 ldr r13, =__mmap_switched 指令表示把__mmap_switched 地址取出来存到r13,这个下面有用,但是没有看到具体哪里跳转到C语言入口, 这里执行的最后一条指令是:
b __enable_mmu .
使能 mmu :
__turn_mmu_on
最后执行的是ret r3,而r3是由r13赋值而来,而r13的值又是从stext 函数里面取的__mmap_switched的地址,所以这里就是跳到到 __mmap_switched 代码:
__mmap_switched宏的文件在目录: kernel-3.18/arch/arm/kernel/head_common.S
所以从上面看就很明显了,r13存了__mmap_switched的地址,然后又赋值给r3, 通过ret指令带回顺利跳转到__mmap_switched 代码中,最后跳转到start_kernel进入 内核c语言执行环境. 这里需要注意一点,在lk启动阶段因为是物理地址,所以需要关闭mmu,而进入内核镜像执行时需要使能mmu和D-cache,因为内核镜像中的地址都是虚拟地址,所以需要使能mmu实现地址转换.
head.S 小结:
1、允许kernel切换到EL2(hypervisor) (EL0/EL1必须实现, EL2/EL3可选实现);
2、关闭 fiq / irq,切换到svc(管理)模式;
3、获取cpu id,并做合法性检查;
4、计算物理地址偏移,创建初始内存页表(PGD,PUD,PMD,PTE),建立物理地址跟虚拟地址映射 PA = f(VA)
5、使能mmu,内核中都是虚拟内存地址,需要mmu提供translation table walk支持;
6、跳转到内核C代码入口 start_kernel,执行内核通用初始化。
1、系统引导程序 【参考:kernel-3.18/Documentation/arm/Booting】
bootloader 要干的主要事情:
找到并初始化内存;
初始化和使能一个串口输出,这个对于调试很重要;
获取CPU类型,指的是具体体现结构CPU的类型,比如ARM Cortex-x系列;
初始化kernel tagged list,向kernel传递系统内存和根文件系统位置和其它信息;
加载 initramfs (基于ram的一种fs);
初始化设备树,跳转到kernel代码段入口stext;
进入kernel执行前的CPU的状态:
r0 = 0,
r1 = CPU 类型.
r2 = kernel参数list的物理地址.
irq & fiq 必须关闭.
MMU 必须关闭,这里内存地址都是物理地址.
D-cache 必须关闭,I-cache 没有要求.
2、Kernel 启动
入口程序是stext (参考 kernel-3.18/arch/arm/kernel/vmlinux.lds.S), 代码位于 kernel-3.18/arch/arm/kernel/head.S 文件中,如下代码: 【head.S 从系统引导程序拿到系统控制权,代码跟体系结构强相关,深入分析需要查阅数据手册学习ARM指令集和体系结构相关知识,这里只关注启动主线流程,不做深入分析】
/*
* 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. * * This code is mostly position independent, so if you link the kernel at * 0xc0008000, you call this at __pa(0xc0008000). * * See linux/arch/arm/tools/mach-types for the complete list of machine * numbers for r1. * * We're trying to keep crap to a minimum; DO NOT add any machine specific * crap here - that's what the boot loader (or in extreme, well justified * circumstances, zImage) is for. */ .arm __HEAD @ .section ".head.text","ax" ENTRY(stext) @ 只读内存布局代码段函数入口.定义一个全局声明 ARM_BE8(setend be ) THUMB( adr r9, BSYM(1f) ) THUMB( bx r9 ) THUMB( .thumb ) THUMB(1: ) bl __hyp_stub_install @ 允许kernel(EL1)切换到hypervisor mode(EL2) safe_svcmode_maskall r9 @ 关闭所有fiq,irq,切换到svc(管理)mode. mrc p15, 0, r9, c0, c0 @ 协处理器指令,获取cpu id,比如armv7 or armv8 bl __lookup_processor_type movs r10, r5 @ 判断cpu id是否有效,movs是会影响cpsr进位bit THUMB( it eq ) beq __error_p adr r3, 2f ldmia r3, {r4, r8} @ 计算物理地址偏移 sub r4, r3, r4 @ (PHYS_OFFSET - PAGE_OFFSET) add r8, r8, r4 @ PHYS_OFFSET bl __vet_atags bl __fixup_smp bl __create_page_tables @ 创建临时页表. ldr r13, =__mmap_switched @ 将 __mmap_switched 地址赋值给r13,关键! @ 使能mmu,因为内核中都是虚拟地址, @ 需要使能mmu实现虚拟地址到物理地址的转换。 adr lr, BSYM(1f) mov r8, r4 ARM( add pc, r10, #PROCINFO_INITFUNC ) THUMB( add r12, r10, #PROCINFO_INITFUNC ) THUMB( ret r12 ) 1: b __enable_mmu ENDPROC(stext)
这里有个关键地方就是 ldr r13, =__mmap_switched 指令表示把__mmap_switched 地址取出来存到r13,这个下面有用,但是没有看到具体哪里跳转到C语言入口, 这里执行的最后一条指令是:
b __enable_mmu .
__enable_mmu: ... b __turn_mmu_on @ 跳转到下一个函数 ENDPROC(__enable_mmu)
使能 mmu :
__turn_mmu_on
ENTRY(__turn_mmu_on) mov r0, r0 instr_sync mcr p15, 0, r0, c1, c0, 0 @ write control reg mrc p15, 0, r3, c0, c0, 0 @ read id reg instr_sync mov r3, r3 mov r3, r13 @ 这里把r13赋值给r3 ret r3 @ 返回r3,相当于 b r3 __turn_mmu_on_end: ENDPROC(__turn_mmu_on)
最后执行的是ret r3,而r3是由r13赋值而来,而r13的值又是从stext 函数里面取的__mmap_switched的地址,所以这里就是跳到到 __mmap_switched 代码:
__mmap_switched宏的文件在目录: kernel-3.18/arch/arm/kernel/head_common.S
__mmap_switched: adr r3, __mmap_switched_data ldmia r3!, {r4, r5, r6, r7} cmp r4, r5 @ Copy data segment if needed 1: cmpne r5, r6 ldrne fp, [r4], #4 strne fp, [r5], #4 bne 1b mov fp, #0 @ Clear BSS (and zero fp) 1: cmp r6, r7 strcc fp, [r6],#4 bcc 1b ARM( ldmia r3, {r4, r5, r6, r7, sp}) THUMB( ldmia r3, {r4, r5, r6, r7} ) THUMB( ldr sp, [r3, #16] ) str r9, [r4] @ Save processor ID str r1, [r5] @ Save machine type str r2, [r6] @ Save atags pointer cmp r7, #0 strne r0, [r7] @ Save control register values b start_kernel @ 这个就是我们要找到内核c入口, @ 但是这个怎么和上面串联起来呢? ENDPROC(__mmap_switched)
所以从上面看就很明显了,r13存了__mmap_switched的地址,然后又赋值给r3, 通过ret指令带回顺利跳转到__mmap_switched 代码中,最后跳转到start_kernel进入 内核c语言执行环境. 这里需要注意一点,在lk启动阶段因为是物理地址,所以需要关闭mmu,而进入内核镜像执行时需要使能mmu和D-cache,因为内核镜像中的地址都是虚拟地址,所以需要使能mmu实现地址转换.
head.S 小结:
1、允许kernel切换到EL2(hypervisor) (EL0/EL1必须实现, EL2/EL3可选实现);
2、关闭 fiq / irq,切换到svc(管理)模式;
3、获取cpu id,并做合法性检查;
4、计算物理地址偏移,创建初始内存页表(PGD,PUD,PMD,PTE),建立物理地址跟虚拟地址映射 PA = f(VA)
5、使能mmu,内核中都是虚拟内存地址,需要mmu提供translation table walk支持;
6、跳转到内核C代码入口 start_kernel,执行内核通用初始化。
相关文章推荐
- kernel 启动流程之 【head.S】 学习笔记
- kernel 启动流程之 【head.S】 学习笔记
- kernel 启动流程之 【设备驱动加载】 学习笔记
- kernel 启动流程之 【设备驱动加载】 学习笔记_好
- kernel 启动流程之 【设备驱动加载】 学习笔记
- 学习笔记(二)——单用户模式、救援模式、运行级别、启动流程、登录文件
- Android 中的WiFi学习笔记(转载)----WIFI启动 代码流程走读---网络连接流程
- 操作系统学习笔记--系统启动流程
- Linux系统启动流程学习笔记
- Linux学习笔记------Linux启动流程
- 学习笔记之centos系统启动流程
- rt_thread学习笔记(1)---rt_thread启动流程分析
- 天嵌科技TQ2440的uboot启动流程分析学习笔记
- nginx学习笔记三(nginx启动框架的处理流程)
- cocos2dx学习笔记-启动流程
- mtk刷机/启动流程学习笔记
- ARM linux kernel启动流程 head.S(一)
- Android 中的WiFi学习笔记(转载)----WIFI启动 代码流程走读---网络连接流程
- Linux 学习笔记 -- 第五部分 Linux 系统管理员 -- 第20章 启动流程、模块管理与 Loader
- Kernel启动流程源码解析 1 head.S