操作系統製作(2)nasm改寫linux0.00

環境:
virtual-box:版本 6.0.10 r132072 (Qt5.6.2)運行的的ubuntu18.04系統。
nasm彙編器:NASM version 2.13.02

講述bochs運行一個簡單AB任務切換的例子,運行效果如下:

一、源代碼
1)bootsect.s代碼

; boot.s 程序
; 首先利用 BIOS 中斷把內核代碼( head 代碼)加載到內存 0x10000 處,然後移動到內存 0 處。
; 最後進入保護模式,並跳轉到內存 0( head 代碼)開始處繼續運行。
BOOTSEG equ 0x07c0 ; 引導扇區(本程序)被 BIOS 加載到內存 0x7c00 處。
SYSSEG equ 0x1000 ; 內核( head)先加載到 0x10000 處,然後移動到 0x0 處。
SYSLEN equ 17 ; 內核佔用的最大磁盤扇區數。
start:
        jmp BOOTSEG:go ; 段間跳轉至 0x7c0:go 處。當本程序剛運行時所有段寄存器值

go:
        mov ax,cs ; 均爲 0。該跳轉語句會把 CS 寄存器加載爲 0x7c0(原爲 0)。
        mov ds,ax ; 讓 DS 和 SS 都指向 0x7c0 段。
        mov ss,ax
        mov sp,0x400 ; 設置臨時棧指針。其值需大於程序末端並有一定空間即可。

; 加載內核代碼到內存 0x10000 開始處。
load_system:
        mov dx,0x0000 ; 利用 BIOS 中斷 int 0x13 功能 2 從啓動盤讀取 head 代碼。
        mov cx,0x0002 ; DH - 磁頭號; DL - 驅動器號; CH - 10 位磁道號低 8 位;
        mov ax,SYSSEG ; CL - 位 7、 6 是磁道號高 2 位,位 5-0 起始扇區號(從 1 計)。
        mov es,ax ; ES:BX - 讀入緩衝區位置( 0x1000:0x0000) 。
        xor bx,bx ; AH - 讀扇區功能號; AL - 需讀的扇區數( 17)。
        mov ax,0x200+SYSLEN
        int 0x13
        jnc ok_load ; 若沒有發生錯誤則跳轉繼續運行,否則死循環。
die:
        jmp die 

; 把內核代碼移動到內存 0 開始處。共移動 8KB 字節(內核長度不超過 8KB)。
ok_load:
        cli ; 關中斷。
        mov ax, SYSSEG ; 移動開始位置 DS:SI = 0x1000:0;目的位置 ES:DI=0:0。
        mov ds, ax
        xor ax, ax
        mov es, ax
        mov cx, 0x1000 ; 設置共移動 4K 次,每次移動一個字(dw) 。
        sub si,si
        sub di,di
        rep movsw ; 執行重複移動指令。
        ; 加載 IDT 和 GDT 基地址寄存器 IDTR 和 GDTR。
        mov ax, BOOTSEG
        mov ds, ax ; 讓 DS 重新指向 0x7c0 段。
        lidt [idt_48] ; 加載 IDTR。 6 字節操作數: 2 字節表長度, 4 字節線性基地址。
        lgdt [gdt_48] ; 加載 GDTR。 6 字節操作數: 2 字節表長度, 4 字節線性基地址。

        ; 設置控制寄存器 CR0(即機器狀態字),進入保護模式。段選擇符值 8 對應 GDT 表中第 2 個段描述符。
        mov ax,0x0001 ; 在 CR0 中設置保護模式標誌 PE(位 0)。
        lmsw ax ; 然後跳轉至段選擇符值指定的段中,偏移 0 處。
        jmp 8:0 ; 注意此時段值已是段選擇符。該段的線性基地址是 0。

