无忧启动论坛

 找回密码
 注册
搜索
系统gho:最纯净好用系统下载站广告联系 微信:wuyouceo QQ:184822951
查看: 11959|回复: 38
打印 上一主题 下一主题

[原创首发]HPA初步认识与分析

[复制链接]
跳转到指定楼层
1#
发表于 2008-7-27 18:42:56 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
                                                           HPA初步认识与分析
                                                                  编程及撰文:Lintel         Ring整理/输入
                                                                       一、前言
     
            早在06年,我就开始有着手写关于HPA保护程序的打算,在07年暑假的资料大收集以及和天风等兄弟讨论之后,基本对HPA的原理以及操作方式有了一定的了解.写好了HPA创建程序之后,由于出现了重大的BUG一直没有解决,所以这个HPA计划一直耽搁了很久.最近因为天风大哥的提起,才继续完成下去,在此,我对天风致以深深的歉意...感谢Ring的辛苦整理和输入
               在开始HPA的分析之前,我们必须知道一些必知的知识。
                                                                        1、什么是HPA?
       HPA,即Host Protected Area (主机保护区域)或Hidden Protected Area(隐藏保护区域),下文简称HPA。
  HPA概念出现在ATA/ATAPI-4标准中ATA-4.2001年第21周),全称为AT Attachment with Packet Interface Extension(ATA/ATAPI),是由ANSI下属机构T13与硬盘生产商协定的。
  HPA是定义在硬盘上的保护区域,它存在于硬盘的高端LBA区域。使用硬盘ATA命令进行设置,在设置了HPA的硬盘上,在表面看来,在使用中没有任何不同,只是一个容量“变小”的硬盘。由于是在硬盘本身设置,所以硬盘中的容量信息改变,因此,在BIOS和操作系统等环境下,是看不到隐藏的区域。
        实质上,HPA就是设置最大的LBA值,以达到保护的目的。




                                                                        2、CPU控制下的外设大家庭
   计算机发展到今天,外设已经五花八门,硬盘、软盘、光盘、U盘、声卡、网卡、SD卡、手柄等等。真是层出不穷,这样外设给我们带来了便利,但同时也带来了问题。比如 ,主板上的接口个数有限,怎样保证这些外设能够正常与主机通信?怎样保证CPU(处理器)能一个不漏地控制每一个外设?CPU如何控制?如何保证CPU不会误操作?CPU控制外设到什么样地程度?如何保证控制下的外设不会“打架”?这些都是初学者要问的问题。请各位听我慢慢道来。
   首先,每一个外设都有一个控制器,这些控制器都是数字控制器,在控制器里面有“寄存器”,这是存储单元,这些东东跟内存单元的结构不一样,但作用跟内存单元一样,都能保存信息,这些寄存器各有作用,各有分类,比如硬盘,在硬盘上有保存保存磁头号,磁道号,扇区号等参数的寄存器,这些寄存器用来告诉硬盘本次操作硬盘要读写的是哪个盘面,哪个磁道哪个扇区的数据,根据寄存器的作用,分为两类,分别叫控制寄存器和状态寄存器。控制寄存器的用来告诉外设,CPU让它干什么活,以及干活时需要的参数。状态寄存器用于外设向CPU报告外设目前的状态。比如目前是否在忙,在干什么活,在干活的过程发生了什么错误,状态寄存器不能主动告诉CPU外设的当前状态,只能被动地由CPU来读取信息,CPU把外设当前状态寄存器的信息读出来,就能够知道外设当前的工作状态。当然,外设也有主动报告CPU的能力。就是通过中断的方式,在此,我就省略了。
   寄存器有的是只写,有的是只读,也有可读可写的。一般而言,控制寄存器的是只写活可读可写的,状态寄存器一般也只是只读的。
   那么,CPU是如何一个不漏地控制多个外设呢?在80X86计算机中,外个外设和CPU都挂在一组系统总线上,每个外设分配一个地址,CPU拿某一个地址去访问某一个寄存器时,只有该寄存器发生回应,或接收总线上的数据,或把自己的数据送到总线上。同一个外设的寄存器或其他外设的寄存器都不会发生回应。这样,CPU用不同的地址就可以访问不同的寄存器,也就可以一个不漏地控制外设了。在访问某个寄存器时,别的寄存寄存器不会发生动作,所以,外设之间不会“打架”,不会互相干扰。同样的,CPU
