• <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>
            隨筆-80  評論-24  文章-0  trackbacks-0
            為了盡快進入內核,并沒有讓loader做太多工作,它的工作其實主要有三項:
            1、保存一些系統參數,如光標位置、擴展內存大小、顯示模式、顯存大小以及硬盤參數等等
            2、將內核移動到指定位置,這里移動到0x0起始的位置
            3、切換進入保護模式
            還是先貼代碼:

              1 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
              2 ; 該文件主要作用是獲取一些系統參數,將參數保存在0xf000:0x0的位置,然后準備gdt,
              3 ; 并切換到保護模式,不過該gdt只是臨時性的,在進入kernel之后還會重新設置gdt;
              4 ; 在保護模式下將kernel從原來位置加載到指定位置,這里我們假定指定位置為0x0開始的地方。
              5 ; 移動kernel文件的方法因為文件的類型不同而不同,在linux3.0.0.12下,gcc版本4.5編譯
              6 ; 為ELF格式的二進制文件,將program segment按照編譯時指定的虛擬地址加載到相應位置。
              7 ; kernel的入口地址為KERNEL_ADDR,注意該值必須和在鏈接內核時指定的入口地址一致,
              8 ; 否則加載內核失敗;成功加載內核后便跳轉到內核執行。
              9 
             10 ; Author :
             11 ; v0.01 2011/11/22
             12 ;
             13 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
             14 jmp start
             15 
             16 PARAM_ADDR    equ 0xf000 ; 系統參數保存在的位置的段基址
             17 KERNEL_ADDR    equ    0x0 ; kernel入口地址,注意,此處的值一定要和編譯kernel文件時指定的入口地址相同
             18 
             19 [SECTION .code16]
             20 [BITS 16]
             21 start:
             22     mov ax, cs ; 當前cs值為:0x8000
             23     mov ds, ax
             24     mov es, ax
             25     sub ax, 0x20
             26     mov ss, ax ; ss地址為0x7fe0
             27     mov sp, 0x200 ; 堆棧大小為512B = 0.5KB
             28 
             29     ; 獲取當前光標位置
             30     mov ah, 0x03
             31     xor bh, bh
             32     int 0x10
             33 
             34     ; 打印一些信息
             35     mov ax, cs
             36     mov es, ax
             37     mov cx, MSG1_LEN
             38     mov bx, 0x0007
             39     mov bp, MSG1 ; 顯示字符串"Loading "
             40     mov ax, 0x1301
             41     int 0x10
             42 
             43     push es ; 先保存es寄存器的值
             44     mov ax, 0x0
             45     mov es, ax
             46     mov byte al, [es:0x7dfb] ; 取出保存在boot文件尾部的loader文件大小值
             47     mov byte [LOADER_LEN], al
             48     mov word ax, [es:0x7dfc]
             49     mov word [KERNEL_LEN], ax ; 取出保存在boot文件尾部的kernel文件大小值
             50     pop es
             51 
             52     ; 取得kernel的入口地址以及第一個program segment在內存中的物理地址
             53     mov bx, es
             54     xor ax, ax
             55     mov byte al, [LOADER_LEN]
             56     shl ax, 0x05
             57     add bx, ax
             58     mov es, bx ; es指向kernel文件頭部
             59     mov si, MSG3
             60     mov edi, 0x0
             61     mov cx, 0x06
             62     cld
             63     repe cmpsb
             64     cmp cx, 0x0 ; 如果kernel文件頭部的魔數確實為“kernel”,則認為是合法的kernel文件
             65     jne no_kernel
             66     mov dword eax, [es:di]
             67     cmp eax, KERNEL_ADDR
             68     jne no_kernel
             69     mov dword [KERNEL_ENTRY], eax ; 取得kernel的入口地址
             70     add di, 0x04
             71     mov word ax, [es:di]
             72     mov word [PROG_SEG_NUM], ax ; 取得kernel文件中program segment的數量
             73     add di, 0x02
             74     xor eax, eax
             75     mov ax, es
             76     shl eax, 4
             77     and edi, 0x0000ffff
             78     add eax, edi
             79     mov dword [PROG_SEG_FIRST], eax ; 保存kernel中第一個program segment在內存中的物理地址
             80 
             81     ; 下面的操作是從linux0.11中“拿”來的
             82     ; 取得當前光標位置
             83     push ds
             84     push es
             85 
             86     mov ax, PARAM_ADDR
             87     mov ds, ax
             88     mov ah, 0x03
             89     xor bh, bh
             90     int 0x10
             91 
             92     ; 獲得擴展內存大小,即1MB以后的內存大小,以KB計
             93     mov word [0], dx
             94     mov ah, 0x88
             95     int 0x15
             96     mov word [2], ax
             97 
             98     ; 顯示卡當前顯示模式
             99     mov ah, 0x0f
            100     int 0x10
            101     mov word [4], bx
            102     mov word [6], ax
            103 
            104     ; 檢查顯示方式(EGA/VGA)并取參數
            105     mov ah, 0x12
            106     mov bl, 0x10
            107     int 0x10
            108     mov word [8], ax
            109     mov word [10], bx
            110     mov word [12], cx
            111 
            112     ; 取第一塊硬盤hd0的信息(復制硬盤參數表)。
            113     ; 第一個硬盤參數表的首地址竟然是中斷向量0x41的向量值!
            114     ; 而第二塊硬盤參數表緊接著第一個表的后面。
            115     ; 中斷向量0x46的向量值也指向這第二塊硬盤的參數表首地址。
            116     ; 表的長度為16字節(0x10)。
            117     mov ax, 0x0000
            118     mov ds, ax
            119     lds si, [4 * 0x41]
            120     mov ax, PARAM_ADDR
            121     mov es, ax
            122     mov di, 0x0080
            123     mov cx, 0x10
            124     rep movsb
            125 
            126     ; 取第二塊硬盤hd1的信息
            127     mov ax, 0x0000
            128     mov ds, ax
            129     lds si, [4 * 0x46]
            130     mov ax, PARAM_ADDR
            131     mov es, ax
            132     mov di, 0x0090
            133     mov cx, 0x10
            134     rep movsb
            135 
            136     ; 檢查系統是否存在第二塊硬盤,不存在則將第二個表清零。
            137     mov ax, 0x1500
            138     mov dl, 0x81
            139     int 0x13
            140     jc no_disk1
            141     cmp ah, 0x03
            142     je is_disk1
            143 no_disk1:
            144     mov ax, PARAM_ADDR
            145     mov es, ax
            146     mov di, 0x0090
            147     mov cx, 0x10
            148     mov ax, 0x00
            149     rep movsb
            150 
            151 is_disk1:
            152     pop es
            153     pop ds
            154     jmp setup_pm
            155 
            156 no_kernel:
            157     ; 獲取當前光標位置
            158     mov ah, 0x03
            159     xor bh, bh
            160     int 0x10
            161 
            162     ; 打印一些信息
            163     mov ax, cs
            164     mov es, ax
            165     mov cx, MSG2_LEN
            166     mov bx, 0x0007
            167     mov bp, MSG2 ; 顯示字符串"[[ NO KERNEL ]]\r\nPress any key to reboot now "
            168     mov ax, 0x1301
            169     int 0x10
            170 
            171     xor ax, ax
            172     int 0x16 ; 等待用戶輸入任意一個鍵盤字符
            173     int 0x19 ; 系統重啟
            174 
            175 setup_pm:
            176     ; 將保護模式代碼段基地址填入GDT1描述符中
            177     xor eax, eax
            178     mov ax, cs
            179     shl eax, 4
            180     add eax, PMCODE
            181     mov word [CS_TMP + 2], ax
            182     shr eax, 16
            183     mov byte [CS_TMP + 4], al
            184     mov byte [CS_TMP + 7], ah
            185 
            186     ; 將保護模式數據段基地址填入GDT1描述符中
            187     xor eax, eax
            188     mov ax, ds
            189     shl eax, 4
            190     add eax, PMDATA
            191     mov word [DS_TMP + 2], ax
            192     shr eax, 16
            193     mov byte [DS_TMP + 4], al
            194     mov byte [DS_TMP + 7], ah
            195 
            196     ; 填充GDTPTR結構體
            197     ; 該結構體將被加載進gdtr寄存器
            198     xor eax, eax
            199     mov ax, ds
            200     shl eax, 4
            201     add eax, GDT
            202     mov dword [GDTPTR + 2], eax
            203 
            204     lgdt [GDTPTR]
            205 
            206     ; 關閉中斷
            207     cli
            208 
            209     ; 通過設置鍵盤控制器的端口值來打開A20地址線
            210     call empty_8042
            211     mov al, 0xd1
            212     out 0x64, al
            213     call empty_8042
            214     mov al, 0xdf
            215     out 0x60, al
            216     call empty_8042
            217 
            218     ; 修改cr0寄存器的最后一位PE位
            219     ; 注意lmsw指令僅僅對cr0寄存器的最后四位有影響
            220     mov eax, cr0
            221     or eax, 0x01
            222     lmsw ax
            223 
            224     ; 真正跳轉到保護模式!!!!!!
            225     jmp dword 0x08:0x00
            226 
            227 empty_8042:
            228     nop
            229     nop
            230     in al, 0x64
            231     test al, 0x02
            232     jnz empty_8042
            233     ret
            234 
            235 [section .pmcode]
            236 align 32
            237 [bits 32]
            238 PMCODE:
            239     mov ax, 0x10
            240     mov ds, ax
            241     xor ecx, ecx
            242     mov word cx, [pm_PROG_SEG_NUM] ; 取得kernel文件中program segment的段數
            243     mov dword esi, [pm_PROG_SEG_FIRST] ; 取得kernel文件中的第一個program segment的地址
            244     mov ax, 0x20
            245     mov ds, ax
            246     mov es, ax
            247 move_kernel:
            248     push ecx
            249     mov ecx, [esi] ; 取得program segment在文件中的大小
            250     add esi, 4
            251     mov edi, [esi] ; 取得段在內存中的地址
            252     add esi, 4
            253     cld
            254     rep movsb
            255     pop ecx
            256     loop move_kernel
            257 
            258     mov ax, 0x20
            259     mov ds, ax
            260     mov es, ax
            261     mov ss, ax
            262     mov fs, ax
            263     mov gs, ax
            264     jmp 0x18:KERNEL_ADDR
            265 
            266 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
            267 ; GDT描述符中由低到高的含義如下:
            268 ;  BYTE7  || BYTE6 BYTE5 || BYTE4 BYTE3 BYTE2 || BYTE1 BYTE0 ||
            269 ; 段基地址||      屬性   ||       段基址      ||    段界限   ||
            270 ; (31-24||             ||      (23-0)       ||    (15-0)   ||
            271 ;
            272 ; 屬性含義如下:
            273 7 || 6 || 5 || 4 || 3   2   1   0 || 7 || 6 || 5 || 4   3   2  1   0 ||
            274 ; G ||D/B|| 0 ||AVL|| 段界限(19-16|| P ||DPL|| S ||       TYPE       ||
            275 ;
            276 ; G位:段界限的粒度,0為字節,1為4KB
            277 ; D/B;在可執行代碼段中,這一位叫做D位:
            278 ;        D=1時指令使用32位地址及32位或8位操作數;D=0時使用16位地址及16位或8位操作數
            279 ;      在數據段描述符中,這一位叫做B位:
            280 ;        B=1時段上部界限為4GB;B=0時段上部界限為64KB
            281 ;      在堆棧段描述符中,這一位叫做B位:
            282 ;        B=1時隱式的堆棧訪問指令(比如push、pop、call等)使用32位堆棧指針寄存器esp
            283 ;        B=0時隱式的堆棧訪問指令使用16位堆棧指針寄存器sp
            284 ; AVL: 保留
            285 ; P位: 1代表段在內存中存在;0代表段在內存中不存在
            286 ; DPL:描述符特權級
            287 ; S位:1代表是數據段或者代碼段描述符;0代表是系統段或者門描述符
            288 ;TYPE:說明描述符類型
            289 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
            290 
            291 [section .pmdata]
            292 align 32
            293 [bits 32]
            294 PMDATA:
            295         ; 第一個描述符空,不使用
            296 GDT:    db 0x00,    0x00
            297         db 0x00,    0x00,    0x00
            298         db 0x00,    0x00
            299         db 0x00
            300 
            301         ; 第二個描述符為保護模式代碼所在的段
            302         ; 段基地址在切換到保護模式前填入;
            303         ; 界限為0xfffff,段界限粒度為4KB,因此段長為4GB;
            304         ; 是可執行可讀的32位代碼段
            305 CS_TMP:    db 0xff,    0xff
            306         db 0x00,    0x00,    0x00
            307         db 0x9a,    0xcf
            308         db 0x00
            309 
            310         ; 第三個描述符為切換到保護模式用到的數據段
            311         ; 段基地址為PMDATA,在切換到保護模式前填入;
            312         ; 界限為0xfffff,段界限粒度為4KB,因此段長為4GB;
            313         ; 是可讀可寫32位數據段
            314 DS_TMP:    db 0xff,    0xff
            315         db 0x00,    0x00,    0x00
            316         db 0x92,    0xcf
            317         db 0x00
            318 
            319         ; 第四個描述符的段基地址為0;
            320         ; 界限為0xfffff,段界限粒度為4KB;
            321         ; 是可執行可讀的32位代碼段
            322 CODE:    db 0xff,    0xff
            323         db 0x00,    0x00,    0x00
            324         db 0x9a,    0xcf
            325         db 0x00
            326 
            327         ; 第五個描述符的段基地址為0;
            328         ; 界限為0xfffff,段界限粒度為4KB;
            329         ; 是可讀可寫32位數據段
            330 DATA:    db 0xff,    0xff
            331         db 0x00,    0x00,    0x00
            332         db 0x92,    0xcf
            333         db 0x00
            334 
            335 GDT_LEN                equ $ - GDT
            336 GDTPTR                dw GDT_LEN - 1
            337                     dd 0
            338 
            339 ; 在實模式下直接使用LOADER_LEN等標號就能取出內存中的內容
            340 ; 在保護模式下想要取LOADER_LEN等內存中的內容需要使用基于段基地址的偏移
            341 pm_LOADER_LEN        equ $ - PMDATA
            342 LOADER_LEN:            db 0 ; 記錄loader文件的大小
            343 
            344 pm_KERNEL_LEN        equ $ - PMDATA
            345 KERNEL_LEN:            dw 0 ; 記錄kernel文件的大小,注意,這里指的是從System.Image解壓出來前的大小
            346 
            347 pm_KERNEL_ENTRY        equ $ - PMDATA
            348 KERNEL_ENTRY:        dd 0 ; 記錄kernel的入口虛擬地址
            349 
            350 pm_PROG_SEG_FIRST    equ $ - PMDATA
            351 PROG_SEG_FIRST        dd 0 ; 記錄kerne文件中第一個program segment在內存中的物理地址
            352 
            353 pm_PROG_SEG_NUM        equ $ - PMDATA
            354 PROG_SEG_NUM        dw 0 ; 記錄kerne文件中program segment的數量
            355 
            356 ; 以下為在實模式下需要打印的字符串
            357 MSG1:                db 1310"Loading "1310
            358 MSG1_LEN            equ $ - MSG1
            359 
            360 MSG2:                db 1310"[[ NO KERNEL ]]"1310"Press any key to reboot now "1310
            361 MSG2_LEN            equ $ - MSG2
            362 
            363 MSG3:                db "kernel"
            364 MSG3_LEN            equ $ - MSG3
            365 

                 思路比較清晰,首先保存系統參數,這個步驟是從linux中拿過來的,會在以后分頁機制以及tty中用到;其次是將內核移動到內存地址0x0的位置,這部分需要參照
            http://www.shnenglu.com/myjfm/archive/2011/11/20/160556.htmlhttp://www.shnenglu.com/myjfm/archive/2011/11/20/160558.html兩部分,這兩部分是kernel的組織方式,將ELF格式的kernel分program segment移動到指定位置;最后是切換進入保護模式,這里需要加載gdt,loader準備了5個全局段描述符,其中兩個是專門為剛剛切換到保護模式后運行所準備的代碼段和數據段描述符。兩外兩個是從絕對物理地址0x0開始,大小為4G的代碼段和數據段描述符,不過等到進入kernel還需要重新設置段描述符,因此這只是權易之計。
            posted on 2011-11-22 20:08 myjfm 閱讀(738) 評論(0)  編輯 收藏 引用 所屬分類: 操作系統
            久久精品国产亚洲沈樵| 2021国产精品午夜久久| 久久精品成人免费网站| 久久久无码精品午夜| 性高湖久久久久久久久| 久久这里只精品国产99热| 久久久久亚洲AV成人网人人软件| 18禁黄久久久AAA片| 91精品国产91久久久久久| 亚洲欧美伊人久久综合一区二区 | 欧美日韩久久中文字幕| 久久久久久夜精品精品免费啦| 精品无码久久久久久久动漫| 久久婷婷五月综合色奶水99啪| 国产欧美久久久精品| 久久久黄色大片| 久久久久久久综合日本| av国内精品久久久久影院| 久久久久久精品无码人妻| 久久国产成人亚洲精品影院| 国产精品毛片久久久久久久| 久久精品无码一区二区WWW| 久久综合成人网| 精品久久久久国产免费| 久久这里只精品国产99热| 色狠狠久久AV五月综合| 欧美国产成人久久精品| 欧美日韩精品久久久久| 久久久无码精品亚洲日韩京东传媒| 久久亚洲AV无码西西人体| 一本一道久久精品综合| 中文字幕一区二区三区久久网站| 国产精品久久久久9999| 国产精品久久久久久久久免费| 亚洲中文字幕无码久久2017| 久久精品国产免费观看| 亚洲欧美成人久久综合中文网| 亚洲第一永久AV网站久久精品男人的天堂AV | 国内精品久久久久久中文字幕| 成人a毛片久久免费播放| 久久99精品久久久久久野外 |