自制操作系统Antz(2)——进入保护模式 (上) jmp到保护模式
Antz系统更新地址: https://www.cnblogs.com/LexMoon/category/1262287.htm
Linux内核源码分析地址:https://www.cnblogs.com/LexMoon/category/1267413.html
0. 如果你不知道什么是保护模式
你可能不知道什么是保护模式,没有关系,在你知道之前让我们先来看一段代码,如果你没有接触过这些内容,可能会觉得一头雾水,不知所云,不要紧,我们可以一点一点来分析。
os.asm :
%include "pm.inc" ; 常量, 宏, 以及一些说明 org 0100h
jmp LABEL_BEGIN [SECTION .gdt]
; GDT
; 段基址, 段界限 , 属性
LABEL_GDT: Descriptor , , ; 空描述符
LABEL_DESC_CODE32: Descriptor , SegCode32Len - , DA_C + DA_32 ; 非一致代码段,
LABEL_DESC_VIDEO: Descriptor 0B8000h, 0ffffh, DA_DRW ; 显存首地址
; GDT 结束 GdtLen equ $ - LABEL_GDT ; GDT长度
GdtPtr dw GdtLen - ; GDT界限
dd ; GDT基地址 ; GDT 选择子
SelectorCode32 equ LABEL_DESC_CODE32 - LABEL_GDT
SelectorVideo equ LABEL_DESC_VIDEO - LABEL_GDT
; END of [SECTION .gdt] [SECTION .s16]
[BITS ]
LABEL_BEGIN:
mov ax, cs
mov ds, ax
mov es, ax
mov ss, ax
mov sp, 0100h ; 初始化 位代码段描述符
xor eax, eax
mov ax, cs
shl eax,
add eax, LABEL_SEG_CODE32
mov word [LABEL_DESC_CODE32 + ], ax
shr eax,
mov byte [LABEL_DESC_CODE32 + ], al
mov byte [LABEL_DESC_CODE32 + ], ah ; 为加载 GDTR 作准备
xor eax, eax
mov ax, ds
shl eax,
add eax, LABEL_GDT ; eax <- gdt 基地址
mov dword [GdtPtr + ], eax ; [GdtPtr + ] <- gdt 基地址 ; 加载 GDTR
lgdt [GdtPtr] ; 关中断
cli ; 打开地址线A20
in al, 92h
or al, 00000010b
out 92h, al ; 准备切换到保护模式
mov eax, cr0
or eax,
mov cr0, eax ; 真正进入保护模式
jmp dword SelectorCode32: ; 执行这一句会把 SelectorCode32 装入 cs, 并跳转到 Code32Selector: 处
; END of [SECTION .s16] [SECTION .s32]; 位代码段. 由实模式跳入.
[BITS ] LABEL_SEG_CODE32:
mov ax, SelectorVideo
mov gs, ax ; 视频段选择子(目的) mov edi, ( * + ) * ; 屏幕第 行, 第 列。
mov ah, 0Ch ; : 黑底 : 红字
mov al, 'P'
mov [gs:edi], ax ; 到此停止
jmp $ SegCode32Len equ $ - LABEL_SEG_CODE32
; END of [SECTION .s32]
pm.inc节选 :
; 描述符
; usage: Descriptor Base, Limit, Attr
; Base: dd
; Limit: dd (low bits available)
; Attr: dw (lower bits of higher byte are always )
%macro Descriptor
dw % & 0FFFFh ; 段界限 ( 字节)
dw % & 0FFFFh ; 段基址 ( 字节)
db (% >> ) & 0FFh ; 段基址 ( 字节)
dw ((% >> ) & 0F00h) | (% & 0F0FFh) ; 属性 + 段界限 + 属性 ( 字节)
db (% >> ) & 0FFh ; 段基址 ( 字节)
%endmacro ; 共 字节
读完之后你可能一头雾水,但是这段代码已经完成了实模式到保护模式的转换。
nasm os.asm -o os.com
先使用nasm编译os.asm生成os.com文件。
然后使用DOS-BOX打开。
    