访问内存时,其地址不是外设的寄存器地址。所有的外设不会有回应,所以CPU不会误操作外设。
    根据外设的基本结构,你是否已经知道CPU是如何控制外设了。显然,CPU控制外设的方法无非就是读写外设的寄存器。比如,CPU要从硬盘读文件,那么CPU只需要把磁头号,磁道号,扇区号要读的数据量等参数写入硬盘对应的寄存器,然后向硬盘的命令寄存器填写一个开始命令,硬盘控制器就在接到命令控制磁头移动到对应的磁道,对准扇区,读取数据。送到硬盘的数据寄存器,至于磁头目前在什么位置,怎样移动,顺时针移动还是逆时针移动,以多快大速度移动,有多大的加速度达到目的扇区等等。这些都不是CPU所能控制的,也就是说CPU只能告诉外设要干什么活以及干活过程所需要的参数,
至于外设是怎么干活,如硬盘的磁头怎么移动这些事是CPU所不能控制的。
   主板上的接口个数有限,怎样保证各种外设能连接主板并和CPU通信呢?答案是标准接口,如IDE接口、串口、USB、并口、PS/2、PCI接口等等。至于你拿的是USB接口的打印机还是游戏手柄还是USB鼠标还是别的什么东东,主板就管不了了。
    如何判断外设是否正常,是否存在问题呢?我们经常说计算机BIOS进行自检,即检查计算机连接什么外设,这些外设是否正常工作,如果某个外设出现故障,BIOS能根据不同的故障发出不同的警报声。BIOS也是一段程序,它凭什么能做到上面所说的事情呢?原来,工程师在设计的时候,就考虑了自检功能。比如鼠标就设计了一个查询/应答命令。BIOS检查计算机是否存在鼠标时,只要向鼠标发一个查询命令,送到鼠标的命令寄存器。如果计算机连接有鼠标,那么鼠标就会进行回应,然后BIOS读状态寄存器。如果读出的值时0xff或0x00,则表示鼠标不存在或有故障。
    现在知道了吧?CPU就是通过系统总线的I/O方式操作外设的。

                                                              二、硬盘操作必须知道的
  通过上一部分,我们已经知道CPU时如何操作外设了。现在以下的时必须知道的。
                                                                        1、ATA简介
   硬盘接口IDE(Integrated Drive Electronies)也称AT总线接口,时当前硬盘常见的一种接口。(注意,S-ATA同样属于ATA的行列)。
   IDE的概念最早由Texan 和 Compaq公司提出,目的是把硬盘控制器嵌入到驱动器中。
   1988年10月,ANSI中的X3T9.2(即T13)工作组的一个委员会开始讨论IDE的有关问题,1993年2月发表了该标准的3.1版本。使其成为了一个正式的ANSI标准,并赋予了新名称—ATA(AT Attachment), 从概念上说,ATA与IDE具有相同的含义。
   随着硬盘制作工艺的提高,ATA标准也在不断改进,目前最新的版本是ATA/ATAPI-8。

                                                                     2.硬盘的ATA命令

  IDE控制器中有2组寄存器;命令寄存器和控制寄存器。命令寄存器被用来接受命令和传送数据;控制寄存器组用作磁盘控制。这2个寄存器组CS1FX/和CS3FX/信号区分。CS1FX/的地址范围是1F0H~1F7H,CS3FX/的地址范围是3F0H~3F7H,具体如表2所示。

  目前,许多计算机配置了2个IDE接口,对于第2个IDE接口,这2个信号的地址范围分别是170H~177H和370H~377H。
  在ATA标准中以寄存器方式传送数据、命令和地址。这些寄存器除数据寄存器为16位以外,其它寄存器均为8位。PC机分配给寄存器的地址有二组,一组为1F0H~1F8H,另一组为170H~178H。通常ATA适配器采用IRQ14中断。
            对于新的SATA的I/O地址,如果BIOS中提供了IDE模式的映射,那么其地址与IDE1口地址相同.
            对于独立的SATA/RAID控制器,比如VIA 8237R 其地址可能为100H
  ⑴数据寄存器(1F0H R/W):这是一个16位PIO数据寄存器,用于对扇区的读、写和格式化操作。CPU通过该寄存器向硬盘控制寄存器写入或从硬盘控制寄存器读出扇区缓冲区的数据,如使用“REP OUTSW”或“REP INSW”指令,通过数据寄存器也可以进行DMA方式的数据传输。
  错误寄存器(1F1H R):该寄存器是1个8位的寄存器,它反映控制寄存器在诊断方式或操作方式下的错误原因。在不同方式下有不同的意义。
  ①诊断方式:硬盘控制器在加电、复位或执行驱动器诊断命令以后的工作方式。此时驱动器包含诊断码,该代码反映了诊断后的结果,如表所示。

























  ②操作方式:硬盘控制器执行除诊断命令以外的所有命令后进入该方式,如果状态寄存器的ERR=1,则该寄存器包含命令执行后的错误代码,错误寄存器的内容如表所示。


















  ⑵特性寄存器(1F1H W):一般情况下不使用该寄存器,根据ATA标准它被用来设置接口的某些特征。
