天天看點

分頁機制和動态頁面配置設定

分頁機制概述

  分頁其實就是記憶體塊的映射管理。在我們之前的章節中,我們都是使用的分段管理模式,處理器中負責分段的部件是段部件,段管理機制是Intel處理器最基本的處理機制,在任何時候都是無法關閉的。而當開啟了分頁管理之後,處理器會把4GB的記憶體分成長度相同的段,也就是說用長度固定的頁來代替長度不一的段。頁的配置設定由處理器固件來進行,可以實作非常高效的操作。頁的最小機關是4KB,也就是說4GB的記憶體可以分成1048576個頁(頁的實體位址的低12位全是0)。

  在分頁模式下,作業系統可以建立一個為所有任務共用的4GB虛拟記憶體空間。也可以為每一個任務建立一個獨立的4GB虛拟記憶體空間,當一個程式加載的時候,作業系統既要在虛拟記憶體空間中配置設定空間,而且也要在實體記憶體中配置設定相應的也頁面。另外,如果允許頁共享,多個段或者多個程式可以用同一個頁來存放各自的資料。4GB虛拟記憶體空間隻是一個用來訓示記憶體使用狀況的一個機制,當作業系統加載一個程式并且建立為任務時,作業系統在虛拟記憶體空間中尋找空閑的段,并映射到空閑的頁中取去。然後,到真正加載程式的時候,再把原本屬于段的資料按頁的尺寸拆分,分開寫入相應的頁中。頁最大的好處就是友善作業系統進行記憶體管理,特别是虛拟記憶體管理,每個任務都可以有4GB虛拟記憶體,但是假如機器沒有那麼大的記憶體,作業系統也可以認為确實存在那麼大的記憶體,當一個程式使用的記憶體超過了實際的實體記憶體,那麼作業系統就會搜尋那些暫時用不到的頁,并且把他們轉移到磁盤中,并且調入馬上要使用的頁。(當然這種操作非常地花費時間,這就是為什麼小記憶體的電腦會有很嚴重的卡頓現象)。

  Intel處理器的最基礎的分頁管理機制就是二級管理機制。(當然現在更新的處理器支援更複雜的分頁操作,但是教材也沒有提及。)如果每個操使用直接分頁管理(也就是把4GB的記憶體直接分成1048576個頁),這需要1048576個表項,每個表項是4位元組,是以映射表的總大小是4MB。事實上程式往往用不到4GB的記憶體,是以1級映射是一個很嚴重的浪費。但是如果采用階層化分頁管理,那麼就會産生巨大的記憶體節約。所謂Intel的分頁管理,其實就是把頁拆成3個部分(頁目錄,頁表和頁)。

  頁目錄(Page Directory Table,PDT) 的實體位址由PDBR(Page Directory Base Register,也就是CR3寄存器) 指定,每個任務都可以有自己的頁目錄。每個任務的TSS段就有自己的CR3的實體位址。每次進行任務切換的時候,CR3的内容都會被替換改為新任務的CR3域中的實體位址。頁目錄也是一個的頁。但是他裡面存放的是頁表的位址,是以頁目錄可以指向1024個頁表。

  頁表也可以指向1024個頁,每個項也是4個位元組,同樣頁表的大小也是頁的大小。

  處理器有頁部件,專門負責線性位址到實體位址的轉換工作。它首先将段部件送來的32位線性位址截成3段,分别是高10位(頁目錄的索引),中間的10位(頁表的索引)和低12位(頁内偏移)。作業系統要負責填寫頁目錄和頁表位址,然後程式的記憶體通路就可以像上圖那樣進行轉換了。

分頁機制和動态頁面配置設定

頁目錄、頁表和CR3的填寫

分頁機制和動态頁面配置設定
  • P(Present)是存在位,為“1”時,表示頁表或者頁位于記憶體中,否則,表示頁表或者頁不在記憶體中,必須先予以建立,或者從磁盤調入記憶體後方可使用。
  • RW(Read/Write)是讀/寫位,為“0”時表示這樣的頁隻能讀取,為“1”時,可讀可寫。

    -US(User/Supervisor)是使用者/管理位。為“1”時,允許所有特權級别的程式通路;為“0”時,隻允許特權級為0,1,2的程式通路,特權級為3的程式不能通路。

  • PWT(Page-Level Write-Through)是頁級通寫位,和高速緩存有關,“通寫”是處理器高速緩存的一種工作方式,這意味用來間接決定是否采用此種方式來改善頁面的通路效率。
  • A(Accessed)是通路位。該位由處理器固件來設定,用來訓示此表項所指向的頁是否被通路過。(這個坑爹的屬性将會在練習題中展現)。
  • D(Dirty)是髒位。該位由處理器固件來設定,用來訓示此表項所指向的頁是否被寫入資料。(和A位一樣,練習題會有展現)。
  • PAT(Page Attribute Table)頁屬性表支援位。此位涉及更為複雜的分頁系統。和頁高速緩存有關。
  • G(Global)是全局位,用來隻是該表項指向的頁是否為全局屬性。如果頁是全局屬性的,那麼,他将一直在高速緩存中一直儲存(位址轉換速度會加快)。因為頁高速緩存的緩存容量有限,隻能存放頻繁使用的那些表項。而且,當因為任務切換等原因改變CR3寄存器的内容時,整個頁高速緩存的内容都會被重新整理。
  • AVL位被處理器忽略,軟體可以使用。

代碼清單

;代碼清單16-2
         ;檔案名:c16.asm
         ;檔案說明:使用者程式 
         program_length   dd program_end          ;程式總長度#0x00
         entry_point      dd start                ;程式入口點#0x04
         salt_position    dd salt_begin           ;SALT表起始偏移量#0x08 
         salt_items       dd (salt_end-salt_begin)/256 ;SALT條目數#0x0C

