前面我们完成了boot(mbr)读取Loader的任务,但我们一直都 在实模式下验证的。
本章我们将尝试保护模式下运行下loader(本章工程归档百度网盘,直接取用:http://pan.baidu.com/s/1eSeTwMQ).
首先我们要改造下我们的Loader,最简单的肯定要包含保护模式需要的内容。
实模式下,地址访问实际是按照段寄存器:偏移进行的, 实际的物理地址=段寄存器*16+偏移,比如段寄存器为0x2000, 偏移为0x1000, 实际对就的物理地址:0x2000*16+0x1000=0x21000;
但在进入保护模式下后,段寄存器的含义变化了,它引入了一个新的概念:段选择子,本质上它是一个索引,通过它找到这个段真正的起始地址(未开户分布时),找到后,计算物理地址=段起始地址+偏移地址。
先上代码(http://pan.baidu.com/s/1eSeTwMQ):
loader.asm
%include "base_phy.inc"
section code align=16 vstart=0
jmp start
%include "filesystem.inc"
;;;;;;;;;;;栈区空间预留;;;;;;;
stack_rsv:
times 1024 db 0
stack_top:
;;;;;;;;;;;数据区预留;;;;;;;;
loaderMsg db "loader is running...", 0ah,0dh;
loaderMsgLen equ $-loaderMsg
gdt_tbl: dw 0 ;gdt界限
dd MACRO_GDT_ADDR ;gdt物理地址
;;;;;;;;;;;代码区;;;;;;;;
start:
mov ax, cs
mov ds, ax
mov es, ax
mov ss, ax
mov ax, 0
mov gs, ax
;栈顶赋值
mov sp, stack_top
;显示loader运行信息
mov cx, loaderMsgLen ; CX = 串长度
push cx
mov cx, loaderMsg
push cx
call showString
pop cx
pop cx
jmp next
;showString(字符串地址,字符串长度)
;在当前当前光标处显示字符串信息
;字符串地址: bp+4
;字符串长度: bp+6
showString:
push bp
mov bp, sp
pusha
mov ax, 0x0300
mov bx, 0
int 0x10
mov cx, [bp+6] ; CX = 串长度
mov bp, [bp+4]
mov ax, 0x1301 ; AH = 0x13, AL = 0x1
mov bx, 0x7 ; 页号为0(BH = 0) 黑底白字(BL = 0x7)
int 0x10 ; int 0x10
popa
pop bp
ret
next:
;安装gdt
xor ebx, ebx
mov ebx, [gdt_tbl+2]
;0#号描述符的槽位
mov dword [gs:ebx],0x0
mov dword [gs:ebx+0x04],0x0
;创建1#描述符,保护模式下的代码段描述符
mov dword [gs:ebx+0x08],0x0000ffff ;基地址为0,界限0xFFFFF,DPL=00
mov dword [gs:ebx+0x0c],0x00cf9800 ;4KB粒度,代码段描述符,向上扩展
;创建2#描述符,保护模式下的数据段和堆栈段描述符
mov dword [gs:ebx+0x10],0x0000ffff ;基地址为0,界限0xFFFFF,DPL=00
mov dword [gs:ebx+0x14],0x00cf9200 ;4KB粒度,数据段描述符,向上扩展
;初始化描述符表寄存器GDTR
mov word [gdt_tbl],23 ;描述符表的界限
lgdt [gdt_tbl]
in al,0x92 ;南桥芯片内的端口
or al,0000_0010B
out 0x92,al
;打开A20
mov eax, cs
shl eax,4
add eax, flush
mov dword [cs:Enter_32+2], eax ;修改指令偏移地址
cli ;中断机制尚未工作
mov eax,cr0
or eax,1
mov cr0,eax ;设置PE位
;以下进入保护模式... ...
Enter_32:
jmp dword 0x0008:flush ;16位的描述符选择子:32位偏移
[bits 32]
flush:
mov eax,0x00010 ;加载数据段(4GB)选择子
mov ds,eax
mov es,eax
mov fs,eax
mov gs,eax
mov ss,eax ;加载堆栈段(4GB)选择子
mov esp,0x7000 ;堆栈指针
jmp $
运行效果如下 :
首先先编译并安装 我们的Loader
再运行bochs
怎么样,从效果上来看和之前 的实模式下是一模一样的,可以说是没有任何差别。
但这也正好说明我们的保护模式是没有问题的,运行正常,否则如果修改有问题,后果你也懂的。
这段代码下章节我来解释下,其余改动不多,有些改动甚至非常规(比如96行,这 实际是直接在实模式下修改了指令区代码)