; 下面是全局描述符表 GDT 的內容。其中包含 3 個段描述符。第 1 個不用,另 2 個是代碼和數據段描述符。
;---------------------------------------------------------------------------------------------
;      |31|30|29|28|27|26|25|24|23|22|21|20|19|18|17|16|15|14|13|12|11|10|9|8|7|6|5|4|3|2|1|0|
;high  |_____________________word3_____________________|__________________word2______________|      
;low   |_____________________word1_____________________|__________________word0______________|
;
;---------------------------------------------------------------------------------------------
;
;---------------------------------------------------------------------------------------------
;      |31|30|29|28|27|26|25|24|23|22|21|20|19|18|17|16|15|14|13|12|11|10|9|8|7|6|5|4|3|2|1|0|
;high  |____Base_addr(31~24)___|___________|limit(19~16|___________|___TYPE__|__addr(23~16)__|      
;low   |_______________Base_addr(15~0)_________________|____________Seg_limit(15~0)__________|

gdt:
        dw 0,0,0,0 ; 段描述符 0,不用。每個描述符項佔 8 字節。

        dw 0x07FF ; 段描述符 1。 8Mb - 段限長值=2047 (2048*4096=8MB)。
        dw 0x0000 ; 段基地址=0x00000。
        dw 0x9A00 ; 是代碼段,可讀/執行。
        dw 0x00C0 ; 段屬性顆粒度=4KB, 80386。

        dw 0x07FF ; 段描述符 2。 8Mb - 段限長值=2047 (2048*4096=8MB)。
        dw 0x0000 ; 段基地址=0x00000。
        dw 0x9200 ; 是數據段,可讀寫。
        dw 0x00C0 ; 段屬性顆粒度=4KB, 80386。

; 下面分別是 LIDT 和 LGDT 指令的 6 字節操作數。
idt_48:
        dw 0 ; IDT 表長度是 0。
        dw 0,0 ; IDT 表的線性基地址也是 0。
gdt_48:
        dw 0x7ff ; GDT 表長度是 2048 字節,可容納 256 個描述符項。
        dw 0x7c00+gdt,0 ; GDT 表的線性基地址在 0x7c0 段的偏移 gdt 處。

end:
        times 510-($-$$) db 0
        dw 0xAA55 ; 引導扇區有效標誌。必須處於引導扇區最後 2 字節處。                                                   

2)head.s代碼

; head.s 包含 32 位保護模式初始化設置代碼、時鐘中斷代碼、系統調用中斷代碼和兩個任務的代碼。
; 在初始化完成之後程序移動到任務 0 開始執行,並在時鐘中斷控制下進行任務 0 和 1 之間的切換操作。
LATCH equ 11930		; 定時器初始計數值,即每隔 10 毫秒發送一次中斷請求。
		;0x08	;00001 0 00b	--->GDT1	; 是代碼段選擇符.
		;0x10	;00010 0 00b	--->GDT2	; 是數據段描述符.
SCRN_SEL equ 0x18 	;00011 0 00b	--->GDT3	; 屏幕顯示內存段選擇符。
TSS0_SEL equ 0x20 	;00100 0 00b	--->GDT4	; 任務 0 的 TSS 段選擇符。
LDT0_SEL equ 0x28	;00101 0 00b 	--->GDT5	; 任務 0 的 LDT 段選擇符。
TSS1_SEL equ 0X30 	;00110 0 00b	--->GDT6	; 任務 1 的 TSS 段選擇符。
LDT1_SEL equ 0x38 	;00111 0 00b	--->GDT7	; 任務 1 的 LDT 段選擇符。

bits 32			;nasm instruction:32 bits model.
startup_32:
; 首先加載數據段寄存器 DS、堆棧段寄存器 SS 和堆棧指針 ESP。所有段的線性基地址都是 0。
	mov eax,0x10 	; 0x10 是 GDT 中數據段選擇符。00010 0 00b --->GDT2
	mov ds,ax
	lss esp,[init_stack]
; 在新的位置重新設置 IDT 和 GDT 表。
	call setup_idt	; 設置 IDT。先把 256 箇中斷門都填默認處理過程的描述符。
	call setup_gdt	; 設置 GDT。
	mov eax,0x10	; 在改變了 GDT 之後重新加載所有段寄存器。
	mov ds,ax
	mov es,ax
	mov fs,ax
	mov gs,ax
	lss esp,[init_stack]
