Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 7 Jun 2013 13:30:59 +0000 (UTC)
From:      Steven Hartland <smh@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-8@freebsd.org
Subject:   svn commit: r251494 - in stable/8: sbin/camcontrol sys/sys
Message-ID:  <201306071330.r57DUxsQ015855@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: smh
Date: Fri Jun  7 13:30:59 2013
New Revision: 251494
URL: http://svnweb.freebsd.org/changeset/base/251494

Log:
  MFC r249115:
  Adds security command to camcontrol which provides the ability to secure erase
  SSD's

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

Modified: stable/8/sbin/camcontrol/camcontrol.8
==============================================================================
--- stable/8/sbin/camcontrol/camcontrol.8	Fri Jun  7 12:42:49 2013	(r251493)
+++ stable/8/sbin/camcontrol/camcontrol.8	Fri Jun  7 13:30:59 2013	(r251494)
@@ -27,7 +27,7 @@
 .\"
 .\" $FreeBSD$
 .\"
-.Dd June 4, 2012
+.Dd June 7, 2013
 .Dt CAMCONTROL 8
 .Os
 .Sh NAME
@@ -184,6 +184,21 @@
 .Op device id
 .Op generic args
 .Nm
+.Ic security
+.Op device id
+.Op generic args
+.Op Fl d Ar pwd
+.Op Fl e Ar pwd
+.Op Fl f
+.Op Fl h Ar pwd
+.Op Fl k Ar pwd
+.Op Fl l Ar high|maximum
+.Op Fl q
+.Op Fl s Ar pwd
+.Op Fl T Ar timeout
+.Op Fl U Ar user|master
+.Op Fl y
+.Nm
 .Ic help
 .Sh DESCRIPTION
 The
@@ -856,6 +871,123 @@ specifies automatic standby timer value 
 .It Ic sleep
 Put ATA device into SLEEP state. Note that the only way get device out of
 this state may be reset.
+.It Ic security
+Update or report security settings, using an ATA identify command (0xec).
+By default,
+.Nm
+will print out the security support and associated settings of the device.
+The
+.Ic security
+command takes several arguments:
+.Bl -tag -width 0n
+.It Fl d Ar pwd
+.Pp
+Disable device security using the given password for the selected user according
+to the devices configured security level.
+.It Fl e Ar pwd
+.Pp
+Erase the device using the given password for the selected user.
+.Pp
+.Em WARNING! WARNING! WARNING!
+.Pp
+Issuing a secure erase will
+.Em ERASE ALL
+user data on the device and may take several hours to complete.
+.Pp
+When this command is used against an SSD drive all its cells will be marked as
+empty, restoring it to factory default write performance. For SSD's this action
+usually takes just a few seconds.
+.It Fl f
+.Pp
+Freeze the security configuration of the specified device.
+.Pp
+After command completion any other commands that update the device lock mode
+shall be command aborted. Frozen mode is disabled by power-off or hardware reset. 
+.It Fl h Ar pwd
+.Pp
+Enhanced erase the device using the given password for the selected user.
+.Pp
+.Em WARNING! WARNING! WARNING!
+.Pp
+Issuing an enhanced secure erase will 
+.Em ERASE ALL
+user data on the device and may take several hours to complete.
+.Pp
+An enhanced erase writes predetermined data patterns to all user data areas,
+all previously written user data shall be overwritten, including sectors that
+are no longer in use due to reallocation.
+.It Fl k Ar pwd
+.Pp
+Unlock the device using the given password for the selected user according to
+the devices configured security level.
+.It Fl l Ar high|maximum
+.Pp
+Specifies which security level to set when issuing a
+.Fl s Ar pwd
+command. The security level determines device behavior when the master
+password is used to unlock the device. When the security level is set to high
+the device requires the unlock command and the master password to unlock.
+When the security level is set to maximum the device requires a secure erase
+with the master password to unlock.
+.Pp
+This option must be used in conjunction with one of the security action commands.
+.Pp
+Defaults to
+.Em high
+.It Fl q
+.Pp
+Be quiet, do not print any status messages.
+This option will not disable the questions, however.
+To disable questions, use the
+.Fl y
+argument, below.
+.It Fl s Ar pwd
+.Pp
+Password the device (enable security) using the given password for the selected
+user. This option can be combined with other options such as
+.Fl e Em pwd
+.Pp
+A master password may be set in a addition to the user password. The purpose of
+the master password is to allow an administrator to establish a password that
+is kept secret from the user, and which may be used to unlock the device if the
+user password is lost.
+.Pp
+.Em Note:
+Setting the master password does not enable device security.
+.Pp
+If the master password is set and the drive supports a Master Revision Code
+feature the Master Password Revision Code will be decremented.
+.It Fl T Ar timeout
+.Pp
+Overrides the default timeout, specified in seconds, used for both
+.Fl e
+and
+.Fl h
+this is useful if your system has problems processing long timeouts correctly.
+.Pp
+Usually the timeout is calculated from the information stored on the drive if
+present, otherwise it defaults to 2 hours.
+.It Fl U Ar user|master
+.Pp
+Specifies which user to set / use for the running action command, valid values
+are user or master and defaults to master if not set.
+.Pp
+This option must be used in conjunction with one of the security action commands.
+.Pp
+Defaults to
+.Em master
+.It Fl y
+.Pp
+Confirm yes to dangerous options such as
+.Fl e
+without prompting for confirmation.
+.Pp
+.El
+If the password specified for any action commands doesn't match the configured
+password for the specified user the command will fail.
+.Pp
+The password in all cases is limited to 32 characters, longer passwords will
+fail.
 .It Ic help
 Print out verbose usage information.
 .El
