bin/111024: [request][patch] atacontrol: support for stand-by timer and un-protect area

URATAN Shigenobu uratan at po.iijnet.or.jp
Fri Mar 30 06:50:10 UTC 2007


>Number:         111024
>Category:       bin
>Synopsis:       [request][patch] atacontrol: support for stand-by timer and un-protect area
>Confidential:   no
>Severity:       non-critical
>Priority:       low
>Responsible:    freebsd-bugs
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:
>Class:          change-request
>Submitter-Id:   current-users
>Arrival-Date:   Fri Mar 30 06:50:02 GMT 2007
>Closed-Date:
>Last-Modified:
>Originator:     URATAN Shigenobu
>Release:        6.2-RELEASE
>Organization:
personal
>Environment:
FreeBSD  6.2-RELEASE FreeBSD 6.2-RELEASE #0: Fri Jan 12 10:40:27 UTC 2007     root at dessler.cse.buffalo.edu:/usr/obj/usr/src/sys/GENERIC  i386
>Description:
with the patch, 3 new functions will be added to atacontrol.

  1) atacontrol max device          (set max LBA to native)
  2) atacontrol idle device minutes (set idle timer)
  3) atacontrol pow device          (check power mode)

Some note PC has delivered with its recovery data in HDD
and it is protected. (means hidden by SET_MAX feature.)
Above 1) is used to unlock this protection to backup and
restore the recovery data, etc.
After reseting max LBA by 1), you shall re-attach the HDD by
'atacontrol reinit' to make kernel parameters refresh.
   (shall be booted from installer CD...)

With above 2), you can set up stand-by timer function of ATA-HDD,
and confirm current HDD's power save state with function 3).
Also you can disable stand-by timer if you specify ZERO minute.

If you try to use HDD's stand-by timer function, you may modify
ATA timeout setting in kernel, see pr=111023 to get more info.

>How-To-Repeat:

>Fix:


Patch attached with submission follows:

--- atacontrol.c.orig	Sun Mar 25 22:21:39 2007
+++ atacontrol.c	Mon Mar 26 14:11:23 2007
@@ -23,7 +23,7 @@
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
- * $FreeBSD: src/sbin/atacontrol/atacontrol.c,v 1.36.2.5 2006/03/16 21:30:09 sos Exp $
+ * $FreeBSD: src/sbin/atacontrol/atacontrol.c,v 1.36.2.5 2006/03/16 21:30:09 xxx Exp $
  */
 
 #include <sys/types.h>
@@ -38,6 +38,9 @@
 #include <string.h>
 #include <sysexits.h>
 
+int ata_set_max(int fd);
+int ata_set_idle_timer(int fd, int time);
+int ata_check_power_mode(int fd);
 const char *mode2str(int mode);
 int str2mode(char *str);
 void usage(void);
@@ -47,6 +50,176 @@
 int ata_cap_print(int fd);
 int info_print(int fd, int channel, int prchan);
 