; 設置 8253 定時芯片。把計數器通道 0 設置成每隔 10 毫秒向中斷控制器發送一箇中斷請求信號。
	mov al,0x36	; 控制字:設置通道 0 工作在方式 3、 計數初值採用二進制。
	mov edx,0x43	; 8253 芯片控制字寄存器寫端口。
	out dx,al
	mov eax,LATCH	; 初始計數值設置爲 LATCH( 1193180/100) ,即頻率 100HZ。
	mov edx,0x40	; 通道 0 的端口。
	out dx,al	; 分兩次把初始計數值寫入通道 0。
	mov al,ah
	out dx,al
; 在 IDT 表第 8 和第 128( 0x80) 項處分別設置定時 中斷門 描述符 和 系統調用 陷阱門 描述符。
	mov eax,0x00080000	; 中斷程序屬內核,即 EAX 高字是內核代碼段選擇符 0x0008。
	mov ax,timer_interrupt	; 設置定時中斷門描述符。取定時中斷處理程序地址。
	mov dx,0x8E00		; 中斷門類型是 14(屏蔽中斷),特權級 0 或硬件使用。
	mov ecx,0x08		; 開機時 BIOS 設置的時鐘中斷向量號 8。這裏直接使用它。
	lea esi,[idt+ecx*8]	; 把 IDT 描述符 0x08 地址放入 ESI 中,然後設置該描述符。idt+64bytes offset
	mov [esi],eax
	mov [esi+4],edx

	mov ax,system_interrupt ; 設置系統調用陷阱門描述符。取系統調用處理程序地址。
	mov dx,0xef00		; 陷阱門類型是 15,特權級 3 的程序可執行。
	mov ecx,0x80		; 系統調用向量號是 0x80。
	lea esi,[idt+ecx*8]	; 把 IDT 描述符項 0x80 地址放入 ESI 中,然後設置該描述符。
	mov [esi],eax
	mov [esi+4],edx
; 好了,現在我們爲移動到任務 0(任務 A)中執行來操作堆棧內容,在堆棧中人工建立中斷返回時的場景。
	pushf			; 復位標誌寄存器 EFLAGS 中的嵌套任務標誌。
	and dword [esp],0xffffbfff
	popf
	mov eax,TSS0_SEL	; 把任務 0 的 TSS 段選擇符加載到任務寄存器 TR。
	ltr ax
	mov eax,LDT0_SEL	; 把任務 0 的 LDT 段選擇符加載到局部描述符表寄存器 LDTR。
	lldt ax			; TR 和 LDTR 只需人工加載一次,以後 CPU 會自動處理。
	mov dword[current],0
	sti			; 現在開啓中斷,並在棧中營造中斷返回時的場景。
	push long 0x17		; 把任務 0 當前局部空間數據段(堆棧段)選擇符入棧。
	push long init_stack	; 把堆棧指針入棧(也可以直接把 ESP 入棧)。
	pushf			; 把標誌寄存器值入棧。
	push long 0x0f		; 把當前局部空間代碼段選擇符入棧。
	push long task0		; 把代碼指針入棧。
	iret			; 執行中斷返回指令,從而切換到特權級 3 的任務 0 中執行。

; 以下是設置 GDT 和 IDT 中描述符項的子程序。
setup_gdt:			; 使用 6 字節操作數 lgdt_48 設置 GDT 表位置和長度。
	lgdt [lgdt_48]
	ret

; 段代碼暫時設置 IDT 表中所有 256 箇中斷門描述符都爲同一個默認值,均使用默認的中斷處理過程
;gnore_int。設置的具體方法是:首先在 eax 和 edx 寄存器對中分別設置好默認中斷門描述符的 0-3
; 節和 4-7 字節的內容,然後利用該寄存器對循環往 IDT 表中填充默認中斷門描述符內容。
setup_idt:			; 把所有 256 箇中斷門描述符設置爲使用默認處理過程。
	lea edx,[ignore_int]	; 設置方法與設置定時中斷門描述符的方法一樣。
	mov eax,0x00080000	; 選擇符爲 0x0008。
	mov ax,dx
	mov dx,0x8E00		; 中斷門類型,特權級爲 0。
	lea edi,[idt]
	mov ecx,256		; 循環設置所有 256 個門描述符項。
