Long mode triple fault

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
sevobal
Member
Member
Posts: 63
Joined: Sun Oct 22, 2006 7:11 am

Long mode triple fault

Post by sevobal »

Hey everyone,

I have a little problem and I hope someone here can help me. I've written a bootloader which loads the kernel.bin from a fat12 filesystem. The kernel setups protected mode and prints out some messages from protected mode. So far so good. Now I'm trying to activate long mode. These are the steps I made:

1. Activating PAE
2. Loading a valid PML4 Pointer into cr3
3. Activating IA32w mode
4. Activating paging

Everything is okay until here. But now I'm only in compatibility mode. So I guess I should load a valid 64-Bit GDT and jump into a 64-Bit code segment like this:

Code: Select all

; Step five: Jump into 64 Bit long mode code region
lgdt[gdtr_x64]
jmp SYS_CODE_SEL_X64:do_lm
But now the CPU fires up an exception (13) and triple faults. I've no guess what I'm doing wrong. Here is the bochs output of the error maybe it could help:

Code: Select all

00153228493i[BIOS ] Booting from 0000:7c00
00174057028e[CPU0 ] interrupt(long mode): IDT entry extended attributes DWORD4 TYPE != 0
00174057028e[CPU0 ] interrupt(long mode): IDT entry extended attributes DWORD4 TYPE != 0
00174057028i[CPU0 ] CPU is in compatibility mode (active)
00174057028i[CPU0 ] CS.d_b = 32 bit
00174057028i[CPU0 ] SS.d_b = 32 bit
00174057028i[CPU0 ] EFER   = 0x00000500
00174057028i[CPU0 ] | RAX=00000000e0000011  RBX=0000000000000000
00174057028i[CPU0 ] | RCX=00000000c0000008  RDX=00000000600b86d8
00174057028i[CPU0 ] | RSP=0000000000000ff8  RBP=0000000000000000
00174057028i[CPU0 ] | RSI=00000000000004a2  RDI=0000000000303000
00174057028i[CPU0 ] |  R8=0000000000000000   R9=0000000000000000
00174057028i[CPU0 ] | R10=0000000000000000  R11=0000000000000000
00174057028i[CPU0 ] | R12=0000000000000000  R13=0000000000000000
00174057028i[CPU0 ] | R14=0000000000000000  R15=0000000000000000
00174057028i[CPU0 ] | IOPL=0 id vip vif ac vm RF nt of df if tf sf zf AF PF cf
00174057028i[CPU0 ] | SEG selector     base    limit G D
00174057028i[CPU0 ] | SEG sltr(index|ti|rpl)     base    limit G D
00174057028i[CPU0 ] |  CS:0010( 0002| 0|  0) 00000500 ffffffff 1 1
00174057028i[CPU0 ] |  DS:0018( 0003| 0|  0) 00000500 ffffffff 1 1
00174057028i[CPU0 ] |  SS:0018( 0003| 0|  0) 00000500 ffffffff 1 1
00174057028i[CPU0 ] |  ES:0008( 0001| 0|  0) 00000000 ffffffff 1 1
00174057028i[CPU0 ] |  FS:0008( 0001| 0|  0) 00000000 ffffffff 1 1
00174057028i[CPU0 ] |  GS:0008( 0001| 0|  0) 00000000 ffffffff 1 1
00174057028i[CPU0 ] |  MSR_FS_BASE:0000000000000000
00174057028i[CPU0 ] |  MSR_GS_BASE:0000000000000000
00174057028i[CPU0 ] | RIP=0000000000000785 (0000000000000785)
00174057028i[CPU0 ] | CR0=0xe0000011 CR2=0x00000000600b86d8
00174057028i[CPU0 ] | CR3=0x00300000 CR4=0x00000020
00174057028i[CPU0 ] 0x0000000000000785>> mov byte ptr es:[edx], 0x64 : 26C60264
00174057028e[CPU0 ] exception(): 3rd (13) exception with no resolution, shutdown status is 00h, resetting
00174057028i[SYS  ] bx_pc_system_c::Reset(HARDWARE) called
00174057028i[CPU0 ] cpu hardware reset
What did I wrong?

Thank you!
smoothCoder
Member
Member
Posts: 43
Joined: Sat Aug 28, 2010 10:32 pm

