標籤:style blog class code c tar
最近終於把實現多任務的微核心調試了一遍,我們闡述了如何在保護模式下切換任務。同時知識包括:gdt,idt,ldt,tss,時鐘中斷服務,特權級切換,顯存編程,boot和loader功能,bios調用等等。詳細知識還要在實踐中摸索學習,希望大家一起進步。這篇文章僅僅做個記錄,如沒有親身調試過代碼,可能不大好理解。接下幾天重點看看0.12啟動程式,多分頁需要更加深入瞭解。
;#Mode=Dos;放在.code前面.386p.model smallLATCH=11930SCRN_SEL=18HTSS0_SEL= 20HLDT0_SEL=28HTSS1_SEL=30HLDT1_SEL=38H.codestart:mov eax,10hmov ds,axlss esp,FWORD ptr [init_stack]call setup_idtcall setup_gdtmov eax,10hmov ds,axmov es,axmov fs,axmov gs,axlss esp,FWORD ptr [init_stack]mov al,36hmov edx,43hout dx,almov eax,LATCH mov edx,40hout dx,almov al,ahout dx,almov eax,80000hmov ecx,timer_interruptmov ax,cxmov dx,8e00hmov ecx,8lea esi,[ecx * 8 + idt]mov [esi],eaxmov [esi + 4],edxmov ecx,system_interruptmov ax,cxmov dx,0ef00hmov ecx,80hlea esi,[ecx * 8 + idt]mov [esi],eaxmov [esi + 4],edx;//增加的代碼mov ecx,div_intmov ax,cxmov dx,0ef00hmov ecx,0lea esi,[ecx * 8 + idt]mov [esi],eaxmov [esi + 4],edxpushfdand DWORD ptr [esp],0ffffbfffhpopfdmov eax,TSS0_SELltr axmov eax,LDT0_SELlldt ax;mov DWORD ptr [current],0;問題1:首碼2edb 0c7h, 05hdd currentdd 0sti;///增加代碼;///push 17hpush init_stackpushfdpush 0fhpush task0iretdsetup_gdt:lgdt FWORD ptr [lgdt_opcode]retsetup_idt:mov edx, ignore_initmov eax,80000hmov ax,dxmov dx,8e00hmov edi, idtmov ecx,256rp_idt:mov [edi],eaxmov [edi + 4],edxadd edi,8dec ecxjne rp_idtlidt FWORD ptr [lidt_opcode]retwrite_char:push gspush ebxmov ebx,SCRN_SELmov gs,bxmov ebx,DWORD ptr [scr_loc]shl ebx,1mov BYTE ptr gs:[ebx],alshr ebx,1inc ebxcmp ebx,2000jb @fmov ebx,0@@:;mov DWORD ptr [scr_loc],ebxdb 89h, 1dhdd scr_locpop ebxpop gsret;align 4ignore_init:push dspush eaxmov eax,10hmov ds,axmov eax,67call write_charpop eaxpop dsiretd;//增加代碼div_int: iret;align 4timer_interrupt:push dspush eaxmov eax,10hmov ds,axmov al,20hout 20h,almov eax,1cmp DWORD ptr [current],eaxje n1;mov DWORD ptr [current],eaxdb 0a3hdd currentBYTE 0eahWORD 0, 0, TSS1_SELjmp n2n1:;mov DWORD ptr [current],0db 0c7h, 05hdd currentdd 0db 0eahWORD 0,0, TSS0_SELn2:pop eaxpop dsiretd;align 4system_interrupt:push dspush edxpush ecxpush ebxpush eaxmov edx,10hmov ds,dxcall write_charpop eaxpop ebxpop ecxpop edxpop dsiretdcurrent: DWORD 0scr_loc: DWORD 0;align 4lidt_opcode:WORD256*8-1DWORDidtlgdt_opcode:WORD(end_gdt-gdt)-1DWORDgdt;align 4idt:QWORD 256 dup (0)gdt:QWORD 0000000000000000hQWORD 00c09a00000007ffhQWORD 00c09200000007ffhQWORD 00c0920b80000002hWORD 68h, tss0, 0e900h, 0WORD 40h, ldt0, 0e200h, 0WORD 68h, tss1, 0e900h, 0WORD 40h, ldt1, 0e200h, 0end_gdt:DWORD 128 dup (0)init_stack:DWORD init_stackWORD 10h;align 4ldt0:QWORD 0000000000000000hQWORD 00c0fa00000003ffhQWORD 00c0f200000003ffhtss0:DWORD 0DWORD krn_stk0, 10hDWORD 0, 0, 0, 0, 0DWORD 0, 0, 0, 0, 0DWORD 0, 0, 0, 0, 0DWORD 0, 0, 0, 0, 0, 0DWORD LDT0_SEL, 8000000hDWORD 128 dup (0)krn_stk0:ldt1:QWORD 0000000000000000hQWORD 00c0fa00000003ffhQWORD 00c0f200000003ffhtss1:DWORD 0DWORD krn_stk1, 10hDWORD 0, 0, 0, 0, 0DWORD task1, 200hDWORD 0, 0, 0, 0DWORD usr_stk1, 0, 0, 0DWORD 17h, 0fh, 17h, 17h, 17h, 17hDWORD LDT1_SEL, 8000000hDWORD 128 dup (0)krn_stk1:task0:mov eax,17hmov ds,axmov al,65int 80hmov ecx,0fffht1:loop t1jmp task0task1:mov eax,17hmov ds,axmov al,66int 80hmov ecx,0fffht2:loop t2jmp task1DWORD 128 dup (0)usr_stk1:end start
要感謝一個熱心網友提供的核心鏡像,學到不少東西。下面是我調試的記錄:
程式碼分析:
57:pushl $0x17
pushl $init_stack
pushfl
pushl $0x0f
pushl $task0
iret
57:入棧任務0的ss,(更改了特權級為3)
入棧任務0的堆棧指標esp
入棧標誌寄存器
入棧任務0的程式碼片段選擇符(cs)
入棧如任務的指令指標(eip)
iret退棧,分別賦值了ss,esp,eflags,cs,eip。
效果:
1.CPU特權級由0->3,因為cs寄存器的內容由0x10(RPL位為0)轉為0x0f(RPL為3)
2.跳轉到任務0開始執行。cs得到LDT中程式碼片段描述符基址:0x00,位移量eip=task0,這樣就跳轉到任務0處開始執行
總結:任務0開始在特權級3上面開始執行。
iret指令之前各個寄存器的值:
r:
ebx: 0x00000000 0
esp: 0x00000bf1 3057
ebp: 0x00000000 0
esi: 0x000001c5 453
edi: 0x000009c5 2501
eip: 0x000000d8
sreg:
es:0x0010, dh=0x00c09300, dl=0x000007ff, valid=1
Data segment, base=0x00000000, limit=0x007fffff, Read/Write, Accessed
cs:0x0008, dh=0x00c09b00, dl=0x000007ff, valid=3
Code segment, base=0x00000000, limit=0x007fffff, Execute/Read, Accessed, 32-bit
ss:0x0010, dh=0x00c09300, dl=0x000007ff, valid=7
Data segment, base=0x00000000, limit=0x007fffff, Read/Write, Accessed
ds:0x0010, dh=0x00c09300, dl=0x000007ff, valid=7
Data segment, base=0x00000000, limit=0x007fffff, Read/Write, Accessed
fs:0x0010, dh=0x00c09300, dl=0x000007ff, valid=1
Data segment, base=0x00000000, limit=0x007fffff, Read/Write, Accessed
gs:0x0010, dh=0x00c09300, dl=0x000007ff, valid=1
Data segment, base=0x00000000, limit=0x007fffff, Read/Write, Accessed
ldtr:0x0028, dh=0x0000e200, dl=0x0c0b0040, valid=1
tr:0x0020, dh=0x0000eb00, dl=0x0c230068, valid=1
gdtr:base=0x000009c5, limit=0x3f
idtr:base=0x000001c5, limit=0x7ff
執行完iret指令後:
r:
ebx: 0x00000000 0
esp: 0x00000c05 3077
ebp: 0x00000000 0
esi: 0x000001c5 453
edi: 0x000009c5 2501
eip: 0x0000110b
ss:0x17
cs:0x0f
-------------------------------------------------------------------------
224:int $0x80
在bochs中輸入:s就會進入到中斷程式:system_interrupt
在IDT表中有三種門描述符:中斷門、陷阱門、任務門。
他們靠什麼區別呢?描述符中有個TYPE欄位。中斷門:13、陷阱門:14、任務門:5。
int 0x80。執行完指令之後,系統檢測到中斷,然後根據中斷號80,找到相應處理常式。這是一個陷阱門描述符。
根據完全剖析:121分析
(1)處理過程將在高特權級上執行時就會發生堆棧的切換。堆棧切換的過程如下:
處理器從當前任務的TSS段中取得中斷處理過程使用的堆棧的段選擇符和棧指標(例如tss.ss0、tss.esp0)。然後處理器會把被中斷程式的棧選擇符和棧指標壓入新棧中。
(2)一旦執行int 80,就會執行堆棧切換,切換到任務0的核心堆棧--krn_stk0(esp)、0x10(ss),在哪裡找要切換到的核心棧呢?在當前任務的TSS中有核心棧地址。
還有個問題需要考慮,執行流程怎麼跳轉到系統調用程式?當然是通過陷阱門的們描述符,裡面指定了處理常式的地址,然後更改eip就ok了。
(3)進入核心態,後通過iret退棧(原來cs又彈出來),進入使用者態
執行完int 0x80,此時堆棧確實切換了,並且把緣任務的東西入棧
| STACK 0x00000e77 [0x00001117]---棧頂---[EIP]
| STACK 0x00000e7b [0x0000000f]----------[cs]
| STACK 0x00000e7f [0x00000246]----------[eflags]
| STACK 0x00000e83 [0x00000c05]----------[原esp]
| STACK 0x00000e87 [0x00000017]----------[原ss]
此時寄存器資訊:
cs:0x0008, dh=0x00c09b00, dl=0x000007ff, valid=1
Code segment, base=0x00000000, limit=0x007fffff, Execute/Read, Accessed, 32-bit
ss:0x0010, dh=0x00c09300, dl=0x000007ff, valid=1
Data segment, base=0x00000000, limit=0x007fffff, Read/Write, Accessed
ds:0x0017, dh=0x00c0f300, dl=0x000003ff, valid=1
Data segment, base=0x00000000, limit=0x003fffff, Read/Write, Accessed
fs:0x0000, dh=0x00c09300, dl=0x000007ff, valid=0
gs:0x0000, dh=0x00c09300, dl=0x000007ff, valid=0
ldtr:0x0028, dh=0x0000e200, dl=0x0c0b0040, valid=1
tr:0x0020, dh=0x0000eb00, dl=0x0c230068, valid=1
gdtr:base=0x000009c5, limit=0x3f
idtr:base=0x000001c5, limit=0x7ff
esp: 0x00000e77 3703
eip: 0x00000199
這個時候cs=0x10,說CPU的特權級由3升到0。
異常和中斷過程中的保護:CPL必須小於等於門的DPL。這裡是滿足的,因為任務0的CPL是3,系統調用陷阱門的DPL也是3。
在system_interrupt執行最後一句iret指令之前,寄存器狀態和上面一樣。
執行完iret之後:
esp: 0x00000c05 3077
ebp: 0x00000000 0
esi: 0x000001c5 453
edi: 0x000009c5 2501
eip: 0x00001117(對應int 0x10後面那條指令)
cs:0x000f, dh=0x00c0fb00, dl=0x000003ff, valid=1
Code segment, base=0x00000000, limit=0x003fffff, Execute/Read, Accessed, 32-bit
ss:0x0017, dh=0x00c0f300, dl=0x000003ff, valid=1
Data segment, base=0x00000000, limit=0x003fffff, Read/Write, Accessed
ds:0x0017, dh=0x00c0f300, dl=0x000003ff, valid=1
Data segment, base=0x00000000, limit=0x003fffff, Read/Write, Accessed
fs:0x0000, dh=0x00c09300, dl=0x000007ff, valid=0
gs:0x0000, dh=0x00001000, dl=0x00000000, valid=0
ldtr:0x0028, dh=0x0000e200, dl=0x0c0b0040, valid=1
tr:0x0020, dh=0x0000eb00, dl=0x0c230068, valid=1
gdtr:base=0x000009c5, limit=0x3f
idtr:base=0x000001c5, limit=0x7ff
iret指令切換到原來的棧中(陷阱門那節書中有說到這點)
調用ljmp $TSS1_SEL,$0之前寄存器資訊:
eax: 0x00000001 1
ecx: 0x0000097c 2428
edx: 0x0000ef00 61184
ebx: 0x00000000 0
esp: 0x00000e6f 3695
ebp: 0x00000000 0
esi: 0x000001c5 453
edi: 0x000009c5 2501
eip: 0x0000017c
sreg:
es:0x0000, dh=0x00c09300, dl=0x000007ff, valid=0
cs:0x0008, dh=0x00c09b00, dl=0x000007ff, valid=3
Code segment, base=0x00000000, limit=0x007fffff, Execute/Read, Accessed, 32-bit
ss:0x0010, dh=0x00c09300, dl=0x000007ff, valid=7
Data segment, base=0x00000000, limit=0x007fffff, Read/Write, Accessed
ds:0x0010, dh=0x00c09300, dl=0x000007ff, valid=7
Data segment, base=0x00000000, limit=0x007fffff, Read/Write, Accessed
fs:0x0000, dh=0x00c09300, dl=0x000007ff, valid=0
gs:0x0000, dh=0x00001000, dl=0x00000000, valid=0
ldtr:0x0028, dh=0x0000e200, dl=0x0c0b0040, valid=1
tr:0x0020, dh=0x0000eb00, dl=0x0c230068, valid=1
gdtr:base=0x000009c5, limit=0x3f
idtr:base=0x000001c5, limit=0x7ff
執行之後:
切換到任務0或者1,從TSS取得寄存器資訊。特權級由0->3。
---------------------------------------
任務0一直在運行,然後收到時鐘中斷,這個時候會從特權級3躍升到特權級0,並且也會實現切換到當前任務的核心棧。