首页
社区
课程
招聘
[原创]386保护模式[学习一、二]
发表于: 2008-4-5 10:53 16156

[原创]386保护模式[学习一、二]

2008-4-5 10:53
16156

看雪的朋友门我来鸟。。
在编程区里:看给位大侠将的什么中断,内核,听得我直发毛。。
因为有很多专业词汇,内核知识。。从来没听过不知道大侠们在讲什么!!!
那么本人不甘心 。我就从头开始学吧
..现在那点笔记出来跟大家分享一下!!!
由于是笔记,也许记得有点糟糕!!

呵呵,来看雪总想发点东西,!!请见谅啊!!!

【保护模式编程、一】
【8086模式编程】

如果想更深、更亲近的了解电脑软件。

那么学习cpu是你的必选!!

386是CPU史的一大转折点,那386做基础课是最好不过了。那么我们将开始进行学习之旅!!!

大家跟我一块学习吧,呵呵!!!

一、准备工作

1、NASM 编译环境

2、虚拟机

Virtual PC(Windows平台 ,执行比较快,即模拟又虚拟硬件)、

WMWarve(WIndows平台 虚拟硬件,)、

Bochs(支持Windows平台、也支持在Linux平台上运行 有RPM版本的)

我们这些生长在Windows这棵大树下的朋友们,还是用Virtual PC吧.。

3、写虚拟启动镜像文件的程序

:不知道我观察的对不对?

用Nasm 编译一个bin 然后将它转换为img 镜像文件的时候。

只要文件大小符合软驱的标准就能启动。那么就代表a.bin 与a.img 文件的内容一模样就是文件大小不一样!

我是不太了解镜像文件格式.我用的是Virtual PC。

二、开始接触引导程序

1、Com文件

Com文件是纯二进制的文件,也是直接与Cpu交换的顺序指令文件。

Com文件的大小是有限制的,不能超过64KB.

因为8086时代的CPU地址线是20位的,20位能表达的数值也就是fffffh(1MB )

而寄存器最高也只是16位,无法用5个F的形式来表达地址,所以用CS(段基地址)*16:IP(偏移地址)来寻址!

80386后通用寄存器都得到了32位扩展! 而Cpu地址线也得到了32位的扩展。

引导程序前期是需要进入实模式的,因为这是硬件上的限制是IA32的限制。

386cpu只有两种模式: 实模式与保护模式!!!!,,

2、引导程序

引导程序也是有限制的,这个限制是靠Bios处理的,

开机后Bios经过自检后,会从软驱或者硬盘的0面0磁道1扇区搜寻一个程序文件。

该文件的数据必需是等于512Byte,并且以aa55h结尾的(高高低低)。那么bios会认为它是引导程序,

这个时候就会把该512byte 装载到内存7c00开始处。然后将主控权交给程序的第一行代码。

那么这个时候程序脱离Bios的控制。Cpu将执行程序的代码.

三、写一个引导程序

引导程序可以说是非常简单:

1、boot.asm(nasm 的源文件如下)

;-----------------------欲编译,这里改成100h就是com程序 -------------------------------------------

%define _BOOT_DEBUG_   ;做调试的时候用100h
%ifdef _BOOT_DEBUG_
org   0100h
%else
org 07c00h    ; 告诉编译器 以下代码段将从07c00h内存地址处开始
%endif
mov    ax,cs             ;让数据段与附加段寄存器跟代码段一样,因为COM代码数据是混合.

mov   ds,ax

mov   es,ax

call   _HelloWorld        ;让程序显示一个HelloWorld

jmp      $                  ;$表示当前地址 无限循环

_HelloWorld:

mov ax,strHello        ;取得字符串的地址

mov bp,ax              ;给堆栈基寄存器

mov cx,strLen

mov   ax,1301H       ;ah代表功能号