Re: Long mode triple fault

Post by smoothCoder »

Yo have the CR2 not equal to 0. CR2 contain the addres of an page that has problem.

Why the CR3 reg have so high value? 3MB?!!

Where is suppoused to be you PML4 table?
sevobal
Member
Member
Posts: 63
Joined: Sun Oct 22, 2006 7:11 am

Re: Long mode triple fault

Post by sevobal »

My PML is located at the 3 MB mark. So this value is correct. I don't get the CR2 stuff. What do you mean...should I set it to zero or is it the error code of the exception?
smoothCoder
Member
Member
Posts: 43
Joined: Sat Aug 28, 2010 10:32 pm

Re: Long mode triple fault

Post by smoothCoder »

Ok. I have not programing calculator right now but simulating it on paper, it seems that you have shifted 12 bits to the left the base addres of the PML4 table. Don't do this. Just load the adress in the CR3 directly. So it must be 3000h

Cheers!
sevobal
Member
Member
Posts: 63
Joined: Sun Oct 22, 2006 7:11 am

Re: Long mode triple fault

Post by sevobal »

I tried 3000h but then the exceptions fires directly after enabling paging and not at the "jmp SYS_CODE_SEL_X64:do_lm. And as far as I know cr3 should contain the real physical address (0x300000) of the PML4 Table...or? I've seen it this way in a lot of tutorials, but maybe I've misunderstood something.
smoothCoder
Member
Member
Posts: 43
Joined: Sat Aug 28, 2010 10:32 pm

Re: Long mode triple fault

Post by smoothCoder »

If your PML4 table address is 300000h, then CR3 have to be 300000h, if you PML4 table is at 3000h, then CR3 have to have the value of 3000h. Often(inclusive myself) programers do the mistake to shift left the base address of the PML4 table the first time they enter long mode.
If this don't work for you, probably your table structure is bad. Try to print the table structure on screen. The "no shift" aply to all the tables in all the page structure.

I hope this help!
smoothCoder
Member
Member
Posts: 43
Joined: Sat Aug 28, 2010 10:32 pm

Re: Long mode triple fault

Post by smoothCoder »

Sorry I have not saw your first reply. I have not the Intel manual right now, but if your CR2 is not 0, then you have trouble with the page structure, this is why I am sure you page tables are bad.
sevobal
Member
Member
Posts: 63
Joined: Sun Oct 22, 2006 7:11 am

Re: Long mode triple fault

Post by sevobal »

Okay thank you for all your tipps. Tomorrow I'll try to fix my page tables. Too late today here (Germany). But the last thing for today I could do is to post my init_pml4 function:

Code: Select all

setup_pml4:
    mov edi, 0x300000

    mov eax, 0x30100F
    stosd
    mov ecx, 0x03FF
    xor eax, eax
    rep stosd

    mov eax, 0x30200F
    stosd
    mov ecx, 0x03FF
    xor eax, eax
    rep stosd

    mov ecx, 0x0200
    mov esi, 0x008B

    map.loop:
    mov eax, esi
    stosd
    xor eax, eax
    stosd
    add esi, 0x200000
    sub ecx, 1
    jnz map.loop

    ret
And after calling this function I set CR3 to 300000h.

So enough for today :D
smoothCoder
Member
Member
Posts: 43
Joined: Sat Aug 28, 2010 10:32 pm

Re: Long mode triple fault

Post by smoothCoder »

I thing you "map.loop" section is bad.
The first error I see is that you reuse the ECX. The second "stosd" reduces ECX to 0, then you are coding "sub ECX, 1". It is wrong to sub 1 from 0.

The second error I see is in your way to make virtual remaping of memory. You are mapping only two 2MB chunks of each 1GB region. But this can be just a consecuence of another error.

just try to change you code with this one:

Code: Select all

   setup_pml4:
    mov edi, 0x300000

    mov eax, 0x30100F
    stosd
    mov ecx, 0x03FF
    xor eax, eax
    rep stosd

    mov eax, 0x30200F
    stosd
    mov ecx, 0x03FF
    xor eax, eax
    rep stosd

    mov esi, 0x008B         ;Maybe you want to set this to 8F becouse of the previous tables
    
    mov [ds:edi], esi                    ;I hope DS is 0 here
    add edi, 4
    mov dword [ds:edi], 0
    add edi, 4

    mov ecx, 0x0200 - 1

    fill_1GB_table_with_2MB_enties:
    add esi, 0x0200000
    mov [edi], esi
    add edi, 4
    mov dword [ds:edi], 0
    add edi, 4
    dec ecx
    jnz fill_1GB_table_with_2MB_enties

    ret

Hope this works!
sevobal
Member
Member
Posts: 63
Joined: Sun Oct 22, 2006 7:11 am

Re: Long mode triple fault

Post by sevobal »

Thanks for your support. I still got a triple fault, but now the error output of bochs differs from the original one:

Code: Select all

00153228493i[BIOS ] Booting from 0000:7c00
00174057027e[CPU0 ] interrupt(long mode): unsupported gate type 1
00174057027e[CPU0 ] interrupt(long mode): IDT entry extended attributes DWORD4 TYPE != 0
00174057027i[CPU0 ] CPU is in long mode (active)
00174057027i[CPU0 ] CS.d_b = 16 bit
00174057027i[CPU0 ] SS.d_b = 32 bit
00174057027i[CPU0 ] EFER   = 0x00000500
00174057027i[CPU0 ] | RAX=00000000e000003c  RBX=0000000000000000
00174057027i[CPU0 ] | RCX=00000000c0000008  RDX=0000000000000100
00174057027i[CPU0 ] | RSP=0000000000001000  RBP=0000000000000000
00174057027i[CPU0 ] | RSI=000000000000048d  RDI=0000000000303000
00174057027i[CPU0 ] |  R8=0000000000000000   R9=0000000000000000
00174057027i[CPU0 ] | R10=0000000000000000  R11=0000000000000000
00174057027i[CPU0 ] | R12=0000000000000000  R13=0000000000000000
00174057027i[CPU0 ] | R14=0000000000000000  R15=0000000000000000
00174057027i[CPU0 ] | IOPL=0 id vip vif ac vm RF nt of df if tf sf zf af PF cf
00174057027i[CPU0 ] | SEG selector     base    limit G D
00174057027i[CPU0 ] | SEG sltr(index|ti|rpl)     base    limit G D
00174057027i[CPU0 ] |  CS:0010( 0002| 0|  0) 00000500 ffffffff 1 0
00174057027i[CPU0 ] |  DS:0018( 0003| 0|  0) 00000500 ffffffff 1 1
00174057027i[CPU0 ] |  SS:0018( 0003| 0|  0) 00000500 ffffffff 1 1
00174057027i[CPU0 ] |  ES:0008( 0001| 0|  0) 00000000 ffffffff 1 1
00174057027i[CPU0 ] |  FS:0008( 0001| 0|  0) 00000000 ffffffff 1 1
00174057027i[CPU0 ] |  GS:0008( 0001| 0|  0) 00000000 ffffffff 1 1
00174057027i[CPU0 ] |  MSR_FS_BASE:0000000000000000
00174057027i[CPU0 ] |  MSR_GS_BASE:0000000000000000
00174057027i[CPU0 ] | RIP=00000000000008a7 (00000000000008a7)
00174057027i[CPU0 ] | CR0=0xe0000011 CR2=0x00000000e000007f
00174057027i[CPU0 ] | CR3=0x00300000 CR4=0x00000020
00174057027i[CPU0 ] 0x00000000000008a7>> imul esi, dword ptr ds:[rax+r12+67], 0x66205550 : 426974204350552066
00174057027e[CPU0 ] exception(): 3rd (13) exception with no resolution, shutdown status is 00h, resetting
00174057027i[SYS  ] bx_pc_system_c::Reset(HARDWARE) called
00174057027i[CPU0 ] cpu hardware reset
Activating paging and beeing in legacy mode still works fine. The triple fault still happens at the far jmp. I hope the error output helps. There are a few differents between the original one and this one.
smoothCoder
Member
Member
Posts: 43
Joined: Sat Aug 28, 2010 10:32 pm

Re: Long mode triple fault

Post by smoothCoder »

I don't know what it could be.

