[SOLVED]Protected Mode: GDT issues

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.
Post Reply
mogana100
Posts: 6
Joined: Sat Sep 22, 2012 5:38 pm

[SOLVED]Protected Mode: GDT issues

Post by mogana100 »

Hi

I've got a few issues with my code. In theory, it opens Gate A20, sets up a GDT, and switches to Protected Mode.
Practically, it generates a General Protection Fault when attemping to do a far jump.

boot.asm

Code: Select all

[BITS 16]

org 0x7C00

mov ax, 0x2000
mov ss, ax
xor sp, sp

xor ax, ax
xor bx, bx
xor cx, cx
xor dx, dx

retry:
mov ah, 0
mov dl, [boot_device]
int 0x13
jc retry
mov ax, 0x3000
mov es, ax
mov ah, 0x02
mov al, [sectors_to_load]					;Sectors to Load
mov dl, [boot_device]						;Drive Number (1st Floppy: 0, 2nd Floppy: 1, 1st Harddisk: 0x80)
mov dh, 0x00								;Head 0
mov cx, 0x0002								;Cylinder 0, Sector 2

int 0x13
jc retry
jmp 0x3000:0x0000

boot_device db 0
sectors_to_load db 2

times 510-($-$$) db 0
db 0x55
db 0xAA
kernel.asm

Code: Select all

org 0x3000
;section .text
;[BITS 16]

init:
mov ax, 0x3000
mov ds, ax
mov es, ax

xor ax, ax
xor bx, bx
xor cx, cx
xor dx, dx

mainloop:
call enable_A20								;enable A20
cli
lgdt [GDT_ADDR]								;load GDT
xchg bx, bx
mov eax, cr0								;switch to Protected Mode
or eax, 0x0001
mov cr0, eax
xchg bx, bx
jmp 0x3000:asm_pm_main						;far jump to assembly kernel
cli											;if anything fails, suspend CPU forever
hlt

;;;;;;;;;;;;;;;;
;;  A20 Gate  ;;
;;;;;;;;;;;;;;;;

enable_A20:
cli       
call    wait65k
mov     al,0xAD
out     0x64,al
call    wait65k
mov     al,0xD0
out     0x64,al
call    wait65k
in      al,0x60
push    ax
call    wait65k
mov     al,0xD1
out     0x64,al
call    wait65k
pop     ax
or      al,2
out     0x60,al
call    wait65k
mov     al,0xAE
out     0x64,al
call    wait65k
sti
ret
 
;;;;;;;;;;;;;;;;
;; AUX CALLS  ;;
;;;;;;;;;;;;;;;;

print_hex_byte: 
push ax
shr al, 4
cmp al, 10
sbb al, 69h
das
mov ah, 0Eh
int 10h
pop ax
ror al, 4
shr al, 4
cmp al, 10
sbb al, 69h
das
mov ah, 0Eh
int 10h
ret

wait65k:
mov ax, 0xFFFF
.loop:
dec ax
jnz .loop
ret

[BITS 32]

asm_pm_main:
xor eax, eax
mov ax, 0x10
mov ds, ax
mov es, ax
mov ss, ax
xor eax, eax
mov fs, ax
mov gs, ax
mov esp, 0x200000
mov esi, message1
mov edi, 0xb8000
call memcpy

cli
hlt

memcpy:
lodsb
cmp eax, 0
je .done
stosb
jmp memcpy
.done:
ret

message1 db "Hello World", 0x0D, 0x0A, 0

;;;;;;;;;;;;;;;;;;;
;;      GDT      ;;
;;;;;;;;;;;;;;;;;;;
GDT_NULL:
dq 0										;NULL-Descriptor
GDT_KERNEL_CODE:
dw 0x0400									;Word 0: Limit 0:15
dw 0x0000									;Word 1: Base 0:15
dw 0x9A00									;Word 2: Access Byte [1001 1010: Present, Ring 0, Executable(Code Segment), own Level Only, Readable, Accessed] (High Byte) and Base 16:23 (Low Byte)
dw 0x00C0									;Word 3: Base Addr 24:31 (High Byte) and FLAGS [Granularity 4kiB (Paging Size), 32-Bit Protected Mode] (Low Byte, High Nibble) + Limit 16:19 (Low Byte, Low Nibble)
GDT_KERNEL_DATA:
dw 0x0400									;Word 0: Limit 0:15
dw 0x0000									;Word 1: Base 0:15
dw 0x9A00									;Word 2: Access Byte [1001 1010: Present, Ring 0, Executable(Code Segment), own Level Only, Readable, Accessed] (High Byte) and Base 16:23 (Low Byte)
dw 0x00C0									;Word 3: Base Addr 24:31 (High Byte) and FLAGS [Granularity 4kiB (Paging Size), 32-Bit Protected Mode] (Low Byte, High Nibble) + Limit 16:19 (Low Byte, Low Nibble)

