0

我正在尝试编写简单的引导加载程序。

我想以实模式加载 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 和切换到保护模式的更多详细信息?

谢谢

4

1 回答 1

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 手册中进行了描述。您所要做的就是了解这些内容并注意您正在做的事情以避免出现上述两个错误。

于 2012-06-16T05:40:13.617 回答