1.简介:

8086实模式

80286才出现保护模式,保护模式下的段寄存器存储的是段选择子,不在是8086实模式的段基址了!

[扩展知识]:可以看了后面知识在回头看一下这段.

[8086各个段寄存器和通用寄存器都是16位,地址总线20位,采用段式段+偏移可访问物理内存1M]

[80286各个段寄存器和通用寄存器都是16位,地址总线24位,采用段式选择子+偏移可访问虚拟内存1G;物理内存16M]

80286全局描述符表一项是占8字节,其中段基址占3字节(24位与地址总线一致)

[80386段寄存器依然16位,通用寄存器也各自扩展到了32位,地址总线32位,采用段页式(选择子+偏移+分页)可访问虚拟内存4G;物理内存4G]

80386全局描述符表一项也是占8字节,其中段基址占4字节(32位与地址总线一致),也是在80286描述符基础上增加了些位域内容

2.选择子

保护模式下的段寄存器存储的是段选择子!这时候选择子相当于数组中的索引,那么怎么用这13位索引获取段基址呢?段寄存器16位每个位域代表意思具体结构如下:

用高13位索引获取段基址,这样我们必须要在内存某个地方去保存例如实模式下的各个段寄存器的值,另外在加上一些限制和保护体现,最终会形成一种叫做全局描述符表!全局描述符表只是一个概念,也就是描述一个数据的结构组成。描述符表由一项一项的描述符组成,其中每一项占8字节。

既然是索引,就像数组那样总该有个地址可以确定这个数组的首地址吧,在80386中有一个这样的寄存器GDTR里面由软件写入地址,这个地址里存放的便是全局描述符表的首地址,然后利用[首地址+8*索引值]就可以算出最终所要找到的全局描述符表中的一项,即  段选择子指向的描述符地址= GDTR里存的地址 +8×索引

选择子高13位索引即可索引到2的13次方8192个描述符[0~8191],与GDT界限的16位界限一致,因GDTR是48位寄存器,其中32位是GDTR存储的基址,16位是界限!即GDT界限大小为2的16次方65536,又因为每个描述符占8位,65536/8=8192!所以综上所述,选择子高13位索引和描述符表界限一致!

扩展知识:如何获取段基址https://blog.csdn.net/jadeshu/article/details/72837230

......待添加内容

3.进入保护模式需要以下几个步骤:

  1. 打开A20

  2. 加载gdt  :进入保护模式需要使用GDTR,则需要加载设置GDTR

  3. 将cr0寄存器的PE位置设为1

演示程序

mbr.s

;主引导程序
;------------------------------------------------------------
SECTION MBR vstart=0x7c00
mov ax,cs
mov ds,ax
mov es,ax
mov ss,ax
mov fs,ax
mov sp,0x7c00
mov ax,0xb800
mov gs,ax ; 清屏
;利用0x06号功能,上卷全部行,则可清屏。
; -----------------------------------------------------------
;INT 0x10 功能号:0x06 功能描述:上卷窗口
;------------------------------------------------------
;输入:
;AH 功能号= 0x06
;AL = 上卷的行数(如果为0,表示全部)
;BH = 上卷行属性
;(CL,CH) = 窗口左上角的(X,Y)位置
;(DL,DH) = 窗口右下角的(X,Y)位置
;无返回值:
mov ax, 0600h
mov bx, 0700h
mov cx, 0 ; 左上角: (0, 0)
mov dx, 184fh ; 右下角: (80,25),
; 因为VGA文本模式中,一行只能容纳80个字符,共25行。
; 下标从0开始,所以0x18=24,0x4f=79
int 10h ; int 10h ; 输出字符串:MBR
mov byte [gs:0x00],'L'
mov byte [gs:0x01],0xA4 mov byte [gs:0x02],'o'
mov byte [gs:0x03],0xA4 mov byte [gs:0x04],'a'
mov byte [gs:0x05],0xA4 ;A表示绿色背景闪烁,4表示前景色为红色 mov byte [gs:0x06],'d'
mov byte [gs:0x07],0xA4 mov byte [gs:0x08],'!'
mov byte [gs:0x09],0xA4 mov eax,0x2 ; 起始扇区lba地址.第2个扇区
mov bx,0x900 ; 写入的地址 0x900
mov cx,4 ; 待读入的扇区数 4
call rd_disk_m_16 ; 以下读取程序的起始部分(一个扇区) jmp 0x900 ;跳转到0x900地址 ;-------------------------------------------------------------------------------
;功能:读取硬盘n个扇区
rd_disk_m_16:
;-------------------------------------------------------------------------------
; eax=LBA扇区号
; ebx=将数据写入的内存地址
; ecx=读入的扇区数
mov esi,eax ;备份eax
mov di,cx ;备份cx
;读写硬盘:
;第1步:设置要读取的扇区数
mov dx,0x1f2
mov al,cl
out dx,al ;读取的扇区数 mov eax,esi ;恢复ax ;第2步:将LBA地址存入0x1f3 ~ 0x1f6 ;LBA地址7~0位写入端口0x1f3
mov dx,0x1f3
out dx,al ;LBA地址15~8位写入端口0x1f4
mov cl,8
shr eax,cl
mov dx,0x1f4
out dx,al ;LBA地址23~16位写入端口0x1f5
shr eax,cl
mov dx,0x1f5
out dx,al shr eax,cl
and al,0x0f ;lba第24~27位
or al,0xe0 ; 设置7~4位为1110,表示lba模式
mov dx,0x1f6
out dx,al ;第3步:向0x1f7端口写入读命令,0x20
mov dx,0x1f7
mov al,0x20
out dx,al ;第4步:检测硬盘状态
.not_ready:
;同一端口,写时表示写入命令字,读时表示读入硬盘状态
nop
in al,dx
and al,0x88 ;第4位为1表示硬盘控制器已准备好数据传输,第7位为1表示硬盘忙
cmp al,0x08
jnz .not_ready ;若未准备好,继续等。 ;第5步:从0x1f0端口读数据
mov ax, di
mov dx, 256
mul dx
mov cx, ax ; di为要读取的扇区数,一个扇区有512字节,每次读入一个字,
; 共需di*512/2次,所以di*256
mov dx, 0x1f0
.go_on_read:
in ax,dx
mov [bx],ax
add bx,2
loop .go_on_read
ret ; times 510-($-$$) db 0
; db 0x55,0xaa

