操作系统:从实模式切换到保护模式
来源:互联网 发布:大拿监控软件下载 编辑:程序博客网 时间:2024/05/16 10:16
转载请注明出处http://blog.csdn.net/sx154893743/article/details/12378053
最近一直在学着自己动手制作操作系统,昨天总算是有了点成效,现作博客一篇整理下思路。
本次提供的代码是从实模式到保护模式的切换,并在保护模式下打印"Hello, world!"。
实验环境
lenovo G470
ubuntu11.10
bochs-2.4.6
环境搭建请参考http://blog.csdn.net/sx154893743/article/details/9341621,这里只说一点需要注意的地方:如果bochs是直接用apt-get install以命令行形式下载安装的话,将没有调试功能;若需要调试功能,请到bochs官网下载压缩包后安装。
相关知识
从实模式切换到保护模式,主要有以下5个步骤:
1、准备GDT
2、用lgdt指令加载gdtr
3、打开A20地址线
4、将cr0寄存器的最后一位(PE位)置1:
PE位为0时,CPU运行在实模式下;
PE位为1时,CPU运行在保护模式下.
5、跳转,进入保护模式
GDT
GDT全称为Global Descriptor Table,即全局描述符表。实模式下,物理地址=段值*16+偏移值,寻址能力仅仅达到1MB。这显然不能满足人们的需求,所以后来引入了保护模式。在保护模式下,虽然段值仍然由原来16位的cs、ds等寄存器表示,但此时它仅仅变成了一个索引,这个索引指向一个数据结构的一个表项,表项中详细定义了段的起始地址、界限、属性等。这个数据结构就是GDT。GDT的作用就是用来提供段式存储机制,现在我们来看看描述符的结构:
这里各个字段、标志的含义看起来很复杂,实际上需要我们了解的只有段界限、段基址、D/B位、S位以及TYPE。在本次实验代码中,我们可通过宏设置好各描述符的段界限和段属性:
%macro Descriptor 3
dw %2 & 0FFFFh ;段界限1
dw %1 & 0FFFFh ;段基址1
db (%1 >> 16) & 0FFh ;段基址2
dw ((%2 >> 8) & 0F00h) | (%3 & 0F0FFh) ;属性1+段界限+属性2
db (%1 >> 24) & 0FFh ;段基址3
%endmacro
至于段基址,则可由具体代码填充进去。
段属性如下:
DA_32 equ 4000h ;32位段(D/B位为1,其他为0)
DA_C equ 98h ;存在的只执行代码段(1001:P=1存在,
;ring0级,s=1数据段/代码段描述符;
;1000:只执行)
DA_DRW equ 92h ;存在的可读写数据段(1001;
;0010:读/写)
这里只列出了很少一部分属性,其他属性可依据需求自由设置,下面是TYPE描述符类型:
数据段和代码段描述符:(S=1)
类型(TYPE)字段
描述符类型
说明
十进制
位11
位10
位9
位8
E
W
A
0
0
0
0
0
数据
只读
1
0
0
0
1
数据
只读,已访问
2
0
0
1
0
数据
可读/写
3
0
0
1
1
数据
可读/写,已访问
4
0
1
0
0
数据
向下扩展,只读
5
0
1
0
1
数据
向下扩展,只读,已访问
6
0
1
1
0
数据
向下扩展,可读/写
7
0
1
1
1
数据
向下扩展,可读/写,已访问
C
R
A
8
1
0
0
0
代码
仅执行
9
1
0
0
1
代码
仅执行,已访问
10
1
0
1
0
代码
执行/可读
11
1
0
1
1
代码
执行/可读,已访问
12
1
1
0
0
代码
一致性段,仅执行
13
1
1
0
1
代码
一致性段,仅执行,已访问
14
1
1
1
0
代码
一致性段,执行/可读
15
1
1
1
1
代码
一致性段,执行/可读,已访问
系统段和门描述符:(S=0)
类型(TYPE)字段
说明
十进制
位11
位10
位9
位8
0
0
0
0
0
Reserved
保留
1
0
0
0
1
16-Bit TSS (Available)
16位 TSS(可用)
2
0
0
1
0
LDT
LDT
3
0
0
1
1
16-Bit TSS (Busy)
16位 TSS(忙)
4
0
1
0
0
16-Bit Call Gate
16位调用门
5
0
1
0
1
Task Gate
任务门
6
0
1
1
0
16-Bit Interrupt Gate
16位中断门
7
0
1
1
1
16-Bit Trap Gate
16位陷阱门
8
1
0
0
0
Reserved
保留
9
1
0
0
1
32-Bit TSS (Available)
32位TSS(可用)
10
1
0
1
0
Reserved
保留
11
1
0
1
1
32-Bit TSS (Busy)
32位TSS(忙)
12
1
1
0
0
32-Bit Call gate
32位调用门
13
1
1
0
1
Reserved
保留
14
1
1
1
0
32-Bit Interrupt Gate
32位中断门
15
1
1
1
1
32-Bit Trap Gate
32位陷阱门
选择子
我们已经大致了解了GDT,那么计算机如何找到GDT的表项(描述符)呢?这里我们就需要用到选择子了。在保护模式下,CS等段寄存器会载入相应段的选择子,计算机会通过选择子来判断我们需要的是哪个描述符。段选择子结构如下 BYTE15——————BYTE3
BYTE2
BYTE1——BYTE0
描述符索引
TI
RPL
TI标识是全局描述符表索引还是局部描述符表索引:为0时表示GDT,为1时表示LDT;RPL为特权级,表示ring0~ring3。
寄存器gdtr和cr0
我们在用GDT前,需要先找到GDT,这里就需要用到一个特殊的寄存器:gdtr。gdtr示意图如下:| 32位基地址 | 16位界限 |
cr0寄存器最后一位为PE位:PE=0时,CPU运行在实模式下;PE=1时,CPU运行在保护模式下。
实验步骤
1、进入自己的工作目录,也就是你准备编写代码的那个目录,在终端输入bximage命令生成一张虚拟软盘。如下图:
2、编写代码,保存文件名boot_lv.asm。
; ====================================================
; 保护模式
; nasm boot_lv.asm -o boot_lv.bin
; dd if=boot_lv.bin of=a.img bs=512 count=1 conv=notrunc
; 主要过程:
; 1、准备GDT
; 2、用lgdt指令加载gdtr
; 3、打开A20地址线
; 4、将cr0寄存器的最后一位(PE位)置1:
; PE位为0时,CPU运行在实模式下;
; PE位为1时,CPU运行在保护模式下.
; 5、跳转,进入保护模式
;
; Made by Lv
; 2013.10.06
; 完成基本功能:实模式到保护模式,并打印字符串
; ====================================================
;描述符
;Descriptor base, limit, attr
;base: dd ;段基址
;limit: dd ;段界限
;attr: dw
;[SECTION .macro]
%macro Descriptor 3
dw %2 & 0FFFFh ;段界限1
dw %1 & 0FFFFh ;段基址1
db (%1 >> 16) & 0FFh ;段基址2
dw ((%2 >> 8) & 0F00h) | (%3 & 0F0FFh) ;属性1+段界限+属性2
db (%1 >> 24) & 0FFh ;段基址3
%endmacro
DA_32 equ 4000h ;32位段(D/B位为1,其他为0)
DA_C equ 98h ;存在的只执行代码段(1001:P=1存在,
;ring0级,s=1数据段/代码段描述符;
;1000:只执行)
DA_DRW equ 92h ;存在的可读写数据段(1001;
;0010:读/写)
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
LABEL_DESC_DATA: Descriptor 0, DataLen - 1, DA_DRW
GdtLen equ $ - LABEL_GDT ;GDT长度
GdtPtr dw GdtLen - 1 ;GDT界限
dd 0 ;GDT基址
SelectorCode32 equ LABEL_DESC_CODE32 - LABEL_GDT
SelectorVideo equ LABEL_DESC_VIDEO - LABEL_GDT
SelectorData equ LABEL_DESC_DATA - LABEL_GDT
[SECTION .data]
ALIGN 32
[BITS 32]
LABEL_SEG_DATA:
Message db "Hello, world!", 0
OffsetMessage equ Message - $$
DataLen equ $ - LABEL_SEG_DATA
SegDataAddress equ $$
[SECTION .s16]
[BITS 16]
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, 4
add eax, LABEL_SEG_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
;填充数据段描述符的段基址部分
xor eax, eax
mov ax, ds
shl eax, 4
add eax, LABEL_SEG_DATA
mov word [LABEL_DESC_DATA + 2], ax
shr eax, 16
mov byte [LABEL_DESC_DATA + 4], al
mov byte [LABEL_DESC_DATA + 7], ah
;填充GDTR的GDT基址部分
xor eax, eax
mov ax, ds
shl eax, 4
add eax, LABEL_GDT
mov dword [GdtPtr + 2], eax
;lgdt指令加载GDTR
lgdt [GdtPtr]
;此处注意关中断,
;因为保护模式下中断处理方式与实模式不同,不关掉中断会出错
cli
;打开A20地址线
in al, 92h
or al, 00000010b
out 92h, al
;cr0的PE位置为1,准备金如保护模式
mov eax, cr0
or eax, 1
mov cr0, eax
;进入保护模式
;保护模式下,原来的段寄存器存储变为选择子
jmp dword SelectorCode32:0
SegCode16Len equ $ - LABEL_BEGIN
[SECTION .s32]
[BITS 32]
LABEL_SEG_CODE32:
mov ax, SelectorVideo
mov gs, ax
mov ax, SelectorData
mov ds, ax
mov esi, OffsetMessage
mov edi, (80 * 0 + 0) * 2
mov ah, 0Ch ;0000:黑底 1010:红字
cld
.loop:
lodsb ;(AL)<-[ds:esi]
test al, al
jz .break
mov [gs:edi], ax
add edi, 2
jmp .loop
.break:
jmp $
SegCode32Len equ $ - LABEL_SEG_CODE32
times 510 - (SegCode32Len + SegCode16Len + DataLen + GdtLen + 36) db 0
dw 0xaa55
3、生成bin文件
nasm boot_lv.asm -o boot_lv.bin
4、将bin文件写入虚拟软盘
dd if=boot_lv.bin of=a.img bs=512 count=1 conv=notrunc
5、运行bochs,如图:
注意,这里是带调试功能的bochs,输入c,表示执行代码,直到遇到断点;q表示退出。
这里是输入不带参数的bochs,bochs会在当前目录下顺序寻找以下文件作为默认配置文件:
.bochsrc
bochsrc
bochsrc.txt
bochsrc.bxrc(仅对windows有效)
实验结果如图:
当然,在代码出现错误,无法得到想要结果的情况下,就需要用到bochs强大的调试功能了,这里只简单列出bochs部分调试指令:
行为 指令 举例
在某物理地址设置断点
b addr
b 0x30400
显示当前所有断点信息
info break
info break
继续执行,直到遇上断点
c
c
单步执行
s
s
单步执行(遇到函数则跳过)
n
n
查看寄存器信息
info cpu
r
fp
sreg
creg
info cpu
r
fp
sreg
creg
查看堆栈
print-stack
print-stack
查看内存物理地址内容
xp /nuf addr
xp /40bx 0x9013e
查看线性地址内容
x /nuf addr
x /40bx 0x13e
反汇编一段内存
u start end
u 0x30400 0x3040D
反汇编执行的每一条指令
trace-on
trace-on
每执行一条指令就打印CPU信息
trace-reg
trace-reg on
参考资料
1、《Orange's一个操作系统的实现》
2、http://baike.baidu.com/link?url=6fCMHjAh8rkkxK5PQo7HzqzOeK5sdm6Y5tSVdK0LQOlftf4AXQ36MvEA4fnvqh-xF7275mJkzwrgmjuELZW3YK
3、http://www.cnblogs.com/hongzg1982/articles/2111254.html
- 操作系统:从实模式切换到保护模式
- 操作系统引导--从实模式到保护模式
- 从实模式到保护模式
- 操作系统实验二:从实模式跳转到保护模式
- 如何调试从实模式切换到保护模式的程序
- 从实模式到保护模式(一、二)
- 《自己动手写操作系统》第三章 pmtest1——从实模式到保护模式
- 从零开始搭建环境编写操作系统 AT&T GCC (二)从实模式到保护模式
- 任务切换的方法——《x86汇编语言:从实模式到保护模式》读书笔记37
- 任务切换——《x86汇编语言:从实模式到保护模式》读书笔记38
- 文章标题x86汇编语言从实模式到保护模式-任务切换
- 从实模式到保护模式跳转的详解
- 0.11之路(四):从实模式到保护模式
- 《X86汇编语言:从实模式到保护模式》读书笔记之引言
- X86汇编语言从实模式到保护模式(一)
- 《x86汇编语言:从实模式到保护模式》读书笔记之后记
- x86从实模式到保护模式学习记录之Bochsdbg
- x86从实模式到保护模式实验说明
- C++虚函数与纯虚函数
- 【早已没了生活的理由】
- IPC$入侵:远程磁盘映射
- 自话遗传算法(带实例)
- 软件测试总结
- 操作系统:从实模式切换到保护模式
- 示例:DOS下串口通信源程序
- CSS里的 no-repeat 是什么意思
- XAML设置RadNumericUpDown的NumberFormatInfo
- mediawiki 1.21.1版本二次开发的问题集
- 细菌繁殖
- js 通过 this 传 id
- error:org.hibernate.LazyInitializationException: could not initialize proxy - no Session
- SecureCRT 实用配置