;-------------------------------------------------------------------------------

         ;符号位址檢索表
         salt_begin:                                     

         PrintString      db  '@PrintString'
                     times 256-($-PrintString) db 0
                     
         TerminateProgram db  '@TerminateProgram'
                     times 256-($-TerminateProgram) db 0
;-------------------------------------------------------------------------------

         reserved  times 256*500 db 0            ;保留一個空白區,以示範分頁

;-------------------------------------------------------------------------------
         ReadDiskData     db  '@ReadDiskData'
                     times 256-($-ReadDiskData) db 0
         
         PrintDwordAsHex  db  '@PrintDwordAsHexString'
                     times 256-($-PrintDwordAsHex) db 0
         
         salt_end:

         message_0        db  0x0d,0x0a,
                          db  '  ............User task is running with '
                          db  'paging enabled!............',0x0d,0x0a,0

         space            db  0x20,0x20,0
         
;-------------------------------------------------------------------------------
      [bits 32]
;-------------------------------------------------------------------------------

start:
          
         mov ebx,message_0
         call far [PrintString]
         
         xor esi,esi
         mov ecx,88
  .b1:
         mov ebx,space
         call far [PrintString] 
         
         mov edx,[esi*4]
         call far [PrintDwordAsHex]
         
         inc esi
         loop .b1 
        
         call far [TerminateProgram]              ;退出,并将控制權傳回到核心 
    
;-------------------------------------------------------------------------------
program_end:
           
;代碼清單16-1
         ;檔案名:c16_core.asm
         ;檔案說明:保護模式微型核心程式 

         ;以下常量定義部分。核心的大部分内容都應當固定
         core_code_seg_sel     equ  0x38    ;核心代碼段選擇子
         core_data_seg_sel     equ  0x30    ;核心資料段選擇子 
         sys_routine_seg_sel   equ  0x28    ;系統公共例程代碼段的選擇子 
         video_ram_seg_sel     equ  0x20    ;視訊顯示緩沖區的段選擇子
         core_stack_seg_sel    equ  0x18    ;核心堆棧段選擇子
         mem_0_4_gb_seg_sel    equ  0x08    ;整個0-4GB記憶體的段的選擇子

;-------------------------------------------------------------------------------
         ;以下是系統核心的頭部,用于加載核心程式 
         core_length      dd core_end       ;核心程式總長度#00

         sys_routine_seg  dd section.sys_routine.start
                                            ;系統公用例程段位置#04

         core_data_seg    dd section.core_data.start
                                            ;核心資料段位置#08

         core_code_seg    dd section.core_code.start
                                            ;核心代碼段位置#0c


         core_entry       dd start          ;核心代碼段入口點#10
                          dw core_code_seg_sel

;===============================================================================
         [bits 32]
;===============================================================================
SECTION sys_routine vstart=0                ;系統公共例程代碼段 
;-------------------------------------------------------------------------------
         ;字元串顯示例程
put_string:                                 ;顯示0終止的字元串并移動光标 
                                            ;輸入:DS:EBX=串位址
         push ecx
  .getc:
         mov cl,[ebx]
         or cl,cl
         jz .exit
         call put_char
         inc ebx
         jmp .getc

  .exit:
         pop ecx
         retf                               ;段間傳回

;-------------------------------------------------------------------------------
put_char:                                   ;在目前光标處顯示一個字元,并推進
                                            ;光标。僅用于段内調用 
                                            ;輸入:CL=字元ASCII碼 
         pushad

         ;以下取目前光标位置
         mov dx,0x3d4
         mov al,0x0e
         out dx,al
         inc dx                             ;0x3d5
         in al,dx                           ;高字
         mov ah,al

         dec dx                             ;0x3d4
         mov al,0x0f
         out dx,al
         inc dx                             ;0x3d5
         in al,dx                           ;低字
         mov bx,ax                          ;BX=代表光标位置的16位數

         cmp cl,0x0d                        ;回車符?
         jnz .put_0a
         mov ax,bx
         mov bl,80
         div bl
         mul bl
         mov bx,ax
         jmp .set_cursor

  .put_0a:
         cmp cl,0x0a                        ;換行符?
         jnz .put_other
         add bx,80
         jmp .roll_screen

  .put_other:                               ;正常顯示字元
         push es
         mov eax,video_ram_seg_sel          ;0x800b8000段的選擇子
         mov es,eax
         shl bx,1
         mov [es:bx],cl
         pop es

         ;以下将光标位置推進一個字元
         shr bx,1
         inc bx

  .roll_screen:
         cmp bx,2000                        ;光标超出螢幕?滾屏
         jl .set_cursor

         push ds
         push es
         mov eax,video_ram_seg_sel
         mov ds,eax
         mov es,eax
         cld
         mov esi,0xa0                       ;小心!32位模式下movsb/w/d 
         mov edi,0x00                       ;使用的是esi/edi/ecx 
         mov ecx,1920
         rep movsd
         mov bx,3840                        ;清除螢幕最底一行
         mov ecx,80                         ;32位程式應該使用ECX
  .cls:
         mov word[es:bx],0x0720
         add bx,2
         loop .cls

         pop es
         pop ds

         mov bx,1920

  .set_cursor:
         mov dx,0x3d4
         mov al,0x0e
         out dx,al
         inc dx                             ;0x3d5
         mov al,bh
         out dx,al
         dec dx                             ;0x3d4
         mov al,0x0f
         out dx,al
         inc dx                             ;0x3d5
         mov al,bl
         out dx,al

         popad
         
         ret                                

