5

我试图直接写入物理内存位置,所以我使用汇编函数首先禁用分页,写入值,然后重新启用分页,但由于某种原因,尝试写入时仍会触发页面错误价值。

据我了解,在 x86-32bit 中,通过翻转 cr0 中的第 32 位来打开和关闭分页,所以这是我的汇编函数:

mov 4(%esp), %ecx //address
mov 8(%esp), %edx //value

mov %cr0, %eax
and $0x7fffffff, %eax
mov %eax, %cr0

mov %edx, (%ecx) //this line still triggers a page fault somehow

or $0x80000000, %eax
mov %eax, %cr0

ret

这是实现我想做的正确方法吗?如果是这样,为什么在 cr0 中的位翻转时仍然会触发页面错误?

4

2 回答 2

3

Intel 64 and IA-32 Architectures Software Developer's Manual System Programming Guide描述了如何禁用分页,作为从保护模式切换回实模式的过程的一部分:

9.9.2 切换回实地址模式

如果软件使用 MOV CR0 指令清除 CR0 寄存器中的 PE 位,处理器将从保护模式切换回实地址模式。重新进入实地址模式的过程应执行以下步骤:

  1. 禁用中断。CLI 指令禁用可屏蔽硬件中断。NMI 中断可以通过外部电路禁用。
  2. 如果启用分页,请执行以下操作:

    • 将程序控制转移到标识映射到物理地址的线性地址(即,线性地址等于物理地址)。
    • 确保 GDT 和 IDT 在身份映射页面中。
    • 清除 CR0 寄存器中的 PG 位。
    • 将 0H 移入 CR3 寄存器以刷新 TLB。

看来你错过了最后一步。TLB(翻译后备缓冲区)是 CPU 缓存页表条目的地方,并且在清除 PG 位后仍然处于活动状态。您需要清除 TLB 否则 CPU 将继续使用它。

请注意,在再次设置 PG 位之前,您必须重新加载 CR3。此外,由于您所做的事情非常不寻常,您可能会遇到模拟器的错误和兼容性问题。它可能只能在切换回实模式的过程中正确处理禁用分页,因为这可能是唯一经过测试的场景。即使是物理 CPU 在这方面也可能存在问题。

于 2015-11-23T19:28:03.983 回答
3

当跳转指令(仅远跳转?)完成时,CR0 寄存器中的更改将生效。

然而,禁用分页并不是一个好主意:您必须保证代码位于 1:1 映射内存中并且中断被禁用。

如果您使用堆栈,您还必须确保堆栈以 1:1 映射。

通过将 ecx 中的物理地址映射到虚拟地址然后写入虚拟地址的方式来修改页表要容易得多。

于 2015-11-22T14:14:47.317 回答