青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

jake1036

linux內核學習之啟動程序模塊

                              linux引導程序解析
    bootsect程序,駐留在磁盤的第一個扇區(qū)中(0磁道 0磁頭 1 扇區(qū))。在BIOS加點檢測之后,該引導程序會自動地加載在內存的0x7c00處。
   bootsect程序在運行時,會首先將自身移動到0x90000處開始執(zhí)行,并將從第二個扇區(qū)開始的共4個扇區(qū)大小的setup程序移動到,緊緊挨著該程序的0x90200處。 
 然后會使用BIOS中斷int13 取當前引導盤的參數(shù),接著在屏幕上顯示Loading System的字符串,最后把磁盤上setup后面的system模塊加載到內存0x10000開始的地方。隨后確定根文件系統(tǒng)的設備號,若沒有指定,則根據(jù)所保存的引導盤的每磁道扇區(qū)數(shù)目,判斷出盤的類型和種類,并保存在設備號root_dev中。
   最后長跳轉到setup程序的開始處,執(zhí)行setup程序。

  下面為分析的源代碼:
  

!SYSSIZE = 0x3000
.global begtext , begdata , begbss , endtext , enddata , endbss
.text
begtext:
.data
begdata:
.bss
begbss:
.text
     SETUPLEN 
= 4         ! nr of setup-sectors
     BOOTSEG 
= 0x07c0
     INITSEG 
= 0x9000
     SETUPSEG 
= 0x9020
     SYSSEG 
= 0x1000
     ENDSEG 
= SYSSEG + SYSSIZE
ROOT_DEV 
= 0x306
   
 entry start
 start:

!將bootsect自身移動到0x90000處,并跳轉開始執(zhí)行
       mov ax , #BOOTSEG
       mov ds , ax
       mov ax , #INITSEG
       mov es , ax
       mov cx , #
256
       sub si , si
       sub di , di
       rep
          movw
       jmpi go , INITSEG
               

  
!跳轉過后修改段寄存器
   go:
       mov ax , cs
       mov ds , ax
       mov es , ax 
       mov ss , ax
       mov sp , #
0xFF00

 
!利用BIOS中斷INT 13將 setup模塊,從磁盤第2個扇區(qū)開始讀到0x90200開始處,共讀4個扇區(qū)

   load_setup:
       mov dx , #
0x0000    ! drive 0  , head 0
       mov cx , #
0x0002    ! sector 2 , track 0
       mov bx , #
0x0200    ! address = 512 , in INITSEG 
       mov ax , #
0x0200 + SETUP_LEN ! service 2 , nr of sectors
       
int 0x13
       jnc ok_load_setup
       mov dx , #
0x0000       !出錯則重新執(zhí)行加載程序
       mov ax , #
0x0000
       
int 0x13
       j load_setup

!利用int13 中斷,得到磁盤驅動器的參數(shù),特別是每道磁道的扇區(qū)數(shù)量
       mov ax , 
0x0800
       mov dl , 
0x00
       
int 0x13
!重新設置es的值      
       mov sectors , cx
       mov ax , #INITSEG
       mov es , ax
!print some message
       mov ah , #
0x03    !讀光標位置,返回光標位置在dx中
       xor bh , bh
       
int 0x10

       mov cx , #
24
       mov bx , #
0x0007
       mov bp , #msg1
       mov ax , #
0x1301
       
int 0x10
  
! ok we have written the message ,現(xiàn)在開始將system模塊加載到0x10000開始處
       mov ax , #SYSSEG
       mov es , ax
       call read_it         
!讀磁盤上system模塊,es為輸入?yún)?shù)
       call kill_motor      
!關閉馬達

!確定根文件系統(tǒng)所在的設備號
       seg cs
       mov ax , root_dev
       cmp ax , #
0
       jne root_defined
      
       seg cs
       mov bx , sectors
       mov ax , #
0x0208
       cmp bx , #
15
       je root_defined
       mov ax , #
0x021c
       cmp bx , #
18
       je root_defined
  undef_root:
        jmp undef_root
  root_defined:
        seg cs
        mov root_dev , ax
        jmpi 
0 , SETUPSEG      !此處跳進setup程序

!下面是將system模塊加載進內存的子函數(shù)
      
  sread: .word 
1 + SETUPLEN   !sectors read of current track
  head:  .word 
0
  track: .word 
0
 
  
!保證es在64kb處
  read_it :
     mov  ax , es
     test ax , #
0xfff
  die: jne die
     xor  bx , bx
  rp_read:            
!接著判斷是否已經(jīng)讀入全部的數(shù)據(jù),比較當前所讀的段是否就是系統(tǒng)數(shù)據(jù)末端所處的段
     mov ax , es      