rp_idt:
       	mov [edi],eax
	mov [edi+4],edx
	add edi,8
	dec ecx
	jne rp_idt
	lidt [lidt_48]		; 最後用 6 字節操作數加載 IDTR 寄存器。
	ret

; 顯示字符子程序。取當前光標位置並把 AL 中的字符顯示在屏幕上。整屏可顯示 80 X 25 個字符。
write_char:
	push gs			; 首先保存要用到的寄存器, EAX 由調用者負責保存。
	push ebx
	mov ebx,SCRN_SEL	; 然後讓 GS 指向顯示內存段( 0xb8000) 。
	mov gs,bx
	mov bx,[scr_loc]	; 再從變量 scr_loc 中取目前字符顯示位置值。
	shl ebx,1		; 因爲在屏幕上每個字符還有一個屬性字節,因此字符
	mov [gs:ebx],al		; 實際顯示位置對應的顯示內存偏移地址要乘 2。
	shr ebx,1		; 把字符放到顯示內存後把位置值除 2 加 1,此時位置值對
	inc ebx			; 應下一個顯示位置。如果該位置大於 2000,則復位成 0。
	cmp ebx,2000
	jb .1
	mov ebx,0
.1:
	mov [scr_loc],ebx	; 最後把這個位置值保存起來( scr_loc),
	pop ebx			; 並彈出保存的寄存器內容,返回。
	pop gs
	ret

; 以下是 3 箇中斷處理程序:默認中斷、定時中斷和系統調用中斷。
; ignore_int 是默認的中斷處理程序,若系統產生了其他中斷,則會在屏幕上顯示一個字符'C'。
align 4
ignore_int:
	push ds
	push eax
	mov eax,0x10		; 首先讓 DS 指向內核數據段,因爲中斷程序屬於內核。
	mov ds,ax
	mov eax,67		; 在 AL 中存放字符'C'的代碼,調用顯示程序顯示在屏幕上。
	call write_char
	pop eax
	pop ds
	iret

; 這是定時中斷處理程序。其中主要執行任務切換操作。
align 4
timer_interrupt:
	push ds
	push eax
	mov eax,0x10		; 首先讓 DS 指向內核數據段。
	mov ds,ax
	mov al,0x20		; 然後立刻允許其他硬件中斷,即向 8259A 發送 EOI 命令。
	out 0x20,al
	mov eax,1		; 接着判斷當前任務,若是任務 1 則去執行任務 0,或反之。
	cmp [current],eax
	je .1
	mov [current],eax	; 若當前任務是 0,則把 1 存入 current,並跳轉到任務 1
	jmp TSS1_SEL:0		; 去執行。注意跳轉的偏移值無用,但需要寫上。
	jmp .2
.1:
	mov dword[current],0
	jmp TSS0_SEL:0 
.2:
	pop eax
	pop ds
	iret

; 系統調用中斷 int 0x80 處理程序。該示例只有一個顯示字符功能。
align 4
system_interrupt:
	push ds
	push edx
	push ecx
	push ebx
	push eax
	mov edx,0x10		; 首先讓 DS 指向內核數據段。
	mov ds,dx
	call write_char		; 然後調用顯示字符子程序 write_char,顯示 AL 中的字符。
	pop eax
	pop ebx
	pop ecx
	pop edx
	pop ds
	iret

;/*********************************************/
current:dd 0 ; 當前任務號( 0 或 1)。
scr_loc:dd 0 ; 屏幕當前顯示位置。按從左上角到右下角順序顯示。

align 4
lidt_48:
	dw 256*8-1 ; 加載 IDTR 寄存器的 6 字節操作數:表長度和基地址。
	dd idt