⑶扇区数寄存器(1F2 R/W):它记录读、写命令的扇区数。当多扇区传输时,每完成1个扇区操作,该寄存器自动减1,直至为0,如果初值为0,则表示256。如果有错误发生,该寄存器包含已经操作成功的扇区数。
  ⑷扇区号寄存器(1F3H R/W):它记录读、写和校验命令指定的起始扇区号。如果驱动器使用逻辑块寻址(LBA,logical lock address)方式,该寄存器记录逻辑扇区号的0字节。
  ⑸柱面号寄存器:(1F4H 1F5H R/W):它记录读、写、校验、寻址和格式化命令指定的柱面号,ATA标准允许65536柱面,但早期的IDE控制器中只允许1024个柱面。低8位在1F4H寄存器中,高8位在1F5H寄存器中。如果是LBA寻址方式,这2个寄存器包含起始扇区的1和2字节。
  ⑹驱动器/磁头寄存器(1F6H R/W):它记录读、写、校验、寻址和格式化命令指定的驱动器号、磁头号和寻址方式。其定义如表所示。














⑺状态寄存器(1F7H R):它放映硬盘驱动器执行命令后的状态,该寄存器要清除中断请求信号,如果要避免清除中断,可以读辅助状态寄存器3F6H,这2个寄存器的内容完全相同,其定义如表所示。















   
  命令寄存器(1F7H W):该寄存器接收处理器输出的HDC命令,命令格式和含义如表7所示。其中12种是强制性的(M),其它是选择性的(O)。有些命令有2个操作码,后1个是早期操作码,有的驱动器上仍在使用,如CON-NER驱动器。

  辅助状态寄存器(3F6H R):它包含与状态寄存器相同的内容,但读该寄存时不会清除中断请求信号。
硬盘控制寄存器(3F6H W):该寄存器定义如表所示。
















  驱动地址寄存器(3F7H R):该寄存器包含命令执行后的某些信息,它与软盘驱动器共享,D7位是软盘驱动器的更换磁盘位。寄存器的所有位都是负逻辑。其定义如表所示。









关于SATA口的定义I/O地址,还有待测试证明验证




               硬盘的编址方式
  ATA标准允许65536个柱面,每个扇区512B。扇区寻址有2种方式:物理寻址方式和逻辑寻址方式。
  物理寻址方式(CHS方式):用柱面、磁头和扇区表示1个特定的扇区。起始扇区是0磁道、0磁头、1扇区,接下来是2扇区,知道EOF扇区,接下来是同一柱面1头、1扇区……
  逻辑地址方式(LBA方式):对于参数寄存器来说,其柱面值最大为65536,磁头数最大为16,扇区最大为255。因此参数寄存器可支持的最大磁盘容量为65536*16*255=13.69GB。而BIOS所能支持的最大值分别为1024、255、63,支持的最大容量为1024*255*63=8.4GB。
  IDE接口对磁盘的容量限制是由BIOS和参数寄存器二者结合产生的。因此柱面、磁头和扇区数被限制在1024、16、63,最大只能支持1024*16*63=528MB。采用逻辑块方式寻址可以突破528MB的容量限制。该方式以28位的宽度可寻址到2^28=268435455块扇区,容量达137GB。如图10所示。
  逻辑块地址与物理地址的关系为:LBA地址=(柱面号*磁头数+磁头号)*扇区数+扇区数-1
  采用LBA方式寻址,没有磁头和磁道的转换操作,在访问连扇区时,操作速度比物理地址方式要快。
  LBA寻址方式虽然需要BIOS做些修改,但它与Microsoft和IBM的INT13功能扩展规范是兼容的。为了能够用LBA方式存取大于528MB的硬盘,IDE提供了二种方式供主机系统选择,这二种方式均在CMOS中设置。


  第1种方式称为自动配置,这主要是向那些不用BIOS存取磁盘的操作系统(如Netware和UNIX)提供的。它运用标准DPT(磁盘参数表)从IDE驱动器上获取有关驱动器容量的信息传向操作系统。
  第2种方式是为了DOS和WINDOWS设计的,称为自动转换。在CMOS设置中选择这种类型、主机加电初始化时,BIOS会创建1张EDPT(增强型磁盘参数表)。EDPT表包含2组驱动器参数:一组来自Identify命令获取的驱动器信息;另一组则是BIOS给操作系统的,这些信息不是驱动器返回信息的简单拷贝,它还要做些转换。根据Identify命令返回的信息,BIOS将实际的CHS转换成IDE的CHS或者转换成LBA的CHS,能够支持的最大磁盘容量为8.4GB,而且BIOS改动最小。






                                                                    3.编程环境下的硬盘I/O操作






                                                       三、实践硬盘I/O操作

                                                                     1.MBR代码的操作


                                                                      2.编写HPA Boot Loader
