在x86 32位系统中禁用分页

5

我尝试直接写入物理内存位置,因此我使用汇编函数首先禁用分页,写入值,然后重新启用分页,但不知何故,在尝试写入该值时仍会引发页面错误。

据我了解,在x86-32位中,通过翻转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中的位时仍然会触发页面错误?


2
你是否试图在已启用分页的操作系统上禁用分页?这是你自己的操作系统还是众所周知的操作系统(Linux、Windows、MacOS)?当现代操作系统已经启用分页时,绝对不应该禁用分页。要写入物理地址,请使用操作系统提供的设施(/dev/memDevice\PhysicalMemory)或内核提供的函数。顺便说一下,看起来(我不习惯AT&T语法),你在代码中倒置了值和地址。 - Neitsa
1
实际上那是第31位,因为第0位是第一位。 - cadaniluk
2
禁用分页会导致错误吗?如果该代码正在运行的页面具有不同的物理和虚拟地址,那么是否会导致故障?或者说,现在EIP是物理地址,会导致其他指令运行。您是否在bochs中单步执行此操作以查看发生故障时确切发生了什么? - Peter Cordes
1
此外,我认为“理智”的方式是将物理页面映射到虚拟内存中的某个位置,然后再写入。不过那样太无聊了。疯狂的方式肯定看起来很有趣。 - Peter Cordes
1
如果代码、堆栈、GDT、IDT、所有中断处理程序以及它们可能使用的一切都是身份映射的,那么禁用和重新启用分页将是“安全”的;但这也会很慢(使所有TLB条目无效),而且你必须想知道为什么要在第一次启用分页时进行身份映射。;-) - Brendan
显示剩余3条评论
2个回答

3
当执行跳转指令(只有远跳转?)时,CR0寄存器的改变会生效。
然而,禁用分页不是一个好主意:您必须保证代码位于1:1映射的内存中,并且中断已被禁用。
如果使用堆栈,还必须确保堆栈被映射为1:1。
更容易的方法是以ecx中的物理地址被映射到虚拟地址的方式修改页表,然后写入虚拟地址。

3

Intel 64和IA-32体系结构软件开发人员手册系统编程指南描述了如何禁用分页作为从保护模式切换回实模式的过程的一部分:

9.9.2 切换回实地址模式

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

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

    • 将程序控制传输到与物理地址相同的线性地址(即,线性地址等于物理地址)。
    • 确保GDT和IDT在标识映射的页面中。
    • 清除CR0寄存器中的PG位。
    • 将0H移动到CR3寄存器中以刷新TLB。
  3. 清除TLB或CPU将继续使用它。请注意,您必须在再次设置PG位之前重新加载CR3。

由于您所做的事情非常不寻常,因此您可能会遇到与模拟器的兼容性问题和错误。只有在测试过的情况下,才能正确地处理禁用分页作为切换回实模式的过程的一部分,这很可能是唯一已经测试过的方案。即使是物理CPU也可能在这个领域存在问题。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接