前言
我们开始实现了loader,从mbr跳转到loader。并执行的loader的代码。在屏幕上打印了"2 LOADER"。
1.进入保护模式
保护模式,顾名思义,就是为了让操作系统变的更加安全。相比实模式,在保护模式下也可以充分利用计算机资源。
1.打开A20地址线
IBM为了兼容以前的cpu,必须兼容实模式。在兼容实模式下情况,又要有保护模式。为了解决这个问题,IBM在键盘控制器上的一些输出线来控制第21根地址线。
-
如果A20Gate被打开,当访问到0x100000~0x10FFFEF之间的地址时,CPU将真正访问这块内存地址。
-
如果A20Gate被打开,当访问到0x100000~0x10FFFEF之间的地址时,CPU采用地址回绕
in al,0x92 or al,0000_0010B out 0x92,al
打开A20地址只有三行代码
2.打开CR0寄存器的PE位
mov eax, cr0
or eax,0x00000001
mov cr0, eax
代码比较简单,就只有三行。
3.加载全局描述符表GDT
这个是进入保护模式最麻烦的部分。前面说道,保护模式对于实模式来说更加安全。那么如何来保证一些关键的内存不能被用户随便访问了。那这个时候就需要GDT了。将内存映射到这个表里面。并对每一块内存进行说明。保护模式也不是直接影cs:ip来执行代码。而是有个新的玩意,叫选择子,顾名思义,选择子就是在GDT里面选择内存进行访问。这样有个好处,就是比直接用cs:ip进行访问更加安全。
%include "boot.inc"
section loader vstart=LOADER_BASE_ADDR
LOADER_STACK_TOP equ LOADER_BASE_ADDR
jmp loader_start ; 此处的物理地址是:
;构建gdt及其内部的描述符
GDT_BASE: dd 0x00000000
dd 0x00000000
CODE_DESC: dd 0x0000FFFF
dd DESC_CODE_HIGH4
DATA_STACK_DESC: dd 0x0000FFFF
dd DESC_DATA_HIGH4
VIDEO_DESC: dd 0x80000007 ;limit=(0xbffff-0xb8000)/4k=0x7
dd DESC_VIDEO_HIGH4 ; 此时dpl已改为0
GDT_SIZE equ $ - GDT_BASE
GDT_LIMIT equ GDT_SIZE - 1
times 60 dq 0 ; 此处预留60个描述符的slot
SELECTOR_CODE equ (0x0001<<3) + TI_GDT + RPL0 ; 相当于(CODE_DESC - GDT_BASE)/8 + TI_GDT + RPL0
SELECTOR_DATA equ (0x0002<<3) + TI_GDT + RPL0 ; 同上
SELECTOR_VIDEO equ (0x0003<<3) + TI_GDT + RPL0 ; 同上
;以下是定义gdt的指针,前2字节是gdt界限,后4字节是gdt起始地址
gdt_ptr dw GDT_LIMIT
dd GDT_BASE
loadermsg db '2 loader in real.'
loader_start:
;------------------------------------------------------------
;INT 0x10 功能号:0x13 功能描述:打印字符串
;------------------------------------------------------------
;输入:
;AH 子功能号=13H
;BH = 页码
;BL = 属性(若AL=00H或01H)
;CX=字符串长度
;(DH、DL)=坐标(行、列)
;ES:BP=字符串地址
;AL=显示输出方式
; 0——字符串中只含显示字符,其显示属性在BL中。显示后,光标位置不变
; 1——字符串中只含显示字符,其显示属性在BL中。显示后,光标位置改变
; 2——字符串中含显示字符和显示属性。显示后,光标位置不变
; 3——字符串中含显示字符和显示属性。显示后,光标位置改变
;无返回值
mov sp, LOADER_BASE_ADDR
mov bp, loadermsg ; ES:BP = 字符串地址
mov cx, 17 ; CX = 字符串长度
mov ax, 0x1301 ; AH = 13, AL = 01h
mov bx, 0x001f ; 页号为0(BH = 0) 蓝底粉红字(BL = 1fh)
mov dx, 0x1800 ;
int 0x10 ; 10h 号中断
;---------------------------------------- 准备进入保护模式 ------------------------------------------
;1 打开A20
;2 加载gdt
;3 将cr0的pe位置1
;----------------- 打开A20 ----------------
in al,0x92
or al,0000_0010B
out 0x92,al
;----------------- 加载GDT ----------------
lgdt [gdt_ptr]
;----------------- cr0第0位置1 ----------------
mov eax, cr0
or eax, 0x00000001
mov cr0, eax
;jmp dword SELECTOR_CODE:p_mode_start ; 刷新流水线,避免分支预测的影响,这种cpu优化策略,最怕jmp跳转,
jmp SELECTOR_CODE:p_mode_start ; 刷新流水线,避免分支预测的影响,这种cpu优化策略,最怕jmp跳转,
; 这将导致之前做的预测失效,从而起到了刷新的作用。
[bits 32]
p_mode_start:
mov ax, SELECTOR_DATA
mov ds, ax
mov es, ax
mov ss, ax
mov esp,LOADER_STACK_TOP
mov ax, SELECTOR_VIDEO
mov gs, ax
mov byte [gs:160], 'P'
jmp $
这里的loader.s进行了修改。最后用远跳转刷新流水线,清空段描述符寄存器。
2.编译运行
调用info gdt查看是否建立成功