One of the reasons I don't like the tutorial that this comes from because it creates more headaches for people that use it when they want to enter protected mode. The top of the file is where all the badness starts:
Code: Select all
bits 16
start:
mov ax, 07C0h ;set data segment
mov ds, ax ;load number into register
By itself there is nothing wrong with this, BUT if you want to put an address in the GDT record it has to be relative to the beginning of physical memory and not the beginning of the segment. So this:
places the address of
gdt in gdt_desc. The address of
gdt is relative to the beginning of the segment 0x7c0 not the beginning of memory. To make it relative to the beginning of memory you have to add 0x7c00. You also make the mistake of making the length a byte rather than a word so it should be
dw gdt_end - gdt -1(the size also should have 1 subtracted from it) and
dw gdt should have originally been
dd gdt. Your
gdt_desc should look like:
Code: Select all
gdt_desc:
dw gdt_end - gdt - 1
dd gdt + 0x7c00
I have fixed up the address of
gdt by using
dd 0x7c00+gdt . Unfortunately you have the same problem with the FAR JMP getting into protected mode. It too needs fixing up so it would be
but then any addresses you use in
clear_pipe have to be adjusted. You also have this code that sets DS to 0:
Code: Select all
load_gdt:
cli
xor ax, ax
mov ds, ax
lgdt [gdt_desc]
You can't do this since
gdt_desc is relative to the beginning of the segment, not the beginning of physical memory.Since I've made the address adjustments above it should be:
Since you loaded the kernel at 0x1000:0x0000 and you are now in 32-bit protected mode at the point you do this:
Code: Select all
;JUMP TO EXECUTE KERNEL!
jmp 0x1000:0x0
you have the problem that 0x1000:0x0000 is a real mode segmented address.You just need a JMP to the linear address that 0x1000:0x0000 represents. The linear address of 0x1000:0x0000 is (0x1000<<4)+0x0000=0x10000. So what you really need is
Code: Select all
;JUMP TO EXECUTE KERNEL!
jmp 0x08:0x10000
You could have also used a near 32-bit jump but it has to be adjusted by the same value 0x7c00 again which would be
. All this adjusting of addresses is very messy though,and could have been avoided if we set ORG to 0x7c00 and DS to 0 at the start. Since 0x0000:0x7c00 and linear address 0x07c00 point to the same place we can avoid all the address fixups and do this in a more sane fashion:
Code: Select all
org 0x7c00 ; Use an ORG relative to beginning of physical memory
bits 16
start:
xor ax, ax ;set data segment to 0
; since we use ORG 0x7c00 ((0x0000<<4)+0x7c00)=0x07c00
mov ds, ax
mov ss, ax
mov sp, 0x7c00 ;set stack pointer just below bootloader out of the way at 0x0000:0x7c00
;set VESA graphic mode
mov ax, 0x4F02 ;set VBE
mov bx, 0x4103 ;set graphic mode 800x600 256 colours
int 0x10 ;start BIOS interrupt
;LOAD KERNEL
;reset floppy
mov ah, 0
mov dl, 0
int 13h ;call reset
;read floppy
mov ax, 1000h ;load kernel to 0x1000:0x0000
mov es, ax ; Set segment to 0x1000
mov bx, 0 ; and offset to 0
mov ah, 0x02 ;read function
mov al, 100 ;100 sectors
mov ch, 1 ;track 1
mov cl, 2 ;start sector is 2
mov dh, 0 ;head 0
mov dl, 0 ;floppy A
int 13h ;call read
jmp load_gdt
;global descriptor table
gdt:
gdt_null:
dq 0
gdt_code:
dw 0FFFFh
dw 0
db 0
db 10011010b
db 11001111b
db 0
gdt_data:
dw 0FFFFh
dw 0
db 0
db 10010010b
db 11001111b
db 0
gdt_end:
gdt_desc:
dw gdt_end - gdt - 1
dd gdt
;load gdt
load_gdt:
cli
lgdt [gdt_desc]
;jump into protected mode
mov eax, cr0
or eax, 1
mov cr0, eax
jmp 0x0008:clear_pipe
;NOW WE ARE IN PROTECTED MODE
bits 32
clear_pipe:
mov ax, 0x0010
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
mov esp, 090000h ;set stack pointer
;JUMP TO EXECUTE KERNEL!
jmp 0x10000
times 510-($-$$) db 0 ;Pad remainder of boot sector with 0s
dw 0xAA55 ;The standard PC boot signature