[Midnightbsd-cvs] src [9774] trunk/sys/sys/ata.h: add security command to camcontrol to secure erase SSDs.

laffer1 at midnightbsd.org laffer1 at midnightbsd.org
Thu Feb 22 00:10:17 EST 2018


Revision: 9774
          http://svnweb.midnightbsd.org/src/?rev=9774
Author:   laffer1
Date:     2018-02-22 00:10:17 -0500 (Thu, 22 Feb 2018)
Log Message:
-----------
add security command to camcontrol to secure erase SSDs.

Modified Paths:
--------------
    trunk/sbin/camcontrol/camcontrol.8
    trunk/sbin/camcontrol/camcontrol.c
    trunk/sys/sys/ata.h

Property Changed:
----------------
    trunk/sbin/camcontrol/camcontrol.8

Modified: trunk/sbin/camcontrol/camcontrol.8
===================================================================
--- trunk/sbin/camcontrol/camcontrol.8	2018-02-22 05:08:40 UTC (rev 9773)
+++ trunk/sbin/camcontrol/camcontrol.8	2018-02-22 05:10:17 UTC (rev 9774)
@@ -25,7 +25,7 @@
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.\" $MidnightBSD: src/sbin/camcontrol/camcontrol.8,v 1.3 2012/10/28 19:22:30 laffer1 Exp $
+.\" $MidnightBSD$
 .\"
 .Dd June 4, 2012
 .Dt CAMCONTROL 8
@@ -228,6 +228,21 @@
 .Op Fl y
 .Op Fl s
 .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
@@ -1072,6 +1087,123 @@
 .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 fwdownload
 Program firmware of the named SCSI device using the image file provided.
 .Pp
@@ -1240,6 +1372,30 @@
 Send the SMP REPORT GENERAL command to ses0, and display the number of PHYs
 it contains.
 Display SMP errors if the command fails.
+.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 ,


Property changes on: trunk/sbin/camcontrol/camcontrol.8
___________________________________________________________________
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Modified: trunk/sbin/camcontrol/camcontrol.c
===================================================================
--- trunk/sbin/camcontrol/camcontrol.c	2018-02-22 05:08:40 UTC (rev 9773)
+++ trunk/sbin/camcontrol/camcontrol.c	2018-02-22 05:10:17 UTC (rev 9774)
@@ -87,7 +87,8 @@
 	CAM_CMD_SMP_PC		= 0x00000019,
 	CAM_CMD_SMP_PHYLIST	= 0x0000001a,
 	CAM_CMD_SMP_MANINFO	= 0x0000001b,
-	CAM_CMD_DOWNLOAD_FW	= 0x0000001c
+	CAM_CMD_DOWNLOAD_FW	= 0x0000001c,
+	CAM_CMD_SECURITY	= 0x0000001d
 } cam_cmdmask;
 
 typedef enum {
@@ -140,6 +141,7 @@
 static const char smprg_opts[] = "l";
 static const char smppc_opts[] = "a:A:d:lm:M:o:p:s:S:T:";
 static const char smpphylist_opts[] = "lq";
+static char pwd_opt;
 #endif
 
 static struct camcontrol_opts option_table[] = {
@@ -183,6 +185,7 @@
 	{"standby", CAM_CMD_STANDBY, CAM_ARG_NONE, "t:"},
 	{"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"},
 #endif /* MINIMALISTIC */
 	{"help", CAM_CMD_USAGE, CAM_ARG_NONE, NULL},
 	{"-?", CAM_CMD_USAGE, CAM_ARG_NONE, NULL},
@@ -274,7 +277,10 @@
 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))
@@ -1328,56 +1334,94 @@
 	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);
+	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 (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);
 	}
-	ccb = cam_getccb(device);
 
-	if (ccb == NULL) {
-		warnx("couldn't allocate CCB");
-		return(1);
+	/* Disable freezing the device queue */
+	ccb->ccb_h.flags |= CAM_DEV_QFRZDIS;
+
+	if (arglist & CAM_ARG_ERR_RECOVER)
+		ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER;
+
+	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);
 	}
 
