如何直接从 32 位分页切换到 PAE 分页?

How to switch from 32-bit to PAE paging directly?

提问人:Akib Azmain Turja 提问时间:5/11/2021 最后编辑:Peter CordesAkib Azmain Turja 更新时间:6/13/2021 访问量:394

问:

我正在为我的个人研究开发一个微内核。我选择在 运行我的内核,为用户空间程序留下 3.75 GiB。当我的内核启动时,它会设置 32 位分页(使用硬编码的页面目录和页面表)。然后,它会检查主机是否支持 PAE,并设置页面目录指针表 (PDPT)。但是当我尝试将其加载到 .根据英特尔软件开发人员手册:0xf0000000%cr3

软件可以通过更改 CR4 的值在 32 位分页和 PAE 分页之间转换。PAE 与 MOV 到 CR4。

于是尝试使用以下代码切换到PAE分页:

movl %cr4, %eax
orl $(1 << 5), %eax
movl %eax, %cr4
movl %ebx, %cr3 // %ebx holds physical address to PDPT

或者,在 Intel 语法(或 NASM)中:

mov eax, cr4
or eax, 1 << 5
mov cr4, eax
mov cr3, ebx // ebx holds physical address to PDPT

但它失败了(在 QEMU 上)。它写入 ,设置下一条指令,执行它(至少 GDB 是这样说的),然后重置。我之前尝试过写信,但仍然是一样的结果。%cr4%eip%cr3%cr4

然后我尝试通过以下方式切换到 PAE 分页:unset PG -> set PAE -> write to -> set PG,我成功了。但我想直接切换到 PAE 分页。这怎么可能?%cr3

程序集 x86 osdev 页表

评论

0赞 Peter Cordes 5/11/2021
当 CR3 仍指向使用旧版 32 位格式的页表时,代码提取可能会失败。您的模拟器是否向您显示三重故障原因?如果没有,请使用 BOCHS。我想知道真正的 CPU 是否会让它发生工作(在现有翻译中使用 TLB 命中),除非切换 PAE 刷新 TLB 条目。禁用中断后,如果这些指令位于同一页面中,则甚至可能在某种程度上是安全的(实际上,如果不是在纸上)。尽管在 VM 中,可以随时逐出 TLB 条目的更多方法。
0赞 Peter Cordes 5/11/2021
我不认为您可以制作一个顶级页面目录,如果以一种方式解释,则指向有效的传统 32 位 PTE,如果以这种方式解释,则指向有效的 PAE 2nd 级别?如果我猜对了,这可能是您在 CR3 中需要的。(也许使用选择高位的 EIP,以便它将在顶级表中选择不同的条目,因此您可以有两种不同的格式。
3赞 Brendan 5/11/2021
@PeterCordes:这在理论上应该有效 - 例如,如果代码的 8 MiB,那么它将使用“CR3 处页面偏移量 8”处的页面目录条目进行普通分页,并使用 PAE 使用“CR3 处页面中的偏移量 0”处的 PDPT 条目。我敢打赌,原来的提问者正在较低的地址使用身份映射代码(对于可能具有“未发现的勘误表”的东西,将不得不重新设计太多)。
0赞 Akib Azmain Turja 5/15/2021
@Brendan 我没有使用身份映射。我正在分两阶段加载我的分页结构。首先,我将前 16 MiB 的内存映射到函数并在函数中映射。然后我跳到上半部分并检查 PAE 是否可用。然后,我生成适当的结构,其中下半部分未映射并加载它。0x00xf0000000_start

答:

3赞 Brendan 5/11/2021 #1

然后我尝试通过以下方式切换到 PAE 分页:unset PG -> set PAE -> write to %cr3 -> set PG,我成功了。但我想直接切换到 PAE 分页。这怎么可能?

这是不可能的。

如果“普通分页”已经在使用/启用中,那么你不能原子化地启用 PAE 并同时加载 CR3,因此(无论你是先加载 CR3 然后加载 CR4,还是先加载 CR4 然后尝试加载 CR3)无论哪个指令先发生,都会在获取第二条指令之前使 CPU 崩溃。

唯一的方法是暂时禁用分页。

3赞 Akib Azmain Turja 5/12/2021 #2

最后,我找到了一种直接切换到 PAE 寻呼的方法,这要归功于 @Brendan 和他的宝贵意见。要直接从 32 位分页切换到 PAE 分页,我不得不欺骗 CPU。我的内核的虚拟基础位于 。因此,在我跳到更高的一半后,前 960 个偏微分方程未被使用。因此,我将新的 PDPT(页面目录指针表)复制到我最初的 32 位页面目录中。然后我设置了 PAE 位,CPU 很高兴,因为它只读取保存 PDPT 的初始页面目录的前 32 个字节。0xf0000000

过程是这样的:

  1. memcpy (initial_page_directory, new_pdpt, 32);
  2. 启用 中的 PAE 位。CPU 很高兴地从您在步骤 1 中覆盖的初始页面目录的前 32 个字节中读取 PDPT。%cr4
  3. 将新 PDPT 加载到 .%cr3

注意:如果您尝试在步骤 1 和 2 之间访问前 32 MiB 的映射内存(如果已映射它)(可能是未定义的行为或可能是三重故障和重置),则此过程将不起作用。

评论

0赞 mevets 5/13/2021
你认为这适用于你遇到的每个版本的英特尔CPU吗?听起来你在一个地区。其次,您是否在虚拟机中尝试过?我之所以这么问,是因为你可能会侥幸逃脱,因为任何固有的不一致都可能被快速过渡所掩盖;而你的序列将导致多个 VMIcrients,这可能对你的混合模型不太满意。写一个与位置无关的简介来做,复制到统一映射的内存中,然后跳转到它,这真的不难。happens to work on this implementation
0赞 Akib Azmain Turja 5/17/2021
处理器是否会访问我从未访问过的偏微分方程,@mevets?
0赞 mevets 5/17/2021
它可能。它确实以推测方式执行,这样做可能会破坏页面缓存。英特尔未指定其中的大部分内容,因此发现的行为可能不是很可靠。
0赞 Peter Cordes 9/21/2021
您可以通过 MTRR 设置为 uncacheable 或 WC 来防止推测性访问。甚至可能在页表或页目录条目的 PAT 标志中(如果可行)。