内核启动流程1——汇编部分

本文详细解析了Linux内核的启动流程,包括确定处理器类型、机器类型、验证内核标记列表、创建临时页表及开启MMU等关键步骤。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

注:本文是学习朱老师课程整理的笔记,基于linux2.6.35.7和九鼎X210BV3S开发板进行移植。

内核启动入口

从顶层Makefile可以看到vmlinux的生成规则:

vmlinux image - including updated kernel symbolsvmlinux: $(vmlinux-lds) $(vmlinux-init) $(vmlinux-main) vmlinux.o $(kallsyms.o) FORCE


其中$(vmlinux-lds)是链接脚本,对于ARM而言,就是arch/arm/kernel/vmlinux.lds文件(连接脚本并不是直接提供的,而是提供了一个汇编文件vmlinux.lds.S,然后在编译的时候再去编译这个汇编文件得到真正的链接脚本vmlinux.lds。)。从该脚本的如下内容可以看出,入口符号是stext:

SECTIONS
{
. = 0xC0000000 + 0x00008000;
.init : { /* Init code and data                */
_stext = .;
_sinittext = .;
*(.head.text)
*(.init.text) *(.cpuinit.text) *(.meminit.text)
_einittext = .;


其中$(vmlinux-lds)是链接脚本,对于ARM而言,就是arch/arm/kernel/vmlinux.lds文件(连接脚本并不是直接提供的,而是提供了一个汇编文件vmlinux.lds.S,然后在编译的时候再去编译这个汇编文件得到真正的链接脚本vmlinux.lds。)。从该脚本的如下内容可以看出,入口符号是stext,在arch/arm/kernel/head.S中:

__HEAD
 ENTRY(stext)
         setmode PSR_F_BIT | PSR_I_BIT | SVC_MODE, r9 @ 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
         movs    r8, r5                          @ invalid machine (r5=0)?
         beq     __error_a                       @ yes, error 'a'
         bl      __vet_atags
         bl      __create_page_tables
 ……

         ldr     r13, __switch_data              @ address to jump to after
                                                 @ mmu has been enabled
         adr     lr, BSYM(__enable_mmu)          @ return (PIC) address
  ARM(   add     pc, r10, #PROCINFO_INITFUNC     )
  THUMB( add     r12, r10, #PROCINFO_INITFUNC    )
  THUMB( mov     pc, r12                         )
 ENDPROC(stext)




上面的启动流程可以概括为如下几个步骤:
  1. 第79行,确定内核运行在SVC模式下,并且关闭IRQ和FIQ中断,这样能保证内核启动过程不会被外部中断干扰。
  2. 第81-84行,确定处理器类型。
  3. 第85-87行,确定机器类型。
  4. 第88行,验证内核标记列表。
  5. 第89行,创建临时页表。
  6. 第98行,切换数据。
  7. 第100行,开启MMU。
  8. 第101行,调用特定平台的__cpu_flush函数。
  9. 最终跳转到C语言函数start_kernel()(在__switch_data结束时,调用“b start_kernel”)。

确定处理器类型

arch/arm/kernel/head.S中:
         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'

通过cp15协处理器的c0获得processor id ,然后__lookup_processor_type 把处理器类型存储在r5中。
__lookup_processor_type检验cpu id的合法性方法是:内核会维护一个本内核支持的CPU ID号码的数组,然后该函数所做的就是将从硬件中读取的cpu id号码和数组中存储的各个id号码依次对比,如果没有一个相等则不合法,如果有一个相等的则合法。将匹配到的proc_info_list的基地址存到r5中,0表示没有找到对应的processor type。

确定机器类型
         bl      __lookup_machine_type           @ r5=machinfo
         movs    r8, r5                          @ invalid machine (r5=0)?
         beq     __error_a 

__lookup_machine_type设计理念和思路和上面校验cpu id的函数一样的,不同之处是本函数校验的是机器码。

验证内核标记列表

内核调用_vet_atags来验证内核标记列表是否有效。验证的内容包括:uboot给内核的传参是否字节对齐(4字节对齐)和是否以ATAG_CORE开始。

创建临时页表

内核调用__create_page_tables建立页表。linux内核本身被连接在虚拟地址处,因此kernel希望尽快建立页表并且启动MMU进入虚拟地址工作状态。kernel建立页表其实分为2步。第一步,kernel先建立了一个段式页表(和uboot中之前建立的页表一样,页表以1MB为单位来区分的),这里的函数就是建立段式页表的。段式页表本身比较好建立(段式页表1MB一个映射,4GB空间需要4096个页表项,每个页表项4字节,因此一共需要16KB内存来做页表),坏处是比较粗不能精细管理内存;第二步,再去建立一个细页表(4kb为单位的细页表),然后启用新的细页表废除第一步建立的段式映射页表。

调用平台特定的__cpu_flush函数

在开启MMU之前,要做一些工作,比如清楚指令缓存ICache、数据缓存DCache、Writebuffer和TLB等。这些工作一般通过cp15协处理器来实现,并且是平台相关的。就是__cpu_flush函数需要做的。

开启MMU

开启MMU由函数__enbale_mmu实现。

切换数据

开启MMU后进入__switch_data部分,然后执行__mmap_switched函数,构建C语言运行环境(复制数据段、清除bss段),保存起来cpu id号、机器码、tag传参的首地址。最后由start_kernel跳转到C语言运行阶段。

总结:汇编阶段主要就是校验启动合法性、建立段式映射的页表并开启MMU以方便使用内存、跳入C阶段。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值