5
votes

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?

2
Are you trying to disable paging on an OS with paging already enabled? Is it your own OS or a well known OS (linux, Windows, MacOS)? You should absolutely not disable paging on a modern OS when it has already been enabled. To write to a physical address either use a facility provided by the OS (/dev/mem or Device\PhysicalMemory) or a function provided by the kernel.BTW, It seems (I'm not used to AT&T syntax) that you inverted value an address in your code.Neitsa
Actually that's bit 31 because bit 0 is the first bit.cadaniluk
Won't disabling paging cause a fault if that code is running from a page that has different physical and virtual addresses? Or rather, cause some other instruction to run, now that EIP is a physical address. Have you single-stepped this in bochs or something to see exactly what is going on when you get the fault?Peter Cordes
Also, I think the sane way would be to map the physical page into virtual memory somewhere, and then write to it. That's the boring way, though. The insane way certainly looks interesting.Peter Cordes
If the code, stack, GDT, IDT, all interrupt handlers and everything they might use is all identity mapped, then it'd be "safe" to disable and re-enable paging; but it'd also be slow (invalidate all TLB entries), and you'd have to wonder why you bothered enabling paging in the first place if everything is identity mapped.. ;-)Brendan

2 Answers

3
votes

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.

3
votes

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:

  1. Disable interrupts. A CLI instruction disables maskable hardware interrupts. NMI interrupts can be disabled with external circuitry.
  2. 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.