Linux 内核启动
来源:互联网 发布:零点网络 编辑:程序博客网 时间:2024/05/16 17:48
转自:http://celinux.wikidot.com/linuxboot
Linux 内核启动
Fold
Table of Contents
Bootloader 如何加载内核?
ARM Linux 内核启动条件
Machine ID 和 内核参数如何传递
Linux 内核启动
Linux 内核入口
压缩内核的解压缩过程
start_kernel() 之前
start_kernel() 启动过程
Bootloader 如何加载内核?
ARM Linux 内核启动条件
- 从 Bootloader 跳转到内核开始执行时,需满足一下条件:
- MMU: off
- D-Cache: off
- I-Cache: don’t care
- r0 = 0
- r1 = Machine ID
- r2 = Parameter tag address
// 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 pointer. *...
- ARM11系统中, 如果要使用 D-Cache, 必须打开 MMU. 所以现在很多 bootloader 也是打开MMU,使用虚拟地址工作的。 这时候需要:
- 建立一个简单的页表,至少保证内核加载地址空间和bootloader本省使用的空间可访问
- 为了使用方便,可以将虚拟地址映射到和物理地址一样的地址空间;或者使用和内核空间中一样的虚拟地址
Machine ID 和 内核参数如何传递
- 在 U-Boot 中, Machine ID 和内核参数地址是由 bootloader 指定的。 例如,XScale系统中有如下代码:
// board/lubbock/lubbock.c int board_init (void){ /* arch number of Lubbock-Board */ gd->bd->bi_arch_number = MACH_TYPE_LUBBOCK; /* adress of boot parameters */ gd->bd->bi_boot_params = 0xa0000100; return 0;}
- 启动内核时将 bi_arch_number 和 bi_boot_params 传给内核:
// lib_arm/bootm.c do_bootm_linux () { machid = bd->bi_arch_number; ... theKernel = (void (*)(int, int, uint))images->ep; ... theKernel (0, machid, bd->bi_boot_params); }
- u-boot 中, 内核的起始地址从uImage的文件头中得到。
Linux 内核启动
Linux 内核入口
Linux内核的入口在 arch/arm/kernel/head.S 中定义:
.section ".text.head", "ax"ENTRY(stext) msr cpsr_c, #PSR_F_BIT | PSR_I_BIT | SVC_MODE @ ensure svc mode @ and irqs disabled mrc p15, 0, r9, c0, c0 @ get processor id bl __lookup_processor_type @ r5=procinfo r9=cpuid movs r10, r5 @ invalid processor (r5=0)? beq __error_p @ yes, error 'p' bl __lookup_machine_type @ r5=machinfo ...
- 该段代码在链接的时候会被链接到 0xC0008000 地址。该链接地址是在 arch/arm/kernel/vmlinux.S 中指定的:
OUTPUT_ARCH(arm)ENTRY(stext)jiffies = jiffies_64;SECTIONS{ . = 0xC0000000 + 0x00008000; <------ 内核起始地址,为虚拟地址 .text.head : { <------ text.head 段 _stext = .; _sinittext = .; *(.text.head) } .init : { /* Init code and data */....
压缩内核的解压缩过程
start_kernel() 之前
在执行 C 函数 start_kernel() 之前, 内核首先需要执行一段汇编代码,完成相关的检查及初始化工作:
- 检查CPU ID
- 检查内核参数
- 建立初始内核页表,该页表会被后面执行的paging_init() 覆盖掉
- 打开 MMU,数据缓存以及命令缓存
.section ".text.head", "ax"ENTRY(stext) __lookup_processor_type __lookup_machine_type __vet_atags --> check tag header __create_page_tables ldr r13, __switch_data --> save tag address to __atags_pointer adr lr, __enable_mmu add pc, r10, #PROCINFO_INITFUNC ...
- __switch_data 的地址保存在 r13, __enable_mmu 的地址保存在 lr 寄存器
- 注意: adr 是一个虚拟指令(pseudo-instruction) , 详细说明参考: ADR ARM pseudo-instruction or ARM ADR
- 最后一条指令: add pc, r10, #PROCINFO_INITFUNC
- 此时,r10 保存了 xxx_proc_info 的起始地址, 由 __lookup_machine_type 函数根据CPU种类设置好的。
- 例如,i.MX31 (ARMv6体系结构), 则 r10 保存了 __arm6_proc_info 的起始地址:
--- arch/arm/mm/proc-arm6_7.S ---__arm6_proc_info: .long 0x41560600 .long 0xfffffff0 .long 0x00000c1e .long PMD_TYPE_SECT | \ PMD_BIT4 | \ PMD_SECT_AP_WRITE | \ PMD_SECT_AP_READ b __arm6_setup .long cpu_arch_name ...
- 也就是说,跳转到 __arm6_setup 中执行,
- __arm6_setup 完成之后该函数执行 mov pc, lr , 由于 lr 已经保存了 _enable_mmu 的地址, 所以会跳转到 _enable_mmu 执行, 打开MMU.
__enable_mmu: ... b __turn_mmu_onENDPROC(__enable_mmu)__turn_mmu_on: ... mov pc, r13ENDPROC(__turn_mmu_on)
- __enable_mmu 调用 __turn_mmu_on 打开MMU,然后调用 mov pc, r13 返回。
- 由于r13 保存了 __switch_data 的起始地址,也就是 __mmap_switched 的地址, 因此MMU打开之后会执行 __mmap_switched 函数
---- arch/arm/kernel/head-common.S ----__switch_data: .long __mmap_switched .long __data_loc @ r4 ....__mmap_switched: ... ldmia r3, {r4, r5, r6, r7, sp} str r9, [r4] @ Save processor ID str r1, [r5] @ Save machine type str r2, [r6] @ Save atags pointer bic r4, r0, #CR_A @ Clear 'A' bit stmia r7, {r0, r4} @ Save control register values b start_kernel
- __mmap_switched 完成之后就跳到 C 程序中的 start_kernel() 执行了。汇编代码段到此结束。
start_kernel() 启动过程
- Linux内核启动地址
- Linux 内核启动分析
- Linux内核启动参数
- Linux内核启动分析
- Linux内核启动
- LINUX内核启动过程
- Linux内核启动地址
- LINUX 内核启动参数
- linux内核启动流程
- Linux 内核启动分析
- Linux内核启动流程
- linux内核启动流程
- linux内核启动过程
- Linux内核启动过程
- linux内核启动流程
- linux内核 启动android
- linux内核启动参数
- linux 内核启动
- HDU1284——钱币兑换问题
- Opencv (Opencv2)结合MFC学习数字图像处理---图片解码(3)
- linux 常用文本操作命令集
- HDU1272:小希的迷宫
- svn在linux下的使用(svn命令行)
- Linux 内核启动
- linux终端不能输入中文解决方法
- 使用PHP通过SMTP发送邮件新手指南
- 结构体的浅复制和深复制
- Linux安装JDK详细步骤
- Eclipse C++ 环境搭建
- 添加android-support-v4 错误 java.lang.ClassNotFoundException: android.support.v4.view.ViewPager in loade
- Android使用内置Camera应用程序进行图像捕获
- fufs环境搭建