svn commit: r251498 - in stable/9: sbin/camcontrol sys/sys

Steven Hartland smh at FreeBSD.org
Fri Jun 7 14:23:12 UTC 2013


Author: smh
Date: Fri Jun  7 14:23:11 2013
New Revision: 251498
URL: http://svnweb.freebsd.org/changeset/base/251498

Log:
  MFC r249895:
  Add Host Protected Area (HPA) support to camcontrol
  
  MFC r249904:
  mdoc: remove superfluous paragraph macro.

Modified:
  stable/9/sbin/camcontrol/camcontrol.8
  stable/9/sbin/camcontrol/camcontrol.c
  stable/9/sys/sys/ata.h
Directory Properties:
  stable/9/sbin/camcontrol/   (props changed)
  stable/9/sys/   (props changed)
  stable/9/sys/sys/   (props changed)

Modified: stable/9/sbin/camcontrol/camcontrol.8
==============================================================================
--- stable/9/sbin/camcontrol/camcontrol.8	Fri Jun  7 14:10:18 2013	(r251497)
+++ stable/9/sbin/camcontrol/camcontrol.8	Fri Jun  7 14:23:11 2013	(r251498)
@@ -27,7 +27,7 @@
 .\"
 .\" $FreeBSD$
 .\"
-.Dd June 4, 2012
+.Dd April 24, 2013
 .Dt CAMCONTROL 8
 .Os
 .Sh NAME
@@ -243,6 +243,18 @@
 .Op Fl U Ar user|master
 .Op Fl y
 .Nm
+.Ic hpa
+.Op device id
+.Op generic args
+.Op Fl f
+.Op Fl l
+.Op Fl P
+.Op Fl p Ar pwd
+.Op Fl q
+.Op Fl s Ar max_sectors
+.Op Fl U Ar pwd
+.Op Fl y
+.Nm
 .Ic help
 .Sh DESCRIPTION
 The
@@ -1204,6 +1216,73 @@ password for the specified user the comm
 .Pp
 The password in all cases is limited to 32 characters, longer passwords will
 fail.
+.It Ic hpa
+Update or report Host Protected Area details.
+By default
+.Nm
+will print out the HPA support and associated settings of the device.
+The
+.Ic hpa
+command takes several optional arguments:
+.Bl -tag -width 0n
+.It Fl f
+.Pp
+Freeze the HPA configuration of the specified device.
+.Pp
+After command completion any other commands that update the HPA configuration
+shall be command aborted.
+Frozen mode is disabled by power-off or hardware reset.
+.It Fl l
+.Pp
+Lock the HPA configuration of the device until a successful call to unlock or
+the next power-on reset occurs.
+.It Fl P
+.Pp
+Make the HPA max sectors persist across power-on reset or a hardware reset.
+This must be used in combination with
+.Fl s Ar max_sectors
+.
+.It Fl p Ar pwd
+.Pp
+Set the HPA configuration password required for unlock calls.
+.It Fl q
+.Pp
+Be quiet, do not print any status messages.
+This option will not disable the questions.
+To disable questions, use the
+.Fl y
+argument, below.
+.It Fl s Ar max_sectors
+.Pp
+Configures the maximum user accessible sectors of the device.
+This will change the number of sectors the device reports.
+.Pp
+.Em WARNING! WARNING! WARNING!
+.Pp
+Changing the max sectors of a device using this option will make the data on
+the device beyond the specified value inaccessible.
+.Pp
+Only one successful
+.Fl s Ar max_sectors
+call can be made without a power-on reset or a hardware reset of the device.
+.It Fl U Ar pwd
+.Pp
+Unlock the HPA configuration of the specified device using the given password.
+If the password specified doesn't match the password configured via
+.Fl p Ar pwd
+the command will fail.
+.Pp
+After 5 failed unlock calls, due to password miss-match, the device will refuse
+additional unlock calls until after a power-on reset.
+.It Fl y
+.Pp
+Confirm yes to dangerous options such as
+.Fl e
+without prompting for confirmation
+.Pp
+.El
+The password for all HPA commands is limited to 32 characters, longer passwords
+will fail.
 .It Ic fwdownload
 Program firmware of the named SCSI device using the image file provided.
 .Pp
@@ -1396,6 +1475,28 @@ data from the device, so backup your dat
 .Pp
 This command can be used used against an SSD drive to restoring it to
 factory default write performance.
