慢慢欣赏arm64内核启动17 primary_entry之__create_page_tables代码第四部分map_memory剩下的流程

ARM64启动过程分析 文章目录arm64启动过程分析arm64启动过程分析(一)boot protocolarm64启动过程分析(二)内核启动第一步arm64启动过程分析(三)创建启动阶段页表arm64启动过程分析(四)为开启mmu进行的cpu初始化arm64启动过程分析(五)开启mmuarm64启动过程分析(六)进入start_kernelarm64启动过程分析(七)其他一些功能实现 arm64启动过程分析 参考:DDI0487F_b_armv8_arm.pdf ​ 蜗窝科技 ARMv8A Arch arm64启动过程分析 阅读详情

展示代码

	.macro map_memory, tbl, rtbl, vstart, vend, flags, phys, pgds, istart, iend, tmp, count, sv
	sub \vend, \vend, #1
	add \rtbl, \tbl, #PAGE_SIZE
	mov \sv, \rtbl
	mov \count, #0
	compute_indices \vstart, \vend, #PGDIR_SHIFT, \pgds, \istart, \iend, \count
	populate_entries \tbl, \rtbl, \istart, \iend, #PMD_TYPE_TABLE, #PAGE_SIZE, \tmp
	mov \tbl, \sv
	mov \sv, \rtbl

#if SWAPPER_PGTABLE_LEVELS > 3
	compute_indices \vstart, \vend, #PUD_SHIFT, #PTRS_PER_PUD, \istart, \iend, \count
	populate_entries \tbl, \rtbl, \istart, \iend, #PMD_TYPE_TABLE, #PAGE_SIZE, \tmp
	mov \tbl, \sv
	mov \sv, \rtbl
#endif

#if SWAPPER_PGTABLE_LEVELS > 2
	compute_indices \vstart, \vend, #SWAPPER_TABLE_SHIFT, #PTRS_PER_PMD, \istart, \iend, \count
	populate_entries \tbl, \rtbl, \istart, \iend, #PMD_TYPE_TABLE, #PAGE_SIZE, \tmp
	mov \tbl, \sv
#endif

	compute_indices \vstart, \vend, #SWAPPER_BLOCK_SHIFT, #PTRS_PER_PTE, \istart, \iend, \count
	bic \count, \phys, #SWAPPER_BLOCK_SIZE - 1
	populate_entries \tbl, \count, \istart, \iend, \flags, #SWAPPER_BLOCK_SIZE, \tmp
	.endm

分析代码

之前的章节我们分析完了函数 map_memory 的注释部分,以及第1到第7行,并分析了函数 compute_indices 和 populate_entries 的内部实现。
下面我们继续分析该函数。

我们再来看第6行,第六行实际上根据虚拟地址 vstart 和 vend,以及当前是全局页表(全局页表可以提供 PGDIR_SHIFT 和 一个页表所能容纳的页表项 pgds)
计算出全局页表项的起始索引 istart 和结束索引 iend
count 为0,表示全局页表的表项只在一个页表里面,不可能跨页表,当然 count 也当出参,统计全局页表的表项数目

接着第7行,根据第6行,将 对应的下一级页表的物理地址 rtbl 填写到 本级页表也就是全局页表的表项,区间为 [start, iend],
其中,表项的属性由 PMD_TYPE_TABLE 定义,rtbl 只定义了下一级页表的第一个页表起始地址,那么下一级页表的后续页表的起始地址需要根据 rtbl + n * PAGE_SIZE 而定
n 代表第n个页表,n的范围为 [0, count)

第8行将 sv 的只存放到 tbl 里面,sv原来的值是 rtbl,表明下一次 要填充次级页表的表项

第9行将 rtbl 的值存放在 sv 中,这时的 rtbl 经过第7行函数 populate_entries 的内部消化,已经进化成了 下下级页表的物理基地址

第11行 SWAPPER_PGTABLE_LEVELS 我们在源代码寻找其值

#if ARM64_SWAPPER_USES_SECTION_MAPS
#define SWAPPER_PGTABLE_LEVELS  (CONFIG_PGTABLE_LEVELS - 1)
#define IDMAP_PGTABLE_LEVELS    (ARM64_HW_PGTABLE_LEVELS(PHYS_MASK_SHIFT) - 1)
#else
#define SWAPPER_PGTABLE_LEVELS  (CONFIG_PGTABLE_LEVELS)
#define IDMAP_PGTABLE_LEVELS    (ARM64_HW_PGTABLE_LEVELS(PHYS_MASK_SHIFT))
#endif