GDT_ADDR:
dw GDT_ADDR - GDT_NULL - 1
dd GDT_NULL

times 1024-($-$$) db 0
According to the internal debugger of Bochs, the GDT isn't set up correctly (every entry is zero, base is zero, limit is 65536, so its obvious that somewhere is a serious bug)

As far as i can tell, there are no faulty offsets, but i'm not sure (im not a hardcore assembly programmer).
What I KNOW is that the bootloader, and enabling of Gate A20 works (testet in Bochs and Qemu, apart from that A20 is always enabled in bochs (at least in my version))

I already disassembled the code, viewed at the memory dump (Bochs), went through the code step by step, but i'm still stuck.
Last edited by mogana100 on Sun Sep 23, 2012 11:28 am, edited 1 time in total.
User avatar
Kazinsal
Member
Member
Posts: 559
Joined: Wed Jul 13, 2011 7:38 pm
Libera.chat IRC: Kazinsal
Location: Vancouver
Contact:

Re: Protected Mode: GDT issues

Post by Kazinsal »

Okay, you've got a few things wrong here, so I'm going to point them out to you and hope this fixes it:
  • ORG is (simplistically speaking) an offset from a segment base. Your kernel seems to be loaded to 3000:0000, so your ORG should be zero.
  • The "base" entry in the GDTR is actually a linear 32-bit address counting from the very bottom (0x00000000) of memory, so just putting GDT_NULL in there doesn't work. You need to compute the whole linear address of GDT_NULL, which is best done by using your segment registers and a 32-bit register (let's say EAX) to mimic how the x86 computes the linear address of real-mode segment:offset pairs (if you don't know how this is done you might want to read a bit more before continuing your journey on OS development!)
  • Your concept of segmentation and selectors in protected mode is partially wrong. "jmp 0x3000:asm_pm_main" will crash because the instruction attempts to fetch the selector at offset 0x3000 in the GDT. However you do seem to understand that 0x10 is your kernel data segment's selector -- ask yourself what your 32-bit kernel code segment is then!
I suggest you look into some of the stuff on the wiki like Bare Bones and whatever else may be linked from Getting Started. It might help answer some questions you may have about why stuff works a certain way or doesn't work at all.
mogana100
Posts: 6
Joined: Sat Sep 22, 2012 5:38 pm

Re: Protected Mode: GDT issues

Post by mogana100 »

Ok, I fixed org (now 0x0000)

Also, I think I fixed the base address of the GDT:

Code: Select all

cli											;clear interrupt flag
mov eax, 0x3000								;segment
shl eax, 4									;shift left 4 times (multiply by 16)
add eax, GDT_NULL							;add offset
mov [GDT_BASE], eax							;save base addr
lgdt [GDT_ADDR]								;load GDT
Is this approach correct?

About the far jump: yes, I was a bit confused, because I now have descriptors(duh), but now its fixed

Code: Select all

jmp 0x8:asm_pm_main							;far jump to assembly kernel
After these changes, it still doesn't work.

But now I know that somewhere is a offset problem:

Bochs says:
Bochs wrote: 00023049250i[BIOS ] Booting from 0000:7c00
00024034771i[CPU0 ] LOCK prefix unallowed (op1=0x53, modrm=0x00)
00024034773e[CPU0 ] read_RMW_virtual_dword_32(): segment limit violation
00024034773e[CPU0 ] interrupt(): gate descriptor is not valid sys seg (vector=0x0d)
00024034773e[CPU0 ] interrupt(): gate descriptor is not valid sys seg (vector=0x08)
00024034773i[CPU0 ] CPU is in protected mode (active)
00024034773i[CPU0 ] CS.mode = 32 bit
00024034773i[CPU0 ] SS.mode = 16 bit
00024034773i[CPU0 ] EFER = 0x00000000
00024034773i[CPU0 ] | EAX=60000011 EBX=00000000 ECX=00090000 EDX=00000000
00024034773i[CPU0 ] | ESP=0000fffc EBP=00000000 ESI=000e0000 EDI=0000ffac
00024034773i[CPU0 ] | IOPL=0 id vip vif ac vm RF nt of df if tf sf zf af PF cf
00024034773i[CPU0 ] | SEG sltr(index|ti|rpl) base limit G D
00024034773i[CPU0 ] | CS:0008( 0001| 0| 0) 00000000 00400fff 1 1
00024034773i[CPU0 ] | DS:3000( 0005| 0| 0) 00030000 0000ffff 0 0
00024034773i[CPU0 ] | SS:2000( 0005| 0| 0) 00020000 0000ffff 0 0
00024034773i[CPU0 ] | ES:3000( 0005| 0| 0) 00030000 0000ffff 0 0
00024034773i[CPU0 ] | FS:0000( 0005| 0| 0) 00000000 0000ffff 0 0
00024034773i[CPU0 ] | GS:0000( 0005| 0| 0) 00000000 0000ffff 0 0
00024034773i[CPU0 ] | EIP=00000099 (00000099)
00024034773i[CPU0 ] | CR0=0x60000011 CR2=0x00000000
00024034773i[CPU0 ] | CR3=0x00000000 CR4=0x00000000
00024034773e[CPU0 ] exception(): 3rd (13) exception with no resolution, shutdown status is 00h, resetting
00024034773i[SYS ] bx_pc_system_c::Reset(HARDWARE) called
According to this, it seems that I execute bogus code (because I don't use any LOCK-Prefixes).
The GDT is still not loaded properly: It contains only 16-Bit code descriptors (and limit is still 0xFFFF), but I can't spot any errors.
User avatar
Brendan
Member
Member
Posts: 8561
Joined: Sat Jan 15, 2005 12:00 am
Location: At his keyboard!
Contact:

Re: Protected Mode: GDT issues

Post by Brendan »

Hi,
mogana100 wrote:Also, I think I fixed the base address of the GDT:
I think you need to do the same for the base address of the code descriptor at offset 0x0008 in your GDT.


Cheers,

Brendan
For all things; perfection is, and will always remain, impossible to achieve in practice. However; by striving for perfection we create things that are as perfect as practically possible. Let the pursuit of perfection be our guide.
mogana100
Posts: 6
Joined: Sat Sep 22, 2012 5:38 pm

Re: Protected Mode: GDT issues

Post by mogana100 »

Hi
I think you need to do the same for the base address of the code descriptor at offset 0x0008 in your GDT.
Sorry, but I don't understand what you mean: do you want me to edit the descriptors, or the address of the far jump?
Anyway, I've tried both, and neither of them worked.
User avatar
Combuster
Member
Member
Posts: 9301
Joined: Wed Oct 18, 2006 3:45 am
Libera.chat IRC: [com]buster
Location: On the balcony, where I can actually keep 1½m distance
Contact:

Re: Protected Mode: GDT issues

Post by Combuster »

00024034771i[CPU0 ]00024034773i[CPU0 ] CS.mode = 32 bit
(...)
It contains only 16-Bit code descriptors (and limit is still 0xFFFF)
You're contradicting yourself. Maybe because you try to dump the GDT after a CPU reset?

Also, either proposed fix would have executed the right code, although the CPU and assembler would in one case disagree about the exact value of the instruction pointer. So that makes another point where you haven't actually done what you wrote.
"Certainly avoid yourself. He is a newbie and might not realize it. You'll hate his code deeply a few years down the road." - Sortie
[ My OS ] [ VDisk/SFS ]
mogana100
Posts: 6
Joined: Sat Sep 22, 2012 5:38 pm

Re: Protected Mode: GDT issues

Post by mogana100 »

Ok, I just recognized that I overlooked a very important part of the bochs error log, while I was trying to fix the base addresses: my code still crashed, so I assumed that nothing changed(noob mistake?). But, instead of a segment limit violation, it says:
load_seg_reg(SS): not writable data segment
EIP was A2, the part where I copy 0x10 into SS. In order to check my assumption, I put jmp $ just before mov ss, ax, and it didn't crash.
I don't know why, but i can set any segment register but SS to 0x10 (my data descriptor). Maybe because the stack grows downwards? But so far, i haven't seen any approach with a seperate descriptor for the stack.

EDIT:

IT WORKS!!!

The cause is so extremely embarrassing, but for Users with the same problem:
Look closely at the access bytes of the code and the data descriptor: they are equal: both descriptors are code descriptors.
This happened, because lazy as I am, i just copied the code selector, so I don't have to write the (nearly equal) data descriptor again, but I forgot to change the access byte.

ergo => fail

cheers
mogana100
Post Reply