linux head.s 詳解

來源:互聯網
上載者:User

上一篇是boot.s,這是head.s。關於head.s的作用為

head.s程式運行在32位保護模式下,其中主要包括初始設定的代碼、時鐘中斷int 0x08的過程代碼、系統調用中斷int
0x80的過程代碼以及任務A和任務B等的代碼和資料。其中初始設定工作主要包括:①重新設定GDT表;②設定系統定時器晶片;③重新設定IDT表並且設
置時鐘和系統調用中斷門;④移動到任務A中執行。

在虛擬位址空間中head.s程式的核心代碼和任務代碼分配圖4-41所示。實際上,本核心樣本中所有代碼和資料區段都對應到實體記憶體同一個地區
上,即從實體記憶體0開始的地區。GDT中全域程式碼片段和資料區段描述符的內容都設定為:基地址為0x0000;段限長值為0x07ff。因為顆粒度為1,所以
實際段長度為8MB。而全域顯示資料區段被設定成:基地址為0xb8000;段限長值為0x0002,所以實際段長度為8KB,對應到顯示記憶體地區上。


 
圖4-41  核心和任務在虛擬位址空間中的分配

兩個任務在LDT中程式碼片段和資料區段描述符的內容也都設定為:基地址為0x0000;段限長值為0x03ff,實際段長度為4MB。因此線上性地址空
間中這個"核心"的代碼和資料區段與任務的代碼和資料區段都從線性地址0開始並且由於沒有採用分頁機制,所以它們都直接對應物理地址0開始處。在head程式
編譯出的目標檔案中以及最終得到的磁碟片映像檔案中,代碼和資料的組織形式4-42所示。


 
圖4-42  核心映像檔案和記憶體中head代碼和資料分布


於處於特權級0的代碼不能直接把控制權轉移到特權級3的代碼中執行,但中斷返回操作是可以的,因此當初始化GDT、IDT和定時晶片結束後,我們就利用中
斷返回指令IRET來啟動第1個任務。具體實現方法是在初始堆棧init_stack中人工設定一個返迴環境,即把任務0的TSS段選擇符載入到任務寄存
器LTR中、LDT段選擇符載入到LDTR中以後,把任務0的使用者棧指標(0x17:init_stack)和代碼指標(0x0f:task0)以及標誌
寄存器值壓入棧中,然後執行中斷返回指令IRET。該指令會彈出堆棧上的堆棧指標作為任務0使用者棧指標,恢複假設的任務0的標誌寄存器內容,並且彈出棧中
代碼指標放入CS:EIP寄存器中,從而開始執行任務0的代碼,完成了從特權級0到特權級3代碼的控制轉移。

為了每隔10ms切換啟動並執行任務,head.s程式中把定時器晶片8253的通道0設定成每經過10ms就向中斷控制晶片8259A發送一個時鐘中
斷請求訊號。PC的ROM
BIOS開機時已經在8259A中把時鐘插斷要求訊號設定成中斷向量8,因此我們需要在中斷8的處理過程中執行任務切換操作。任務切換的實現方法是查看
current變數中當前運行任務號。如果current當前是0,就利用任務1的TSS選擇符作為運算元執行遠跳轉指令,從而切換到任務1中執行,否則
反之。

每個任務在執行時,會首先把一個字元的ASCII碼放入寄存器AL中,然後調用系統中斷調用int
0x80,而該系統調用處理過程則會調用一個簡單的字元寫屏子程式,把寄存器AL中的字元顯示在螢幕上,同時把字元顯示的螢幕的下一個位置記錄下來,作為
下一次顯示字元的螢幕位置。在顯示過一個字元後,任務代碼會使用迴圈語句延遲一段時間,然後又跳轉到任務代碼開始處繼續迴圈執行,直到運行了10ms而發
生了定時中斷,從而代碼會切換到另一個任務去運行。對於任務A,寄存器AL中將始終存放字元"A",而任務B運行時AL中始終存放字元"B"。因此在程式
運行時我們將看到一連串的字元"A"和一連串的字元"B"連續不斷地間隔顯示在螢幕上,4-43所示。


  
(點擊查看大圖)圖4-43  簡單核心啟動並執行螢幕顯示情況