+.Bd -literal -offset indent
+camcontrol hpa ada0
+.Ed
+.Pp
+Report HPA support and settings for ada0 (also reported via
+identify).
+.Bd -literal -offset indent
+camcontrol hpa ada0 -s 10240
+.Ed
+.Pp
+Enables HPA on ada0 setting the maximum reported sectors to 10240.
+.Pp
+.Em WARNING! WARNING! WARNING!
+.Pp
+This will
+.Em PREVENT ACCESS
+to all data on the device beyond this limit until HPA is disabled by setting
+HPA to native max sectors of the device, which can only be done after a
+power-on or hardware reset!
+.Pp
+.Em DO NOT
+use this on a device which has an active filesystem!
 .Sh SEE ALSO
 .Xr cam 3 ,
 .Xr cam_cdbparse 3 ,

Modified: stable/9/sbin/camcontrol/camcontrol.c
==============================================================================
--- stable/9/sbin/camcontrol/camcontrol.c	Fri Jun  7 14:10:18 2013	(r251497)
+++ stable/9/sbin/camcontrol/camcontrol.c	Fri Jun  7 14:23:11 2013	(r251498)
@@ -45,6 +45,10 @@ __FBSDID("$FreeBSD$");
 #include <ctype.h>
 #include <err.h>
 #include <libutil.h>
+#ifndef MINIMALISTIC
+#include <limits.h>
+#include <inttypes.h>
+#endif
 
 #include <cam/cam.h>
 #include <cam/cam_debug.h>
@@ -88,7 +92,8 @@ typedef enum {
 	CAM_CMD_SMP_PHYLIST	= 0x0000001a,
 	CAM_CMD_SMP_MANINFO	= 0x0000001b,
 	CAM_CMD_DOWNLOAD_FW	= 0x0000001c,
-	CAM_CMD_SECURITY	= 0x0000001d
+	CAM_CMD_SECURITY	= 0x0000001d,
+	CAM_CMD_HPA		= 0x0000001e
 } cam_cmdmask;
 
 typedef enum {
@@ -135,6 +140,29 @@ struct camcontrol_opts {
 };
 
 #ifndef MINIMALISTIC
+struct ata_res_pass16 {
+	u_int16_t reserved[5];
+	u_int8_t flags;
+	u_int8_t error;
+	u_int8_t sector_count_exp;
+	u_int8_t sector_count;
+	u_int8_t lba_low_exp;
+	u_int8_t lba_low;
+	u_int8_t lba_mid_exp;
+	u_int8_t lba_mid;
+	u_int8_t lba_high_exp;
+	u_int8_t lba_high;
+	u_int8_t device;
+	u_int8_t status;
+};
+
+struct ata_set_max_pwd
+{
+	u_int16_t reserved1;
+	u_int8_t password[32];
+	u_int16_t reserved2[239];
+};
+
 static const char scsicmd_opts[] = "a:c:dfi:o:r";
 static const char readdefect_opts[] = "f:GP";
 static const char negotiate_opts[] = "acD:M:O:qR:T:UW:";
@@ -186,6 +214,7 @@ static struct camcontrol_opts option_tab
 	{"sleep", CAM_CMD_SLEEP, CAM_ARG_NONE, ""},
 	{"fwdownload", CAM_CMD_DOWNLOAD_FW, CAM_ARG_NONE, "f:ys"},
 	{"security", CAM_CMD_SECURITY, CAM_ARG_NONE, "d:e:fh:k:l:qs:T:U:y"},
+	{"hpa", CAM_CMD_HPA, CAM_ARG_NONE, "Pflp:qs:U:y"},
 #endif /* MINIMALISTIC */
 	{"help", CAM_CMD_USAGE, CAM_ARG_NONE, NULL},
 	{"-?", CAM_CMD_USAGE, CAM_ARG_NONE, NULL},
@@ -280,6 +309,8 @@ static int atapm(struct cam_device *devi
 		 char *combinedopt, int retry_count, int timeout);
 static int atasecurity(struct cam_device *device, int retry_count, int timeout,
 		       int argc, char **argv, char *combinedopt);
+static int atahpa(struct cam_device *device, int retry_count, int timeout,
+		  int argc, char **argv, char *combinedopt);
 
 #endif /* MINIMALISTIC */
 #ifndef min
@@ -1128,6 +1159,38 @@ xferrate_bailout:
 }
 
 static void