!如果不是,就跳轉至下面的ok1標號處繼續(xù)讀數(shù)據(jù)  
     cmp ax , #ENDSEG
     jb ok1_read
     ret              
!如果到達了系統(tǒng)末端,就結束此循環(huán)
 
  ok1_read:           
!計算和驗證當前磁道上需要讀取的扇區(qū)數(shù)目,放在ax寄存器中,根據(jù)當前磁道還未讀取的扇區(qū)數(shù)和 
                      
!段內數(shù)據(jù)字節(jié)開始偏移的位置,計算如果全部讀取這些未讀扇區(qū),所讀的字節(jié)是否會超過64kb的限制
                      
!若會超過,則根據(jù)此次最多能讀入的字節(jié)數(shù),反算出需要讀取的扇區(qū)數(shù)。
  seg cs
  mov ax , sectors    
!取每磁道的扇區(qū)數(shù)
  sub ax , sread      
!減去當前磁道已讀扇區(qū)數(shù)
  mov cx , ax         
!cx = ax 為當前磁道的未讀扇區(qū)數(shù)
  shl cx , #
9         !當前未讀的字節(jié)數(shù)
  add cx , bx         
!此次操作之后,段內偏移地址現(xiàn)在的值
  jnc ok2_read        
!若沒有超過64kb,則跳轉至ok2_read
  je  ok2_read

 
!若加上此次將讀取的磁道上所有未讀扇區(qū)時會超過64kb,則反算出 可以最多加載多少 扇區(qū)數(shù)目 
  xor ax , ax
  sub ax , bx
  shr ax , #
9          !轉換成扇區(qū)數(shù)目
 
ok2_read:
  
!讀當前磁道上指定開始扇區(qū)(cl)和需讀扇區(qū)數(shù)(al)的數(shù)據(jù)到es:bx開始處。然后將磁道上已經(jīng)讀取的扇區(qū)數(shù)目
  
!與磁道最大扇區(qū)數(shù)sectors作比較,如果小于sectors說明當前磁道上還有扇區(qū)未讀
  call read_track
  mov cx , ax         
!cx等于當前操作以讀扇區(qū)數(shù)目
  add ax , sread      
!加上當前磁道已讀扇區(qū)數(shù)目
  seg cs           
  cmp ax , sectors    
!如果當前磁道上還有扇區(qū)未讀,則跳轉到ok3_read
  jne ok3_read
 
  
!如果該磁道的當前磁頭面所有扇區(qū)已經(jīng)讀完,則讀該磁道的下一磁頭面(1號磁頭)上的數(shù)據(jù),如果已經(jīng)讀完則去讀下一磁道
  mov ax , #
1
  sub ax , head                
!判斷當前的磁頭號,如果是0磁頭,則去讀1磁頭
  jne ok4_read                 
!讀1號磁頭
  inc track                    
!讀下一磁道
ok4_read:
  mov head , ax                
!保存當前的磁頭號
  xor ax , ax                  
!清除當前磁道的已讀扇區(qū)數(shù)
ok3_read:
  
!如果當前磁道上還有未讀的扇區(qū),則首先保存當前磁道的已讀扇區(qū)數(shù)目,然后調整存放數(shù)據(jù)的開始位置,若小于64kb邊界值
  
!則跳轉到rp_read處,繼續(xù)讀數(shù)據(jù)
  mov sread , ax            
!保存當前磁道的已讀扇區(qū)數(shù)
  shl cx   , #
9             !上次已讀扇區(qū)數(shù)*512字節(jié)
  add bx   , cx             
!調整當前段內數(shù)據(jù)開始位置
  jnc rp_read     

  
!否則說明已經(jīng)讀取64kb數(shù)據(jù),此時調整當前段,為讀下一段數(shù)據(jù)作準備
   mov ax , es
   add ax , #
0x1000
   mov es , ax
   xor bx , bx
   jmp rp_read

 

 
!read_track 子程序,讀當前磁道上指定開始扇區(qū)和需讀扇區(qū)數(shù)的數(shù)據(jù)到es:bx開始處。
 
!int 0x13 , ah =2 ,al=需讀扇區(qū)數(shù),es:bx 緩沖區(qū)開始位置

 read_track:
       push ax
       push bx
       push cx
       push dx

       mov dx , track
       mov cx , sread
       inc cx
   
       mov ch , dl
       mov dx , head
       mov dh , dl 
       mov dl , #
0
       and dx , #
0x0100
       mov ah , #
2
       
int 0x13     
       jc bad_rt

       pop dx
       pop cx
       pop bx
       pop ax
       ret

      bad_rt: mov ax , #