所以其值依赖于 ARM64_SWAPPER_USES_SECTION_MAPS 和 CONFIG_PGTABLE_LEVELS
ARM64_SWAPPER_USES_SECTION_MAPS 在之前的章节分析过,值为1
而 CONFIG_PGTABLE_LEVELS 的值如下

$ cat .config | grep CONFIG_PGTABLE_LEVELS
CONFIG_PGTABLE_LEVELS=4

所以 SWAPPER_PGTABLE_LEVELS 的值为3,也就是说,调用函数 __create_page_tables 生成三级页表,这样导致第11行到第16行并没有编译。

而第18行到第22行则编译并运行。
第18行涉及到的宏 SWAPPER_TABLE_SHIFT 定义如下

/* Initial memory map size */
#if ARM64_SWAPPER_USES_SECTION_MAPS
#define SWAPPER_BLOCK_SHIFT     SECTION_SHIFT
#define SWAPPER_BLOCK_SIZE      SECTION_SIZE
#define SWAPPER_TABLE_SHIFT     PUD_SHIFT
#else
#define SWAPPER_BLOCK_SHIFT     PAGE_SHIFT
#define SWAPPER_BLOCK_SIZE      PAGE_SIZE
#define SWAPPER_TABLE_SHIFT     PMD_SHIFT
#endif

由于ARM64_SWAPPER_USES_SECTION_MAPS 已经定义,所以在源代码搜索 PUD_SHIFT 定义如下

$ grep -rn PUD_SHIFT arch/arm64/
arch/arm64/include/asm/pgtable-hwdef.h:59:#define PUD_SHIFT		ARM64_HW_PGTABLE_LEVEL_SHIFT(1)

由此可以计算 PUD_SHIFT 的含义

#define ARM64_HW_PGTABLE_LEVEL_SHIFT(n)	((PAGE_SHIFT - 3) * (4 - (n)) + 3)
	= (12 - 3) * (4 - 1) + 3 
	= 30

这说明在这一级页表中,将虚拟地址转换为索引需要偏移的位数是30
第18行另外涉及的一个宏定义是 PTRS_PER_PMD

$ grep -rnw PTRS_PER_PMD arch/arm64/
arch/arm64/include/asm/pgtable-hwdef.h:52:#define PTRS_PER_PMD		PTRS_PER_PTE

而 PTRS_PER_PTE 的定义如下

$ grep -rnw PTRS_PER_PTE arch/arm64/
arch/arm64/include/asm/pgtable-hwdef.h:43:#define PTRS_PER_PTE		(1 << (PAGE_SHIFT - 3))
	= (1 << (12 - 3))
	= ( 1 << 9)
	= 512

这说明,表示该页表最多支持的索引数目是512,范围是 2 ^ 9
所以,该行语句的含义是计算三级页表的页表项的虚拟地址 vstart vend 在第二级页表的索引 istart 和 iend的值,其中,在虚拟地址空间中,其范围是 (30 + 9, 30} = [38, 30]

第20行涉及到第一个宏定义是 PMD_TYPE_TABLE,这个宏定义之前的章节我们分析过

$ grep -rn PMD_TYPE_TABLE ./arch/arm64/
./arch/arm64/include/asm/pgtable-hwdef.h:109:#define PMD_TYPE_TABLE		(_AT(pmdval_t, 3) << 0)

对于 _AT 来说,它是一个类型转换宏,用于显式地将一个值转换为指定的类型,以避免隐式类型转换可能带来的问题。在这个上下文中,它将数字 3 转换为 pmdval_t 类型。
这个值(即3)在页表操作中用于指示PMD条目指向下一级页表。
该宏定义表示了页表项的属性。
另一个宏定义是 PAGE_SIZE,一个页的大小,代表每个页表项指向的下一级页表的步长。
所以第20行的含义是根据第18行计算出来的第三级页表的页表索引 istart 和 iend,填充第二级页表的页表项,第二级页表的页表项起始地址在 tbl,
填充的内容为第三级页表的物理地址,其起始地址为 rtbl,属性为 PMD_TYPE_TABLE,第三级页表是相邻的,每个页表的大小是 PAGE_SIZE。
第20行执行完毕后,count 的值为第三级页表需要填充的页表项

第21行依次类推,将第三级页表的基地址存放在 tbl 中

第24行到第26行是最后一步,将物理页面的地址填充到第三级页表的页表项中。

第24行涉及两个宏,SWAPPER_BLOCK_SHIFT 在之前的章节我们分析过,值为21,宏 PTRS_PER_PTE 的值之前页分析过,值为512。
所以,该行的含义是是计算三级页表的页表项的虚拟地址 vstart vend 在第三级页表的索引 istart 和 iend的值,其中,在虚拟地址空间中,其范围是 (21 + 9, 21} = [29, 21]