lgdt_48:
	dw (end_gdt-gdt)-1 ; 加載 GDTR 寄存器的 6 字節操作數:表長度和基地址。
	dd gdt

align 8
; LDT
;---------------------------------------------------------------------------------------------
;      |31|30|29|28|27|26|25|24|23|22|21|20|19|18|17|16|15|14|13|12|11|10|9|8|7|6|5|4|3|2|1|0|
;high  |_____________________word3_____________________|__________________word2______________|
;low   |_____________________word1_____________________|__________________word0______________|
;
;---------------------------------------------------------------------------------------------
;
;---------------------------------------------------------------------------------------------
;      |31|30|29|28|27|26|25|24|23|22|21|20|19|18|17|16|15|14|13|12|11|10|9|8|7|6|5|4|3|2|1|0|
;high  |___________entry_offset_addr(31~16)____________|_P|_DPL_|____________________________|
;low   |_______________Seg_select______________________|_______entry_offset_addr(15~0)_______|

idt:
	times 256 dq 0 ; IDT 空間。共 256 個門描述符,每個 8 字節,佔用 2KB。

; 下面是全局描述符表 GDT 的內容。其中包含 3 個段描述符。第 1 個不用,另 2 個是代碼和數據段描述符。
;---------------------------------------------------------------------------------------------
;      |31|30|29|28|27|26|25|24|23|22|21|20|19|18|17|16|15|14|13|12|11|10|9|8|7|6|5|4|3|2|1|0|
;high  |_____________________word3_____________________|__________________word2______________|
;low   |_____________________word1_____________________|__________________word0______________|
;
;---------------------------------------------------------------------------------------------
;
;---------------------------------------------------------------------------------------------
;      |31|30|29|28|27|26|25|24|23|22|21|20|19|18|17|16|15|14|13|12|11|10|9|8|7|6|5|4|3|2|1|0|
;high  |____Base_addr(31~24)___|___________|limit(19~16|___________|___TYPE__|__addr(23~16)__|
;low   |_______________Base_addr(15~0)_________________|____________Seg_limit(15~0)__________|

gdt:
	dq 0x0000000000000000 ; GDT 表。第 1 個描述符不用。
	dq 0x00c09a00000007ff ; 第 2 個是內核代碼段描述符。其選擇符是 0x08。
	dq 0x00c09200000007ff ; 第 3 個是內核數據段描述符。其選擇符是 0x10。
	dq 0x00c0920b80000002 ; 第 4 個是顯示內存段描述符。其選擇符是 0x18。
	dw 0x68, tss0, 0xe900, 0x0 ; 第 5 個是 TSS0 段的描述符。其選擇符是 0x20
	dw 0x40, ldt0, 0xe200, 0x0 ; 第 6 個是 LDT0 段的描述符。其選擇符是 0x28
	dw 0x68, tss1, 0xe900, 0x0 ; 第 7 個是 TSS1 段的描述符。其選擇符是 0x30
	dw 0x40, ldt1, 0xe200, 0x0 ; 第 8 個是 LDT1 段的描述符。其選擇符是 0x38
end_gdt:
	times 128 dd 0 ; 初始內核堆棧空間。
init_stack: ; 剛進入保護模式時用於加載 SS:ESP 堆棧指針值。
	dd init_stack ; 堆棧段偏移位置。
	dw 0x10 ; 堆棧段同內核數據段。

; 下面是任務 0 的 LDT 表段中的局部段描述符。
align 8
ldt0:
	dq 0x0000000000000000 ; 第 1 個描述符,不用。
	dq 0x00c0fa00000003ff ; 第 2 個局部代碼段描述符,對應選擇符是 0x0f。
	dq 0x00c0f200000003ff ; 第 3 個局部數據段描述符,對應選擇符是 0x17。
; 下面是任務 0 的 TSS 段的內容。注意其中標號等字段在任務切換時不會改變。
tss0:
	dd 0			; back link
	dd krn_stk0, 0x10	; esp0, ss0
	dd 0, 0, 0, 0, 0	; esp1, ss1, esp2, ss2, cr3
	dd 0, 0, 0, 0, 0	; eip, eflags, eax, ecx, edx
	dd 0, 0, 0, 0, 0	; ebx esp, ebp, esi, edi
	dd 0, 0, 0, 0, 0, 0	; es, cs, ss, ds, fs, gs
	dd LDT0_SEL, 0x8000000	; ldt, trace bitmap

times 128 dd 0 ; 這是任務 0 的內核棧空間。
krn_stk0:

; 下面是任務 1 的 LDT 表段內容和 TSS 段內容。
align 8
ldt1:
	dq 0x0000000000000000 ; 第 1 個描述符,不用。
	dq 0x00c0fa00000003ff ; 選擇符是 0x0f,基地址 equ 0x00000。
	dq 0x00c0f200000003ff ; 選擇符是 0x17,基地址 equ 0x00000。

tss1:
	dd 0					;/* back link */
	dd krn_stk1, 0x10			;/* esp0, ss0 */
	dd 0, 0, 0, 0, 0			;/* esp1, ss1, esp2, ss2, cr3 */
	dd task1, 0x200				;/* eip, eflags */
	dd 0, 0, 0, 0				;/* eax, ecx, edx, ebx */
	dd usr_stk1, 0, 0, 0			;/* esp, ebp, esi, edi */
	dd 0x17,0x0f,0x17,0x17,0x17,0x17	;/* es, cs, ss, ds, fs, gs */
	dd LDT1_SEL, 0x8000000			;/* ldt, trace bitmap */

	times 128 dd 0				; 這是任務 1 的內核棧空間。其用戶棧直接使用初始棧空間。
krn_stk1:

; 下面是任務 0 和任務 1 的程序,它們分別循環顯示字符'A'和'B'。
task0:
	mov eax,0x17				; 首先讓 DS 指向任務的局部數據段。
	mov ds,ax				; 因爲任務沒有使用局部數據,所以這兩句可省略。
	mov al,65				; 把需要顯示的字符'A'放入 AL 寄存器中。
	int 0x80				; 執行系統調用,顯示字符。
	mov ecx,0xfff				; 執行循環,起延時作用。
.1:
	loop .1
	jmp task0				; 跳轉到任務代碼開始處繼續顯示字符。
task1:
	mov al,66				; 把需要顯示的字符'B'放入 AL 寄存器中。
	int 0x80				; 執行系統調用,顯示字符。
	mov ecx,0xfff				; 延時一段時間,並跳轉到開始處繼續循環顯示。
.1:
	loop .1
	jmp task1

	times 128 dd 0				; 這是任務 1 的用戶棧空間。
usr_stk1:

二、相關技術介紹
1)全局描述符