0
       mov dx , #
0
       
int 0x13
       pop dx
       pop cx
       pop bx
       pop ax
       jmp read_track   

   
!關閉軟驅馬達的子程序
 kill_motor:
       push dx
       mov  dx , #
0x3f2
       mov  al , #
0
       outb
       pop dx
       ret
       
 sectors:
      .word 
0        !存放當前啟動軟盤每磁道的扇區(qū)數(shù)      
 msg1:
      .
byte 13 , 10
      .ascii 
"Loading system  "
      .
byte 13 , 10 , 13 ,10
.org 
508
 root_dev:
      .word ROOT_DEV    
!這里存放根文件系統(tǒng)的所在設備號
 boot_flag:
      .word 
0xAA55

.text
  endtext:
.data
  enddata:
.bss
  endbss:

         

 

           2     setup.s程序分析

      setup.s是一個操作系統(tǒng)的加載程序,他的主要作用就是利用BIOS的讀取機器系統(tǒng)數(shù)據(jù),并將這些數(shù)據(jù)保存到0x90000開始的位置,(覆蓋了bootsect程序所在的地方)。這些參數(shù)將被內核中相關程序使用。參數(shù)諸如光標位置 ,顯存等信息。

   然后setup程序將system模塊從 0x10000-0x8ffff(任務system模塊不會超過512kb) ,整體移動到絕對內存地址為0x0000處。

  接著加載中斷描述表寄存器idtr和全局描述表寄存器gdtr, 開啟A20地址線,重新設置兩個中斷控制芯片8259A,將硬件中斷號重新設置為0x20---0x2f。最后設置CPU的控制寄存器CR0,從而進入32位保護模式運行,并跳入到system模塊最前面部分的head.s程序繼續(xù)運行。

  為了能讓head.s在32位保護模式下運行,在本程序臨時設置了中斷描述符表(IDT)和全局描述符表(GDT),

  在GDT中設置了當前代碼段的描述符和數(shù)據(jù)段的描述符,在head.s中會重新設置這些描述符表。必須使用lgdt把描述符表的基地址告知CPU,再將機器狀態(tài)字置位即可進入32位保護模式。

 

 

INITSEG = 0x9000 !we move boot here
SYSSEG 
= 0x1000
SETUPSEG 
= 0x9020 !本程序所在的段地址

.global begtext , begdata , begbss , endtext , enddata , endbss
.text
    begtext:
.data
    begdata:
.bss
    begbss:
.text
    
    entry start
    start:   
    
!保存光標位置已備以后需要
    
!這段代碼使用BIOS中斷取屏幕當前的光標位置,然后保存在內存0x90000處就可以使用
    mov ax , #INITSEG  
    mov ds , ax
    mov ah , #
0x03
    xor bh , bh
    
int 0x10         !利用BIOS中斷 將當前光標位置存檔到 dx
    mov [
0] , dx     !將光標位置存放在0x90000處

    
!得到內存的大小值
    
!利用BIOS中斷0x15功能號 ah=0x88取系統(tǒng)所含擴展內存大小并保存在內存0x90002處。
    mov ah , #
0x88 
    
int 0x15
    mov [
2] , ax
    
    
!得到顯示卡的屬性
    
!調用BIOS中斷0x10,功能號ah=0x0f
    
!返回:ah=字符列數(shù);al=顯示模式;bh=當前顯示頁
    
!0x90004存放當前頁 ,0x90006存放顯示模式,0x90007存放字符列數(shù)

    mov ah , #
0x0f
    
int 0x10
    mov [
4] , bx  !bh=display page
    mov [
6] , ax  !al =video mode , ah = window width
    


    
!檢查顯示方式并取參數(shù)
     mov ah , #
0x12
     mov bl , #
0x10
     
int 0x10
     mov [
8] , ax
     mov [
10] , bx
     mov [
12] , cx
     
 
    
!取得第一個硬盤的信息
     mov ax , #
0x0000
     mov ds , ax
     lds si , [
4 * 0x41]
     mov ax , #INITSEG
     mov es , ax
     mov di , #
0x0080
     mov cx , #
0x10
     rep 
      movsb

    
!取得第二個硬盤
     mov ax , #
0x0000
     mov ds , ax
     lds si , [
4 * 0x46]  !取中斷向量0x46的值,即hd1的參數(shù)值 ------> ds:si
     mov ax , #INITSEG   
     mov es , ax
     mov di , #
0x0090    !傳輸目的地址 0x9000:0x0090
     mov cx , #
0x10
     rep 
        movsb
     
    
!檢查系統(tǒng)是否有第二個硬盤
     mov ax , #
0x01500
     mov dl , #