本文参考以下资料:

<ATA接口技术>
<Information Technology -AT Attachment with Packet Interface Extension >  (ATA/ATAPI-4)
<BIOS研发技术剖析(基本概论与设置)>
<“El Torito”  Bootable CD-ROM Format  Specification  Version 1.0>
<FAT32文件系统程序和说明>
<自己动手写操作系统>

转载请注明原出处,谢谢合作.



如果本文有遗漏,错误.请跟贴说明或者请联系我:

lintel77@qq.com

(未完,整理中)

[ 本帖最后由 lintel 于 2008-8-2 15:05 编辑 ]
2#
发表于 2008-7-27 18:48:39 | 只看该作者
终于有人正式挑起这个话题了,希望能结出沉甸甸的硕果,密切关注中。。。。。。
回复

使用道具 举报

3#
发表于 2008-7-27 18:54:40 | 只看该作者
希望楼主能写出自己的HPA一键恢复出来,贡献给广大无忧网民!
回复

使用道具 举报

4#
发表于 2008-7-27 19:00:05 | 只看该作者
相比论坛上的几篇有关的hpa文章相比,还有待增加深度!

[ 本帖最后由 xdg3669 于 2008-7-27 19:01 编辑 ]
回复

使用道具 举报

5#
 楼主| 发表于 2008-7-27 19:18:39 | 只看该作者
吃饭去,这个Ring,输错太多了.改得好辛苦...
回复

使用道具 举报

6#
发表于 2008-7-28 10:36:05 | 只看该作者
不错,期待.

这里:"1988年10月,ANSI中的X3T9.2(即T13)工作组的一个委员会开始讨论IDE的有关问题,1933年2月发表了该标准的3.1版本。"

是否是1993年才是对呢?
回复

使用道具 举报

7#
发表于 2008-7-28 10:37:37 | 只看该作者
照抄一个给需要的朋友:
设置硬盘最大可读取扇区源码:
======================================
/* setmax.c - aeb, 000326 - use on 2.4.0test9 or newer */
/* IBM part thanks to Matan Ziv-Av <matan@svgalib.org> */
/*
* Results on Maxtor disks:
* The jumper that clips capacity does not influence the value returned
* by READ_NATIVE_MAX_ADDRESS, so it is possible to set the jumper
* and let the kernel, or a utility (like this one) run at boot time
* restore full capacity.
* For example, run "setmax -d 0 /dev/hdX" for suitable X.
* Kernel patches exist that do the same.
*
* Results on IBM disks:
* The jumper that clips capacity is ruthless. You clipped capacity.
* However, if your BIOS hangs on a large disk, do not use the jumper
* but find another machine and use a utility (like this one) to
* clip the non-volatile max address.
* For example, run "setmax -m 66055248 /dev/hdX" for suitable X.
* Now go back to your first machine and proceed as with Maxtor drives above.
*/
#include <stdio.h>
#include <fcntl.h>
#include <getopt.h>
#include <linux/hdreg.h>

#ifndef HDIO_DRIVE_CMD_AEB
#define HDIO_DRIVE_CMD_AEB        0x031e
#endif

#define INITIALIZE_DRIVE_PARAMETERS 0x91
#define READ_NATIVE_MAX_ADDRESS 0xf8
#define CHECK_POWER_MODE        0xe5
#define SET_MAX                        0xf9

#define LBA        0x40
#define VV        1                /* if set in sectorct then NOT volatile */

struct idecmdin {
        unsigned char cmd;
        unsigned char feature;
        unsigned char nsect;
        unsigned char sect, lcyl, hcyl;
        unsigned char select;
};

struct idecmdout {
        unsigned char status;
        unsigned char error;
        unsigned char nsect;
        unsigned char sect, lcyl, hcyl;
        unsigned char select;
};

unsigned int
tolba(unsigned char *args) {
        return ((args[6] & 0xf) << 24) + (args[5] << 16) + (args[4] << 8) + args[3];
}

void
fromlba(unsigned char *args, unsigned int lba) {
        args[3] = (lba & 0xff);
        lba >>= 8;
        args[4] = (lba & 0xff);
        lba >>= 8;
        args[5] = (lba & 0xff);
        lba >>= 8;
        args[6] = (args[6] & 0xf0) | (lba & 0xf);
}