;-------------------------------------------------------------------------------
read_hard_disk_0:                           ;從硬碟讀取一個邏輯扇區
                                            ;EAX=邏輯扇區号
                                            ;DS:EBX=目标緩沖區位址
                                            ;傳回:EBX=EBX+512
         push eax 
         push ecx
         push edx
      
         push eax
         
         mov dx,0x1f2
         mov al,1
         out dx,al                          ;讀取的扇區數

         inc dx                             ;0x1f3
         pop eax
         out dx,al                          ;LBA位址7~0

         inc dx                             ;0x1f4
         mov cl,8
         shr eax,cl
         out dx,al                          ;LBA位址15~8

         inc dx                             ;0x1f5
         shr eax,cl
         out dx,al                          ;LBA位址23~16

         inc dx                             ;0x1f6
         shr eax,cl
         or al,0xe0                         ;第一硬碟  LBA位址27~24
         out dx,al

         inc dx                             ;0x1f7
         mov al,0x20                        ;讀指令
         out dx,al

  .waits:
         in al,dx
         and al,0x88
         cmp al,0x08
         jnz .waits                         ;不忙,且硬碟已準備好資料傳輸 

         mov ecx,256                        ;總共要讀取的字數
         mov dx,0x1f0
  .readw:
         in ax,dx
         mov [ebx],ax
         add ebx,2
         loop .readw

         pop edx
         pop ecx
         pop eax
      
         retf                               ;段間傳回 

;-------------------------------------------------------------------------------
;彙編語言程式是極難一次成功,而且調試非常困難。這個例程可以提供幫助 
put_hex_dword:                              ;在目前光标處以十六進制形式顯示
                                            ;一個雙字并推進光标 
                                            ;輸入:EDX=要轉換并顯示的數字
                                            ;輸出:無
         pushad
         push ds
      
         mov ax,core_data_seg_sel           ;切換到核心資料段 
         mov ds,ax
      
         mov ebx,bin_hex                    ;指向核心資料段内的轉換表
         mov ecx,8
  .xlt:    
         rol edx,4
         mov eax,edx
         and eax,0x0000000f
         xlat
      
         push ecx
         mov cl,al                           
         call put_char
         pop ecx
       
         loop .xlt
      
         pop ds
         popad
         
         retf
      
;-------------------------------------------------------------------------------
set_up_gdt_descriptor:                      ;在GDT内安裝一個新的描述符
                                            ;輸入:EDX:EAX=描述符 
                                            ;輸出:CX=描述符的選擇子
         push eax
         push ebx
         push edx

         push ds
         push es

         mov ebx,core_data_seg_sel          ;切換到核心資料段
         mov ds,ebx

         sgdt [pgdt]                        ;以便開始處理GDT

         mov ebx,mem_0_4_gb_seg_sel
         mov es,ebx

         movzx ebx,word [pgdt]              ;GDT界限
         inc bx                             ;GDT總位元組數,也是下一個描述符偏移
         add ebx,[pgdt+2]                   ;下一個描述符的線性位址

         mov [es:ebx],eax
         mov [es:ebx+4],edx

         add word [pgdt],8                  ;增加一個描述符的大小

         lgdt [pgdt]                        ;對GDT的更改生效

         mov ax,[pgdt]                      ;得到GDT界限值
         xor dx,dx
         mov bx,8
         div bx                             ;除以8,去掉餘數
         mov cx,ax
         shl cx,3                           ;将索引号移到正确位置

         pop es
         pop ds

         pop edx
         pop ebx
         pop eax

         retf
;-------------------------------------------------------------------------------
make_seg_descriptor:                        ;構造存儲器和系統的段描述符
                                            ;輸入:EAX=線性基位址
                                            ;      EBX=段界限
                                            ;      ECX=屬性。各屬性位都在原始
                                            ;          位置,無關的位清零 
                                            ;傳回:EDX:EAX=描述符
         mov edx,eax
         shl eax,16
         or ax,bx                           ;描述符前32位(EAX)構造完畢

         and edx,0xffff0000                 ;清除基位址中無關的位
         rol edx,8
         bswap edx                          ;裝配基址的31~24和23~16  (80486+)

         xor bx,bx
         or edx,ebx                         ;裝配段界限的高4位

         or edx,ecx                         ;裝配屬性

         retf

;-------------------------------------------------------------------------------
make_gate_descriptor:                       ;構造門的描述符(調用門等)
                                            ;輸入:EAX=門代碼在段内偏移位址
                                            ;       BX=門代碼所在段的選擇子 
                                            ;       CX=段類型及屬性等(各屬
                                            ;          性位都在原始位置)
                                            ;傳回:EDX:EAX=完整的描述符
         push ebx
         push ecx
      
         mov edx,eax
         and edx,0xffff0000                 ;得到偏移位址高16位 
         or dx,cx                           ;組裝屬性部分到EDX
       
         and eax,0x0000ffff                 ;得到偏移位址低16位 
         shl ebx,16                          
         or eax,ebx                         ;組裝段選擇子部分
      
         pop ecx
         pop ebx
      
         retf                                   
                             
;-------------------------------------------------------------------------------
allocate_a_4k_page:                         ;配置設定一個4KB的頁
                                            ;輸入:無
                                            ;輸出:EAX=頁的實體位址
         push ebx
         push ecx
         push edx
         push ds
         
         mov eax,core_data_seg_sel
         mov ds,eax
         
         xor eax,eax
  .b1:
         bts [page_bit_map],eax
         jnc .b2
         inc eax
         cmp eax,page_map_len*8
         jl .b1
         
         mov ebx,message_3
         call sys_routine_seg_sel:put_string
         hlt                                ;沒有可以配置設定的頁,停機 
         
  .b2:
         shl eax,12                         ;乘以4096(0x1000) 
         
         pop ds
         pop edx
         pop ecx
         pop ebx
         
         ret
         
