操作系统还原真相 第五章 开启保护模式

一、相关基础
1、如何定位一个内存地址:
  • 实模式下:段基址:偏移地址,其中段寄存器中直接存储的是段基址
  • 保护模式下:段基址:偏移地址,其中段寄存器中存储的并不是段基址,而是存储的选择子,其中选择子的作用是为了在段描述符表GDT中定位一个段描述符,其中段描述符里面存储了段基址。
2、什么是段描述符表和段描述符以及选择子呢
  • 段描述符表是一个连续的内存空间,里面存储了段描述符,每个段描述符由8个字节组成也就是64位,段描述符中存储了相关的信息,其中包含了段基址。

在这里插入图片描述

  • 选择子里面存储了段描述符在段描述符表中的位置,用来索引段描述符。

在这里插入图片描述

3、如何用选择子来索引段描述符呢
  • GDTR寄存器的结构:

在这里插入图片描述

  • GDTR寄存器是用来存储GDT的首地址的,而选择子中高13位对应了GDT中第几个段描述符,GDT中的索引是从0开始的,第0个描述符又不可用,每个描述符又占用8个字节。
  • GDT中某个段描述符的首地址:GDTR高32位+选择子中高13位*8;
二、实验记录
1、实验目的

本次的实验目的主要是在内存中构建3个段描述符(代码段,数据段,显存段)以及创建相应的选择子,最后开启保护模式。

2、实验步骤
1、构建段描述符
  • 为了更方便了构建段描述符,我们把每个字段以及每个字段的不同情形分别宏定义出来放到boot.inc头文件当中
;一一一一一一- gdt 描述符属性 一一一一一一 
DESC_G_4K equ 1000_0000_0000_0000_0000_0000b   ;G位为第23位,置1代
												;表
;段界限为单位4k
DESC_D_32 equ 1_00_0000_0000_0000_0000_0000b ;D/B 宇段,第22位
							;对代码段来说是D位,置1表示指令中的有效地址及
							;操作数是32位,指令有效地址用EIP寄存器。

DESC_L equ 0_0000_0000_0000_0000_0000_0000b ; 64位代码标记,我们在32位CPU下编程,
;标记为0便可
DESC_AVL equ 0_0000_0000_0000_0000_0000b ;CPU不用此位,暂置为
DESC_LIMIT_CODE2 equ 1111_0000_0000_0000_0000b  ;段界限16~19位
				;全设为1,它在下面代码中会与段界限的0~15位拼成0xFFFF,
				;0xFFFF*4k等于4G,段基址设为0,采用平坦模型

DESC_LIMIT_DATA2 equ DESC_LIMIT_CODE2 ;
DESC_LIMIT_VIDEO2 equ 0000_0000_0000_0000_0000b ;
DESC_P equ 1_000_0000_0000_0000b   ;第15位,表示段存在
DESC_DPL_0 equ 00_0_0000_0000_0000b ;DPL在13~14位 0为最高特权级
DESC_DPL_1 equ 01_0_0000_0000_0000b 
DESC_DPL_2 equ 10_0_0000_0000_0000b 
DESC_DPL_3 equ 11_0_0000_0000_0000b 
DESC_S_CODE equ 1_0000_0000_0000b ; S为0时表示系统段, S为1时表示非系统段。
DESC_S_DATA equ DESC_S_CODE 
DESC_S_sys equ 0_0000_0000_0000b 
DESC_TYPE_CODE equ 1000_0000_0000b ;x=1,c=0, r=0,a=0 ,即代码段是可执行的,非一致
;性,不可读,己访问位a清0 配合S使用
DESC_TYPE_DATA equ 0010_0000_0000b ;
;x=0,e=0,w=1,a=0 数据段是不可执行的,向上扩展的,可写,己访问位a清0。
  • 根据段描述符的格式和每个字段的作用构建三个段描述符的高32位
