Linux内存初始化-启动阶段的内存初始化

news2024/9/20 9:39:04
本文代码基于ARM64平台, Linux kernel 5.15

在加载kernel 之前, kernel对于系统是有一定要求的,明确规定了boot阶段必须要把MMU关闭:

arch/arm64/kernel/head.S
 /*
  * Kernel startup entry point.
  * ---------------------------
  *
  * The requirements are:
  *   MMU = off, D-cache = off, I-cache = on or off,
  *   x0 = physical address to the FDT blob.

那么在进入kernel之后, 就必须有一个使能MMU, 建立映射的过程, 本文描述kernel启动阶段进行内存初始化相关的操作。

流程

在初始化阶段,我们mapping二段地址,一段是identity mapping,其实就是把物理地址mapping到物理地址上去,在打开MMU的时候需要这样的mapping(ARM ARCH强烈推荐这么做的)。第二段是kernel image mapping,内核代码欢快的执行当然需要将kernel running需要的地址(kernel txt、dernel rodata、data、bss等等)进行映射了。映射之后, 系统内存的状态大致如图所示:

(图中的地址为实验机器的地址, 仅供参考)

启动入口

kernel的 启动入口是_text , 它定义在.head.text 中

arch/arm64/kernel/vmlinux.lds.S
.....
ENTRY(_text)
.....
 SECTIONS
{
         . = KIMAGE_VADDR;

         .head.text : {
                 _text = .;
                 HEAD_TEXT
         }
         .....
}

arch/arm64/kernel/head.S
         __HEAD
         /*
          * DO NOT MODIFY. Image header expected by Linux boot-loaders.
          */
         efi_signature_nop                       // special NOP to identity as PE/COFF executable
         b       primary_entry                   // branch to kernel start, magic
         .quad   0                               // Image load offset from start of RAM, little-endian
         le64sym _kernel_size_le                 // Effective size of kernel image, little-endian
         .....


SYM_CODE_START(primary_entry)
         bl      preserve_boot_args
         bl      init_kernel_el                  // w0=cpu_boot_mode
         adrp    x23, __PHYS_OFFSET
         and     x23, x23, MIN_KIMG_ALIGN - 1    // KASLR offset, defaults to 0
         bl      set_cpu_boot_mode_flag
         bl      __create_page_tables

kernel 启动后, 最终会调用到__create_page_tables这个函数, 这是创建启动页表的关键函数。

调用过程如下_text ->primary_entry->__create_page_tables

初始化准备

arch/arm64/kernel/head.S
        SYM_FUNC_START_LOCAL(__create_page_tables)
         mov     x28, lr

         /*
          * Invalidate the init page tables to avoid potential dirty cache lines
          * being evicted. Other page tables are allocated in rodata as part of
          * the kernel image, and thus are clean to the PoC per the boot
          * protocol.
          */
         adrp    x0, init_pg_dir
         adrp    x1, init_pg_end
         sub     x1, x1, x0
         bl      __inval_dcache_area
         /*
          * Clear the init page tables.
          */
         adrp    x0, init_pg_dir
         adrp    x1, init_pg_end
         sub     x1, x1, x0
 1:      stp     xzr, xzr, [x0], #16
         stp     xzr, xzr, [x0], #16
         stp     xzr, xzr, [x0], #16
         stp     xzr, xzr, [x0], #16
         subs    x1, x1, #64
         b.ne    1b

__create_page_tables 初始化时, 执行的时候会把init_pg_dir 对应区域的cache清空, 然后把对应区域的内存清零。

init_pg_dir 就是启动阶段用来映射kernel text的页表, 它的本身是位于 kenrel 的bss段

arch/arm64/kernel/vmlinux.lds.S
        BSS_SECTION(0, 0, 0)

         . = ALIGN(PAGE_SIZE);
         init_pg_dir = .;
         . += INIT_DIR_SIZE;
         init_pg_end = .;

映射IDMAP_TEXT

identity mapping实际上就是建立了整个内核使能MMU相关代码的一致性mapping,就是将物理地址所在的虚拟地址段mapping到物理地址上去。为什么这么做呢?ARM ARM文档中有一段话:

If the PA of the software that enables or disables a particular stage of address translation differs from its VA, speculative instruction fetching can cause complications. ARM strongly recommends that the PA and VA of any software that enables or disables a stage of address translation are identical if that stage of translation controls translations that apply to the software currently being executed.

由于打开MMU操作的时候,内核代码欢快的执行,这时候有一个地址映射ON/OFF的切换过程,这种一致性映射可以保证在在打开MMU那一点附近的程序代码可以平滑切换。

下面是__create_page_tables函数中处理IDMAP的部分

arch/arm64/kernel/head.S
 SYM_FUNC_START_LOCAL(__create_page_tables)
                .......
       /*
          * Create the identity mapping.
          */
         adrp    x0, idmap_pg_dir
         adrp    x3, __idmap_text_start          // __pa(__idmap_text_start)
         ldr_l   x4, idmap_ptrs_per_pgd
         mov     x5, x3                          // __pa(__idmap_text_start)
         adr_l   x6, __idmap_text_end            // __pa(__idmap_text_end)

         // 根据下面的注释可以看到, x3, x6是需要映射的虚拟地址的起始和结束地址(第三个和第四个参数),这里穿的是__idmap_text_start和__idmap_text_end对应的物理地址, 同时映射的目的物理地址也传的是x3(第六个参数)
         map_memory x0, x1, x3, x6, x7, x3, x4, x10, x11, x12, x13, x14


 /*
  * Map memory for specified virtual address range. Each level of page table needed supports
  * multiple entries. If a level requires n entries the next page table level is assumed to be
  * formed from n pages.
  *
  *      tbl:    location of page table
  *      rtbl:   address to be used for first level page table entry (typically tbl + PAGE_SIZE)
  *      vstart: start address to map
  *      vend:   end address to map - we map [vstart, vend]
  *      flags:  flags to use to map last level entries
  *      phys:   physical address corresponding to vstart - physical memory is contiguous
  *      pgds:   the number of pgd entries
  *
  * Temporaries: istart, iend, tmp, count, sv - these need to be different registers
  * Preserves:   vstart, vend, flags
  * Corrupts:    tbl, rtbl, istart, iend, tmp, count, sv
  */
         .macro map_memory, tbl, rtbl, vstart, vend, flags, phys, pgds, istart, iend, tmp, count, sv

在map的时候, 实际上是map的idmap_text_start到idmap_text_end这一段地址。那么这段地址里面有哪些内容呢?

#define IDMAP_TEXT                                      \
         . = ALIGN(SZ_4K);                               \
         __idmap_text_start = .;                         \
         *(.idmap.text)                                  \
         __idmap_text_end = .;

arch/arm64/kernel/vmlinux.lds.S
         ...........
       .text : ALIGN(SEGMENT_ALIGN) {  /* Real text segment            */
               _stext = .;             /* Text and read-only data      */
                       IRQENTRY_TEXT
                       SOFTIRQENTRY_TEXT
                       ENTRY_TEXT
                       TEXT_TEXT
                       SCHED_TEXT
                       CPUIDLE_TEXT
                       LOCK_TEXT
                       KPROBES_TEXT
                       HYPERVISOR_TEXT
                       IDMAP_TEXT
                       HIBERNATE_TEXT
                       TRAMP_TEXT
                       *(.fixup)
                       *(.gnu.warning)
               . = ALIGN(16);
               *(.got)                 /* Global offset table          */
       }

可以看到, .idmap.text 是被放在.text段中的

$ nm vmlinux | grep __idmap_text_end
ffff800010d8b760 T __idmap_text_end