;-------------------------------------------------------------------------------
alloc_inst_a_page:                          ;配置設定一個頁,并安裝在目前活動的
                                            ;層級分頁結構中
                                            ;輸入:EBX=頁的線性位址
         push eax
         push ebx
         push esi
         push ds
         
         mov eax,mem_0_4_gb_seg_sel
         mov ds,eax
         
         ;檢查該線性位址所對應的頁表是否存在
         mov esi,ebx
         and esi,0xffc00000
         shr esi,20                         ;得到頁目錄索引,并乘以4 
         or esi,0xfffff000                  ;頁目錄自身的線性位址+表内偏移 

         test dword [esi],0x00000001        ;P位是否為“1”。檢查該線性位址是 
         jnz .b1                            ;否已經有對應的頁表
          
         ;建立該線性位址所對應的頁表 
         call allocate_a_4k_page            ;配置設定一個頁做為頁表 
         or eax,0x00000007
         mov [esi],eax                      ;在頁目錄中登記該頁表
          
  .b1:
         ;開始通路該線性位址所對應的頁表 
         mov esi,ebx
         shr esi,10
         and esi,0x003ff000                 ;或者0xfffff000,因高10位是零 
         or esi,0xffc00000                  ;得到該頁表的線性位址
         
         ;得到該線性位址在頁表内的對應條目(頁表項) 
         and ebx,0x003ff000
         shr ebx,10                         ;相當于右移12位,再乘以4
         or esi,ebx                         ;頁表項的線性位址 
         call allocate_a_4k_page            ;配置設定一個頁,這才是要安裝的頁
         or eax,0x00000007
         mov [esi],eax 
          
         pop ds
         pop esi
         pop ebx
         pop eax
         
         retf  

;-------------------------------------------------------------------------------
create_copy_cur_pdir:                       ;建立新頁目錄,并複制目前頁目錄内容
                                            ;輸入:無
                                            ;輸出:EAX=新頁目錄的實體位址 
         push ds
         push es
         push esi
         push edi
         push ebx
         push ecx
         
         mov ebx,mem_0_4_gb_seg_sel
         mov ds,ebx
         mov es,ebx
         
         call allocate_a_4k_page            
         mov ebx,eax
         or ebx,0x00000007
         mov [0xfffffff8],ebx
         
         mov esi,0xfffff000                 ;ESI->目前頁目錄的線性位址
         mov edi,0xffffe000                 ;EDI->新頁目錄的線性位址
         mov ecx,1024                       ;ECX=要複制的目錄項數
         cld
         repe movsd 
         
         pop ecx
         pop ebx
         pop edi
         pop esi
         pop es
         pop ds
         
         retf
         
;-------------------------------------------------------------------------------
terminate_current_task:                     ;終止目前任務
                                            ;注意,執行此例程時,目前任務仍在
                                            ;運作中。此例程其實也是目前任務的
                                            ;一部分 
         mov eax,core_data_seg_sel
         mov ds,eax

         pushfd
         pop edx
 
         test dx,0100_0000_0000_0000B       ;測試NT位
         jnz .b1                            ;目前任務是嵌套的,到.b1執行iretd 
         jmp far [program_man_tss]          ;程式管理器任務 
  .b1: 
         iretd

sys_routine_end:

;===============================================================================
SECTION core_data vstart=0                  ;系統核心的資料段 
;------------------------------------------------------------------------------- 
         pgdt             dw  0             ;用于設定和修改GDT 
                          dd  0

         page_bit_map     db  0xff,0xff,0xff,0xff,0xff,0x55,0x55,0xff
                          db  0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff
                          db  0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff
                          db  0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff
                          db  0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55
                          db  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
                          db  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
                          db  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
         page_map_len     equ $-page_bit_map
                          
         ;符号位址檢索表
         salt:
         salt_1           db  '@PrintString'
                     times 256-($-salt_1) db 0
                          dd  put_string
                          dw  sys_routine_seg_sel

         salt_2           db  '@ReadDiskData'
                     times 256-($-salt_2) db 0
                          dd  read_hard_disk_0
                          dw  sys_routine_seg_sel

         salt_3           db  '@PrintDwordAsHexString'
                     times 256-($-salt_3) db 0
                          dd  put_hex_dword
                          dw  sys_routine_seg_sel

         salt_4           db  '@TerminateProgram'
                     times 256-($-salt_4) db 0
                          dd  terminate_current_task
                          dw  sys_routine_seg_sel

         salt_item_len   equ $-salt_4
         salt_items      equ ($-salt)/salt_item_len

         message_0        db  '  Working in system core,protect mode.'
                          db  0x0d,0x0a,0

         message_1        db  '  Paging is enabled.System core is mapped to'
                          db  ' address 0x80000000.',0x0d,0x0a,0
         
         message_2        db  0x0d,0x0a
                          db  '  System wide CALL-GATE mounted.',0x0d,0x0a,0
         
         message_3        db  '********No more pages********',0
         
         message_4        db  0x0d,0x0a,'  Task switching...@_@',0x0d,0x0a,0
         
         message_5        db  0x0d,0x0a,'  Processor HALT.',0
         
        
         bin_hex          db '0123456789ABCDEF'
                                            ;put_hex_dword子過程用的查找表 

         core_buf   times 512 db 0          ;核心用的緩沖區

         cpu_brnd0        db 0x0d,0x0a,'  ',0
         cpu_brand  times 52 db 0
         cpu_brnd1        db 0x0d,0x0a,0x0d,0x0a,0

         ;任務控制塊鍊
         tcb_chain        dd  0

         ;核心資訊
         core_next_laddr  dd  0x80100000    ;核心空間中下一個可配置設定的線性位址        
         program_man_tss  dd  0             ;程式管理器的TSS描述符選擇子 
                          dw  0