loader.s


;-------------- gdt描述符属性 -----------
DESC_G_4K equ 1_00000000000000000000000b
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
;x=1,c=0,r=0,a=0 代码段是可执行的,非依从的,不可读的,已访问位a清0.
DESC_TYPE_CODE equ 1000_00000000b
;x=0,e=0,w=1,a=0 数据段是不可执行的,向上扩展的,可写的,已访问位a清0.
DESC_TYPE_DATA equ 0010_00000000b 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_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
;--------------------------------------------
section loader vstart=0x900 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 ; 8[1000] 相当于(CODE_DESC - GDT_BASE)/8 + TI_GDT + RPL0
SELECTOR_DATA equ (0x0002<<3) + TI_GDT + RPL0 ; 16[10000] 同上
SELECTOR_VIDEO equ (0x0003<<3) + TI_GDT + RPL0 ; 24[11000] 同上 ;以下是定义gdt的指针,前2字节是gdt界限,后4字节是gdt起始地址
gdt_ptr dw GDT_LIMIT
dd GDT_BASE
;====================================================================== loader_start: ;----------------- 准备进入保护模式 -------------------
;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跳转,
; 这将导致之前做的预测失效,从而起到了刷新的作用。 [bits 32]
p_mode_start:
mov ax, SELECTOR_DATA
mov ds, ax
mov es, ax
mov ss, ax
mov esp,0x900
mov ax, SELECTOR_VIDEO
mov gs, ax ;160=80个字符[80*2]
mov byte [gs:160*8], 'J'
mov byte [gs:160*8+1],0x02 mov byte [gs:160*8+2], 'a'
mov byte [gs:160*8+3],0x02 mov byte [gs:160*8+4], 'd'
mov byte [gs:160*8+5],0x02 mov byte [gs:160*8+6], 'e'
mov byte [gs:160*8+7],0x02 mov byte [gs:160*8+8], ' '
mov byte [gs:160*8+9],0x00 mov byte [gs:160*8+10], 'O'
mov byte [gs:160*8+11],0x02 mov byte [gs:160*8+12], 'S'
mov byte [gs:160*8+13],0x02 jmp $

程序说明图:

(1)是将硬盘中的MBR加载到内存0X7C00开始的位置,由BIOS完成

(2)mbr.s中将loader加载到内存0X900开始的位置

执行流程 BIOS[CS:IP 0XFFF0:FFF0]--》CS:IP[0:0X7C00]----(MBR将loader加载到0X900)--->CS:IP[0:0X900]---(loader中)

最后进行汇编编译

nasm mbr.S -o mbr                  编译产生mbr文件

        nasm loader.S -o loader          编译产生loader文件

编译后产生的两个文件放入C++工程中,在用上节中的C++代码进行编译结果生成boot.img文件,最后直接用Bochs进行测试,前面章节都有介绍如何操作,就不做讲解了!

实验结果:

 

