2011年9月29日 星期四

c run time environment in x86 protected mode (0)

以下是之前寫的版本, 有一些錯誤, 花了幾天時間, 終於修正這些錯誤。保留下來當作歷史紀錄。我在 dosbox 測試, 沒想到在 dosbox 測試可以成功, 但在 qemu, bochs dos 環境會當機, 看來 dosbox 的測試不太可靠。請不要看這篇寫的資料。

改參考: http://descent-incoming.blogspot.com/2011/10/c-run-time-environment-in-x86-protected.html

程式首先進入 x86 保護模式, 讀寫 5MB 的位址, 然後呼叫 c function kmain, kmain 會使用 inline assembly 設定 al 暫存器的值, 再呼叫 5M_mem_rw_mix_c.S 的 DispAL 將 al 暫存器的值印要螢幕上。

asm("movb $0xab, %al");

要能開始使用 C 語言最主要是將要 stack 設定好, 因為 call 指令會使用 stack, 因為是在 x86 保護模式下, 所以是以 selector 來設定 ss 暫存器, 而 5M_mem_rw_mix_c.S 163~166 就是在設定 stack, 一旦 stack 設好, 就可以使用 call 指令來呼叫 c function。

而 C 語言呼叫慣例那是另外一件事, 會稍微複雜一點, 不過那也只發生在傳遞參數時才會比較複雜, 在這階段不太需要考慮這件事情。為什麼我不寫? 因為我還沒搞懂 XD

紅色的 AB 就是透過 inline assembly 設定 al 暫存器的值, 然後將 al 的內容印出來。最後一樣切回真實模式並回到 DOS box 提示符號下。

From write_os

