网站首页
手机版

在DOS实模式下直接存取4GB内存

更新时间:2006-01-23 14:29:36作者:未知

作为软件开发人员,大多数对于保护模式都感到神秘和不易理解。本人在开发32位微内核抢占式多线程操作系统过程中,深入了解到CPU的地址机理,在这里将分析CPU的工作原理,解开保护模式的神秘面纱,读者将会发现保护模式其实与实模式一样简单和易于控制。在此基础上用四五十行C语言程序做到进出保护模式和在实模式之下直接访问整个4GB内存空间。
虽然有许多书籍对保护模式作解释,但没有一本能简单明了地解释清楚,冗长烦杂的术语让人看着想打瞌睡,甚至还有许多用汇编写的(可能根本不能运行的)保护模式试验程序,事实上用C语言本身就可以做保护模式的进出工作。
我们可能知道CPU上电后从ROM中的BIOS开始运行,而Intel文档却说80x86CUP上电总是从最高内存下16字节开始执行,那么BIOS是处在内存的最顶端64K(FFFF0000H)还是1M之下的64K(F0000H)处呢?事实上在这两个地方都同时出现(可用后面存取4GB内存的程序验证)。
为什么?为了弄清楚以上问题,首先要了解CPU是如何处理物理地址的?真的是在实模式下用段寄存器左移4位与偏移量相加,在保护模式下用段描述符中的基地址加偏移量而两者是毫无关联的吗?答案是两者其实是一样的。当Intel把80286推出时其地址空间变成了24位,从8086的20位到24位,十分自然地要加大段寄存器才行,实际上它们都被加大了,只是由于保护的原因加大的部分没有被程序看见,到了80386之后地址又从24位加大到32位(80386SX是24位)。整个段寄存器如下图所示:
@@12A08400.GIF;图1@@
在8086中CPU只有“看得见部分”,从而也直接参与了地址形成运算,但在80286之后,在“看不见部分”中已经包含了地址值,“看得见部分”就退化为只是一个标号再也不用参与地址形成运算了。地址的形成总是从“不可看见部分”取出基址值与偏移相加形成地址。也就是说在实模式下当一个段寄存器被装入一个值时,“看不见部分”的界限被设成FFFFH,基址部分才是要装入值左移4位,属性部分设成16位0特权级。这个过程与保护模式时装入一个段存器是同理的,只是保护模式的“不可见部分”是从描述表中取值,而实模式是一套固定的过程。
对于CPU在形成地址时,是没有实模式与保护模式之分的,它只管用基址(“不可见部分”)去加上偏移量。实模式与保护模式的差别实际上只是保护处理部件是否工作得更精确而已,比如不允许代码段的写入。实模式下的段寄存装入有固定的形成办法从而也就不需要保护模式的“描述符”了,因此保持了与8086/8088的兼容性。而“描述符”也只是为了装入段寄存器的“不可见部分”而设的。
从上面的“整个段寄存器”可见CPU的地址形成与“看得见部分”的当前值毫无关系,这也解释了为什么在刚进入保护模式时后面的代码依然被正确地运行而这时代码段寄存器CS的值却还是进入保护模式前的实模式值,或者从保护模式回到实模式时代码段CS被改变之前程序是正常地工作,而不会“突变”到CS左移4位的地址上去,比如在保护模式时CS是08H的选择器,到了实模式时CS还是08H但地址不会突然变成80H加上偏段量中去。因为地址的形成不理会段寄存器“看得见部分”的当前值,这一个值只是在被装入时对CPU有用。
地址的形成与CPU的工作模式无关,也就是说实模式与0特权级保护模式不分页时是一模一样的。明白了这一机理,在实模式下一样可以处理通常被认为只有在保护模式才能做的事,比如访问整个机器的内存。可以不必理会保护模式下的众多术语,或者更易于理解,如选择器就是“看得见部分”,描述符是为了装入“不可见部分”而设的。
作为验证CPU的这种机理,这里写了一个实模式下访问4GB内存的C程序。有一些书籍也介绍有同样功能的汇编程序,但它们都错误地认为是利用80386芯片的设计疏漏。实际上Intel本身就在使用这种办法,使得CPU上电时能从FFFFFFF0H处开始第一条指令,这种技术在286之后的每一台机器每一次冷启动时都使用,只是我们不知道罢了。CPU上电也整个代码段寄存器是这样的:
@@12A08401.GIF;图2@@
EIP=0000FFF0H
这样CS∶EIP形成了FFFFFFF0H的物理地址,当CPU进行一次远跳转重新装入CS时,基址就变了。
为了访问4G内存空间,必须有一个段寄存器的“不可见部分”的界限为4G-1,基址为0,这样就包含了4GB内存,不必理会可见部分的值。显然要让段寄存器在实模式下直接装入这些值是不可能的。唯一的办法是让CPU进入一会儿保护模式在装入了段寄存器之后马上回到实模式。
进入保护模式十分简单,只要建好GDT把CRO寄存器的位0置上1,CPU就在保护模式了,从前面所分析CPU地址形成机理可知,这时不必理会寄存器的“看得见部分”值是否合法,各种段寄存器是一样可用的,就像没进保护模式一样。在把一个包含有4GB地址空间的值装入某个段寄存器之后就可返回实模式。
预先可建好GDT如下:
unsigned long GDT-Table[]={0,0, //空描述符,必须为零0x0000FFFF,0xCF9A00, //32位平面式代码段0x0000FFFF,0xCF9200 } , //32位平面式数据段只是为了访问数据的话只要2个GDT就足够了,因为并没有重装代码段,这里给出3个GDT只是为了完整性。
通常在进入保护模式时要关闭所有的中断,把IDTR的界限设置为0,CPU自动关闭所有中断,包括NMI,返回实模式后恢复IDTR并开中断。
另外A20地址线的控制对于正确访问整个内存也很重要,在进入保护模式前要让8042打开A20地址线。
在这个例子里FS段寄存器设成可访问4GB内存的基址和界限,由于在DOS中很少有程序会用到GS、FS这两个386增加的段寄存器,当要读写4GB范围中的任一个地方都可通过FS段来达到,直到FS在实模式下被重装入冲掉为止。
这个例子在386SX、386DX、486上都运行通过。例子里加有十分详细的注释,由于这一程序是用BC 3.1编译连接的,而其连接器不能为DOS程序处理32位寄存器,所以直接在代码中加入操作码前缀0x66和地址前缀0x67,以便让DOS实模式下的16位程序可用32位寄存器和地址。程序的右边以注释形式给出等效的32位指令。要注意16位的指令中mov al, byte ptr [BX]的指令码正好是32位的指令mov al, byte ptr[EDI]。
读者可用这个程序验证BIOS是否同时在两个区域出现。如果有线性定址能力的VESA显示卡(如TVGA9440)还可进一步验证线性显示缓冲区在1MB之上的工作情况。
#include <dos.h>
unsigned long GDT-Table[]=
{0,0, //NULL - 00H
0x0000FFFF,0x00CF9A00, //Code32 - 08h Base=0 Limit=4G-1 Size=4G
0x0000FFFF,0x00CF9200 //Data32 - 10h Base=0 Limit=4G-1 Size=4G
};
unsigned char OldIDT [6]={0}; //Save The IDTR before Enter Protect Mode.
unsigned char pdescr-tmp [6]={0}; //NULL The IDTR s Limit=0 CPU will
// disable all Interrupts, include NMI.
#define KeyWait() {while(inportb(0x64) &2);}
void A20Enable(void)
{
keyWait ();
outportb(0x64,0xD1);
KeyWait();
outportb(0x60,0xDF); //Enable A20 with 8042.
KeyWait();
outportb(0x64,0xFF);
KeyWait ();
}
void LoadFSLimit4G(void)
{
A20Enable (); //Enable A20
//***
Disable ints & Null IDT
//***
asm {
CLI //Disable inerrupts
SIDT OldIDT //Save OLD IDTR
LIDT pdescr-tmp //Set up empty IDT.Disable any interrupts,
} // Include NMI.
//***
Lodd GDTR
//***
asm{ // The right Code is Real, But BC++ s Linker NOT
// Work with 32bits Code.
db 0x66 //32 bit Operation Prefix in 16 Bit DOS.
MOV CX,DS //MOV ECX,DS
db 0x66 //Get Data segment physical Address
SHL CX,4 //SHL ECX,4
MOV word ptr pdescr-tmp [0],(3*8-1)
//MOV word ptr pdescr-tmp [0], (3*8-1)
db 0x66
XOR AX,AX //XOR EAX,EAX
MOV AX,offset GDT-Table
// MOV AX,offset GDT-Table
db 0x66
ADD AX,CX //ADD EAX,ECX
MOV word ptr pdescr-tmp [2], AX
//GDTR Base low16 bits
db 0x66
SHR AX,16 //SHR EAX,16
MOV word ptr pdescr-tmp [4],AX
//GDTR Base high16 bits
LGDT pdescr-tmp //Load GDTR
}
//****
//* Enter 32 bit Flat Protected Mode
//****
asm{
mov DX,0x10 // The Data32 Selector
db 0x66,0x0F,0x20,0xC0 // MOV EAX,CR0
db 0x66
MOV BX,AX // MOV EBX,EAX
OR AX,1
db 0x66,0x0F,0x22,0xC0
//MOV CRO,EAX // Set Protection enable bit
JMP Flsuh
} //Clear machine perform cache.
flush: // Now In Flat Mode, But The CS is Real Mode Value.
asm { //And it s attrib is 16Bit Code Segment.
db 0x66
MOV AX,BX //MOV EAX,EBX
db 0x8E,0xE2 //MOV FS,DX
//Load FS Base=0 Size=4G now
db 0x66,0x0F,0x22,0xC0 //MOV CRO,EAX
//Return Real Mode.
LIDT OldIDT //LIDT OldIDT //Restore IDTR
STI // STI //Enable INTR
}
}
unsigned char ReadByte (unsigned long Address)
{
asm db 0x66
asm mov di,word ptr Address // MOV EDI, Address
asm db 0x67 //32 bit Address Prefix
asm db 0x64 //FS:
asm mov al,byte ptr [BX] // =MOV AL, FS: [EDI]
return -AL;
}
unsigned char WriteByte(unsigned Long Address)
{
asm db 0x66
asm mov di,word ptr Address //MOV EDI, Address
asm db 0x67 //32 bit Address Prefix
asm db 0x64 //FS:
asm mov byte ptr [BX],al //=MOV FS: [EDI],AL
return -AL;
}
//////// Don t Touch Above Code ///
# include <stdio, h>
void Dump4G (unsigned long Address)
{
int i;
int j;
for (i=0; i<20; i++)
{
printf (“%081X: ”, (Address+i*16));
for (j=0; j<16;j++)
printf ("% 02X" ,ReadByte (Address+i*16+j));
printf (" ");
for (j=0;j<16;j++)
{
if (ReadByte (Address+i*16+j) <0x20) printf (" . ");
else printf (" %C ", ReadByte (Address+i*16+j));
}
printf ("n");
}
}
main ()
{
unsigned long Address=0;
unsigned long tmp;
LoadFSLimit4G ();
printf ("====Designed By Southern. 1995.7.17====n");
printf (" Now you can Access The Machine All 4G Memory.n");
printf (" Input the Start Memory Physical to DUMP. n");
printf (" Press D to Cuntinue DUMP, 0 to End & Quit, n");
do {
printf ("-");
scanf ("%IX", &tmp);
if (tmp==0x0d) Address+=(20*16);
else Address=tmp;
Dump4G (Address);
}while (Address !=0);
return 0;


本文标签:

为您推荐

浅谈计算机网络环境下的网络教学

网络的发展,创造出全新的网络文化。不少学校顺应时代需要建设了校园网,并连入了互联网,开始了网络教学的尝试。教师在教学过程中如何根据网络教学不同的教学模式,发挥教师的主导作用和激发学生自主学习使学生成为认识的主体,这就要了解网上教学的教学模式、特点、问

2011-11-11 17:33

计算机网络词汇翻译浅析

本文分析了计算机网络词汇翻译中的懒惰现象,对计算机网络词汇翻译中采用的归化异化两种方法进行了论述,认为该懒惰现象具有相对的积极意义。同时,网络词汇既有一般科技术语翻译的特点,又有它独特的翻译方法。对网络词汇的翻译要根据其接受程度和读者的文化包容性,以

2011-11-11 17:32

项目教学法在计算机网络课程中的应用

提高学生计算机知识水平和实践动手能力,是中职学校计算机教育的根本任务。由于中职学生学习积极性不高,计算机教学效率低下,在中职计算机网络课程中采用项目教学法十分必要。

2011-11-11 17:31

浅谈计算机网络中服务的概念

由系统管理员管理的结构化计算机环境和只有一台或几台孤立计算机组成的计算机环境的主要区别是服务。针对计算机网络中的服务概念进行了阐述。

2011-11-11 17:30

浅谈高职院校计算机网络实践教学

目前大部分高职院校都开设有计算机网络技术专业,而实训教学则是计算机网络技术专业教学中很重要的一个组成部分。本文结合当前高职院校计算机网络技术专业的发展趋势,根据实训教学的特点,从网络实训课程的开设、网络实训的过程、网络综合实训等多方面对网络技术专业的

2011-11-11 17:29

浅谈网站服务器安全维护技巧

服务器的维护至关重要,稍有闪失即会使整个 网络 陷入瘫痪。目前,恶意的网络攻击行为包括两类:一是恶意的攻击行为,如拒绝服务攻击、网络病毒等,这些行为消耗大量的服务器资源,影响服务器的运行速度和正常 工作 ,甚至使服务器所在的网络瘫痪;另外一类是恶意的入

2011-11-11 17:29

加载中...