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

本文代码基于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 函数中就设置好了

 

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

黑马Redis视频教程实战篇(六)

目录 一、附近商户 1.1、GEO数据结构的基本用法 1.2、导入店铺数据到GEO 1.3、实现附近商户功能 二、用户签到 2.1、BitMap功能演示 2.2、实现签到功能 2.3、签到统计 2.4、关于使用bitmap来解决缓存穿透的方案 三、UV统计 3.1、HyperLogLog 3.2、测试百万数据的统…

【HarmonyOS】初识低代码平台开发元服务

【关键字】 HarmonyOS、低代码平台、元服务开发、拖拽式开发 【写在前面】 今天要分享的是HarmonyOS中的低代码开发相关的内容,低代码开发是DevEco Studio提供的一种UI界面可视化的构建方式,通过图形化的自由拖拽数据的参数化配置,可以快速…

2419286-92-1,Sulfo-Cy5.5 NHS ester,磺酸基Cyanine5.5-活性酯,用于标记抗体

Sulfo-Cyanine5.5 NHS ester,sulfo Cy5.5(Et) NHS,sulfo Cy5.5 SE,磺酸基Cy5.5-活性酯 (文章资料汇总来源于:陕西新研博美生物科技有限公司小编MISSwu)​ 产品结构式: 产品规格: 1…

SAP-MM-发票-采购附加成本处理简介

一.采购附加成本处理: 原材料的采购成本包括采购成本(采购单价*采购数量)和相关采购附加成本(运输费、保险费、报关费、仓储费、滞期费、租船费、码头费及代理费等费用),对于采购附加成本主要有…

基于双视角图表示算法的双向人职匹配偏好建模推荐系统构建

基于双视角图表示算法的双向人职匹配偏好建模推荐系统构建 文章目录 基于双视角图表示算法的双向人职匹配偏好建模推荐系统构建1. 传统推荐系统模型2. 协同过滤算法3. 基于双视角图表示学习算法的模型构建3.1 数据输入3.2 双视角交互图的构建3.3 混合偏好传播策略3.4 对于双向意…

git使用X篇_2_Git全套教程IDEA版(git、GitHub、Gitee码云、搭建公司内部GitLab、与IDEA集成等内容)

本文是根据以下视频及网上总结进行更新后的介绍git使用的博文。包含了git、GitHub、Gitee码云、搭建公司内部GitLab、与IDEA集成等内容。 笔记来源:【尚硅谷】5h打通Git全套教程IDEA版(涵盖GitHub\Gitee码云\GitLab) 文章目录 初识 Git0、内容…

ReadDataByIdentifier(0x22)服务

ReadDataByIdentifier(0x22)服务 ReadDataByIdentifier服务允许客户端从一个或多个dataIdentifiers标识的服务器请求数据记录值。 客户端请求消息包含一个或多个两字节的dataIdentifier值,用于标识服务器维护的数据记录 允许的dataIdentifie…

Vue3小兔鲜:组合式写法入门

Vue3&#xff1a;组合式写法入门 Date: May 11, 2023 认识Vue3 1. Vue3组合式API体验 通过 Counter 案例 体验Vue3新引入的组合式API <script> export default {data(){return {count:0}},methods:{addCount(){this.count}} } </script><script setup> imp…

Linux 高级篇-定制自己的Linux 系统

Linux 高级篇-定制自己的Linux 系统 基本介绍 通过裁剪现有Linux 系统(CentOS7.6)&#xff0c;创建属于自己的min Linux 小系统&#xff0c;可以加深我们对linux 的理解。利用centos7.6&#xff0c;搭建一个小小linux 系统, 很有趣。 基本原理 启动流程介绍&#xff1a; 制…

(转载)基于混合粒子群算法的TSP问题求解(matlab实现)

1 理论基础 标准粒子群算法通过追随个体极值和群体极值完成极值寻优&#xff0c;虽然操作简单&#xff0c;且能够快速收敛&#xff0c;但是随着迭代次数的不断增加&#xff0c;在种群收敛集中的同时&#xff0c;各粒子也越来越相似&#xff0c;可能在局部最优解周边无法跳出。…