和全局描述符相關的有:
!)全局描述符表寄存器 GDTR
GDTR 寄存器(48位)格式如下:
在這裏插入圖片描述
指令 LGDT 用於加載GDTR 寄存器的內容。

範例如下:

lgdt [gdt_48]
gdt_48:
        dw 0x7ff        ; (16位表長度)GDT 表長度是 2048 字節,可容納 256 個描述符項。
        dw 0x7c00+gdt,0 ; (32位線性基地址)GDT 表的線性基地址在 0x7c0 段的偏移 gdt 處。

每一個GDT表項稱爲一個段描述符(64位),32位模式下,可以通過段選擇符(16位)來索引到對應的段描述符。

段描述符(64位)的通用格式如下:
在這裏插入圖片描述
根據TYPE的值不同,段描述符(64位)可以分爲代碼段、數據段和系統段描述符。
而代碼段、數據段描述符又可細分爲十幾種不同的描述符。

注意:可以看出,段描述符由基地址+段限長組成,描述的是一個內存段的範圍,沒有偏移值什麼的。

!!)段選擇符
進入32位保護模式後,段寄存器(cs,ds,es,gs,fs等,都是16位寄存器)選用的都是段選擇符。
段選擇符(16位)格式如下:
在這裏插入圖片描述
段選擇符 3 個字段內容:
 請求特權級 RPL( Requested Privilege Level);0~1位
 表指示標誌 TI( Table Index); 2位
 索引值( Index)。3~15位

