• <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>

            jake1036

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

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

               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       !出錯則重新執行加載程序
                   mov ax , #
            0x0000
                   
            int 0x13
                   j load_setup

            !利用int13 中斷,得到磁盤驅動器的參數,特別是每道磁道的扇區數量
                   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 ,現在開始將system模塊加載到0x10000開始處
                   mov ax , #SYSSEG
                   mov es , ax
                   call read_it         
            !讀磁盤上system模塊,es為輸入參數
                   call kill_motor      
            !關閉馬達

            !確定根文件系統所在的設備號
                   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模塊加載進內存的子函數
                  
              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:            
            !接著判斷是否已經讀入全部的數據,比較當前所讀的段是否就是系統數據末端所處的段
                 mov ax , es      
            !如果不是,就跳轉至下面的ok1標號處繼續讀數據  
                 cmp ax , #ENDSEG
                 jb ok1_read
                 ret              
            !如果到達了系統末端,就結束此循環
             
              ok1_read:           
            !計算和驗證當前磁道上需要讀取的扇區數目,放在ax寄存器中,根據當前磁道還未讀取的扇區數和 
                                  
            !段內數據字節開始偏移的位置,計算如果全部讀取這些未讀扇區,所讀的字節是否會超過64kb的限制
                                  
            !若會超過,則根據此次最多能讀入的字節數,反算出需要讀取的扇區數。
              seg cs
              mov ax , sectors    
            !取每磁道的扇區數
              sub ax , sread      
            !減去當前磁道已讀扇區數
              mov cx , ax         
            !cx = ax 為當前磁道的未讀扇區數
              shl cx , #
            9         !當前未讀的字節數
              add cx , bx         
            !此次操作之后,段內偏移地址現在的值
              jnc ok2_read        
            !若沒有超過64kb,則跳轉至ok2_read
              je  ok2_read

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

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

             

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

             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        !存放當前啟動軟盤每磁道的扇區數      
             msg1:
                  .
            byte 13 , 10
                  .ascii 
            "Loading system  "
                  .
            byte 13 , 10 , 13 ,10
            .org 
            508
             root_dev:
                  .word ROOT_DEV    
            !這里存放根文件系統的所在設備號
             boot_flag:
                  .word 
            0xAA55

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

                     

             

                       2     setup.s程序分析

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

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

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

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

              在GDT中設置了當前代碼段的描述符和數據段的描述符,在head.s中會重新設置這些描述符表。必須使用lgdt把描述符表的基地址告知CPU,再將機器狀態字置位即可進入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取系統所含擴展內存大小并保存在內存0x90002處。
                mov ah , #
            0x88 
                
            int 0x15
                mov [
            2] , ax
                
                
            !得到顯示卡的屬性
                
            !調用BIOS中斷0x10,功能號ah=0x0f
                
            !返回:ah=字符列數;al=顯示模式;bh=當前顯示頁
                
            !0x90004存放當前頁 ,0x90006存放顯示模式,0x90007存放字符列數

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


                
            !檢查顯示方式并取參數
                 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的參數值 ------> ds:si
                 mov ax , #INITSEG   
                 mov es , ax
                 mov di , #
            0x0090    !傳輸目的地址 0x9000:0x0090
                 mov cx , #
            0x10
                 rep 
                    movsb
                 
                
            !檢查系統是否有第二個硬盤
                 mov ax , #
            0x01500
                 mov dl , #
            0x81
                 
            int 0x13
                 jc no_disk1
                 cmp ah , #
            3
                 je is_disk1
                 
              no_disk1:              
            !第二塊硬盤不存在,所以清空參數表
                 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位保護模式運行,首先加載機器狀態字,也稱控制寄存器cr0 
              
            !在設置該bit之后,隨后的一條指令必須是一條段間跳轉指令,一用于刷新當前指令隊列
              
            !因為CPU在執行一條指令之前就已經從內存讀取該指令并對其進行解碼。
              
              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
                
              
            !數據段選擇符的值
                 .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模塊之后開始的扇區中,即從磁盤上第6個扇區開始位置。 linux內核一般大約有120KB ,在磁盤上大概占用240個扇區。
             
               之后我們將在保護模式下編程,head.s使用 as 和ld 編譯器和連接器。 這段程序實際上處于絕對地址0處開始的地方,首先是加載各個數據段寄存器,重新設置中斷描述符表idt,共256項,并使各個表項指向一個只報錯誤的啞中斷子程序 ignore_int。 

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

              最后,head.s 程序利用返回指令將預先放置在堆棧中的 main.c程序的入口地址彈出,去執行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


                #該返回指令執行先前壓入堆棧的main函數的入口地址
                ret


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

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

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

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

            L6:
                 jmp L6 

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


            (4)完整的代碼如下


            .text
              .global _idt , _gdt , _pg_dir , _tmp_floppy_area
            _pg_dir:  #頁目錄將會設置在這里,所以該程序會被覆蓋掉
             
            startup_32:
              movl $
            0x10 , %eax #0x10已經是全局描述符的在描述符表中的偏移值
              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地址線是否已經打開,采用的方法是向內存0x000000處寫入任意的一個數值
              #然后看內存地址0x100000是否也是這個數值,如果一樣的話,就說明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   #向協處理器發送初始化命令
               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字節,一張頁表可以映射1024 
            * 4kb的內存空間 ,即4M
             .org 
            0x1000
              pg0:

             .org 
            0x2000
              pg1:

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

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

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

            after_page_tables:
             pushl $
            0          #這些是main函數的參數
             pushl $
            0
             pushl $
            0
             pushl $L6         #main函數的返回地址
             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個字節
               push  
            %es
               push  
            %fs
              
               movl $
            0x10 , %eax
               mov  
            %ax  , %ds
               mov  
            %ax  , %es
               mov  
            %ax  , %fs
               pushl $int_msg   #向printk函數傳遞參數 
               call _printk     #該函數在
            /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


                #該返回指令執行先前壓入堆棧的main函數的入口地址
                ret

                #140行將壓入堆棧的main指令彈出,并跳到main函數中去
             .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字節,初始化為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 閱讀(839) 評論(0)  編輯 收藏 引用

            一本色综合久久| 伊人久久无码精品中文字幕| 奇米综合四色77777久久| 99精品国产在热久久无毒不卡| 狠色狠色狠狠色综合久久| 久久无码国产| 久久亚洲AV成人出白浆无码国产 | 久久综合狠狠色综合伊人| 久久久久久青草大香综合精品| 偷偷做久久久久网站| 青草影院天堂男人久久| 2020久久精品亚洲热综合一本| 国产午夜久久影院| 免费久久人人爽人人爽av| 国产精品热久久毛片| 性高湖久久久久久久久| 久久综合精品国产一区二区三区| 97久久香蕉国产线看观看| 思思久久99热只有频精品66| 国产免费久久精品99久久| 2021精品国产综合久久| 久久精品一本到99热免费| 九九久久精品国产| 999久久久免费国产精品播放| 亚洲精品无码久久一线| 亚洲精品无码久久久久AV麻豆| 狠狠精品干练久久久无码中文字幕| 久久精品亚洲日本波多野结衣 | 久久久久国产精品三级网| 99久久精品国产麻豆| 欧美黑人激情性久久| 色欲av伊人久久大香线蕉影院| 亚洲国产一成久久精品国产成人综合| 亚洲嫩草影院久久精品| 26uuu久久五月天| 99久久国产免费福利| 久久精品成人欧美大片 | 热综合一本伊人久久精品| 久久久国产精品| 国产精品乱码久久久久久软件 | 国产精品伊人久久伊人电影|