0x81
     
int 0x13
     jc no_disk1
     cmp ah , #
3
     je is_disk1
     
  no_disk1:              
!第二塊硬盤不存在,所以清空參數(shù)表
     mov ax , #INITSEG
     mov es , ax
     mov di , #
0x0090
     mov cx , #
0x10
     mov ax , #
0x00
     rep
       stosb
 is_disk1:
    
!從此開始進入了保護模式
     cli
    
!首先把system模塊移動到正確的位置
     
    mov ax , #
0x0000
    cld
 do_move:
    mov es , ax 
    add ax , #
0x1000
    cmp ax , #
0x9000
    jz end_move
    mov ds , ax
    sub di , di
    sub si , si
    mov cx , #
0x8000
    rep
       movsw
    jmp do_move
  
 end_move:
    
  
!在此處加載段描述符表,這里需要設置全局描述符表和中斷描述符表
  mov ax , #SETUPSEG
  mov ds , ax
  lidt idt_48
  lgdt gdt_48
  
  
!打開A20地址線
  call empty_8042
  mov  al , #
0xD1
  
out  #0x64 , al 
  

  call empty_8042
  mov al , #
0xDF
  
out #0x60 , al
  call empty_8042

  
!8259芯片主片端口是0x20 - 0x29 ,從片的端口是0xA0 - 0xA9 。
  mov al , #
0x11
  
out #0x20, al
  .word 
0x00eb , 0x00eb
  
out #0xA0 , al
  .word 
0x00eb , 0x00eb
  
  
!8259芯片設置中斷號從0x20開始
  mov al , #
0x20
  
out #0x21 , al
  .word 
0x00eb , 0x00eb
  mov al , #
0x28
  
out #0xA1 , al
  .word 
0x00eb , 0x00eb
  mov al , #
0x04
  
out #0x21 , al 



  .word 
0x00eb0x00eb
  mov al , #
0x02
  
out #0xA1 , al
  

  .word 
0x00eb0x00eb
  mov al , #
0x01
  
out #0x21 , al


  .word 
0x00eb , 0x00eb
  
out #0xA1 , al
  .word 
0x00eb , 0x00eb
  mov al , #
0xFF
  
out #0x21 , al
  .word 
0x00eb , 0x00eb
  
out #0xA1 , al


  
!下面設置并進入32位保護模式運行,首先加載機器狀態(tài)字,也稱控制寄存器cr0 
  
!在設置該bit之后,隨后的一條指令必須是一條段間跳轉指令,一用于刷新當前指令隊列
  
!因為CPU在執(zhí)行一條指令之前就已經(jīng)從內存讀取該指令并對其進行解碼。
  
  mov ax , #
0x0001
  lmsw ax 
  jmpi 
0,8
  

  
!下面這個子程序檢查鍵盤命令隊列是否為空
 empty_8042:
   .word 
0x00eb , 0x00eb
   
in al , #0x64
   test al , #
2
   jnz empty_8042
   ret
  
  
!全局描述符表開始處
  gdt:
     .word 
0,0,0,0
     
  
!代碼段選擇符的值  
     .word 
0x07FF
     .word 
0x0000
     .word 
0x9A00
     .word 
0x00C0
    
  
!數(shù)據(jù)段選擇符的值
     .word 
0x07FF
     .word 
0x0000
     .word 
0x9200
     .word 
0x00C0
  
  idt_48:
     .word 
0
     .word 
0 , 0
  gdt_48:
     .word 
0x800
     .word 
512 + gdt , 0x9
  
  .text
   endtext:
  .data
   enddata:
  .bss
   endbss:



   三 head.s程序
       功能描述:
      head.s程序在被編譯生成目標文件之后會與內核其他程序一起被鏈接成system模塊,位于system模塊最前面,所以稱之為head程序的原因。system模塊將被放置在磁盤上setup模塊之后開始的扇區(qū)中,即從磁盤上第6個扇區(qū)開始位置。 linux內核一般大約有120KB ,在磁盤上大概占用240個扇區(qū)。
 
   之后我們將在保護模式下編程,head.s使用 as 和ld 編譯器和連接器。 這段程序實際上處于絕對地址0處開始的地方,首先是加載各個數(shù)據(jù)段寄存器,重新設置中斷描述符表idt,共256項,并使各個表項指向一個只報錯誤的啞中斷子程序 ignore_int。 

   在設置了中斷描述符表之后,本程序又重新設置了全局段描述符表gdt,主要是把gdt表設置在比較合理的地方,接著設置管理內存的分頁處理機制,將頁目錄表放在絕對物理地址0開始處,緊隨后邊將放置可以尋址16MB內存的4個頁表,并設置它們的表項。

  最后,head.s 程序利用返回指令將預先放置在堆棧中的 main.c程序的入口地址彈出,去執(zhí)行main()程序。



  以下是部分代碼分析
   (1)首先是建立IDT和GDT表
   
