文章目录

  • 实模式
  • 保护模式
    • 实模式的五宗罪
    • 寄存器扩展
    • 内存访问
    • 段描述符
    • 全局描述符表
    • 段选择子
  • 进入保护模式
    • 打开A20地址线并加载全局描述符表GDT
    • 打开保护模式开关cr0
    • GO GO GO
  • 参考文献

写在前面:自制操作系统Gos 第二章第五篇:主要内容是如何进入保护模式

实模式

我们既然这篇主要的内容是保护模式,那么其实就绕不开它的对立面:实模式了。

实模式,我们顾名思义其实就大概也能猜出来一点:**实模式下程序用到都是真实的物理地址!**像这个系列的前四章的程序其实都是在实模式下面运行的。

而在实模式下访问内存用到的就是我们之前用到的cs:ip寄存器组合。这里再强调一遍:cs中存储的是段基地址,ip中存储的是这个段中的偏移

好了,实模式就聊到这里吧,他也不是今天的主角。

保护模式

保护模式看到这四个名字,不知道读者有没有跟我一样的疑惑:它在执剑保护着谁呢?

实模式的五宗罪

抱歉,少了两种…

凡是都是要比较的,我们先来看看实模式有什么不足吧:

  1. 实模式下程序和操作系统是同权平级的,同等的权力滋长了用户程序的欲望
  2. 实模式下用的都是真实地址,能够肆意遨游内存空间。听起来很好?但是这就给了黑客大哥们可乘之机
  3. 访问超过64KB的内存区域需要切换段基地址。因为寄存器是16位,最大访问就是216=64KB
  4. 一次只能运行一个程序。哦豁,不能边听歌边打游戏
  5. 总共20条地址线,最大访问空间只有小小的1M

为了克服这些缺陷,也是保护模式的使命。硬件厂商设计出了保护模式。其最大的功能是:物理内存地址不能直接被程序访问,程序的虚拟地址需要被转换成物理地址后再去访问。而地址转换是由操作系统和处理器共同协作完成的,处理器在硬件上提供地址转换不见,操作系统提供转换过程中所需要的页表

而程序是怎么转换的,可以看这篇博客:程序地址转换

寄存器扩展

进入保护模式之后,最大的变化其实就是寄存器从16位扩展为了32位,这样寄存器就能访问到4GB的空间了。


这里的低16位是为了兼容。

内存访问

刚刚我们提出了保护模式在保护谁这么一个问题。其实保护的有很多,不过最主要的就是保护内存了。

在保护模式下,段偏移地址还是和实模式下一样,但是段基址的变化可大了去了。

在保护模式下,专门新增了一个数据结构:全局描述符表 来存放内存段的描述信息。每个表项被称之为段描述符,其大小为64位,用来描述各个内存段的起始地址、大小权限等信息。


64位可不是小数字,何况还是一个表项的大小。所以全局描述符表被放在了内存之中,通过寄存器GDTR指向它。


但是全局描述符表毕竟在内存中,对于CPU来说还是太慢了。所以为了提高获取段信息的效率,使用段描述符缓冲寄存器。CPU将获取到的内存段信息存入其中,之后再访问就直接从寄存器取数据就可以了

这样,段寄存器中保存的就不是实模式下的段基址了,而是全局描述符表项的下标,被称之为选择子

段描述符

刚刚我们列举了实模式的五宗罪。而段描述符就是为了应对其而生的,所以其必然有一些属性去保护内存的安全。

  • 实模式下用户程序可以破坏存储代码的内存区域,所以要添加个内存段类型属性来阻止这种行为
  • 实模式下用户程序和操作系统是同一级别,所以要添加特权级属性来区分用户程序和操作系统
  • 内存段是一片内存区域,访问内存就要提供段基址,所以要有段基址属性
  • 为了限制程序访问的内存范围,需要段界限属性


段界限表示段边界的扩展最值。扩展方向主要有上下两种。对于数据段和代码段,段的扩展方式是向上;对于栈段,短的扩展方向是向下。

  • 段界限:段界限用20位来表示,其是一个单位量。单位一般是1字节或者4K,所以他最多能访问4GB的空间
  • G:如果G位为0,表示段界限的粒度为1字节;如果G位为1,段界限粒度为4KB
  • D/B:对于代码段来说,此位是D位。若D为0,表示指令中的有效地址和操作数是16位;若D为1,表示其为32位;对于栈段来说,此位是B位,同理。
  • L:用来设置是否是64位代码段
  • AVL:暂时无用
  • P:表示此段是否在内存中,因为根据置换算法,其可能在磁盘中
  • DPL:表示当前段的特权级
  • S:表示是否是系统段
  • TYPE:总共四位,表示此描述符的类型详情如下表
