首页 文章

从加载的段切换到保护模式

提问于
浏览
0

我正在尝试编写简单的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和切换到保护模式的更多细节吗?

谢谢

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手册中描述了所有必要的细节 . 所有你需要做的就是理解这些东西,并注意你正在做的事情,以避免上述两个错误 .

相关问题