core_data_end:
               
;===============================================================================
SECTION core_code vstart=0
;-------------------------------------------------------------------------------
fill_descriptor_in_ldt:                     ;在LDT内安裝一個新的描述符
                                            ;輸入:EDX:EAX=描述符
                                            ;          EBX=TCB基位址
                                            ;輸出:CX=描述符的選擇子
         push eax
         push edx
         push edi
         push ds

         mov ecx,mem_0_4_gb_seg_sel
         mov ds,ecx

         mov edi,[ebx+0x0c]                 ;獲得LDT基位址
         
         xor ecx,ecx
         mov cx,[ebx+0x0a]                  ;獲得LDT界限
         inc cx                             ;LDT的總位元組數,即新描述符偏移位址
         
         mov [edi+ecx+0x00],eax
         mov [edi+ecx+0x04],edx             ;安裝描述符

         add cx,8                           
         dec cx                             ;得到新的LDT界限值 

         mov [ebx+0x0a],cx                  ;更新LDT界限值到TCB

         mov ax,cx
         xor dx,dx
         mov cx,8
         div cx
         
         mov cx,ax
         shl cx,3                           ;左移3位,并且
         or cx,0000_0000_0000_0100B         ;使TI位=1,指向LDT,最後使RPL=00 

         pop ds
         pop edi
         pop edx
         pop eax
     
         ret
      