$ nm vmlinux | grep __idmap_text_start
ffff800010d8b000 T __idmap_text_start
arch/arm64/kernel/head.S
        .section ".idmap.text","awx"

 /*
  * Starting from EL2 or EL1, configure the CPU to execute at the highest
  * reachable EL supported by the kernel in a chosen default state. If dropping
  * from EL2 to EL1, configure EL2 before configuring EL1.
  *
  * Since we cannot always rely on ERET synchronizing writes to sysregs (e.g. if
  * SCTLR_ELx.EOS is clear), we place an ISB prior to ERET.
  *
  * Returns either BOOT_CPU_MODE_EL1 or BOOT_CPU_MODE_EL2 in w0 if
  * booted in EL1 or EL2 respectively.

在head.S文件中, 定义了这个段, 很多内存初始化的代码都被放到了这个段里, 比如enable_mmu,primary_switch

$ nm vmlinux | grep __enable_mmu
ffff800010d8b268 T __enable_mmu

$ nm vmlinux | grep __primary_switch
ffff800010d8b330 t __primary_switch
ffff800011530330 t __primary_switched

上面可以看到, 在和使能mmu相关的代码, 实际上都被放到了__idmap_text 里面, 保证切换MMU时能够平滑切换。

Idmap 实际上被映射了两次,既映射到了其kernle text的虚拟地址, 又映射到了它的物理地址。

映射kernel

创建IDMAP之后, 还会将kernel text相关的内容进行映射, 保证kernel可以正常运行

arch/arm64/kernel/head.S
 SYM_FUNC_START_LOCAL(__create_page_tables)
                    /*
          * Map the kernel image (starting with PHYS_OFFSET).
          */
         adrp    x0, init_pg_dir
         mov_q   x5, KIMAGE_VADDR                // compile time __va(_text)
         add     x5, x5, x23                     // add KASLR displacement
         mov     x4, PTRS_PER_PGD
         adrp    x6, _end                        // runtime __pa(_end)
         adrp    x3, _text                       // runtime __pa(_text)
         sub     x6, x6, x3                      // _end - _text
         add     x6, x6, x5                      // runtime __va(_end)

         map_memory x0, x1, x5, x6, x7, x3, x4, x10, x11, x12, x13, x14

可以看到, 这里是将_text_end 这段物理地址映射到对应的虚拟地址上。

使能MMU

arch/arm64/kernel/head.S
 SYM_FUNC_START(__enable_mmu)
         mrs     x2, ID_AA64MMFR0_EL1
         ubfx    x2, x2, #ID_AA64MMFR0_TGRAN_SHIFT, 4
         cmp     x2, #ID_AA64MMFR0_TGRAN_SUPPORTED
         b.ne    __no_granule_support
         update_early_cpu_boot_status 0, x2, x3
         adrp    x2, idmap_pg_dir                                                                                               ---------------(1)
         phys_to_ttbr x1, x1
         phys_to_ttbr x2, x2
         msr     ttbr0_el1, x2                   // load TTBR0                                      ---------------(2)
         offset_ttbr1 x1, x3
         msr     ttbr1_el1, x1                   // load TTBR1                                      ---------------(3)
         isb    
         msr     sctlr_el1, x0                                                                                                      ---------------(4)
         isb
         /*
          * Invalidate the local I-cache so that any instructions fetched
          * speculatively from the PoC are discarded, since they may have
          * been dynamically patched at the PoU.
          */
         ic      iallu
         dsb     nsh
         isb
         ret
 SYM_FUNC_END(__enable_mmu)

(1)(2) 这里吧idmap_pg_dir的地址传给了ttbr0_el1;这里需要说明下, arm64 会在MMU时, 0x0000 0000 0000 0000 ~ 0xFFFF 0000 0000 0000 地址空间的内容会用ttbr0_el1 进行转换, 此时由于都是直接运行的物理地址, 所以IDMAP相关的映射全部都会走ttbr0_el1

(3) 将x1 的值赋给ttbr1_el1, 0xFFFF 0000 0000 0000 ~ 0xFFFF FFFF FFFF FFFF 空间的地址映射用用到这个寄存器, 其实就是kernel space相关的地址。在启动阶段调用__enable_mmu时, x1传的是init_pg_dir的地址。

arch/arm64/kernel/head.S
 SYM_FUNC_START_LOCAL(__primary_switch)
 #ifdef CONFIG_RANDOMIZE_BASE
         mov     x19, x0                         // preserve new SCTLR_EL1 value
         mrs     x20, sctlr_el1                  // preserve old SCTLR_EL1 value
 #endif

         adrp    x1, init_pg_dir
         bl      __enable_mmu

(4) 使能MMU, x0的值在调用__enable_mmu之前的__cpu_setup 函数中就设置好了

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/617099.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