DESC_CODE_HIGH4 equ (0x00 << 24) + DESC_G_4K + DESC_D_32 + \
DESC_L + DESC_AVL + DESC_LIMIT_CODE2 + \
DESC_P + DESC_DPL_0 + DESC_S_CODE +\
DESC_TYPE_CODE + 0x00 

DESC_DATA_HIGH4 equ (0x00 << 24) + DESC_G_4K + DESC_D_32 +\
DESC_L + DESC_AVL + DESC_LIMIT_DATA2 + \
DESC_P + DESC_DPL_0 + DESC_S_DATA + \
DESC_TYPE_DATA + 0x00

DESC_VIDEO_HIGH4 equ (0x00 << 24) + DESC_G_4K + DESC_D_32 +\
DESC_L + DESC_AVL + DESC_LIMIT_CODE2 + DESC_P + \
DESC_DPL_0 + DESC_S_DATA + DESC_TYPE_DATA + 0x0B   ;注意书上这里写的是0x00,写错了
;显存起始地址应该是0xB8000
  • 创建3个段描述符

GDT中第0个段描述符不可用,把位置留出来即可

;构建 gdt 及其内部的描述符
GDT_BASE: dd 0x00000000       ;第0个段描述符不可用
		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  故段界限为7
			dd  DESC_VIDEO_HIGH4  ;此时dpl为0
			 
GDT_SIZE equ $ - GDT_BASE ; 先是通过地址差来获得 GDT的大小,进而用 GDT大小减1得到了段界限
GDT_LIMIT equ GDT_SIZE - 1   ;用于构建GDTR的段界限
times 60 dq 0 ;此处预留 60 个描述符的空位
  • 设置GDTR寄存器字段,后续加载GDTR直接加载该地址即可。
;以下是 gdt 的指针即GDTR,前2字节是gdt界限,后4字节是gdt起始地址 后面代码使用lgdt指令时会用上
gdt_ptr dw GDT_LIMIT 
dd GDT_BASE 
2、创建选择子
  • 创建选择子每个字段处于不同情形的宏定义,方便创建选择子
;一一一一一一 选择子属性一一一一一一一
RPL0 equ 00b 
RPL1 equ 01b 
RPL2 equ 10b 
RPL3 equ 11b 
TI_GDT equ 000b 
TI_LDT equ 100b
  • 创建3个段描述符的选择子

GDT中第0个段描述符不可用,但是要把位置留出来,因此代码段为第1个段描述符索引为1,数据段为第2个为2,显存段为第3个为3。

;以下是构建代码段、数据段、显存段选择子
SELECTOR_CODE equ (0x0001 << 3) + TI_GDT + RPL0 
;相当于[(CODE_DESC - GDT_BASE) /8 ]<<3+ TI_GDT + RPL0 
;内存地址的编号是一个存储单元8比特,这里CODE_DESC - GDT_BASE应该等于8
;书里的备注应该写掉了"<<3"
SELECTOR_DATA equ (0x0002<< 3) + TI_GDT + RPL0 
SELECTOR_VIDEO equ (0x0003 << 3) + TI_GDT + RPL0 
3、进入保护模式
  • 打开A20
in al,0x92
or al, 0000_0010B 
out 0x92,al 
  • 加载GDT(也就是设置好gdtr,gdtr记录着gdt的起始地址)
lgdt [gdt_ptr ] 
  • cr0位置置1
mov eax, cr0 
or eax, 0x00000001 
mov cr0, eax 
3、实验代码
1、修改mbr.s中的代码

loader.bin 超过了 512 字节,读入磁盘扇区的数量不在是1啦。

mov cx, 1; 待读入内存的扇区数
call rd_disk_m_16;

修改后代码
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
mov cx, 4; 改成待读入4个扇区, loader.bin 超过了 512 字节
call rd_disk_m_16;
2、loader.s

构建段描述符表GDT以及选择子,开启保护模式(打开A20,设置cr0为1,加载GDT),通过选择子和段描述符的方式访问显存,打印字符’P’,悬停在此处。