I can only recommend to you to simplify you code as much as possible(once functional you can expand the code and make it more complex). I have not debug utilities, often the only way I have to debug is printing on screen GDTs, IDTs, Page-tables, PCI-headers etc. And often to determine which instruction caused the mal-function, I comment that instruction and test it this way or put some "jmp $" before it to see if problem persist.
Some questions to ask yourself:
Is you entry in the GDT correctly pointing to a 64-bit code segment? Is your code 64-bit code? Have you disabled the interruptions(in compatibility mode int-routines have to be 64-bit code)? You are activating user supervisor bits to all memory, have you tried simple with 03h/83h(present and read-write bits)?

Good luck!
sevobal
Member
Member
Posts: 63
Joined: Sun Oct 22, 2006 7:11 am

Re: Long mode triple fault

Post by sevobal »

Of course Interrupts are disabled. I've no idea, what's going wrong, so I'm posting my complete 2nd bootloader code. Maybe I've missed something:

Code: Select all

org 0x0
bits 16

    ; cs was set by the bootloader before jumping to this code

    cli                                      ; clear interrupts
    push cs                                  ; Insure DS=CS
    push cs
    pop ds
    pop es
    sti

;************************************************
; Activating 32 Bit Protected Mode
;************************************************
    mov ax, 0x0000
    mov es, ax
    mov word [es:0x0000], ds
    
    xor ebx,ebx
    mov bx,ds                                ; BX = segment
    shl ebx,4                                ; BX = "linear" address of segment base
    mov eax,ebx
    mov [gdt2 + 2],ax                        ; set base address of 32-bit segments
    mov [gdt3 + 2],ax
    mov [gdt4 + 2],ax                        ; set base address of 16-bit segments
    mov [gdt5 + 2],ax
    mov [gdt1_x64 + 2],ax
    mov [gdt2_x64 + 2],ax
    shr eax,16
    mov [gdt2 + 4],al
    mov [gdt3 + 4],al
    mov [gdt4 + 4],al
    mov [gdt5 + 4],al
    mov [gdt1_x64 + 4],al
    mov [gdt2_x64 + 4],al
		
    mov [gdt2 + 7],ah
    mov [gdt3 + 7],ah
    mov [gdt4 + 7],ah
    mov [gdt5 + 7],ah
    mov [gdt1_x64 + 7],ah
    mov [gdt2_x64 + 7],ah

    lea eax,[gdt + ebx]                      ; Now EAX contains the physical address of the x86 GDT
    mov [gdtr + 2],eax
    lea eax,[gdt_x64 + ebx]                  ; Now EAX contains the physical address of the x64 GDT
    mov [gdtr_x64 + 2],eax

    lea eax,[start_of_idt + ebx]             ; Now EAX contains the physical address of the x86 IDT
    mov [idt_pointer + 2],eax

    pushad                                   ; Save cursor line
    mov ah, 0x03
    mov bh, 0x00
    int 0x10

    mov byte [cursor_pos], dh

    mov ah, 0x02                             ; Hide cursor
    mov bh, 0x00
    mov dl, 0x00
    mov dh, 0x19
    int 0x10
    popad

    cli                                      ; Disable interrupts

    xor ax,ax
    mov es,ax
    mov edx,[es:0x0D * 4]                    ; INT 0Dh vector -> EDX
    mov [es:0x0D * 4 + 2],cs
    lea ax,[trap]
    mov [es:0x0D * 4],ax
  
; Store real mode instruction pointer and code segment. 
    mov ax,cs
    mov [RealModeCS],ax
    lea ax,[do_rm]
    mov [RealModeIP],ax

; Loading temporary GDT and IDT
    lgdt [gdtr]
    lidt [idt_pointer]

; Activating 32-Bit Protected Mode
    mov eax,cr0
    or al,1
    mov cr0,eax

    jmp SYS_CODE_SEL:do_pm                   ; FAR-JMP into 32-Bit code segment

; Real-mode interrupt 0Dh handler:
trap:	
    mov ax,0xB800
    mov fs,ax
    mov byte [fs:0x9C],'!'
    pop ax                                   ; point stacked IP beyond...
    add ax,5                                 ; ...the offending instruction
    push ax
    iret