路径规划算法:基于松鼠优化的路径规划算法- 附代码

路径规划算法:基于松鼠优化的路径规划算法- 附代码 文章目录 路径规划算法:基于松鼠优化的路径规划算法- 附代码1.算法原理1.1 环境设定1.2 约束条件1.3 适应度函数 2.算法结果3.MATLAB代码4.参考文献 摘要:本文主要介绍利用智能优化算法松鼠…

入门大数据就业前景怎么样?

时势造英雄,对个人而言亦是如此。跟随趋势,找准自己未来发力的赛道,在合适的时间干合适的事,就是抓住自己的未来。 猎聘大数据研究院发布了《2022未来人才就业趋势报告》 从排名来看,2022年1-4月各行业中高端人才平均…

mac m1/m2 安装 ps 2023 插件无法显示扩展界面

碎碎念:一直在踩坑的路上,甚至想休息时间玩一会儿 ps 都能踩坑 问题描述 新的 m2 芯片 mac 安装了色环插件后,在窗口界面中没有找到扩展,且在首选项->增效工具的旧版扩展也是灰色的 题外话:记录一下 mac 的 photo…

2023尚上优选-社区团购 优选电商Spring Cloud Alibaba

尚上优选2023最新企业级微服务架构项目 分布式微服务后端VUE、小程序 尚上优选是真实居住社区内居民团体的一种互联网线上线下购物消费行为,是依托真实社区的一种区域化、小众化、本地化、网络化的团购形式。简而言之,它是依托社区和团长社交关系实现生…

DHCP与DHCPv6讲解

目录 DHCP DHCP端口号 DHCP报文 DHCP工作过程 DHCP租期续租 DHCPv6 DHCPv6端口号 DHCPv6报文 DHCPv6工作原理 DHCP DHCP端口号 DHCP主要有两个端口号,分别是UDP67和UDP68 DHCP客户端向DHCP服务器发送报文时采用68端口号,DHCP服务器向DHCP客户端…

整合开源治理经验,共谋开源社区发展|2023 开放原子全球开源峰会开源社区治理与运营分论坛即将启幕

在数智时代广泛连接、同步演进和网状协作特性的催化下,开源社区正在成为技术应用和行业数字化发展的重要推动力量。开展数字技术开源社区的有效治理,对调和相互冲突的内外部需求、协调相互竞合的参与主体、整合差异化的绩效目标具有重要理论和实践意义。…

ChatGPT 时代,程序员的生存之道

ChatGPT 近期炙手可热,仿佛没有什么问题是它不能解决的。出于对 ChatGPT 的好奇,我们决定探索下它对于前端开发人员来讲,是作为辅助工具多一些,还是主力工具更多一些? 2D 能力测试 我们就挑选一个著名的递归回溯问题—…

自学黑客(网络安全),一般人我劝你还是算了!

网络安全在当今信息社会越来越受到重视,但不同于Java、C/C等后端开发岗位有非常明晰的学习路线,网络安全更多是靠自己摸索,要学的东西又杂又多,难成体系。 这也是为什么我经常劝别人不要入网络安全的坑,因为一般人真坚…

调用腾讯API实现人体关键点分析

目录 1. 作者介绍2. 调用腾讯API实现人体关键点分析2.1 API的简介2.2 腾讯API使用工具2.4 MPII数据集介绍 3. 实验流程与结果3.1调用百度API流程3.2完整代码3.3测试结果 1. 作者介绍 张思怡,女,西安工程大学电子信息学院,2022级研究生&#…

MongoDB简单快速入门

MongoDB简单快速入门 简单介绍应用场景安装与启动基础操作SpringBoot整合MongoDB 简单介绍 MongoDB是一个开源、高性能、无模式的文档型数据库。NoSQL数据库产品中的一种,是最想关系型数据库的非关系型数据库 应用场景 安装与启动 直接将安装的压缩包进行解压&…

unity 3d实现下雨、雾气、萤火虫和火花四溅的粒子效果

