https://blog.csdn.net/qq_59848320/article/details/128744163?spm=1001.2101.3001.6650.2&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7EYuanLiJiHua%7EPosition-2-128744163-blog-121042836.235%5Ev29%5Epc_relevant_default_base3&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7EYuanLiJiHua%7EPosition-2-128744163-blog-121042836.235%5Ev29%5Epc_relevant_default_base3&utm_relevant_index=5
前言
保护模式,顾名思义,就是为了让操作系统变的更加安全。相比实模式,在保护模式下也可以充分利用计算机资源。
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进行了修改。最后用远跳转刷新流水线,清空段描述符寄存器。
调用info gdt查看是否建立成功
联系客服