2023/5/29总结

abstract修饰符 抽象类就是当类和类之间有相同特征时&#xff0c;将这些共同的特征提取出来&#xff0c;形成的就是抽象类。 抽象类的特点&#xff1a; 抽象类和抽象方法必须使用abstract 关键字修饰&#xff1a;publicabstract class 类名 / public abstract void eat();抽象…

测评补单操作在美客多店铺及产品优化中的决定性角色:深度解读

许多经营美客多平台的商家有一种观念&#xff0c;他们认为美客多平台的规则与亚马逊有所区别。在美客多上&#xff0c;店铺比产品更重要&#xff0c;而且平台的竞争相对较小。因此&#xff0c;他们认为在美客多平台进行补单操作是不必要的。 然而&#xff0c;是否真的如此呢&a…

FM实现F4帮助系列三:弹出框多筛选条件的搜索帮助(根据搜索帮助筛选字段)...

函数&#xff1a;F4IF_GET_SHLP_DESCR F4IF_START_VALUE_REQUEST 效果图&#xff1a; 本例子代码&#xff1a; 找到需要的帮助: *& Report ZLM_TEST_045 REPORT zlm_test_045. TABLES makt. DATA: BEGIN OF str_f4, matnr TYPE matnr, maktx TYPE maktx, END OF str_f4.…

react antd Modal里Form设置值不起作用

问题描述&#xff1a; react antd Modal里Form设置值不起作用&#xff0c;即使用form的api。比如&#xff1a;编辑时带出原有的值。 造成的原因&#xff1a;一般设置值都是在声明周期里设置&#xff0c;比如&#xff1a;componentDidMounted里设置&#xff0c;hook则在useEff…

国际植物命名数据库(International Plant Names Index)

功能介绍 https://www.ipni.org/ 是国际植物命名数据库&#xff08;International Plant Names Index&#xff09;的官方网站。国际植物命名数据库是一个全球性的植物命名和分类资源&#xff0c;旨在提供植物命名信息的权威来源。以下是该网站的一些特点和功能&#xff1a; 植…

Web Scoket简述

Web Socket 简介 初次接触 Web Socket 的人&#xff0c;我们已经有了 HTTP 协议&#xff0c;为什么还需要另一个协议&#xff1f;它能带来什么好处&#xff1f; 因为 HTTP 协议有一个缺陷&#xff1a;通信只能由客户端发起。http基于请求响应实现。 &#xff08;准确来说HTTP…

使用Python进行接口性能测试:从入门到高级

前言&#xff1a; 在今天的网络世界中&#xff0c;接口性能测试越来越重要。良好的接口性能可以确保我们的应用程序可以在各种网络条件下&#xff0c;保持流畅、稳定和高效。Python&#xff0c;作为一种广泛使用的编程语言&#xff0c;为进行接口性能测试提供了强大而灵活的工…

尚硅谷大数据hadoop教程_mapReduce

p67 课程介绍 p68概述 p69 mapreduce核心思想 p70 wordcount源码 序列化类型 mapReduce三类进程 p71 编程规范 用户编写的程序分成三个部分&#xff1a;Mapper、Reducer和Driver。 P72 wordcount需求案例分析 p 73 -78 案例环境准备 &#xff08;1&#xff09;创建maven…

基于Html5的在线资料库的设计与实现(asp.NET,SQLServer)

在线资料库系统采用.NET开发平台进行开发&#xff0c;开发工具采用Microsoft Visual Studio 2010集成开发环境&#xff0c;后台编程语言采用C#编程语言来进行编程开发&#xff0c;数据库我们采用当下流行的SQL Server 2008数据库管理系统来存放平台中的数据信息&#xff0c;整个…

Nginx 之 Tomcat 负载均衡、动静分离

一.详细安装及操作实例&#xff08;Nginx 七层代理&#xff09; 首先至少准备三台服务器 Nginx 服务器&#xff1a;192.168.247.131:80 Tomcat服务器1&#xff1a;192.168.247.133:80 Tomcat服务器2&#xff1a;192.168.247.134:8080 192.168.247.134:80811.部署Nginx 负载均…