0
votes

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

1

1 Answers

1
votes

First problem is here:

gdtr:
dw      gdt_end-gdt-1 ; Length of the gdt
dd      0x500+gdt ; physical address of gdt

gdt:

0x500 is the real-mode segment, but where does that segment start in the physical memory? At 0x5000, right? So, why 0x500+gdt?

Second problem is here:

bits 16
org 0
...
jmp     codesel:go_pm

bits 32
go_pm:
...
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

You define the 32-bit code segment as starting at physical address 0, but your 32-bit code's address 0 corresponds to physical address 0x5000. Why? Because you asked for it with org 0 and loading your code at 0x500:0. You have the same problem [waiting to happen] with the data segment.

I've noticed something else suspicious:

mov ax, 0x7000
mov ss, ax
mov sp, ss

Are you sure you want SS=SP=0x7000? I can't say it's wrong (I didn't do all the math), but SS and SP are not the same thing and loading them with the same value certainly looks odd.

All the necessary details are described in Intel's/AMD's CPU manuals. All you have to do is understand that stuff and pay attention to what you're doing to avoid bugs like the above two.