Linux 0 11學習總結 引導啟動程式部分

2021-06-13 11:46:02 字數 4417 閱讀 4859

linux-0.11學習總結——引導啟動程式部分

1.   bios啟動

intel設計cpu開機加電即進入16位的實模式下執行,cs的值預設為0xffff,ip的值預設為0x0000,這樣cs:ip指向了0xffff0的位址位置。bios程式位址只有8kb,範圍為0xfe000~0xfffff。

bios程式主要實現了以下功能:

a)        構建中斷向量表(0x00000~0x003ff)1kb記憶體空間;

b)       bios資料區(0x00400~0x004ff)256位元組

c)        在大約56kb以後的位置(0x0e2ce)構建中斷向量服務程式(8kb)

其中,中斷向量表中有256個中斷向量,每個中斷向量佔4個位元組,前兩個位元組為cs,後兩個位元組為ip。注意,此時的中斷與後面保護模式下的中斷不一樣。

最後,bios程式通過int 0x19中斷將軟碟機中第一扇區(512b)程式bootsect內容拷貝至0x07c00處。

2.   bootsect引導

在記憶體0x07c00處儲存的bootsect程式主要完成的功能有三點:1將自身程式複製到0x90000;2載入軟碟機4個扇區內容setup到0x90200處;3載入軟碟機240個扇區system模組到0x10000(此時時間較長,故需要在螢幕上顯示:loading system…)

該部分有乙個地方非常值得我學習:

jmpi go, initseg

go: mov ax, cs

這兩行**雖然簡短,但非常精妙。bootsect**是在一邊執行的過程中一邊複製到0x90000,增加go乙個偏移,使得跳轉之後的程式可以在新的位置接著繼續往下執行,而不是進入了乙個不斷複製的死迴圈。

bootsect借助bios中斷int 0x13,將setup.s與system模組分別載入到記憶體0x90200與0x00000中。最後,程式通過jmpi 0, setupseg完成跳轉。

3.    setup程式

該模組程式開始提取核心執行所需要的機器系統資料載入到0x90000~0x901fc位置,覆蓋了原來的bootsect部分,這對後面的main函式執行發揮重要的作用!

接下來,setup將system模組的程式從0x10000複製到0x00000位置。然後再設定中斷描述符表與全域性描述符表。具體**如下:

lidt    [idt_48]              ;load idt with 0,0

…gdt:

dw 0,0,0,0 ; dummy

dw 0x07ff ; 8mb - limit=2047 (2048*4096=8mb)

dw 0x0000 ; base address=0

dw 0x9a00 ; code read/exec

dw 0x00c0 ; granularity=4096, 386

data_descriptor:

dw 0x07ff ; 8mb - limit=2047 (2048*4096=8mb)

dw 0x0000 ; base address=0

dw 0x9200 ; data read/write

dw 0x00c0 ; granularity=4096, 386

idt_48:

dw 0 ; idt limit=0

dw 0,0 ; idt base=0l

gdt_48:

dw 0x800 ; gdt limit=2048, 256 gdtentries

dw 512+gdt,0x9 ; gdt base = 0x9***x

仔細閱讀該部分**,可以很好地理解intel對中斷描述符與全域性描述符的資料結構控制。首先,我們要知道idt共佔六個位元組,前兩個位元組為段長度,後四個位元組為段基位址;gdt共佔八個位元組,兩個位元組為段限長,四個位元組為段基位址,其他分別為特權級、段讀寫權等,如下圖:

全域性描述符表指向的是可共享的全域性段描述符,可以為資料段描述符,也可以為系統段描述符。總共有256個gdt,共256*8=2048個長度,故上述**0x800代表的就是段限長。而gdt定義的第乙個段描述符為0,0,0,0;第二個為0x00c09200000007ff代入上圖的描述符即可換算出段基址、段限長等資訊。

mov ax,0x0001      ;protected mode (pe) bit

lmsw ax ;this is it;

jmpi 0, 8 ;jmp offset 0 of segment 8 (cs)

lmsw是將0x0001賦值給cr0,其中控制暫存器cr0的最低位pe置高,設定處理器的工作方式為保護模式。jmpi一句,0為段內便宜,8為保護模式下的段選擇符,即1000,低兩位為特權級0,第三位0為gdt,若為1即ldt,1代表選擇gdt中的第一項,即上述所述的0x00c09200000007ff,可以看出段基址為0,故程式在此跳轉到0x00000處執行head.s的程式**。

4.   head.s程式執行

與前面不同的是,從head.s開始程式的彙編變成了at&t彙編,而之前的彙編均為intel彙編。其中system模組(共120kb,240個扇區)包括head.s的25kb+184b以及剩下的main函式部分。head程式主要完成的功能在於分頁機制以及開啟cr0的最高位pg。

最值得一提的是head.s程式最後對main函式的呼叫。由於這個main函式是作業系統的,呼叫後沒有返回,此處linux採用的一種巧妙的方法是利用ret呼叫main函式。

after_page_tables:

pushl $0 #these are the parameters to main :-)

pushl $0

pushl $0

pushl $l6 #return address for main, if it decides to.

pushl $__main

jmp setup_paging

setup_paging:

movl $1024*5,%ecx /* 5 pages - pg_dir+4 page tables */

xorl %eax,%eax

xorl %edi,%edi /* pg_dir is at 0x000 */

cld;rep;stosl

movl $pg0+7,_pg_dir /* set present bit/user r/w */

movl $pg1+7,_pg_dir+4 /* --------- " " --------- */

movl $pg2+7,_pg_dir+8 /* --------- " " --------- */

movl $pg3+7,_pg_dir+12 /* --------- " " --------- */

movl $pg3+4092,%edi

movl $0xfff007,%eax /* 16mb - 4096 + 7 (r/w user,p) */

std1: stosl /*fill pages backwards - more efficient :-) */

subl $0x1000,%eax

jge 1b

xorl %eax,%eax /* pg_dir is at 0x0000 */

movl %eax,%cr3 /* cr3 - page directory start */

movl %cr0,%eax

orl $0x80000000,%eax

movl %eax,%cr0 /* set paging (pg) bit */

ret /*this also flushes prefetch-queue */

可以看出在after_page_tables中事先將main函式的入口位址eip手動地壓到堆疊中,當下次進行ret返回時,程式將返回該指標位址到eip,從而實現了main函式的跳轉。

Linux0 11學習記錄

最近在學習linux0.11,遇到了一些編譯和執行的問題,好在最終能順利執行,在此作一下記錄,以方便自己和他人參考 我的編譯環境 linux bogon 3.16.0 4 686 pae 1 smp debian 3.16.39 1 deb8u2 2017 03 07 i686 gnu linux ...

Linux0 11學習筆記

目前狀態 中斷程式已設定,第乙個任務 init task已初始化 sched.c main 函式裡通過move to user mode 進入到task 0。目的 手工建立第二個任務 task 1,並和task 0不停切換。做法 在sched.c 建立第二個任務結構,所有值採用第乙個 union t...

Linux0 11核心引導啟動過程概述

linux0.11僅支援x86架構。它的核心引導啟動程式在資料夾boot內,共有三個彙編 檔案。按照啟動流程依次是 1 bootsect.s。boot是啟動引導的意思,sect即sector,是扇區的意思,二者合在一起啟動引導扇區。這是 磁碟引導程式。2 setup.s 3 head.s 前兩個匯程...