@@ -974,6 +1106,30 @@ camcontrol negotiate -n da -u 3 -R 20.00
 Negotiate a sync rate of 20MHz and an offset of 15 with da3.
 Then send a
 Test Unit Ready command to make the settings take effect.
+.Bd -literal -offset indent
+camcontrol security ada0
+.Ed
+.Pp
+Report security support and settings for ada0
+.Bd -literal -offset indent
+camcontrol security ada0 -u user -s MyPass 
+.Ed
+.Pp
+Enable security on device ada0 with the password MyPass
+.Bd -literal -offset indent
+camcontrol security ada0 -u user -e MyPass
+.Ed
+.Pp
+Secure erase ada0 which has had security enabled with user password MyPass
+.Pp
+.Em WARNING! WARNING! WARNING!
+.Pp
+This will
+.Em ERASE ALL
+data from the device, so backup your data before using!
+.Pp
+This command can be used used against an SSD drive to restoring it to
+factory default write performance.
 .Sh SEE ALSO
 .Xr cam 3 ,
 .Xr cam_cdbparse 3 ,

Modified: stable/8/sbin/camcontrol/camcontrol.c
==============================================================================
--- stable/8/sbin/camcontrol/camcontrol.c	Fri Jun  7 12:42:49 2013	(r251493)
+++ stable/8/sbin/camcontrol/camcontrol.c	Fri Jun  7 13:30:59 2013	(r251494)
@@ -77,7 +77,8 @@ typedef enum {
 	CAM_CMD_IDENTIFY	= 0x00000013,
 	CAM_CMD_IDLE		= 0x00000014,
 	CAM_CMD_STANDBY		= 0x00000015,
-	CAM_CMD_SLEEP		= 0x00000016
+	CAM_CMD_SLEEP		= 0x00000016,
+	CAM_CMD_SECURITY	= 0x00000017
 } cam_cmdmask;
 
 typedef enum {
@@ -127,6 +128,7 @@ struct camcontrol_opts {
 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:";
+static char pwd_opt;
 #endif
 
 struct camcontrol_opts option_table[] = {
@@ -161,6 +163,7 @@ struct camcontrol_opts option_table[] = 
 	{"idle", CAM_CMD_IDLE, CAM_ARG_NONE, "t:"},
 	{"standby", CAM_CMD_STANDBY, CAM_ARG_NONE, "t:"},
 	{"sleep", CAM_CMD_SLEEP, CAM_ARG_NONE, ""},
+	{"security", CAM_CMD_SECURITY, CAM_ARG_NONE, "d:e:fh:k:l:qs:T:U:y"},
 #endif /* MINIMALISTIC */
 	{"help", CAM_CMD_USAGE, CAM_ARG_NONE, NULL},
 	{"-?", CAM_CMD_USAGE, CAM_ARG_NONE, NULL},
@@ -225,7 +228,10 @@ static int scsireportluns(struct cam_dev
 static int scsireadcapacity(struct cam_device *device, int argc, char **argv,
 			    char *combinedopt, int retry_count, int timeout);
 static int atapm(struct cam_device *device, int argc, char **argv,
-			    char *combinedopt, int retry_count, int timeout);
+		 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);
+
 #endif /* MINIMALISTIC */
 #ifndef min
 #define min(a,b) (((a)<(b))?(a):(b))
@@ -1260,55 +1266,93 @@ atacapprint(struct ata_params *parm)
 	printf("free-fall                      %s	%s\n",
 		parm->support2 & ATA_SUPPORT_FREEFALL ? "yes" : "no",
 		parm->enabled2 & ATA_SUPPORT_FREEFALL ? "yes" : "no");
-	printf("data set management (TRIM)     %s\n",
-		parm->support_dsm & ATA_SUPPORT_DSM_TRIM ? "yes" : "no");
+	printf("Data Set Management (DSM/TRIM) ");
+	if (parm->support_dsm & ATA_SUPPORT_DSM_TRIM) {
+		printf("yes\n");
+		printf("DSM - max 512byte blocks       ");
+		if (parm->max_dsm_blocks == 0x00)
+			printf("yes              not specified\n");
+		else
+			printf("yes              %d\n",
+				parm->max_dsm_blocks);
+
+		printf("DSM - deterministic read       ");
+		if (parm->support3 & ATA_SUPPORT_DRAT) {
+			if (parm->support3 & ATA_SUPPORT_RZAT)
+				printf("yes              zeroed\n");
+			else
+				printf("yes              any value\n");
+		} else {
+			printf("no\n");
+		}
+	} else {
+		printf("no\n");
+	}
 }
 
 static int
-ataidentify(struct cam_device *device, int retry_count, int timeout)
+scsi_cam_pass_16_send(struct cam_device *device, union ccb *ccb, int quiet)
 {
-	union ccb *ccb;
-	struct ata_params *ident_buf;
-	struct ccb_getdev cgd;
-	u_int i, error = 0;
-	int16_t *ptr;
+	struct ata_pass_16 *ata_pass_16;
+	struct ata_cmd ata_cmd;
 
-	if (get_cgd(device, &cgd) != 0) {
-		warnx("couldn't get CGD");
-		return(1);
-	}
-	ccb = cam_getccb(device);
+	ata_pass_16 = (struct ata_pass_16 *)ccb->csio.cdb_io.cdb_bytes;
+	ata_cmd.command = ata_pass_16->command;
+	ata_cmd.control = ata_pass_16->control;
+	ata_cmd.features = ata_pass_16->features;
 
-	if (ccb == NULL) {
-		warnx("couldn't allocate CCB");
-		return(1);
+	if (arglist & CAM_ARG_VERBOSE) {
+		warnx("sending ATA %s via pass_16 with timeout of %u msecs",
+		      ata_op_string(&ata_cmd),
+		      ccb->csio.ccb_h.timeout);
 	}
 
-	/* cam_getccb cleans up the header, caller has to zero the payload */
-	bzero(&(&ccb->ccb_h)[1],
-	      sizeof(struct ccb_ataio) - sizeof(struct ccb_hdr));
+	/* Disable freezing the device queue */
+	ccb->ccb_h.flags |= CAM_DEV_QFRZDIS;
 
-	ptr = (uint16_t *)malloc(sizeof(struct ata_params));
+	if (arglist & CAM_ARG_ERR_RECOVER)
+		ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER;
 
-	if (ptr == NULL) {
-		cam_freeccb(ccb);
-		warnx("can't malloc memory for identify\n");
-		return(1);
+	if (cam_send_ccb(device, ccb) < 0) {
+		if (quiet != 1 || arglist & CAM_ARG_VERBOSE) {
+			warn("error sending ATA %s via pass_16",
+			     ata_op_string(&ata_cmd));
+		}
+
+		if (arglist & CAM_ARG_VERBOSE) {
+			cam_error_print(device, ccb, CAM_ESF_ALL,
+					CAM_EPF_ALL, stderr);
+		}
+
+		return (1);
 	}
-	bzero(ptr, sizeof(struct ata_params));
 
-	cam_fill_ataio(&ccb->ataio,
-		      retry_count,
-		      NULL,
-		      /*flags*/CAM_DIR_IN,
-		      MSG_SIMPLE_Q_TAG,
-		      /*data_ptr*/(u_int8_t *)ptr,
-		      /*dxfer_len*/sizeof(struct ata_params),
-		      timeout ? timeout : 30 * 1000);
-	if (cgd.protocol == PROTO_ATA)
-		ata_28bit_cmd(&ccb->ataio, ATA_ATA_IDENTIFY, 0, 0, 0);
-	else
-		ata_28bit_cmd(&ccb->ataio, ATA_ATAPI_IDENTIFY, 0, 0, 0);
+	if (!(ata_pass_16->flags & AP_FLAG_CHK_COND) &&
+	    (ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
+		if (quiet != 1 || arglist & CAM_ARG_VERBOSE) {
+			warnx("ATA %s via pass_16 failed",
+			      ata_op_string(&ata_cmd));
+		}
+		if (arglist & CAM_ARG_VERBOSE) {
+			cam_error_print(device, ccb, CAM_ESF_ALL,
+					CAM_EPF_ALL, stderr);
+		}
+
+		return (1);
+	}
+
+	return (0);
+}
+
+
+static int
+ata_cam_send(struct cam_device *device, union ccb *ccb, int quiet)
+{
+	if (arglist & CAM_ARG_VERBOSE) {
+		warnx("sending ATA %s with timeout of %u msecs",
+		      ata_op_string(&(ccb->ataio.cmd)),
+		      ccb->ataio.ccb_h.timeout);
+	}
 
 	/* Disable freezing the device queue */
 	ccb->ccb_h.flags |= CAM_DEV_QFRZDIS;
@@ -1317,47 +1361,247 @@ ataidentify(struct cam_device *device, i
 		ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER;
 
 	if (cam_send_ccb(device, ccb) < 0) {
-		perror("error sending ATA identify");
+		if (quiet != 1 || arglist & CAM_ARG_VERBOSE) {
+			warn("error sending ATA %s",
+			     ata_op_string(&(ccb->ataio.cmd)));
+		}
 
 		if (arglist & CAM_ARG_VERBOSE) {
 			cam_error_print(device, ccb, CAM_ESF_ALL,
 					CAM_EPF_ALL, stderr);
 		}
 
-		free(ptr);
-		cam_freeccb(ccb);
-		return(1);
+		return (1);
 	}
 
 	if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
-		error = 1;
+		if (quiet != 1 || arglist & CAM_ARG_VERBOSE) {
+			warnx("ATA %s failed: %d",
+			      ata_op_string(&(ccb->ataio.cmd)), quiet);
+		}
 
 		if (arglist & CAM_ARG_VERBOSE) {
 			cam_error_print(device, ccb, CAM_ESF_ALL,
 					CAM_EPF_ALL, stderr);
 		}
+
+		return (1);
 	}
 
-	cam_freeccb(ccb);
+	return (0);
+}
+
+static int
+ata_do_pass_16(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 quiet)
+{
+	if (data_ptr != NULL) {
+		ata_flags |= AP_FLAG_BYT_BLOK_BYTES |
+			    AP_FLAG_TLEN_SECT_CNT;
+		if (flags & CAM_DIR_OUT)
+			ata_flags |= AP_FLAG_TDIR_TO_DEV;
+		else
+			ata_flags |= AP_FLAG_TDIR_FROM_DEV;
+	} else {
+		ata_flags |= AP_FLAG_TLEN_NO_DATA;
+	}
+
+	bzero(&(&ccb->ccb_h)[1],
+	      sizeof(struct ccb_scsiio) - sizeof(struct ccb_hdr));
+
+	scsi_ata_pass_16(&ccb->csio,
+			 retries,
+			 NULL,
+			 flags,
+			 tag_action,
+			 protocol,
+			 ata_flags,
+			 features,
+			 sector_count,
+			 lba,
+			 command,
+			 /*control*/0,
+			 data_ptr,
+			 dxfer_len,
+			 /*sense_len*/SSD_FULL_SIZE,
+			 timeout);
+
+	return scsi_cam_pass_16_send(device, ccb, quiet);
+}
+
+static int
+ata_try_pass_16(struct cam_device *device)
+{
+	struct ccb_pathinq cpi;
+
+	if (get_cpi(device, &cpi) != 0) {
+		warnx("couldn't get CPI");
+		return (-1);
+	}
+
+	if (cpi.protocol == PROTO_SCSI) {
+		/* possibly compatible with pass_16 */
+		return (1);
+	}
+
+	/* likely not compatible with pass_16 */
+	return (0);
+}
+
+static int
+ata_do_28bit_cmd(struct cam_device *device, union ccb *ccb, int retries,
+		 u_int32_t flags, u_int8_t protocol, u_int8_t tag_action,
+		 u_int8_t command, u_int8_t features, u_int32_t lba,
+		 u_int8_t sector_count, u_int8_t *data_ptr, u_int16_t dxfer_len,
+		 int timeout, int quiet)
+{
+
+
+	switch (ata_try_pass_16(device)) {
+	case -1:
+		return (1);
+	case 1:
+		/* Try using SCSI Passthrough */
+		return ata_do_pass_16(device, ccb, retries, flags, protocol,
+				      0, tag_action, command, features, lba,
+				      sector_count, data_ptr, dxfer_len,
+				      timeout, quiet);
+	}
+
+	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);
+
+	ata_28bit_cmd(&ccb->ataio, command, features, lba, sector_count);
+	return ata_cam_send(device, ccb, quiet);
+}
+
+static void
+dump_data(uint16_t *ptr, uint32_t len)
+{
+	u_int i;
+
+	for (i = 0; i < len / 2; i++) {
+		if ((i % 8) == 0)
+			printf(" %3d: ", i);
+		printf("%04hx ", ptr[i]);
+		if ((i % 8) == 7)
+			printf("\n");
+	}
+	if ((i % 8) != 7)
+		printf("\n");
+}
+
+static int
+ata_do_identify(struct cam_device *device, int retry_count, int timeout,
+		union ccb *ccb, struct ata_params** ident_bufp)
+{
+	struct ata_params *ident_buf;
+	struct ccb_pathinq cpi;
+	struct ccb_getdev cgd;
+	u_int i, error;
+	int16_t *ptr;
+	u_int8_t command, retry_command;
+
+	if (get_cpi(device, &cpi) != 0) {
+		warnx("couldn't get CPI");
+		return (-1);
+	}
+
+	/* Neither PROTO_ATAPI or PROTO_SATAPM are used in cpi.protocol */
+	if (cpi.protocol == PROTO_ATA) {
+		if (get_cgd(device, &cgd) != 0) {
+			warnx("couldn't get CGD");
+			return (-1);
+		}
+
+		command = (cgd.protocol == PROTO_ATA) ?
+		    ATA_ATA_IDENTIFY : ATA_ATAPI_IDENTIFY;
+		retry_command = 0;
+	} else {
+		/* We don't know which for sure so try both */
+		command = ATA_ATA_IDENTIFY;
+		retry_command = ATA_ATAPI_IDENTIFY;
+	}
+
+	ptr = (uint16_t *)calloc(1, sizeof(struct ata_params));
+	if (ptr == NULL) {
+		warnx("can't calloc memory for identify\n");
+		return (1);
+	}
+
+	error = ata_do_28bit_cmd(device,
+				 ccb,
+				 /*retries*/retry_count,
+				 /*flags*/CAM_DIR_IN,
+				 /*protocol*/AP_PROTO_PIO_IN,
+				 /*tag_action*/MSG_SIMPLE_Q_TAG,
+				 /*command*/command,
+				 /*features*/0,
+				 /*lba*/0,
+				 /*sector_count*/(u_int8_t)sizeof(struct ata_params),
+				 /*data_ptr*/(u_int8_t *)ptr,
+				 /*dxfer_len*/sizeof(struct ata_params),
+				 /*timeout*/timeout ? timeout : 30 * 1000,
+				 /*quiet*/1);
 
 	if (error != 0) {
-		free(ptr);
-		return(error);
+		if (retry_command == 0) {
+			free(ptr);
+			return (1);
+		}
+		error = ata_do_28bit_cmd(device,
+					 ccb,
+					 /*retries*/retry_count,
+					 /*flags*/CAM_DIR_IN,
+					 /*protocol*/AP_PROTO_PIO_IN,
+					 /*tag_action*/MSG_SIMPLE_Q_TAG,
+					 /*command*/retry_command,
+					 /*features*/0,
+					 /*lba*/0,
+					 /*sector_count*/(u_int8_t)
+					     sizeof(struct ata_params),
+					 /*data_ptr*/(u_int8_t *)ptr,
+					 /*dxfer_len*/sizeof(struct ata_params),
+					 /*timeout*/timeout ? timeout : 30 * 1000,
+					 /*quiet*/0);
+
+		if (error != 0) {
+			free(ptr);
+			return (1);
+		}
 	}
 
-	for (i = 0; i < sizeof(struct ata_params) / 2; i++)
+	error = 1;
+	for (i = 0; i < sizeof(struct ata_params) / 2; i++) {
 		ptr[i] = le16toh(ptr[i]);
+		if (ptr[i] != 0)
+			error = 0;
+	}
+
 	if (arglist & CAM_ARG_VERBOSE) {
 		fprintf(stdout, "%s%d: Raw identify data:\n",
 		    device->device_name, device->dev_unit_num);
-		for (i = 0; i < sizeof(struct ata_params) / 2; i++) {
-			if ((i % 8) == 0)
-			    fprintf(stdout, " %3d: ", i);
-			fprintf(stdout, "%04x ", (uint16_t)ptr[i]);
-			if ((i % 8) == 7)
-			    fprintf(stdout, "\n");
-		}
+		dump_data(ptr, sizeof(struct ata_params));
+	}
+
+	/* check for invalid (all zero) response */
+	if (error != 0) {
+		warnx("Invalid identify response detected");
+		free(ptr);
+		return (error);
 	}
+
 	ident_buf = (struct ata_params *)ptr;
 	if (strncmp(ident_buf->model, "FX", 2) &&
 	    strncmp(ident_buf->model, "NEC", 3) &&
@@ -1378,15 +1622,636 @@ ataidentify(struct cam_device *device, i
 	ata_bpack(ident_buf->media_serial, ident_buf->media_serial,
 	    sizeof(ident_buf->media_serial));
 
-	fprintf(stdout, "%s%d: ", device->device_name,
-		device->dev_unit_num);
+	*ident_bufp = ident_buf;
+
+	return (0);
+}
+
+
+static int
+ataidentify(struct cam_device *device, int retry_count, int timeout)
+{
+	union ccb *ccb;
+	struct ata_params *ident_buf;
+
+	if ((ccb = cam_getccb(device)) == NULL) {
+		warnx("couldn't allocate CCB");
+		return (1);
+	}
+
+	if (ata_do_identify(device, retry_count, timeout, ccb, &ident_buf) != 0) {
+		cam_freeccb(ccb);
+		return (1);
+	}
+
+	printf("%s%d: ", device->device_name, device->dev_unit_num);
 	ata_print_ident(ident_buf);
 	camxferrate(device);
 	atacapprint(ident_buf);
 
 	free(ident_buf);
+	cam_freeccb(ccb);
 
-	return(0);
+	return (0);
+}
+#endif /* MINIMALISTIC */
+
+
+#ifndef MINIMALISTIC
+enum {
+	ATA_SECURITY_ACTION_PRINT,
+	ATA_SECURITY_ACTION_FREEZE,
+	ATA_SECURITY_ACTION_UNLOCK,
+	ATA_SECURITY_ACTION_DISABLE,
+	ATA_SECURITY_ACTION_ERASE,
+	ATA_SECURITY_ACTION_ERASE_ENHANCED,
+	ATA_SECURITY_ACTION_SET_PASSWORD
+} atasecurity_action;
+
+static void
+atasecurity_print_time(u_int16_t tw)
+{
+
+	if (tw == 0)
+		printf("unspecified");
+	else if (tw >= 255)
+		printf("> 508 min");
+	else
+		printf("%i min", 2 * tw);
+}
+
+static u_int32_t
+atasecurity_erase_timeout_msecs(u_int16_t timeout)
+{
+
+	if (timeout == 0)
+		return 2 * 3600 * 1000; /* default: two hours */
+	else if (timeout > 255)
+		return (508 + 60) * 60 * 1000; /* spec says > 508 minutes */
+
+	return ((2 * timeout) + 5) * 60 * 1000; /* add a 5min margin */
+}
+
+
+static void
+atasecurity_notify(u_int8_t command, struct ata_security_password *pwd)
+{
+	struct ata_cmd cmd;
+
+	bzero(&cmd, sizeof(cmd));
+	cmd.command = command;
+	printf("Issuing %s", ata_op_string(&cmd));
+
+	if (pwd != NULL) {
+		char pass[sizeof(pwd->password)+1];
+
+		/* pwd->password may not be null terminated */
+		pass[sizeof(pwd->password)] = '\0';
+		strncpy(pass, pwd->password, sizeof(pwd->password));
+		printf(" password='%s', user='%s'",
+			pass,
+			(pwd->ctrl & ATA_SECURITY_PASSWORD_MASTER) ?
+			"master" : "user");
+
+		if (command == ATA_SECURITY_SET_PASSWORD) {
+			printf(", mode='%s'",
+			       (pwd->ctrl & ATA_SECURITY_LEVEL_MAXIMUM) ?
+			       "maximum" : "high");
+		}
+	}
+
+	printf("\n");
+}
+
+static int
+atasecurity_freeze(struct cam_device *device, union ccb *ccb,
+		   int retry_count, u_int32_t timeout, int quiet)
+{
+
+	if (quiet == 0)
+		atasecurity_notify(ATA_SECURITY_FREEZE_LOCK, NULL);
+
+	return ata_do_28bit_cmd(device,
+				ccb,
+				retry_count,
+				/*flags*/CAM_DIR_NONE,
+				/*protocol*/AP_PROTO_NON_DATA,
+				/*tag_action*/MSG_SIMPLE_Q_TAG,
+				/*command*/ATA_SECURITY_FREEZE_LOCK,
+				/*features*/0,
+				/*lba*/0,
+				/*sector_count*/0,
+				/*data_ptr*/NULL,
+				/*dxfer_len*/0,
+				/*timeout*/timeout,
+				/*quiet*/0);
+}
+
+static int
+atasecurity_unlock(struct cam_device *device, union ccb *ccb,
+		   int retry_count, u_int32_t timeout,
+		   struct ata_security_password *pwd, int quiet)
+{
+
+	if (quiet == 0)
+		atasecurity_notify(ATA_SECURITY_UNLOCK, pwd);
+
+	return ata_do_28bit_cmd(device,
+				ccb,
+				retry_count,
+				/*flags*/CAM_DIR_OUT,
+				/*protocol*/AP_PROTO_PIO_OUT,
+				/*tag_action*/MSG_SIMPLE_Q_TAG,
+				/*command*/ATA_SECURITY_UNLOCK,
+				/*features*/0,
+				/*lba*/0,
+				/*sector_count*/0,
+				/*data_ptr*/(u_int8_t *)pwd,
+				/*dxfer_len*/sizeof(*pwd),
+				/*timeout*/timeout,
+				/*quiet*/0);
+}
+
+static int
+atasecurity_disable(struct cam_device *device, union ccb *ccb,
+		    int retry_count, u_int32_t timeout,
+		    struct ata_security_password *pwd, int quiet)
+{
+
+	if (quiet == 0)
+		atasecurity_notify(ATA_SECURITY_DISABLE_PASSWORD, pwd);
+	return ata_do_28bit_cmd(device,
+				ccb,
+				retry_count,
+				/*flags*/CAM_DIR_OUT,
+				/*protocol*/AP_PROTO_PIO_OUT,
+				/*tag_action*/MSG_SIMPLE_Q_TAG,
+				/*command*/ATA_SECURITY_DISABLE_PASSWORD,
+				/*features*/0,
+				/*lba*/0,
+				/*sector_count*/0,
+				/*data_ptr*/(u_int8_t *)pwd,
+				/*dxfer_len*/sizeof(*pwd),
+				/*timeout*/timeout,
+				/*quiet*/0);
+}
+
+
+static int
+atasecurity_erase_confirm(struct cam_device *device,
+			  struct ata_params* ident_buf)
+{
+
+	printf("\nYou are about to ERASE ALL DATA from the following"
+	       " device:\n%s%d,%s%d: ", 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 ERASE ALL DATA? (yes/no) ");
+
+		if (fgets(str, sizeof(str), stdin) != NULL) {
+			if (strncasecmp(str, "yes", 3) == 0) {
+				return (1);
+			} else if (strncasecmp(str, "no", 2) == 0) {
+				return (0);
+			} else {
+				printf("Please answer \"yes\" or "
+				       "\"no\"\n");
+			}
+		}
+	}
+
+	/* NOTREACHED */
+	return (0);
+}
+
+static int
+atasecurity_erase(struct cam_device *device, union ccb *ccb,
+		  int retry_count, u_int32_t timeout,
+		  u_int32_t erase_timeout,
+		  struct ata_security_password *pwd, int quiet)
+{
+	int error;
+
+	if (quiet == 0)
+		atasecurity_notify(ATA_SECURITY_ERASE_PREPARE, NULL);
+
+	error = ata_do_28bit_cmd(device,
+				 ccb,
+				 retry_count,
+				 /*flags*/CAM_DIR_NONE,
+				 /*protocol*/AP_PROTO_NON_DATA,
+				 /*tag_action*/MSG_SIMPLE_Q_TAG,
+				 /*command*/ATA_SECURITY_ERASE_PREPARE,
+				 /*features*/0,
+				 /*lba*/0,
+				 /*sector_count*/0,
+				 /*data_ptr*/NULL,
+				 /*dxfer_len*/0,
+				 /*timeout*/timeout,
+				 /*quiet*/0);
+
+	if (error != 0)
+		return error;
+
+	if (quiet == 0)
+		atasecurity_notify(ATA_SECURITY_ERASE_UNIT, pwd);
+
+	error = ata_do_28bit_cmd(device,
+				 ccb,
+				 retry_count,
+				 /*flags*/CAM_DIR_OUT,
+				 /*protocol*/AP_PROTO_PIO_OUT,
+				 /*tag_action*/MSG_SIMPLE_Q_TAG,
+				 /*command*/ATA_SECURITY_ERASE_UNIT,
+				 /*features*/0,
+				 /*lba*/0,
+				 /*sector_count*/0,
+				 /*data_ptr*/(u_int8_t *)pwd,
+				 /*dxfer_len*/sizeof(*pwd),
+				 /*timeout*/erase_timeout,
+				 /*quiet*/0);
+
+	if (error == 0 && quiet == 0)
+		printf("\nErase Complete\n");
+
+	return error;
+}
+
+static int
+atasecurity_set_password(struct cam_device *device, union ccb *ccb,
+			 int retry_count, u_int32_t timeout,
+			 struct ata_security_password *pwd, int quiet)
+{
+
+	if (quiet == 0)
+		atasecurity_notify(ATA_SECURITY_SET_PASSWORD, pwd);
+
+	return ata_do_28bit_cmd(device,
+				 ccb,
+				 retry_count,
+				 /*flags*/CAM_DIR_OUT,
+				 /*protocol*/AP_PROTO_PIO_OUT,
+				 /*tag_action*/MSG_SIMPLE_Q_TAG,
+				 /*command*/ATA_SECURITY_SET_PASSWORD,
+				 /*features*/0,
+				 /*lba*/0,
+				 /*sector_count*/0,
+				 /*data_ptr*/(u_int8_t *)pwd,
+				 /*dxfer_len*/sizeof(*pwd),
+				 /*timeout*/timeout,
+				 /*quiet*/0);
+}
+
+static void
+atasecurity_print(struct ata_params *parm)
+{
+
+	printf("\nSecurity Option           Value\n");
+	if (arglist & CAM_ARG_VERBOSE) {
+		printf("status                    %04x\n",
+		       parm->security_status);
+	}
+	printf("supported                 %s\n",
+		parm->security_status & ATA_SECURITY_SUPPORTED ? "yes" : "no");
+	if (!(parm->security_status & ATA_SECURITY_SUPPORTED))
+		return;
+	printf("enabled                   %s\n",
+		parm->security_status & ATA_SECURITY_ENABLED ? "yes" : "no");
+	printf("drive locked              %s\n",
+		parm->security_status & ATA_SECURITY_LOCKED ? "yes" : "no");
+	printf("security config frozen    %s\n",
+		parm->security_status & ATA_SECURITY_FROZEN ? "yes" : "no");
+	printf("count expired             %s\n",
+		parm->security_status & ATA_SECURITY_COUNT_EXP ? "yes" : "no");
+	printf("security level            %s\n",
+		parm->security_status & ATA_SECURITY_LEVEL ? "maximum" : "high");
+	printf("enhanced erase supported  %s\n",
+		parm->security_status & ATA_SECURITY_ENH_SUPP ? "yes" : "no");
+	printf("erase time                ");
+	atasecurity_print_time(parm->erase_time);
+	printf("\n");
+	printf("enhanced erase time       ");
+	atasecurity_print_time(parm->enhanced_erase_time);
+	printf("\n");
+	printf("master password rev       %04x%s\n",
+		parm->master_passwd_revision,
+		parm->master_passwd_revision == 0x0000 ||
+		parm->master_passwd_revision == 0xFFFF ?  " (unsupported)" : "");
+}
+
+/*
+ * Validates and copies the password in optarg to the passed buffer.
+ * If the password in optarg is the same length as the buffer then
+ * the data will still be copied but no null termination will occur.
+ */
+static int
+ata_getpwd(u_int8_t *passwd, int max, char opt)
+{
+	int len;
+
+	len = strlen(optarg);
+	if (len > max) {
+		warnx("-%c password is too long", opt);
+		return (1);
+	} else if (len == 0) {
+		warnx("-%c password is missing", opt);
+		return (1);
+	} else if (optarg[0] == '-'){
+		warnx("-%c password starts with '-' (generic arg?)", opt);
+		return (1);
+	} else if (strlen(passwd) != 0 && strcmp(passwd, optarg) != 0) {
+		warnx("-%c password conflicts with existing password from -%c",
+		      opt, pwd_opt);
+		return (1);
+	}
+
+	/* Callers pass in a buffer which does NOT need to be terminated */
+	strncpy(passwd, optarg, max);
+	pwd_opt = opt;
+
+	return (0);
+}
+
+static int
+atasecurity(struct cam_device *device, int retry_count, int timeout,
+	    int argc, char **argv, char *combinedopt)
+{
+	union ccb *ccb;
+	struct ata_params *ident_buf;
+	int error, confirm, quiet, c, action, actions, setpwd;
+	int security_enabled, erase_timeout, pwdsize;
+	struct ata_security_password pwd;
+
+	actions = 0;
+	setpwd = 0;
+	erase_timeout = 0;
+	confirm = 0;
+	quiet = 0;
+

*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201306071330.r57DUxsQ015855>