圖4-43是我們在Bochs類比軟體中運行這個核心樣本的螢幕顯示情況。細心的讀者會發現,在圖中底端一行上顯示出一個字元"C"。這是由於PC
偶然產生了一個不是時鐘中斷和系統調用中斷的其他中斷。因為我們已經在程式中給所有其他中斷安裝了一個預設中斷處理常式。當出現一個其他中斷時,系統就會
運行這個預設中斷處理常式,於是就會在螢幕上顯示一個字元"C",然後退出中斷。

**********************************************************************************************************************************************

head.s

01 # head.s 包含32位保護模式初始化設定代碼、時鐘中斷代碼、系統調用中斷代碼和兩個任務的代碼。
02 # 在初始化完成之後程式移動到任務0開始執行,並在時鐘中斷控制下進行任務0和1之間的切換操作。
03 LATCH  = 11930   # 定時器初始計數值,即每隔10ms發送一次插斷要求。
04 SCRN_SEL  = 0x18   # 螢幕顯示記憶體段選擇符。
05 TSS0_SEL  = 0x20  # 任務0的TSS段選擇符。
06 LDT0_SEL  = 0x28 # 任務0的LDT段選擇符。
07 TSS1_SEL  = 0X30   # 任務1的TSS段選擇符。
08 LDT1_SEL  = 0x38  # 任務1的LDT段選擇符。
09 .text
10 startup_32:
11 # 首先載入資料區段寄存器DS、堆棧段寄存器SS和堆棧指標ESP。所有段的線性基地址都是0。
12movl $0x10,%eax   # 0x10是GDT中資料區段選擇符。
13mov %ax,%ds
14lss init_stack,%esp
15 # 在新的位置重新設定IDT和GDT表。
16call setup_idt # 設定IDT。先把256個中斷門都填預設處理過程的描述符。
17call setup_gdt  # 設定GDT。
18movl $0x10,%eax # 在改變了GDT之後重新載入所有段寄存器。
19mov %ax,%ds
20mov %ax,%es
21mov %ax,%fs
22mov %ax,%gs
23lss init_stack,%esp
24 # 設定8253定時晶片。把計數器通道0設定成每隔10ms向中斷控制器發送一個插斷要求訊號。
25movb $0x36, %al   # 控制字:設定通道0工作在方式3、計數初值採用二進位。
26movl $0x43, %edx  # 8253晶片控制字寄存器寫連接埠。
27outb %al, %dx
28movl $LATCH, %eax  # 初始計數值設定為LATCH(1193180/100),即頻率100Hz。
29movl $0x40, %edx  # 通道0的連接埠。
30outb %al, %dx # 分兩次把初始計數值寫入通道0。
31movb %ah, %al
32outb %al, %dx
33 # 在IDT表第8和第128(0x80)項處分別設定定時中斷門描述符和系統調用陷阱門描述符。
34movl $0x00080000, %eax # 中斷程式屬核心,即EAX高字是核心程式碼片段選擇符0x0008。
08h IRQ0: Implemented by the system timing component; called 18.2 times per second (once every 55 ms) by the PIC