;-------------------------------------------------------------------------------
load_relocate_program:                      ;加載并重定位使用者程式
                                            ;輸入: PUSH 邏輯扇區号
                                            ;      PUSH 任務控制塊基位址
                                            ;輸出:無 
         pushad
      
         push ds
         push es
      
         mov ebp,esp                        ;為通路通過堆棧傳遞的參數做準備
      
         mov ecx,mem_0_4_gb_seg_sel
         mov es,ecx
      
         ;清空目前頁目錄的前半部分(對應低2GB的局部位址空間) 
         mov ebx,0xfffff000
         xor esi,esi
  .b1:
         mov dword [es:ebx+esi*4],0x00000000
         inc esi
         cmp esi,512
         jl .b1
         
         ;以下開始配置設定記憶體并加載使用者程式
         mov eax,core_data_seg_sel
         mov ds,eax                         ;切換DS到核心資料段

         mov eax,[ebp+12*4]                 ;從堆棧中取出使用者程式起始扇區号
         mov ebx,core_buf                   ;讀取程式頭部資料
         call sys_routine_seg_sel:read_hard_disk_0

         ;以下判斷整個程式有多大
         mov eax,[core_buf]                 ;程式尺寸
         mov ebx,eax
         and ebx,0xfffff000                 ;使之4KB對齊 
         add ebx,0x1000                        
         test eax,0x00000fff                ;程式的大小正好是4KB的倍數嗎? 
         cmovnz eax,ebx                     ;不是。使用湊整的結果

         mov ecx,eax
         shr ecx,12                         ;程式占用的總4KB頁數 
         
         mov eax,mem_0_4_gb_seg_sel         ;切換DS到0-4GB的段
         mov ds,eax

         mov eax,[ebp+12*4]                 ;起始扇區号
         mov esi,[ebp+11*4]                 ;從堆棧中取得TCB的基位址
  .b2:
         mov ebx,[es:esi+0x06]              ;取得可用的線性位址
         add dword [es:esi+0x06],0x1000
         call sys_routine_seg_sel:alloc_inst_a_page

         push ecx
         mov ecx,8
  .b3:
         call sys_routine_seg_sel:read_hard_disk_0
         inc eax
         loop .b3

         pop ecx
         loop .b2

         ;在核心位址空間内建立使用者任務的TSS
         mov eax,core_data_seg_sel          ;切換DS到核心資料段
         mov ds,eax

         mov ebx,[core_next_laddr]          ;使用者任務的TSS必須在全局空間上配置設定 
         call sys_routine_seg_sel:alloc_inst_a_page
         add dword [core_next_laddr],4096
         
         mov [es:esi+0x14],ebx              ;在TCB中填寫TSS的線性位址 
         mov word [es:esi+0x12],103         ;在TCB中填寫TSS的界限值 
          
         ;在使用者任務的局部位址空間内建立LDT 
         mov ebx,[es:esi+0x06]              ;從TCB中取得可用的線性位址
         add dword [es:esi+0x06],0x1000
         call sys_routine_seg_sel:alloc_inst_a_page
         mov [es:esi+0x0c],ebx              ;填寫LDT線性位址到TCB中 

         ;建立程式代碼段描述符
         mov eax,0x00000000
         mov ebx,0x000fffff                 
         mov ecx,0x00c0f800                 ;4KB粒度的代碼段描述符,特權級3
         call sys_routine_seg_sel:make_seg_descriptor
         mov ebx,esi                        ;TCB的基位址
         call fill_descriptor_in_ldt
         or cx,0000_0000_0000_0011B         ;設定選擇子的特權級為3
         
         mov ebx,[es:esi+0x14]              ;從TCB中擷取TSS的線性位址
         mov [es:ebx+76],cx                 ;填寫TSS的CS域 

         ;建立程式資料段描述符
         mov eax,0x00000000
         mov ebx,0x000fffff                 
         mov ecx,0x00c0f200                 ;4KB粒度的資料段描述符,特權級3
         call sys_routine_seg_sel:make_seg_descriptor
         mov ebx,esi                        ;TCB的基位址
         call fill_descriptor_in_ldt
         or cx,0000_0000_0000_0011B         ;設定選擇子的特權級為3
         
         mov ebx,[es:esi+0x14]              ;從TCB中擷取TSS的線性位址
         mov [es:ebx+84],cx                 ;填寫TSS的DS域 
         mov [es:ebx+72],cx                 ;填寫TSS的ES域
         mov [es:ebx+88],cx                 ;填寫TSS的FS域
         mov [es:ebx+92],cx                 ;填寫TSS的GS域
         
         ;将資料段作為使用者任務的3特權級固有堆棧 
         mov ebx,[es:esi+0x06]              ;從TCB中取得可用的線性位址
         add dword [es:esi+0x06],0x1000
         call sys_routine_seg_sel:alloc_inst_a_page
         
         mov ebx,[es:esi+0x14]              ;從TCB中擷取TSS的線性位址
         mov [es:ebx+80],cx                 ;填寫TSS的SS域
         mov edx,[es:esi+0x06]              ;堆棧的高端線性位址 
         mov [es:ebx+56],edx                ;填寫TSS的ESP域 

         ;在使用者任務的局部位址空間内建立0特權級堆棧
         mov ebx,[es:esi+0x06]              ;從TCB中取得可用的線性位址
         add dword [es:esi+0x06],0x1000
         call sys_routine_seg_sel:alloc_inst_a_page

         mov eax,0x00000000
         mov ebx,0x000fffff
         mov ecx,0x00c09200                 ;4KB粒度的堆棧段描述符,特權級0
         call sys_routine_seg_sel:make_seg_descriptor
         mov ebx,esi                        ;TCB的基位址
         call fill_descriptor_in_ldt
         or cx,0000_0000_0000_0000B         ;設定選擇子的特權級為0

         mov ebx,[es:esi+0x14]              ;從TCB中擷取TSS的線性位址
         mov [es:ebx+8],cx                  ;填寫TSS的SS0域
         mov edx,[es:esi+0x06]              ;堆棧的高端線性位址
         mov [es:ebx+4],edx                 ;填寫TSS的ESP0域 

         ;在使用者任務的局部位址空間内建立1特權級堆棧
         mov ebx,[es:esi+0x06]              ;從TCB中取得可用的線性位址
         add dword [es:esi+0x06],0x1000
         call sys_routine_seg_sel:alloc_inst_a_page

         mov eax,0x00000000
         mov ebx,0x000fffff
         mov ecx,0x00c0b200                 ;4KB粒度的堆棧段描述符,特權級1
         call sys_routine_seg_sel:make_seg_descriptor
         mov ebx,esi                        ;TCB的基位址
         call fill_descriptor_in_ldt
         or cx,0000_0000_0000_0001B         ;設定選擇子的特權級為1

         mov ebx,[es:esi+0x14]              ;從TCB中擷取TSS的線性位址
         mov [es:ebx+16],cx                 ;填寫TSS的SS1域
         mov edx,[es:esi+0x06]              ;堆棧的高端線性位址
         mov [es:ebx+12],edx                ;填寫TSS的ESP1域 

         ;在使用者任務的局部位址空間内建立2特權級堆棧
         mov ebx,[es:esi+0x06]              ;從TCB中取得可用的線性位址
         add dword [es:esi+0x06],0x1000
         call sys_routine_seg_sel:alloc_inst_a_page

         mov eax,0x00000000
         mov ebx,0x000fffff
         mov ecx,0x00c0d200                 ;4KB粒度的堆棧段描述符,特權級2
         call sys_routine_seg_sel:make_seg_descriptor
         mov ebx,esi                        ;TCB的基位址
         call fill_descriptor_in_ldt
         or cx,0000_0000_0000_0010B         ;設定選擇子的特權級為2

         mov ebx,[es:esi+0x14]              ;從TCB中擷取TSS的線性位址
         mov [es:ebx+24],cx                 ;填寫TSS的SS2域
         mov edx,[es:esi+0x06]              ;堆棧的高端線性位址
         mov [es:ebx+20],edx                ;填寫TSS的ESP2域 


         ;重定位SALT 
         mov eax,mem_0_4_gb_seg_sel         ;通路任務的4GB虛拟位址空間時用 
         mov es,eax                         
                                                    
         mov eax,core_data_seg_sel
         mov ds,eax
      
         cld

         mov ecx,[es:0x0c]                  ;U-SALT條目數 
         mov edi,[es:0x08]                  ;U-SALT在4GB空間内的偏移 
  .b4:
         push ecx
         push edi
      
         mov ecx,salt_items
         mov esi,salt
  .b5:
         push edi
         push esi
         push ecx

         mov ecx,64                         ;檢索表中,每條目的比較次數 
         repe cmpsd                         ;每次比較4位元組 
         jnz .b6
         mov eax,[esi]                      ;若比對,則esi恰好指向其後的位址
         mov [es:edi-256],eax               ;将字元串改寫成偏移位址 
         mov ax,[esi+4]
         or ax,0000000000000011B            ;以使用者程式自己的特權級使用調用門
                                            ;故RPL=3 
         mov [es:edi-252],ax                ;回填調用門選擇子 
  .b6:
      
         pop ecx
         pop esi
         add esi,salt_item_len
         pop edi                            ;從頭比較 
         loop .b5
      
         pop edi
         add edi,256
         pop ecx
         loop .b4

         ;在GDT中登記LDT描述符
         mov esi,[ebp+11*4]                 ;從堆棧中取得TCB的基位址
         mov eax,[es:esi+0x0c]              ;LDT的起始線性位址
         movzx ebx,word [es:esi+0x0a]       ;LDT段界限
         mov ecx,0x00408200                 ;LDT描述符,特權級0
         call sys_routine_seg_sel:make_seg_descriptor
         call sys_routine_seg_sel:set_up_gdt_descriptor
         mov [es:esi+0x10],cx               ;登記LDT選擇子到TCB中

         mov ebx,[es:esi+0x14]              ;從TCB中擷取TSS的線性位址
         mov [es:ebx+96],cx                 ;填寫TSS的LDT域 

         mov word [es:ebx+0],0              ;反向鍊=0
      
         mov dx,[es:esi+0x12]               ;段長度(界限)
         mov [es:ebx+102],dx                ;填寫TSS的I/O位圖偏移域 
      
         mov word [es:ebx+100],0            ;T=0
      
         mov eax,[es:0x04]                  ;從任務的4GB位址空間擷取入口點 
         mov [es:ebx+32],eax                ;填寫TSS的EIP域 

         pushfd
         pop edx
         mov [es:ebx+36],edx                ;填寫TSS的EFLAGS域 

         ;在GDT中登記TSS描述符
         mov eax,[es:esi+0x14]              ;從TCB中擷取TSS的起始線性位址
         movzx ebx,word [es:esi+0x12]       ;段長度(界限)
         mov ecx,0x00408900                 ;TSS描述符,特權級0
         call sys_routine_seg_sel:make_seg_descriptor
         call sys_routine_seg_sel:set_up_gdt_descriptor
         mov [es:esi+0x18],cx               ;登記TSS選擇子到TCB

         ;建立使用者任務的頁目錄
         ;注意!頁的配置設定和使用是由頁位圖決定的,可以不占用線性位址空間 
         call sys_routine_seg_sel:create_copy_cur_pdir
         mov ebx,[es:esi+0x14]              ;從TCB中擷取TSS的線性位址
         mov dword [es:ebx+28],eax          ;填寫TSS的CR3(PDBR)域
                   
         pop es                             ;恢複到調用此過程前的es段 
         pop ds                             ;恢複到調用此過程前的ds段
      
         popad
      
         ret 8                              ;丢棄調用本過程前壓入的參數 
      
