Page 1 of 2
Long mode triple fault
Posted: Fri Sep 24, 2010 1:46 pm
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!
Re: Long mode triple fault
Posted: Fri Sep 24, 2010 2:11 pm
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?
Re: Long mode triple fault
Posted: Fri Sep 24, 2010 2:22 pm
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?
Re: Long mode triple fault
Posted: Fri Sep 24, 2010 2:27 pm
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!
Re: Long mode triple fault
Posted: Fri Sep 24, 2010 2:42 pm
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.
Re: Long mode triple fault
Posted: Fri Sep 24, 2010 2:49 pm
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!
Re: Long mode triple fault
Posted: Fri Sep 24, 2010 2:54 pm
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.
Re: Long mode triple fault
Posted: Fri Sep 24, 2010 3:04 pm
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
Re: Long mode triple fault
Posted: Fri Sep 24, 2010 3:53 pm
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!
Re: Long mode triple fault
Posted: Sat Sep 25, 2010 2:19 am
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.
Re: Long mode triple fault
Posted: Sat Sep 25, 2010 5:00 am
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!
Re: Long mode triple fault
Posted: Sat Sep 25, 2010 5:38 am
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:
Re: Long mode triple fault
Posted: Sat Sep 25, 2010 6:10 am
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).
Re: Long mode triple fault
Posted: Sat Sep 25, 2010 6:50 am
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
Re: Long mode triple fault
Posted: Sat Sep 25, 2010 7:28 am
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 ¬¬