我正在尝试编写简单的bootloader。
我想在实模式下加载boot0,跳转到boot0并从那里加载完整的内核。然后切换到保护模式并执行内核代码。
到目前为止,我有:
;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
接下来的4个段为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
当我尝试从BIOS加载的第一个段执行此操作时,进入保护模式正常。 每次从加载的段执行此操作的尝试都会在“jmp codesel:go_pm”
行崩溃文件的结构是: 1段 - 初始化 4段 - boot0(加载到0x500段) 4个段 - 内核(加载到0x2000段)
我只在GDT中更改了“dd 0x500 + gdt; gdt的物理地址”,但看起来还不够。 你能告诉我还有什么我应该改变或提供任何参考资料,我可以阅读有关GDT和切换到保护模式的更多细节吗?
由于
答案 0 :(得分:1)
第一个问题在这里:
gdtr:
dw gdt_end-gdt-1 ; Length of the gdt
dd 0x500+gdt ; physical address of gdt
gdt:
0x500是实模式段,但该段在物理内存中的起始位置是什么?在0x5000,对吗?那么,为什么0x500+gdt
?
第二个问题在这里:
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
您将32位代码段定义为从物理地址0开始,但您的32位代码的地址0对应于物理地址0x5000。为什么?因为您使用org 0
要求它并将代码加载到0x500:0。您对数据段有同样的问题[等待发生]。
我注意到其他可疑的事情:
mov ax, 0x7000
mov ss, ax
mov sp, ss
您确定要SS = SP = 0x7000吗?我不能说它是错的(我没有做所有的数学运算),但SS和SP不是一回事,用相同的值加载它们肯定看起来很奇怪。
英特尔/ AMD的CPU手册中描述了所有必要的细节。所有你需要做的就是理解这些东西,并注意你正在做的事情,以避免上述两个错误。