;************************************************
;* Now we are in 32 Bit protected mode
;************************************************
[BITS 32]
do_pm:
    xor edi,edi
    xor esi,esi

; Load 32-Bit protected mode selectors
    mov ax, SYS_DATA_SEL
    mov ds, ax
    mov ss, ax

; Load linear code segment descriptor
    mov ax, LINEAR_SEL
    mov es, ax
    mov fs, ax
    mov gs, ax

; Stack
    mov esp, 0x00001000

; Check if the CPU supports 64 Bit long mode.
; If not the boot proccess gets canceled
    mov eax, 80000000h
    cpuid
    cmp eax, 80000000h
    jbe no_long_mode
    mov eax, 80000001h
    cpuid
    bt edx, 29
    jnc no_long_mode
    jmp long_ok

no_long_mode:
    hlt

long_ok:

; Enabling 64 Bit long mode in five steps
; Step one: Activating the physical address extension (PAE)

    mov eax, cr4
    bts eax, 5
    mov cr4, eax

; Step two: Load PML4
    call setup_pml4

    mov eax, 0x300000        ; Pointer to PML4 table (located at 3 MB mark).
    mov cr3, eax

; Step three: Enabling IA-32e mode
    mov ecx, 0xC0000080
    rdmsr
    bts eax, 8
    wrmsr

;Step four: Enabling paging
    mov eax, cr0
    bts eax, 31
    mov cr0, eax

; Step five: Jump into 64 Bit long mode code region
    lgdt[gdtr_x64]
    jmp SYS_DATA_SEL_X64:do_lm

;************************************************
;* RealMode Data
;************************************************
do_rm:

RealModeIP:
    dw 0

RealModeCS:
    dw 0

;************************************************
;* Includes (x86)
;************************************************
%include './hardware/cpu/gdt.inc'
%include './hardware/cpu/gdt_x64.inc'
%include './hardware/cpu/idt.inc'
%include "./hardware/cpu/pml4.inc"

;************************************************
;* Now we are in 64 Bit long mode
;************************************************
[BITS 64]

do_lm:
    hlt
gdt.inc:

Code: Select all

gdtr:	dw gdt_end - gdt - 1	; GDT limit
	dd gdt                  ; (GDT base gets set above)

;************************************************
; Global descriptor table (GDT) (just temporary)
;************************************************

; null descriptor
NULL_SEL	equ	00h
gdt:	dw 0			; limit 15:0
	dw 0			; base 15:0
	db 0			; base 23:16
	db 0			; type
	db 0			; limit 19:16, flags
	db 0			; base 31:24

; linear data segment descriptor
LINEAR_SEL	equ	$-gdt
	dw 0xFFFF		; limit 0xFFFFF
	dw 0			; base 0
	db 0
	db 0x92			; present, ring 0, data, expand-up, writable
        db 0xCF                 ; page-granular, 32-bit
	db 0

; code segment descriptor
SYS_CODE_SEL	equ	$-gdt
gdt2:   dw 0xFFFF               ; limit 0xFFFFF
	dw 0			; (base gets set above)
	db 0
	db 0x9A			; present, ring 0, code, non-conforming, readable
        db 0xCF                 ; page-granular, 32-bit
	db 0

; data segment descriptor
SYS_DATA_SEL	equ	$-gdt
gdt3:   dw 0xFFFF               ; limit 0xFFFFF
	dw 0			; (base gets set above)
	db 0
	db 0x92			; present, ring 0, data, expand-up, writable
  db 0xCF                 ; page-granular, 32-bit
	db 0


; a code segment descriptor that is 'appropriate' for real mode
; (16-bit, byte-granular, limit=0xFFFF)
REAL_CODE_SEL	equ	$-gdt
gdt4:   dw 0xFFFF
	dw 0			; (base gets set above)
	db 0
	db 0x9A			; present, ring 0, code, non-conforming, readable
	db 0			; byte-granular, 16-bit
	db 0

; a data segment descriptor that is 'appropriate' for real mode
; (16-bit, byte-granular, limit=0xFFFF)
REAL_DATA_SEL	equ	$-gdt
gdt5:   dw 0xFFFF
	dw 0			; (base gets set above)
	db 0
	db 0x92			; present, ring 0, data, expand-up, writable
	db 0			; byte-granular, 16-bit
	db 0
	    	