+#define TIMEOUT  10		/* [sec] */
+
+int
+ata_set_max(int fd)
+{
+	/*
+	 * do NOT use ATA_READ_NATIVE_MAX_ADDDRESS48 and ATA_SET_MAX_ADDRESS48 !
+	 *
+	 * shall use  ATA_READ_NATIVE_MAX_ADDDRESS   and ATA_SET_MAX_ADDRESS and
+	 * make the kernel to switch to 48bit mode by making conditions below !
+	 *
+	 *    (request->u.ata.lba >= ATA_MAX_28BIT_LBA (268435455UL)) ||
+	 *    (request->u.ata.count > 256)
+	 */
+#define VALUE_VOLATILE  0
+
+	struct ata_params params;
+	struct ata_ioc_request req;
+	u_int64_t native_max;
+
+	/*
+	 * get identify device data at first
+	 */
+	if (ioctl(fd, IOCATAGPARM, &params) < 0)
+		return errno;
+
+	/*
+	 * get native max lba
+	 */
+	bzero(&req, sizeof(req));
+	req.u.ata.command = ATA_READ_NATIVE_MAX_ADDDRESS;
+	req.flags = ATA_CMD_CONTROL;
+	req.timeout = TIMEOUT;
+	if(params.support.command2 & ATA_SUPPORT_ADDRESS48)
+		req.u.ata.count |= 0x1000;		/* force to use 48bit command */
+
+	if (ioctl(fd, IOCATAREQUEST, &req) < 0) {
+		printf("fail ioctl()\n");
+		return errno;
+	}
+	native_max = req.u.ata.lba;
+	printf("native max lba: %llu/0x%llx\n", native_max, native_max);
+
+	/*
+	 * set max lba to native value
+	 */
+	bzero(&req, sizeof(req));
+	req.u.ata.command = ATA_SET_MAX_ADDRESS;
+#if VALUE_VOLATILE
+	req.u.ata.count = 1;		/* Value Volatile */
+#else /* VALUE_VOLATILE */
+	req.u.ata.count = 0;		/* Value NOT Volatile */
+#endif /* VALUE_VOLATILE */
+	req.u.ata.lba = native_max;
+	req.flags = ATA_CMD_CONTROL;
+	req.timeout = TIMEOUT;
+	if(params.support.command2 & ATA_SUPPORT_ADDRESS48)
+		req.u.ata.count |= 0x1000;		/* force to use 48bit command */
+
+	if (ioctl(fd, IOCATAREQUEST, &req) < 0) {
+		printf("fail ioctl()\n");
+		return errno;
+	}
+
+	return 0;
+}
+
+int
+ata_set_idle_timer(int fd, int time)
+{
+	struct ata_ioc_request req;
+	int sc;
+
+	/*
+	 * given unit is [min], convert it to [sec]
+	 */
+	time *= 60;
+
+	/*
+	 * convert time to Sector Count register value
+	 *
+	 *   SecReg | Corresponding timeout period
+	 *   -------+----------------------------------
+	 *        0 | Timeout disabled
+	 *    - - - + - - - - - - - - - - - - - - - -
+	 *        1 |      5sec
+	 *        2 |     10sec
+	 *       .. |     ..
+	 *      239 |   1195sec
+	 *      240 |   1200sec  (20min)
+	 *    - - - + - - - - - - - - - - - - - - - -
+	 *      241 |   1800sec  (30min, 0.5hour)
+	 *      242 |   3600sec  (60min, 1.0hour)
+	 *       .. |     ..
+	 *      250 |  18000sec (300min, 5.0hour)
+	 *      251 |  19800sec (330min, 5.5hour)
+	 *    ------+---------------------------------
+	 *      252 |      21min
+	 *      253 | Period between 8 hours and 12 hours
+	 *      254 |    (reserved)
+	 *      255 |      21min 15seconds
+	 *   -------+----------------------------------
+	 */
+	if(time <= 0) {
+		sc = 0;					/* Timeout disabled */
+	} else if(time <= 1200) {
+		sc = (time + 4) / 5;	/* 5sec step until 20min */
+	} else if(time <= 19800) {
+		sc = (time + (30*60-1)) / (30*60) + 240;
+								/* 30min step until 5.5hour */
+	} else {
+		 sc = 0xfd;				/* Period between 8hours and 12hours */
+	}
+
+	bzero(&req, sizeof(req));
+	req.u.ata.command = ATA_IDLE_CMD;	/* 8 */
+	req.u.ata.count = sc;				/* 16, ==> COUNT */
+	req.flags = ATA_CMD_CONTROL;
+	req.timeout = TIMEOUT;
+
+	if (ioctl(fd, IOCATAREQUEST, &req) < 0) {
+		printf("fail ioctl()\n");
+		return errno;
+	}
+
+	printf("set idle timer ");
+	if(sc == 0) {
+		printf("disabled\n");
+	} else if(sc <= 240) {
+		printf("to %d min\n", (sc * 5) / 60);
+	} else if(sc <= 251) {
+		printf("to %d min\n", (sc - 240) * 30);
+	} else {
+		printf("to about 8--12 hours\n");
+	}
+
+	return 0;
+}
+
+int
+ata_check_power_mode(int fd)
+{
+#define ATA_CHECK_POWER_MODE  0xe5
+	struct ata_ioc_request req;
+
+	bzero(&req, sizeof(req));
+	req.u.ata.command = ATA_CHECK_POWER_MODE;
+	req.flags = ATA_CMD_CONTROL;
+	req.timeout = TIMEOUT;
+
+	if (ioctl(fd, IOCATAREQUEST, &req) < 0) {
+		printf("fail ioctl()\n");
+		return errno;
+	}
+
+	switch(req.u.ata.count) {
+	case 0x00:	printf("in Standby mode\n");
+				break;
+	case 0x80:	printf("in Idle mode\n");
+				break;
+	case 0xff:	printf("in Active (or Idle) mode\n");
+				break;
+	default:	printf("unknown\n");
+				break;
+	}
+
+	return 0;
+}
+
+
 const char *
 mode2str(int mode)
 {
@@ -111,6 +284,9 @@
 		"        atacontrol status array\n"
 		"        atacontrol mode device [mode]\n"
 		"        atacontrol cap device\n"
+		"        atacontrol max device          (set max LBA to native)\n"
+		"        atacontrol idle device minutes (set idle timer)\n"
+		"        atacontrol pow device          (check power mode)\n"
 	);
 	exit(EX_USAGE);
 }