35movw $timer_interrupt, %ax   # 設定定時中斷門描述符。取定時中斷處理常式地址。
36movw $0x8E00, %dx   # 中斷門類型是14(屏蔽中斷),特權級0或硬體使用。
37movl $0x08, %ecx   # 開機時BIOS設定的時鐘中斷向量號8。這裡直接使用它。
38lea idt(,%ecx,8), %esi   # 把IDT描述符0x08地址放入ESI中,然後設定該描述符。
39movl %eax,(%esi)
40movl %edx,4(%esi)
41movw $system_interrupt, %ax  # 設定系統調用陷阱門描述符。取系統調用處理常式地址。
42movw $0xef00, %dx   # 陷阱門類型是15,特權級3的程式可執行。
43movl $0x80, %ecx  # 系統調用向量號是0x80。
44lea idt(,%ecx,8), %esi # 把IDT描述符項0x80地址放入ESI中,然後設定該描述符。
45movl %eax,(%esi)
46movl %edx,4(%esi)
47 # 好了,現在我們為移動到任務0(任務A)中執行來操作堆棧內容,在堆棧中人工建立中斷返回時的情境。
48pushfl  # 複位標誌寄存器EFLAGS中的嵌套任務標誌。
將堆棧指標遞減 4(如果當前運算元大小屬性為 32),並將 EFLAGS 寄存器的全部內容壓入堆棧;或將堆棧指標遞減
2(如果當前運算元大小屬性為 16),並將 EFLAGS 寄存器的低 16 位(即 FLAGS 寄存器)壓入堆棧。(這些指令執行
POPF/POPFD 指令的逆操作)。將整個 EFLAGS 寄存器複製到堆棧時,不會複製 VM 與 RF 標誌(位 16 與
17);相反,在儲存到堆棧的 EFLAGS 映像中,這些標誌的值會被清除
49andl $0xffffbfff, (%esp)
50popfl
51movl $TSS0_SEL, %eax  # 把任務0的TSS段選擇符載入到任務寄存器TR。
52ltr %ax
53movl $LDT0_SEL, %eax   # 把任務0的LDT段選擇符載入到局部描述符表寄存器LDTR。
54lldt %ax  # TR和LDTR只需人工載入一次,以後CPU會自動處理。
55movl $0, current # 把當前任務號0儲存在current變數中。
56sti  # 現在開啟中斷,並在棧中營造中斷返回時的情境。
57pushl $0x17 # 把任務0當前局部空間資料段(堆棧段)選擇符入棧。
58pushl $init_stack  # 把堆棧指標入棧(也可以直接把ESP入棧)。
59pushfl  # 把標誌寄存器值入棧。
60pushl $0x0f # 把當前局部空間程式碼片段選擇符入棧。
61pushl $task0  # 把代碼指標入棧。
62iret # 執行中斷返回指令,從而切換到特權級3的任務0中執行。
63
64 # 以下是設定GDT和IDT中描述符項的子程式。
65 setup_gdt:   # 使用6位元組運算元lgdt_opcode設定GDT表位置和長度。
66lgdt lgdt_opcode
67ret
# 這段代碼暫時設定IDT表中所有256個中斷門描述符都為同一個預設值,均使用預設的中斷處理過程
# ignore_int。設定的具體方法是:首先在eax和edx寄存器對中分別設定好預設中斷門描述符的0~3
# 位元組和4~7位元組的內容,然後利用該寄存器對迴圈往IDT表中填充預設中斷門描述符內容。
68 setup_idt: # 把所有256個中斷門描述符設定為使用預設處理過程。
69lea ignore_int,%edx   # 設定方法與設定定時中斷門描述符的方法一樣。
70movl $0x00080000,%eax # 選擇符為0x0008。
71movw %dx,%ax
72movw $0x8E00,%dx   # 中斷門類型,特權級為0。
73lea idt,%edi
74mov $256,%ecx   # 迴圈設定所有256個門描述符項。
75 rp_idt: movl %eax,(%edi)
76movl %edx,4(%edi)
77addl $8,%edi
78dec %ecx
79jne rp_idt
80lidt lidt_opcode # 最後用6位元組運算元載入IDTR寄存器。
81ret
82
83 # 顯示字元子程式。取當前游標位置並把AL中的字元顯示在螢幕上。整屏可顯示80×25個字元。
84 write_char:
85push %gs   # 首先儲存要用到的寄存器,EAX由調用者負責儲存。
86pushl %ebx
87mov $SCRN_SEL, %ebx  # 然後讓GS指向顯示記憶體段(0xb8000)。
88mov %bx, %gs
89movl scr_loc, %bx  # 再從變數scr_loc中取目前字元顯示位置值。
90shl $1, %ebx  # 因為在螢幕上每個字元還有一個屬性位元組,因此字元
91movb %al, %gs:(%ebx) # 實際顯示位置對應的顯示記憶體位移地址要乘2。
92shr $1, %ebx # 把字元放到顯示記憶體後把位置值除2加1,此時位置值對
93incl %ebx  # 應下一個顯示位置。如果該位置大於2000,則複位成0。
94cmpl $2000, %ebx
95jb 1f
96movl $0, %ebx
97 1:movl %ebx, scr_loc   # 最後把這個位置值儲存起來(scr_loc),
98popl %ebx  # 並彈出儲存的寄存器內容,返回。
99pop %gs
100ret
101
102 # 以下是3個中斷處理常式:預設中斷、定時中斷和系統調用中斷。
103 # ignore_int是預設的中斷處理常式,若系統產生了其他中斷,則會在螢幕上顯示一個字元"C"。
104 .align 2
105 ignore_int:
106push %ds
107pushl %eax
108movl $0x10, %eax  # 首先讓DS指向核心資料區段,因為中斷程式屬於核心。
109mov %ax, %ds
110movl $67, %eax # 在AL中存放字元"C"的代碼,調用顯示程式顯示在螢幕上。
111call write_char
112popl %eax
113pop %ds
114iret
115
116 # 這是定時中斷處理常式。其中主要執行任務切換操作。
117 .align 2
118 timer_interrupt:
119push %ds
120pushl %eax
121movl $0x10, %eax   # 首先讓DS指向核心資料區段。
122mov %ax, %ds
123movb $0x20, %al   # 然後立刻允許其他硬體中斷,即向8259A發送EOI命令。
124outb %al, $0x20
125movl $1, %eax   # 接著判斷當前任務,若是任務1則去執行任務0,或反之。
126cmpl %eax, current
127je 1f
128movl %eax, current   # 若當前任務是0,則把1存入current,並跳轉到任務1
129ljmp $TSS1_SEL, $0  # 去執行。注意跳轉的位移值無用,但需要寫上。
130jmp 2f
131 1:movl $0, current  # 若當前任務是1,則把0存入current,並跳轉到任務0
132ljmp $TSS0_SEL, $0   # 去執行。
133 2:popl %eax
134pop %ds
135iret
136
137 # 系統調用中斷int 0x80處理常式。該樣本只有一個顯示字元功能。
138 .align 2
139 system_interrupt:
140push %ds
141pushl %edx
142pushl %ecx
143pushl %ebx
144pushl %eax
145movl $0x10, %edx  # 首先讓DS指向核心資料區段。
146mov %dx, %ds
147call write_char  # 然後調用顯示字元子程式write_char,顯示AL中的字元。
148popl %eax
149popl %ebx
150popl %ecx
151popl %edx
152pop %ds
153iret
154
155 /*********************************************/
156 current:.long 0  # 當前任務號(0或1)。
157 scr_loc:.long 0  # 螢幕當前顯示位置。按從左上方到右下角順序顯示。
158
159 .align 2
160 lidt_opcode:
161.word 256*8-1   # 載入IDTR寄存器的6位元組運算元:表長度和基地址。
162.long idt
163 lgdt_opcode:
164.word (end_gdt-gdt)-1   # 載入GDTR寄存器的6位元組運算元:表長度和基地址。
165.long gdt
166
167 .align 3
168 idt: .fill 256,8,0   # IDT空間。共256個門描述符,每個8位元組,佔用2KB。
169
170 gdt: .quad 0x0000000000000000  # GDT表。第1個描述符不用。
171.quad 0x00c09a00000007ff  # 第2個是核心程式碼片段描述符。其選擇符是0x08。
172.quad 0x00c09200000007ff  # 第3個是核心資料區段描述符。其選擇符是0x10。
173.quad 0x00c0920b80000002  # 第4個是顯示記憶體段描述符。其選擇符是0x18。
174.word 0x68, tss0, 0xe900, 0x0 # 第5個是TSS0段的描述符。其選擇符是0x20
175.word 0x40, ldt0, 0xe200, 0x0  # 第6個是LDT0段的描述符。其選擇符是0x28
176.word 0x68, tss1, 0xe900, 0x0   # 第7個是TSS1段的描述符。其選擇符是0x30
177.word 0x40, ldt1, 0xe200, 0x0   # 第8個是LDT1段的描述符。其選擇符是0x38
178 end_gdt:
179.fill 128,4,0 # 初始核心堆棧空間。
180 init_stack:  # 剛進入保護模式時用於載入SS:ESP堆棧指標值。
181.long init_stack # 堆棧段位移位置。
182.word 0x10  # 堆棧段同核心資料區段。
183
184 # 下面是任務0的LDT表段中的局部段描述符。
185 .align 3
186 ldt0:.quad 0x0000000000000000   # 第1個描述符,不用。
187.quad 0x00c0fa00000003ff  # 第2個局部程式碼片段描述符,對應選擇符是0x0f。
188.quad 0x00c0f200000003ff  # 第3個局部資料區段描述符,對應選擇符是0x17。
189 # 下面是任務0的TSS段的內容。注意其中標號等欄位在任務切換時不會改變。
190 tss0:.long 0 /* back link */
191.long krn_stk0, 0x10  /* esp0, ss0 */
192.long 0, 0, 0, 0, 0 /* esp1, ss1, esp2, ss2, cr3 */
193.long 0, 0, 0, 0, 0 /* eip, eflags, eax, ecx, edx */
194.long 0, 0, 0, 0, 0 /* ebx esp, ebp, esi, edi */
195.long 0, 0, 0, 0, 0, 0 /* es, cs, ss, ds, fs, gs */
196.long LDT0_SEL, 0x8000000 /* ldt, trace bitmap */
197
198.fill 128,4,0 # 這是任務0的核心棧空間。
199 krn_stk0:
200
201 # 下面是任務1的LDT表段內容和TSS段內容。
202 .align 3
203 ldt1:.quad 0x0000000000000000  # 第1個描述符,不用。
204.quad 0x00c0fa00000003ff  # 選擇符是0x0f,基地址 = 0x00000。
205.quad 0x00c0f200000003ff  # 選擇符是0x17,基地址 = 0x00000。
206
207 tss1:.long 0 /* back link */
208.long krn_stk1, 0x10   /* esp0, ss0 */
209.long 0, 0, 0, 0, 0 /* esp1, ss1, esp2, ss2, cr3 */
210.long task1, 0x200  /* eip, eflags */
211.long 0, 0, 0, 0 /* eax, ecx, edx, ebx */
212.long usr_stk1, 0, 0, 0   /* esp, ebp, esi, edi */
213.long 0x17,0x0f,0x17,0x17,0x17,0x17 /* es, cs, ss, ds, fs, gs */
214.long LDT1_SEL, 0x8000000   /* ldt, trace bitmap */
215
216.fill 128,4,0   # 這是任務1的核心棧空間。其使用者棧直接使用初始棧空間。
217 krn_stk1:
218
219 # 下面是任務0和任務1的程式,它們分別迴圈顯示字元"A"和"B"。
220 task0:
221movl $0x17, %eax   # 首先讓DS指向任務的局部資料區段。
222movw %ax, %ds   # 因為任務沒有使用局部資料,所以這兩句可省略。
223movl $65, %al  # 把需要顯示的字元"A"放入AL寄存器中。
224int $0x80  # 執行系統調用,顯示字元。
225movl $0xfff, %ecx  # 執行迴圈,起延時作用。
226 1:loop 1b
227jmp task0  # 跳轉到任務代碼開始處繼續顯示字元。
228 task1:
229movl $66, %al  # 把需要顯示的字元"B"放入AL寄存器中。
230int $0x80  # 執行系統調用,顯示字元。
231movl $0xfff, %ecx # 延時一段時間,並跳轉到開始處繼續迴圈顯示。
232 1:loop 1b
233jmp task1
234
235.fill 128,4,0  # 這是任務1的使用者棧空間。
236 usr_stk1:
****************************************************************************************************************************
相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.