自己動手寫操作系統(一)--bootloader

 

一、bootloader代碼

#計算機啓動運行在實模式,主要經過以下幾個步驟進入保護模式

.text

#.code16表示16位代碼段

.code16

.global start

start:

#1、將ds、es和ss段寄存器均設置成cs段寄存器的值,並將棧頂指針esp指向0x7c00,棧向低地址增長。這步操作其實也可省略,因爲在16位代碼段中還用不到其他段寄存器,在需要使用的時候再初始

#化也不遲。

movw %cs,%ax

     movw %ax,%ds                                   # -> Data Segment

     movw %ax,%es                                   # -> Extra Segment

     movw %ax,%ss                                   # -> Stack Segment

movl $0x7C00,%esp

 

#2、關中斷,在後面我們在內存中會建立中斷向量表,所以事先關好中斷,防止在建表過程中來了中斷,所以事先屏蔽,防止這種情況產生。

cli

 

#3、打開地址線A20。實際上若我們使用qemu跑這個程序時,A20默認已經打開了,但爲了兼容性,最好還是手動將A20地址線打開,讀者可以試一試將打開A20代碼刪去後,在保護模式(32位代碼段#)下用回滾機制測試時是否仍然顯示字符

#8042(鍵盤控制器)端口的P21和A20相連,置1則打開

#0x64端口 讀:位1=1 輸入緩衝器滿(0x60/0x64口有給8042的數據)

#0x64端口 寫: 0xd1->寫8042的端口P2,比如位2控制P21 當寫時,0x60端口提供數據,比如0xdf,這裏面把P2置1

waitforbuffernull1:

#先確定8042是不是爲空,如果不爲空,則一直等待

xorl %eax,%eax

inb $0x64,%al

testb $0x2,%al

jnz waitforbuffernull1

#8042中沒有命令,則開始向0x64端口發出寫P2端口的命令

movb $0xd1,%al

outb %al,$0x64

waitforbuffernull2:

#再確定8042是不是爲空,如果不爲空,則一直等待

xorl %eax,%eax

inb $0x64,%al

testb $0x2,%al

jnz waitforbuffernull2

#向0x60端口發送數據,即把P2端口設置爲0xdf

movb $0xdf,%al

outb %al,$0x60

 

#在實模式下,不能通過BIOS中斷輸出字符來判斷A20是否真的打開。因爲16位代碼段只能進行16位尋址,所以你無法訪問到第20位,也就不可能判斷A20是否爲1了。

/* movl $0x1,%eax

movl %eax,0x136f20 # loop forever if it isn't

cmpl %eax,0x036f20

je disperror

dispsuccess:

movw $success      ,%ax

movw %ax        ,%bp                             # es:bp = 串地址

movw $12         ,%cx                              # cs = 串長度

movw $0x1301 ,%ax                              # ah=0x13:顯示字符串 ,al=0x1:顯示輸出方式

movw $0x000c ,%bx                              # bh=0 :第0頁,  bl=0xc :高亮 黑底紅字

movb $0           ,%dl                               # 在0行0列顯示

int $0x10                                # 調用BIOS提供的int服務0x10的0x13功能:顯示字符串  

#ret

disperror:*/

 

#4、加載gdtr,將內存中的gdt表結構讀入gdtr寄存器

lgdt gdt_48

 

#5、打開保護模式,將cr0的位0置爲1,一般而言BIOS中斷只在實模式下進行調用

movl %cr0,%eax

orl  $0x1,%eax

movl %eax,%cr0

 

#6、進入到32位代碼段。0x8代表段選擇子(16位)——0000000000001000,其中最後2爲代表特權級,linux內核只使用了2個特權級(0和3),00代表0特權級(內核級),倒數第3位的代表是gdt(全局描述符表)還是idt(局部描述#符表),0代表全局描述符表,前13位代表gdt的項數(第1項),代碼段。所以0x8代表特權級爲0(內核級)的全局代碼段,promode代表偏移地址。

ljmp $0x8,$promode

 

#7、32位代碼段

promode:

.code32

#注意、此時不能再像實模式下的16位代碼段一樣將ds、es、ss設置成cs的值了,因爲此時是32位保護模式,將代碼段和數據段分開了,儘管它們的基地址一樣,段限長一樣,即指向同一段地址空間,但 #兩個段的屬性不同,具體看IA32手冊中對gdt表中的描述符屬性。ds、es、ss均可看作數據段的內容,如果要設置的話,按如下代碼進行設置。同樣,此段程序中仍未用到這些段寄存器,所以這段代碼可#寫可不寫,0x10按照選擇子的描述——000000000001000,前13位代表gdt的項數(第2項),數據段。所以0x10代表特權級爲0(內核級)的全局數據段。

movw $0x10,%ax

     movw %ax,%ds                                   # -> Data Segment

     movw %ax,%es                                   # -> Extra Segment

     movw %ax,%ss                                   # -> Stack Segment

#8、用回滾機制判斷0x100000內存地址的數據和0x000000內存地址的數據是否相同,從而確定是否已經打開A20

movl $0x1,%eax

movl %eax,0x100200

cmpl 0x000200,%eax

#9、若兩個內存的數據不相同,則說明A20爲1,即A20地址線已經打開了,跳轉到display程序中在VGA中顯示字符'Y',若沒打開則跳轉到loop0死循環。

jne display

jmp loop0

#10、此時處於保護模式,不能調用BIOS中斷,只能通過VGA來顯示字符從而判斷A20是否真的打開。

display:

#11、0x18代表gdt的第3項,本程序中設置的是VGA段(顯示器內存)

movw $0x18,%ax

movw %ax,%gs

movl $((80*1+1)*2),%edi/*第11行,79列*/

movb $0x0c,%ah/*高四位表示黑底,低四位表示紅字*/

movb $'Y',%al/*顯示的字符*/

movw %ax,%gs:(%edi)

loop0:                                                          /* 無限循環 */

jmp loop0

/*success:

 .string "open A20 Success!"

error:

 .string "open A20 Error!"*/

#在內存中做一塊GDT表

.align 2

gdt:

.word 0,0,0,0

 

.word 0xFFFF   #第1項 CS  基地址爲0

.word 0x0000

.word 0x9A00

.word 0x00C0

 

.word 0xFFFF   #第2項 DS  基地址爲0

.word 0x0000

.word 0x9200

.word 0x00C0

 

.word 0xFFFF   #第3項 VGA 基地址位0xb8000

.word 0x8000

.word 0x920b

.word 0x0000

#將gdtr專用寄存器指向我們在內存中做的一塊GDT表,GDTR寄存器格式:48位(高32位地址+低16位限長),intel是小端方式

gdt_48:

#gdt表限長 sizeof(gdt)-1 低地址,放在gdtr的低字節

.word 0x1f

#gdt表基址  高地址,放在gdtr的高字節

.long gdt

.org    0x1fe, 0x00                                       /* 0x1fe=510,表示從ret後的位置開始,直到510處結束的代碼/數據空間,填寫0x00 */

.word 0xaa55                                              /* 合法的主引導扇區標識 */

 

二、makefile代碼

all:bootsector

bootsector.o:bootsector.S

as -o bootsector.o bootsector.S

bootsector:bootsector.o

ld  --oformat  binary -e start -Ttext 0x7c00 -o bootsector bootsector.o #bootmain.o

run:bootsector

qemu-system-i386 -fda bootsector

clean:

rm bootsector.o bootsector

 

三、安裝qemu環境,然後輸入以下命令

make run

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章