-	/* 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));
+	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);
+		}
 
-	ptr = (uint16_t *)malloc(sizeof(struct ata_params));
+		return (1);
+	}
 
-	if (ptr == NULL) {
-		cam_freeccb(ccb);
-		warnx("can't malloc memory for identify\n");
-		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);
 	}
-	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);
-
 	/* Disable freezing the device queue */
 	ccb->ccb_h.flags |= CAM_DEV_QFRZDIS;
 
@@ -1385,7 +1429,10 @@
 		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,
@@ -1392,40 +1439,237 @@
 					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) &&
@@ -1446,19 +1690,640 @@
 	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;
+
+	memset(&pwd, 0, sizeof(pwd));
+
+	/* default action is to print security information */
+	action = ATA_SECURITY_ACTION_PRINT;
+
+	/* user is master by default as its safer that way */
+	pwd.ctrl |= ATA_SECURITY_PASSWORD_MASTER;
+	pwdsize = sizeof(pwd.password);
+
+	while ((c = getopt(argc, argv, combinedopt)) != -1) {
+		switch(c){
+		case 'f':
+			action = ATA_SECURITY_ACTION_FREEZE;
+			actions++;
+			break;
+
+		case 'U':
+			if (strcasecmp(optarg, "user") == 0) {
+				pwd.ctrl |= ATA_SECURITY_PASSWORD_USER;
+				pwd.ctrl &= ~ATA_SECURITY_PASSWORD_MASTER;
+			} else if (strcasecmp(optarg, "master") != 0) {
+				pwd.ctrl |= ATA_SECURITY_PASSWORD_MASTER;
+				pwd.ctrl &= ~ATA_SECURITY_PASSWORD_USER;
+			} else {
+				warnx("-U argument '%s' is invalid (must be "
+				      "'user' or 'master')", optarg);
+				return (1);
+			}
+			break;
+
+		case 'l':
+			if (strcasecmp(optarg, "high") == 0) {
+				pwd.ctrl |= ATA_SECURITY_LEVEL_HIGH;
+				pwd.ctrl &= ~ATA_SECURITY_LEVEL_MAXIMUM;
+			} else if (strcasecmp(optarg, "maximum") == 0) {
+				pwd.ctrl |= ATA_SECURITY_LEVEL_MAXIMUM;
+				pwd.ctrl &= ~ATA_SECURITY_LEVEL_HIGH;
+			} else {
+				warnx("-l argument '%s' is unknown (must be "
+				      "'high' or 'maximum')", optarg);
+				return (1);
+			}
+			break;
+
+		case 'k':
+			if (ata_getpwd(pwd.password, pwdsize, c) != 0)
+				return (1);
+			action = ATA_SECURITY_ACTION_UNLOCK;
+			actions++;
+			break;
+
+		case 'd':
+			if (ata_getpwd(pwd.password, pwdsize, c) != 0)
+				return (1);
+			action = ATA_SECURITY_ACTION_DISABLE;
+			actions++;
+			break;
+
+		case 'e':
+			if (ata_getpwd(pwd.password, pwdsize, c) != 0)
+				return (1);
+			action = ATA_SECURITY_ACTION_ERASE;
+			actions++;
+			break;
+
+		case 'h':
+			if (ata_getpwd(pwd.password, pwdsize, c) != 0)
+				return (1);
+			pwd.ctrl |= ATA_SECURITY_ERASE_ENHANCED;
+			action = ATA_SECURITY_ACTION_ERASE_ENHANCED;
+			actions++;
+			break;
+
+		case 's':
+			if (ata_getpwd(pwd.password, pwdsize, c) != 0)
+				return (1);
+			setpwd = 1;
+			if (action == ATA_SECURITY_ACTION_PRINT)
+				action = ATA_SECURITY_ACTION_SET_PASSWORD;
+			/*
+			 * Don't increment action as this can be combined
+			 * with other actions.
+			 */
+			break;
+
+		case 'y':
+			confirm++;
+			break;
+
+		case 'q':
+			quiet++;
+			break;
+
+		case 'T':
+			erase_timeout = atoi(optarg) * 1000;
+			break;
+		}
+	}
+
+	if (actions > 1) {
+		warnx("too many security actions specified");
+		return (1);
+	}
+
+	if ((ccb = cam_getccb(device)) == 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_SECURITY_ACTION_PRINT) {
+		atasecurity_print(ident_buf);
+		free(ident_buf);
+		cam_freeccb(ccb);
+		return (0);
+	}
+
+	if ((ident_buf->support.command1 & ATA_SUPPORT_SECURITY) == 0) {
+		warnx("Security not supported");
+		free(ident_buf);
+		cam_freeccb(ccb);
+		return (1);
+	}
+
+	/* default timeout 15 seconds the same as linux hdparm */
+	timeout = timeout ? timeout : 15 * 1000;
+
+	security_enabled = ident_buf->security_status & ATA_SECURITY_ENABLED;
+
+	/* first set the password if requested */
+	if (setpwd == 1) {
+		/* confirm we can erase before setting the password if erasing */
+		if (confirm == 0 &&
+		    (action == ATA_SECURITY_ACTION_ERASE_ENHANCED ||
+		    action == ATA_SECURITY_ACTION_ERASE) &&
+		    atasecurity_erase_confirm(device, ident_buf) == 0) {
+			cam_freeccb(ccb);
+			free(ident_buf);
+			return (error);
+		}
+
+		if (pwd.ctrl & ATA_SECURITY_PASSWORD_MASTER) {
+			pwd.revision = ident_buf->master_passwd_revision;
+			if (pwd.revision != 0 && pwd.revision != 0xfff &&
+			    --pwd.revision == 0) {
+				pwd.revision = 0xfffe;
+			}
+		}
+		error = atasecurity_set_password(device, ccb, retry_count,
+						 timeout, &pwd, quiet);
+		if (error != 0) {
+			cam_freeccb(ccb);
+			free(ident_buf);
+			return (error);
+		}
+		security_enabled = 1;
+	}
+
+	switch(action) {
+	case ATA_SECURITY_ACTION_FREEZE:
+		error = atasecurity_freeze(device, ccb, retry_count,
+					   timeout, quiet);
+		break;
+
+	case ATA_SECURITY_ACTION_UNLOCK:
+		if (security_enabled) {
+			if (ident_buf->security_status & ATA_SECURITY_LOCKED) {
+				error = atasecurity_unlock(device, ccb,
+					retry_count, timeout, &pwd, quiet);
+			} else {
+				warnx("Can't unlock, drive is not locked");
+				error = 1;
+			}
+		} else {
+			warnx("Can't unlock, security is disabled");
+			error = 1;
+		}
+		break;
+
+	case ATA_SECURITY_ACTION_DISABLE:
+		if (security_enabled) {
+			/* First unlock the drive if its locked */
+			if (ident_buf->security_status & ATA_SECURITY_LOCKED) {
+				error = atasecurity_unlock(device, ccb,
+							   retry_count,
+							   timeout,
+							   &pwd,
+							   quiet);
+			}
+
+			if (error == 0) {
+				error = atasecurity_disable(device,
+							    ccb,
+							    retry_count,
+							    timeout,
+							    &pwd,
+							    quiet);
+			}
+		} else {
+			warnx("Can't disable security (already disabled)");
+			error = 1;
+		}
+		break;
+
+	case ATA_SECURITY_ACTION_ERASE:
+		if (security_enabled) {
+			if (erase_timeout == 0) {
+				erase_timeout = atasecurity_erase_timeout_msecs(
+				    ident_buf->erase_time);
+			}
+
+			error = atasecurity_erase(device, ccb, retry_count,
+					          timeout, erase_timeout, &pwd,
+						  quiet);
+		} else {
+			warnx("Can't secure erase (security is disabled)");
+			error = 1;
+		}
+		break;
+
+	case ATA_SECURITY_ACTION_ERASE_ENHANCED:
+		if (security_enabled) {
+			if (ident_buf->security_status & ATA_SECURITY_ENH_SUPP) {
+				if (erase_timeout == 0) {
+					erase_timeout =
+					    atasecurity_erase_timeout_msecs(
+						ident_buf->enhanced_erase_time);
+				}
+
+				error = atasecurity_erase(device, ccb,
+							  retry_count, timeout,
+							  erase_timeout, &pwd,
+							  quiet);
+			} else {
+				warnx("Enhanced erase is not supported");
+				error = 1;
+			}
+		} else {
+			warnx("Can't secure erase (enhanced), "
+			      "(security is disabled)");
+			error = 1;
+		}
+		break;
+	}
+
+	cam_freeccb(ccb);
+	free(ident_buf);
+
+	return (error);
+}
+#endif /* MINIMALISTIC */
+
+/*
  * Parse out a bus, or a bus, target and lun in the following
  * format:
  * bus
@@ -5832,6 +6697,10 @@
 "        camcontrol standby    [dev_id][generic args][-t time]\n"
 "        camcontrol sleep      [dev_id][generic args]\n"
 "        camcontrol fwdownload [dev_id][generic args] <-f fw_image> [-y][-s]\n"
+"        camcontrol security   [dev_id][generic args]\n"
+"                              <-d pwd | -e pwd | -f | -h pwd | -k pwd>\n"
+"                              [-l <high|maximum>] [-q] [-s pwd] [-T timeout]\n"
+"                              [-U <user|master>] [-y]\n"
 #endif /* MINIMALISTIC */
 "        camcontrol help\n");
 	if (!printlong)
