保护模式

完整代码

; ==========================================
; pmtest1.asm
; 编译方法:nasm pmtest1.asm -o pmtest1.bin
; ========================================== ;
;
;
;
%include "pm.inc" ; 常量, 宏, 以及一些说明 org 07c00h
jmp LABEL_BEGIN [SECTION .gdt]
; GDT
; 段基址, 段界限 , 属性
LABEL_GDT: Descriptor 0, 0, 0 ; 空描述符
LABEL_DESC_CODE32: Descriptor 0, SegCode32Len - 1, DA_C + DA_32; 非一致代码段
LABEL_DESC_VIDEO: Descriptor 0B8000h, 0ffffh, DA_DRW ; 显存首地址
; GDT 结束 GdtLen equ $ - LABEL_GDT ; GDT长度
GdtPtr dw GdtLen - 1 ; GDT界限
dd 0 ; GDT基地址 ; GDT 选择子
SelectorCode32 equ LABEL_DESC_CODE32 - LABEL_GDT
SelectorVideo equ LABEL_DESC_VIDEO - LABEL_GDT
; END of [SECTION .gdt] [SECTION .s16]
[BITS 16]
LABEL_BEGIN:
mov ax, cs
mov ds, ax
mov es, ax
mov ss, ax
mov sp, 0100h ; 初始化 32 位代码段描述符
xor eax, eax
mov ax, cs
shl eax, 4
add eax, LABEL_SEG_CODE32 ; [SECTION .s32]这个段的物理地址
;分成三个部分赋值给描述符 DESC_CODE32
mov word [LABEL_DESC_CODE32 + 2], ax
shr eax, 16
mov byte [LABEL_DESC_CODE32 + 4], al
mov byte [LABEL_DESC_CODE32 + 7], ah ; 为加载 GDTR 作准备
;把GDT的物理地址填充到了GdtPtr这个6字节的数据结构
xor eax, eax
mov ax, ds
shl eax, 4
add eax, LABEL_GDT ; eax <- gdt 基地址
mov dword [GdtPtr + 2], eax ; [GdtPtr + 2] <- gdt 基地址 ; 加载 GDTR 将GdtPtr指示的6字节加载到寄存器gdtr
lgdt [GdtPtr] ; 关中断因为保护模式下中断处理的机制不同,不关中断会出现错误
cli ; 打开地址线A20
in al, 92h
or al, 00000010b
out 92h, al ; 准备切换到保护模式
mov eax, cr0
or eax, 1
mov cr0, eax ; 真正进入保护模式
jmp dword SelectorCode32:0 ; 执行这一句会把 SelectorCode32 装入 cs,
; 并跳转到 Code32Selector:0 处
; END of [SECTION .s16] [SECTION .s32]; 32 位代码段. 由实模式跳入.
[BITS 32] ;保护模式跳转到此处
LABEL_SEG_CODE32:
mov ax, SelectorVideo
mov gs, ax ; 视频段选择子(目的) mov edi, (80 * 11 + 79) * 2 ; 屏幕第 11 行, 第 79 列。
mov ah, 0Ch ; 0000: 黑底 1100: 红字
mov al, 'P'
mov [gs:edi], ax ; 到此停止
jmp $ SegCode32Len equ $ - LABEL_SEG_CODE32
; END of [SECTION .s32]

分析

[SECTION .gdt]
; GDT
; 段基址, 段界限 , 属性
LABEL_GDT: Descriptor 0, 0, 0 ; 空描述符
LABEL_DESC_CODE32: Descriptor 0, SegCode32Len - 1, DA_C + DA_32; 非一致代码段
LABEL_DESC_VIDEO: Descriptor 0B8000h, 0ffffh, DA_DRW ; 显存首地址
; GDT 结束 GdtLen equ $ - LABEL_GDT ; GDT长度
GdtPtr dw GdtLen - 1 ; GDT界限
dd 0 ; GDT基地址 ; GDT 选择子
SelectorCode32 equ LABEL_DESC_CODE32 - LABEL_GDT
SelectorVideo equ LABEL_DESC_VIDEO - LABEL_GDT
; END of [SECTION .gdt]

这一段定义了3个Descriptor,至于Descriptor,是作者于pm.inc中定义的一个宏,类似于一个结构体:

%macro Descriptor 3
dw %2 & 0FFFFh ; 段界限1
dw %1 & 0FFFFh ; 段基址1
db (%1 >> 16) & 0FFh ; 段基址2
dw ((%2 >> 8) & 0F00h) | (%3 & 0F0FFh) ; 属性1 + 段界限2 + 属性2
db (%1 >> 24) & 0FFh ; 段基址3
%endmacro ; 共 8 字节

这个结构体实际就是全局描述符表(GDT)中描述符的定义。

这个宏接受3个参数,分别是段基址,段界限和属性。然后将这三个参数加以转换成图中描述符对应的格式。

至于如何转换,以及为什么段描述符格式这么奇怪请参考这里。

[SECTION .gdt]中定义了3个描述符。处理器规定,GDT中的第一个描述符必须是空描述符,或者叫哑描述符或NULL描述符。所以LABEL_GDT就被定义为一个空的描述符。LABEL_DESC_CODE32为代码段描述符,LABEL_DESC_VIDEO为图形显示段的描述符。属性这里暂且不探讨。

接着定义了对应的选择子,直观上看来,它好像就是描述符相对于GDT基址的偏移。但实际上它是如图所示的结构:

所以,当TI和RPL都为0时,选择子就变成了对应描述符相对于GDT基址的偏移。而描述符的大小为8字节,所以选择子为8的倍数,因此最后三位必然为0。

总之,整个寻址方式如下:

接着把GDT的物理地址填充到了GdtPtr这个6字节的数据结构中

; 为加载 GDTR 作准备
;把GDT的物理地址填充到了GdtPtr这个6字节的数据结构
xor eax, eax
mov ax, ds
shl eax, 4
add eax, LABEL_GDT ; eax <- gdt 基地址
mov dword [GdtPtr + 2], eax ; [GdtPtr + 2] <- gdt 基地址

加载到寄存器gdtr

lgdt [GdtPtr]

这一句的作用是将GdtPtr指示的6字节加载到寄存器gdtr。gdtr的结构如图:

关掉中断:

cli

保护模式下的中断机制和实模式不同,因此,原有的中断向量表不再适用。而且在保护模式下,BIOS中断都不能再用,因为它们是实模式下的代码。在重新设置保护模式下的中断环境之前,必须关中断。

打开地址线A20:

	in	al, 92h
or al, 00000010b
out 92h, al

原因是:实模式下的程序只能寻址1MB内存,因为它依赖16位的段地址左移4位,加上16位的偏移地址来访问内存。当逻辑段地址达到最大值0xFFFF时,再加一,结果为0x100000.但因为它只能维持20位的地址,进位自然丢失,地址又绕回最低地址0x00000。但后来,到了80286时代,处理器有24根地址线,因此地址在达到0xffff时不应回0。为了能在80286机器上运行8086程序且不出现问题,便有了强制第21根线A20为0的做法。上述代码则是打开A20,使其可以为1。

切换到保护模式:

	mov	eax, cr0
or eax, 1
mov cr0, eax

CR0为处理器内部的控制寄存器,结构如图所示:

当把第0位置为1时,系统就运行于保护模式之下了。

跳转,正式进入保护模式:

jmp	dword SelectorCode32:0

ps:最后,书中的代码在我这里是无法在bochs上正常运行的。需要把类似[SECTION ...]的语句去掉,在代码最后加上:

times 510-($-$$) db 0
dw 0xaa55