5M_mem_rw_mix_c.S 1 /*
2 ref: Orange'S:一个操作系统的实现
3 do the 5M memory r/w
4 link with C code, but not yet ok.
5 */
6 /* chapter3/1/loader.S
7
8 Author: Wenbo Yang <solrex@gmail.com> <http://solrex.cn>
9
10 This file is part of the source code of book "Write Your Own OS with Free
11 and Open Source Software". Homepage @ <http://share.solrex.cn/WriteOS/>.
12
13 This file is licensed under the GNU General Public License; either
14 version 3 of the License, or (at your option) any later version. */
15
16 #include "pm.h"
17
18 .code16
19 .text
20 jmp LABEL_BEGIN /* jump over the .data section. */
21
22 /* NOTE! Wenbo-20080512: Actually here we put the normal .data section into
23 the .code section. For application SW, it is not allowed. However, we are
24 writing an OS. That is OK. Because there is no OS to complain about
25 that behavior. :) */
26
27 /* Global Descriptor Table */
28 LABEL_GDT: Descriptor 0, 0, 0
29 LABEL_DESC_NORMAL: Descriptor 0, 0xffff, DA_DRW # Normal descriptor is for back to real mode.
30 LABEL_DESC_CODE32: Descriptor 0, (SegCode32Len - 1), (DA_C + DA_32)
31 LABEL_DESC_CODE16: Descriptor 0, 0xffff, DA_C # 非一致程式碼段, 16
32 LABEL_DESC_DATA: Descriptor 0, DataLen-1, DA_DRW # Data
33 LABEL_DESC_STACK: Descriptor 0, TopOfStack, DA_DRWA+DA_32 # Stack, 32 位
34 LABEL_DESC_TEST: Descriptor 0x500000, 0xffff, DA_DRW
35 LABEL_DESC_VIDEO: Descriptor 0xB8000, 0xffff, DA_DRW
36
37 .set GdtLen, (. - LABEL_GDT) /* GDT Length */
38
39 GdtPtr: .2byte (GdtLen - 1) /* GDT Limit */
40 .4byte 0 /* GDT Base */
41
42 /* GDT Selector */
43 .set SelectorNormal, (LABEL_DESC_NORMAL - LABEL_GDT)
44 .set SelectorCode32, (LABEL_DESC_CODE32 - LABEL_GDT)
45 .set SelectorCode16, (LABEL_DESC_CODE16 - LABEL_GDT)
46 .set SelectorData, (LABEL_DESC_DATA - LABEL_GDT)
47 .set SelectorStack, (LABEL_DESC_STACK - LABEL_GDT)
48 .set SelectorTest, (LABEL_DESC_TEST - LABEL_GDT)
49 .set SelectorVideo, (LABEL_DESC_VIDEO - LABEL_GDT)
50
51
52 /* Program starts here. */
53 LABEL_BEGIN:
54 mov %cs, %ax /* Move code segment address(CS) to data segment */
55 mov %ax, %ds /* register(DS), ES and SS. Because we have */
56 mov %ax, %es /* embedded .data section into .code section in */
57 mov %ax, %ss /* the start(mentioned in the NOTE above). */
58
59 movw $0x100, %sp
60 nop
61 movw %ax, (LABEL_GO_BACK_TO_REAL+3) # modify segment value, indexed memory mode, ref professional aeesmbly language p 102.
62
63 /* Initialize 16-bits code segment descriptor. */
64 xor %eax, %eax
65 mov %cs, %ax
66 shl $4, %eax
67 addl $(LABEL_SEG_CODE16), %eax
68 movw %ax, (LABEL_DESC_CODE16 + 2)
69 shr $16, %eax
70 movb %al, (LABEL_DESC_CODE16 + 4)
71 movb %ah, (LABEL_DESC_CODE16 + 7)
72
73 /* Initialize 32-bits code segment descriptor. */
74 xor %eax, %eax
75 mov %cs, %ax
76 shl $4, %eax
77 addl $(LABEL_SEG_CODE32), %eax
78 movw %ax, (LABEL_DESC_CODE32 + 2)
79 shr $16, %eax
80 movb %al, (LABEL_DESC_CODE32 + 4)
81 movb %ah, (LABEL_DESC_CODE32 + 7)
82
83 # initialize data segment descriptor
84 xor %eax, %eax
85 mov %ds, %ax
86 shl $4, %eax
87 addl $(LABEL_DATA), %eax
88 movw %ax, (LABEL_DESC_DATA + 2)
89 shr $16, %eax
90 movb %al, (LABEL_DESC_DATA + 4)
91 movb %ah, (LABEL_DESC_DATA + 7)
92
93 # initialize stack segment descriptor
94 xor %eax, %eax
95 mov %ds, %ax
96 shl $4, %eax
97 addl $(LABEL_STACK), %eax
98 movw %ax, (LABEL_DESC_STACK + 2)
99 shr $16, %eax
100 movb %al, (LABEL_DESC_STACK + 4)
101 movb %ah, (LABEL_DESC_STACK + 7)
102
103 /* Prepared for loading GDTR */
104 xor %eax, %eax
105 mov %ds, %ax
106 shl $4, %eax
107 add $(LABEL_GDT), %eax /* eax <- gdt base*/
108 movl %eax, (GdtPtr + 2)
109
110 /* Load GDTR(Global Descriptor Table Register) */
111 lgdtw GdtPtr
112
113 /* Clear Interrupt Flags */
114 cli
115
116 /* Open A20 line. */
117 inb $0x92, %al
118 orb $0b00000010, %al
119 outb %al, $0x92
120
121 /* Enable protect mode, PE bit of CR0. */
122 movl %cr0, %eax
123 orl $1, %eax
124 movl %eax, %cr0
125
126 /* Mixed-Size Jump. */
127 ljmp $SelectorCode32, $0 /* Thanks to earthengine@gmail, I got */
128 /* this mixed-size jump insn of gas. */
129 /* this calls far jump (ptr 16:32) in intel manual) */
130
131 LABEL_REAL_ENTRY: # 從保護模式跳回到實模式就到了這裡
132 mov %cx, %ax
133 mov %ax, %ds
134 mov %ax, %es
135 mov %ax, %ss
136
137 # mov sp, [SPValueInRealMode]
138
139 in $0x92, %al
140 and $0b11111101, %al # close A20 line
141 out %al, $0x92
142
143 sti # 開中斷
144
145 mov $0x4c00, %ax
146 int $0x21 # 回到 DOS
147 # END of .code16
148
149 LABEL_SEG_CODE32:
150 .code32
151
152 mov $(SelectorData), %ax
153 mov %ax, %ds # 資料段選擇子
154 mov $(SelectorTest), %ax
155 mov %ax, %es # 測試段選擇子
156
157
158
159
160 mov $(SelectorVideo), %ax
161 mov %ax, %gs /* Video segment selector(dest) */
162
163 mov $(SelectorStack), %ax
164 mov %ax, %ss # 堆疊段選擇子
165
166 mov $(TopOfStack), %esp
167
168 /*
169 movl $((80 * 10 + 0) * 2), %edi
170 movb $0xC, %ah # 0000: Black Back 1100: Red Front
171 movb $'P', %al
172
173 mov %ax, %gs:(%edi)
174 */
175
176 # print string "In Protect Mode now. ^-^"
177 movb $0x0c, %ah # 0000: 黑底 1100: 紅字
178 xor %esi, %esi
179 xor %edi, %edi
180 mov $(OffsetPMMessage), %esi # data string offset
181 movl $((80 * 10 + 0) * 2), %edi # 目的資料偏移。螢幕第 10 行, 第 0 列。
182 cld # Clear Direction Flag, ref: http://www.fermi.mn.it/linux/quarta/x86/cld.htm
183 # After CLD is executed, string operations will increment the index
184 # (SI and/or DI) that they use.
185 .1:
186 lodsb # For legacy mode, Load byte at address DS:(E)SI into AL.
187 # For 64-bit mode load byte at address (R)SI into AL.
188 # ref: http://siyobik.info/main/reference/instruction/LODS%2FLODSB%2FLODSW%2FLODSD%2FLODSQ
189 test %al, %al # result is 0, zf sets to 1.
190 jz .2 # zf = 1 jump
191 # mov [gs:edi], ax
192 mov %ax, %gs:(%edi)
193 add $2, %edi
194 jmp .1
195 .2: # 顯示完畢
196
197 #push %eax # Multiboot magic number
198 # push %ebx # Multiboot data structure
199
200 movb $0xef, %al
201 call kmain
202 movb $0xa9, %al
203 call DispAL
204 /*
205 call DispReturn
206 #movb $0xa9, %al
207 #call DispAL
208
209 call TestRead
210 call TestWrite
211 call TestRead
212 */
213
214 ljmpl $SelectorCode16,$0
215 # jmpl $SelectorCode16,$0 # it works
216
217 # ------------------------------------------------------------------------
218 TestRead:
219 xor %esi, %esi
220 mov $8, %ecx
221 .loop:
222 mov %es:(%esi), %al
223 call DispAL
224 inc %esi
225 loop .loop
226 call DispReturn
227
228 ret
229 # TestRead 結束-----------------------------------------------------------
230
231
232 # ------------------------------------------------------------------------
233 TestWrite:
234 pushl %esi
235 pushl %edi
236 xor %esi, %esi
237 xor %edi, %edi
238 mov $(OffsetStrTest), %esi # data offset
239 cld # Clear Direction Flag, ref: http://www.fermi.mn.it/linux/quarta/x86/cld.htm
240 # After CLD is executed, string operations will increment the index
241 # (SI and/or DI) that they use.
242 .6:
243 lodsb # For legacy mode, Load byte at address DS:(E)SI into AL.
244 # For 64-bit mode load byte at address (R)SI into AL.
245 # ref: http://siyobik.info/main/reference/instruction/LODS%2FLODSB%2FLODSW%2FLODSD%2FLODSQ
246
247 test %al, %al
248 jz .5 # zf = 1 jump
249 # mov [es:edi], al
250 mov %al, %es:(%edi)
251 inc %edi
252 jmp .6
253 .5:
254
255 popl %edi
256 popl %esi
257 ret
258 # TestWrite 結束----------------------------------------------------------
259
260
261 # ------------------------------------------------------------------------
262 # 顯示 AL 中的數字
263 # 默認地:
264 # 數字已經存在 AL 中
265 # edi 始終指向要顯示的下一個字元的位置
266 # 被改變的暫存器:
267 # ax, edi
268 # ------------------------------------------------------------------------
269 .globl DispAL
270 DispAL:
271 pushl %ecx
272 pushl %edx
273
274 movb $0x0c, %ah # 0000: 黑底 1100: 紅字
275 movb %al, %dl
276 shr $4, %al
277 movl $2, %ecx
278 .begin:
279 andb $0x0f, %al
280 cmp $9, %al
281 ja .3 # cf=0, zf=0, above 9 (>9)
282 #addb $'0', %al
283 addb $0x30, %al
284 jmp .4
285 .3:
286 sub $0x0A, %al
287 #add $'A', %al
288 add $0x41, %al
289 .4:
290 #mov [gs:edi], ax
291 mov %ax, %gs:(%edi)
292 add $2, %edi
293
294 mov %dl, %al
295 loop .begin
296 add $2, %edi
297
298 popl %edx
299 popl %ecx
300
301 ret
302 # DispAL 結束-------------------------------------------------------------
303
304
305 # ------------------------------------------------------------------------
306 DispReturn:
307 pushl %eax
308 pushl %ebx
309 mov %edi, %eax
310 movb $160, %bl
311 divb %bl # %eax/160, 商 al, 餘數 ah.
312 and $0x0FF, %eax
313 inc %eax # ++ %eax
314 mov $160, %bl
315 mul %bl
316 mov %eax, %edi
317 popl %ebx
318 popl %eax
319 ret
320 # DispReturn 結束---------------------------------------------------------
321
322 /*
323 kmain:
324 pushl %ebp
325 movl %esp, %ebp
326 popl %ebp
327 ret
328 .size kmain, .-kmain
329 .ident "GCC: (Ubuntu/Linaro 4.5.2-8ubuntu4) 4.5.2"
330 */
331 # .section .note.GNU-stack,"",@progbits
332
333
334 /* Get the length of 32-bit segment code. */
335 .set SegCode32Len, . - LABEL_SEG_CODE32
336
337 #[SECTION .data1] ; 資料段
338 #ALIGN 32
339 #[BITS 32]
340 LABEL_DATA:
341 SPValueInRealMode: .2byte 0x0
342 # string
343 PMMessage: .ascii "In Protect Mode now. ^-^\0" # 在保護模式中顯示
344 .set OffsetPMMessage, (PMMessage - LABEL_DATA)
345 #StrTest: .ascii "B\0"
346 StrTest: .ascii "ABCDEFGHIJKLMNOPQRSTUVWXYZ\0"
347 #OffsetStrTest equ StrTest - $$
348 .set OffsetStrTest , (StrTest - LABEL_DATA)
349 #DataLen equ $ - LABEL_DATA
350 .set DataLen, . - LABEL_DATA
351 /* 32-bit global stack segment. */
352 LABEL_STACK:
353 .space 512, 0
354 .set TopOfStack, (. - LABEL_STACK - 1)
355
356 # END of [SECTION .data1]
357
358
359 LABEL_SEG_CODE16:
360 .code16
361 #jmp .
362 # back to real mode
363 mov $SelectorNormal, %ax
364 mov %ax, %ds
365 mov %ax, %es
366 mov %ax, %fs
367 mov %ax, %gs
368 mov %ax, %ss
369
370 mov %cr0, %eax
371 and $0b11111110, %al
372 mov %eax, %cr0
373
374
375 LABEL_GO_BACK_TO_REAL:
376 #.2byte 0xea66
377 #.4byte 0x00000000
378 #.2byte LABEL_REAL_ENTRY
379 jmp $0, $LABEL_REAL_ENTRY # 段位址會在程序開始處被設置成正確的值
380
381
382 .set Code16Len, . - LABEL_SEG_CODE16



