• <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  評(píng)論-24  文章-0  trackbacks-0
            為了盡快進(jìn)入內(nèi)核,并沒有讓loader做太多工作,它的工作其實(shí)主要有三項(xiàng):
            1、保存一些系統(tǒng)參數(shù),如光標(biāo)位置、擴(kuò)展內(nèi)存大小、顯示模式、顯存大小以及硬盤參數(shù)等等
            2、將內(nèi)核移動(dòng)到指定位置,這里移動(dòng)到0x0起始的位置
            3、切換進(jìn)入保護(hù)模式
            還是先貼代碼:

              1 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
              2 ; 該文件主要作用是獲取一些系統(tǒng)參數(shù),將參數(shù)保存在0xf000:0x0的位置,然后準(zhǔn)備gdt,
              3 ; 并切換到保護(hù)模式,不過該gdt只是臨時(shí)性的,在進(jìn)入kernel之后還會(huì)重新設(shè)置gdt;
              4 ; 在保護(hù)模式下將kernel從原來位置加載到指定位置,這里我們假定指定位置為0x0開始的地方。
              5 ; 移動(dòng)kernel文件的方法因?yàn)槲募念愋筒煌煌趌inux3.0.0.12下,gcc版本4.5編譯
              6 ; 為ELF格式的二進(jìn)制文件,將program segment按照編譯時(shí)指定的虛擬地址加載到相應(yīng)位置。
              7 ; kernel的入口地址為KERNEL_ADDR,注意該值必須和在鏈接內(nèi)核時(shí)指定的入口地址一致,
              8 ; 否則加載內(nèi)核失敗;成功加載內(nèi)核后便跳轉(zhuǎn)到內(nèi)核執(zhí)行。
              9 
             10 ; Author :
             11 ; v0.01 2011/11/22
             12 ;
             13 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
             14 jmp start
             15 
             16 PARAM_ADDR    equ 0xf000 ; 系統(tǒng)參數(shù)保存在的位置的段基址
             17 KERNEL_ADDR    equ    0x0 ; kernel入口地址,注意,此處的值一定要和編譯kernel文件時(shí)指定的入口地址相同
             18 
             19 [SECTION .code16]
             20 [BITS 16]
             21 start:
             22     mov ax, cs ; 當(dāng)前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     ; 獲取當(dāng)前光標(biāo)位置
             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的入口地址以及第一個(gè)program segment在內(nèi)存中的物理地址
             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文件頭部的魔數(shù)確實(shí)為“kernel”,則認(rèn)為是合法的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的數(shù)量
             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中第一個(gè)program segment在內(nèi)存中的物理地址
             80 
             81     ; 下面的操作是從linux0.11中“拿”來的
             82     ; 取得當(dāng)前光標(biāo)位置
             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     ; 獲得擴(kuò)展內(nèi)存大小,即1MB以后的內(nèi)存大小,以KB計(jì)
             93     mov word [0], dx
             94     mov ah, 0x88
             95     int 0x15
             96     mov word [2], ax
             97 
             98     ; 顯示卡當(dāng)前顯示模式
             99     mov ah, 0x0f
            100     int 0x10
            101     mov word [4], bx
            102     mov word [6], ax
            103 
            104     ; 檢查顯示方式(EGA/VGA)并取參數(shù)
            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的信息(復(fù)制硬盤參數(shù)表)。
            113     ; 第一個(gè)硬盤參數(shù)表的首地址竟然是中斷向量0x41的向量值!
            114     ; 而第二塊硬盤參數(shù)表緊接著第一個(gè)表的后面。
            115     ; 中斷向量0x46的向量值也指向這第二塊硬盤的參數(shù)表首地址。
            116     ; 表的長(zhǎng)度為16字節(jié)(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     ; 檢查系統(tǒng)是否存在第二塊硬盤,不存在則將第二個(gè)表清零。
            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     ; 獲取當(dāng)前光標(biāo)位置
            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 ; 等待用戶輸入任意一個(gè)鍵盤字符
            173     int 0x19 ; 系統(tǒng)重啟
            174 
            175 setup_pm:
            176     ; 將保護(hù)模式代碼段基地址填入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     ; 將保護(hù)模式數(shù)據(jù)段基地址填入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結(jié)構(gòu)體
            197     ; 該結(jié)構(gòu)體將被加載進(jìn)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     ; 關(guān)閉中斷
            207     cli
            208 
            209     ; 通過設(shè)置鍵盤控制器的端口值來打開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指令僅僅對(duì)cr0寄存器的最后四位有影響
            220     mov eax, cr0
            221     or eax, 0x01
            222     lmsw ax
            223 
            224     ; 真正跳轉(zhuǎn)到保護(hù)模式!!!!!!
            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的段數(shù)
            243     mov dword esi, [pm_PROG_SEG_FIRST] ; 取得kernel文件中的第一個(gè)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] ; 取得段在內(nèi)存中的地址
            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為字節(jié),1為4KB
            277 ; D/B;在可執(zhí)行代碼段中,這一位叫做D位:
            278 ;        D=1時(shí)指令使用32位地址及32位或8位操作數(shù);D=0時(shí)使用16位地址及16位或8位操作數(shù)
            279 ;      在數(shù)據(jù)段描述符中,這一位叫做B位:
            280 ;        B=1時(shí)段上部界限為4GB;B=0時(shí)段上部界限為64KB
            281 ;      在堆棧段描述符中,這一位叫做B位:
            282 ;        B=1時(shí)隱式的堆棧訪問指令(比如push、pop、call等)使用32位堆棧指針寄存器esp
            283 ;        B=0時(shí)隱式的堆棧訪問指令使用16位堆棧指針寄存器sp
            284 ; AVL: 保留
            285 ; P位: 1代表段在內(nèi)存中存在;0代表段在內(nèi)存中不存在
            286 ; DPL:描述符特權(quán)級(jí)
            287 ; S位:1代表是數(shù)據(jù)段或者代碼段描述符;0代表是系統(tǒng)段或者門描述符
            288 ;TYPE:說明描述符類型
            289 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
            290 
            291 [section .pmdata]
            292 align 32
            293 [bits 32]
            294 PMDATA:
            295         ; 第一個(gè)描述符空,不使用
            296 GDT:    db 0x00,    0x00
            297         db 0x00,    0x00,    0x00
            298         db 0x00,    0x00
            299         db 0x00
            300 
            301         ; 第二個(gè)描述符為保護(hù)模式代碼所在的段
            302         ; 段基地址在切換到保護(hù)模式前填入;
            303         ; 界限為0xfffff,段界限粒度為4KB,因此段長(zhǎng)為4GB;
            304         ; 是可執(zhí)行可讀的32位代碼段
            305 CS_TMP:    db 0xff,    0xff
            306         db 0x00,    0x00,    0x00
            307         db 0x9a,    0xcf
            308         db 0x00
            309 
            310         ; 第三個(gè)描述符為切換到保護(hù)模式用到的數(shù)據(jù)段
            311         ; 段基地址為PMDATA,在切換到保護(hù)模式前填入;
            312         ; 界限為0xfffff,段界限粒度為4KB,因此段長(zhǎng)為4GB;
            313         ; 是可讀可寫32位數(shù)據(jù)段
            314 DS_TMP:    db 0xff,    0xff
            315         db 0x00,    0x00,    0x00
            316         db 0x92,    0xcf
            317         db 0x00
            318 
            319         ; 第四個(gè)描述符的段基地址為0;
            320         ; 界限為0xfffff,段界限粒度為4KB;
            321         ; 是可執(zhí)行可讀的32位代碼段
            322 CODE:    db 0xff,    0xff
            323         db 0x00,    0x00,    0x00
            324         db 0x9a,    0xcf
            325         db 0x00
            326 
            327         ; 第五個(gè)描述符的段基地址為0;
            328         ; 界限為0xfffff,段界限粒度為4KB;
            329         ; 是可讀可寫32位數(shù)據(jù)段
            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 ; 在實(shí)模式下直接使用LOADER_LEN等標(biāo)號(hào)就能取出內(nèi)存中的內(nèi)容
            340 ; 在保護(hù)模式下想要取LOADER_LEN等內(nèi)存中的內(nèi)容需要使用基于段基地址的偏移
            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文件中第一個(gè)program segment在內(nèi)存中的物理地址
            352 
            353 pm_PROG_SEG_NUM        equ $ - PMDATA
            354 PROG_SEG_NUM        dw 0 ; 記錄kerne文件中program segment的數(shù)量
            355 
            356 ; 以下為在實(shí)模式下需要打印的字符串
            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 

                 思路比較清晰,首先保存系統(tǒng)參數(shù),這個(gè)步驟是從linux中拿過來的,會(huì)在以后分頁(yè)機(jī)制以及tty中用到;其次是將內(nèi)核移動(dòng)到內(nèi)存地址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移動(dòng)到指定位置;最后是切換進(jìn)入保護(hù)模式,這里需要加載gdt,loader準(zhǔn)備了5個(gè)全局段描述符,其中兩個(gè)是專門為剛剛切換到保護(hù)模式后運(yùn)行所準(zhǔn)備的代碼段和數(shù)據(jù)段描述符。兩外兩個(gè)是從絕對(duì)物理地址0x0開始,大小為4G的代碼段和數(shù)據(jù)段描述符,不過等到進(jìn)入kernel還需要重新設(shè)置段描述符,因此這只是權(quán)易之計(jì)。
            posted on 2011-11-22 20:08 myjfm 閱讀(738) 評(píng)論(0)  編輯 收藏 引用 所屬分類: 操作系統(tǒng)
            久久国产乱子精品免费女| 久久久久久久精品成人热色戒| 精品久久人人爽天天玩人人妻| 国产精品免费久久久久影院| 精品999久久久久久中文字幕| 婷婷五月深深久久精品| 久久久久夜夜夜精品国产| 久久天天躁狠狠躁夜夜2020老熟妇| 一级A毛片免费观看久久精品| 亚洲AV日韩精品久久久久久久 | 国产精品久久久香蕉| 狠狠精品久久久无码中文字幕| 久久成人国产精品二三区| 国产成人久久AV免费| 精品久久久无码中文字幕| 伊人久久大香线蕉综合影院首页 | 日韩精品久久久久久久电影蜜臀| 国产精品99久久久久久人| 久久久久亚洲AV成人网| 亚洲va久久久噜噜噜久久天堂| 久久久久久国产精品无码下载| 欧美精品国产综合久久| 久久人人爽人人人人爽AV | 日韩一区二区久久久久久 | 久久国产乱子伦免费精品| 亚洲精品国产自在久久| 久久精品国产AV一区二区三区| 亚洲午夜久久久久久久久久| 国内精品久久久久伊人av| 国产成人香蕉久久久久| 国内精品久久国产| 国产成人精品免费久久久久| 国产精品九九久久免费视频| 亚洲日本va午夜中文字幕久久| 欧美va久久久噜噜噜久久| 久久99国产亚洲高清观看首页| 久久精品中文字幕有码| 色诱久久久久综合网ywww| 久久久久久毛片免费看| 热re99久久6国产精品免费| 久久精品国产精品亚洲人人 |