内存段类型 X C R A 说明
代码段 1 0 0 - 只执行代码段
1 0 1 - 可执行可读代码段
1 1 0 - 可执行一致性代码段
1 1 1 - 可执行、可读、一致性代码段
内存段类型 X E W A 说明
数据段 0 0 0 - 只读数据段
0 0 1 - 可读写数据段
0 1 0 - 只读、向下扩展数据段
0 1 1 - 可读写,向下扩展数据段

全局描述符表

一个段描述符只能用来定义一个内存段,而全局描述符GDT用来存储多个段描述符。其相当于描述符的数组,数组的每个元素都是8字节的描述符,可以用选择子中提供的下标在GDT中索引段描述符。

全局描述符表存储在内存中,专门的寄存器GDTR指向它之后,CPU才知道它在哪里,才能去取段描述符数据。

:
要访问这个寄存器不能用mov,只能用lgdt指令

段选择子

现在段描述符有了,全局描述符表也有了,还差一个段选择子了。

段寄存器CS、DS、ES、FS、GS、SS在保护模式中存储的就是段选择子selector。选择子基本上来说是一个索引值,但是也只是基本上了。

  • TI:用来指示段选择子是全局表GDT还是局部表LDT
  • RPL:用来表示当前的特权级

进入保护模式

进入保护模式总共有三步:

  • 打开A20地址线
  • 加载全局描述符表GDT
  • 将 cr0 寄存器的 PE 位置置为1

打开A20地址线并加载全局描述符表GDT

在实模式上总共有20跟地址线,编号0~19。而在保护模式下,打开A20地址线意味着进入保护模式。

而打开A20Gate的方式是及其简单的,只需要将端口0x92的第一位置为1就可以了:

in al,0x92
or al,0000_0010B
out 0x92,al

而加载全局描述符则更简单了:

# gdt_ptr 为我们创建的全局描述符表的位置
lgdt [gdt_ptr]

打开保护模式开关cr0

CRx系列都是控制寄存器,其是CPU的窗口,既可以用来展示CPU的内部状态,也可以用来控制CPU的运行机制。

而我们今天要用到的就是第一位PE位。其是保护模式的开关,而其他除了无效位也有各自的用途:

标志位 描述
PE 保护模式开关
MP 监控协处理器
EM 仿真? TODO
TS 任务选择
ET 扩展类型
NE 数字错误
WP 写保护
AM 对齐掩码
NW 不写?TODO
CD 禁用缓存
PG 开启分页

而打开这个开关的代码也很简单:

mov eax,cr0
or eax,0x00000001
mov cr0,eax

GO GO GO

下面就上源码了,首先是记录全局描述符表以及段描述符信息的头文件boot.inc:

;*************************
LOADER_BASE_ADDR equ 0x900
LOADER_START_SECTOR equ 0x2     ;第二扇区;*************************
;新增段描述符属性以及段选择子
DESC_G_4k   equ 1_00000000000000000000000b  ;表示描述符粒度为4k
DESC_D_32   equ 1_0000000000000000000000b
DESC_L  equ 0_000000000000000000000b    ;64位代码标记,此处标记为0
DESC_AVL    equ 0_00000000000000000000b ;CPU不用此位,暂时置为0
DESC_LIMIT_CODE2    equ 1111_0000000000000000b
DESC_LIMIT_DATA2    equ DESC_LIMIT_CODE2
DESC_LIMIT_VIDEO2   equ 0000_000000000000000b
DESC_P  equ 1_000000000000000b
DESC_DPL_0  equ 00_0000000000000b
DESC_DPL_1  equ 01_0000000000000b
DESC_DPL_2  equ 10_0000000000000b
DESC_DPL_3  equ 11_0000000000000b
DESC_S_CODE equ 1_000000000000b
DESC_S_DATA equ DESC_S_CODE
DESC_S_sys  equ 0_000000000000b
DESC_TYPE_CODE  equ 1000_00000000b  ;x=1,c=0,r=0,a=0 代码段是可执行的,非一致性,不可读,已访问位a清0
DESC_TYPE_DATA  equ 0010_00000000b  ;x=0,e=0,w=1,a=0 数据段是不可执行的,向上扩展的,可写,已访问位a清0;定义代码段的高4字节
;(0x00 << 24)表示段基址24~31的部分
;DESC_G_4k: 4k的粒度
;DESC_D_32: 表示描述符中的D/B位
;DESC_L: 段描述符L位
;DESC_AVL: 无意义
;DESC_LIMIT_CODE2: 段界限第二部分,和第一部分组成0xfffff
;DESC_P: 表示段是否存在
;DESC_DPL_0: 表示特权级是0
;DESC_S_CODE: 表示代码段的S位,其为1表示其为普通内存段,非代码段
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 + 0x00DESC_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 + 0x00DESC_VIDEO_HIGH4 equ (0x00 << 24) + DESC_G_4k + DESC_D_32 + \
DESC_L + DESC_AVL + DESC_LIMIT_VIDEO2 + \
DESC_P + DESC_DPL_0 + DESC_S_DATA + \
DESC_TYPE_DATA + 0x0B;*************************
;选择子属性
RPL0    equ 00b         ;特权级别
RPL1    equ 01b
RPL2    equ 10b
RPL3    equ 11b
TI_GDT  equ 000b
TI_LDT  equ 100b

