<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    codefans

    導航

    <2025年7月>
    293012345
    6789101112
    13141516171819
    20212223242526
    272829303112
    3456789

    統計

    常用鏈接

    留言簿(2)

    隨筆分類

    隨筆檔案

    文章分類

    文章檔案

    程序設計鏈接

    搜索

    最新評論

    閱讀排行榜

    評論排行榜

    開發教程][原創] 操作系統DIY - 進入保護模式

    預備知識:

    1. 熟悉 i386 CPU寄存器,了解實模式及保護模式模式;
    2. 了解A20門
    3. 文本模式下直接顯存操作

    涉及工具:
      NASM,一個文本編輯器(我用的是ConText + NASM語法高亮),QEMU/VMWARE虛擬機

    前言:

      近期確實很忙,論壇里有一位朋友寫的代碼進入不了保護模式;我最初也是對保護模式相當敬畏,因為32位比16位要“復雜”的多;當時一直不敢下手,偶爾的嘗試有如蜻蜓點水,但最終以失敗告終。在學校的圖書館里幾乎找不到386保護模式匯編的資料,更不用說CPU相關的書了;不知道看了多少可憐的教材后,終于湊出了一點起眼的代碼,不過還是失敗了。我最終是通過一個Bona Fide 的實例教程解決了問題:實例程序在我的開發過程中起到了很重要的作用。

      由于時間原因,這篇文章將主要以代碼來說明,因為我的確沒太多的時間再去介紹“實模式”,“保護模式”,GDT,IDT,A20等等等等相關的名詞、概念及規范;這些東西在我的網站里已經收羅了:http://www.xemean.net/resource/ ,其中有中文也有英文的,有的甚至是圖文并茂,網絡上也有不少的例子,但在這里強烈建議的一本電子教程是:《80X86保護模式教程》 ,這本書詳細地介紹了如何對 80X86 CPU進行編程,包括進入保護模式,保護模式的中斷,多任務等等。另外,值得一提的是:由楊季文等人編著的,清華大學出版社出版的《80x86匯編語言程序設計教程》也是一本不錯的書。

      此文適合于有一定基礎,但又不能實現保護模式切換的朋友。

      本文將以我上次寫的“啟動你的計算機”的代碼為基礎,演示進入保護模式,但程序還是在引導區內工作。

      姑且不理會保護模式下“復雜”的內存管理,多任務,中斷,實際上進入保護只要:
    mov  eax,cr0  ; 控制寄存器CR0 -> EAX
    or  eax,1        ; 最低位置1,即PE位
    mov  cr0,eax  ; 寫回CR0

    I386兼容CPU使用CR0這個寄存器來“控制”或者說決定CPU的工作狀態,命名為“控制寄存器中”,其中CR0的最低位叫PE位,中文翻譯應該是“保護模式允許”位,如果對CR0的PE位置1,則CPU就工作在保護模式下。不幸的是,我們并不能直接對CR0進行操作,但是卻可以通過通用寄存器對其修改,上面便是開啟保護模式大門的實例。當然,只有上面的代碼你可能永遠也進不了保護模式。

      保護模式與實模式有一個區別在于,段寄存器不再保存實際的內存地址,CPU已經有32位尋址的能力,也就是能訪問4G的內存,似乎用32位的EIP就可以訪問4G了,但Intel并沒有想得那么簡單,段寄存器在內存管理方面還有很大的作用。另外,之所以叫保護模式,是因為CPU還能不同應用層的代碼進行保護,這在16位實模式是做不到的。因此引入了GDT,及描述符的概念。(這就得請各位看官參看一些資料了)
      CPU中有一個高速的寄存器用來保存GDT表在內存中的位置以及GDT表的大小:GDT的大小用16位來表示,GDT的物理地址用32位來表示(以保證GDT能在4G內存的任意位置),因此GDT高速寄存器(GDTR)占48位,已經不能用一個32位的寄存器來表示了,因此要在內存中表示出GDTR內容,書上說這叫“偽描述符”,GDTR由下面的指令裝載:
    lgdt [__GDTR]

    其中__GDTR是GDT偽描述符的地址,一口氣,我們作如下數據定義:

    ALIGN 4
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    ; GDT 偽描述符
    ; 參考保護模式相關文檔以獲得關于GDT偽描述
    ; 符更詳細的資料
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    __GDTR:
           dw GDT_END - GDT-1      ; GDT表的長度,由編譯器計算
           dd GDT                           ; GDT物理地址,由編譯器計算
    ;<- END OF __GDTR
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    ; GDT entry
    ; 參考保護模式相關文檔以獲得關于GDT的更
    ; 詳細的資料
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    ALIGN 8 ; 對齊,以保護CPU訪問GDT的速度
    GDT:
    ; 第一個GDT作為保留項,以0填充
    ; reserved GDT
        dd 0
        dd 0

    osCodeSel equ   $-GDT ; 內核用的代碼段選擇子
    oscode:
              dw    0xffff
              dw    0
              db    0
              db    10011010b ; 0x9A ,可讀/可執行 代碼段
              db    11011111b ;
              db    0

    osDataSel equ   $-GDT ; 內核用的數據段選擇子
    osdata:
              dw    0xffff
              dw    0
              db    0
              db    10010010b ; 0x92 ,可讀/寫 數據段
              db    11011111b ;
              db    0
    GDT_END:  ;<- END OF GDT

    完成如上的定義之后,就可以著手進入保護模式了,過程大致為:

    1. 禁止所有中斷
    2. 打開A20門
    3. 加載GDTR
    4. 置PE位
    5. 初始化保護模式下的寄存器
    6. 一個遠跳轉到32位代碼以清除當前(實模式)的CS及EIP

    如果跳轉成功,則CPU就可以工作在保護模式下。保護模式并不像實模式下有很多BIOS中斷可用,這就意味著我們必須自己寫鍵盤、顯卡等等驅動,計算機的幾乎所有資源都由內核來管理,當然,也由你來實現各種設備的驅動。

    為了顯示我們的程序已經成功地工作在保護模式下,我們必須在32位模式時在屏幕上寫點什么東西,直接寫顯存吧!演示程序對顯存進行操作,結果是屏幕的第三行第1列顯示了一個洋紅色的字母P。

    源碼編譯:nasmw -f bin boot.asm -o boot.bin

    用WinImage寫入軟盤鏡像,然后用Qemu或VMware啟動。注:不知道什么原因,這段代碼并不能在Bochs下工作。

    拍照以示留念:

    Image1.jpg

    程序源碼如下:

     

    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    ; boot.asm
    ; A demo to show how bootsect works
    ; Last modified:2005-4-3 10:57:45 
    ; Copyright (c) 2005,E-mean X.
    ;
    ; This program is released under GPL,See document for details
    ; You can use this code anywhere you want in condition keep autor's info
    ; original
    ;
    ; Author: E-mean X.
    ; Contact: xemean@sina.com
    ; Website: http://www.xemean.net/
    ; April,02,2005
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    ;**************************************************************************
    ; 16位代碼
    bits 16         ; 偽指令,告訴編譯器這是 16位代碼
    org 0x7C00      ; 偽指令,告訴編譯器這段代碼由0x0:0x7C00開始
    ;===========================================================================
    ; 程序執行的第一條指令必須是跳轉(如果你想使用FAT12這類文件系統的磁盤)
    ; 必須占用3字節
    ;===========================================================================
    jmp SHORT main ; 2 bits,跳轉到主程序執行
    nop            ; 1 bit
    ;===========================================================================
    ; FAT12 文件系統頭,從NYAOS 借過來的,可以參考相關的文檔以獲得更多細節
    ; 這個塊會讓 Winimage 認出編譯后的二進制文件為有效的引導文件
    ; 如果不使用這個塊,Winimage將不會將其作為引導程序處理
    ; 但我們可以借助其它方法和工具處理,比如DEBUG
    ;===========================================================================
    bsOEM       db "ExOS0.02"               ; OEM String,任意你喜歡的8字節ASCII碼
    bsSectSize  dw 512                      ; Bytes per sector
    bsClustSize db 1                        ; Sectors per cluster
    bsRessect   dw 1                        ; # of reserved sectors
    bsFatCnt    db 2                        ; # of fat copies
    bsRootSize  dw 224                      ; size of root directory
    bsTotalSect dw 2880                     ; total # of sectors if < 32 meg
    bsMedia     db 0xF0                     ; Media Descriptor
    bsFatSize   dw 9                        ; Size of each FAT
    bsTrackSect dw 18                       ; Sectors per track
    bsHeadCnt   dw 2                        ; number of read-write heads
    bsHidenSect dd 0                        ; number of hidden sectors
    bsHugeSect  dd 0                        ; if bsTotalSect is 0 this value is
                                            ; the number of sectors
    bsBootDrv   db 0                        ; holds drive that the bs came from
    bsReserv    db 0                        ; not used for anything
    bsBootSign  db 29h                      ; boot signature 29h
    bsVolID     dd 0                        ; Disk volume ID also used for temp
                                            ; sector # / # sectors to load
    bsVoLabel   db "NO NAME    "            ; Volume Label
    bsFSType    db "FAT12   "               ; File System type <- FAT 12文件系統
    ;===========================================================================
    ; Main start here
    ;===========================================================================
    main:
         cli  ; 關閉可屏蔽中斷,以備我們接下來初始化寄存器的工作
         mov  ax,cs ; 將代碼段傳給ax,實模式下,代碼段與數據段沒什么分別
         mov  ds,ax ; 數據段寄存器,實際上都是0
         mov  es,ax ; 附加段
         mov  ax,ss ; 堆棧段
         mov  sp,0x7C00-1   ; 堆棧指針,指向0x7BFF
         sti  ; 基本工作已經完成,開放中斷

         ;
         mov  ax,0x0003
         int  0x10

         mov  si,msgHello ; 使 si指向 "Hello World!"字符串
         call printStr    ; 調用顯示子程序
         
         mov  si,fixLine  ; 回車換行
         call printStr
         
         mov  si,msgMore  ; 顯示swithing to protect mode
         call printStr
         
         ; 打開A20門,幾乎所有想進入保護模式的程序都通過A20門來實現
         ; 當然,也有其它辦法,比如 int 0x15,不過并不推薦,因為可能不兼容
         ; 參考a20門以獲得更多細節

          cli
          call kbdwait
          mov al,0xD1
          out 0x64,al
          call kbdwait
          mov al,0xDF
          out 0x60,al
          call kbdwait

          lgdt [__GDTR]           ; 加載偽描述符到GDT高速寄存器
          mov  eax,cr0            ; 將控制寄存器CR0的值放到eax中
          or   eax,1              ; 置PE位
          mov  cr0,eax            ; 寫回CR0,這時候PE已經被置位了
                                  ; 進入保護模式的工作完成了一大半:
                                  ; 另一小半是:我們當前的寄存器還在
                                  ; 16位模式下工作

          mov  eax,osDataSel      ; 初始化所有段寄存器
          mov  ds,ax
          mov  es,ax
          mov  ss,ax
          mov  fs,ax
          mov  gs,ax
          mov  esp,0x7C00-1       ; 新的堆棧
          jmp  osCodeSel:code32   ; 一個遠跳轉以"沖"掉當前實模式的代碼段CS及指令指針EIP
                                  ; 以使其使用保護模式的CS(注意:是選擇子),及EIP

    ;------- END OF MAIN ----------------

    ;===========================================================================
    ; printStr
    ; sub function for print a string to screen by INT 10H
    ; 入口:es:si = 指向目標字符串
    ; 返回:無
    ;===========================================================================
    printStr:
         push si  ; 保護寄存器
         push ax
         push bx

              cld    ; 清除進位標志位,這個標志位會影響 si 的遞增方向
              mov  ah,0x0E    ; int 0x10 子功能號,顯示字符,參看相關資料以獲得細節
              mov  bx,0x0007  ; 頁號0,字符前景色 7,淺灰色,試著改變這個數值
                              ; 會給你的文字增添色彩
         .nextChar:
              lodsb           ; [si] -> al,取一個字節碼
              or  al,al       ; 如果取得的字節是0,則表示字符串結束
              jz  .OK         ; 退出
              int 0x10        ; 調用BIOS int 10h 中斷
              jmp .nextChar   ; 繼續下一個字符,直到遇到0
         .OK:
         pop  bx ; 恢復寄存器
         pop  ax
         pop  si
         ret     ; 返回調用程序
    ;------- END OF printStr --------------
    ;=========================================================================
    ; 等待鍵盤緩沖區清空
    kbdw0:
          jmp short $+2
          in al,0x60
    kbdwait:
          jmp short $+2
          in al,0x64
          test al,1
          jnz kbdw0
          test al,2
          jnz kbdwait
          ret
    ;------ END OF kbdwait -----------------
    ; data area

    msgHello db 'Hello World!',0          ; 以物理 0結束
    msgMore  db 'Swithing to protect mode ...',0
    fixLine  db 13,10,0   ; 回車,換行的ASCII碼

    ALIGN 4
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    ; GDT 偽描述符
    ; 參考保護模式相關文檔以獲得關于GDT偽描述
    ; 符更詳細的資料
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    __GDTR:
           dw GDT_END - GDT-1               ; GDT表長度
           dd GDT                           ; GDT物理地址
    ;<- END OF __GDTR
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    ; GDT entry
    ; 參考保護模式相關文檔以獲得關于GDT的更
    ; 詳細的資料
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    ALIGN 8 ; 對齊
    GDT:
    ; 第一個GDT作為保留項,以0填充
    ; reserved GDT
        dd 0
        dd 0

    osCodeSel equ   $-GDT ; 內核用的代碼段選擇子
    oscode:
              dw    0xffff
              dw    0
              db    0
              db    10011010b ; 0x9A ,可讀/可執行 代碼段
              db    11011111b ;
              db    0

    osDataSel equ   $-GDT ; 內核用的數據段選擇子
    osdata:
              dw    0xffff
              dw    0
              db    0
              db    10010010b ; 0x92 ,可讀/寫 數據段
              db    11011111b ;
              db    0
    GDT_END:  ;<- END OF GDT
    ;**************************************************************************
    ; 32位代碼
    bits 32   ; 告訴編譯器這段代碼在32位模式下工作
    code32:
    ; take a break
         nop ; 我不知道這三個NOP會不會起作用
         nop
         nop
         ; 接下來讓我們直接向顯存寫數據 
         mov   [0xB8000+80*2*2],BYTE 'P'  ; 在屏幕的第三行第一列寫字母'P'
         mov   [0xB8000+80*2*2+1],BYTE 13 ; 字母P的顏色為洋紅色
         jmp   $
         

    bits 16
    ; 引導程序必須為512字節,不用的地方以0填充
      times 510-($-$$) db 0            ; $表示程序當前位置,$$表示程序開始位置,由編譯器自動計算

    BOOT_SIGN     DW 0xAA55            ; 最后兩個字節為引導標志55AA

    posted on 2005-09-14 00:23 春雷的博客 閱讀(539) 評論(0)  編輯  收藏


    只有注冊用戶登錄后才能發表評論。


    網站導航:
     
    主站蜘蛛池模板: 爽爽日本在线视频免费| 国产精品99精品久久免费| 免费A级毛片无码无遮挡内射| 国产成人A人亚洲精品无码| 一个人免费播放在线视频看片| 亚洲国产精品不卡毛片a在线| 日本高清免费中文在线看| 免费人成在线观看视频播放| 添bbb免费观看高清视频| 亚洲人成人无码网www国产| xxxxx做受大片在线观看免费| 亚洲精品乱码久久久久久按摩 | 香港特级三A毛片免费观看| 在线视频免费国产成人| 特级aa**毛片免费观看| 亚洲日韩中文字幕在线播放| 国产一级片免费看| 4444亚洲国产成人精品| 色窝窝免费一区二区三区| 久久无码av亚洲精品色午夜| 亚洲国产激情一区二区三区| 国产午夜精品免费一区二区三区| 亚洲视频欧洲视频| 精品久久洲久久久久护士免费| 黄色网址大全免费| 久久久久久a亚洲欧洲AV| 免费人成网站在线观看10分钟| 亚洲AV色欲色欲WWW| 亚洲真人日本在线| 久久久久久毛片免费播放| 亚洲午夜理论片在线观看| 久久久久亚洲av毛片大| 1000部拍拍拍18免费网站| 在线观看亚洲电影| 亚洲av色福利天堂| 免费被黄网站在观看| 精品人妻系列无码人妻免费视频 | 久久久久亚洲?V成人无码| 精品无码人妻一区二区免费蜜桃| 亚洲色偷偷综合亚洲AV伊人蜜桃| 国产乱辈通伦影片在线播放亚洲|