十、 直接读取SATA硬盘之(三)、DOS下访问高于1MB的内存空间
(1)原理
DOS下,CPU是处于“实模式”,不能访问高于1MB的内存。如何去访问4GB内存?比如这个F7906000H。那就必须要让CPU能切换到“保护模式”。
在保护模式下,地址的生成比较复杂,段是通过一系列被称之为“描述符表”的表所定义的。要扩大寻址范围就必须扩大“段描述符高速缓冲寄存器”中的界限值,但CPU在实模式下不能改变这些界限值,必须在保护模式下完成该操作。
有2种方法:
l 一个是DOS 程序中嵌入保护模式程序段,参见文章《DOS实方式下直接访问4GB内存》、《DOS操作系统下PCI板卡访问方法的研究与实现》等很多文章,还有:
DOS下如何访问4G内存 http://www.cnblogs.com/kuwoyidai/archive/2010/07/14/1777527.html
在DOS实模式下直接存取4GB内存 http://www.docin.com/p-215328775.html
这个方法未试。
l 另一个是采用INT 15H BIOS 中断调用,这里采用这种方法。重点参考文章《DOS下PCI卡内存映射空间的访问方法》,以及里面的程序。
INT 15H - SYSTEM - COPY EXTENDED MEMORY
AH = 87h
CX = number of words to copy (max 8000h) ―――注意:长度是按WORD为单位
ES:SI -> global descriptor table GDT表
Return:
CF set on error
CF clear if successful
AH = status
00h source copied into destination
01h parity error
02h interrupt error
03h address line 20 gating failed
80h invalid command (PC,PCjr)
86h unsupported function (XT,PS30)
Format of global descriptor table:
Offset Size Description
00h 16 BYTEs zeros
10h WORD source segment length in bytes (=2*CX-1 or greater)――长度限制
12h 3 BYTEs 24-bit(3字节) linear source address, low byte first
15h BYTE source segment access rights (=93h)
16h WORD zero
18h WORD destination segment length in bytes (=2*CX-1 or greater) ――长度限制
1Ah 3 BYTEs 24-bit(3字节) linear destination address, low byte first
1Dh BYTE destination segment access rights (=93h)
1Eh 18 BYTEs zeros
上面对“描述符”的说明不是很清楚,可参考相关文章,比如:
Global Descriptor Table http://wiki.osdev.org/Global_Descriptor_Table
(2)可以读取内存的程序
read4g2.h:
typedef struct Descriptorstruct // 描述符结构 长8字节
{
unsigned int size ; //段地址的长度限制,以字节计,可以=传输字节数 实际发现,设成0或1等,都不影响实际的传输字节数,即这个是无效的。
unsigned int base_low_word;
unsigned char base_mid_byte;
unsigned char attr1 ;
unsigned char attr2 ;
unsigned char base_high_byte;
} Descriptor ;
typedef struct GDTStruct // GDT 表结构
{
Descriptor BlankDsc; //空描述符
Descriptor GDTDsc; //GDTR 的基地址和大小
Descriptor SrcDsc; // 源段描述符
Descriptor DstDsc; // 目标段描述符
Descriptor BiosCS; // 代码段描述符
Descriptor BiosSS; // 栈段描述符
} GDT;
GDT MoveGDT = { //共48字节
{0 ,0 ,0 ,0 ,0 ,0} , //8字节0
{0 ,0 ,0 ,0 ,0 ,0} , //8字节0
{0 ,0 ,0 ,0x92 ,0x40 ,0} , // 源 00 00长度限制 00 00 00地址低3字节 92权限 40权限和长度限制 00地址高字节
{0 ,0 ,0 ,0x92 ,0x40 ,0} , // 目的
{0 ,0 ,0 ,0 ,0 ,0} , //8字节0
{0 ,0 ,0 ,0 ,0 ,0} //8字节0
};
void memmove(unsigned long sou_addr ,unsigned long des_addr ,unsigned int num)
// 数据移动函数 num为字节数
{
GDT mcb;
struct REGPACK reg;
MoveGDT.SrcDsc.base_low_word = sou_addr & 0x0000ffff ;
MoveGDT.SrcDsc.base_mid_byte = sou_addr >> 16 ;
MoveGDT.SrcDsc.size = num; //段地址长度限制,为字节数
MoveGDT.SrcDsc.base_high_byte = sou_addr >> 24 ;
MoveGDT.DstDsc.base_low_word = des_addr & 0x0000ffff ;
MoveGDT.DstDsc.base_mid_byte = des_addr >> 16 ;
MoveGDT.DstDsc.size = num; //段地址长度限制,为字节数
MoveGDT.DstDsc.base_high_byte = des_addr >> 24 ;
mcb=MoveGDT; //另外赋一个GDT变量,因为INT15会改变这个变量值
reg.r_es = FP_SEG( &mcb) ;
reg.r_si = FP_OFF( &mcb) ;
reg.r_cx = num >> 1 ; //长度是按WORD来算的,故要除以2。num=2或3,则CX=1,传输2个字节;num=0或1,则CX=0,不传输任何字节
reg.r_ax = 0x8700 ;
intr( 0x15 , ® ) ;
}
read4g2.c:
#include <stdio.h>
#include <bios.h>
#include <stdlib.h>
#include <conio.h>
#include <dos.h>
#include "read4g2.h"
int main(int argc, char *argv[])
{
unsigned char ctemp,cdata;
int i,i2,i3;
unsigned long sou_addr,des_addr,ltemp;
unsigned char databuf[2000];
des_addr=(unsigned long)FP_SEG(databuf)*16+(unsigned long)FP_OFF(databuf);
if(argc<2)
{
printf("you must input args!");
return 0;
}
if(strlen(argv[1]) != 8) { printf("ADDR must be 8 chars long!"); return 0;}
sou_addr=0;
for(i=0;i< 8;i++)
{
argv[1] = toupper(argv[1]);
ctemp=(unsigned char)argv[1];
if (ctemp>=0x30 && ctemp<=0x39)
{
ctemp=ctemp-0x30;
}
else if (ctemp>=0x41 && ctemp<=0x46)
{
ctemp=ctemp-55;
}
else
{ printf("ADDR char error"); return 0; }
sou_addr+=((unsigned long)ctemp) << (4*(7-i));
}
//以下分2部分,读、写
if (argc>2) goto fillmemory;
printf ("READing. source addr=%08lX dest addr=%08lX \nnow 256 byte data in hex is:\n",sou_addr,des_addr);
memmove( sou_addr , des_addr , 512);
i2=0;
for(i=0; i<256; i++)
{
printf("%02X ",databuf);
i2++;
if(i2 >=16 ) {i2=0; printf("\n");}
}
return 0;
fillmemory:
if( strcmp(argv[2],"fill") ) {printf("error param fill\n"); return 0;}
if(argc<5 ) {printf("error no data\n"); return 0;}
cdata=0;
for(i=0;i< 2;i++)
{
argv[3] = toupper(argv[3]);
ctemp=(unsigned char)argv[3];
if (ctemp>=0x30 && ctemp<=0x39)
{
ctemp=ctemp-0x30;
}
else if (ctemp>=0x41 && ctemp<=0x46)
{
ctemp=ctemp-55;
}
else
{ printf("data char error"); return 0; }
cdata+=ctemp << (4*(1-i));
}
i3= atoi(argv[4]);
if(i3<0 || i3>1000 ) {printf("length error!"); return 0; }
for(i=0; i<1000; i++)
{
databuf=cdata;
//cdata++;
}
ltemp=sou_addr;
sou_addr=des_addr;
des_addr=ltemp;
printf ("WRITing. source addr=%08lX dest addr=%08lX with data %02X length %d\n",sou_addr,des_addr,cdata,i3);
memmove( sou_addr , des_addr , i3);
return 0;
}
(3)说明
用这个INT15H方法,实际测试发现,描述符结构中,段地址的长度限制设成0或1等等,都不影响实际的传输字节数,即这个是无效的。
CX是WORD的数量,num=2或3,则CX=1,传输2个字节;num=0或1,则CX=0,不传输任何字节。
即每次传输的数据单位只能是以WORD计的!!
加载或不加载HIMEM.SYS,均不影响。也不用人工进行打开A20地址线操作。
(4)DOS下运行
读取SATA AHCI控制器的内存空间F7906000H: read4g2 F7906000
如果要填充某一段内存为0xff,长256字节:read4g2 00a00000 fill ff 256
文章评论(0条评论)
登录后参与讨论