然后便是正餐loader.S

%include "boot.inc"
section loader vstart=LOADER_BASE_ADDR
LOADER_STACK_TOP equ LOADER_BASE_ADDR
jmp loader_start;构建全局段描述符GDT及其内部描述符
GDT_BASE:   dd 0x00000000dd 0x00000000
CODE_DESC:  dd 0x0000FFFFdd DESC_CODE_HIGH4
DATA_STACK_DESC:    dd 0x0000FFFFdd DESC_DATA_HIGH4
VIDEO_DESC: dd 0x80000007      ;limit=(0xbffff-0xb8000)/4k=0x7dd DESC_VIDEO_HIGH4 ;此时DPL为0GDT_SIZE    equ $ - GDT_BASE
GDT_LIMIT   equ GDT_SIZE - 1
times 60 db 0   ;预留60个描述符的空位SELECTOR_CODE   equ (0x0001<<3) + TI_GDT + RPL0     ;相当于(CODE_DESC - GDT_BASE)/8 + TI_GDT + RPI0
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_LIMITdd  GDT_BASE
loadermsg   db  'Gos loading...';*************************
loader_start:
;*************************
; 打印字符串mov sp,LOADER_BASE_ADDR     ;初始化栈指针mov bp,loadermsg            ;字符串压栈mov cx,14                   ;字符串长度mov ax,0x1301               ;ah=13,al=01h 调用子功能号13    01表示输出方式mov bx,0x000F               ;页号为0,黑底白字0Fmov dx,0x1800               ;字符串打印在0x18的那一行int 0x10                    ;10中断;*************************
;进入保护模式
;1.打开A20 地址线
;2.加载全局段描述符表
;3.设置保护模式标志位 cr0的pe位为1;*************************
;打开A20地址线
in al,0x92
or al,0000_0010B
out 0x92,al;*************************
;加载gdt
lgdt [gdt_ptr];*************************
;开启保护模式
mov eax,cr0
or eax,0x00000001
mov cr0,eaxjmp dword SELECTOR_CODE:p_mode_start       ;刷新流水线;*************************
[bits 32]
p_mode_start:mov ax,SELECTOR_DATAmov ds,axmov es,axmov ss,axmov esp,LOADER_STACK_TOPmov ax,SELECTOR_VIDEOmov gs,axmov byte [gs:160],'G'jmp $

之后编译源代码:

nasm -I ../include/ -o loader.bin loader.S

将源代码写入hd60.img硬盘之中:

sudo dd if=./loader.bin of=/bochs/bo_tmp/bin/hd60M.img bs=512 count=4 seek=2 conv=notrunc

参考文献

[1] 操作系统真相还原

