I have the following assembly code to set up paging for long mode.
org 0x7e00
bits 32
mov eax, 0x08000008
mov cr3, eax
pml4t:
mov dword [0x8000], 0x0900000f
mov dword [0x8004], 0x0
pdpt:
mov dword [0x9000], 0x0a00000f
mov dword [0x9004], 0x0
pdt:
mov dword [0xa000], 0x0b00000f
mov dword [0xa004], 0x0
pt:
mov eax, 0x200
mov ebx, 0x0000000f
mov ecx, 0xb000
next_entry:
mov dword [ecx], ebx
add ecx, 0x4
mov dword [ecx], 0x0
add ebx, 0x1000000 ;add 4096 to the adress pointed to by ebx (the next physical page)
add ecx, 0x4
sub eax, 0x1
cmp eax, 0x0
jne next_entry
mov eax, cr4 ;enable PAE-paging
or eax, 1 << 5
mov cr4, eax
mov ecx, 0xC0000080 ;set long mode bit in EFER MSR
rdmsr
or eax, 1 << 8
wrmsr
mov eax, cr0 ;enable paging
or eax, 1 << 31
mov cr0, eax
halt:
hlt
jmp halt
The bootloader code before that will load this code at address 0x7e00. It will then jump in segment 0x8 to that address. I know this works properly since I tested it does.
I get the following error in Bochs stating that the physical address (0x7e91) of the hlt instruction is not found (found in the RIP register).
Booting from 0000:7c00
00017470660i[CPU0 ] CPU is in compatibility mode (active)
00017470660i[CPU0 ] CS.mode = 32 bit
00017470660i[CPU0 ] SS.mode = 16 bit
00017470660i[CPU0 ] EFER = 0x00000500
00017470660i[CPU0 ] | RAX=00000000e0000011 RBX=000000000000000f
00017470660i[CPU0 ] | RCX=00000000c0000080 RDX=0000000000000000
00017470660i[CPU0 ] | RSP=0000000000008000 RBP=0000000000000000
00017470660i[CPU0 ] | RSI=00000000000e0000 RDI=000000000000ffac
00017470660i[CPU0 ] | R8=0000000000000000 R9=0000000000000000
00017470660i[CPU0 ] | R10=0000000000000000 R11=0000000000000000
00017470660i[CPU0 ] | R12=0000000000000000 R13=0000000000000000
00017470660i[CPU0 ] | R14=0000000000000000 R15=0000000000000000
00017470660i[CPU0 ] | IOPL=0 id vip vif ac vm RF nt of df if tf SF zf af PF cf
00017470660i[CPU0 ] | SEG sltr(index|ti|rpl) base limit G D
00017470660i[CPU0 ] | CS:0008( 0001| 0| 0) 00000000 ffffffff 1 1
00017470660i[CPU0 ] | DS:0010( 0002| 0| 0) 00000000 ffffffff 1 1
00017470660i[CPU0 ] | SS:0010( 0005| 0| 0) 00000100 0000ffff 0 0
00017470660i[CPU0 ] | ES:0000( 0005| 0| 0) 00000000 0000ffff 0 0
00017470660i[CPU0 ] | FS:0000( 0005| 0| 0) 00000000 0000ffff 0 0
00017470660i[CPU0 ] | GS:0000( 0005| 0| 0) 00000000 0000ffff 0 0
00017470660i[CPU0 ] | MSR_FS_BASE:0000000000000000
00017470660i[CPU0 ] | MSR_GS_BASE:0000000000000000
00017470660i[CPU0 ] | RIP=0000000000007e91 (0000000000007e91)
00017470660i[CPU0 ] | CR0=0xe0000011 CR2=0x0000000000000080
00017470660i[CPU0 ] | CR3=0x0000000008000008 CR4=0x00000020
(0).[17470660] ??? (physical address not available)
00017470660e[CPU0 ] exception(): 3rd (14) exception with no resolution, shutdown status is 00h, resetting
00017470660i[SYS ] bx_pc_system_c::Reset(HARDWARE) called
00017470660i[CPU0 ] cpu hardware reset
00017470660i[APIC0 ] allocate APIC id=0 (MMIO enabled) to 0x0000fee00000
00017470660i[CPU0 ] CPUID[0x00000000]: 0000000b 756e6547 6c65746e 49656e69
00017470660i[CPU0 ] CPUID[0x00000001]: 00020652 00010800 0298e3ff bfebfbff
00017470660i[CPU0 ] CPUID[0x00000002]: 55035a01 00f0b2dd 00000000 09ca212c
00017470660i[CPU0 ] CPUID[0x00000003]: 00000000 00000000 00000000 00000000
00017470660i[CPU0 ] CPUID[0x00000004]: 1c004121 01c0003f 0000003f 00000000
00017470660i[CPU0 ] CPUID[0x00000005]: 00000040 00000040 00000003 00001120
00017470660i[CPU0 ] CPUID[0x00000006]: 00000007 00000002 00000001 00000000
00017470660i[CPU0 ] CPUID[0x00000007]: 00000000 00000000 00000000 00000000
00017470660i[CPU0 ] CPUID[0x00000008]: 00000000 00000000 00000000 00000000
00017470660i[CPU0 ] CPUID[0x00000009]: 00000000 00000000 00000000 00000000
00017470660i[CPU0 ] WARNING: Architectural Performance Monitoring is not implemented
00017470660i[CPU0 ] CPUID[0x0000000a]: 07300403 00000004 00000000 00000603
00017470660i[CPU0 ] CPUID[0x0000000b]: 00000000 00000000 00000000 00000000
00017470660i[CPU0 ] CPUID[0x80000000]: 80000008 00000000 00000000 00000000
00017470660i[CPU0 ] CPUID[0x80000001]: 00000000 00000000 00000001 28100000
00017470660i[CPU0 ] CPUID[0x80000002]: 65746e49 2952286c 726f4320 4d542865
00017470660i[CPU0 ] CPUID[0x80000003]: 35692029 55504320 20202020 4d202020
00017470660i[CPU0 ] CPUID[0x80000004]: 30323520 20402020 30342e32 007a4847
00017470660i[CPU0 ] CPUID[0x80000005]: 00000000 00000000 00000000 00000000
00017470660i[CPU0 ] CPUID[0x80000006]: 00000000 00000000 01006040 00000000
00017470660i[CPU0 ] CPUID[0x80000007]: 00000000 00000000 00000000 00000100
00017470660i[CPU0 ] CPUID[0x80000008]: 00003028 00000000 00000000 00000000
00017470660i[CPU0 ] CPU Features supported:
00017470660i[CPU0 ] x87
00017470660i[CPU0 ] 486ni
00017470660i[CPU0 ] pentium_ni
00017470660i[CPU0 ] p6ni
00017470660i[CPU0 ] mmx
00017470660i[CPU0 ] debugext
00017470660i[CPU0 ] vme
00017470660i[CPU0 ] pse
00017470660i[CPU0 ] pae
00017470660i[CPU0 ] pge
00017470660i[CPU0 ] pse36
00017470660i[CPU0 ] mtrr
00017470660i[CPU0 ] pat
00017470660i[CPU0 ] sysenter_sysexit
00017470660i[CPU0 ] clflush
00017470660i[CPU0 ] sse
00017470660i[CPU0 ] sse2
00017470660i[CPU0 ] sse3
00017470660i[CPU0 ] ssse3
00017470660i[CPU0 ] sse4_1
00017470660i[CPU0 ] sse4_2
00017470660i[CPU0 ] popcnt
00017470660i[CPU0 ] mwait
00017470660i[CPU0 ] vmx
00017470660i[CPU0 ] smx
00017470660i[CPU0 ] longmode
00017470660i[CPU0 ] lm_lahf_sahf
00017470660i[CPU0 ] nx
00017470660i[CPU0 ] cmpxhg16b
00017470660i[CPU0 ] rdtscp
00017470660i[CPU0 ] aes_pclmulqdq
00017470660i[CPU0 ] x2apic
00017470660i[PLUGIN] reset of 'pci' plugin device by virtual method
00017470660i[PLUGIN] reset of 'pci2isa' plugin device by virtual method
00017470660i[PLUGIN] reset of 'cmos' plugin device by virtual method
00017470660i[PLUGIN] reset of 'dma' plugin device by virtual method
00017470660i[PLUGIN] reset of 'pic' plugin device by virtual method
00017470660i[PLUGIN] reset of 'pit' plugin device by virtual method
00017470660i[PLUGIN] reset of 'vga' plugin device by virtual method
00017470660i[PLUGIN] reset of 'floppy' plugin device by virtual method
00017470660i[PLUGIN] reset of 'acpi' plugin device by virtual method
00017470660i[PLUGIN] reset of 'hpet' plugin device by virtual method
00017470660i[PLUGIN] reset of 'ioapic' plugin device by virtual method
00017470660i[PLUGIN] reset of 'keyboard' plugin device by virtual method
00017470660i[PLUGIN] reset of 'harddrv' plugin device by virtual method
00017470660i[PLUGIN] reset of 'pci_ide' plugin device by virtual method
00017470660i[PLUGIN] reset of 'unmapped' plugin device by virtual method
00017470660i[PLUGIN] reset of 'biosdev' plugin device by virtual method
00017470660i[PLUGIN] reset of 'speaker' plugin device by virtual method
00017470660i[PLUGIN] reset of 'extfpuirq' plugin device by virtual method
00017470660i[PLUGIN] reset of 'parallel' plugin device by virtual method
00017470660i[PLUGIN] reset of 'serial' plugin device by virtual method
00017470660i[PLUGIN] reset of 'gameport' plugin device by virtual method
00017470660i[PLUGIN] reset of 'iodebug' plugin device by virtual method
00017470660i[PLUGIN] reset of 'usb_uhci' plugin device by virtual method
Next at t=17470661
(0) [0x0000fffffff0] f000:fff0 (unk. ctxt): jmpf 0xf000:e05b ; ea5be000f0
I set one entry per table until I reach the last table which contains 512 entries. It should identity map 2MB of memory.
As shown in the debugger CR3 is 0x0000000008000008
which references address 0x8000 in memory. As stated in Intel's documentation (see https://www.intel.com/content/dam/www/public/us/en/documents/manuals/64-ia-32-architectures-software-developer-vol-3a-part-1-manual.pdf in the 4th chapter about IA-32e paging)
CR3 bits
Bit Contents
Position(s)
2:0 Ignored
3 (PWT) Page-level write-through
4 (PCD) Page-level cache disable
11:5 Ignored
M–1:12 Physical address of the 4-KByte aligned PML4 table used for linear-address translation
63:M Reserved (must be 0)
Note: M is 52
In CR3 all bits are zero except bit 3 so I enabled Write-Through. The address of the PML4 table starts at the 12th bit and the address found is 0x8000.
If I use xp /512bx 0x8000
in Bochs debugger it shows me the memory at the first table (0x8000) it reads:
0x0000000000008000 <bogus+ 0>: 0x0f 0x00 0x00 0x09 0x00 0x00 0x00 0x00
The result is 0f 00 00 09 00 00 00 00
in little endian which translates to 00 00 00 00 09 00 00 0f
in readable big endian.
The format is similar to CR3 for the entry (address starting at the 12th bit). The address referenced here is 0x9000.
If I use xp /512bx 0x9000
in Bochs debugger it shows me the memory at the second table (0x9000) it reads:
0x0000000000009000 <bogus+ 0>: 0x0f 0x00 0x00 0x0a 0x00 0x00 0x00 0x00
The result is 0f 00 00 0a 00 00 00 00
in little endian which translates to 00 00 00 00 0a 00 00 0f
in big endian.
The address referenced is 0xa000.
If I use xp /512bx 0xa000
in Bochs debugger it shows me the memory at the third table (0xa000) it reads:
0x000000000000a000 <bogus+ 0>: 0x0f 0x00 0x00 0x0b 0x00 0x00 0x00 0x00
The result is 0f 00 00 0b 00 00 00 00
in little endian which translates to 00 00 00 00 0b 00 00 0f
in big endian.
The address referenced is 0xb000.
If I use xp /4096bx 0xb000
in Bochs debugger it shows me the memory at the last table (0xb000) it reads:
<bochs:5> xp /4096bx 0xb000
[bochs]:
0x000000000000b000 <bogus+ 0>: 0x0f 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x000000000000b008 <bogus+ 8>: 0x0f 0x00 0x00 0x01 0x00 0x00 0x00 0x00
0x000000000000b010 <bogus+ 16>: 0x0f 0x00 0x00 0x02 0x00 0x00 0x00 0x00
0x000000000000b018 <bogus+ 24>: 0x0f 0x00 0x00 0x03 0x00 0x00 0x00 0x00
0x000000000000b020 <bogus+ 32>: 0x0f 0x00 0x00 0x04 0x00 0x00 0x00 0x00
0x000000000000b028 <bogus+ 40>: 0x0f 0x00 0x00 0x05 0x00 0x00 0x00 0x00
0x000000000000b030 <bogus+ 48>: 0x0f 0x00 0x00 0x06 0x00 0x00 0x00 0x00
0x000000000000b038 <bogus+ 56>: 0x0f 0x00 0x00 0x07 0x00 0x00 0x00 0x00
0x000000000000b040 <bogus+ 64>: 0x0f 0x00 0x00 0x08 0x00 0x00 0x00 0x00
The first entry is 0x0f 0x00 0x00 0x00 0x00 0x00 0x00 0x00
which translates to 00 00 00 00 00 00 00 0f
in big endian. It references address 0x0. It means that a virtual address of 0x00 00 00 00 00 00 01 23
should map to 0x123 in physical RAM. It should be identity mapped for 2MB of memory.
The 8th entry is 0x0f 0x00 0x00 0x07 0x00 0x00 0x00 0x00
which translates to 00 00 00 00 07 00 00 0f
. This should reference a physical 4KB page starting at address 0x7000.
If I use address 0x7e91 it translates to 0x00 00 00 00 00 00 7e 91
. This address should now be virtual since paging was enabled. It references PLM4 entry 0 which points to 0x9000. It references PDPT entry 0 which points to 0xa000. It references PDT entry 0 which points to 0xb000. It references entry 7 (8th) of the page table which points to 0x7000. The last 12 bits (0xe91) are the offset in the page. The virtual address should thus be identity mapped because 0x7e91 translates to 0x7e91.
Questions are the following.
Why is my paging implementation not working?
Have I missed something with the bit ordering in memory (little endian vs big endian)?
Why does CR2 which should contain the virtual address causing the page fault contain 0x80?
Is there something I missed?
As stated in Intel's documentation ...
11:5 Ignored M–1:12 Physical address of ...
This description seems to be misleading:
In the early x86-32 CPUs, CR3 (and not bits [32:12] of CR3) of CR3 were the pointer to (the physical address of) the page directory table.
Because the low 12 bits of that address always had to be 0, some of these bits (PCD and PWT) were used for other purposes in later CPUs.
If this is still true for 64-bit mode, CR3=0x8000008
does not specify an address of 0x8000
, but an address of 0x8000000
.
User contributions licensed under CC BY-SA 3.0