详解Linux内核在arm上的启动过程

良许Linux

共 2995字,需浏览 6分钟

 ·

2021-10-01 23:30


Linux内核加载过程


通常,Linux内核都是经过gzip加载过之后的映像文件。

  • bootloader复制压缩内核到内存空间。

  • 内核自解压。

  • 运行内核。


编译完成的Linux内核存放在哪里?


  • ./vmlinux     elf格式未压缩内核。

  • arch/arm/boot/compressed/vmlinux    压缩以后的elf格式内核。

  • arch/arm/boot/zImage    压缩内核。


压缩内核(zImage)的入口


  • /arch/arm/boot/compressed/vmlinux.lds    该文件为编译器指定link顺序。

  • ENTRY(_start)    压缩内核从.start段开始执行。

  • 在/arch/arm/boot/compressed/head.S中执行以下爱操作:

        (1)检测系统空间。

        (2)初始化C代码空间。

        (3)跳转到C代码decompress_kernel,

                 arch/arm/boot/compressed/misc.c中。


解压之前的串口输出


  • include/asm-arm/arch-s3c2410/uncompress.h    中定义了puts作为串口输出函数。

  • 解压结束之后,程序跳转到r5:解压之后内核的起始地址。


开始真正的Linux内核


1、入口在arch/arm/kernel/head-armv.S

2、查找处理器类型

  • __lookup_processor_type

  • __lookup_architecture_type

3、初始化页表:__creat_page_tables

4、初始化C代码空间

5、跳转到C代码中,start_kernel


ARM的MMU单元


MMU:内存管理单元

作用:

  • 虚拟地址到物理地址的映射

  • 存储器访问权限

  • 控制Cache


通过MMU的访存


  • MMU会先查找TLB中的虚拟地址表

  • 如果TLB中没有虚拟地址的入口,硬件从主存储器中的转换表中获取转换与访问权限。


ARM的MMU访存原理



ARM的MMU页表格式


MMU支持基于节或者页的存储器访问。


  • 节:1MB的存储器块

  • 大页:64KB的存储器块

  • 小页:4KB的存储器块

  • 微页:1KB的存储器块


页表的级别


存在主存储器内的转换页表有两个级别:

  • 第一级表:存储节转换表与指向第二级表的指针

  • 第二级表:

    (1)存储大页和小页的转换表。

    (2)存储微页的转换表。


一级页表的地址


第一级表占用空间16KB,必须16KB对齐



第一级描述符


一级表每个入口描述了它所关联的1MB虚拟地址是如何映射的。



节描述符


  • Bits[1:0] 描述符类型(10b 表示节描述符)

  • Bits[3:2] 高速缓存(cache)和缓冲位(buffer)

  • Bits[4] 由具体实现定义

  • Bits[8:5] 控制的节的16 种域之一

  • Bits[9] 现在没有使用,应该为零

  • Bits[11:10] 访问控制(AP)

  • Bits[19:12] 现在没有使用,应该为零

  • Bits[31:20] 节基址,形成物理地址的高12 位


节的转换过程



临时内核页表的创建 __create_page_tables


__create_page_tables:
pgtbl r4 @ page table address 0x30008000-0x4000
mov r0, r4 @r0=0x30004000
mov r3, #0
add r2, r0, #0x4000
1: str r3, [r0], #4
str r3, [r0], #4
str r3, [r0], #4
str r3, [r0], #4
teq r0, r2
bne 1b

把一级页表0x30004000-0xa0080000清空


krnladr r2, r4 @ start of kernel

r4=0xa0004000,r2 = 内核起始地址所在1MB对齐空间,0x30000000


add r3, r8, r2 @ flags + kernel base

r8 为从处理器信息中得到的MMU 页表标志,r8=0xc0e, r3=0x30000c0e


str r3, [r4, r2, lsr #18]@ identity mapping

地址:0x300068000, value:0x30000c0e


add r0, r4, #(TEXTADDR & 0xff000000) >> 18 
@ start of kernel
bic r2, r3, #0x00f00000
str r2, [r0] @ PAGE_OFFSET + 0MB
add r0, r0, #(TEXTADDR & 0x00f00000) >> 18
str r3, [r0], #4 @ KERNEL + 0MB
......


映射表内容



映射结果



进入C代码


init/main.c中的start_kernel函数,进入到了Linux内核代码中。

  • printk函数

  • 重新初始化页表

  • 初始化中断,trap_init

  • 设置系统定时器、控制台…

  • 创建内核进程init




本公众号全部博文已整理成一个目录,请在公众号里回复「m」获取!

推荐阅读:

爱了,这款神器!

再见!程序员!这次情况严重升级了...

iptables 长文详解,值得收藏细读


5T技术资源大放送!包括但不限于:C/C++,Linux,Python,Java,PHP,人工智能,单片机,树莓派,等等。在公众号内回复「1024」,即可免费获取!!


浏览 66
点赞
评论
收藏
分享

手机扫一扫分享

举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

举报