;-------------------------------------------------------------------------------
append_to_tcb_link:                         ;在TCB鍊上追加任務控制塊
                                            ;輸入:ECX=TCB線性基位址
         push eax
         push edx
         push ds
         push es
         
         mov eax,core_data_seg_sel          ;令DS指向核心資料段 
         mov ds,eax
         mov eax,mem_0_4_gb_seg_sel         ;令ES指向0..4GB段
         mov es,eax
         
         mov dword [es: ecx+0x00],0         ;目前TCB指針域清零,以訓示這是最
                                            ;後一個TCB
                                             
         mov eax,[tcb_chain]                ;TCB表頭指針
         or eax,eax                         ;連結清單為空?
         jz .notcb 
         
  .searc:
         mov edx,eax
         mov eax,[es: edx+0x00]
         or eax,eax               
         jnz .searc
         
         mov [es: edx+0x00],ecx
         jmp .retpc
         
  .notcb:       
         mov [tcb_chain],ecx                ;若為空表,直接令表頭指針指向TCB
         
  .retpc:
         pop es
         pop ds
         pop edx
         pop eax
         
         ret
         
;-------------------------------------------------------------------------------
start:
         mov ecx,core_data_seg_sel          ;令DS指向核心資料段 
         mov ds,ecx

         mov ecx,mem_0_4_gb_seg_sel         ;令ES指向4GB資料段 
         mov es,ecx

         mov ebx,message_0                    
         call sys_routine_seg_sel:put_string
                                         
         ;顯示處理器品牌資訊 
         mov eax,0x80000002
         cpuid
         mov [cpu_brand + 0x00],eax
         mov [cpu_brand + 0x04],ebx
         mov [cpu_brand + 0x08],ecx
         mov [cpu_brand + 0x0c],edx
      
         mov eax,0x80000003
         cpuid
         mov [cpu_brand + 0x10],eax
         mov [cpu_brand + 0x14],ebx
         mov [cpu_brand + 0x18],ecx
         mov [cpu_brand + 0x1c],edx

         mov eax,0x80000004
         cpuid
         mov [cpu_brand + 0x20],eax
         mov [cpu_brand + 0x24],ebx
         mov [cpu_brand + 0x28],ecx
         mov [cpu_brand + 0x2c],edx

         mov ebx,cpu_brnd0                  ;顯示處理器品牌資訊 
         call sys_routine_seg_sel:put_string
         mov ebx,cpu_brand
         call sys_routine_seg_sel:put_string
         mov ebx,cpu_brnd1
         call sys_routine_seg_sel:put_string

         ;準備打開分頁機制
         
         ;建立系統核心的頁目錄表PDT
         ;頁目錄表清零 
         mov ecx,1024                       ;1024個目錄項
         mov ebx,0x00020000                 ;頁目錄的實體位址
         xor esi,esi
  .b1:
         mov dword [es:ebx+esi],0x00000000  ;頁目錄表項清零 
         add esi,4
         loop .b1
         
         ;在頁目錄内建立指向頁目錄自己的目錄項
         mov dword [es:ebx+4092],0x00020003 

         ;在頁目錄内建立與線性位址0x00000000對應的目錄項
         mov dword [es:ebx+0],0x00021003    ;寫入目錄項(頁表的實體位址和屬性)      

         ;建立與上面那個目錄項相對應的頁表,初始化頁表項 
         mov ebx,0x00021000                 ;頁表的實體位址
         xor eax,eax                        ;起始頁的實體位址 
         xor esi,esi
  .b2:       
         mov edx,eax
         or edx,0x00000003                                                      
         mov [es:ebx+esi*4],edx             ;登記頁的實體位址
         add eax,0x1000                     ;下一個相鄰頁的實體位址 
         inc esi
         cmp esi,256                        ;僅低端1MB記憶體對應的頁才是有效的 
         jl .b2
         
  .b3:                                      ;其餘的頁表項置為無效
         mov dword [es:ebx+esi*4],0x00000000  
         inc esi
         cmp esi,1024
         jl .b3 

         ;令CR3寄存器指向頁目錄,并正式開啟頁功能 
         mov eax,0x00020000                 ;PCD=PWT=0
         mov cr3,eax

         mov eax,cr0
         or eax,0x80000000
         mov cr0,eax                        ;開啟分頁機制

         ;在頁目錄内建立與線性位址0x80000000對應的目錄項
         mov ebx,0xfffff000                 ;頁目錄自己的線性位址 
         mov esi,0x80000000                 ;映射的起始位址
         shr esi,22                         ;線性位址的高10位是目錄索引
         shl esi,2
         mov dword [es:ebx+esi],0x00021003  ;寫入目錄項(頁表的實體位址和屬性)
                                            ;目标單元的線性位址為0xFFFFF200
                                             
         ;将GDT中的段描述符映射到線性位址0x80000000
         sgdt [pgdt]
         
         mov ebx,[pgdt+2]
         
         or dword [es:ebx+0x10+4],0x80000000
         or dword [es:ebx+0x18+4],0x80000000
         or dword [es:ebx+0x20+4],0x80000000
         or dword [es:ebx+0x28+4],0x80000000
         or dword [es:ebx+0x30+4],0x80000000
         or dword [es:ebx+0x38+4],0x80000000
         
         add dword [pgdt+2],0x80000000      ;GDTR也用的是線性位址 
         
         lgdt [pgdt]
        
         jmp core_code_seg_sel:flush        ;重新整理段寄存器CS,啟用高端線性位址 
                                             
   flush:
         mov eax,core_stack_seg_sel
         mov ss,eax
         
         mov eax,core_data_seg_sel
         mov ds,eax
          
         mov ebx,message_1
         call sys_routine_seg_sel:put_string

         ;以下開始安裝為整個系統服務的調用門。特權級之間的控制轉移必須使用門
         mov edi,salt                       ;C-SALT表的起始位置 
         mov ecx,salt_items                 ;C-SALT表的條目數量 
  .b4:
         push ecx   
         mov eax,[edi+256]                  ;該條目入口點的32位偏移位址 
         mov bx,[edi+260]                   ;該條目入口點的段選擇子 
         mov cx,1_11_0_1100_000_00000B      ;特權級3的調用門(3以上的特權級才
                                            ;允許通路),0個參數(因為用寄存器
                                            ;傳遞參數,而沒有用棧) 
         call sys_routine_seg_sel:make_gate_descriptor
         call sys_routine_seg_sel:set_up_gdt_descriptor
         mov [edi+260],cx                   ;将傳回的門描述符選擇子回填
         add edi,salt_item_len              ;指向下一個C-SALT條目 
         pop ecx
         loop .b4

         ;對門進行測試 
         mov ebx,message_2
         call far [salt_1+256]              ;通過門顯示資訊(偏移量将被忽略) 
      
         ;為程式管理器的TSS配置設定記憶體空間
         mov ebx,[core_next_laddr]
         call sys_routine_seg_sel:alloc_inst_a_page
         add dword [core_next_laddr],4096

         ;在程式管理器的TSS中設定必要的項目 
         mov word [es:ebx+0],0              ;反向鍊=0

         mov eax,cr3
         mov dword [es:ebx+28],eax          ;登記CR3(PDBR)

         mov word [es:ebx+96],0             ;沒有LDT。處理器允許沒有LDT的任務。
         mov word [es:ebx+100],0            ;T=0
         mov word [es:ebx+102],103          ;沒有I/O位圖。0特權級事實上不需要。
         
         ;建立程式管理器的TSS描述符,并安裝到GDT中 
         mov eax,ebx                        ;TSS的起始線性位址
         mov ebx,103                        ;段長度(界限)
         mov ecx,0x00408900                 ;TSS描述符,特權級0
         call sys_routine_seg_sel:make_seg_descriptor
         call sys_routine_seg_sel:set_up_gdt_descriptor
         mov [program_man_tss+4],cx         ;儲存程式管理器的TSS描述符選擇子 

         ;任務寄存器TR中的内容是任務存在的标志,該内容也決定了目前任務是誰。
         ;下面的指令為目前正在執行的0特權級任務“程式管理器”後補手續(TSS)。
         ltr cx

         ;現在可認為“程式管理器”任務正執行中

         ;建立使用者任務的任務控制塊 
         mov ebx,[core_next_laddr]
         call sys_routine_seg_sel:alloc_inst_a_page
         add dword [core_next_laddr],4096
         
         mov dword [es:ebx+0x06],0          ;使用者任務局部空間的配置設定從0開始。
         mov word [es:ebx+0x0a],0xffff      ;登記LDT初始的界限到TCB中
         mov ecx,ebx
         call append_to_tcb_link            ;将此TCB添加到TCB鍊中 
      
         push dword 50                      ;使用者程式位于邏輯50扇區
         push ecx                           ;壓入任務控制塊起始線性位址 
       
         call load_relocate_program         
      
         mov ebx,message_4
         call sys_routine_seg_sel:put_string
         
         call far [es:ecx+0x14]             ;執行任務切換。
         
         mov ebx,message_5
         call sys_routine_seg_sel:put_string

         hlt
            
core_code_end:

;-------------------------------------------------------------------------------
SECTION core_trail
;-------------------------------------------------------------------------------
core_end:
           

繼續閱讀