《Orange’s》保护模式的更多相关文章

  1. 简单物联网:外网访问内网路由器下树莓派Flask服务器

    最近做一个小东西,大概过程就是想在教室,宿舍控制实验室的一些设备. 已经在树莓上搭了一个轻量的flask服务器,在实验室的路由器下,任何设备都是可以访问的:但是有一些限制条件,比如我想在宿舍控制我种花 ...

  2. 利用ssh反向代理以及autossh实现从外网连接内网服务器

    前言 最近遇到这样一个问题,我在实验室架设了一台服务器,给师弟或者小伙伴练习Linux用,然后平时在实验室这边直接连接是没有问题的,都是内网嘛.但是回到宿舍问题出来了,使用校园网的童鞋还是能连接上,使 ...

  3. 外网访问内网Docker容器

    外网访问内网Docker容器 本地安装了Docker容器,只能在局域网内访问,怎样从外网也能访问本地Docker容器? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装并启动Docker容器 ...

  4. 外网访问内网SpringBoot

    外网访问内网SpringBoot 本地安装了SpringBoot,只能在局域网内访问,怎样从外网也能访问本地SpringBoot? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装Java 1 ...

  5. 外网访问内网Elasticsearch WEB

    外网访问内网Elasticsearch WEB 本地安装了Elasticsearch,只能在局域网内访问其WEB,怎样从外网也能访问本地Elasticsearch? 本文将介绍具体的实现步骤. 1. ...

  6. 怎样从外网访问内网Rails

    外网访问内网Rails 本地安装了Rails,只能在局域网内访问,怎样从外网也能访问本地Rails? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装并启动Rails 默认安装的Rails端口 ...

  7. 怎样从外网访问内网Memcached数据库

    外网访问内网Memcached数据库 本地安装了Memcached数据库,只能在局域网内访问,怎样从外网也能访问本地Memcached数据库? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装 ...

  8. 怎样从外网访问内网CouchDB数据库

    外网访问内网CouchDB数据库 本地安装了CouchDB数据库,只能在局域网内访问,怎样从外网也能访问本地CouchDB数据库? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装并启动Cou ...

  9. 怎样从外网访问内网DB2数据库

    外网访问内网DB2数据库 本地安装了DB2数据库,只能在局域网内访问,怎样从外网也能访问本地DB2数据库? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装并启动DB2数据库 默认安装的DB2 ...

  10. 怎样从外网访问内网OpenLDAP数据库

    外网访问内网OpenLDAP数据库 本地安装了OpenLDAP数据库,只能在局域网内访问,怎样从外网也能访问本地OpenLDAP数据库? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装并启动 ...

随机推荐

  1. vue 中 相同的路由不会跳转,更改路由的办法

    vue 开发的项目,路由跳转的时候,是相同的路由是不会跳转,页面也不会有更新的 有时候 必须要跳转怎么办, 更改一个参数,num,一直在改变.就可以进入了. this.$router.push({ p ...

  2. 基于STM8的IIC协议---STM8-第五章

    1. 综述 I2C(IIC,Inter-Integrated Circuit),两线式串行总线,由PHILIPS公司开发用于连接微控制器及其外围设备. 它是由数据线SDA和时钟SCL构成的串行总线,可 ...

  3. [UE4]Add Offset

    在原来值的基础上增加偏移

  4. docker(ce) on Ubuntu

    Note: # - requires given linux commands to be executed with root privileges either directly as a roo ...

  5. 爬虫——BeautifulSoup和Xpath

    爬虫我们大概可以分为三部分:爬取——>解析——>存储 一 Beautiful Soup: Beautiful Soup提供一些简单的.python式的函数用来处理导航.搜索.修改分析树等功 ...

  6. git 每次push和pull都需要提交密码

    每次从远程仓库拉代码或者向远程仓库提交代码的时候需要输入密码的解决方法 执行 $ git config --global credential.helper store 结果: 查看C:\用户\用户名 ...

  7. 【Python】爬虫

    参考:http://www.cnblogs.com/fnng/p/3576154.html import urllib,re page = urllib.urlopen('http://trend.b ...

  8. upload三种上传方式(上)---Servlet---post---commons-fileupload.1.2.1.jar方式请求上传文件

    上传前进行的配置选项: 1.在下方的Servers中,右键你的tomcat--open,选中下面两个配置. 第一个:Serve modules without publishing 作用:tomcat ...

  9. SpringCloud系列八:Zuul 路由访问(Zuul 的基本使用、Zuul 路由功能、zuul 过滤访问、Zuul 服务降级)

    1.概念:Zuul 路由访问 2.具体内容 在现在为止所有的微服务都是通过 Eureka 找到的,但是在很多的开发之中为了规范微服务的使用,提供有一个路由的处理控制组件:Zuul,也就是说 Zuul ...

  10. nltk-贝叶斯分类器

    本人小白一枚,专业统计,之前做过质量工程,现转行将近一年,开始记录我的学习过程及踩过的坑. 第一篇:用贝叶斯分类器(本文使用NLTK中的NaiveBayesClassifier)将5000多个样本进行 ...