TI=0 表示描述符在 GDT 中;
TI=1 表示描述符在 LDT 中。
索引字段給出了描述符在 GDT 或 LDT 表中的索引項號。
範例如下:

jmp 8,0

8是cs寄存器中的值,轉換爲二進制00001 0 00b,查找段選擇符格式可知:
索引值爲00001,即第一個段描述符;
T1=0,即段描述符在GDT中;
特權級爲0.

!!!)GDT表(64位)
範例如下:

; 下面是全局描述符表 GDT 的內容。其中包含 3 個段描述符。第 1 個不用,另 2 個是代碼和數據段描述符。
gdt: 
        dw 0,0,0,0 ; 段描述符 0,不用。每個描述符項佔 8 字節。

        dw 0x07FF ; 段描述符 1。 8Mb - 段限長值=2047 (2048*4096=8MB)。
        dw 0x0000 ; 段基地址=0x00000。
        dw 0x9A00 ; 是代碼段,可讀/執行。
        dw 0x00C0 ; 段屬性顆粒度=4KB, 80386。

        dw 0x07FF ; 段描述符 2。 8Mb - 段限長值=2047 (2048*4096=8MB)。
        dw 0x0000 ; 段基地址=0x00000。
        dw 0x9200 ; 是數據段,可讀寫。
        dw 0x00C0 ; 段屬性顆粒度=4KB, 80386。

對應於上面jmp 8,0的解析,可知,選擇的段是段描述符1。根據段描述符1的內容,查看上面的GDT表(64位)格式,可知,對應段基地址0x00000000。因此,程序跳轉到線性地址0x00000000處運行。

2)中斷描述符

和中斷描述符相關的有:
!)中斷描述符表寄存器 IDTR
IDTR寄存器(48位)格式如下:
在這裏插入圖片描述
可知,中斷描述符表寄存器和全局描述符表寄存器是一樣的結構。

指令 LIDT 用於加載IDTR 寄存器的內容。

範例如下:

lidt [idt_48]

align 4
lidt_48:
        dw 256*8-1              ; 加載 IDTR 寄存器的 6 字節操作數:表長度和基地址。
        dd idt 

align 8
idt:
        times 256 dq 0          ; IDT 空間。共 256 個門描述符,每個 8 字節,佔用 2KB。

IDT 表中可以存放三種類型的門描述符:
 中斷門( Interrupt gate)描述符
 陷阱門( Trap gate)描述符
 任務門( Task gate)描述符

IDT表項(64位)格式如下:
在這裏插入圖片描述
可以看出,IDT表項和GDT表項,格式是不一樣的。
注意:可以看出,IDT表中的門描述符由 段選擇符(16位)+偏移值(32位) 組成。
這個段選擇符一般是內核代碼段0x00080000。

中斷過程調用如下:
,圖。

中斷向量相當於一個IDT表索引,查找中斷描述符表(64位),然後根據中斷描述符表(64位)的段選擇符(16位)查找GDT表項,得到段基址+段限長,然後根據IDT表項的偏移值(32位),得到具體的線性地址(異常或中斷處理過程的地址)。

###設置IDT表項,int IDT段索引就可以跳轉到相應的偏移地址處執行程序。

以上GDT表和IDT表描述完畢,如果要切換任務,則還需要TSS任務表

3)任務

和任務相關的有:
!)任務寄存器 TR(16位)
TR 寄存器用於存放當前任務 TSS 段的 16 位段選擇符、 32 位基地址、 16 位段長度和描述符屬性值。
它引用 GDT 表中的一個 TSS 類型的描述符。指令 LTR 和 STR 分別用於加載和保存 TR 寄存器的段選擇符
部分。當使用 LTR 指令把選擇符加載進任務寄存器時, TSS 描述符中的段基地址、段限長度以及描述符屬
性會被自動地加載到任務寄存器中。當執行任務切換時,處理器會把新任務的 TSS 的段選擇符和段描述符
自動地加載進任務寄存器 TR 中。