Gos —— 开启保护模式相关推荐

  1. Bochs源码分析 - 20: 开启保护模式

    前言 在邓志老师的<x86/x64体系探索及编程>中讲述了开启保护模式的顺序,我尝试着翻阅intel手册,但很遗憾似乎自己没有找到对此的描述(应该是有的,但是自己没找到,日后找到了会补充上 ...

  2. [书]操作系统真象还原 -- 第5章 开启保护模式、开启分页机制

    mbr:加载loader,跳转 loader: 1)调用BIOS中断获取内存大小; 2)构建GDT.开启保护模式;  3)构建页目录表和页表.开启分页机制; FILE:loader.asm ; FIL ...

  3. redis踩坑:redis哨兵开启了保护模式导致主从切换不同步

    故障表现 哨兵只存在两个的时候,当哨兵模式的redis主节点挂掉以后,业务组件不能切换到新主节点 故障原因 redis哨兵依旧认为旧主为主节点,没有触发failover 故障原因定位 哨兵集群部署方式 ...

  4. x86CPU 实模式 保护模式 傻傻分不清楚? 基于Xv6-OS 分析CR0 寄存器

    基于Xv6-OS 分析CR0 寄存器 之前一直认为晕乎乎的...啥?什么时候切换real model,怎么切换,为什么要切换? ------------------------------------ ...

  5. 跳转到保护模式并显示一个LOGO

    注:本程序为原创,若发现bug,万望指出,若有问题,欢迎交流,转载请指明出处.若能有助于一二访客,幸甚. 以下为结果截图,显示的LOGO为小篆字体的欢迎 baby os 加载完成...几个字. 保护模 ...

  6. 操作系统引导--从实模式到保护模式

    从开始到保护--系统开机引导 ------没有一个文档能写的通俗易懂,我希望写出来. 开机引导和实模式: 两个星期加上假期吐血整理,所述为计算机的开机引导,其中包括一系列计算机内存设置等等,由于没有老 ...

  7. 32位x86处理器编程导入——《x86汇编语言:从实模式到保护模式》读书笔记08

    在说正题之前,我们先看2个概念. 1.指令集架构(ISA) ISA 的全称是 instruction set architecture,中文就是指令集架构,是指对程序员实际"可见" ...

  8. 3.操作系统——CPU的实模式、保护模式和长模式

    有实模式.保护模式.长模式 实模式16(实地址模式) 真实分为两个方面: 运行真实指令.不区分指令动作,只是直接执行指令的真实功能 发往内存的地址是真实.不加限制的. 总结来说就是,这个模式下直接往物 ...

  9. i386 Linux内核进入保护模式引导流程

    在系统引导过程中,Bootloader将内核镜像加载到内存后,并将控制权转交给内核       ,通过长转移指令跳转到入口startup_32. 实际上进入startup_32入口前,CPU已经处于了 ...

  10. [书]x86汇编语言:从实模式到保护模式 -- 第17章 中断、任务切换、分页机制、平坦模型

    # 任务切换 内核任务.用户任务1.用户任务2,之前的轮询切换 利用RTC芯片的硬件中断来实现任务切换 计算机主板上有实时时钟芯片RTC,可以设置RTC芯片,使得它每次更新CMOS中的时间信息后,发出 ...

最新文章

  1. Go 学习笔记(28)— nil(nil 不能比较、不是关键字或保留字、nil 没有默认类型、不同类型的 nil 指针是一样的、不同类型的 nil 是不能比较的、相同类型的 nil 可能也无法比较)
  2. 【算法与数据结构】在n个数中取第k大的数(基础篇)
  3. 面试官问你B树和B 树,就把这篇文章丢给他
  4. 复现经典:《统计学习方法》第22章 无监督学习方法总结
  5. c语言填空题删除字符串k右边,计算机二级C语言上机模拟试题及解题思路
  6. springboot的IOC依赖注入与控制反转-举例(转载+自己整理)
  7. python123外汇兑换计算器_Python 3.x--使用re模块,实现计算器运算实例
  8. 4-STM32物联网开发WIFI(ESP8266)+GPRS(Air202)系统方案升级篇(远程升级WIFI内部程序)
  9. 学计算机的普通学生那里就业,学计算机我后悔了 现在好就业吗
  10. hdu 6203 ping ping ping(贪心+树状数组+dfs序)
  11. 游戏 mysql优化工具_MySQL 性能优化神器 Explain 使用分析
  12. 树莓派android p,Android P最新测试版带来更多的UI和图标方面的改进
  13. bzoj 1800 [Ahoi2009]fly 飞行棋——模拟
  14. 操作系统课程设计之磁盘调度系统的设计与实现c语言
  15. Matlab底层算法实现图像转置--宽高互换
  16. 如何实现网易公开课的倍速播放?
  17. 秀米html编辑器,ueditor集成秀米编辑器
  18. 元宇宙专题001 | 他们居然将元宇宙和心理学写到一起了
  19. 微软总裁:杀手机器人的崛起「势不可挡」【智能快讯】
  20. 解决windows下无法ctrl+A全选数据快捷键的问题

热门文章

  1. 永恒之蓝方程式利用工具使用教程
  2. knx智能照明控制系统电路图_智能照明KNX灯控软件
  3. ML之SHAP:机器学习可解释性之SHAP值之理解单样本单特征预测
  4. 速算扣除法php,关于速算扣除数法的计税方法 这两点必须知道
  5. java 利用Future做超时任务处理
  6. domcontentloaded事件和laod事件区别
  7. MATLAB算法实战应用案例精讲-【图像处理】缺陷检测(补充篇)(附matlab实现代码)
  8. python lncrna_一文解决TCGA任意肿瘤的差异lncRNA,miRNA,mRNA
  9. Windows11 校园网连ftp登录上传作业失败
  10. Pr 入门教程:如何使用项目面板?