int
get_identity(int fd) {
        unsigned char args[4+512] = {WIN_IDENTIFY,0,0,1,};
        struct hd_driveid *id = (struct hd_driveid *)&args[4];

        if (ioctl(fd, HDIO_DRIVE_CMD, &args)) {
                perror("HDIO_DRIVE_CMD");
                fprintf(stderr,
                        "WIN_IDENTIFY failed - trying WIN_PIDENTIFY\n");
                args[0] = WIN_PIDENTIFY;
                if (ioctl(fd, HDIO_DRIVE_CMD, &args)) {
                        perror("HDIO_DRIVE_CMD");
                        fprintf(stderr,
                               "WIN_PIDENTIFY also failed - giving up\n");
                        exit(1);
                }
        }

        printf("lba capacity: %d sectors (%lld bytes)\n",
               id->lba_capacity,
               (long long) id->lba_capacity * 512);
}

/*
* result: in LBA mode precisely what is expected
*         in CHS mode the correct H and S, and C mod 65536.
*/
unsigned int
get_native_max(int fd, int slave) {
        unsigned char args[7];
        int i, max;

        for (i=0; i<7; i++)
                args = 0;
        args[0] = READ_NATIVE_MAX_ADDRESS;
        args[6] = (slave ? 0x10 : 0) | LBA;
        if (ioctl(fd, HDIO_DRIVE_CMD_AEB, &args)) {
                perror("HDIO_DRIVE_CMD_AEB failed READ_NATIVE_MAX_ADDRESS");
                for (i=0; i<7; i++)
                        printf("%d = 0x%x\n", args, args);
                exit(1);
        }
        return tolba(args);
}

/*
* SET_MAX_ADDRESS requires immediately preceding READ_NATIVE_MAX_ADDRESS
*
* On old Maxtor disk: this fails for delta <= 254, succeeds for delta >= 255.
* (So, in order to get the last 255*512=130560 bytes back one has to reboot.
* Side effect: reset to CurCHS=16383/16/63, CurSects=16514064.)
* On new Maxtor disk: this works.
* On IBM disk without jumper: this works.
*/
void
set_max_address(int fd, int slave, int delta, int max, int volat) {
        unsigned char args[7];
        int i, nativemax, newmax;

        nativemax = get_native_max(fd, slave);
        printf("nativemax=%d (0x%x)\n", nativemax, nativemax);

        for (i=0; i<7; i++)
                args = 0;
        args[0] = SET_MAX;
        args[1] = 0;
        args[2] = (volat ? 0 : 1);
        if (delta != -1)
                newmax = nativemax-delta;
        else
                newmax = max-1;
        fromlba(args, newmax);
        args[6] |= LBA;

        if (ioctl(fd, HDIO_DRIVE_CMD_AEB, &args)) {
                perror("HDIO_DRIVE_CMD_AEB failed SET_MAX");
                for (i=0; i<7; i++)
                        printf("%d = 0x%x\n", args, args);
                exit(1);
        }
}

static char short_opts[] = "d:m:";
static const struct option long_opts[] = {
        { "delta",        required_argument,        NULL,        'd' },
        { "max",        required_argument,        NULL,        'm' },
        { NULL, 0, NULL, 0 }
};

static char *usage_txt =
"Call: setmax [-d D] [-m M] DEVICE\n"
"\n"
"The call  \"setmax --max M DEVICE\"  will do a SET_MAX command\n"
"to set the non-volatile max number of accessible sectors to M.\n"
"\n"
"The call  \"setmax --delta D DEVICE\"  will do a SET_MAX command\n"
"to set the maximum accessible sector number D sectors\n"
"below end-of-disk.\n"
"\n"
"The call  \"setmax DEVICE\"  will do a READ_NATIVE_MAX_ADDRESS\n"
"command, and report the maximum accessible sector number.\n"
"\n"
"This is IDE-only. Probably DEVICE is /dev/hdx for some x.\n\n";

main(int argc, char **argv){
        int fd, c;
        int delta, max, volat;

        /* If you modify device, also update slave, if necessary. */
        /* The kernel already does this for us since 2.4.0test9. */
        /* master: hda, hdc, hde; slave: hdb, hdd, hdf */
        char *device = NULL;        /* e.g. "/dev/hda" */
        int slave = 0;

        delta = max = volat = -1;
        while ((c = getopt_long (argc, argv, short_opts, long_opts, NULL)) != -1) {
                switch(c) {
                case 'd':
                        delta = atoi(optarg);
                        volat = 1;
                        break;
                case 'm':
                        max = atoi(optarg);
                        volat = 0;
                        break;
                case '?':
                default:
                        fprintf(stderr, "unknown option\n");
                        fprintf(stderr, usage_txt);
                        exit(1);
                }
        }

        if (optind < argc)
                device = argv[optind];
        if (!device) {
                fprintf(stderr, "no device specified - "
                        "use e.g. \"setmax /dev/hdb\"\n");
                fprintf(stderr, usage_txt);
                exit(1);
        }
        printf("Using device %s\n", device);

        fd = open(device, O_RDONLY);
        if (fd == -1) {
                perror("open");
                exit(1);
        }

        if (delta != -1 || max != -1) {
                if (delta != -1)
                        printf("setting delta=%d\n", delta);
                else
                        printf("setting max=%d\n", max);
                set_max_address(fd, slave, delta, max, volat);
        } else {
                int mad = get_native_max(fd, slave);
                long long bytes = (long long) (mad+1) * 512;
                int hMB = (bytes+50000000)/100000000;

                printf("native max address: %d\n", mad);
                printf("that is %lld bytes, %d.%d GB\n",
                       bytes, hMB/10, hMB%10);
        }
        get_identity(fd);

        return 0;
}


