I am trying to write simple bootloader.
I would like to load boot0 in real mode, jump to boot0 and load full kernel from there. Then switch to protected mode and execute kernel code.
So far I have:
;First segment loaded by BIOS:
bits 16
org 0
jmp 0x07c0:start
start:
mov ax, cs
mov ds, ax
mov es, ax
mov al, 0x03
mov ah, 0
int 0x10
mov si, welcome_msg
call print
mov ax, 0x500 ;load boot0 to 0x500
mov es, ax ;value should be in es
mov cl, 2 ;sector number to be loaded
mov al, 4 ;number of sectors to load
call loadsector
jmp 0x500:0000
loadsector:
mov bx, 0
mov dl, 0 ;load from floppy=0
mov dh, 0
mov ch, 0
mov ah, 2
int 0x13
jc error
ret
times 510 - ($-$$) db 0
dw 0xaa55
Next 4 segments as boot0:
bits 16
org 0
mov ax, cs
mov ds, ax
mov es, ax
mov ax, 0x7000
mov ss, ax
mov sp, ss
;Printing from tutorial
mov ax,0xb800 ; Load gs to point to video memory
mov gs,ax ; We intend to display a brown A in real mode
mov word [gs:80],0x0248 ; displaymov word [gs:0],0x641 ; display
mov word [gs:82],0x0145 ; displaymov word [gs:0],0x641 ; display
mov word [gs:84],0x034C ; displaymov word [gs:0],0x641 ; display
mov word [gs:86],0x044C ; displaymov word [gs:0],0x641 ; display
mov word [gs:88],0x054F ; displaymov word [gs:0],0x641 ; display
;load kernel system
mov ax, 0x2000
mov es, ax
mov cl, 6 ;after boot0 will be full kernel
mov al, 4 ;for now only 4 sectors
call loadsector ;load kernel
jmp protected_mode_run
loadsector:
mov bx, 0
mov dl, floppy
mov dh, 0
mov ch, 0
mov ah, 2
int 0x13
jc error
ret
protected_mode_run:
cli
lgdt [gdtr]
mov eax,cr0 ; The lsb of cr0 is the protected mode bit
or al,0x01 ; Set protected mode bit
mov cr0,eax ; Mov modified word to the control register
jmp codesel:go_pm
bits 32
go_pm:
mov ax,datasel
mov ds,ax ; Initialise ds & es to data segment
mov es,ax
mov ax,videosel ; Initialise gs to video memory
mov gs,ax
mov word [gs:0],0x741 ; Display white A in protected mode
spin: jmp spin ; Loop
;TODO: instead jump to loaded code here
bits 16
gdtr:
dw gdt_end-gdt-1 ; Length of the gdt
dd 0x500+gdt ; physical address of gdt
gdt:
nullsel equ $-gdt ; $->current location,so nullsel = 0h
gdt0: ; Null descriptor,as per convention gdt0 is 0
dd 0 ; Each gdt entry is 8 bytes, so at 08h it is CS
dd 0 ; In all the segment descriptor is 64 bits
codesel equ $-gdt ; This is 8h,ie 2nd descriptor in gdt
code_gdt: ; Code descriptor 4Gb flat segment at 0000:0000h
dw 0x0ffff ; Limit 4Gb bits 0-15 of segment descriptor
dw 0x0000 ; Base 0h bits 16-31 of segment descriptor (sd)
db 0x00 ; Base addr of seg 16-23 of 32bit addr,32-39 of sd
db 0x09a ; P,DPL(2),S,TYPE(3),A->Present bit 1,Descriptor
; privilege level 0-3,Segment descriptor 1 ie code
db 0x0cf ; Upper 4 bits G,D,0,AVL ->1 segment len is page
; granular, 1 default operation size is 32bit seg
; Lower nibble bits 16-19 of segment limit
db 0x00 ; Base addr of seg 24-31 of 32bit addr,56-63 of sd
datasel equ $-gdt ; ie 10h, beginning of next 8 bytes for data sd
data_gdt: ; Data descriptor 4Gb flat seg at 0000:0000h
dw 0x0ffff ; Limit 4Gb
dw 0x0000 ; Base 0000:0000h
db 0x00 ; Descriptor format same as above
db 0x092
db 0x0cf
db 0x00
videosel equ $-gdt ; ie 18h,next gdt entry
dw 3999 ; Limit 80*25*2-1
dw 0x8000 ; Base 0xb8000
db 0x0b
db 0x92 ; present,ring 0,data,expand-up,writable
db 0x00 ; byte granularity 16 bit
db 0x00
gdt_end:
times 2048 - ($-$$) db 0
Entering protected mode works fine when I try to do it from first segment loaded from BIOS. Every attempt to do this from loaded segment crashes at line "jmp codesel:go_pm"
Structure of a file is: 1 segment - init 4 segments - boot0 (loaded to 0x500 segment) 4 segments - kernel (loaded to 0x2000 segment)
I only changed "dd 0x500+gdt ; physical address of gdt" in GDT but it looks like not enough. Could you tell me what else should I change or provide any reference where I could read in more details about GDT and switching to protected mode?
Thanks