gdt_end:
gdt_x64.inc:

Code: Select all

gdtr_x64: dw gdt_end_x64 - gdt_x64 - 1	; GDT limit
          dq gdt_x64                    ; (GDT base gets set above)

;************************************************
; Global descriptor table for x64 mode (GDT)
;************************************************

gdt_x64:
    dw 0
    dw 0
    db 0
    db 0
    db 0
    db 0

LINEAR_SEL_X64 equ $-gdt_x64
    dw 0xFFFF
    dw 0x0000
    db 0x00
    db 10010010b
    db 0xAF
    db 0x00

SYS_CODE_SEL_X64 equ $-gdt_x64
gdt1_x64:
     dw 0xFFFF
     dw 0x0000
     db 0x00
     db 10011010b
     db 0xAF
     db 0x00
    
SYS_DATA_SEL_X64 equ $-gdt_x64
gdt2_x64:
    dw 0xFFFF
    dw 0x0000
    db 0x00
    db 10010010b
    db 0xAF
    db 0x00
    
gdt_end_x64:
smoothCoder
Member
Member
Posts: 43
Joined: Sat Aug 28, 2010 10:32 pm

Re: Long mode triple fault

Post by smoothCoder »

Try to code "gdt_x64 equ ($-second_stage_program_begining) + <place where you have loaded in memory the second stage loader>" instead "gdt_x64:"
This will give you the absolute address.

Ok. I see now that you are setting the GDT table address with lea instruction, but I can't see where you are calculating the physical address of the GDT pointer(gdtr_x64).
sevobal
Member
Member
Posts: 63
Joined: Sun Oct 22, 2006 7:11 am

Re: Long mode triple fault

Post by sevobal »

Okay. So the code gets loaded to 0050h:0000 so the physical address of the gdtr_x64 should be:

50h * 16 + gdtr_x64 = 500h + gdtr_x64

Code: Select all

lgdt[0x500 + gdtr_x64]
jmp SYS_DATA_SEL_X64:do_lm
And still triple fault :(
Last edited by sevobal on Sat Sep 25, 2010 7:28 am, edited 1 time in total.
smoothCoder
Member
Member
Posts: 43
Joined: Sat Aug 28, 2010 10:32 pm

Re: Long mode triple fault

Post by smoothCoder »

NASM syntax:

Code: Select all

[bits 16]
bootloader:
xor AX, AX
mov DS, AX
lgdt[GDT_descriptor]
mov EBX, CR0
or BL, 1
mov CR0, EBX
jmp protected_code_segment:((protected_code - bootloader)+7C00h)

[BITS 32]

protected_code:

mov dword [DS:1000h], 2003h
mov dword [DS:2000h]. 3003h
mov byte [DS:3000h], 83h
mov byte [DS:3008h], 83h      ;This entry is for you, I don't use yet memory above 2MB

mov EAX, 1000h
mov CR3, EAX

mov EAX, CR4
or AL, 32
mov CR4, EAX

mov ECX, 0c0000080h
rdmsr
or EAX, 100h
wrmsr

mov EDX, CR0
or EDX, 80000000h
mov CR0, EDX

jmp long_code_segment:long_code

[BITS 64]

long_code equ ($-bootloader)+7c00h
mov byte [0b8000h], "!"
jmp $

GDT:
dq 0

protected_code_segment equ $ - GDT
dw 0FFFFh
dw 0
db  0
db 10011000b
db 11001111b
db 0

long_code_segment equ $ - GDT
dw 0
dw 0
db 0
db 10011000b
db 10110000b
db 0
final_of_GDT:

GDT_descriptor equ ($ - bootloader)+7c00h
dw ((final_of_GDT-GDT)-1)
dq (GDT - bootloader) + 7C00h
Try to boot from this.
A am not copy-pasting this, so there can be erratum.

PD: Sorry, I missed the "cli", put it in the begining(after the "bootloader" label).

PD2:If somebody want to try this snippet, just have to put some align assembler directive to align properly the GDT. I forgot this too ¬¬
Last edited by smoothCoder on Sat Sep 25, 2010 8:10 am, edited 1 time in total.
Post Reply