#建立IDT表

setup_idt:
  lea ignore_int   , 
%edx
  movl $
0x00080000 , %eax  
  movw 
%dx , %ax
  movw $
0x8E00 , %dx
  lea _idt , 
%edi
  mov $
256 , %ecx 
 
rp_sidt:  
  movl 
%eax , (%edi)  #eax的高16位是選擇符 ,低16位是段內偏移的低16位
  movl 
%edx , 4(%edi) #edx的高16位是段內偏移地址的高16位,低16位是權限位 
  addl $
8 , %edi
  dec 
%ecx            #重復設置總共256個中斷描述符
  jne rp_sidt
  lidt idt_descr      #加載中斷描述符表寄存器
  ret

setup_gdt:  
  lgdt gdt_descr     #加載全局描述符表寄存器
  ret 

 由代碼可知, 256個idt均指向了一個啞中斷ignore_int,加載gdt的過程更簡單,只是將gdt描述符表的基地址加載進gdtr寄存器。

(2) 頁目錄表和頁表之間的映射
    在linux1.1中 , 在絕對內存地址的0x000000處是一個大小為4k的頁目錄表,然后在內存0x1000,0x2000,0x3000,0x4000處分別是4個頁表的首地址,也就是說linux0.11僅僅能訪問16M的         內存空間,內存映射的算法如下:
 首先在內存0x00000即頁目錄表設置4個頁表首地址,注意添加權限屬性。 然后從最后一個頁表的最后一個表項,倒序的將物理地址添加進頁表中,最后一個頁表項的內容是 64M - 4096 + 7 (7表示頁面在內存,且用戶可讀可訪問)。
  .align 2
setup_paging:      #首先為5頁內存進行清空處理
                   #1個頁目錄表,4個頁表 
    movl $
1024 * 5 , %ecx
    xorl 
%eax , %eax
    xorl 
%edi , %edi
    
    cld ; rep ; stosl
   
    #頁目錄中只需要4個頁目錄, 7是屬性,表示該頁存在內存中,且用戶可以訪問
    movl $pg0 
+ 7 , _pg_dir
    movl $pg1 
+ 7 , _pg_dir + 4
    movl $pg2 
+ 7 , _pg_dir + 8
    movl $pg3 
+ 7 , _pg_dir + 12

                            #從最后一項 倒序的寫入    
    movl $pg3 
+ 4092 , %edi #最后一頁的最后一項
    movl $
0xfff007 , %eax #16M - 4096 +7 
    std
    stosl
    subl $
0x1000 , %eax
    jge 1b


    #設置頁目錄表基址寄存器cr3的值,指向頁目錄表。cr3中保存的是頁目錄表的物理地址
    xorl 
%eax , %eax
    movl 
%eax , %cr3
    #設置啟動分頁處理
    movl 
%cr0 , %eax
    orl 
%0x80000000 , %eax 
    movl 
%eax , %cr0


    #該返回指令執(zhí)行先前壓入堆棧的main函數(shù)的入口地址
    ret


 (3)head中還需要為程序跳轉進main函數(shù)作準備,當完成了頁面設置的時候,上面代碼的最后一句ret,即
     完成了跳入main函數(shù)中繼續(xù)執(zhí)行。 設置main函數(shù)的代碼如下:
   
  
 .org 0x5000 #定義下面的內存數(shù)據(jù)塊從偏移0x5000處開始

_tmp_floppy_area:
  .fill 
1024 , 1 ,0 #共保留1024項,每項1字節(jié),

#下面這些代碼為跳轉到main函數(shù)中,做準備

after_page_tables:
 pushl $
0          #這些是main函數(shù)的參數(shù)
 pushl $
0
 pushl $
0
 pushl $L6         #main函數(shù)的返回地址
 pushl $_main      #_main 是編譯程序對main的內部表示法
 jmp setup_paging  #跳轉到建立頁表映射

L6:
     jmp L6 

  可以看出執(zhí)行完setup_paging之后的ret指令,將把_main 加載進 指令寄存器,進行執(zhí)行。


(4)完整的代碼如下


.text
  .global _idt , _gdt , _pg_dir , _tmp_floppy_area
_pg_dir:  #頁目錄將會設置在這里,所以該程序會被覆蓋掉
 
startup_32:
  movl $
0x10 , %eax #0x10已經(jīng)是全局描述符的在描述符表中的偏移值
  mov 
