Linux内核启动流程(上)

嵌入式技术

1368人已加入

描述

Linux内核启动流程我们分上下两篇来理解:

上篇是板级引导阶段,一般是用汇编实现。

下篇是通用内核启动阶段,一般是C语言实现。

本文先讲解上篇,大家看到汇编不用担心看不懂,在内核启动阶段,没有特别复杂的流程,都是顺序执行,只需一句一句阅读代码即可。

如何理解板级引导阶段?Linux内核支持不同的芯片架构,比如我们熟知的ARM架构、Intel的X86架构、MPIS架构、RISC-V架构等等,可以在Linux内核源码的arch目录看到不同的芯片架构源码放在对应的文件夹内。每种芯片架构的目录下都包含boot、configs、kernel、lib、Kconfig等文件或目录。

内核

我们以ARM为例,源码放在arch/arm目录下,如下图所示。

内核

1、文件入口

ARM架构的Linux内核的链接脚本文件放在arch/arm/kernel/vmlinux.lds,链接脚本定义了整个内核编译之后的链接过程,决定了一个可执行程序文件的入口和各个section的存储位置。Line 21的OUTPUT_ARCH(arm)指示了该链接脚本针对的是ARM芯片架构,Line 22的ENTRY(stext)指明了内核入口为stext。因此,我们分析Linux内核启动流程,就从stext入口分析。

内核

2、函数入口

ENTRY(stext)是Linux内核的入口函数,该函数定义在arch/arm/kernel/head.S文件。

内核

根据代码注释,stext是Kernel startup entry point,一般地,它从解压代码中获取,这里解压代码是指按照Linux内核压缩格式提取内核可执行文件,它的启动要求如下:

  • 关闭MMU
  • 关闭D-cache
  • 不关心I-cache
  • R0=0
  • R1=机器ID
  • R2=atags或设备树文件地址

3、调用safe_svcmode_maskall函数,确保安全进入SVC模式并屏蔽所有中断

内核

4、获取处理器ID并找到对应的procinfo

主要实现函数是__lookup_processor_type。

内核

Linux 内核将每种处理器都抽象为一个struct proc_info_list,因此可以通过process id 来找到对应的 proc_info 结构,__lookup_processor_type 函数在__lookup_processor_type_data中找到对应处理器的 proc info,将其保存到 R5 寄存器中。

内核

内核

内核

5、调用函数__vet_atags 验证 atags 或设备树(dtb)的合法性

内核

6、调用__create_page_tables创建页表

7、准备启动start_kernel

内核

start_kernel函数是在__mmap_switched函数内调用的。

上图的line 146指示将__mmap_switched的地址赋给R13,在line 158的__enable_mmu函数内的__turn_mmu_on函数内执行R13,成功进入start_kernel.

__INIT
__mmap_switched:


  mov  r7, r1
  mov  r8, r2
  mov  r10, r0


  adr  r4, __mmap_switched_data
  mov  fp, #0


#if defined(CONFIG_XIP_DEFLATED_DATA)
   ARM(  ldr  sp, [r4], #4 )
 THUMB(  ldr  sp, [r4] )
 THUMB(  add  r4, #4 )
  bl  __inflate_kernel_data    @ decompress .data to RAM
  teq  r0, #0
  bne  __error
#elif defined(CONFIG_XIP_KERNEL)
   ARM(  ldmia  r4!, {r0, r1, r2, sp} )
 THUMB(  ldmia  r4!, {r0, r1, r2, r3} )
 THUMB(  mov  sp, r3 )
  sub  r2, r2, r1
  bl  memcpy        @ copy .data to RAM
#endif


   ARM(  ldmia  r4!, {r0, r1, sp} )
 THUMB(  ldmia  r4!, {r0, r1, r3} )
 THUMB(  mov  sp, r3 )
  sub  r2, r1, r0
  mov  r1, #0
  bl  memset        @ clear .bss


  ldmia  r4, {r0, r1, r2, r3}
  str  r9, [r0]      @ Save processor ID
  str  r7, [r1]      @ Save machine type
  str  r8, [r2]      @ Save atags pointer
  cmp  r3, #0
  strne  r10, [r3]      @ Save control register values
  mov  lr, #0
  b  start_kernel
ENDPROC(__mmap_switched)

内核

内核

start_kernel函数定义在init/main.c文件内,我们在下一章节再进行分析。

内核

8、小节

内核

下一篇讲解通用内核启动阶段。

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分