文章目录 先看最终效果1. 下雨效果2. 雾气效果3. 萤火虫和火花四溅的效果 3d下雨粒子效果涟漪效果雨滴和涟漪效果结合水花效果雨滴涟漪水花结合问题雾气效果萤火虫火花效果萤火虫和火花效果结合 先看最终效果 1. 下雨效果 2. 雾气效果 3. 萤火虫和火花四溅的效果 3d下雨粒子效…

【C/C++】基础知识之string字符串

创作不易&#xff0c;本篇文章如果帮助到了你&#xff0c;还请点赞 关注支持一下♡>&#x16966;<)!! 主页专栏有更多知识&#xff0c;如有疑问欢迎大家指正讨论&#xff0c;共同进步&#xff01; &#x1f525;c系列专栏&#xff1a;C/C零基础到精通 &#x1f525; 给大…

整数规划在数学建模中的应用及MATLAB实现

2023年9月数学建模国赛期间提供ABCDE题思路加Matlab代码,专栏链接(赛前一个月恢复源码199,欢迎大家订阅):http://t.csdn.cn/Um9Zd 目录 整数规划基本概念 整数规划原理 MATLAB实现 1. 使用intlinprog求解整数规划问题 数学建模案例&#xff1a;设备选购优化 问题建模 MA…

时间序列预测 | Matlab基于鲸鱼算法优化随机森林(WOA-RF)与随机森林(RF)的时间序列预测对比

文章目录 效果一览文章概述部分源码参考资料效果一览 文章概述 时间序列预测 | Matlab基于鲸鱼算法优化随机森林(WOA-RF)与随机森林(RF)的时间序列预测对比 评价指标包括:MAE、RMSE和R2等,代码质量极高,方便学习和替换数据。要求2018版本及以上。 部分源码 %---------------…

算法刷题-数组-二分查找

算法刷题-数组-二分查找 二分查找思路二分法第一种写法二分法第二种写法 总结相关题目推荐python语言版本 二分查找 力扣题目链接 给定一个 n 个元素有序的&#xff08;升序&#xff09;整型数组 nums 和一个目标值 target &#xff0c;写一个函数搜索 nums 中的 target&…

数据结构与算法系列之快速排序

&#x1f497; &#x1f497; 博客:小怡同学 &#x1f497; &#x1f497; 个人简介:编程小萌新 &#x1f497; &#x1f497; 如果博客对大家有用的话&#xff0c;请点赞关注再收藏 &#x1f31e; 快速排序 快速排序是任取待排序元素序列中的某元素作为基准值&#xff0c;按照…

【QT】TCP/UDP详解及实现

TCP/UDP TCP/IP模型TCP协议头部格式三次握手四次挥手 UDP协议头部格式 Socket编程tcpudp代码实现服务端&#xff1a;客户端&#xff1a; 总结 TCP/IP模型 TCP模型是一个常见的网络协议参考模型&#xff0c;也称为TCP/IP模型或互联网模型。它是指TCP/IP协议族中的一组协议&…

总结:Linux系统上面定时备份mysql指定数据库的解决方案

总结&#xff1a;Linux系统上面定时备份mysql指定数据库的解决方案 一Mysql数据库本身就自带备份数据库命令1.mysql本身自带备份数据库为一个sql文件的命令&#xff0c;只需要在操作系统的终端里面执行就好了 二Linux和Unix操作系统都自带一个定时任务执行器&#xff1a;cronta…

浅结反静态调试2

文章目录 SMC 自解码什么是SMC&#xff1f;原理示例动调 SMC 自解码 什么是SMC&#xff1f; 简而言之&#xff0c;就是程序中的部分代码在运行之前被加密成一段数据&#xff0c;不可反编译&#xff0c;通过程序运行后执行相关的解码功能&#xff0c;对加密的代码数据进行动态…

Linux :: vim 编辑器的初次体验:三种 vim 常用模式 及 使用:打开编辑、退出保存关闭vim

前言&#xff1a;本篇是 Linux 基本操作篇章的内容&#xff01; 笔者使用的环境是基于腾讯云服务器&#xff1a;CentOS 7.6 64bit。 学习集&#xff1a; C 入门到入土&#xff01;&#xff01;&#xff01;学习合集Linux 从命令到网络再到内核&#xff01;学习合集 目录索引&am…