+atahpa_print(struct ata_params *parm, u_int64_t hpasize, int header)
+{
+	u_int32_t lbasize = (u_int32_t)parm->lba_size_1 |
+				((u_int32_t)parm->lba_size_2 << 16);
+
+	u_int64_t lbasize48 = ((u_int64_t)parm->lba_size48_1) |
+				((u_int64_t)parm->lba_size48_2 << 16) |
+				((u_int64_t)parm->lba_size48_3 << 32) |
+				((u_int64_t)parm->lba_size48_4 << 48);
+
+	if (header) {
+		printf("\nFeature                      "
+		       "Support  Enabled   Value\n");
+	}
+
+	printf("Host Protected Area (HPA)      ");
+	if (parm->support.command1 & ATA_SUPPORT_PROTECTED) {
+		u_int64_t lba = lbasize48 ? lbasize48 : lbasize;
+		printf("yes      %s     %ju/%ju\n", (hpasize > lba) ? "yes" : "no ",
+		        lba, hpasize);
+
+		printf("HPA - Security                 ");
+		if (parm->support.command1 & ATA_SUPPORT_MAXSECURITY)
+			printf("yes\n");
+		else
+			printf("no\n");
+	} else {
+		printf("no\n");
+	}
+}
+
+static void
 atacapprint(struct ata_params *parm)
 {
 	u_int32_t lbasize = (u_int32_t)parm->lba_size_1 |
@@ -1554,6 +1617,83 @@ ata_do_28bit_cmd(struct cam_device *devi
 	return ata_cam_send(device, ccb, quiet);
 }
 
+static int
+ata_do_cmd(struct cam_device *device, union ccb *ccb, int retries,
+	   u_int32_t flags, u_int8_t protocol, u_int8_t ata_flags,
+	   u_int8_t tag_action, u_int8_t command, u_int8_t features,
+	   u_int64_t lba, u_int8_t sector_count, u_int8_t *data_ptr,
+	   u_int16_t dxfer_len, int timeout, int force48bit)
+{
+	int retval;
+
+	retval = ata_try_pass_16(device);
+	if (retval == -1)
+		return (1);
+
+	if (retval == 1) {
+		int error;
+
+		/* Try using SCSI Passthrough */
+		error = ata_do_pass_16(device, ccb, retries, flags, protocol,
+				      ata_flags, tag_action, command, features,
+				      lba, sector_count, data_ptr, dxfer_len,
+				      timeout, 0);
+
+		if (ata_flags & AP_FLAG_CHK_COND) {
+			/* Decode ata_res from sense data */
+			struct ata_res_pass16 *res_pass16;
+			struct ata_res *res;
+			u_int i;
+			u_int16_t *ptr;
+
+			/* sense_data is 4 byte aligned */
+			ptr = (uint16_t*)(uintptr_t)&ccb->csio.sense_data;
+			for (i = 0; i < sizeof(*res_pass16) / 2; i++)
+				ptr[i] = le16toh(ptr[i]);
+
+			/* sense_data is 4 byte aligned */
+			res_pass16 = (struct ata_res_pass16 *)(uintptr_t)
+			    &ccb->csio.sense_data;
+			res = &ccb->ataio.res;
+			res->flags = res_pass16->flags;
+			res->status = res_pass16->status;
+			res->error = res_pass16->error;
+			res->lba_low = res_pass16->lba_low;
+			res->lba_mid = res_pass16->lba_mid;
+			res->lba_high = res_pass16->lba_high;
+			res->device = res_pass16->device;
+			res->lba_low_exp = res_pass16->lba_low_exp;
+			res->lba_mid_exp = res_pass16->lba_mid_exp;
+			res->lba_high_exp = res_pass16->lba_high_exp;
+			res->sector_count = res_pass16->sector_count;
+			res->sector_count_exp = res_pass16->sector_count_exp;
+		}
+
+		return (error);
+	}
+
+	bzero(&(&ccb->ccb_h)[1], sizeof(struct ccb_ataio) -
+	      sizeof(struct ccb_hdr));
+	cam_fill_ataio(&ccb->ataio,
+		       retries,
+		       NULL,
+		       flags,
+		       tag_action,
+		       data_ptr,
+		       dxfer_len,
+		       timeout);
+
+	if (force48bit || lba > ATA_MAX_28BIT_LBA)
+		ata_48bit_cmd(&ccb->ataio, command, features, lba, sector_count);
+	else
+		ata_28bit_cmd(&ccb->ataio, command, features, lba, sector_count);
+
+	if (ata_flags & AP_FLAG_CHK_COND)
+		ccb->ataio.cmd.flags |= CAM_ATAIO_NEEDRESULT;
+
+	return ata_cam_send(device, ccb, 0);
+}
+
 static void
 dump_data(uint16_t *ptr, uint32_t len)
 {
@@ -1571,6 +1711,278 @@ dump_data(uint16_t *ptr, uint32_t len)
 }
 
 static int
+atahpa_proc_resp(struct cam_device *device, union ccb *ccb,
+		 int is48bit, u_int64_t *hpasize)
+{
+	struct ata_res *res;
+
+	res = &ccb->ataio.res;
+	if (res->status & ATA_STATUS_ERROR) {
+		if (arglist & CAM_ARG_VERBOSE) {
+			cam_error_print(device, ccb, CAM_ESF_ALL,
+					CAM_EPF_ALL, stderr);
+			printf("error = 0x%02x, sector_count = 0x%04x, "
+			       "device = 0x%02x, status = 0x%02x\n",
+			       res->error, res->sector_count,
+			       res->device, res->status);
+		}
+
+		if (res->error & ATA_ERROR_ID_NOT_FOUND) {
+			warnx("Max address has already been set since "
+			      "last power-on or hardware reset");
+		}
+
+		return (1);
+	}
+
+	if (arglist & CAM_ARG_VERBOSE) {
+		fprintf(stdout, "%s%d: Raw native max data:\n",
+			device->device_name, device->dev_unit_num);
+		/* res is 4 byte aligned */
+		dump_data((uint16_t*)(uintptr_t)res, sizeof(struct ata_res));
+
+		printf("error = 0x%02x, sector_count = 0x%04x, device = 0x%02x, "
+		       "status = 0x%02x\n", res->error, res->sector_count,
+		       res->device, res->status);
+	}
+
+	if (hpasize != NULL) {
+		if (is48bit) {
+			*hpasize = (((u_int64_t)((res->lba_high_exp << 16) |
+			    (res->lba_mid_exp << 8) | res->lba_low_exp) << 24) |
+			    ((res->lba_high << 16) | (res->lba_mid << 8) |
+			    res->lba_low)) + 1;
+		} else {
+			*hpasize = (((res->device & 0x0f) << 24) |
+			    (res->lba_high << 16) | (res->lba_mid << 8) |
+			    res->lba_low) + 1;
+		}
+	}
+
+	return (0);
+}
+
+static int
+ata_read_native_max(struct cam_device *device, int retry_count,
+		      u_int32_t timeout, union ccb *ccb,
+		      struct ata_params *parm, u_int64_t *hpasize)
+{
+	int error;
+	u_int cmd, is48bit;
+	u_int8_t protocol;
+
+	is48bit = parm->support.command2 & ATA_SUPPORT_ADDRESS48;
+	protocol = AP_PROTO_NON_DATA;
+
+	if (is48bit) {
+		cmd = ATA_READ_NATIVE_MAX_ADDRESS48;
+		protocol |= AP_EXTEND;
+	} else {
+		cmd = ATA_READ_NATIVE_MAX_ADDRESS;
+	}
+
+	error = ata_do_cmd(device,
+			   ccb,
+			   retry_count,
+			   /*flags*/CAM_DIR_IN,
+			   /*protocol*/protocol,
+			   /*ata_flags*/AP_FLAG_CHK_COND,
+			   /*tag_action*/MSG_SIMPLE_Q_TAG,
+			   /*command*/cmd,
+			   /*features*/0,
+			   /*lba*/0,
+			   /*sector_count*/0,
+			   /*data_ptr*/NULL,
+			   /*dxfer_len*/0,
+			   timeout ? timeout : 1000,
+			   is48bit);
+
+	if (error)
+		return (error);
+
+	return atahpa_proc_resp(device, ccb, is48bit, hpasize);
+}
+
+static int
+atahpa_set_max(struct cam_device *device, int retry_count,
+	      u_int32_t timeout, union ccb *ccb,
+	      int is48bit, u_int64_t maxsize, int persist)
+{
+	int error;
+	u_int cmd;
+	u_int8_t protocol;
+
+	protocol = AP_PROTO_NON_DATA;
+
+	if (is48bit) {
+		cmd = ATA_SET_MAX_ADDRESS48;
+		protocol |= AP_EXTEND;
+	} else {
+		cmd = ATA_SET_MAX_ADDRESS;
+	}
+
+	/* lba's are zero indexed so the max lba is requested max - 1 */
+	if (maxsize)
+		maxsize--;
+
+	error = ata_do_cmd(device,
+			   ccb,
+			   retry_count,
+			   /*flags*/CAM_DIR_OUT,
+			   /*protocol*/protocol,
+			   /*ata_flags*/AP_FLAG_CHK_COND,
+			   /*tag_action*/MSG_SIMPLE_Q_TAG,
+			   /*command*/cmd,
+			   /*features*/ATA_HPA_FEAT_MAX_ADDR,
+			   /*lba*/maxsize,
+			   /*sector_count*/persist,
+			   /*data_ptr*/NULL,
+			   /*dxfer_len*/0,
+			   timeout ? timeout : 1000,
+			   is48bit);
+
+	if (error)
+		return (error);
+
+	return atahpa_proc_resp(device, ccb, is48bit, NULL);
+}
+
+static int
+atahpa_password(struct cam_device *device, int retry_count,
+		u_int32_t timeout, union ccb *ccb,
+		int is48bit, struct ata_set_max_pwd *pwd)
+{
+	int error;
+	u_int cmd;
+	u_int8_t protocol;
+
+	protocol = AP_PROTO_PIO_OUT;
+	cmd = (is48bit) ? ATA_SET_MAX_ADDRESS48 : ATA_SET_MAX_ADDRESS;
+
+	error = ata_do_cmd(device,
+			   ccb,
+			   retry_count,
+			   /*flags*/CAM_DIR_OUT,
+			   /*protocol*/protocol,
+			   /*ata_flags*/AP_FLAG_CHK_COND,
+			   /*tag_action*/MSG_SIMPLE_Q_TAG,
+			   /*command*/cmd,
+			   /*features*/ATA_HPA_FEAT_SET_PWD,
+			   /*lba*/0,
+			   /*sector_count*/0,
+			   /*data_ptr*/(u_int8_t*)pwd,
+			   /*dxfer_len*/sizeof(struct ata_set_max_pwd),
+			   timeout ? timeout : 1000,
+			   is48bit);
+
+	if (error)
+		return (error);
+
+	return atahpa_proc_resp(device, ccb, is48bit, NULL);
+}
+
+static int
+atahpa_lock(struct cam_device *device, int retry_count,
+	    u_int32_t timeout, union ccb *ccb, int is48bit)
+{
+	int error;
+	u_int cmd;
+	u_int8_t protocol;
+
+	protocol = AP_PROTO_NON_DATA;
+	cmd = (is48bit) ? ATA_SET_MAX_ADDRESS48 : ATA_SET_MAX_ADDRESS;
+
+	error = ata_do_cmd(device,
+			   ccb,
+			   retry_count,
+			   /*flags*/CAM_DIR_OUT,
+			   /*protocol*/protocol,
+			   /*ata_flags*/AP_FLAG_CHK_COND,
+			   /*tag_action*/MSG_SIMPLE_Q_TAG,
+			   /*command*/cmd,
+			   /*features*/ATA_HPA_FEAT_LOCK,
+			   /*lba*/0,
+			   /*sector_count*/0,
+			   /*data_ptr*/NULL,
+			   /*dxfer_len*/0,
+			   timeout ? timeout : 1000,
+			   is48bit);
+
+	if (error)
+		return (error);
+
+	return atahpa_proc_resp(device, ccb, is48bit, NULL);
+}
+
+static int
+atahpa_unlock(struct cam_device *device, int retry_count,
+	      u_int32_t timeout, union ccb *ccb,
+	      int is48bit, struct ata_set_max_pwd *pwd)
+{
+	int error;
+	u_int cmd;
+	u_int8_t protocol;
+
+	protocol = AP_PROTO_PIO_OUT;
+	cmd = (is48bit) ? ATA_SET_MAX_ADDRESS48 : ATA_SET_MAX_ADDRESS;
+
+	error = ata_do_cmd(device,
+			   ccb,
+			   retry_count,
+			   /*flags*/CAM_DIR_OUT,
+			   /*protocol*/protocol,
+			   /*ata_flags*/AP_FLAG_CHK_COND,
+			   /*tag_action*/MSG_SIMPLE_Q_TAG,
+			   /*command*/cmd,
+			   /*features*/ATA_HPA_FEAT_UNLOCK,
+			   /*lba*/0,
+			   /*sector_count*/0,
+			   /*data_ptr*/(u_int8_t*)pwd,
+			   /*dxfer_len*/sizeof(struct ata_set_max_pwd),
+			   timeout ? timeout : 1000,
+			   is48bit);
+
+	if (error)
+		return (error);
+
+	return atahpa_proc_resp(device, ccb, is48bit, NULL);
+}
+
+static int
+atahpa_freeze_lock(struct cam_device *device, int retry_count,
+		   u_int32_t timeout, union ccb *ccb, int is48bit)
+{
+	int error;
+	u_int cmd;
+	u_int8_t protocol;
+
+	protocol = AP_PROTO_NON_DATA;
+	cmd = (is48bit) ? ATA_SET_MAX_ADDRESS48 : ATA_SET_MAX_ADDRESS;
+
+	error = ata_do_cmd(device,
+			   ccb,
+			   retry_count,
+			   /*flags*/CAM_DIR_OUT,
+			   /*protocol*/protocol,
+			   /*ata_flags*/AP_FLAG_CHK_COND,
+			   /*tag_action*/MSG_SIMPLE_Q_TAG,
+			   /*command*/cmd,
+			   /*features*/ATA_HPA_FEAT_FREEZE,
+			   /*lba*/0,
+			   /*sector_count*/0,
+			   /*data_ptr*/NULL,
+			   /*dxfer_len*/0,
+			   timeout ? timeout : 1000,
+			   is48bit);
+
+	if (error)
+		return (error);
+
+	return atahpa_proc_resp(device, ccb, is48bit, NULL);
+}
+
+
+static int
 ata_do_identify(struct cam_device *device, int retry_count, int timeout,
 		union ccb *ccb, struct ata_params** ident_bufp)
 {
@@ -1701,6 +2113,7 @@ ataidentify(struct cam_device *device, i
 {
 	union ccb *ccb;
 	struct ata_params *ident_buf;
+	u_int64_t hpasize;
 
 	if ((ccb = cam_getccb(device)) == NULL) {
 		warnx("couldn't allocate CCB");
@@ -1712,10 +2125,21 @@ ataidentify(struct cam_device *device, i
 		return (1);
 	}
 
+	if (ident_buf->support.command1 & ATA_SUPPORT_PROTECTED) {
+		if (ata_read_native_max(device, retry_count, timeout, ccb,
+					ident_buf, &hpasize) != 0) {
+			cam_freeccb(ccb);
+			return (1);
+		}
+	} else {
+		hpasize = 0;
+	}
+
 	printf("%s%d: ", device->device_name, device->dev_unit_num);
 	ata_print_ident(ident_buf);
 	camxferrate(device);
 	atacapprint(ident_buf);
+	atahpa_print(ident_buf, hpasize, 0);
 
 	free(ident_buf);
 	cam_freeccb(ccb);
@@ -2044,6 +2468,245 @@ ata_getpwd(u_int8_t *passwd, int max, ch
 	return (0);
 }
 
+enum {
+	ATA_HPA_ACTION_PRINT,
+	ATA_HPA_ACTION_SET_MAX,
+	ATA_HPA_ACTION_SET_PWD,
+	ATA_HPA_ACTION_LOCK,
+	ATA_HPA_ACTION_UNLOCK,
+	ATA_HPA_ACTION_FREEZE_LOCK
+};
+
+static int
+atahpa_set_confirm(struct cam_device *device, struct ata_params* ident_buf,
+		   u_int64_t maxsize, int persist)
+{
+	printf("\nYou are about to configure HPA to limit the user accessible\n"
+	       "sectors to %ju %s on the device:\n%s%d,%s%d: ", maxsize,
+	       persist ? "persistently" : "temporarily",
+	       device->device_name, device->dev_unit_num,
+	       device->given_dev_name, device->given_unit_number);
+	ata_print_ident(ident_buf);
+
+	for(;;) {
+		char str[50];
+		printf("\nAre you SURE you want to configure HPA? (yes/no) ");
+
+		if (NULL != fgets(str, sizeof(str), stdin)) {
+			if (0 == strncasecmp(str, "yes", 3)) {
+				return (1);
+			} else if (0 == strncasecmp(str, "no", 2)) {
+				return (0);
+			} else {
+				printf("Please answer \"yes\" or "
+				       "\"no\"\n");
+			}
+		}
+	}
+
+	/* NOTREACHED */
+	return (0);
+}
+
+static int
+atahpa(struct cam_device *device, int retry_count, int timeout,
+       int argc, char **argv, char *combinedopt)
+{
+	union ccb *ccb;
+	struct ata_params *ident_buf;
+	struct ccb_getdev cgd;
+	struct ata_set_max_pwd pwd;
+	int error, confirm, quiet, c, action, actions, setpwd, persist;
+	int security, is48bit, pwdsize;
+	u_int64_t hpasize, maxsize;
+
+	actions = 0;
+	setpwd = 0;
+	confirm = 0;
+	quiet = 0;
+	maxsize = 0;
+	persist = 0;
+	security = 0;
+
+	memset(&pwd, 0, sizeof(pwd));
+
+	/* default action is to print hpa information */
+	action = ATA_HPA_ACTION_PRINT;
+	pwdsize = sizeof(pwd.password);
+
+	while ((c = getopt(argc, argv, combinedopt)) != -1) {
+		switch(c){
+		case 's':
+			action = ATA_HPA_ACTION_SET_MAX;
+			maxsize = strtoumax(optarg, NULL, 0);
+			actions++;
+			break;
+
+		case 'p':
+			if (ata_getpwd(pwd.password, pwdsize, c) != 0)
+				return (1);
+			action = ATA_HPA_ACTION_SET_PWD;
+			security = 1;
+			actions++;
+			break;
+
+		case 'l':
+			action = ATA_HPA_ACTION_LOCK;
+			security = 1;
+			actions++;
+			break;
+
+		case 'U':
+			if (ata_getpwd(pwd.password, pwdsize, c) != 0)
+				return (1);
+			action = ATA_HPA_ACTION_UNLOCK;
+			security = 1;
+			actions++;
+			break;
+
+		case 'f':
+			action = ATA_HPA_ACTION_FREEZE_LOCK;
+			security = 1;
+			actions++;
+			break;
+
+		case 'P':
+			persist = 1;
+			break;
+
+		case 'y':
+			confirm++;
+			break;
+
+		case 'q':
+			quiet++;
+			break;
+		}
+	}
+
+	if (actions > 1) {
+		warnx("too many hpa actions specified");
+		return (1);
+	}
+
+	if (get_cgd(device, &cgd) != 0) {
+		warnx("couldn't get CGD");
+		return (1);
+	}
+
+	ccb = cam_getccb(device);
+	if (ccb == NULL) {
+		warnx("couldn't allocate CCB");
+		return (1);
+	}
+
+	error = ata_do_identify(device, retry_count, timeout, ccb, &ident_buf);
+	if (error != 0) {
+		cam_freeccb(ccb);
+		return (1);
+	}
+
+	if (quiet == 0) {
+		printf("%s%d: ", device->device_name, device->dev_unit_num);
+		ata_print_ident(ident_buf);
+		camxferrate(device);
+	}
+
+	if (action == ATA_HPA_ACTION_PRINT) {
+		error = ata_read_native_max(device, retry_count, timeout, ccb,
+					    ident_buf, &hpasize);
+		if (error == 0)
+			atahpa_print(ident_buf, hpasize, 1);
+
+		cam_freeccb(ccb);
+		free(ident_buf);
+		return (error);
+	}
+
+	if (!(ident_buf->support.command1 & ATA_SUPPORT_PROTECTED)) {
+		warnx("HPA is not supported by this device");
+		cam_freeccb(ccb);
+		free(ident_buf);
+		return (1);
+	}
+
+	if (security && !(ident_buf->support.command1 & ATA_SUPPORT_MAXSECURITY)) {
+		warnx("HPA Security is not supported by this device");
+		cam_freeccb(ccb);
+		free(ident_buf);
+		return (1);
+	}
+
+	is48bit = ident_buf->support.command2 & ATA_SUPPORT_ADDRESS48;
+
+	/*
+	 * The ATA spec requires:
+	 * 1. Read native max addr is called directly before set max addr
+	 * 2. Read native max addr is NOT called before any other set max call
+	 */
+	switch(action) {
+	case ATA_HPA_ACTION_SET_MAX:
+		if (confirm == 0 &&
+		    atahpa_set_confirm(device, ident_buf, maxsize,
+		    persist) == 0) {
+			cam_freeccb(ccb);
+			free(ident_buf);
+			return (1);
+		}
+
+		error = ata_read_native_max(device, retry_count, timeout,
+					    ccb, ident_buf, &hpasize);
+		if (error == 0) {
+			error = atahpa_set_max(device, retry_count, timeout,
+					       ccb, is48bit, maxsize, persist);
+			if (error == 0) {
+				/* redo identify to get new lba values */
+				error = ata_do_identify(device, retry_count,
+							timeout, ccb,
+							&ident_buf);
+				atahpa_print(ident_buf, hpasize, 1);
+			}
+		}
+		break;
+
+	case ATA_HPA_ACTION_SET_PWD:
+		error = atahpa_password(device, retry_count, timeout,
+					ccb, is48bit, &pwd);
+		if (error == 0)
+			printf("HPA password has been set\n");
+		break;
+
+	case ATA_HPA_ACTION_LOCK:
+		error = atahpa_lock(device, retry_count, timeout,
+				    ccb, is48bit);
+		if (error == 0)
+			printf("HPA has been locked\n");
+		break;
+
+	case ATA_HPA_ACTION_UNLOCK:
+		error = atahpa_unlock(device, retry_count, timeout,
+				      ccb, is48bit, &pwd);
+		if (error == 0)
+			printf("HPA has been unlocked\n");
+		break;
+
+	case ATA_HPA_ACTION_FREEZE_LOCK:
+		error = atahpa_freeze_lock(device, retry_count, timeout,
+					   ccb, is48bit);
+		if (error == 0)
+			printf("HPA has been frozen\n");
+		break;
+
+	default:
+		errx(1, "Option currently not supported");
+	}
+
+	cam_freeccb(ccb);
+	free(ident_buf);
+
+	return (error);
+}
+
 static int
 atasecurity(struct cam_device *device, int retry_count, int timeout,
 	    int argc, char **argv, char *combinedopt)
@@ -6701,6 +7364,8 @@ usage(int printlong)
 "                              <-d pwd | -e pwd | -f | -h pwd | -k pwd>\n"
 "                              [-l <high|maximum>] [-q] [-s pwd] [-T timeout]\n"
 "                              [-U <user|master>] [-y]\n"
+"        camcontrol hpa        [dev_id][generic args] [-f] [-l] [-P] [-p pwd]\n"
+"                              [-q] [-s max_sectors] [-U pwd] [-y]\n"
 #endif /* MINIMALISTIC */
 "        camcontrol help\n");
 	if (!printlong)
@@ -6851,6 +7516,17 @@ usage(int printlong)
 "-T timeout        overrides the timeout (seconds) used for erase operation\n"
 "-U <user|master>  specifies which user to set: user or master\n"
 "-y                don't ask any questions\n"
+"hpa arguments:\n"
+"-f                freeze the HPA configuration of the device\n"
+"-l                lock the HPA configuration of the device\n"
+"-P                make the HPA max sectors persist\n"
+"-p pwd            Set the HPA configuration password required for unlock\n"
+"                  calls\n"
+"-q                be quiet, do not print any status messages\n"
+"-s sectors        configures the maximum user accessible sectors of the\n"
+"                  device\n"
+"-U pwd            unlock the HPA configuration of the device\n"
+"-y                don't ask any questions\n"
 );
 #endif /* MINIMALISTIC */
 }
@@ -7075,6 +7751,10 @@ main(int argc, char **argv)
 		case CAM_CMD_DEVLIST:
 			error = getdevlist(cam_dev);
 			break;
+		case CAM_CMD_HPA:
+			error = atahpa(cam_dev, retry_count, timeout,
+				       argc, argv, combinedopt);
+			break;
 #endif /* MINIMALISTIC */
 		case CAM_CMD_DEVTREE:
 			error = getdevtree();

Modified: stable/9/sys/sys/ata.h
==============================================================================
--- stable/9/sys/sys/ata.h	Fri Jun  7 14:10:18 2013	(r251497)
+++ stable/9/sys/sys/ata.h	Fri Jun  7 14:23:11 2013	(r251498)
@@ -289,6 +289,23 @@ struct ata_params {
 #define ATA_DEV_SLAVE		0x10
 #define ATA_DEV_LBA		0x40
 
+/* ATA limits */
+#define ATA_MAX_28BIT_LBA	268435455UL
+
+/* ATA Status Register */
+#define ATA_STATUS_ERROR	0x01
+#define ATA_STATUS_DEVICE_FAULT	0x20
+
+/* ATA Error Register */
+#define ATA_ERROR_ABORT		0x04
+#define ATA_ERROR_ID_NOT_FOUND	0x10
+
+/* ATA HPA Features */
+#define ATA_HPA_FEAT_MAX_ADDR	0x00
+#define ATA_HPA_FEAT_SET_PWD	0x01
+#define ATA_HPA_FEAT_LOCK	0x02
+#define ATA_HPA_FEAT_UNLOCK	0x03
+#define ATA_HPA_FEAT_FREEZE	0x04
 
 /* ATA transfer modes */
 #define ATA_MODE_MASK           0x0f


More information about the svn-src-stable-9 mailing list