Page 1 of 1

[solved] Long Mode: Page fault when jumping into 64-bit code

Posted: Mon Apr 28, 2008 3:41 am
by AndreaOrru
Excuse me for my (probably) bad english.

I writed a bootloader that should enter in 32-bit Protected Mode and then setup basic paging before enter in Long Mode.
But when I far jump into 64-bit code, I get a page fault (error 14, isn't it?).

This is the relevant section of my bochsout.txt:

Code: Select all

00002778367i[CPU0 ] >> jmp far 0008:00007cda : EADA7C00000800
00002778367e[CPU0 ] exception(): 3rd (14) exception with no resolution, shutdown status is 00h, resetting
And this is what the debugger says:

Code: Select all

<bochs:21> s
Next at t=2778371
(0) [0x00007cd3] 0018:0000000000007cd3 (unk. ctxt): jmp far 0008:00007cda     ; eada7c00000800
<bochs:22> s
Next at t=2778371
(0) [0xfffffff0] f000:fff0 (unk. ctxt): jmp far f000:e05b         ; ea5be000f0
Obviously, the 0008 selector is a 64-bit valid GDT entry (I think).
Here it is:

Code: Select all

        ; Code Segment descriptor:
        code64_desc:
            ; Limit = 0xFFFFF, Base = 0x00000000
            dw 0xFFFF       ; Limit = FFFFx
            dw 0x0000       ; Base = 0000xxxx
            db 0x00         ; Base = xxxx00xx
            db 10011010b    ; Access Byte = (Present, Highest Privilege, Executable, Read/Write)
            db 0xAF         ; 1st nibble: Flags = 1010 (Granularity, Long Mode)
                            ; 2nd nibble: Limit = xxxxF
            db 0x00         ; Base = xxxxxx00
The far jump is just after the mov cr0, as the AMD manual says:

Code: Select all

        ; Enable paging:
        mov eax, cr0            ; Read CR0.
        bts eax, 31             ; Set PE=1.
        mov cr0, eax            ; Write CR0.
        
        jmp 8:start_kernel      ; Jump to the kernel.
    
    
; Long Mode:
[BITS 64]
    
    ;*****************************************
    ;*          Jump to the kernel           *
    ;*****************************************
    start_kernel:
        mov rsp, 0x9FFF0    ; Set the 64-bit Stack Pointer.
        
        jmp 0x10000         ; Jump to the kernel.

And this is how I setup paging:

Code: Select all

    ;*****************************************
    ;*         Setup Paging Tables           *
    ;*****************************************
    paging:
        ; Clear the paging tables:
        xor eax, eax            ; Set EAX to 0x00000000 (value to write).
        mov edi, 0x8000         ; EDI = Address from which start to write.
        mov ecx, 0x1000         ; ECX = Number of dwords to write.
        rep stosd               ; Write the dwords.
        
        ; *** Map the first 8 MB of memory ***
        
        ; PML4 located at 0x8000:
        mov dword [0x8000], 0x9000 + 11b                    ; 1st PML4 Entry -> PDP table.
        ; PDP located at 0x9000:
        mov dword [0x9000], 10000h + 11b                    ; 1st PDP Entry -> PD table.
        ; PD located at 0x10000:
        mov dword [0x10000 + (8*0)], 0h + 110000011b        ; 1st PD Entry -> 0 MB
        mov dword [0x10000 + (8*1)], 200000h + 110000011b   ; 2nd PD Entry -> 2 MB
        mov dword [0x10000 + (8*2)], 400000h + 110000011b   ; 3rd PD Entry -> 4 MB
        mov dword [0x10000 + (8*3)], 600000h + 110000011b   ; 4th PD Entry -> 6 MB
        mov dword [0x10000 + (8*4)], 800000h + 110000011b   ; 5th PD Entry -> 8 MB
        ;          address   entry    frame      flags

What's wrong? Can you help me?


P.S. I'm using NASM 2.02 in a 32-bit environment, and Bochs 2.3.6 compiled with x86-64 support.

Posted: Mon Apr 28, 2008 3:54 am
by Combuster
I'm missing
1) the code that writes CR4 and EFER
2) the register dump you get upon crash

Posted: Mon Apr 28, 2008 3:58 am
by AndreaOrru
1)

Code: Select all

    ;*****************************************
    ;*        Enter 64-bit Long Mode         *
    ;*****************************************
    long_mode:
        ; Enable Physical-Address Extensions:
        mov eax, cr4            ; Copy CR4 value into EAX.
        bts eax, 5              ; Set the CR4.PAE bit on.
        mov cr4, eax            ; Put back EAX value into CR4.
        
        ; Load CR3 with the physical address of the PML4:
        mov eax, 0x8000         ; Pointer to PML4 table.
        mov cr3, eax            ; Initialize CR3 with PML4 base.

        ; Enable the long mode:
        mov ecx, 0xC0000080     ; EFER MSR number.
        rdmsr                   ; Read EFER.
        bts eax, 8              ; Set LME=1.
        wrmsr                   ; Write EFER.
        
        ; Enable paging:
        mov eax, cr0            ; Read CR0.
        bts eax, 31             ; Set PE=1.
        mov cr0, eax            ; Write CR0.
        
        jmp 8:start_kernel      ; Jump to the kernel.