;------------------------
%include "boot.inc" 
section loader vstart=LOADER_BASE_ADDR 
LOADER_STACK_TOP equ LOADER_BASE_ADDR  ;相同内存地址,地址之下便是栈
jmp loader_start 

;构建 gdt 及其内部的描述符
GDT_BASE: dd 0x00000000       ;第0个段描述符不可用
		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  故段界限为7
			dd  DESC_VIDEO_HIGH4  ;此时dpl为0
			 
GDT_SIZE equ $ - GDT_BASE ; 先是通过地址差来获得 GDT的大小,进而用 GDT大小减1得到了段界限
GDT_LIMIT equ GDT_SIZE - 1   ;用于构建GDTR的段界限
times 60 dq 0 ;此处预留 60 个描述符的空位

;以下是构建代码段、数据段、显存段选择子
SELECTOR_CODE equ (0x0001 << 3) + TI_GDT + RPL0 
;相当于[(CODE_DESC - GDT_BASE) /8 ]<<3+ TI_GDT + RPL0 
;内存地址的编号是一个存储单元8比特,这里CODE_DESC - GDT_BASE应该等于8
;书里的备注应该写掉了"<<3"
SELECTOR_DATA equ (0x0002<< 3) + TI_GDT + RPL0 
SELECTOR_VIDEO equ (0x0003 << 3) + TI_GDT + RPL0 

;以下是 gdt 的指针即GDTR,前2字节是gdt界限,后4字节是gdt起始地址 后面代码使用lgdt指令时会用上
gdt_ptr dw GDT_LIMIT 
dd GDT_BASE 

 loadermsg db '2 loader in real.' 

 loader_start: 
 
;------------------------------------------------------------
; INT 0x10 功能号: 0x13 功能描述:打印字符串
;------------------------------------------------------------
;输入:
;AH 子功能号=13H
;BH =页码
;BL =属性(若 AL=OOH OlH)
;CX =字符串长度
; (DH DL )=坐标{行、列)
;ES:BP=字符串地址
;AL=显示输出方式
; 一一字符串中只含显示字符,其显示属性在 BL
;显示后,光标位置不变
; 一一字符串中只含显示字符,其显示属性在 BL
;显示后,光标位置改变
; 一一字符事中含显示字符和显示属性。显示后,光标位置不变
; 一一字符串中含显示字符和显示属性。显示后,光标位置改变
;无返回值
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  ;  dh=0x18 十进制为24,代表行数;dl=0x00 表示列数。使用显存的文本模式下,一共25行,所以2 loader in real 会出现在屏幕最后一行
int 0x10 ; 10h号中断 由于AH=0x13,所以该BIOS中断后会执行打印字符串的中断处理程序。
 
; 一一一一一一一一一一 准备进入保护模式 一一一一一一一一一一一一一一-
;1 打开 A20
;2 加载 gdt
;3 将cr0 的 pe 位置1 

;一一一一一一一-打开 A20 一一一一一
in al,0x92
or al, 0000_0010B 
out 0x92,al 
 
;一一一一一一一一加载 GDT (也就是设置好gdtr,gdtr记录着gdt的起始地址)一一一一一一一-
lgdt [gdt_ptr ] 

;一一一一一一一一 cr0位置1 一一一一一一一-
mov eax, cr0 
or eax, 0x00000001 
mov cr0, eax 

jmp dword SELECTOR_CODE:p_mode_start  ;刷新流水线

[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:0xA0], 'P' 	;mov byte [gs:160],’ P ’ ;一行80个字符,每个字符的显示要占俩个字节也就是160
 
	jmp $
3、boot.inc

一些宏定义主要在这里面,更方面、灵活的写代码。

;一一一一一一loader和 kernel

LOADER_BASE_ADDR equ 0x900 
LOADER_START_SECTOR equ  0x2 
 
;一一一一一一- gdt 描述符属性 一一一一一一 
DESC_G_4K equ 1000_0000_0000_0000_0000_0000b   ;G位为第23位,置1代
												;表
