2024年1月9日发(作者:南门虹颖)
一键恢复技术方案调研报告
一、传统的一键恢复技术
现在几乎所有的品牌电脑都提供类似“急救中心”的系统一键还原功能,其原理是品牌机在出厂前会将操作系统的镜像存放于一个隐藏的系统分区中,一旦电脑出现不易修复的故障或系统由于病毒攻击、错误操作等原因而彻底崩溃,用户可以通过一个预置的特定快捷键将系统还原至品牌机出厂时的状态,使用起来的确十分方便。
原理是修改硬盘的主引导记录(MBR),用自己的一段引导代码来代替默认的引导代码,这段引导代码的作用就是在启动时给用户一个提示“按某某键开始恢复系统”(当然这个提示一般都是英文的),在用户按下这个键后,激活硬盘上的一个隐藏的主分区(Primary
Partition),然后从这个分区启动某个恢复软件来用事先备份好的系统恢复用户损坏的系统(或者给用户的系统分区制做备份)。当然,这里所谈到的一键恢复,都是指早期的一键恢复,包括IBM、联想、七喜等笔记本所用的一键恢复以及由此引发出来的像Foxrao的一键恢复精灵,而最新的一键恢复都开始使用HPA技术。
早期的一键恢复最大的特点就是那个隐藏分区在分区软件(包括Windows的磁盘管理器)中是可见的,你随时可以删除它,这也是不安全因素之一。当然,这用来应付普通用户的情况已经足够了。
二、基于HPA技术的系统恢复
传统的一键恢复有如下缺点:隐藏分区可被破坏、删除,造成无法成功恢复系统;隐藏分区必须为一个主分区,会对用户造成一些不便。
基于HPA技术的一键恢复系统则有如下优势:HPA技术可以提供用户级安全性保护,以全面保护HPA分区不被未授权用户访问、修改及删除;在最高等级的安全性下,无法访问、看到、删除HPA分区,无法克隆,而最低的安全性设置下,可以在操作系统中看到该分区,可删除、可克隆、可更新数据;不必占用一个主分区。
2.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(Logical Block Addressing,逻辑块寻址模式,说明见附录)区域。使用硬盘ATA命令进行设置,在设置了HPA的硬盘上,在表面看来,在使用中没有任何不同,只是一个容量“变小”的硬盘。由于是在硬盘本身设置,所以硬盘中的容量信息改变,因此,在BIOS和操作系统等环境下,是看不到隐藏的区域。
实质上,HPA就是设置最大的LBA值(设置最大可读扇区源码见附录),以达到保护的目的。
通俗的理解就是设置读取的硬盘最大的扇区号,从而把高端的内容隐藏起来,这个最大的扇区号能够写在硬盘的某个地方,因此即使把这个硬盘挂到其它机器上,正常情况下也看不到隐藏的内容,Fdisk、PowerQuest PartitionMagic 之类的工具也把这个硬盘当做一个稍小
容量的硬盘。现在HPA已经是用ATA-5的标准了,这个标准需要在HDD的 Firmware支持的。phoenix开发的Core Managed Environment (cME)、IBM的Access IBM、联想主板上的recovery easy II,都是利用HPA的例子。
图1 IBM HPA分区结构示意图
图2 HPA创建过程
2.2目前采用HPA技术的恢复系统简介
系统集成商
1、IBM的Access IBM
在2003年以后推出的机型,IBM采用新的HPA技术取代以前的基于分区的产品恢复方案(Partition-based recovery solutions)。
HPA在启动过程中通过Enter键或Access Thinkpad键来中断启动过程并激活一个叫做Access IBM Predesktop Menu的程序,该程序可以提供诊断系统、操作系统恢复、BIOS升级/恢复等功能。而原来的D2D系统是通过在开机过程中按F11激活Service Partition,并进行操作系统恢复。HPA技术可以集成Rapid Restore PC功能,可以在HPA分区中对用户数据进行备份。
网上对IBM的一键恢复系统研究的比较多。
2、联想的一键恢复4.5及leos系统
联想的一键恢复系统资料比较少,官方也只是简单的介绍,可下载leos安装光盘进行分析。
3、方正急救恢复系统
方正的急救恢复系统的相关资料太少了,在官方主页也没有找到任何资料,只是在网上下载了厂商部署急救中心工具盘,也没有安装成功。
第三方厂商
1、phoenix的 Phoenix FirstWare Recover Pro
2、超未来资讯有限公司的PCClone v1.64
三、一键恢复系统设计
3.1 设计要求
通过手工改造,实现安装方便、使用稳定,简单、一键恢复系统必须健壮,不易损坏等特点。
3.2 设计思路
使用HPAtool创建一个(现在还不能确定准确数量,一个还是两个,或者更多)HPA分区,修改MBR代码(引导程序示例以及注释可参考附录),使其在启动过程中按某个热键实现从HPA区中启动自己加载的备份软件(从速度等方面考虑,采用Ghost 8.3),进行备份、还原操作。
3.3目前存在的问题
(1) HPA分区的创建
大部分工具在创建HPA分区时都创建了多个分区,然后向相应模块加入到相应分区中。为什么创建多个HPA分区呢?难道多个分区中也存在一个类似“主分区”以实现启动功能的分区吗?IBM HPA的相关资料表明,在HPA分区中确实存在一个类似于硬盘MBR的部分,里面记录了各HPA分区的相关内容,就和分区表相似。但是,在“类似于硬盘MBR的部分”中,“可引导代码”部分和“第一个80分区”是由分区软件创建的?还是后来加入进去的?如果是后来加入进去的,具体的某个数据位代表什么含义呢?联想的已经做成bin模块向进加了。
(2) 如何访问、管理HPA分区
如果是系统集成商的,比如IBM、lenovo,则几乎不是什么问题,采用原装程序即可。但是,我们是DIY的,就必须考虑一下了。
既然int 13不能访问HPA分区,如果才能从该分区启动,并运行Ghost呢?方正提供了一个工具,UlockHPA,该工具运行于DOS环境,可打开HPA分区,进行操作,这只是一个方向。如果按照这个方向进行思考,则:运行该工具所需要的DOS环境从何而来,难道还需要在创建一个主分区安装DOS吗?可以在MBR或者在接下来的几个扇区(前63个扇区)中添加代码,实现相应功能吗?如果这样可以,那最好不过了。当按下热键时,自动打开HPA分区,当备份软件操作完成时,自动关闭该分区(这可能需要本分区其他脚本的支持了)。
(3) 当MBR遭到破坏时,可否手动快速恢复MBR,可否从光盘启动,从HPA分区提取镜像文件,进行恢复呢?
(4) 如何快速、方便的安装一键恢复系统。
四、所需基础知识
4.1 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接口等等。
如何判断外设是否正常,是否存在问题呢?我们经常说计算机BIOS进行自检,即检查计算机连接什么外设,这些外设是否正常工作,如果某个外设出现故障,BIOS能根据不同的故障发出不同的警报声。BIOS也是一段程序,它凭什么能做到上面所说的事情呢?原来,工程师在设计的时候,就考虑了自检功能。比如鼠标就设计了一个查询/应答命令。BIOS检查计算机是否存在鼠标时,只要向鼠标发一个查询命令,送到鼠标的命令寄存器。如果计算机连接有鼠标,那么鼠标就会进行回应,然后BIOS读状态寄存器。如果读出的值时0xff或0x00,则表示鼠标不存在或有故障。
总之,CPU是通过系统总线的I/O方式操作外设的。
4.2 硬盘操作基础知识
硬盘基本知识
硬盘基本知识如磁道、扇区、柱面、磁头数、簇、MBR、DBR等,可参考如下网址:
/blog/360822
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。
硬盘ATA命令
IDE控制器中有2组寄存器:命令寄存器和控制寄存器。命令寄存器被用来接受命令和传送数据;控制寄存器组用作磁盘控制。这2个寄存器组CS1FX/和CS3FX/信号区分。CS1FX/的地址范围是1F0H~1F7H,CS3FX/的地址范围是3F0H~3F7H。
目前,许多计算机配置了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方式的数据传输。
(1)错误寄存器(1F1HR):该寄存器是1个8位的寄存器,它反映控制寄存器在诊断方式或操作方式下的错误原因。在不同方式下有不同的意义。
诊断方式:硬盘控制器在加电、复位或执行驱动器诊断命令以后的工作方式。此时驱动器包含诊断码,该代码反映了诊断后的结果。
操作方式:硬盘控制器执行除诊断命令以外的所有命令后进入该方式,如果状态寄存器的ERR=1,则该寄存器包含命令执行后的错误代码。
(2)特性寄存器(1F1HW):一般情况下不使用该寄存器,根据ATA标准它被用来设置接口的某些特征。
(3)扇区数寄存器(1F2 R/W):它记录读、写命令的扇区数。当多扇区传输时,每完成1个扇区操作,该寄存器自动减1,直至为0,如果初值为0,则表示256。如果有错误发生,该寄存器包含已经操作成功的扇区数。
(4)扇区号寄存器(1F3H R/W):它记录读、写和校验命令指定的起始扇区号。如果驱动器使用逻辑块寻址(LBA,logical lock address)方式,该寄存器记录逻辑扇区号的0字节。
(5)柱面号寄存器:(1F4H 1F5H R/W):它记录读、写、校验、寻址和格式化命令指定的柱面号,ATA标准允许65536柱面,但早期的IDE控制器中只允许1024个柱面。低8位在1F4H寄存器中,高8位在1F5H寄存器中。如果是LBA寻址方式,这2个寄存器包含起始扇区的1和2字节。
(6)驱动器/磁头寄存器(1F6H R/W):它记录读、写、校验、寻址和格式化命令指定的驱动器号、磁头号和寻址方式。
(7)状态寄存器(1F7HR):它放映硬盘驱动器执行命令后的状态,该寄存器要清除中断请求信号,如果要避免清除中断,可以读辅助状态寄存器3F6H,这2个寄存器的内容完全相同。
(8)命令寄存器(1F7HW):该寄存器接收处理器输出的HDC命令,命令格式和含义如表
7所示。其中12种是强制性的(M),其它是选择性的(O)。有些命令有2个操作码,后1个是早期操作码,有的驱动器上仍在使用,如CON-NER驱动器。
(9)辅助状态寄存器(3F6HR):它包含与状态寄存器相同的内容,但读该寄存时不会清除中断请求信号。
(10)硬盘控制寄存器(3F6HW)
(11)驱动地址寄存器(3F7HR):该寄存器包含命令执行后的某些信息,它与软盘驱动器共享,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。
逻辑块地址与物理地址的关系为:LBA地址=(柱面号*磁头数+磁头号)*扇区数+扇区数-1。
采用LBA方式寻址,没有磁头和磁道的转换操作,在访问连扇区时,操作速度比物理地址方式要快。
LBA寻址方式虽然需要BIOS做些修改,但它与Microsoft和IBM的INT13功能扩展规范是兼容的。为了能够用LBA方式存取大于528MB的硬盘,IDE提供了二种方式供主机系统选择,这二种方式均在CMOS中设置。
硬盘I/O操作
(1)MBR代码的操作
(2)编写HPA Boot Loader
五、参考资料
ATA/ATAPI-4 revision 18 (final draft)
/project/
ATA/ATAPI-5 revision 3 (final draft)
/project/
还有ATA/ATAPI-6、ATA/ATAPI-7、ATA/ATAPI-8等其他资料,可以从/
下载。
Access IBM HPA header 标准文档 ANSI+NCITS+346-2001。相关链接地址:/ansidoc ... NSI+INCITS+346-2001
<“El Torito” Bootable CD-ROM Format Specification Version 1.0>
<自己动手写操作系统>
SYSOFT时空论坛,/
无忧启动论坛,/?fid=48
/topic/host-protected-area
嵌入式存储系统恢复机制的设计与实现,作 者:海深、陆阳、袁菲,合肥工业大学计算机与信息学院,合肥,230009 刊 名:计算机工程
硬盘备份和恢复软件的设计与实现,作 者: 王兴众、黄晓萍,中国地质大学,信息工程学院,湖北,武汉,430074 刊 名: 河南机电高等专科学校学报
六、附录
6.1 设置硬盘最大可读取扇区源码(C语言):
/* setmax.c - aeb, 000326 - use on 2.4.0test9 or newer */
/* IBM part thanks to Matan Ziv-Av
/*
* 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
#include
#include
#include
#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_PIDENTIFYn");
args[0] = WIN_PIDENTIFY;
if (ioctl(fd, HDIO_DRIVE_CMD, &args)) {
perror("HDIO_DRIVE_CMD");
fprintf(stderr,
"WIN_PIDENTIFY also failed - giving upn");
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%xn", 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%xn", 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] DEVICEn"
"n"
"The call "setmax --max M DEVICE" will do a SET_MAX commandn"
"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 commandn"
"to set the maximum accessible sector number D sectorsn"
"below end-of-disk.n"
"n"
"The call "setmax DEVICE" will do a READ_NATIVE_MAX_ADDRESSn"
"command, and report the maximum accessible sector number.n"
"n"
"This is IDE-only. Probably DEVICE is /dev/hdx for some ";
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 optionn");
fprintf(stderr, usage_txt);
exit(1);
}
}
if (optind < argc)
device = argv[optind];
if (!device) {
fprintf(stderr, "no device specified - "
" "setmax /dev/hdb"n");
fprintf(stderr, usage_txt);
exit(1);
}
printf("Using device %sn", device);
fd = open(device, O_RDONLY);
if (fd == -1) {
perror("open");
exit(1);
}
if (delta != -1 || max != -1) {
if (delta != -1)
printf("setting delta=%dn", delta);
else
printf("setting max=%dn", 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: %dn", mad);
printf("that is %lld bytes, %d.%d GBn",
bytes, hMB/10, hMB%10);
}
get_identity(fd);
return 0;
}
6.2 引导程序的注释文本及引导流程解释
参考/?tid=122262&extra=page%3D5
2024年1月9日发(作者:南门虹颖)
一键恢复技术方案调研报告
一、传统的一键恢复技术
现在几乎所有的品牌电脑都提供类似“急救中心”的系统一键还原功能,其原理是品牌机在出厂前会将操作系统的镜像存放于一个隐藏的系统分区中,一旦电脑出现不易修复的故障或系统由于病毒攻击、错误操作等原因而彻底崩溃,用户可以通过一个预置的特定快捷键将系统还原至品牌机出厂时的状态,使用起来的确十分方便。
原理是修改硬盘的主引导记录(MBR),用自己的一段引导代码来代替默认的引导代码,这段引导代码的作用就是在启动时给用户一个提示“按某某键开始恢复系统”(当然这个提示一般都是英文的),在用户按下这个键后,激活硬盘上的一个隐藏的主分区(Primary
Partition),然后从这个分区启动某个恢复软件来用事先备份好的系统恢复用户损坏的系统(或者给用户的系统分区制做备份)。当然,这里所谈到的一键恢复,都是指早期的一键恢复,包括IBM、联想、七喜等笔记本所用的一键恢复以及由此引发出来的像Foxrao的一键恢复精灵,而最新的一键恢复都开始使用HPA技术。
早期的一键恢复最大的特点就是那个隐藏分区在分区软件(包括Windows的磁盘管理器)中是可见的,你随时可以删除它,这也是不安全因素之一。当然,这用来应付普通用户的情况已经足够了。
二、基于HPA技术的系统恢复
传统的一键恢复有如下缺点:隐藏分区可被破坏、删除,造成无法成功恢复系统;隐藏分区必须为一个主分区,会对用户造成一些不便。
基于HPA技术的一键恢复系统则有如下优势:HPA技术可以提供用户级安全性保护,以全面保护HPA分区不被未授权用户访问、修改及删除;在最高等级的安全性下,无法访问、看到、删除HPA分区,无法克隆,而最低的安全性设置下,可以在操作系统中看到该分区,可删除、可克隆、可更新数据;不必占用一个主分区。
2.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(Logical Block Addressing,逻辑块寻址模式,说明见附录)区域。使用硬盘ATA命令进行设置,在设置了HPA的硬盘上,在表面看来,在使用中没有任何不同,只是一个容量“变小”的硬盘。由于是在硬盘本身设置,所以硬盘中的容量信息改变,因此,在BIOS和操作系统等环境下,是看不到隐藏的区域。
实质上,HPA就是设置最大的LBA值(设置最大可读扇区源码见附录),以达到保护的目的。
通俗的理解就是设置读取的硬盘最大的扇区号,从而把高端的内容隐藏起来,这个最大的扇区号能够写在硬盘的某个地方,因此即使把这个硬盘挂到其它机器上,正常情况下也看不到隐藏的内容,Fdisk、PowerQuest PartitionMagic 之类的工具也把这个硬盘当做一个稍小
容量的硬盘。现在HPA已经是用ATA-5的标准了,这个标准需要在HDD的 Firmware支持的。phoenix开发的Core Managed Environment (cME)、IBM的Access IBM、联想主板上的recovery easy II,都是利用HPA的例子。
图1 IBM HPA分区结构示意图
图2 HPA创建过程
2.2目前采用HPA技术的恢复系统简介
系统集成商
1、IBM的Access IBM
在2003年以后推出的机型,IBM采用新的HPA技术取代以前的基于分区的产品恢复方案(Partition-based recovery solutions)。
HPA在启动过程中通过Enter键或Access Thinkpad键来中断启动过程并激活一个叫做Access IBM Predesktop Menu的程序,该程序可以提供诊断系统、操作系统恢复、BIOS升级/恢复等功能。而原来的D2D系统是通过在开机过程中按F11激活Service Partition,并进行操作系统恢复。HPA技术可以集成Rapid Restore PC功能,可以在HPA分区中对用户数据进行备份。
网上对IBM的一键恢复系统研究的比较多。
2、联想的一键恢复4.5及leos系统
联想的一键恢复系统资料比较少,官方也只是简单的介绍,可下载leos安装光盘进行分析。
3、方正急救恢复系统
方正的急救恢复系统的相关资料太少了,在官方主页也没有找到任何资料,只是在网上下载了厂商部署急救中心工具盘,也没有安装成功。
第三方厂商
1、phoenix的 Phoenix FirstWare Recover Pro
2、超未来资讯有限公司的PCClone v1.64
三、一键恢复系统设计
3.1 设计要求
通过手工改造,实现安装方便、使用稳定,简单、一键恢复系统必须健壮,不易损坏等特点。
3.2 设计思路
使用HPAtool创建一个(现在还不能确定准确数量,一个还是两个,或者更多)HPA分区,修改MBR代码(引导程序示例以及注释可参考附录),使其在启动过程中按某个热键实现从HPA区中启动自己加载的备份软件(从速度等方面考虑,采用Ghost 8.3),进行备份、还原操作。
3.3目前存在的问题
(1) HPA分区的创建
大部分工具在创建HPA分区时都创建了多个分区,然后向相应模块加入到相应分区中。为什么创建多个HPA分区呢?难道多个分区中也存在一个类似“主分区”以实现启动功能的分区吗?IBM HPA的相关资料表明,在HPA分区中确实存在一个类似于硬盘MBR的部分,里面记录了各HPA分区的相关内容,就和分区表相似。但是,在“类似于硬盘MBR的部分”中,“可引导代码”部分和“第一个80分区”是由分区软件创建的?还是后来加入进去的?如果是后来加入进去的,具体的某个数据位代表什么含义呢?联想的已经做成bin模块向进加了。
(2) 如何访问、管理HPA分区
如果是系统集成商的,比如IBM、lenovo,则几乎不是什么问题,采用原装程序即可。但是,我们是DIY的,就必须考虑一下了。
既然int 13不能访问HPA分区,如果才能从该分区启动,并运行Ghost呢?方正提供了一个工具,UlockHPA,该工具运行于DOS环境,可打开HPA分区,进行操作,这只是一个方向。如果按照这个方向进行思考,则:运行该工具所需要的DOS环境从何而来,难道还需要在创建一个主分区安装DOS吗?可以在MBR或者在接下来的几个扇区(前63个扇区)中添加代码,实现相应功能吗?如果这样可以,那最好不过了。当按下热键时,自动打开HPA分区,当备份软件操作完成时,自动关闭该分区(这可能需要本分区其他脚本的支持了)。
(3) 当MBR遭到破坏时,可否手动快速恢复MBR,可否从光盘启动,从HPA分区提取镜像文件,进行恢复呢?
(4) 如何快速、方便的安装一键恢复系统。
四、所需基础知识
4.1 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接口等等。
如何判断外设是否正常,是否存在问题呢?我们经常说计算机BIOS进行自检,即检查计算机连接什么外设,这些外设是否正常工作,如果某个外设出现故障,BIOS能根据不同的故障发出不同的警报声。BIOS也是一段程序,它凭什么能做到上面所说的事情呢?原来,工程师在设计的时候,就考虑了自检功能。比如鼠标就设计了一个查询/应答命令。BIOS检查计算机是否存在鼠标时,只要向鼠标发一个查询命令,送到鼠标的命令寄存器。如果计算机连接有鼠标,那么鼠标就会进行回应,然后BIOS读状态寄存器。如果读出的值时0xff或0x00,则表示鼠标不存在或有故障。
总之,CPU是通过系统总线的I/O方式操作外设的。
4.2 硬盘操作基础知识
硬盘基本知识
硬盘基本知识如磁道、扇区、柱面、磁头数、簇、MBR、DBR等,可参考如下网址:
/blog/360822
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。
硬盘ATA命令
IDE控制器中有2组寄存器:命令寄存器和控制寄存器。命令寄存器被用来接受命令和传送数据;控制寄存器组用作磁盘控制。这2个寄存器组CS1FX/和CS3FX/信号区分。CS1FX/的地址范围是1F0H~1F7H,CS3FX/的地址范围是3F0H~3F7H。
目前,许多计算机配置了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方式的数据传输。
(1)错误寄存器(1F1HR):该寄存器是1个8位的寄存器,它反映控制寄存器在诊断方式或操作方式下的错误原因。在不同方式下有不同的意义。
诊断方式:硬盘控制器在加电、复位或执行驱动器诊断命令以后的工作方式。此时驱动器包含诊断码,该代码反映了诊断后的结果。
操作方式:硬盘控制器执行除诊断命令以外的所有命令后进入该方式,如果状态寄存器的ERR=1,则该寄存器包含命令执行后的错误代码。
(2)特性寄存器(1F1HW):一般情况下不使用该寄存器,根据ATA标准它被用来设置接口的某些特征。
(3)扇区数寄存器(1F2 R/W):它记录读、写命令的扇区数。当多扇区传输时,每完成1个扇区操作,该寄存器自动减1,直至为0,如果初值为0,则表示256。如果有错误发生,该寄存器包含已经操作成功的扇区数。
(4)扇区号寄存器(1F3H R/W):它记录读、写和校验命令指定的起始扇区号。如果驱动器使用逻辑块寻址(LBA,logical lock address)方式,该寄存器记录逻辑扇区号的0字节。
(5)柱面号寄存器:(1F4H 1F5H R/W):它记录读、写、校验、寻址和格式化命令指定的柱面号,ATA标准允许65536柱面,但早期的IDE控制器中只允许1024个柱面。低8位在1F4H寄存器中,高8位在1F5H寄存器中。如果是LBA寻址方式,这2个寄存器包含起始扇区的1和2字节。
(6)驱动器/磁头寄存器(1F6H R/W):它记录读、写、校验、寻址和格式化命令指定的驱动器号、磁头号和寻址方式。
(7)状态寄存器(1F7HR):它放映硬盘驱动器执行命令后的状态,该寄存器要清除中断请求信号,如果要避免清除中断,可以读辅助状态寄存器3F6H,这2个寄存器的内容完全相同。
(8)命令寄存器(1F7HW):该寄存器接收处理器输出的HDC命令,命令格式和含义如表
7所示。其中12种是强制性的(M),其它是选择性的(O)。有些命令有2个操作码,后1个是早期操作码,有的驱动器上仍在使用,如CON-NER驱动器。
(9)辅助状态寄存器(3F6HR):它包含与状态寄存器相同的内容,但读该寄存时不会清除中断请求信号。
(10)硬盘控制寄存器(3F6HW)
(11)驱动地址寄存器(3F7HR):该寄存器包含命令执行后的某些信息,它与软盘驱动器共享,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。
逻辑块地址与物理地址的关系为:LBA地址=(柱面号*磁头数+磁头号)*扇区数+扇区数-1。
采用LBA方式寻址,没有磁头和磁道的转换操作,在访问连扇区时,操作速度比物理地址方式要快。
LBA寻址方式虽然需要BIOS做些修改,但它与Microsoft和IBM的INT13功能扩展规范是兼容的。为了能够用LBA方式存取大于528MB的硬盘,IDE提供了二种方式供主机系统选择,这二种方式均在CMOS中设置。
硬盘I/O操作
(1)MBR代码的操作
(2)编写HPA Boot Loader
五、参考资料
ATA/ATAPI-4 revision 18 (final draft)
/project/
ATA/ATAPI-5 revision 3 (final draft)
/project/
还有ATA/ATAPI-6、ATA/ATAPI-7、ATA/ATAPI-8等其他资料,可以从/
下载。
Access IBM HPA header 标准文档 ANSI+NCITS+346-2001。相关链接地址:/ansidoc ... NSI+INCITS+346-2001
<“El Torito” Bootable CD-ROM Format Specification Version 1.0>
<自己动手写操作系统>
SYSOFT时空论坛,/
无忧启动论坛,/?fid=48
/topic/host-protected-area
嵌入式存储系统恢复机制的设计与实现,作 者:海深、陆阳、袁菲,合肥工业大学计算机与信息学院,合肥,230009 刊 名:计算机工程
硬盘备份和恢复软件的设计与实现,作 者: 王兴众、黄晓萍,中国地质大学,信息工程学院,湖北,武汉,430074 刊 名: 河南机电高等专科学校学报
六、附录
6.1 设置硬盘最大可读取扇区源码(C语言):
/* setmax.c - aeb, 000326 - use on 2.4.0test9 or newer */
/* IBM part thanks to Matan Ziv-Av
/*
* 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
#include
#include
#include
#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_PIDENTIFYn");
args[0] = WIN_PIDENTIFY;
if (ioctl(fd, HDIO_DRIVE_CMD, &args)) {
perror("HDIO_DRIVE_CMD");
fprintf(stderr,
"WIN_PIDENTIFY also failed - giving upn");
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%xn", 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%xn", 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] DEVICEn"
"n"
"The call "setmax --max M DEVICE" will do a SET_MAX commandn"
"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 commandn"
"to set the maximum accessible sector number D sectorsn"
"below end-of-disk.n"
"n"
"The call "setmax DEVICE" will do a READ_NATIVE_MAX_ADDRESSn"
"command, and report the maximum accessible sector number.n"
"n"
"This is IDE-only. Probably DEVICE is /dev/hdx for some ";
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 optionn");
fprintf(stderr, usage_txt);
exit(1);
}
}
if (optind < argc)
device = argv[optind];
if (!device) {
fprintf(stderr, "no device specified - "
" "setmax /dev/hdb"n");
fprintf(stderr, usage_txt);
exit(1);
}
printf("Using device %sn", device);
fd = open(device, O_RDONLY);
if (fd == -1) {
perror("open");
exit(1);
}
if (delta != -1 || max != -1) {
if (delta != -1)
printf("setting delta=%dn", delta);
else
printf("setting max=%dn", 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: %dn", mad);
printf("that is %lld bytes, %d.%d GBn",
bytes, hMB/10, hMB%10);
}
get_identity(fd);
return 0;
}
6.2 引导程序的注释文本及引导流程解释
参考/?tid=122262&extra=page%3D5