@@ -5868,6 +6737,7 @@
 "standby     send the ATA STANDBY command to the named device\n"
 "sleep       send the ATA SLEEP command to the named device\n"
 "fwdownload  program firmware of the named device with the given image"
+"security    report or send ATA security commands to the named device\n"
 "help        this message\n"
 "Device Identifiers:\n"
 "bus:target        specify the bus and target, lun defaults to 0\n"
@@ -5964,7 +6834,24 @@
 "-f fw_image       path to firmware image file\n"
 "-y                don't ask any questions\n"
 "-s                run in simulation mode\n"
-"-v                print info for every firmware segment sent to device\n");
+"-v                print info for every firmware segment sent to device\n"
+"security arguments:\n"
+"-d pwd            disable security using the given password for the selected\n"
+"                  user\n"
+"-e pwd            erase the device using the given pwd for the selected user\n"
+"-f                freeze the security configuration of the specified device\n"
+"-h pwd            enhanced erase the device using the given pwd for the\n"
+"                  selected user\n"
+"-k pwd            unlock the device using the given pwd for the selected\n"
+"                  user\n"
+"-l <high|maximum> specifies which security level to set: high or maximum\n"
+"-q                be quiet, do not print any status messages\n"
+"-s pwd            password the device (enable security) using the given\n"
+"                  pwd for the selected user\n"
+"-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"
+);
 #endif /* MINIMALISTIC */
 }
 
