我们已经准备好了,你呢?

2026我们与您携手共赢,为您的企业形象保驾护航!

大家好,我是劳。

我在这里再次分享莱纳斯大师的文章〜

详细解释了这篇文章,我从耐心阅读中获得了很多收益。

级别有限,因此建议用原始文本阅读它。

初始页面表

在切换到虚拟内存进行执行之前,我们需要建立一个MMU变换表,即页面表,该表用于将物理内存映射到虚拟内存,尽管初始映射不是页面,而是部分()。 ARM架构要求必须将初始页面表放置在物理内存中的16KB边界上,并且其大小始终为16KB。

初始页表表的位置以名为(页面)的符号定义,该符号是内核初始页表表的惯用名称。正如您所意识到的那样,有一个交换的含义,也就是说,稍后将被更复杂的页面替换。

名称“ Page Table”有点误导,因为用ARM术语使用的初始映射实际上称为“ (),而不称为页面。但是,此术语使用有点模糊,因此约定是使用页面来描述启动时虚拟地址的物理地址映射。

- 的定义 - 让我们一一研究。

./arch/arm/kernel/head.S
#define KERNEL_RAM_VADDR (PAGE_OFFSET + TEXT_OFFSET)

正如您所猜测的:应加载内核的虚拟地址,在编译过程中指定。

定义为( +)。如前所述,通常。因此,通常通常,但是使用不同的虚拟内存拆分设置或奇异设置会有所不同。通常,通常来自Arch/Arm/的Y,但也可以在某些高通平台以及某些平台上使用。

我们可以确定它在链接内核时将其传递给链接器:如果您检查了Arch/Arm //.lds.s中的ARM架构的链接文件,则可以看到链接器确实将内核的片段放入。 = +。

