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 セグメント - init 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ですか?

2番目の問題は次のとおりです。

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 00x500:0 でコードをロードして要求したためです。データ セグメントにも同じ問題があります。

他にも疑わしいことに気づきました:

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

SS=SP=0x7000 を使用してもよろしいですか? それが間違っているとは言えませんが (すべての計算を行ったわけではありません)、SS と SP は同じものではなく、同じ値をロードするのは確かに奇妙に見えます。

必要な詳細はすべて、Intel/AMD の CPU マニュアルに記載されています。あなたがしなければならないことは、上記の 2 つのようなバグを避けるために、そのことを理解し、何をしているのかに注意を払うことだけです。

于 2012-06-16T05:40:13.617 に答える