%ax , %ds    
  mov 
%ax , %es
  mov 
%ax , %fs
  mov 
%ax , %gs

  lss _stack_start , 
%esp #設置_stack_start----->ss:esp

  call setup_idt    #調用設置中斷描述符表的子程序
  call setup_gdt    #調用設置全局描述符表的子程序

  movl $
0x10 , %eax  #重新加載所有的段寄存器
  mov 
%ax , %ds
  mov 
%ax , %es
  mov 
%ax , %fs
  mov 
%ax , %gs
  

  lss _stack_start , 
%esp
  
  #以下代碼用來測試A20地址線是否已經(jīng)打開,采用的方法是向內存0x000000處寫入任意的一個數(shù)值
  #然后看內存地址0x100000是否也是這個數(shù)值,如果一樣的話,就說明A20地址線沒有打開
  
    xorl 
%eax , %eax
1:  incl %eax
    movl 
%eax , 0x000000  #地址就不需要加$
    cmpl 
%eax , 0x100000
    je 1b
  

   movl 
%cr0 , %eax       #
   andl $
0x80000011 , %eax
   orl $
2 , %eax
   movl 
%eax , %cr0
   call check_x87
   jmp after_page_tables
 
 check_x87:
   fninit   #向協(xié)處理器發(fā)送初始化命令
   fstsw 
%ax
   cmpb $
0 , %al 
   je 1f 
   movl 
%cr0 , %eax
   xorl $
6 , %eax
   movl 
%eax , %cr0
   ret
  
 .align 
2
  
1: .byte 0xDB , 0xE4
      
#建立IDT表

setup_idt:
  lea ignore_int   , 
%edx
  movl $
0x00080000 , %eax  
  movw 
%dx , %ax
  movw $
0x8E00 , %dx
  lea _idt , 
%edi
  mov $
256 , %ecx 
 
rp_sidt:  
  movl 
%eax , (%edi)  #eax的高16位是選擇符 ,低16位是段內偏移的低16位
  movl 
%edx , 4(%edi) #edx的高16位是段內偏移地址的高16位,低16位是權限位 
  addl $
8 , %edi
  dec 
%ecx            #重復設置總共256個中斷描述符
  jne rp_sidt
  lidt idt_descr      #加載中斷描述符表寄存器
  ret

setup_gdt:  
  lgdt gdt_descr     #加載全局描述符表寄存器
  ret 

 #這里設置四張頁表,可以用來方位16M的內存空間
 #每個頁表大小為4k,每項為4字節(jié),一張頁表可以映射1024 
* 4kb的內存空間 ,即4M
 .org 
0x1000
  pg0:

 .org 
0x2000
  pg1:

 .org 
0x3000
  pg2:
 
 .org 
0x4000
  pg3:
 
 .org 
0x5000 #定義下面的內存數(shù)據(jù)塊從偏移0x5000處開始

_tmp_floppy_area:
  .fill 
1024 , 1 ,0 #共保留1024項,每項1字節(jié),

#下面這些代碼為跳轉到main函數(shù)中,做準備

after_page_tables:
 pushl $
0          #這些是main函數(shù)的參數(shù)
 pushl $
0
 pushl $
0
 pushl $L6         #main函數(shù)的返回地址
 pushl $_main      #_main 是編譯程序對main的內部表示法
 jmp setup_paging  #跳轉到建立頁表映射

L6:
     jmp L6 

 #下面是默認的中斷向量句柄
int_msg:
  .asciz 
"Unknown interrupt\n\r"
  .align 
2 
 ignore_int:
   pushl 
%eax
   pushl 
%ecx 
   pushl 
%edx

   push  
%ds   #入棧占4個字節(jié)
   push  
%es
   push  
%fs
  
   movl $
0x10 , %eax
   mov  
%ax  , %ds
   mov  
%ax  , %es
   mov  
%ax  , %fs
   pushl $int_msg   #向printk函數(shù)傳遞參數(shù) 
   call _printk     #該函數(shù)在
/kernel/printk.c中
   

   popl 
%eax        #返回值
   pop 
%fs
   pop 
%es
   pop 
%ds 
   popl 
%edx
   popl 
%ecx
   popl 
%eax
   iret




   .align 
2
setup_paging:      #首先為5頁內存進行清空處理
                   #1個頁目錄表,4個頁表 
    movl $
1024 * 5 , %ecx
    xorl 
%eax , %eax
    xorl 
%edi , %edi
    
    cld ; rep ; stosl
   
    #頁目錄中只需要4個頁目錄, 7是屬性,表示該頁存在內存中,且用戶可以訪問
    movl $pg0 
+ 7 , _pg_dir
    movl $pg1 