;段界限为单位4k
DESC_D_32 equ 1_00_0000_0000_0000_0000_0000b ;D/B 宇段,第22位
							;对代码段来说是D位,置1表示指令中的有效地址及
							;操作数是32位,指令有效地址用EIP寄存器。

DESC_L equ 0_0000_0000_0000_0000_0000_0000b ; 64位代码标记,我们在32位CPU下编程,
;标记为0便可
DESC_AVL equ 0_0000_0000_0000_0000_0000b ;CPU不用此位,暂置为
DESC_LIMIT_CODE2 equ 1111_0000_0000_0000_0000b  ;段界限16~19位
				;全设为1,它在下面代码中会与段界限的0~15位拼成0xFFFF,
				;0xFFFF*4k等于4G,段基址设为0,采用平坦模型

DESC_LIMIT_DATA2 equ DESC_LIMIT_CODE2 ;
DESC_LIMIT_VIDEO2 equ 0000_0000_0000_0000_0000b ;
DESC_P equ 1_000_0000_0000_0000b   ;第15位,表示段存在
DESC_DPL_0 equ 00_0_0000_0000_0000b ;DPL在13~14位 0为最高特权级
DESC_DPL_1 equ 01_0_0000_0000_0000b 
DESC_DPL_2 equ 10_0_0000_0000_0000b 
DESC_DPL_3 equ 11_0_0000_0000_0000b 
DESC_S_CODE equ 1_0000_0000_0000b ; S为0时表示系统段, S为1时表示非系统段。
DESC_S_DATA equ DESC_S_CODE 
DESC_S_sys equ 0_0000_0000_0000b 
DESC_TYPE_CODE equ 1000_0000_0000b ;x=1,c=0, r=0,a=0 ,即代码段是可执行的,非一致
;性,不可读,己访问位a清0 配合S使用
DESC_TYPE_DATA equ 0010_0000_0000b ;
;x=0,e=0,w=1,a=0 数据段是不可执行的,向上扩展的,可写,己访问位a清0。

DESC_CODE_HIGH4 equ (0x00 << 24) + DESC_G_4K + DESC_D_32 + \
DESC_L + DESC_AVL + DESC_LIMIT_CODE2 + \
DESC_P + DESC_DPL_0 + DESC_S_CODE +\
DESC_TYPE_CODE + 0x00 

DESC_DATA_HIGH4 equ (0x00 << 24) + DESC_G_4K + DESC_D_32 +\
DESC_L + DESC_AVL + DESC_LIMIT_DATA2 + \
DESC_P + DESC_DPL_0 + DESC_S_DATA + \
DESC_TYPE_DATA + 0x00

DESC_VIDEO_HIGH4 equ (0x00 << 24) + DESC_G_4K + DESC_D_32 +\
DESC_L + DESC_AVL + DESC_LIMIT_CODE2 + DESC_P + \
DESC_DPL_0 + DESC_S_DATA + DESC_TYPE_DATA + 0x0B   ;注意书上这里写的是0x00,写错了
;显存起始地址应该是0xB8000

 
;一一一一一一 选择子属性一一一一一一一
RPL0 equ 00b 
RPL1 equ 01b 
RPL2 equ 10b 
RPL3 equ 11b 
TI_GDT equ 000b 
TI_LDT equ 100b
4、实验结果
1、编译loader.s
nasm -o loader.bin loader.s
2、编译mbr.s
nasm -o mbr.bin mbr.s
3、将mbr.bin刻入到第0扇区
dd if=/home/llh/bochs/mbr.bin of=/home/llh/bochs/hd60M.img bs=512 count=1 seek=0 conv=notrunc
4、将loader.bin刻入到第2和3扇区
dd if=/home/llh/bochs/loader.bin of=/home/llh/bochs/hd60M.img bs=512 count=2 seek=2 conv=notrunc
5、模拟bochs
bin/bochs -f bochsrc.disk
6、效果展示

在这里插入图片描述

在这里插入图片描述

  • 35
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值