@@ -6277,9 +7164,12 @@
 		case CAM_CMD_STANDBY:
 		case CAM_CMD_SLEEP:
 			error = atapm(cam_dev, argc, argv,
-						 combinedopt, retry_count,
-						 timeout);
+				      combinedopt, retry_count, timeout);
 			break;
+		case CAM_CMD_SECURITY:
+			error = atasecurity(cam_dev, retry_count, timeout,
+					    argc, argv, combinedopt);
+			break;
 		case CAM_CMD_DOWNLOAD_FW:
 			error = fwdownload(cam_dev, argc, argv, combinedopt,
 			    arglist & CAM_ARG_VERBOSE, retry_count, timeout,

Modified: trunk/sys/sys/ata.h
===================================================================
--- trunk/sys/sys/ata.h	2018-02-22 05:08:40 UTC (rev 9773)
+++ trunk/sys/sys/ata.h	2018-02-22 05:10:17 UTC (rev 9774)
@@ -189,10 +189,10 @@
 	} __packed support, enabled;
 
 /*088*/ u_int16_t       udmamodes;              /* UltraDMA modes */
-/*089*/ u_int16_t       erase_time;
-/*090*/ u_int16_t       enhanced_erase_time;
+/*089*/ u_int16_t       erase_time;             /* time req'd in 2min units */
+/*090*/ u_int16_t       enhanced_erase_time;    /* time req'd in 2min units */
 /*091*/ u_int16_t       apm_value;
