天天看點

自己動手寫核心(第1課:引導程式)(原創)

第1課:引導程式

聲明:轉載請保留:

譯者:http://www.cppblog.com/jinglexy(新的部落格位址是:http://blog.csdn.net/jinglexy)

原作者:xiaoming.mo at skelix dot org

MSN & Email: jinglexy at yahoo dot com dot cn

目标:使"system"從軟碟啟動,并列印"Hello World!" 下載下傳源程式

記憶體尋址

處理器以‘位元組’管理和通路記憶體,每個位元組都有獨立的位址,即實體位址。有兩種位址映射方式:分段和分頁,skelix核心中都用到了。

段對于我們來說再熟悉不過了,先回顧一下dos時期的段吧。它是一個16位的寄存器,是以最多可以直接通路2^16位元組的記憶體,即64K。這對應用程式來說太少了,于是Intel使用Segment:Offset結合方式來表示一個虛拟位址。段寄存器左移4位加上偏移就得到實際的實體位址了。例如,0x 7c 00:0x0189表示實體位址0x 7c 189,而不是0x 7c 000189。計算過程如下:

  7C 000

+ 0189

-------

  7C 189

現在我們來計算最大可以通路的位址:FFFF:FFFF

 FFFF0

+ FFFF

-------

10FFEF

這個範圍是 1M + 65519 bytes, 因為在80386中使用了20位位址線,是以可以額外多通路65519個位元組虛拟位址,例如位址0x100010被映射到位址0x10,通路這兩個位址是等價的。

表示同一個實體位址有多種方式,例如 07C 0:0000和0000: 7C 00 就是一樣的。

另一個概念是線性位址,這個是32位位址,隻有當分頁機制開啟時才有效,文章後面會提到它。

引導過程

當系統上電或RESET時,處理器将執行一些列的初始化,寄存器被設定成非預知狀态,并且cpu處于實模式。也許你想知道cpu是怎樣設定segment:offset為實體位址FFFF0的(0xf000:0xfff0就是bios入口位址),這是因為cs寄存器有一個非可見部分,它儲存了ffff:0000位址,并且cs在初始化時會被裝入f000值。此後以正常方式使用它。當bois取得控制權後,根據使用者配置(從軟驅,硬碟,或cdrom)中讀取第一個sector到 00007C 00,并跳轉到該位址執行(就是引導程式bootstrap)。在bootstrap中我們可以使用bios中斷,但是進入kernel後就不能再使用了。

程式一:使用as和ld的範例

你可以在下載下傳源程式的01/first.cry/bootsect.s

        .text              .text表示代碼段

        .globl             start表示start可以用作外部符号

        .code16            GCC預設使用32位位址和操作數,這裡告訴它使用16位

start:

        jmp      start     死循環

.org    0x1fe,   0x90      .org NEW-LC, FILL,說明:這裡填充0x90,是nop指令的機器碼

.word   0xaa55

講解:.org指令訓示下一個資料位址,為了編譯這個程式,我們寫了一個Makefile,總不能老是敲指令吧,呵呵。

網絡上可以找到很多寫Makefile的資料,編譯選項才是我們關注的焦點。

01/first.cry/Makefile

AS=as                     gcc彙編工具

LD=ld                     gcc連接配接器

.s.o:

    ${AS} -a $< -o $*.o >$*.map

all: final.img

final.img: bootsect

    mv bootsect final.img

bootsect: bootsect.o

    ${LD} --oformat binary -N -e start -Ttext 0x 7c 00 -o bootsect $<

講解:ld可以被配置為支援多于一種的目标檔案. binary表示沒有程式頭和其他資訊,僅僅是一些裸資料。如果沒有這個選項,将被預設連結為elf格式。-N把text和data節設定為可讀寫。-Ttext将text節起始位址設定為0x 7c 00(在jmp和資料引用等重定位連結時會用到這個參考值),所有的引用位址都是在 7c 00這個位址上加出來的。-e選項指定程式入口點

現在我們運作make指令編譯一下:

[[email protected]~/source/os/skelix/01/first.cry]$ ls

bootsect.s  COPYING  Makefile

[[email protected]~/source/os/skelix/01/first.cry]$ make

as -a bootsect.s -o bootsect.o >bootsect.map

ld --oformat binary -N -e start -Ttext 0x 7c 00 -o bootsect bootsect.o

mv bootsect final.img

[[email protected]~/source/os/skelix/01/first.cry]$ ls

bootsect.map  bootsect.o  bootsect.s  COPYING  final.img  Makefile

[[email protected]~/source/os/skelix/01/first.cry]$

現在,我們啟動vmware,運作,載入軟驅映象檔案"final.img",我們得到一個黑屏,這是正确的,因為我們什麼也沒有做。

程式一:顯示 Hello World!

好了,上面的黑屏程式并不是太好玩,現在我們嘗試在上面列印"Hello World!"

01/hello.world/bootsect.s

        .text

        .globl  start

        .code16

start:

        jmp     code

msg:                                   使用jmp指令跳過該變量,這是我們為什麼在Makefile使用-N連結選項了

        .string "Hello World!/x0"

code:

        movw    $0xb800,%ax

        movw    %ax,    %es            es段設定成B800,如前所述,segment:offset位址映射方式,它指向B8000,

                                       這意味着第一個位元組位址是0(映射到B8000),屬性位元組是1(映射到B8001)

                                       B8001值設定為0x07可以将這個byte顔色設定為黑底白字。

        xorw    %ax,    %ax

        movw    %ax,    %ds

        movw    $msg,   %si            為movsb指令設定正确的si和di

        xorw    %di,    %di

        cld

        movb    $0x07,  %al            字的顔色

1:

        cmp     $0,    (%si)

        je      1f    

        movsb

        stosb

        jmp     1b

1:      jmp     1b

.org    0x1fe,  0x90

.word   0xaa55