• <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內(nèi)核學(xué)習(xí)之啟動(dòng)程序模塊

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

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

             
            !利用BIOS中斷INT 13將 setup模塊,從磁盤第2個(gè)扇區(qū)開始讀到0x90200開始處,共讀4個(gè)扇區(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       !出錯(cuò)則重新執(zhí)行加載程序
                   mov ax , #
            0x0000
                   
            int 0x13
                   j load_setup

            !利用int13 中斷,得到磁盤驅(qū)動(dòng)器的參數(shù),特別是每道磁道的扇區(qū)數(shù)量
                   mov ax , 
            0x0800
                   mov dl , 
            0x00
                   
            int 0x13
            !重新設(shè)置es的值      
                   mov sectors , cx
                   mov ax , #INITSEG
                   mov es , ax
            !print some message
                   mov ah , #
            0x03    !讀光標(biāo)位置,返回光標(biāo)位置在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      
            !關(guān)閉馬達(dá)

            !確定根文件系統(tǒng)所在的設(shè)備號(hào)
                   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      !此處跳進(jìn)setup程序

            !下面是將system模塊加載進(jìn)內(nèi)存的子函數(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ù),比較當(dāng)前所讀的段是否就是系統(tǒng)數(shù)據(jù)末端所處的段
                 mov ax , es      
            !如果不是,就跳轉(zhuǎn)至下面的ok1標(biāo)號(hào)處繼續(xù)讀數(shù)據(jù)  
                 cmp ax , #ENDSEG
                 jb ok1_read
                 ret              
            !如果到達(dá)了系統(tǒng)末端,就結(jié)束此循環(huán)
             
              ok1_read:           
            !計(jì)算和驗(yàn)證當(dāng)前磁道上需要讀取的扇區(qū)數(shù)目,放在ax寄存器中,根據(jù)當(dāng)前磁道還未讀取的扇區(qū)數(shù)和 
                                  
            !段內(nèi)數(shù)據(jù)字節(jié)開始偏移的位置,計(jì)算如果全部讀取這些未讀扇區(qū),所讀的字節(jié)是否會(huì)超過64kb的限制
                                  
            !若會(huì)超過,則根據(jù)此次最多能讀入的字節(jié)數(shù),反算出需要讀取的扇區(qū)數(shù)。
              seg cs
              mov ax , sectors    
            !取每磁道的扇區(qū)數(shù)
              sub ax , sread      
            !減去當(dāng)前磁道已讀扇區(qū)數(shù)
              mov cx , ax         
            !cx = ax 為當(dāng)前磁道的未讀扇區(qū)數(shù)
              shl cx , #
            9         !當(dāng)前未讀的字節(jié)數(shù)
              add cx , bx         
            !此次操作之后,段內(nèi)偏移地址現(xiàn)在的值
              jnc ok2_read        
            !若沒有超過64kb,則跳轉(zhuǎn)至ok2_read
              je  ok2_read

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

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

             

             
            !read_track 子程序,讀當(dāng)前磁道上指定開始扇區(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   

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

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

                     

             

                       2     setup.s程序分析

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

               然后setup程序?qū)ystem模塊從 0x10000-0x8ffff(任務(wù)system模塊不會(huì)超過512kb) ,整體移動(dòng)到絕對(duì)內(nèi)存地址為0x0000處。

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

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

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

             

             

            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:   
                
            !保存光標(biāo)位置已備以后需要
                
            !這段代碼使用BIOS中斷取屏幕當(dāng)前的光標(biāo)位置,然后保存在內(nèi)存0x90000處就可以使用
                mov ax , #INITSEG  
                mov ds , ax
                mov ah , #
            0x03
                xor bh , bh
                
            int 0x10         !利用BIOS中斷 將當(dāng)前光標(biāo)位置存檔到 dx
                mov [
            0] , dx     !將光標(biāo)位置存放在0x90000處

                
            !得到內(nèi)存的大小值
                
            !利用BIOS中斷0x15功能號(hào) ah=0x88取系統(tǒng)所含擴(kuò)展內(nèi)存大小并保存在內(nèi)存0x90002處。
                mov ah , #
            0x88 
                
            int 0x15
                mov [
            2] , ax
                
                
            !得到顯示卡的屬性
                
            !調(diào)用BIOS中斷0x10,功能號(hào)ah=0x0f
                
            !返回:ah=字符列數(shù);al=顯示模式;bh=當(dāng)前顯示頁(yè)
                
            !0x90004存放當(dāng)前頁(yè) ,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
                 
             
                
            !取得第一個(gè)硬盤的信息
                 mov ax , #
            0x0000
                 mov ds , ax
                 lds si , [
            4 * 0x41]
                 mov ax , #INITSEG
                 mov es , ax
                 mov di , #
            0x0080
                 mov cx , #
            0x10
                 rep 
                  movsb

                
            !取得第二個(gè)硬盤
                 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)是否有第二個(gè)硬盤
                 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:
                
            !從此開始進(jìn)入了保護(hù)模式
                 cli
                
            !首先把system模塊移動(dòng)到正確的位置
                 
                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:
                
              
            !在此處加載段描述符表,這里需要設(shè)置全局描述符表和中斷描述符表
              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芯片設(shè)置中斷號(hào)從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


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

              
            !下面這個(gè)子程序檢查鍵盤命令隊(duì)列是否為空
             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程序在被編譯生成目標(biāo)文件之后會(huì)與內(nèi)核其他程序一起被鏈接成system模塊,位于system模塊最前面,所以稱之為head程序的原因。system模塊將被放置在磁盤上setup模塊之后開始的扇區(qū)中,即從磁盤上第6個(gè)扇區(qū)開始位置。 linux內(nèi)核一般大約有120KB ,在磁盤上大概占用240個(gè)扇區(qū)。
             
               之后我們將在保護(hù)模式下編程,head.s使用 as 和ld 編譯器和連接器。 這段程序?qū)嶋H上處于絕對(duì)地址0處開始的地方,首先是加載各個(gè)數(shù)據(jù)段寄存器,重新設(shè)置中斷描述符表idt,共256項(xiàng),并使各個(gè)表項(xiàng)指向一個(gè)只報(bào)錯(cuò)誤的啞中斷子程序 ignore_int。 

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

              最后,head.s 程序利用返回指令將預(yù)先放置在堆棧中的 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位是段內(nèi)偏移的低16位
              movl 
            %edx , 4(%edi) #edx的高16位是段內(nèi)偏移地址的高16位,低16位是權(quán)限位 
              addl $
            8 , %edi
              dec 
            %ecx            #重復(fù)設(shè)置總共256個(gè)中斷描述符
              jne rp_sidt
              lidt idt_descr      #加載中斷描述符表寄存器
              ret

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

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

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

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


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


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


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

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

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

            after_page_tables:
             pushl $
            0          #這些是main函數(shù)的參數(shù)
             pushl $
            0
             pushl $
            0
             pushl $L6         #main函數(shù)的返回地址
             pushl $_main      #_main 是編譯程序?qū)ain的內(nèi)部表示法
             jmp setup_paging  #跳轉(zhuǎn)到建立頁(yè)表映射

            L6:
                 jmp L6 

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


            (4)完整的代碼如下


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

              lss _stack_start , 
            %esp #設(shè)置_stack_start----->ss:esp

              call setup_idt    #調(diào)用設(shè)置中斷描述符表的子程序
              call setup_gdt    #調(diào)用設(shè)置全局描述符表的子程序

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

              lss _stack_start , 
            %esp
              
              #以下代碼用來測(cè)試A20地址線是否已經(jīng)打開,采用的方法是向內(nèi)存0x000000處寫入任意的一個(gè)數(shù)值
              #然后看內(nèi)存地址0x100000是否也是這個(gè)數(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位是段內(nèi)偏移的低16位
              movl 
            %edx , 4(%edi) #edx的高16位是段內(nèi)偏移地址的高16位,低16位是權(quán)限位 
              addl $
            8 , %edi
              dec 
            %ecx            #重復(fù)設(shè)置總共256個(gè)中斷描述符
              jne rp_sidt
              lidt idt_descr      #加載中斷描述符表寄存器
              ret

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

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

             .org 
            0x2000
              pg1:

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

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

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

            after_page_tables:
             pushl $
            0          #這些是main函數(shù)的參數(shù)
             pushl $
            0
             pushl $
            0
             pushl $L6         #main函數(shù)的返回地址
             pushl $_main      #_main 是編譯程序?qū)ain的內(nèi)部表示法
             jmp setup_paging  #跳轉(zhuǎn)到建立頁(yè)表映射

            L6:
                 jmp L6 

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

               push  
            %ds   #入棧占4個(gè)字節(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頁(yè)內(nèi)存進(jìn)行清空處理
                               #1個(gè)頁(yè)目錄表,4個(gè)頁(yè)表 
                movl $
            1024 * 5 , %ecx
                xorl 
            %eax , %eax
                xorl 
            %edi , %edi
                
                cld ; rep ; stosl
               
                #頁(yè)目錄中只需要4個(gè)頁(yè)目錄, 7是屬性,表示該頁(yè)存在內(nèi)存中,且用戶可以訪問
                movl $pg0 
            + 7 , _pg_dir
                movl $pg1 
            + 7 , _pg_dir + 4
                movl $pg2 
            + 7 , _pg_dir + 8
                movl $pg3 
            + 7 , _pg_dir + 12

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


                #設(shè)置頁(yè)目錄表基址寄存器cr3的值,指向頁(yè)目錄表。cr3中保存的是頁(yè)目錄表的物理地址
                xorl 
            %eax , %eax
                movl 
            %eax , %cr3
                #設(shè)置啟動(dòng)分頁(yè)處理
                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項(xiàng),每項(xiàng)8字節(jié),初始化為0

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


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
















             

             

             


             

             

             

              

             

                 

             

             

             

             

             

             

             

             

             

             

             

             

             


             

             

             

             

             

             

             

             

             

             

             

             

                

             

             

             

             

             

             

             

             

             

             

             


             

             


             

             

               


             
               

             

             

             

             

             

             

               
              

             


             

             


                         

             

             

             

                  

             

             

             

             

             

             

                  
                  

             

             

             

             

             

             

             
                   
                 

             

             

             

             

             

             

             

             

             

             

             

                
                  



















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


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


            国产精品久久久久9999| 2019久久久高清456| 99精品国产在热久久无毒不卡 | 久久丫精品国产亚洲av| 国产成人无码久久久精品一| av无码久久久久久不卡网站 | 中文字幕久久久久人妻| 东京热TOKYO综合久久精品| 国产视频久久| 天天爽天天狠久久久综合麻豆| 久久亚洲精精品中文字幕| 国产精品99久久久久久www| 亚洲色大成网站www久久九| 久久国产V一级毛多内射| 久久精品aⅴ无码中文字字幕不卡| 国产精品无码久久四虎| 精品国产一区二区三区久久久狼| 久久久无码精品午夜| 久久精品国产只有精品2020| 人妻少妇久久中文字幕一区二区 | 午夜天堂av天堂久久久| 久久久久久毛片免费看| 国产欧美久久一区二区| 无码人妻精品一区二区三区久久久| 国内精品久久久久久麻豆| 99re久久精品国产首页2020| 亚洲AV乱码久久精品蜜桃| 精品伊人久久久| 久久丫忘忧草产品| 亚洲国产婷婷香蕉久久久久久| 精品久久人人妻人人做精品| 精品综合久久久久久98| 91亚洲国产成人久久精品网址| 久久亚洲欧美国产精品 | 久久亚洲国产成人精品无码区| 久久精品99久久香蕉国产色戒 | 国产高潮久久免费观看| 伊人久久精品线影院| 精品久久久久久久无码| 久久久中文字幕| 国产精品欧美亚洲韩国日本久久 |