SECTIONS
{
[...]

. = PAGE_OFFSET + TEXT_OFFSET;
.head.text : {
_text = .;
HEAD_TEXT
}

.text : {
[...]
}

因此,即使我们现在正在分析的这些启动阶段的汇编代码与位置无关,也确实会在地址执行内核。

内核linux7.3_arm linux 内核_内核linux5.1

在物理和虚拟内存空间中,它是RAM上方的一个小区域,通常是32KB。

注意内核上方的小间隙,最常见 - (如果典型的32kb)。尽管这是内核空间的一部分,但这不是汇编和链接的内核的位置。它用于存储初始页面表,ATAG和一些临时存储区域。如果放置内核,它也将用于存储中断向量。尽管内核将在启动开始时使用初始页面表,但内核将稍后将其丢弃。

我们在物理和虚拟内存中添加了偏移量,这是在解压缩代码中完成的。它将内核的第一个字节解压缩为 +,因此,在物理内存中的内核位置与虚拟内存中的相同位置之间的线性增量始终由32位单词中的几个较高的位表示,例如24至31(8bit)(8bit)(8bit),这使我们可以立即使用,并在说明中使用偏移,并在我们面对时使用偏移。由此产生的限制是RAM启动地址必须由16MB()排除。

总结:

swapper_pg_dir
= KERNEL_RAM_VADDR - PG_DIR_SIZE
= PAGE_OFFSET + TEXT_OFFSET - PG_DIR_SIZE
= 0xC0000000 + 0x8000 - 0x4000
= 0xC0004000

因此,在我的示例中,初始页面表将在虚拟内存中,因此对于物理内存地址为。

内核linux7.3_内核linux5.1_arm linux 内核

如果使用LPAE,则页面是,因此我们最终将获得虚拟地址和物理地址,该地址由汇编宏PGTBL计算。

构建初始页面表的方法非常简单:我们首先用零填充页面表,然后构建初始页面表。发生这种情况发生。

手臂页面表格式

ARM32上的页面表布局可以是2级或第3级。级别2页表在ARM文档中称为短形式页面表,并且第3级格式称为长形页面表。长格式是大型物理地址扩展(LPAE)的特征,顾名思义,它可以处理最多40位地址的物理记忆。

这些表可以在1MB(LPAE上的2MB)或16KB页面中转换内存。初始页面表的使用,因此初始页面表中的内存转换为1MB的单位。

这简化了初始映射:您可以使用第一级页表(1级1MB),而无需处理MMU的复杂2级层次结构。

但是,如果我们在LPAE上运行,则中间还有另一个级别,因此我们需要处理两个级别,而不仅仅是一个级别。这就是为什么您在此处找到一些精心设计的LPAE代码的原因。它所做的只是插入一个指向2MB表的64位指针。

内核linux5.1_arm linux 内核_内核linux7.3

当不使用LPAE时,我们只需将MMU指向包含1MB的第一级表即可。使用LPAE时,我们需要一个中间水平来获取包含2MB的表。这些表中的条目被调用。

Linux Page表相关术语

此时,代码从包含三个字母的一些缩写开始。我不确定在谈论初始页面表时解释这些缩写是否有意义,但是开发人员会出于习惯而引用这些缩写,因此我会首先解释它们。

PGD​​(页面)是顶级表,这是MMU遍历分析和页面的起点。在我们的情况下,它位于物理记忆中。在ARM32中,这也是我们写给特殊CP15转换表寄存器(有时称为TTBR0)的地址,该寄存器告诉MMU在哪里可以找到该表。如果我们使用LPAE,则将在该位置释放,以存储由64位指针指向的PMD。

P4D(第4级)和PUD(页面上)是Linux VMM(虚拟存储器管理器)中的概念,它们在级别4或5处处理表层次结构。它们不在ARM上使用,因为ARM仅使用2或3个表。

PMD(页面)仅是LPAE的3级转换。这就是为什么我们需要为LPAE初始页面表保留另一个字节的原因。对于传统的ARM MMU(非LPAE),PMD和PGD是同一件事。这就是为什么代码用于MMU(非LPAE)和MMU(LPAE)的原因。

PTE:S,MAPS PAGE PAGE表格从物理内存到虚拟内存的条目。当系统刚刚启动时,我们不使用PTE,而是映射,因此PMD表是启动时使用的转换表。

再次注意:对于传统的ARM MMU(非LPAE),PGD和PMD是同一件事。对于LPAE,它是不同的。有时称为“ PMD为PGD”。由于我们还将在某种程度上“ P4D和PUD”,因此事情变得越来越困惑。

如果您真的想彻底使用虚拟内存,则需要不时重复上述内容。但是对于我们当前的情况,我们只想设置Page表(实际上),而PGD/P4D等先前的术语是无关紧要的。我们正在构建的是1MB映射到虚拟内存到物理内存。目前,我们甚至没有处理“页面”,因此使用“页面”有点混乱。

表的二进制格式

让我们仅考虑下面的传统手臂MMU的情况。

从物理地址到我们的(4096)32位(4个字节),它们如何工作?

打开MMU之后,程序计数器和所有CPU访问将在虚拟地址上使用,因此转换是这样的:在访问总线之前,虚拟地址将转换为物理地址。

从虚拟地址确定物理地址的方法如下:

我们采用虚拟地址的31至20位,并将其用作表格的索引,以找到32位(4个字节)。

虚拟地址 - (第一个1MB)将在表的前四个字节上由索引转换为我们称为索引0。

虚拟地址 - 将由索引1 AT-将索引编号乘以4是描述符的地址。

等等,

虚拟地址 - 用索引0xfff转换描述符。

聪明,您已经可以找到:字节(16KB,4 Byte * 4096)尺寸的描述符表格跨度为32位,即4GB地址空间。因此,使用此MMU表,我们可以将任何1MB虚拟内存地址块映射到任何1MB物理内存块。

例如:

对于内核虚拟基础地址,表中的索引将为>> 20 = 0xC00,并且可以通过将索引编号乘以4来获得描述符的物理地址。

0xC00 * 4 = 0x3000
0x10004000 + 0x3000 = 0x10007000

它是内核空间中第一个1MB内存的描述符。

32位1MB描述符的格式如下:

MMU将查看位[1:0],“ 10”表示这是一张地图。我们为其他位设置了一些默认值。除此之外,我们只关心将BIT [31:20]设置为正确的物理地址,以便我们有一个工作的描述符。设置初始页面映射的代码是执行这些任务。

对于LPAE,这有点不同:我们使用64位描述符(8个字节),但大小同时为2MB,因此最终表的大小仍然是字节。

身份映射()MMU启用代码

首先,我们围绕符号创建一个身份图,这意味着该代码将在物理和虚拟地址的1:1映射中运行。假设一行代码位于物理地址,则该代码的虚拟地址也将映射到。如果我们检查此代码,我们将看到它放入一个称为.idmap.text的段中。创建一个单独的片段意味着它链接到一个没有其他内部的单独的物理页面,因此我们只为此代码映射完整的1MB(如果恰好在内存中的边界对齐的情况下,甚至可能是两个)。

这通常很好,即使它跨越了2MB的内存,我们也会对其进行分析:如果我们将内核加载到该位置,如上一个示例所示,代码将位于这样的位置,虚拟地址也不会干扰或在(不同的内核空间划分)上干扰内核。如果有人将物理记忆的起始位置放置,我们将遇到严重的麻烦。让我们祈祷这将永远不会发生。

绘制其余部分

接下来,让我们为其余的物理和虚拟内存开始创建一个物理到虚拟内存映射,然后我们一直映射1,直到我们在虚拟内存中到达符号_end,该符号为,该记忆将其设置为内核对象本身的.bss段的末端。

arm linux 内核_内核linux5.1_内核linux7.3

BSS是内核二进制的末端段的惯用名称,这是C编译器将位置分配给所有运行时变量的地方。该细分市场的地址已明确定义,但是其中没有二进制数据,也就是说,该段中的内容是未定义的,这可能是我们当前映射它的任何内容。

值得详细研究此组件循环以了解映射代码的工作方式:

    ldr    r7, [r10, #PROCINFO_MM_MMUFLAGS] @ mm_mmuflags
(...)
add r0, r4, #PAGE_OFFSET >> (SECTION_SHIFT - PMD_ORDER)
ldr r6, =(_end - 1)
orr r3, r8, r7
add r6, r4, r6, lsr #(SECTION_SHIFT - PMD_ORDER)
1: str r3, [r0], #1 << PMD_ORDER
add r3, r3, #1 << SECTION_SHIFT
cmp r0, r6
bls 1b

关于代码的具体实施,我们将其保留给以后的分析。感谢您的阅读,下次见。

- - 结局 -

二维码
扫一扫在手机端查看

本文链接:https://by928.com/8766.html     转载请注明出处和本文链接!请遵守 《网站协议》
我们凭借多年的网站建设经验,坚持以“帮助中小企业实现网络营销化”为宗旨,累计为4000多家客户提供品质建站服务,得到了客户的一致好评。如果您有网站建设、网站改版、域名注册、主机空间、手机网站建设、网站备案等方面的需求,请立即点击咨询我们或拨打咨询热线: 13761152229,我们会详细为你一一解答你心中的疑难。

项目经理在线

我们已经准备好了,你呢?

2020我们与您携手共赢,为您的企业形象保驾护航!

在线客服
联系方式

热线电话

13761152229

上班时间

周一到周五

公司电话

二维码
微信
线