2011年12月18日 星期日

x86: enter to ring3

照著自己動手寫作業系統 (杨文博和于渊這兩本), 終於來到了權限切換, 進度真是緩慢, 這才不過是第三章一半 (于渊版本) 的內容。我離完成整個 OS 還有多遠呢?翻翻後面的章節, 還真的蠻遠的 ...

這次的內容很簡單, 從 ring0 切換到 ring3, code 也很簡單。

設定好 ring3 的 stack 和程式碼區段還有各自的 gtd descriptor, selector 配合下面的五行 push 指令, 就可以從 ring0 切換到 ring3 了。

273 pushl $(SELECT_STACKR3)
274 pushl $(TopOfStackR3)
275 pushl $(SELECT_CODER3)
276 pushl $0
277 lret

從 ring3 切到 ring0 就沒這麼容易了, 得花點時間消化。

cg_to_r3.S
1 /*
2 ref: Orange'S:一个操作系统的实现
3 practice enter to ring3.
4 */
5 /* chapter3/1/loader.S
6
7 Author: Wenbo Yang <solrex@gmail.com> <http://solrex.cn>
8
9 This file is part of the source code of book "Write Your Own OS with Free
10 and Open Source Software"
. Homepage @ <http://share.solrex.cn/WriteOS/>.
11
12 This file is licensed under the GNU General Public License; either
13 version 3 of the License, or (at your option) any later version.
14 */
15
16 #include "pm.h"
17
18 #define video_addr_offset (video_addr-LABEL_DATA)
19
20 .code16
21 .text
22 jmp LABEL_BEGIN /* jump over the .data section. */
23
24 /* NOTE! Wenbo-20080512: Actually here we put the normal .data section into
25 the .code section. For application SW, it is not allowed. However, we are
26 writing an OS. That is OK. Because there is no OS to complain about
27 that behavior. :) */
28
29 /* Global Descriptor Table */
30 LABEL_GDT: Descriptor 0, 0, 0
31 LABEL_DESC_NORMAL: Descriptor 0, 0xffff, DA_DRW # Normal descriptor is for back to real mode.
32 LABEL_DESC_CODE32: Descriptor 0, (SegCode32Len - 1), (DA_C + DA_32)
33 LABEL_DESC_CODE16: Descriptor 0, 0xffff, DA_C # 非一致程式碼段, 16
34 LABEL_DESC_DATA: Descriptor 0, (DataLen-1), DA_DRW # Data
35 LABEL_DESC_STACK: Descriptor 0, TopOfStack, DA_DRWA+DA_32 # Stack, 32 位
36 LABEL_DESC_TEST: Descriptor 0x500000, 0xffff, DA_DRW
37 LABEL_DESC_LDT: Descriptor 0, (LDT_LEN-1), DA_LDT
38 LABEL_DESC_VIDEO: Descriptor 0xB8000, 0xffff, (DA_DRW+DA_DPL3)
39 LABEL_DESC_CODECG: Descriptor 0, (SEG_CODE_CG_LEN - 1), (DA_C + DA_32)
40 LABEL_DESC_CODER3: Descriptor 0, (SEG_CODER3_LEN - 1), (DA_C + DA_32 + DA_DPL3)
41 LABEL_DESC_STACKR3: Descriptor 0, (TopOfStackR3 - 1), (DA_DRWA + DA_32 + DA_DPL3)
42
43 # Gates Descriptor
44 LABEL_CG_TEST: Gate SELECT_CODE_CG, 0, 0xffff, (DA_386CGate + DA_DPL0)
45
46 .set GdtLen, (. - LABEL_GDT) /* GDT Length */
47
48 GdtPtr: .2byte (GdtLen - 1) /* GDT Limit */
49 .4byte 0 /* GDT Base */
50
51 /* GDT Selector */
52 .set SelectorNormal, (LABEL_DESC_NORMAL - LABEL_GDT)
53 .set SelectorCode32, (LABEL_DESC_CODE32 - LABEL_GDT)
54 .set SelectorCode16, (LABEL_DESC_CODE16 - LABEL_GDT)
55 .set SelectorData, (LABEL_DESC_DATA - LABEL_GDT)
56 .set SelectorStack, (LABEL_DESC_STACK - LABEL_GDT)
57 .set SelectorTest, (LABEL_DESC_TEST - LABEL_GDT)
58 .set SelectorLDT, (LABEL_DESC_LDT - LABEL_GDT)
59 .set SelectorVideo, (LABEL_DESC_VIDEO - LABEL_GDT)
60 .set SELECT_CODE_CG, (LABEL_DESC_CODECG - LABEL_GDT)
61 .set SELECT_CODER3, (LABEL_DESC_CODER3 - LABEL_GDT + SA_RPL3)
62 .set SELECT_STACKR3, (LABEL_DESC_STACKR3 - LABEL_GDT + SA_RPL3)
63 .set SELECT_CG_TEST, (LABEL_CG_TEST - LABEL_GDT)
64
65 # LDT
66
67 LABEL_LDT:
68 LABEL_LDT_DESC_CODEA: Descriptor 0, (CodeALen - 1), (DA_C + DA_32) # Code, 32 位
69 .set LDT_LEN, (. - LABEL_LDT) /* LDT Length */
70
71 /* LDT Selector (TI flag set)*/
72 .set SelectorLDTCodeA, (LABEL_LDT_DESC_CODEA - LABEL_LDT + SA_TIL)
73
74
75 /* Program starts here. */
76 LABEL_BEGIN:
77 mov %cs, %ax /* Move code segment address(CS) to data segment */
78 mov %ax, %ds /* register(DS), ES and SS. Because we have */
79 mov %ax, %es /* embedded .data section into .code section in */
80 mov %ax, %ss /* the start(mentioned in the NOTE above). */
81
82 movw $0x100, %sp
83 nop
84 movw %ax, (LABEL_GO_BACK_TO_REAL+3) # modify segment value, indexed memory mode, ref professional aeesmbly language p 102.
85 mov $(SPValueInRealMode), %sp
86
87 /* Initialize 16-bits code segment descriptor. */
88 xor %eax, %eax
89 mov %cs, %ax
90 shl $4, %eax
91 addl $(LABEL_SEG_CODE16), %eax
92 movw %ax, (LABEL_DESC_CODE16 + 2)
93 shr $16, %eax
94 movb %al, (LABEL_DESC_CODE16 + 4)
95 movb %ah, (LABEL_DESC_CODE16 + 7)
96
97 /* Initialize 32-bits code segment descriptor. */
98 xor %eax, %eax
99 mov %cs, %ax
100 shl $4, %eax
101 addl $(LABEL_SEG_CODE32), %eax
102 movw %ax, (LABEL_DESC_CODE32 + 2)
103 shr $16, %eax
104 movb %al, (LABEL_DESC_CODE32 + 4)
105 movb %ah, (LABEL_DESC_CODE32 + 7)
106
107
108 # Initialize CODER3 descriptor
109 xor %eax, %eax
110 mov %cs, %ax
111 shl $4, %eax
112 addl $(LABEL_SEG_CODER3), %eax
113 movw %ax, (LABEL_DESC_CODER3 + 2)
114 shr $16, %eax
115 movb %al, (LABEL_DESC_CODER3 + 4)
116 movb %ah, (LABEL_DESC_CODER3 + 7)
117
118 # Initialize STACK R3 descriptor
119 xor %eax, %eax
120 mov %cs, %ax
121 shl $4, %eax
122 addl $(LABEL_STACKR3), %eax
123 movw %ax, (LABEL_DESC_STACKR3 + 2)
124 shr $16, %eax
125 movb %al, (LABEL_DESC_STACKR3 + 4)
126 movb %ah, (LABEL_DESC_STACKR3 + 7)
127
128
129 /* Initialize call gate code segment descriptor. */
130 xor %eax, %eax
131 mov %cs, %ax
132 shl $4, %eax
133 addl $(LABEL_CODE_CG), %eax
134 movw %ax, (LABEL_DESC_CODECG + 2)
135 shr $16, %eax
136 movb %al, (LABEL_DESC_CODECG + 4)
137 movb %ah, (LABEL_DESC_CODECG + 7)
138
139 # initialize data segment descriptor
140 xor %eax, %eax
141 mov %ds, %ax
142 shl $4, %eax
143 addl $(LABEL_DATA), %eax
144 movw %ax, (LABEL_DESC_DATA + 2)
145 shr $16, %eax
146 movb %al, (LABEL_DESC_DATA + 4)
147 movb %ah, (LABEL_DESC_DATA + 7)
148
149 # initialize stack segment descriptor
150 xor %eax, %eax
151 mov %ds, %ax
152 shl $4, %eax
153 addl $(LABEL_STACK), %eax
154 movw %ax, (LABEL_DESC_STACK + 2)
155 shr $16, %eax
156 movb %al, (LABEL_DESC_STACK + 4)
157 movb %ah, (LABEL_DESC_STACK + 7)
158
159 /* Initialize LDT descriptor in GDT. */
160 xor %eax, %eax
161 mov %ds, %ax
162 shl $4, %eax
163 addl $(LABEL_LDT), %eax
164 movw %ax, (LABEL_DESC_LDT + 2)
165 shr $16, %eax
166 movb %al, (LABEL_DESC_LDT + 4)
167 movb %ah, (LABEL_DESC_LDT + 7)
168
169 /* Initialize code A descriptor in LDT. */
170 xor %eax, %eax
171 mov %ds, %ax
172 shl $4, %eax
173 addl $(LABEL_CODE_A), %eax
174 movw %ax, (LABEL_LDT_DESC_CODEA + 2)
175 shr $16, %eax
176 movb %al, (LABEL_LDT_DESC_CODEA + 4)
177 movb %ah, (LABEL_LDT_DESC_CODEA + 7)
178
179
180 /* Prepared for loading GDTR */
181 xor %eax, %eax
182 mov %ds, %ax
183 shl $4, %eax
184 add $(LABEL_GDT), %eax /* eax <- gdt base*/
185 movl %eax, (GdtPtr + 2)
186
187 /* Load GDTR(Global Descriptor Table Register) */
188 lgdtw GdtPtr
189
190 /* Clear Interrupt Flags */
191 cli
192
193 /* Open A20 line. */
194 inb $0x92, %al
195 orb $0b00000010, %al
196 outb %al, $0x92
197
198 /* Enable protect mode, PE bit of CR0. */
199 movl %cr0, %eax
200 orl $1, %eax
201 movl %eax, %cr0
202
203 /* Mixed-Size Jump. */
204 ljmp $SelectorCode32, $0 /* Thanks to earthengine@gmail, I got */
205 /* this mixed-size jump insn of gas. */
206 /* this calls far jump (ptr 16:32) in intel manual) */
207
208 LABEL_REAL_ENTRY: # 從保護模式跳回到實模式就到了這裡
209 mov %cx, %ax
210 mov %ax, %ds
211 mov %ax, %es
212 mov %ax, %ss
213
214
215 in $0x92, %al
216 and $0b11111101, %al # close A20 line
217 out %al, $0x92
218
219 sti # 開中斷
220
221 mov $0x4c00, %ax
222 int $0x21 # 回到 DOS
223 # END of .code16
224
225 LABEL_SEG_CODE32:
226 .code32
227
228 mov $(SelectorData), %ax
229 mov %ax, %ds # 資料段選擇子
230 mov $(SelectorTest), %ax
231 mov %ax, %es # 測試段選擇子
232
233
234
235
236 mov $(SelectorVideo), %ax
237 mov %ax, %gs /* Video segment selector(dest) */
238
239 mov $(SelectorStack), %ax
240 mov %ax, %ss # 堆疊段選擇子
241
242 mov $(TopOfStack), %esp
243
244 /*
245 movl $((80 * 10 + 0) * 2), %edi
246 movb $0xC, %ah # 0000: Black Back 1100: Red Front
247 movb $'P', %al
248
249 mov %ax, %gs:(%edi)
250 */
251
252 # print string "In Protect Mode now. ^-^"
253 movb $0x0c, %ah # 0000: 黑底 1100: 紅字
254 xor %esi, %esi
255 xor %edi, %edi
256 mov $(OffsetPMMessage), %esi # data string offset
257 movl $((80 * 10 + 0) * 2), %edi # 目的資料偏移。螢幕第 10 行, 第 0 列。
258 cld # Clear Direction Flag, ref: http://www.fermi.mn.it/linux/quarta/x86/cld.htm
259 # After CLD is executed, string operations will increment the index
260 # (SI and/or DI) that they use.
261 .1:
262 lodsb # For legacy mode, Load byte at address DS:(E)SI into AL.
263 # For 64-bit mode load byte at address (R)SI into AL.
264 # ref: http://siyobik.info/main/reference/instruction/LODS%2FLODSB%2FLODSW%2FLODSD%2FLODSQ
265 test %al, %al # result is 0, zf sets to 1.
266 jz .2 # zf = 1 jump
267 # mov [gs:edi], ax
268 mov %ax, %gs:(%edi)
269 add $2, %edi
270 jmp .1
271 .2: # 顯示完畢
272
273 pushl $(SELECT_STACKR3)
274 pushl $(TopOfStackR3)
275 pushl $(SELECT_CODER3)
276 pushl $0
277 lret
278
279 # call gate test
280 #lcall $(SELECT_CG_TEST), $0
281
282 # the call gate setting selector
283 #lcall $(SELECT_CODE_CG), $0
284
285 mov $(SelectorLDT), %ax
286 lldt %ax
287 jmp $(SelectorLDTCodeA), $0
288
289
290 /*
291 movl $0, (video_addr_offset)
292 movb $0xb8, %al
293 call DispAL_m
294
295 movl $(80*(3-1)*2 + (2-1)*2), (video_addr_offset)
296 movb $0xc7, %al
297 call DispAL_m
298
299 #call DispReturn
300 movl $160, (video_addr_offset)
301 call DispReturn
302 movb $0xa9, %al
303 call DispAL
304 call DispReturn
305 movw $0xabcd, %ax
306 call DispAX
307 call DispReturn
308 movl (video_addr_offset), %ax
309 call DispAX
310 call DispReturn
311
312 addl $2, (video_addr_offset)
313 movl (video_addr_offset), %ax
314 call DispAX
315
316 call TestRead
317 call TestWrite
318 call TestRead
319 */
320
321 # ljmpl $SelectorCode16,$0
322 # jmpl $SelectorCode16,$0 # it works
323
324
325
326 # ------------------------------------------------------------------------
327 TestRead:
328 xor %esi, %esi
329 mov $8, %ecx
330 .loop:
331 mov %es:(%esi), %al
332 call DispAL
333 inc %esi
334 loop .loop
335 call DispReturn
336
337 ret
338 # TestRead 結束-----------------------------------------------------------
339
340
341 # ------------------------------------------------------------------------
342 TestWrite:
343 pushl %esi
344 pushl %edi
345 xor %esi, %esi
346 xor %edi, %edi
347 mov $(OffsetStrTest), %esi # data offset
348 cld # Clear Direction Flag, ref: http://www.fermi.mn.it/linux/quarta/x86/cld.htm
349 # After CLD is executed, string operations will increment the index
350 # (SI and/or DI) that they use.
351 .6:
352 lodsb # For legacy mode, Load byte at address DS:(E)SI into AL.
353 # For 64-bit mode load byte at address (R)SI into AL.
354 # ref: http://siyobik.info/main/reference/instruction/LODS%2FLODSB%2FLODSW%2FLODSD%2FLODSQ
355
356 test %al, %al
357 jz .5 # zf = 1 jump
358 # mov [es:edi], al
359 mov %al, %es:(%edi)
360 inc %edi
361 jmp .6
362 .5:
363
364 popl %edi
365 popl %esi
366 ret
367 # TestWrite 結束----------------------------------------------------------
368
369 DispAX:
370 pushl %ebx
371 pushl %ecx
372 pushl %edx
373
374 movw %ax, %dx
375 shr $8, %ax # ah -> al
376 movl $2, %ecx
377 .b:
378 call DispAL_m
379 movw %dx, %ax
380 addl $4, (video_addr_offset)
381 # call DispAL_m
382 # andw 0xff, %ax
383 loop .b
384
385 popl %edx
386 popl %ecx
387 popl %ebx
388
389 ret
390 # end DispAX
391
392 # ------------------------------------------------------------------------
393 # 顯示 AL 中的數字
394 # 默認地:
395 # 數字已經存在 AL 中
396 # edi 始終指向要顯示的下一個字元的位置
397 # 被改變的暫存器:
398 # ax, edi
399 # ------------------------------------------------------------------------
400 DispAL:
401 pushl %ecx
402 pushl %edx
403
404 movb $0x0c, %ah # 0000: 黑底 1100: 紅字
405 movb %al, %dl
406 shr $4, %al
407 movl $2, %ecx
408 .begin:
409 andb $0x0f, %al
410 cmp $9, %al
411 ja .3 # cf=0, zf=0, above 9 (>9)
412 #addb $'0', %al
413 addb $0x30, %al
414 jmp .4
415 .3:
416 sub $0x0A, %al
417 #add $'A', %al
418 add $0x41, %al
419 .4:
420 #mov [gs:edi], ax
421 mov %ax, %gs:(%edi)
422 # mov %ax, %gs:(video_addr_offset)
423 #mov %ax, %gs:(0)
424 add $2, %edi
425
426 # addl $2, (video_addr_offset)
427
428 mov %dl, %al
429 loop .begin
430 add $2, %edi
431 #add $2, (video_addr_offset)
432
433 popl %edx
434 popl %ecx
435
436 ret
437 # DispAL 結束-------------------------------------------------------------
438
439 DispAL_m:
440 pushl %ebx
441 pushl %ecx
442 pushl %edx
443
444 movl (video_addr_offset), %ebx
445 movb $0x0c, %ah # 0000: 黑底 1100: 紅字
446 movb %al, %dl
447 shr $4, %al
448 movl $2, %ecx
449 .begin_1:
450 andb $0x0f, %al
451 cmp $9, %al
452 ja .31 # cf=0, zf=0, above 9 (>9)
453 #addb $'0', %al
454 addb $0x30, %al
455 jmp .41
456 .31:
457 sub $0x0A, %al
458 #add $'A', %al
459 add $0x41, %al
460 .41:
461 #mov [gs:edi], ax
462 #mov %ax, %gs:(%edi)
463 #if 0
464 movw (video_addr_offset), %ax
465 call DispReturn
466 call DispAX
467 #endif
468 movw %ax, %gs:(%ebx)
469 #mov %ax, %gs:(0)
470 add $2, %ebx
471
472 mov %dl, %al
473 loop .begin_1
474 addl $2, %ebx
475
476 popl %edx
477 popl %ecx
478 popl %ebx
479
480 ret
481 # DispAL_m 結束-------------------------------------------------------------
482
483
484 # ------------------------------------------------------------------------
485 DispReturn:
486 pushl %eax
487 pushl %ebx
488 mov %edi, %eax
489 movb $160, %bl
490 divb %bl # %eax/160, 商 al, 餘數 ah.
491 and $0x0FF, %eax
492 inc %eax # ++ %eax
493 mov $160, %bl
494 mul %bl
495 mov %eax, %edi
496 popl %ebx
497 popl %eax
498 ret
499 # DispReturn 結束---------------------------------------------------------
500
501 LABEL_STACKR3:
502 .space 512, 0
503 .set TopOfStackR3, . - LABEL_STACKR3
504
505 LABEL_SEG_CODER3:
506 mov $(SelectorVideo), %ax
507 mov %ax, %gs
508 movl $((80*11+1) *2 ), %edi
509 movb $0xC, %ah
510 movb $'3', %al
511 mov %ax, %gs:(%edi)
512 jmp .
513 .set SEG_CODER3_LEN, . - LABEL_SEG_CODER3
514
515 # test call gate
516 LABEL_CODE_CG:
517 mov $(SelectorVideo), %ax
518 mov %ax, %gs
519 movl $((80*11+0) *2 ), %edi
520 movb $0xC, %ah
521 movb $'C', %al
522
523 mov %ax, %gs:(%edi)
524 lret # long return command
525 .set SEG_CODE_CG_LEN, . - LABEL_CODE_CG
526 LABEL_CODE_A:
527 #if 0
528 # intel syntax
529 mov ax, SelectorVideo
530 mov gs, ax ; 視頻段選擇子(目的)
531
532 mov edi, (80 * 12 + 0) * 2 ; 螢幕第 10 行, 第 0 列。
533 mov ah, 0Ch ; 0000: 黑底 1100: 紅字
534 mov al, 'L'
535 mov [gs:edi], ax
536 #else
537 # at&t syntax
538 #if 1
539 mov $(SelectorVideo), %ax
540 mov %ax, %gs
541 mov $((80 * 12 + 0) * 2), %edi
542 mov $0x0c, %ah
543 mov $'L', %al
544 mov %ax, %gs:(%edi)
545 #else
546 /* can call DispAL function */
547 movb $0xef, %al
548 call DispAL
549 #endif
550 /* jump to real mode code16 and return to dos. */
551 ljmpl $SelectorCode16,$0
552
553 #endif
554 .set CodeALen, (. - LABEL_CODE_A)
555
556 /*
557 kmain:
558 pushl %ebp
559 movl %esp, %ebp
560 popl %ebp
561 ret
562 .size kmain, .-kmain
563 .ident "GCC: (Ubuntu/Linaro 4.5.2-8ubuntu4) 4.5.2"
564 */
565 # .section .note.GNU-stack,"",@progbits
566
567
568 /* Get the length of 32-bit segment code. */
569 .set SegCode32Len, . - LABEL_SEG_CODE32
570
571 #[SECTION .data1] ; 資料段
572 #ALIGN 32
573 #[BITS 32]
574 LABEL_DATA:
575 SPValueInRealMode: .2byte 0x0
576 # string
577 PMMessage: .ascii "In Protect Mode now. ^-^\0" # 在保護模式中顯示
578 .set OffsetPMMessage, (PMMessage - LABEL_DATA)
579 #StrTest: .ascii "B\0"
580 StrTest: .ascii "ABCDEFGHIJKLMNOPQRSTUVWXYZ\0"
581 #OffsetStrTest equ StrTest - $$
582 .set OffsetStrTest , (StrTest - LABEL_DATA)
583 #DataLen equ $ - LABEL_DATA
584 video_addr: .int 0
585 .set DataLen, . - LABEL_DATA
586 /* 32-bit global stack segment. */
587 LABEL_STACK:
588 .space 512, 0
589 .set TopOfStack, (. - LABEL_STACK - 1)
590
591 # END of [SECTION .data1]
592
593
594 LABEL_SEG_CODE16:
595 .code16
596 #jmp .
597 # back to real mode
598 mov $SelectorNormal, %ax
599 mov %ax, %ds
600 mov %ax, %es
601 mov %ax, %fs
602 mov %ax, %gs
603 mov %ax, %ss
604
605 mov %cr0, %eax
606 and $0b11111110, %al
607 mov %eax, %cr0
608
609
610 LABEL_GO_BACK_TO_REAL:
611 #.2byte 0xea66
612 #.4byte 0x00000000
613 #.2byte LABEL_REAL_ENTRY
614 jmp $0, $LABEL_REAL_ENTRY # 段位址會在程序開始處被設置成正確的值
615
616
617 .set Code16Len, . - LABEL_SEG_CODE16
618
619

沒有留言:

張貼留言

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

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