-/*092*/ u_int16_t       master_passwd_revision;
+/*092*/ u_int16_t       master_passwd_revision; /* password revision code */
 /*093*/ u_int16_t       hwres;
 #define ATA_CABLE_ID                    0x2000
 
@@ -229,6 +229,14 @@
 	u_int16_t       reserved121[6];
 /*127*/ u_int16_t       removable_status;
 /*128*/ u_int16_t       security_status;
+#define ATA_SECURITY_LEVEL		0x0100	/* 0: high, 1: maximum */
+#define ATA_SECURITY_ENH_SUPP		0x0020	/* enhanced erase supported */
+#define ATA_SECURITY_COUNT_EXP		0x0010	/* count expired */
+#define ATA_SECURITY_FROZEN		0x0008	/* security config is frozen */
+#define ATA_SECURITY_LOCKED		0x0004	/* drive is locked */
+#define ATA_SECURITY_ENABLED		0x0002	/* ATA Security is enabled */
+#define ATA_SECURITY_SUPPORTED		0x0001	/* ATA Security is supported */
+
 	u_int16_t       reserved129[31];
 /*160*/ u_int16_t       cfa_powermode1;
 	u_int16_t       reserved161;
@@ -377,7 +385,12 @@
 #define         ATA_SF_DIS_RELIRQ       0xdd    /* disable release interrupt */
 #define         ATA_SF_ENAB_SRVIRQ      0x5e    /* enable service interrupt */
 #define         ATA_SF_DIS_SRVIRQ       0xde    /* disable service interrupt */
-#define ATA_SECURITY_FREEE_LOCK         0xf5    /* freeze security config */
+#define ATA_SECURITY_SET_PASSWORD       0xf1    /* set drive password */
+#define ATA_SECURITY_UNLOCK             0xf2    /* unlock drive using passwd */
+#define ATA_SECURITY_ERASE_PREPARE      0xf3    /* prepare to erase drive */
+#define ATA_SECURITY_ERASE_UNIT         0xf4    /* erase all blocks on drive */
+#define ATA_SECURITY_FREEZE_LOCK        0xf5    /* freeze security config */
+#define ATA_SECURITY_DISABLE_PASSWORD   0xf6    /* disable drive password */
 #define ATA_READ_NATIVE_MAX_ADDRESS     0xf8    /* read native max address */
 #define ATA_SET_MAX_ADDRESS             0xf9    /* set max address */
 
@@ -523,6 +536,20 @@
     int                 error;
 };
 
+struct ata_security_password {
+	u_int16_t		ctrl;
+#define ATA_SECURITY_PASSWORD_USER	0x0000
+#define ATA_SECURITY_PASSWORD_MASTER	0x0001
+#define ATA_SECURITY_ERASE_NORMAL	0x0000
+#define ATA_SECURITY_ERASE_ENHANCED	0x0002
+#define ATA_SECURITY_LEVEL_HIGH		0x0000
+#define ATA_SECURITY_LEVEL_MAXIMUM	0x0100
+
+	u_int8_t		password[32];
+	u_int16_t		revision;
+	u_int16_t		reserved[238];
+};
+
 /* pr device ATA ioctl calls */
 #define IOCATAREQUEST           _IOWR('a', 100, struct ata_ioc_request)
 #define IOCATAGPARM             _IOR('a', 101, struct ata_params)



More information about the Midnightbsd-cvs mailing list