屏幕中显示了一个黑底红字的字符 "P"
接下来分析上面的代码:
[SECTION.gdt]段中有三个Descriptor,是一个叫GDT的数组。接下来的GdtLen是GDT的长度。GdtPtr也是个小的数据结构,它有6个字节,前两个字节是GDT的长度GdtLen,后四个字节是GDT的基地址。
另外定义了两个SelectorCode32,SelectorVideo的常量。暂时可不管它。
[BITS 16]明确指明了它是一个16位的代码段,它修改了一些GDT的值,然后执行了一些不常见的指令,最后通过jmp指令进行了跳转。jmp Selectorcode32:0 ,执行这一句会真正进入保护模式,把 SelectorCode32 装入 cs, 并跳转到 SelectorCode32:0 处 。也就是第三个section,即[SECTION.s32]中,这个段是32位的,在结束处的 jmp $ 进入了无限循环。
你可能会疑惑什么是GDT,那些看上去怪怪的指令到底做了什么。它们的内容如下:
1)定义一个叫做GDT的数据结构。
2)后面的16位代码进行了一些与GDT有关的操作。
3)程序最后跳到了32位代码中做了一点操作显存的工作。
那么GDT是什么?它是用来干什么的呢? 程序对GDT做了什么? jmp SelectorCode32:0和我们之前的jmp有什么不同呢?
有了这些问题,我们现在就可以出发去了解保护模式了。
1. GDT
CPU有两种工作模式:实模式和保护模式。
当我们开机时,开始的CPU是工作在实模式下的,经过某种机制之后,才进入保护模式。在保护模式下,CPU有着巨大的寻址模式,并为操作系统提供了更好的硬件模式。 那么从实模式到保护模式的转换其实就类似于政权的更替,开机时是在实模式下,就像皇帝A在执政,他有他的政策。后来通过了一种转换,类似于革命,皇帝B登基,新皇帝登基的那一刻就是一个历史性的 jmp , 然后开始了皇帝B的统治,他也有了他的一套全新的政策。当然新政策比老政策好得多,虽然他变复杂了,这套新政策就是保护模式。
先来回顾一些旧政策,实模式。一个地址是由段地址和偏移地址两部分组成的,物理地址遵从这样的计算公式。
物理地址 = 段地址 x 16 + 偏移地址
其中段值和偏移都是16位的。
从386开始的32位时代,寻址空间可以达到4GB,所以16位寄存器已经不够用了。
在实模式下,16位寄存器需要“段:偏移”才有 1MB的寻址能力,如今我们有了32位寄存器,一个寄存器就有了4GB的寻址哪里。那么是不是段值就可以被抛弃了呢?
其实不然,新政策下仍然使用 “SEG:OFFSET”的形式表示 , 只不保护模式下的段值概念发生了根本性的变化。实模式下,段值还可以看作是地址的一部分。而保护模式下,虽然段值仍然由原来的16位的CS,DS等寄存器表示,但此时他仅仅变成了一个索引,这个索引指向了一个数据结构的表项,其中详细定义了段的起始地址,界限,属性等内容。这个数据结构就是GDT(也可能是LDT)。GDT中的表项也有一个专门的名字,叫做描述符(Descriptor)。
    
也就是说,GDT的作用是用来提供段式存储机制,这种机制是通过段寄存器和GDT中的描述符共同提供的。

之前代码中的宏定义Descriptor这个宏用比较自动化的方法把段基址,段界限和段属性安排在描述符中合适的位置。
再来看看之前代码中定义的一个Descriptor数组,LABEL_GDT ,LABEL_DESC_CODE32 , LABEL_DESC_VIDEO 。
LABEL_DESC_VIDEO的段基址0B8000h,这个描述符指向的正是显存。
那么CS,DS等段寄存器如何与这些段对应起来呢?
在[SECTION.32]中有两句代码是:
mov ax,SelectorVideo
mov gs,ax
段寄存器gs的值变成了SelectorVideo, SelectorVideo的定义是:
SelectorVideo equ LABEL_DESC_VIDEO - LABEL_GDT
直观的看,它好像是DESC_VIDEO这个描述符相对GDT基址的偏移。实际上它有一个专门的名称叫做选择子,它也表示一个偏移,而是稍微复杂一点。
mov [gs:edi] , ax
gs的值是SelectorVideo,它只是对应显存的描述符LABEL_DESC_VIDEO, 这条指令把ax的值写入显存中偏移位edi的位置。
到了这里,可以想到,既然[SECTION.S32]是32位程序,并且在保护模式下执行,那么[SECTION.s16]的任务一定是从实模式向保护模式跳转了。
2. 实模式到保护模式,不一般的 jmp
在[SECTION.s16]段最后。
jmp dword SelectorCode32:0 ;执行这一句会把 SelectorCode32 装入 cs, 并跳转到 SelectorCode32:0 处
跳转的目标是描述符 DESC_CODE32对应的段的首地址,即标号LABEL_SEG_CODDE32处。
此时,新皇帝登基,开始了保护模式。
不过,这个jmp比看起来还要复杂一点,因为它不得不放在16位的段中,目标地址却是32位。从这一点来看,它是混合16位和32位代码。所以写为jmp SelectorCode32:0 是不严谨的,因为偏移地址是32位的,这样编译出来的只是16位的代码。假设目标地址的偏移不是0,而是一个32位的值,比如 jmp SelectorCode32:0x12345678,则编译之后偏移会被截断,只剩下0x5678。
所以需要加上dword,但Nasm允许加在整个地址之前,就是我们之前写的那样,也就是我们为什么那样写了。
那么进入保护模式的步骤就是:
1)准备GDT
2)用 lgdt 加载 gdtr
3)打开 A20
4) 跳转,进入保护模式
自制操作系统Antz(2)——进入保护模式 (上) jmp到保护模式的更多相关文章
- 自制操作系统Antz  -- 系列文章
		自制操作系统Antz day10——实现shell(上) AntzUhl 2018-10-10 16:25 阅读:192 评论:0 Linux内核源码分析 day01——内存寻址 AntzUhl ... 
