06.真實模式進入保護模式
簡介
上一節我們實現了從核心載入器中載入其它扇區程式碼並執行,但始終工作在真實模式狀態下。記憶體定址方式和8086相同,由16位段暫存器的內容乘以16(10H)當做段基地址,加上16位偏移地址形成20位的實體地址,最大定址空間1MB,最大分段64KB。
保護模式與真實模式相比地址轉換方式差異較大:
真實模式下的地址轉換方式,假設我們在ES中存入0x1000,DI中存入0xFFFF,那麼ES:DI=0x1000*0x10+0xFFFF=0x1FFFF。
保護模式下ES:DI=全域性描述符表中第0x200項描述符(一個描述符8個位元組)給出的段基址+0xFFFF
1、描述符
保護模式下引入描述符來描述各種資料段,所有的描述符均為8個位元組(0-7),由第5個位元組說明描述符的型別,型別不同,描述符的結構也有所不同。若干個描述符集中在一起組成描述符表,而描述符表本身也是一種資料段,也使用描述符進行描述。“地址轉換”由描述符表來完成,描述符表是一張地址轉換函式表。
2、選擇子
選擇子是一個2位元組的數,共16位,最低2位表示RPL,第3位表示查表是利用GDT(全域性描述符表)還是LDT(區域性描述符表)進行,最高13位給出了所需的描述符在描述符表中的地址。(注:13位正好足夠定址8K項)有了以上三個概念之後可以進一步工作了,現在程式的執行與真實模式下完全一樣!各段暫存器仍然給出一個“段值”,只是這個“假段值”到真正的段地址的轉換不再是“左移4位”,而是利用描述符表來完成。
3、80x86系列中引入了兩個新暫存器GDTR和LDTR,其中GDTR用於表示GDT在記憶體中的段地址和段限(就是表的最大偏移上限),因此GDTR是一個48位的暫存器,其中32位表示段地址,16位表示段限。
目標
實現真實模式到保護模式的切換需要使用匯編語言實現,boot.s檔案修改如下
;能用於操作記憶體的暫存器只能是bx、bp、si、di ;0x7c00--0x7dff 這512位元組用於啟動區 ;對記憶體的訪問都必須指定段暫存器,沒有顯示指定時將使用ds作為段暫存器 org 0x7c00 LOAD_ADDR EQU 0x9000 ;核心載入偏移地址 mov ax,0 mov ss,ax mov ds,ax mov es,ax mov bx,LOAD_ADDR mov ch,1 ;柱面號 mov dh,0 ;磁頭號 mov cl,2 ;扇區號 mov ah,0x02 ;0x02表示讀盤操作 mov al,1 ;表示連續讀取扇區數 mov dl,0 ;驅動器號,早期有多個軟碟機,一般只有一個寫死0 int 0x13 ;呼叫BIOS實現磁碟讀取 jc error ;讀盤操作失敗,flag標誌暫存器cf 標誌位被置1 jmp bx ;跳轉到載入的記憶體地址開始執行程式碼 fin: hlt jmp fin putloop: mov al,[si] inc si cmp al,0 je fin mov ah,0x0e ;中斷呼叫引數 mov bx,15 ;字元顏色 int 0x10 ;中斷呼叫號 jmp putloop error: mov si,errMsg jmp putloop errMsg: db 'error' db 0
kernel.s檔案如下
;全域性描述符結構 8位元組
; byte7 byte6 byte5 byte4 byte3 byte2 byte1 byte0
; byte6低四位和 byte1 byte0 表示段偏移上限
; byte7 byte4 byte3 byte2 表示段基址
;定義全域性描述符資料結構
;3 表示有3個引數分別用 %1、%2、%3引用引數
;%1:段基址 %2:段偏移上限 %3:段屬性
%macro GDescriptor 3
dw %2 & 0xffff
dw %1 & 0xffff
db (%1>>16) & 0xff
dw ((%2>>8) & 0x0f00) | (%3 & 0xf0ff)
db (%1>>24) & 0xff
%endmacro
DA_32 EQU 0x4000 ; 32 位段
DA_CODE EQU 0x98 ; 只執行程式碼段屬性值
DA_RW EQU 0x92 ; 可讀寫資料段屬性值
org 0x9000
jmp entry
[SECTION .gdt]
;定義全域性描述符 段基址 段偏移上限 段屬性
LABEL_GDT: GDescriptor 0, 0, 0
LABEL_DESC_CODE: GDescriptor 0, SegCodeLen-1, DA_CODE+DA_32
LABEL_DESC_VIDEO: GDescriptor 0xb8000, 0xffff, DA_RW
;gdt 表大小
GdtLen equ $-LABEL_GDT
;gdt表偏移上限和基地址
GdtPtr dw GdtLen-1
dd 0
;cpu開機進入真實模式時使用的段暫存器 cs,ds,es,ss 和偏移地址組成記憶體地址,記憶體地址=段暫存器 * 16 + 偏移地址
;保護模式中段暫存器儲存的是gdt 描述表中各個描述符的偏移,也叫選擇子
SelectorCode32 EQU LABEL_DESC_CODE-LABEL_GDT
SelectorVideo EQU LABEL_DESC_VIDEO-LABEL_GDT
[SECTION .s16]
[BITS 16]
entry:
mov ax,cs
mov ds,ax
mov es,ax
mov ss,ax
mov sp,0x100
;設定LABEL_DESC_CODE描述符段基址
mov eax,0
mov ax,cs
shl eax,4
add eax,SEG_CODE32
mov word [LABEL_DESC_CODE+2],ax
shr eax,16
mov [LABEL_DESC_CODE+4],al
mov [LABEL_DESC_CODE+7],ah
mov eax,0
mov ax,ds
shl eax,4
add eax,LABEL_GDT
mov dword [GdtPtr+2],eax
;設定GDTR 暫存器
lgdt [GdtPtr]
cli ;關閉可可遮蔽中斷,如鍵盤中斷
in al,0x92
or al,0x02
out 0x92,al
mov eax,cr0
or eax,1
mov cr0,eax
jmp dword SelectorCode32:0
[SECTION .s32]
[BITS 32]
SEG_CODE32:
mov ax,SelectorVideo
;gs 暫存器是80386新增的輔助段暫存器
mov gs,ax
;在螢幕中間顯示字串,螢幕為每行80個字元,共25行。低位元組為ascii字元編碼,高位元組為字元顯示屬性
;可參考 《組合語言》 王爽,螢幕顯示相關章節
;在螢幕11行20列開始顯示字元
mov ax,(80*11+20)
mov ecx,2
mul ecx
mov edi,eax
mov ah,00000010b
mov si,msg
putloop:
mov al,[esi]
cmp al,0
je fin
mov [gs:edi],ax
add edi,2
inc esi
jmp putloop
fin:
hlt
jmp fin
msg:
db 'protected mode',0
SEG_CODE32_END: nop
;32為模式程式碼長度
SegCodeLen EQU SEG_CODE32_END-SEG_CODE32
使用nasm 分別編譯boot.s 、kernel.s 生成boot.bat和kernel.bat
使用C語言軟盤功能模組製作虛擬軟盤,main.c
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include "floppy.h"
int main(int argc,char **argv){
FILE *fp = initFloppy("floppy.img");
if(fp == NULL) {
printf("初始化磁碟失敗");
exit(0);
}
FILE *src = fopen("boot.bat", "r");
if(src == NULL) {
printf("檔案開啟失敗");
exit(0);
}
char buf[512];
memset(buf, 0, 512);
fread(buf, 512, 1, src);
buf[510] = 0x55;
buf[511] = 0xaa;
writeFloppy(0, 0, 1, fp, buf);
fclose(src);
memset(buf, 0, 512);
src = fopen("kernel.bat", "r");
if(src == NULL) {
printf("檔案開啟失敗");
exit(0);
}
fread(buf, 512, 1, src);
writeFloppy(1, 0, 2, fp, buf);
fclose(src);
fclose(fp);
return 1;
}
使用C語言檔案操作boot.bat、kernel.bat,生成floppy.img 虛擬軟盤檔案,VirtualBox虛擬機器載入該軟盤檔案效果如下:
我們成功從真實模式進入保護模式,並在保護模式下實現字元顯示!
補充
在記憶體地址空間中,B8000H~BFFFFH共32KB的空間,為8025彩色字元模式的顯示緩衝區,向這個地址空間寫資料,寫入的內容將立即出現在顯示器上.
在8025彩色字元模式下,顯示器可以顯示25行,每行80個字元,每個字元可以有256種屬性(背景色,閃爍,高亮等組合資訊).
這樣一個字元在顯示緩衝區中就要佔兩個位元組,分別存放字元的ASCII碼和屬性,80*25模式下,一屛內容在顯示緩衝區中佔4000個位元組.
顯示緩衝區分為8頁,每頁4KB,顯示器可以任意顯示任意一頁的內容,一般情況下,顯示第0頁的內容,也就是B8000~B8F9FH中的4000個位元組.
顏色屬性位元組格式:
7 6 5 4 3 2 1 0
BL R G B I R G B
閃爍 背景(rgb) 高亮 前景(rgb)