@@ -250,6 +426,13 @@
 		ATA_ACOUSTIC_CURRENT(parm->acoustic),
 		ATA_ACOUSTIC_VENDOR(parm->acoustic),
 		ATA_ACOUSTIC_VENDOR(parm->acoustic));
+
+	printf("host protected area            %s	%s\n",
+		parm->support.command1 & ATA_SUPPORT_PROTECTED ? "yes" : "no",
+		parm->enabled.command1 & ATA_SUPPORT_PROTECTED ? "yes" : "no");
+	printf(" (security extensions)         %s	%s\n",
+		parm->support.command2 & ATA_SUPPORT_MAXSECURITY ? "yes" : "no",
+		parm->enabled.command2 & ATA_SUPPORT_MAXSECURITY ? "yes" : "no");
 }
 
 int
@@ -343,6 +526,60 @@
 		if ((fd = open(device, O_RDONLY)) < 0)
 			err(1, "device not found");
 		ata_cap_print(fd);
+		exit(EX_OK);
+	}
+	if (!strcmp(argv[1], "max") && argc == 3) {
+		int disk;
+		char device[64];
+
+		if (!(sscanf(argv[2], "ad%d", &disk) == 1 ||
+		      sscanf(argv[2], "acd%d", &disk) == 1 ||
+		      sscanf(argv[2], "afd%d", &disk) == 1 ||
+		      sscanf(argv[2], "ast%d", &disk) == 1)) {
+			fprintf(stderr, "atacontrol: Invalid device %s\n",
+				argv[2]);
+			exit(EX_USAGE);
+		}
+		sprintf(device, "/dev/%s", argv[2]);
+		if ((fd = open(device, O_RDONLY)) < 0)
+			err(1, "device not found");
+		ata_set_max(fd);
+		exit(EX_OK);
+	}
+	if (!strcmp(argv[1], "idle") && argc == 4) {
+		int disk;
+		char device[64];
+
+		if (!(sscanf(argv[2], "ad%d", &disk) == 1 ||
+		      sscanf(argv[2], "acd%d", &disk) == 1 ||
+		      sscanf(argv[2], "afd%d", &disk) == 1 ||
+		      sscanf(argv[2], "ast%d", &disk) == 1)) {
+			fprintf(stderr, "atacontrol: Invalid device %s\n",
+				argv[2]);
+			exit(EX_USAGE);
+		}
+		sprintf(device, "/dev/%s", argv[2]);
+		if ((fd = open(device, O_RDONLY)) < 0)
+			err(1, "device not found");
+		ata_set_idle_timer(fd, atoi(argv[3]));
+		exit(EX_OK);
+	}
+	if (!strcmp(argv[1], "pow") && argc == 3) {
+		int disk;
+		char device[64];
+
+		if (!(sscanf(argv[2], "ad%d", &disk) == 1 ||
+		      sscanf(argv[2], "acd%d", &disk) == 1 ||
+		      sscanf(argv[2], "afd%d", &disk) == 1 ||
+		      sscanf(argv[2], "ast%d", &disk) == 1)) {
+			fprintf(stderr, "atacontrol: Invalid device %s\n",
+				argv[2]);
+			exit(EX_USAGE);
+		}
+		sprintf(device, "/dev/%s", argv[2]);
+		if ((fd = open(device, O_RDONLY)) < 0)
+			err(1, "device not found");
+		ata_check_power_mode(fd);
 		exit(EX_OK);
 	}
 

>Release-Note:
>Audit-Trail:
>Unformatted:


More information about the freebsd-bugs mailing list