任務寄存器 TR格式如下:
在這裏插入圖片描述
範例如下:

TSS0_SEL equ 0x20       ;00100 0 00b    --->GDT4        ; 任務 0 的 TSS 段選擇符。

        mov eax,TSS0_SEL ; 把任務 0 的 TSS 段選擇符加載到任務寄存器 TR。
        ltr ax

gdt:
        dq 0x0000000000000000 ; GDT 表。第 1 個描述符不用。
        dq 0x00c09a00000007ff ; 第 2 個是內核代碼段描述符。其選擇符是 0x08。
        dq 0x00c09200000007ff ; 第 3 個是內核數據段描述符。其選擇符是 0x10。
        dq 0x00c0920b80000002 ; 第 4 個是顯示內存段描述符。其選擇符是 0x18。
        dw 0x68, tss0, 0xe900, 0x0 ; 第 5 個是 TSS0 段的描述符。其選擇符是 0x20
        dw 0x40, ldt0, 0xe200, 0x0 ; 第 6 個是 LDT0 段的描述符。其選擇符是 0x28
        dw 0x68, tss1, 0xe900, 0x0 ; 第 7 個是 TSS1 段的描述符。其選擇符是 0x30
        dw 0x40, ldt1, 0xe200, 0x0 ; 第 8 個是 LDT1 段的描述符。其選擇符是 0x38
; 下面是任務 0 的 TSS 段的內容。注意其中標號等字段在任務切換時不會改變。
tss0:
        dd 0                    ; back link
        dd krn_stk0, 0x10       ; esp0, ss0
        dd 0, 0, 0, 0, 0        ; esp1, ss1, esp2, ss2, cr3
        dd 0, 0, 0, 0, 0        ; eip, eflags, eax, ecx, edx
        dd 0, 0, 0, 0, 0        ; ebx esp, ebp, esi, edi
        dd 0, 0, 0, 0, 0, 0     ; es, cs, ss, ds, fs, gs
        dd LDT0_SEL, 0x8000000  ; ldt, trace bitmap

根據任務寄存器 TR(16位)的段選擇符,查詢GDT表,找到 dw 0x68, tss0, 0xe900, 0x0。繼續查詢GDT表項的格式,得到:
32位基地址:tss0
段限長:0x68
屬性:0xe900—>0x1110 1001 0000 0000
P(15):-- 段存在=1
DPL(14-13):=3
S(12):-- 描述符類型(0-系統;1-代碼或數據)=0
TYPE(11-8):0。數據段。

有關代碼或數據段的類型,暫且不提。

!!)TSS 段描述符格式如下:
在這裏插入圖片描述
與其他段一樣,任務狀態段 TSS 也是使用段描述符來定義。TYPE是10B1。

類型字段 TYPE 中的忙標誌 B 用於指明任務是否處於忙狀態。忙狀態的任務是當前正在執行的任務或
等待執行(被掛起)的任務。值爲 0b1001 的類型字段表明任務處於非活動狀態;而值爲 0b1011 的類型字
段表示任務正忙。任務是不可以遞歸執行的,因此處理器使用忙標誌 B 來檢測任何企圖對被中斷執行任務
的調用。
!!!)任務狀態段 TSS
用於恢復一個任務執行的處理器狀態信息被保存在稱爲任務狀態段 TSS( Task state segment)的段中。
在這裏插入圖片描述
範例如下:

; 下面是任務 0 的 TSS 段的內容。注意其中標號等字段在任務切換時不會改變。
tss0:
        dd 0                    ; back link
        dd krn_stk0, 0x10       ; esp0, ss0
        dd 0, 0, 0, 0, 0        ; esp1, ss1, esp2, ss2, cr3
        dd 0, 0, 0, 0, 0        ; eip, eflags, eax, ecx, edx
        dd 0, 0, 0, 0, 0        ; ebx esp, ebp, esi, edi
        dd 0, 0, 0, 0, 0, 0     ; es, cs, ss, ds, fs, gs
        dd LDT0_SEL, 0x8000000  ; ldt, trace bitmap
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章