问题
I am trying to write directly to a physical memory location, so I am using an assembly function to first disable paging, write the value, and then re-enable paging, but for some reason a page fault is still triggered when trying to write the value.
As I understand it, in x86-32bit, paging is set on and off by flipping bit 32 in cr0, so here is my assembly function:
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
Is this the correct way to achieve what I am wanting to do? If so, why is a page fault still being triggered with the bit in cr0 flipped?
回答1:
The change in the CR0 register will become active when a jump instruction (far jump only?) is done.
Disabling the paging however is not a good idea: You have to guarantee that the code is located in 1:1 mapped memory and that interrupts are disabled.
If you use the stack you must also ensure that the stack is mapped 1:1.
It is much easier to modify the page tables in a way that the physical address in ecx is mapped to a virtual address and then to write to the virtual address.
回答2:
The Intel 64 and IA-32 Architectures Software Developer's Manual System Programming Guide describes how to disable paging as part the procedure for switching from protected mode back to real mode:
9.9.2 Switching Back to Real-Address Mode
The processor switches from protected mode back to real-address mode if software clears the PE bit in the CR0 register with a MOV CR0 instruction. A procedure that re-enters real-address mode should perform the following steps:
- Disable interrupts. A CLI instruction disables maskable hardware interrupts. NMI interrupts can be disabled with external circuitry.
If paging is enabled, perform the following operations:
- Transfer program control to linear addresses that are identity mapped to physical addresses (that is, linear addresses equal physical addresses).
- Insure that the GDT and IDT are in identity mapped pages.
- Clear the PG bit in the CR0 register.
- Move 0H into the CR3 register to flush the TLB.
It seems you've missed the last step. The TLB (translation lookaside buffer) is where the CPU caches page table entries and is still active after clearing the PG bit. You need to clear the TLB or the CPU will continue to use it.
Note that you'll have to reload CR3 before setting the PG bit again. Also because what you're doing is very unusual you may run into bugs and compatibility problems with your emulator. It may only be able to handle disabling paging correctly as part of the process of switching back to real mode, as that's likely the only scenario where it's been tested. Even physical CPUs may have issues in this area.
来源:https://stackoverflow.com/questions/33841890/disabling-paging-in-x86-32bit