- 自制操作系统Antz(5)——深入理解保护模式与进入方法
		Antz系统更新地址: https://www.cnblogs.com/LexMoon/category/1262287.html Linux内核源码分析地址:https://www.cnblogs. ... 
- 自制操作系统Antz(3)——进入保护模式 (中) 直接操作显存
		Antz系统更新地址: https://www.cnblogs.com/LexMoon/category/1262287.html Linux内核源码分析地址:https://www.cnblogs. ... 
- 自制操作系统Antz(4)——进入保护模式 (下) 实现内核并从硬盘载入
		Antz系统更新地址: https://www.cnblogs.com/LexMoon/category/1262287.html Linux内核源码分析地址:https://www.cnblogs. ... 
- 自制操作系统Antz(13) 显示图片
		显示图片只是在多媒体课上看着bmp格式图片的突发奇想,然后就实现在了我自己的操作系统 Antz系统更新地址 Linux内核源码分析地址 Github项目地址 效果图: 显示图片的原理 在之前显卡操作时 ... 
- 《30天自制操作系统》笔记3 --- (Day2 上节)完全解析文件系统
		Day2 汇编语言学习与Makefile入门 本文仅带着思路,研究源码里关于文件系统的参数 关于day2主程序部分及更多内容,请看<30天自制操作系统>笔记 导航 发现学习中的变化 源码差 ... 
- 自制操作系统Antz(7)——实现内核 (上)
		Antz系统更新地址: https://www.cnblogs.com/LexMoon/category/1262287.html Linux内核源码分析地址:https://www.cnblogs. ... 
- 自制操作系统Antz(10)——实现shell(上)
		我已经规范了系统代码风格,类似于按照linux分包,把各部分功能区分开了 Antz系统更新地址 Linux内核源码分析地址 Github项目地址 在之前的任务中,我们已经通过直接操作显卡驱动完成了简单 ... 
- 自制操作系统Antz(9)——实现内核 (下) 实现图形化界面
		Antz系统更新地址: https://www.cnblogs.com/LexMoon/category/1262287.html Linux内核源码分析地址:https://www.cnblogs. ... 
随机推荐
- 【腾讯云的1001种玩法】 Laravel 整合万向优图图片管理能力,打造高效图片处理服务
			版权声明:本文由白宦成原创文章,转载请注明出处: 文章原文链接:https://www.qcloud.com/community/article/574549001488234358 来源:腾云阁 h ... 
- 机器人学 —— 机器人感知(Location)
			终于完成了Robotic SLAM 所有的内容了.说实话,课程的内容比较一般,但是作业还是挺有挑战性的.最后一章的内容是 Location. Location 是 Mapping 的逆过程.在给定ma ... 
- PHP 高效导入导出Excel(csv)方法之fgetcsv()和fputcsv()函数
			CSV,是Comma Separated Value(逗号分隔值)的英文缩写,通常都是纯文本文件. 一.CSV数据导入函数fgetcsv() fgetcsv() 函数从文件指针中读入一行并解析 CSV ... 
- java可供判断某字符串是什么编码的一行代码
			System.out.println("中文"); System.out.println("中文".getBytes()); System.out.printl ... 
- python  接口自动化测试(六)使用unittest   批量用例管理
			我们日常项目中的接口测试案例肯定不止一个,当案例越来越多时我们如何管理这些批量案例?如何保证案例不重复?如果案例非常多(成百上千,甚至更多)时如何保证案例执行的效率?如何做(批量)测试数据的管理?如何 ... 
- mybatis04--Mapper动态代理实现
			通过之前的操作,我们发现dao的实现类其实并没有做什么实质性的工作,仅仅是通过sqlSession的相关API定位到StudentMapper映射文件 中的ID中的sql语句,其实真正操作DB的是ma ... 
- java基础学习总结——流
			一.JAVA流式输入/输出原理 
- python 中字符串的格式化
			# 字符串格式化name = input("name:")age = int(input("age:"))job = input("job:" ... 
- Web前端开发推荐书籍
			Web前端开发推荐书籍 前言 学校里没有前端的课程,那如何学习JavaScript,又如何使自己成为一个合格的前端工程师呢? 读 书吧~相对于在网上学习,在项目中学习和跟着有经验的同事学习,书中有着相 ... 
- Codeforces 670 - A/B/C/D/E/F - (Done)
			链接:https://codeforces.com/contest/670 A - Holidays - [水] AC代码: #include<bits/stdc++.h> using n ... 