=========================================
回复

使用道具 举报

8#
发表于 2008-7-28 10:39:40 | 只看该作者
再给一个介绍比较详细的网页。
http://www.answers.com/topic/host-protected-area
回复

使用道具 举报

9#
发表于 2008-7-28 11:49:29 | 只看该作者
原帖由 天风 于 2008-7-28 10:37 发表
照抄一个给需要的朋友:
设置硬盘最大可读取扇区源码:
======================================
/* setmax.c - aeb, 000326 - use on 2.4.0test9 or newer */
/* I ...

这个资料有点老了,新的需要用READ_NATIVE_MAX_ADDR_EXT、SET_MAX_ADDR_EXT
并且LBA地址也变成48位了。
使用SET_MAX_ADDR_EXT还是SET_MAX_ADDR,要对get_identity取得的参数块中的id[83]和id[86]进行判断。
原来的两个函数要改一下:

  1. #define READ_NATIVE_MAX_ADDR_EXT  0x27
  2. #define SET_MAX_ADDR_EXT                0x37
  3. #define TASKFILE_CMD_REQ_NODATA    0
  4. #define TASKFILE_XFER_METHOD_NONE 0
  5. static __u64 get_native_max_address(int fd,__u16 **id_p)
  6. {
  7.       struct hdio_taskfile r;
  8.       __u16 *id;
  9.       *id_p=id=get_identify_data(fd,*id_p);
  10.       if(id&&((id[83]&0xc400)==0x4400)&&(id[86]&0x0400)){
  11.               memset(&r,0,sizeof(r));
  12.               r.cmd_req=TASKFILE_CMD_REQ_NODATA;
  13.               r.xfer_method=TASKFILE_XFER_METHOD_NONE;
  14.               r.lob.command=READ_NATIVE_MAX_ADDR_EXT;
  15.               r.lob.dev=0x40;
  16.               taskfile_std_flags(&r,1);
  17.               if(!do_taskfile_cmd(fd,&r,10))
  18.                       return((__u64)((r.hob.lbah<<16)|(r.hob.lbam<<8)|r.hob.lbal)|((r.lob.lbah<<16)|(r.lob.lbam<<8)|r.lob.lbal))+1;
  19.               perror ("读取硬盘真实容量失败!");
  20.       }
  21.       memset(&r,0,sizeof(r));
  22.       r.cmd_req=TASKFILE_CMD_REQ_NODATA;
  23.       r.xfer_method=TASKFILE_XFER_METHOD_NONE;
  24.       r.lob.command=READ_NATIVE_MAX_ADDR;
  25.       r.lob.dev=0x40;
  26.       taskfile_std_flags(&r,0);
  27.       if (!do_taskfile_cmd(fd,&r,10))
  28.               return ((r.lob.lbah<<16)|(r.lob.lbam<<8)|r.lob.lbal)+1;
  29.       perror ("读取硬盘真实容量失败!");
  30.       return 0;
  31. }
  32. static void set_max_address (int fd, __u16 **id_p, __u64 secs, int nonvolat)
  33. {
  34.       struct hdio_taskfile r;
  35.       __u16 *id;
  36.       __u64 addr=secs-1;
  37.       *id_p=id=get_identify_data(fd,*id_p);
  38.       if(id&&((id[83]&0xc400)==0x4400)&&(id[86]&0x0400)){
  39.               memset(&r,0,sizeof(r));
  40.               r.cmd_req=TASKFILE_CMD_REQ_NODATA;
  41.               r.xfer_method=TASKFILE_XFER_METHOD_NONE;
  42.               r.lob.command=SET_MAX_ADDR_EXT;
  43.               r.lob.feat=0;
  44.               r.lob.dev=0x40;
  45.               r.lob.nsect=nonvolat ? 1 : 0;
  46.               r.lob.lbal=addr&0xff;
  47.               r.lob.lbam=(addr>>8)&0xff;
  48.               r.lob.lbah=(addr>>16)&0xff;
  49.               r.hob.lbal=(addr>>24)&0xff;
  50.               r.hob.lbam=(addr>>32)&0xff;
  51.               r.hob.lbah=(addr>>40)&0xff;
  52.               taskfile_std_flags(&r,1);
  53.               if(!do_taskfile_cmd(fd,&r,10))
  54.                       return;
  55.               perror ("设置HPA失败!");
  56.       }
  57.       if(addr&0xfffffffff0000000LLU){
  58.               fprintf(stderr,"不支持LBA28!\n");
  59.               return;
  60.       }
  61.       memset(&r,0,sizeof(r));
  62.       r.cmd_req=TASKFILE_CMD_REQ_NODATA;
  63.       r.xfer_method=TASKFILE_XFER_METHOD_NONE;
  64.       r.lob.command=SET_MAX_ADDR;
  65.       r.lob.feat=0;
  66.       r.lob.dev=0x40|((addr>>24)&0x0f);
  67.       r.lob.nsect=nonvolat ? 1 : 0;
  68.       r.lob.lbal=addr&0xff;
  69.       r.lob.lbam=(addr>>8)&0xff;
  70.       r.lob.lbah=(addr>>16)&0xff;
  71.       taskfile_std_flags(&r,0);
  72.       if (do_taskfile_cmd(fd,&r,10))
  73.               perror ("设置HPA失败!");
  74.       return;
  75. }
复制代码
***addr的单位是扇区不是字节***
下面是LBA28和LBA48对照表,LBA28只有左边部分(字节),LBA48扩展成字,并且bit24~27也不放原来LBA28的那个位置了(原来在dev reg的低4位)。ATA命令放在这个表下面的一个字节/字。

另外,为了避免被病毒利用加口令部分俺就不提供了:)其实还有一个比较麻烦的是要先取得硬盘I/O口基址,这个要从PCICFG取得。

[ 本帖最后由 netwinxp 于 2008-8-1 08:56 编辑 ]

评分

参与人数 1无忧币 +40 收起 理由
lintel + 40 精品文章

查看全部评分

回复

使用道具 举报

10#
发表于 2008-7-28 12:03:08 | 只看该作者
哈哈,沉默了已久的话题终于又出现了,建议这个帖子加亮
回复

使用道具 举报

11#
 楼主| 发表于 2008-7-28 13:46:04 | 只看该作者
非常感谢大家的关注,请问天风,关于你的源程序包含的库文件该从哪里获得?
回复

使用道具 举报

12#
发表于 2008-7-28 14:38:31 | 只看该作者
#include <linux/hdreg.h>
一看不就知道了:)
回复

使用道具 举报

13#
发表于 2008-7-28 15:25:10 | 只看该作者
我找到的那个很老了,也是我在网络上能找到的唯一有用的拉。
回复

使用道具 举报

14#
发表于 2008-7-28 23:31:40 | 只看该作者
LINUX下用C写,有点意思

我认为这一类东西,还是汇编比较底层一些,当然C也可以
HPA的生成程序,我也反汇编过了,用了好几个中断,端口之类的,而且还有的参数还未查明,关键的部位。

INT 0FH
INT 58H
INT 56H
INT 13H
      AH=41,
      AH=08,
      AH=48,
      AX=8001,
都用到,也需要一些工具书来查一下,才明白,学习中...
回复

使用道具 举报

15#
发表于 2008-7-29 09:10:19 | 只看该作者
原帖由 阿文 于 2008-7-28 23:31 发表
LINUX下用C写,有点意思
我认为这一类东西,还是汇编比较底层一些,当然C也可以
HPA的生成程序,我也反汇编过了,用了好几个中断,端口之类的,而且还有的参数还未查明,关键的部位。
INT 0FH
INT 58H
...

这些中断只是居于ROM的程序为了修改int 19h而加载做的改动,其实非ROM方式最多只需要使用到INT 13H,其实不用也可以。

简单地说ATA命令就是把上面我提到的那个数据块格式把数据分别送到1F1~1F6,然后把ATA命令送往1F7,就算执行完毕(LBA48分两次传送,方式是FIFO)。然后再从1F0口存取数据(要多次),从1F7口读取状态,并不断监视1F7口读取的状态,其中1F3~1F5(部分1F6)传送所需要访问的地址,早期的硬盘接受CHS地址,剩下的就要用LBA地址了,LBA地址是从0开始的(也就是MBR相对LBA地址来说是0),至于每个ATA命令需要哪些参数,和具体参数的含义可以参考ATA标准文档。
通过向3F6口选择通道,1F6口选择主从设备(A0=Master,B0=Slave)。

比如我们要读取主通道Master设备上的MBR可以通过下面来完成(如果用程序的话,要记得加入延迟):
  1. debug
  2.      -o 3F6 04   ;设置通道
  3.      -o 3F6 00
  4.      -i 1F7         ;50H=硬盘存在
  5.      -o 1F2 1     ;读1个扇区
  6.      -o 1F3 0     ;设置开始的LBA地址
  7.      -o 1F4 0
  8.      -o 1F5 0
  9.      -o 1F6 e0    ;选择Master设备,该字节的各位含义参见ATA文档
  10.      -o 1F7 20    ;发送PIO读命令
  11.      -i 1F7          ;58H=硬盘数据准备好
  12.      -a 100
  13.      mov dx,1F0h ;数据口
  14.      mov si,1000h;存放数据的开始地址,如果选择DMA来自动传送,不要让结束地址越过FFFF。
  15.      mov cx,200h ;512字节
  16. L1: in al,dx
  17.      mov [si],al
  18.      inc si
  19.      loop L1
  20.      int 3
  21.      -g
复制代码
以上假设磁盘控制器的数据口基址为1F0。

[ 本帖最后由 netwinxp 于 2008-8-1 12:14 编辑 ]
回复

使用道具 举报

16#
发表于 2008-7-29 15:20:12 | 只看该作者
顶上去,认真学习中......
回复

使用道具 举报

17#
发表于 2008-7-30 09:35:54 | 只看该作者
谢谢分享  谢谢分享
回复

使用道具 举报

18#
发表于 2008-7-30 10:50:52 | 只看该作者
请问一下如何在DOS下检测硬盘是否支持HPA呢? DOS下的工具能否提供一下
回复

使用道具 举报

19#
发表于 2008-7-31 12:27:09 | 只看该作者
没下文啦?好像对此感兴趣的人不是太多:(
回复

使用道具 举报

20#
发表于 2008-7-31 22:27:13 | 只看该作者
我好像找到HPA的定义表了

先把硬盘清理了,试试,
(我所见的HPA表,并没有什么密码之类的,只是一定最大扇区,磁柱等的定义,锁定应该是改写了这个表,直接读取末尾分区的保留扇参数代替写入; 而解锁,应该是恢复原扇区数,启动末尾隐藏分区.)
回复

使用道具 举报

21#
发表于 2008-8-1 10:32:02 | 只看该作者
IDENTIFY返回的信息里面的word82 bit10是是否支持HPA的标志。word83 bit8是"SET_MAX security extension"是否支持的标志,bit10表示是否支持LBA48。

[ 本帖最后由 netwinxp 于 2008-8-1 10:38 编辑 ]
回复

使用道具 举报

22#
发表于 2008-8-1 10:44:19 | 只看该作者
原帖由 netwinxp 于 2008-7-31 12:27 发表
没下文啦?好像对此感兴趣的人不是太多:(

   对此感兴趣的人不是不多,只是如此深入的HPA知识,懂的人不多而已,说不上话了!
回复

使用道具 举报

23#
发表于 2008-8-1 21:42:49 | 只看该作者
顶。解释的越详细越好
回复

使用道具 举报

24#
发表于 2008-8-1 23:39:59 | 只看该作者
就是喜欢 netwinxp 和 xuxuezeng (孤烟)  大侠发的贴,无忧有你更精彩!!!
回复

使用道具 举报

25#
 楼主| 发表于 2008-8-2 15:38:32 | 只看该作者
写完了控制器地址,NETWINXP看看对不对
回复

使用道具 举报

26#
发表于 2008-8-7 20:47:13 | 只看该作者
仔细研究研究,谢谢LZ
回复

使用道具 举报

27#
发表于 2008-8-7 22:50:06 | 只看该作者
继续关注该话题!!!
回复

使用道具 举报

28#
发表于 2008-8-9 23:02:59 | 只看该作者
有点看不懂呀,自己只能看热闹了。
回复

使用道具 举报

29#
发表于 2008-8-20 01:22:21 | 只看该作者
认真学习 认真学习
回复

使用道具 举报

30#
发表于 2008-12-10 15:07:50 | 只看该作者
原帖由 netwinxp 于 2008-7-29 09:10 发表

这些中断只是居于ROM的程序为了修改int 19h而加载做的改动,其实非ROM方式最多只需要使用到INT 13H,其实不用也可以。

简单地说ATA命令就是把上面我提到的那个数据块格式把数据分别送到1F1~1F6,然后把ATA ...


磁盘控制器的数据口基址 如何读取?

一些SATA通过INT13的48功能可以得到,可是有一些机器得不到,这时如何得到磁盘控制器的数据口基址,谢谢!
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

小黑屋|手机版|Archiver|捐助支持|无忧启动 ( 闽ICP备05002490号-1 )

闽公网安备 35020302032614号

GMT+8, 2024-4-19 23:04

Powered by Discuz! X3.3

© 2001-2017 Comsenz Inc.

快速回复 返回顶部 返回列表