2)

Code: Select all

00011040970i[BIOS ] Booting from 0000:7c00
00011111163i[CPU0 ] CPU is in compatibility mode (active)
00011111163i[CPU0 ] CS.d_b = 32 bit
00011111163i[CPU0 ] SS.d_b = 32 bit
00011111163i[CPU0 ] EFER   = 0x00000500
00011111163i[CPU0 ] | RAX=0000000080000011  RBX=0000000000000000
00011111163i[CPU0 ] | RCX=00000000c0000080  RDX=0000000000000000
00011111163i[CPU0 ] | RSP=000000000009fff0  RBP=0000000000000000
00011111163i[CPU0 ] | RSI=00000000ffff0000  RDI=000000000000c000
00011111163i[CPU0 ] |  R8=0000000000000000   R9=0000000000000000
00011111163i[CPU0 ] | R10=0000000000000000  R11=0000000000000000
00011111163i[CPU0 ] | R12=0000000000000000  R13=0000000000000000
00011111163i[CPU0 ] | R14=0000000000000000  R15=0000000000000000
00011111163i[CPU0 ] | IOPL=0 id vip vif ac vm RF nt of df if tf sf ZF af PF cf
00011111163i[CPU0 ] | SEG selector     base    limit G D
00011111163i[CPU0 ] | SEG sltr(index|ti|rpl)     base    limit G D
00011111163i[CPU0 ] |  CS:0018( 0003| 0|  0) 00000000 000fffff 1 1
00011111163i[CPU0 ] |  DS:0010( 0002| 0|  0) 00000000 000fffff 1 1
00011111163i[CPU0 ] |  SS:0010( 0002| 0|  0) 00000000 000fffff 1 1
00011111163i[CPU0 ] |  ES:0010( 0002| 0|  0) 00000000 000fffff 1 1
00011111163i[CPU0 ] |  FS:0010( 0002| 0|  0) 00000000 000fffff 1 1
00011111163i[CPU0 ] |  GS:0010( 0002| 0|  0) 00000000 000fffff 1 1
00011111163i[CPU0 ] |  MSR_FS_BASE:0000000000000000
00011111163i[CPU0 ] |  MSR_GS_BASE:0000000000000000
00011111163i[CPU0 ] | RIP=0000000000007cd3 (0000000000007cd3)
00011111163i[CPU0 ] | CR0=0x80000011 CR1=0x0 CR2=0x0000000000000080
00011111163i[CPU0 ] | CR3=0x00008000 CR4=0x00000020
00011111163i[CPU0 ] >> jmp far 0008:00007cda : EADA7C00000800
00011111163e[CPU0 ] exception(): 3rd (14) exception with no resolution, shutdown status is 00h, resetting
00011111163i[SYS  ] bx_pc_system_c::Reset(SOFTWARE) called
00011111163i[CPU0 ] cpu software reset
00011111163i[APIC0] local apic in CPU 0 initializing
00011111163i[     ] dbg: Quit

Posted: Mon Apr 28, 2008 6:53 am
by AndreaOrru
If it helps, I can attach all the bootloader's code.

Posted: Mon Apr 28, 2008 8:20 am
by AJ
Hi,

You are trying to do a far JMP which is invalid in 64 bit mode. As you do not have a valid 64 bit IDT, the CPU triple-faults when it tries raising the invalid opcode exception. Try pushing CS and RIP and doing a RETQ. Alternatively, push the desired SS, RSP, RFLAGS, CS and RIP and do an IRETQ.

Cheers,
Adam

Posted: Mon Apr 28, 2008 8:40 am
by AndreaOrru
Thanks for your help.

I get the same error.

But... is there something wrong in my method? Should I make things in another way? Normally, how is this far jump performed?

But all the sources of pratically all OSes I have seen use this kind of jump. Maybe is the fact that I'm assembling in a 32-bit environment? I don't think...

EDIT:
I have attached the bootloader's code, maybe you can give it a look.

Posted: Mon Apr 28, 2008 12:08 pm
by AJ
Sorry I don't have time to do any detailed reading through but some things to check may be:

* You jump to the kernel at 0x10000, but this seems to be where you are setting up your page directory (; PD located at 0x10000 )- is that right?
* CR2 is loaded, indicating a page fault.
* At this point, you have no 64 bit IDT (you can't as you aren't in long mode yet). That means the first exception generated will give you a triple fault. You need to check that not even a single exception is occurring. You can get Bochs to log debug events for CPU0, which may give more of a clue.

I'll get back after reading through more if I get a chance.

Cheers,
Adam

Posted: Mon Apr 28, 2008 12:18 pm
by AndreaOrru
* You jump to the kernel at 0x10000, but this seems to be where you are setting up your page directory (; PD located at 0x10000 )- is that right?
I'm a total idiot. This is one of those incredibly stupid errors.
Now, obviously, it works. Thank you very much! :D