转载请注明出处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 |
数据 |
向下扩展,可读/写,已访问 |