+ 7 , _pg_dir + 4
    movl $pg2 
+ 7 , _pg_dir + 8
    movl $pg3 
+ 7 , _pg_dir + 12

                            #從最后一項 倒序的寫入    
    movl $pg3 
+ 4092 , %edi #最后一頁的最后一項
    movl $
0xfff007 , %eax #16M - 4096 +7 
    std
    stosl
    subl $
0x1000 , %eax
    jge 1b


    #設置頁目錄表基址寄存器cr3的值,指向頁目錄表。cr3中保存的是頁目錄表的物理地址
    xorl 
%eax , %eax
    movl 
%eax , %cr3
    #設置啟動分頁處理
    movl 
%cr0 , %eax
    orl 
%0x80000000 , %eax 
    movl 
%eax , %cr0


    #該返回指令執(zhí)行先前壓入堆棧的main函數(shù)的入口地址
    ret

    #140行將壓入堆棧的main指令彈出,并跳到main函數(shù)中去
 .align 
2
   .word 
0 
    idt_descr:
       .word 
256 * 8 - 1
       .
long _idt
 .align 
2
    .word 
0
  gdt_descr:
     .word 
256 * 8 - 1
     .
long _gdt

 .align 
3
 _idt: .fill 
256 , 8 , 0  #共256項,每項8字節(jié),初始化為0

 _gdt: .quad 
0x0000000000000000
       .quad 
0x00c09a0000000fff
       .quad 
0x00c0920000000fff
       .quad 
0x0000000000000000
       .fill 
252 , 8 , 0


 
 當CPU運行在保護模式下,某一時刻GDT和LDT分別只能有一個,分別有寄存器GDTR和IDTR指定它們的表基址。在某一時刻當前LDT表的基址由LDTR寄存器的內容指定并且使用GDT中的某個描述符來加載,即LDT也是由GDT中的描述符來決定。但是在某一時刻同樣也只是由其中的一個被視為活動的。一般對于每個任務使用一個LDT,在運行時,程序可以使用GDT中的描述符以及當前任務的LDT中的描述符
















 

 

 


 

 

 

  

 

     

 

 

 

 

 

 

 

 

 

 

 

 

 


 

 

 

 

 

 

 

 

 

 

 

 

    

 

 

 

 

 

 

 

 

 

 

 


 

 


 

 

   


 
   

 

 

 

 

 

 

   
  

 


 

 


             

 

 

 

      

 

 

 

 

 

 

      
      

 

 

 

 

 

 

 
       
     

 

 

 

 

 

 

 

 

 

 

 

    
      



















posted on 2010-10-08 19:48 kahn 閱讀(857) 評論(0)  編輯 收藏 引用


只有注冊用戶登錄后才能發(fā)表評論。
網(wǎng)站導航: 博客園   IT新聞   BlogJava   博問   Chat2DB   管理