mov bx, 000ch   ; 页号为0(BH = 0) 黑底红字(BL = 0Ch,高亮

mov   dx,0001h      ;显示的行与列

int       10h            ;bios 10h显示中断
ret

strHello:     db "Hello World"
strLen     equ $ - strHello
times 510-($-$$) db 0 ; times重复定义 510-($-$$)个 $$ 表示段的起始地址

dw     0xAA55

那么引导程序完成了,

用nasm boot.asm -o a.com

就可以运行看效果.,

如改成引导程序只需把%define _DEBUG_BOOT_注释 然后nasm boot.asm -o a.bin

然后用工具将a.bin 转换成软驱大小的镜像文件 载入虚拟机启动就可以.

【保护模式编程、二】

【80386保护模式编程】

8086到80386的跳转,80386与8086在硬件上的区别在这就不说了!!

那么80386与8086在软件逻辑上面的区别就是:

8086是实模式,而80386 不仅包括实模式,而且还可以进入保护模式!!!

保护模式不仅不受64KB内存寻址的限制,而且还拥有4GB的寻址空间。这是因为386扩展了20地址线,将它扩展成32位了(32位能表达的字节数就是4GB).

此时的段寄存器不再是段基地了,而被叫做是选择子 ,存放的是一个段描述符的索引值.

而我们的通用寄存器与EIP 也是32位的,可以表达4GB地址!

不过计算机开机后,CPU默认是实模式。这就需要我们编程手动转换到386.

那么我们该怎么去做呢:

一、【准备GDT (全局描述符表)】

首先我们需要准备GDT结构体,它是386保护模式必须的东西。

全局描述符寄存器GDTR 指向的是所有段描述符表的信息.

前面提到得段选择子索引,指得就是指向段描述符的索引.

段描述符是8个字节的结构体、里面存放着段的段界限、段基址、段属性等信息.

LDTR寄存器是指向局部某一个段的描述符表。

段描述符表结构体用一个宏来表示(注意段1 2表示同一个段描述内容被分开来放的):

【段界限】20位、表示段的总长度 这里并不是地址,而是段的字节长度。

【段基址】32位、表示物理地址

【段属性】12位. 系统、门、数据等属性

%macro Descriptor 3     ; 有三个参数:【段界限】、【段基址】、【段属性】

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

看似很简单的结构体 理解起来可不是那么简单!

【Descriptor结构体】有8个字节。

1、【第1、2字节】组合(word) 表示该段的[段界限①], dw %2 & 0FFFFh ;引用第二个参数去掉高16位

2、【第3、4、5字节】组合表示该段的[段基址①],dw %1 & 0FFFFh ;先得到第一个参数(段基址)低WORD。

3、接着把第5个字节赋值,db (%1 >> 16) & 0FFh 去掉第3第4个字节的内容.再把剩下的字节赋值

4、【第6个字节】是与【第7个字节】组合的内容可就更复杂了:

【第6个字节】的内容:

【7(p)        6(DPL)         5(DPL)          4(S)             3(Type)        2(Type)        1(Type)        0(Type)】

0-3位表示:[段属性]、说明存储段描述符所描述的存储段的具体属性。

4位表示:说明描述符的类型, 对于存储段描述符而言,S=1表示是系统段描述符。

5-6位表示:DPL 该段的特权级别也就是Ring 0-3;

7位表示:P:    存在(Present)位。
;   P=1 表示描述符对地址转换是有效的,即描述的段在内存当中.
;   P=0 表示描述符对地址转换无效,即该段不存在。使用该描述符进行内存访问时会引起异常

【第7个字节】的内容:

【7(G)        6(D)         5(0 )          4(AVL)   3(段界限)   2(段界限)    1(段界限)   0(段界限)】

0-3位表示:[段界限②]

4位表示:软件可利用位。80386对该位的使用未做规定,Intel公司也保证今后开发生产的处理器只要与80386兼容,就不会对该位的使用做任何定义或规定。

5位表示:0 ;Intel资料也没表示

6位表示:是一个很特殊的位,在描述可执行段、向下扩展数据段或由SS寄存器寻址的段(通常是堆栈段)的三种描述符中的意义各不相同,通常置1

7位表示:    段界限粒度(Granularity)位。
          G=0 表示界限粒度为字节;
           G=1 表示界限粒度为4K 字节。
          注意,界限粒度只对段界限有效,对段基地址无效,段基地址总是以字节为单位。

那么这段宏dw ((%2 >> 8) & 0F00h) | (%3 & 0F0FFh)表示:

取[段界限]参数除去低16位取 高4位,得到【段界限②】

取[段属性]参数的低8位 12-15位(AVL属性等)

属性 1 + 段界限 2 + 属性 2

【第8个字节】的内容:

[段基址②] 、db (%1 >> 24) & 0FFh 取基地址参数的最高8位

那么一个Descriptor 结构体就这样成形了.

二、【编写程序跳转到保护模式】

%include "386.inc"         ;是Descriptor结构体宏
;%define _DEBUG_BOOT_
%ifdef _DEBUG_BOOT_
    org 0100h
%else
    org 07c00h
%endif

jmp LABEL_BEGIN
[SECTION .gdt]          ;全局描述符数据段
                                     ; 段基址,      段界限     , 属性
LABEL_GDT:     Descriptor        0,                0, 0         ;空描述符
LABEL_DESC_CODE32: Descriptor        0, SegCode32Len - 1, DA_CR | DA_32    ;代码段描述符
LABEL_DESC_DATA:   Descriptor    0,SegDataLen - 1,DA_DRW      ;数据段
LABEL_DESC_VIDEO:   Descriptor    0B8000h,0FFFFh,DA_DRW      ;显示器内存段 由于DOS中断不能随意使用了,,只能输出到显示缓冲区
; GDT 结束

GdtLen equ       $ - 1
GdtPtr   dw GdtLen - 1     ;GDT 的段界限,
     dd   0      ;GDT基地址

; GDT 选择子
SelectorCode32   equ LABEL_DESC_CODE32 - LABEL_GDT     ;代码相对全局描述符起始地址的EA值
SelectorData   equ LABEL_DESC_DATA - LABEL_GDT     ;数据段
SelectorVideo   equ LABEL_DESC_VIDEO - LABEL_GDT     ;显示数据段

[SECTION .s16]     ;16位代码段
[BITS 16]     ;BITS指出处理器的模式 是16位
LABEL_BEGIN:      
mov ax, cs
mov ds, ax
mov es, ax
mov ss, ax       ;初始化段寄存器

;初始化数据
mov eax,strHello
mov word[LABEL_DESC_DATA + 2],ax
mov byte[LABEL_DESC_DATA + 4],al
shr eax,16
mov byte[LABEL_DESC_DATA + 7],ah
; 初始化并把32位段代码的基地址分配给段描述符
mov eax, LABEL_CODE32    ;
mov word [LABEL_DESC_CODE32 + 2], ax          ;ax
shr eax, 16
mov byte [LABEL_DESC_CODE32 + 4], al
mov byte [LABEL_DESC_CODE32 + 7], ah

; 为加载 GDTR 作准备

mov eax, LABEL_GDT  
mov dword [GdtPtr + 2], eax ;得到GDT基地址  

; 加载 全局描述符的信息结构体 到GDTR
lgdt [GdtPtr]

CA20    ;利用键盘端口打开A20地址线

; 将CRO的PE位 也就是 0位 置1 那么就进入386模式了
mov eax, cr0
or eax, 1
mov cr0, eax

jmp dword SelectorCode32:0 ;
;执行这一句会把 SelectorCode32 装入 cs, 并跳转到 Code32Selector:0 处
;这个描述符集合是以一个空描述符开始得,现在LABEL_DESC_CODE32描述符的索引值因该是8,
;所以SelectorCode32的值应该就是LABEL_DESC_CODE32的索引值,Code32Selector:0当中的0是指LABEL_DESC_CODE32 的段基址+ 0
;那么在打开cr0的PE位后,这个JMP指令不再是直接跳到段地址去了;
;而是去GDTR全局描述符寄存器当中去找这个当前CS的索引,当前段基址+偏移 的内存地址了。

[SECTION .s32]; 32 位代码段. 由实模式跳入.
[BITS 32]
LABEL_CODE32:
;保护模式的死循环

mov ax, SelectorVideo
mov gs, ax    ; 视频段选择子(目的)

mov edi, (80 * 10 + 9) * 2 ; 屏幕第 10 行, 第 0 列。
mov ah, 1Ch    ; 0000: 黑底    1100: 红字
mov esi,0

mov ds,SelectData

mov ecx,11
vi:
lodsb
mov [gs:edi], ax
inc edi
LOOPNZ vi
; 到此停止
jmp $

SegCode32Len equ $ - LABEL_CODE32

[SECTION .data]    ;数据段
strHello:     db "Hello World"
SegDataLen   equ $- strHello

【总结】

编写一个386 程序主要用的步骤

1、准备GDT描述符集合结构体

2、用lgdt [gdtPtr] 载入 gdtPtr 这6个字节的结构体,,低字是描述符集合的界限 也就是集合总长度,高双字是描述符集合的基地址.

3、打开A20地址线

有一种方法是向键盘端口 IO,

4、置CR0的PE位 即0位为1

5、JMP [段索引]:[段基址偏移]

呵呵 接下来继续学习啊!!!


[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

收藏
免费 7
支持
分享
最新回复 (14)
雪    币: 709
活跃值: (2420)
能力值: ( LV12,RANK:1010 )
在线值:
发帖
回帖
粉丝
2
汗,这是“操作系统”书上的基本概念吧,也是学习asm必须掌握的内容。

和驱动啊,内核啊还不是有很大的关联。

支持一下

你是hackbase编程区的“解禁”兄吧。哈哈,进步很快哦,加油~~~~
2008-4-5 11:04
0
雪    币: 290
活跃值: (11)
能力值: ( LV9,RANK:170 )
在线值:
发帖
回帖
粉丝
3
没学过啊。。。所以我觉得自己不够系统嘛。。。

谢谢你啊 呵呵,,  

你现在是大侠级别的了,.
2008-4-5 11:10
0
雪    币: 581
活跃值: (149)
能力值: ( LV12,RANK:600 )
在线值:
发帖
回帖
粉丝
4
我也支持个/..........汇编看得俺头晕啊
2008-4-5 11:14
0
雪    币: 290
活跃值: (11)
能力值: ( LV9,RANK:170 )
在线值:
发帖
回帖
粉丝
5
我是起点比较低的菜鸟啊!!!!

没办法慢慢学咯。。
对 我是解禁。。。。最近很忙a ..
2008-4-5 11:14
0
雪    币: 709
活跃值: (2420)
能力值: ( LV12,RANK:1010 )
在线值:
发帖
回帖
粉丝
6
=。=|

呵呵~

忙点儿好啊
2008-4-5 11:22
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7
然后用工具将a.bin 转换成软驱大小的镜像文件


能否给个工具名称我
2008-4-5 12:07
0
雪    币: 290
活跃值: (11)
能力值: ( LV9,RANK:170 )
在线值:
发帖
回帖
粉丝
8
找那种制作虚拟软驱的软件啊 ,,search 一下啊!!!

我这个是手动编写的,。

其实就是把文件大小改了下,,,内容没变

你可以自己写个文件处理程序  自己可以编写一个.img
FloppyWriter  你搜搜吧 呵呵
2008-4-5 12:30
0
雪    币: 1098
活跃值: (193)
能力值: (RANK:210 )
在线值:
发帖
回帖
粉丝
9
这个工具在《自己动手写操作系统》的光盘里有的。源码也有的。
2008-4-5 13:47
0
雪    币: 321
活跃值: (271)
能力值: ( LV13,RANK:1050 )
在线值:
发帖
回帖
粉丝
10
嗯,看过《自己动手写操作系统》这本书,支持楼主。
建议:楼主把自己动手写操作系统的代码,改写成masm的。然后把代码附上,可以专门作成一个专题啊。:)
2008-4-5 14:41
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
11
完全不懂啊 。。
2008-4-5 14:42
0
雪    币: 290
活跃值: (11)
能力值: ( LV9,RANK:170 )
在线值:
发帖
回帖
粉丝
12
呵呵!!!
哈哈!!
专题不敢当啊,,过段时间我把他这本书 完全熟悉了,在说!1
于渊大哥 跟我是一家子啊 呵呵!!!        要好好向他学习                           
2008-4-5 15:44
0
雪    币: 298
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
13

苦于没时间。。。每天CAD。。。
2008-4-7 00:28
0
雪    币: 485
活跃值: (12)
能力值: ( LV9,RANK:490 )
在线值:
发帖
回帖
粉丝
14
好东西,正是我想学习的。希望有下文。。
2008-4-7 00:36
0
雪    币: 224
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
15
果然大神
2017-5-10 13:25
0
游客
登录 | 注册 方可回帖
返回
//