(5)打造简单OS-进入保护模式的更多相关文章

  1. (7)打造简单OS-加载内核

    一.简要说明 我们在第五讲[(5)打造简单OS-进入保护模式]中的mbr.S 汇编文件有段这样的代码 mov eax, 0x2 ; 起始扇区lba地址,从间隔第二个扇区开始 mov bx, 0x900 ...

  2. OSLab:开启保护模式

    日期:2019/5/22 关键词:操作系统:OS:保护模式:A20地址线激活:分页开启:二级页表的设置 PS:OSLAB实验课的整理. 本文主要内容是分析操作系统中一个简易的MBR. 建议先阅读:ht ...

  3. 羽夏看Win系统内核——保护模式篇

    写在前面   此系列是本人一个字一个字码出来的,包括示例和实验截图.由于系统内核的复杂性,故可能有错误或者不全面的地方,如有错误,欢迎批评指正,本教程将会长期更新. 如有好的建议,欢迎反馈.码字不易, ...

  4. 【理解OS】1.保护模式概述

    这个系列文章主要目的是为了记录我个人学习保护模式后的总结与一点点的思考.我也是一个学习者,其中由错误在所难免,若各位朋友指出将不胜感激. 1. Intel CPU的运行模式概述 这里我将粗略介绍Int ...

  5. 【OS】实模式和保护模式区别及寻址方式

    实模式和保护模式区别及寻址方式 转载请注明出处:http://blog.csdn.NET/rosetta 64KB-4GB-64TB? 我记得大学的汇编课程.组成原理课里老师讲过实模式和保护模式的区别 ...

  6. (4.1)打造简单OS-小实验[图形显示]

    主要是实现<简单打造OS>第四小节说到的一个图形界面的实验项目 1.mbr boot.inc ;------------- loader和kernel ---------- LOADER_ ...

  7. x86 保护模式 十 分页管理机制

    x86   保护模式  十  分页管理机制 8.386开始支持分页管理机制 段机制实现虚拟地址到线性地址的转换,分页机制实现线性地址到物理地址的转换.如果不启用分页,那么线性就是物理地址 一  分页管 ...

  8. 打造简单OS-总目录

    1-汇编写入引导区,虚拟机启动步骤 (了解即可) 2-开机BIOS初始化与MBR操作系统引导详解 (了解即可) 3-MBR引导区转移加载简单程序(突破512限制)(了解即可) 4-loader硬盘加载 ...

  9. x86架构:从实模式进入保护模式

    详细的过程说明参考:(1)  https://www.cnblogs.com/Philip-Tell-Truth/p/5211248.html    (2)x86汇编:从实模式到保护模式 这里简化一下 ...

随机推荐

  1. 在Eclipse配置Tomcat服务器+JSP实例创建

    欢迎任何形式的转载,但请务必注明出处. 1.jdk安装及环境配置 点击进入教程 2.Eclipse安装 点击进入官网下载 注意下载完成打开.exe后,出现的界面,有很多版本供选择.选择下图版本 3.T ...

  2. 读取普通java web项目下的WEB-INF目录下的配置文件(application.xml,xx.properties等配置文件)

    一.在Java web工程WEB-INF下创建weixin.properties属性文件 weixin.properties属性文件里内容如下: 二.创建PropertiesUtils.java测试类 ...

  3. linux管道和重定向

    管道 管道应该是等左边的程序执行完,才使用左边的程序的输出执行右边的程序. 但是在测试的时候,如果左边的程序无限循环且不等待的输出,那么左边的程序执行时右边的程序也会执行,个人感觉这是linux的机制 ...

  4. shell 三剑客之 sed 命令详解

    sed 编辑命令 sed 编辑命令对照表 把 /etc/passwd 文件赋值到当前路径下,进行操作 cp /etc/passwd ./ cat -n passwd sed 删除操作 删除 passw ...

  5. Elasticsearch ES索引

    ES是一个基于RESTful web接口并且构建在Apache Lucene之上的开源分布式搜索引擎. 同时ES还是一个分布式文档数据库,其中每个字段均可被索引,而且每个字段的数据均可被搜索,能够横向 ...

  6. dubbo API的使用方式

    本文使用maven方式 1:pom文件 <dependencies> <!-- 引入spring的jar --> <dependency> <groupId& ...

  7. DNS服务——正向查找区 和 逆向查找区

    前言 正向查找区,就是我们最熟知的DNS.即根据域名解析成IP 逆向查找区,即根据IP解析成域名. 他们之间的关系很像ARP和RARP 正向查找区 /etc/named.rfc1912.zones用于 ...

  8. 解决MySQL不需要密码就能登录问题

    因为执行了一个更改数据库root用户密码的命令,当我更改完后,发现用我新密码和旧密码都能登陆,于是感觉没有输密码,直接回车就能登录,而我在配置中也没有进行免密码登陆的操作,最后,执行了一条命令解决up ...

  9. visual studio调试exe程序

    标题:How to debug and profile any EXE with Visual Studio 文章:https://devblogs.microsoft.com/visualstudi ...

  10. 聊天程序——基于Socket、Thread (二)

    聊天程序简述 1.目的:主要是为了阐述Socket,以及应用多线程,本文侧重Socket相关网路编程的阐述.如果您对多线程不了解,大家可以看下我的上一篇博文浅解多线程 . 2.功能:此聊天程序功能实现 ...