青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            久久精品中文| 久久不射2019中文字幕| 国产精品v亚洲精品v日韩精品 | 久久精品欧美日韩精品| 欧美亚洲专区| 久久蜜桃资源一区二区老牛| 久久嫩草精品久久久久| 欧美xart系列在线观看| 亚洲国产精品成人| 亚洲精品免费在线| 亚洲免费小视频| 久久久爽爽爽美女图片| 欧美成人免费一级人片100| 欧美日韩日本视频| 国产综合一区二区| 一本久久a久久免费精品不卡| 午夜亚洲性色视频| 亚洲成人在线网| 亚洲无毛电影| 欧美国产专区| 国一区二区在线观看| 99伊人成综合| 免费亚洲一区| 一级成人国产| 免费看黄裸体一级大秀欧美| 国产精品入口麻豆原神| 亚洲精品视频免费在线观看| 久久不射中文字幕| 日韩亚洲欧美一区| 欧美mv日韩mv亚洲| 国产一区二区欧美| 国产精品日韩一区二区三区| 亚洲国产精品日韩| 欧美在线观看www| 亚洲电影在线播放| 亚洲永久精品大片| 欧美理论电影在线观看| 黄色成人av网站| 欧美亚洲免费高清在线观看| 亚洲激情一区二区| 久久夜色精品国产亚洲aⅴ| 国产女主播在线一区二区| 亚洲精品乱码久久久久久蜜桃91 | 欧美色图天堂网| 亚洲精品久久视频| 欧美r片在线| 欧美制服丝袜第一页| 国产精品区一区| 亚洲一区二区精品| 亚洲精品综合精品自拍| 欧美高清视频一区| 91久久精品国产91久久| 欧美国产精品日韩| 免费不卡在线视频| 亚洲高清不卡在线观看| 欧美jjzz| 欧美激情精品久久久久久久变态 | 亚洲电影在线看| 蜜臀va亚洲va欧美va天堂| 在线视频国产日韩| 欧美韩日一区二区三区| 每日更新成人在线视频| 亚洲黄色片网站| 亚洲黄一区二区| 欧美日韩精品一二三区| 亚洲图片在线| 亚洲欧美日韩国产精品| 国产一级久久| 欧美成人精品h版在线观看| 免费欧美在线| 国产精品99久久久久久宅男| 亚洲图片自拍偷拍| 国产欧美一区二区三区久久| 久久久久国产一区二区三区| 久久九九国产精品怡红院| 亚洲第一天堂av| 91久久久久| 国产精品女主播一区二区三区| 午夜欧美精品| 久久蜜臀精品av| 一区二区欧美激情| 午夜欧美不卡精品aaaaa| 在线免费观看一区二区三区| 亚洲激情视频| 国产三级精品在线不卡| 亚洲高清视频一区二区| 国产精品成人一区二区三区吃奶 | 国产精品人人做人人爽人人添| 午夜精品久久久久久久蜜桃app| 欧美一区二区性| 日韩午夜av电影| 午夜精品久久久久久久99热浪潮| 一区二区亚洲| 一区二区精品在线观看| 影音先锋久久| 亚洲午夜小视频| 亚洲精品小视频在线观看| 羞羞色国产精品| 在线亚洲一区二区| 久久亚洲私人国产精品va| 午夜精品影院| 欧美区视频在线观看| 蜜桃av噜噜一区| 国产热re99久久6国产精品| 91久久精品视频| 在线日韩中文字幕| 欧美在线观看视频一区二区三区| 在线亚洲免费| 欧美黄色片免费观看| 美女日韩欧美| 国产在线视频欧美| 亚洲视频在线观看免费| 亚洲理伦在线| 久久九九久久九九| 亚洲综合视频网| 欧美—级高清免费播放| 久热国产精品| 国产亚洲一区精品| 欧美一级电影久久| 午夜精品福利在线| 欧美日韩视频一区二区三区| 欧美激情精品久久久六区热门 | 久久成人免费网| 欧美日韩综合久久| 亚洲人成网站在线播| 91久久亚洲| 免费观看成人| 亚洲国产高清在线| 亚洲日本视频| 欧美岛国在线观看| 亚洲人成小说网站色在线| 99re8这里有精品热视频免费 | 亚洲国产欧美一区二区三区同亚洲| 国内久久精品视频| 久久精品国产亚洲aⅴ| 久久久综合视频| 黄色亚洲大片免费在线观看| 欧美一区二区三区男人的天堂| 欧美一区二区视频在线| 国产精品高潮在线| 亚洲欧美日韩高清| 久久久五月婷婷| 影音先锋久久资源网| 狂野欧美激情性xxxx| 欧美成人中文字幕在线| 在线日本成人| 激情文学一区| 久久人人爽人人| 欧美激情成人在线视频| 亚洲精品无人区| 欧美午夜在线| 性久久久久久久久| 美女诱惑一区| 一本色道久久加勒比88综合| 欧美亚洲不卡| 欧美中文在线视频| 亚洲激情一区二区| 欧美激情中文字幕在线| 国产日产欧美a一级在线| 免费毛片一区二区三区久久久| 亚洲午夜女主播在线直播| 久久久久久**毛片大全| 女人天堂亚洲aⅴ在线观看| 在线免费观看视频一区| 欧美极品色图| 亚洲欧美日韩在线观看a三区| 久久尤物电影视频在线观看| 亚洲精品一区久久久久久| 欧美色综合网| 久久福利一区| 亚洲精品在线一区二区| 欧美一区二区三区视频在线观看| 亚洲第一福利视频| 国产精品久久久久久久久久免费| 性做久久久久久久免费看| 欧美激情第二页| 羞羞答答国产精品www一本| 亚洲二区视频在线| 国产精品欧美精品| 久久久999精品| 国产嫩草影院久久久久| 久久久人人人| 亚洲一区二区在线播放| 亚洲电影免费观看高清完整版在线 | 性欧美精品高清| 亚洲黄色尤物视频| 久久精品99国产精品日本| 亚洲免费观看视频| 悠悠资源网亚洲青| 国产精品综合不卡av| 欧美激情精品久久久六区热门 | 亚洲第一偷拍| 国产午夜精品一区二区三区欧美 | 亚洲欧美日本国产有色| 亚洲精品国产精品久久清纯直播| 国产一区二区按摩在线观看| 欧美手机在线| 欧美精品三级| 欧美极品在线播放| 欧美.www|