第25行涉及到一个宏定义 SWAPPER_BLOCK_SIZE,其值我们在之前的章节也分析过,其值为 1 << 21,
所以该行的含义是 bic \count, \phys, ((1 << 21) - 1)
即把物理地址的低21bit,也就是 bit[20:0]清理掉

第26行的含义是根据第25行计算出来的内核实际物理地址,填充第三级页表的页表项 istart 和 iend,第三级页表的页表项起始地址在 tbl,
属性为 PMD_TYPE_TABLE,而内核的物理地址是连续的页面,每个页表的大小是 PAGE_SIZE,但是指向的页表大小是SWAPPER_BLOCK_SIZE,也就是2Mbit。

linux 内核ARM64启动 这个是与机器有关的,可能使用了内部算法自动的定位并取得物理内存的大小,这些CPU在内核之外的保留的内存区域(dtb里的 /memreserve/ 的指定区域)空转,并轮询“cpu-release-addr”地址,该地址也在保留区域内。任何描述给内核的内存(包括低于镜像起始地址的),如果没有标记为保留的(dtb里的 /memreserve指定)将被内核认为是可以使用的。所有可写的系统寄存器在这内核镜像将要进入的异常级别(EL)必须在一个更高的异常级别(EL)通过软件初始化,来防止在一个未知的状态执行。 阅读详情

相关推荐

精益六西格玛Python数字化应用开发-Day18 应用场景:家电企业生产效率优化

本文将重点介绍专门针对精益六西格玛黑带使用场景的 Python 库,这些库功能侧重于数据分析和流程优化,特别适用于 DMAIC 方法论和看板管理。考虑到读者具备中高级Python 使用经验,我们将深入探讨各库的高级功能和实际应用应用场景。 某家电企业生产线自动化程度高,数据量巨大,希望通过精益管理提升生产效率。企业借助 Python 脚本,打通各类数据源(MES、ERP、设备日志),自动归集生产数据,分析各工序的生产节拍、设备停机、效率损失,快速定位瓶颈环节

m0_56592100的博客 906

Linux内存细节 内核临时页表 - linux内存管理(二)

在分析__create_page_tables函数之前,需要知道以下的知识。 1、head.S首先确定了processor type和machine type,之后就是创建页表。通过前面的两步,我们已经确定了processor type和machine type。此时,一些特定寄存器的值如下所示: r8 = machine info(struct machine_desc的基地址) r9 = cpu id(通过cp15协处理器获得的cpu id) r10...

生活需要深度 1042

CentOS8安装NVIDIA显卡驱动、CUDA和Anaconda

CentOS8安装NVIDIA显卡驱动和CUDA 参考:CentOS nvidia+cuda+cudnn 安装 安装英伟达显卡驱动 1、检查是否安装了NVIDIA的GPU(硬件层面): lspci | grep -i nvidia 2、安装GCC、kernal组件、dkms等 sudo yum install gcc sudo yum install gcc-c++ sudo yum -y install kernel-devel sudo yum -y install kernel-headers s

智慧的旋风的博客 1万+

Linux内存 -- 启动过程的临时页表创建

页表用时实现内存物理地址到虚拟地址的映射,实现虚拟地址到物理的转化;是虚、实内存地址转化的媒介。页表,是由物理地址向虚拟地址映射时创建的,由软件代码逻辑进行实现;虚拟地址向物理地址的转化需要借助MMU硬件单元实现

hello_yj的专栏 2273

tft_espi 中文字体及自定义字体制作(tft.eSPI模块)

tft.eSPI库自定义字体方法 先下载 processing 然后安装到mixly安装目录下找到tft_eSPI文件夹。

琢磨侠的博客 2763

计算机组成原理课设

计算机组成原理课程设计

qq_63306482的博客 8056

Linux内核head.S分析3 - __create_page_tables

Linux内核head.S __create_page_tables

to_be_better_wen的博客 652

特征金字塔网络(Feature Pyramid Network, FPN)

特征金字塔网络(FPN)是计算机视觉领域的一个里程碑式工作,通过高效的多尺度特征融合,显著提升了目标检测和分割任务的性能。其核心在于自底向上路径、自顶向下路径和横向连接的协同工作,兼顾了高分辨率和强语义信息。FPN的通用性和高效性使其成为许多现代检测框架的标配模块。

Rhett_Butler0922的博客 1727

Linux内存管理(三):“看见”物理内存

