Reputation: 87
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
Upvotes: 0
Views: 963
Reputation: 62068
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.
Upvotes: 1