Page 1 of 1

GPF in Simple Bootloader

Posted: Thu Jun 20, 2013 8:40 pm
by singerng
I'm starting to write a simple 3-stage bootloader. I've already successfully written stage 1, which loads stage 2 (found in the reserved sectors of the hard drive). Stage 2 is responsible for (right now) only switching into a stable protected mode environment (stage 3 will enable higher half and actually start my kernel). The trouble is, I've been following the OSDev Babystep tutorial as a model and no matter how I manipulate the code of my bootloader, it still doesn't seem to work (I hope I'm not missing something obvious). I consistently get a GPF (triple fault, of course) when executing the instruction

Code: Select all

jmp 0x08:pm_start
.

Here is my stage 2 code:

Code: Select all

[ORG 0x4000]
[BITS 16]

lgdt [gdtr]	; load gdt register
mov ebx, 0xBEEF0000

mov eax, cr0	; switch to protected mode by
or al,1			; setting the protected mode bit
mov cr0, eax	; in CR0

jmp 0x08:flush_gdt

[BITS 32]

flush_gdt:
	mov ebx, 0xBEEF0001
	mov ax, 0x10
	mov ebx, 0xBEEF0002
	mov ds, ax
	mov es, ax
	mov fs, ax
	mov gs, ax
	mov ss, ax
	mov bx, 0xBEE2
	ret

mov eax, 0xDEADBEEF
mov ebx, 0xDEADBEEF

jmp $

[BITS 16]

; GDT

gdtr:
	dw gdt_end - gdt - 1	; last byte in table
	dd gdt					; start of table
 
gdt					dd 0,0	; entry 0 is always unused
flatdesc			db 0xff, 0xff, 0, 0, 0, 10010010b, 11001111b, 0
gdt_end:
Here is the register dump/error report from Bochs:

Code: Select all

CPU is in protected mode (active)
00017825947i[CPU0 ] CS.mode = 16 bit
00017825947i[CPU0 ] SS.mode = 16 bit
00017825947i[CPU0 ] EFER   = 0x00000000
00017825947i[CPU0 ] | EAX=60000011  EBX=beef0000  ECX=00090010  EDX=00000080
00017825947i[CPU0 ] | ESP=0000ffd6  EBP=00000000  ESI=000e0000  EDI=0000ffac
00017825947i[CPU0 ] | IOPL=0 id vip vif ac vm RF nt of df if tf sf zf af PF cf
00017825947i[CPU0 ] | SEG sltr(index|ti|rpl)     base    limit G D
00017825947i[CPU0 ] |  CS:4000( 0004| 0|  0) 00040000 0000ffff 0 0
00017825947i[CPU0 ] |  DS:0000( 0005| 0|  0) 00000000 0000ffff 0 0
00017825947i[CPU0 ] |  SS:0000( 0005| 0|  0) 00000000 0000ffff 0 0
00017825947i[CPU0 ] |  ES:4000( 0005| 0|  0) 00040000 0000ffff 0 0
00017825947i[CPU0 ] |  FS:0000( 0005| 0|  0) 00000000 0000ffff 0 0
00017825947i[CPU0 ] |  GS:0000( 0005| 0|  0) 00000000 0000ffff 0 0
00017825947i[CPU0 ] | EIP=00000013 (00000013)
00017825947i[CPU0 ] | CR0=0x60000011 CR2=0x00000000
00017825947i[CPU0 ] | CR3=0x00000000 CR4=0x00000000
(0).[17825947] [0x0000000000040013] 4000:0000000000000013 (unk. ctxt): jmp far 0008:4018         ; ea18400800
UPDATE: I added the code to enable the A20 line and it still doesn't work.

Code: Select all

in al, 0x92
or al, 2
out 0x92, al

Re: GPF in Simple Bootloader

Posted: Fri Jun 21, 2013 8:41 am
by Combuster
Watch your segment registers. You are loading code somewhere different than where you are trying to load data from - a difference of four bits to be precise:

Code: Select all

[ORG 0x4000]
(...)
CS: (...) 00040000

Re: GPF in Simple Bootloader

Posted: Fri Jun 21, 2013 9:51 am
by singerng
I'm not sure I understand... I changed the [ORG] directive to 0x40000, but it still doesn't work. I'm sorry if this seems noobish, but I'm bad with segmentation :(.

Can you explain more clearly what I have to do to fix this problem?

Re: GPF in Simple Bootloader

Posted: Fri Jun 21, 2013 10:08 am
by Nessphoro
Address = segment * 16

Re: GPF in Simple Bootloader

Posted: Fri Jun 21, 2013 1:42 pm
by singerng
I've made a few modifications, but it still isn't working. I noticed that the address of the code was above 16-bits so I moved it down. I also recognized there may have been problems with the GDT given in the tutorial so I rewrote it, but it's still not working (failing on the jmp instruction). Here's the new code:

Code: Select all

[ORG 0x6000]
[BITS 16]

mov ax, 0x600
mov ds, ax

in al, 0x92
or al, 2
out 0x92, al

lgdt [gdtr]	; load gdt register
mov ebx, 0xBEEF0000

mov eax, cr0	; switch to protected mode by
or al,1			; setting the protected mode bit
mov cr0, eax	; in CR0

jmp 0x08:flush_gdt

gdtr:
	dw gdt_end - null_seg - 1	; last byte in table
	dd null_seg					; start of table
 
null_seg					db 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00	; entry 0 (null seg) is always unused
code_seg					db 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x9A, 0xCF, 0x00	; entry 1 (code seg)
data_seg					db 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x92, 0xCF, 0x00	; entry 2 (data seg)
gdt_end:

[BITS 32]

flush_gdt:
	mov ebx, 0xBEEF0001
	mov ax, 0x10
	mov ebx, 0xBEEF0002
	mov ds, ax
	mov es, ax
	mov fs, ax
	mov gs, ax
	mov ss, ax
	mov bx, 0xBEE2
	ret

mov eax, 0xDEADBEEF
mov ebx, 0xDEADBEEF

jmp $

Re: GPF in Simple Bootloader

Posted: Fri Jun 21, 2013 1:44 pm
by Mikemk
Nessphoro wrote:Address = segment * 16
Address = segment * 16 + offset

Re: GPF in Simple Bootloader

Posted: Sat Jun 22, 2013 12:49 am
by Nessphoro
m12 wrote:
Nessphoro wrote:Address = segment * 16
Address = segment * 16 + offset
That was unnecessary.

Re: GPF in Simple Bootloader

Posted: Sat Jun 22, 2013 6:41 am
by Combuster
[ORG 0x6000]
[BITS 16]

mov ax, 0x600
mov ds, ax
So now your code runs at 0x00006000 and expects itself to be at base+offset = 16 * 0x600 + 0x6000 = 2 * 0x6000 = 0x0000C000 ?

There are only constants involved in loading the GDT. You can calculate every value and every address for all accessed bytes in memory by hand and see if it works out before you even try running the code. After all, OSDev isn't about guesswork, so don't turn it into that.