上文介绍了xxx, 本文基于xxx, 平台是x’x’x. 启动页表的创建 在arm64体系架构上,在进入start_kernel之前的汇编代码初始化阶段会进行两次的页表映射: Identity mapping 和 kernel image mapping. 表格: identity mapping:(arm64 kernel image mapping的变化) 用来打开mmu kernel image mapping: 用来跑内核代码,内核代码是在vmalloc区域 一旦设定完了页表,那么打开MMU之后

Hober 5992

智能小车倒车入库功能的实现(python,cpp,ubuntu)

智能小车倒车入库功能的实现(python,cpp,ubuntu)

weixin_62135607的博客 5793

24届数字IC验证——SV+UVM基础知识汇总(一)

汇总整理在面试过程中常见的问题,如果都能回答下来,那么面试肯定不成问题的分别构建不同抽象级的transaction,来实现复杂的激励。(以前是单一层次的sequence,对激励复用、上层复用不塌友好)不同层级之间的sequence没有继承关系,相互之间进行转换低层次的sequence从高层次的sequencer中获取高层次的sequence,并将其转换为低层次的sequence,再通过低层次的sequencer发送出去如何连接:低层次的sequencer包含高层次sequencer的句柄。

qq_42135020的博客 3152

慢慢欣赏arm64内核启动14 primary_entry之__create_page_tables代码第四部分map_memory

第二个参数 rtbl 是下一级页表的起始地址,根据刚才的描述,下一级页表的起始地址其实是不用传达的,注释也做了说明,由于全局目录只占有1个页面,所以在函数的内部是通过 tbl + PAGE_SIZE 传递的。第九个参数 iend,注释里没有说明,根据意思以及上下文代码的实现,应该是 index of end 的缩写,就是下一级页表的终止地址在该级页表的索引值,由于可能存在多个页表的可能性,第十二个参数 sv,也是出参,对应 x14,也是 临时变量,用于存储计算出来的下一级页表的起始物理地址。

shipinsky的博客 1052

YOLOv8/YOLOv11目标检测系统与多目标跟踪检测系统,使用PySide6搭建目标检测系统,支持目标检测、分割、姿势估计、旋转框,用户端系统+后台管理系统可用于大论文凑工作量或毕设必备

YOLOv8/YOLOv11目标检测系统与多目标跟踪检测系统,目标检测、分割、姿势估计、obb(旋转框),用户端系统+后台管理系统开发实战,可用于大论文凑工作量或毕设必备,全网最详细教程,系统支持 目标检测、分割、姿势估计、obb(旋转框)等模型进来检测,是一个通用的目标跟踪与目标检测系统,适配多种场景如:鱼苗检测与跟踪、行人多目标跟踪、车辆多目标跟踪、牛只行为识别与跟踪、口罩佩戴检测、安全帽佩戴检测、火灾检测等。

在职AI算法工程师,擅长计算机视觉,YOLO目标检测、分割等,擅长web、pyqt界面可视化,好内容持续更新中,来这里跟大家一起学习,共同进步 1万+

kernel之启动流程head.S

通过上一篇Makefile我们分析到了,编译出vmlinux的第一个原材料是head.o。. 先回顾下uboot是怎么启动kernel的。uboot将kernel从flash中拷贝到sdram后,设置tag进行工作交接,然后启动内核。theKernel (0, bd-&gt;bi_arch_number, bd-&gt;bi_boot_params); r0 : 0 r1 : bd-&gt;bi...

dachunfree的博客 762

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

ARM64 MMU 映射

本文主要描述 MMU 映射转换的基本知识

kakaBack的专栏 1715

迈瑞BS系列全自动生化分析仪LIS协议接口手册(内含HL7和ASTM)

迈瑞BS系列全自动生化分析仪LIS协议接口手册,内含HL7和ASTM)

庖丁解牛式剖析详解linux内核内存管理子系统-内核head.S的__create_page_tables介绍

各位老铁们,前片文章咱们粗略地堆MMU有了一定的认识,知道了他是谁干什么用的?有了堆MMU的理解,咱们才能进一步理解__create_page_tables。在uboot完成相关硬件的初始化后,最后跳转到kernel的入口函数(假设地址为0x40000000,那么就会将PC设置为0x40000000),此时,。一旦MMU开始后,,所有__create_page_tables的使命是在开启MMU前,做一下前期的准备,那下面我们看下__create_page_tables都做了那些工作?

weixin_43996442的博客 305

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

Linux内核启动代码之__create_page_tables函数分析

IEEE、万方文献及国内外专利下载,请关注微信公众号IEEE 在分析__create_page_tables函数之前,需要知道以下的知识。   1、head.S首先确定了processor type和 machine type,之后就是创建页表。通过前面的两步,我们已经确定了processor type 和machine type。此时,一些特定寄存器的值如下所示: r8 = machin...