k.c ref: http://wiki.osdev.org/Bare_bones 1
2 void kmain( void* mbd, unsigned int magic )
3 //void kmain()
4 {
5 #if 1
6 void DispAL(void);
7 mbd=0;
8 magic=0;
9 #if 0
10 if ( magic != 0x2BADB002 )
11 {
12 /* Something went not according to specs. Print an error */
13 /* message and halt, but do *not* rely on the multiboot */
14 /* data structure. */
15 }
16 #endif
17 /* You could either use multiboot.h */
18 /* (http://www.gnu.org/software/grub/manual/multiboot/multiboot.html#multiboot_002eh) */
19 /* or do your offsets yourself. The following is merely an example. */
20 //char * boot_loader_name =(char*) ((long*)mbd)[16];
21
22 /* Print a letter to screen to see everything is working: */
23 unsigned char *videoram = (unsigned char *) 0xb8000;
24 videoram[160] = 65; /* character 'A' */
25 videoram[161] = 0x07; /* light grey (7) on black (0). */
26 asm("movb $0xab, %al");
27 DispAL();
28 #endif
29 }

沒有留言:

張貼留言

使用 google 的 reCAPTCHA 驗證碼, 總算可以輕鬆留言了。

我實在受不了 spam 了, 又不想讓大家的眼睛花掉, 只好放棄匿名留言。這是沒辦法中的辦法了。留言的朋友需要有 google 帳號。