mrwangwang的专栏 4286

临时内核页表的创建 __create_page_tables

相关宏: .globl swapper_pg_dir .equ swapper_pg_dir, KERNEL_RAM_VADDR - PG_DIR_SIZE 相当于: #define swapper_pg_dir (KERNEL_RAM_VADDR - PG_DIR_SIZE) .macro pgtbl, rd, phys add \rd, \phys, #TEXT_O...

zhoudawei的专栏 927

__create_page_tables

/* * Setup the initial page tables. We only setup the barest * amount which are required to get the kernel running, which * generally means mapping in the kernel code. * * r8 = phys_offset, r9 =

coldsnow33的专栏 1836

linux kernel 从入口到start_kernel 的代码分析

linux kernel 从入口到start_kernel 的代码分析 本文的很多内容是参考了网上某位大侠的文章写的>,有些东西是直接从他那copy过来的。 最近分析了一下u-boot的源码,并写了分文档, 为了能够衔接那篇文章,这次又把arm linux的启动代码大致分析了一下,特此写下了这篇文档。一来是大家可以看看u-boot到底是如何具体跳转到linux下跑的,二来也为自己更深入的学习l

yanlinembed的专栏 583

[zz]ARM64的启动过程之(二):创建启动阶段的页表

ARM64的启动过程之(二):创建启动阶段的页表 作者:linuxer 发布于:2015-10-13 18:18 分类:ARMv8A Arch 一、前言 本文主要描述了ARM64启动过程中,如何建立初始化阶段页表的过程。我们知道,从bootloader到kernel的时候,MMU是off的(顺带的负作用是无法打开data cache),为了提高性能,加快初始化速度,我们必须某个阶段(越早越好...

xiao_budong的博客 679

ARM64 Linux的启动分析

ARM64 Linux的启动分析   1.     找到Linux启动流程 Linux启动,会启动内核编译后的文件vmlinux vmlinux是一个ELF文件,按照./arch/arm64/kernel/vmlinux.lds设定的规则进行链接的 ./arch/arm64/kernel/vmlinux.lds 是 ./arch/arm64/kernel/vmlinux.lds.S编译之

六六哥的博客 4623

ARMv8(aarch64)页表建立过程详细分析

目录 文件修订记录...2 目录...3 1ARMv8存储管理...4 1.1Aarch64 Linux中的内存布局... 4 1.2AArch64的虚拟地址格式...4 1.2.14K页时的虚拟地址...4 1.2.264K页时的虚拟地址...5 2head.S页表建立过程分析...6 2.1页表建立函数__create_page_tables.6 2.1.1pgtbl

Heron——Linux & ARM 3万+

arm64_linux head.S的执行流程(3.18)- 8.stext之__create_page_tables

1.前言 本文基于高通8996平台,kernel版本为3.18.31。 本文主要介绍head.S的__create_page_tables执行流程 2. 页表基础知识 PGD(Page Global Directory)对应Level 0 translation table PUD (Page Upper Directory) 对应Level 1 translation table PMD (Page Middle Directory) 对应Level 2 translation table PTE (

HZero 862

Linux内核4.14版本:ARM64的内核启动过程(一)——start_kernel之前

目录 一、vmlinux.lds.S 二、head.S(进入kernel之前) 2.1 kernel之前bootloder的工作 2.1bootloader和kernel的交互的时候需求 2.3内核执行的第一行代码(_stext) 2.3.1preserve_boot_args() 2.3.2 el2_setup 2.3.3set_cpu_boot_mode_flag 2.3.4__create_page_tables 2.3.5__cpu_setup 2.3.6__pr...

yangguoyu8023的博客 6908

Linux内核启动建立临时页表与开启MMU过程分析(GDB+QEMU跟踪调试)

本文基于arm vexpress开发板+qemu模拟器,深度分析Linux内核启动时,建立临时页表与开启MMU过程。通过GDB对照汇编代码,调试跟踪整个过程。分析为何需要给__turn_mmu_on到__turn_mmu_on_end这段代码建立全等映射。vexpress开发板基于Cortex-A9芯片,配置内核空间和用户空间各2GB,所以内核虚拟地址从0x80000000开始。而开发板物理地址从0x60000000开始。

qq_33549208的博客 1170
上一篇: 慢慢欣赏arm64内核启动16 primary_entry之__create_page_tables代码第四部分populate_entries
下一篇: 慢慢欣赏arm64内核启动18 primary_entry之__create_page_tables代码第五部分
天麓
博客等级 码龄22年 323粉丝 437原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值