From nobody@FreeBSD.org  Wed Aug 17 09:01:22 2011
Return-Path: <nobody@FreeBSD.org>
Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34])
	by hub.freebsd.org (Postfix) with ESMTP id 9146D1065674
	for <freebsd-gnats-submit@FreeBSD.org>; Wed, 17 Aug 2011 09:01:22 +0000 (UTC)
	(envelope-from nobody@FreeBSD.org)
Received: from red.freebsd.org (red.freebsd.org [IPv6:2001:4f8:fff6::22])
	by mx1.freebsd.org (Postfix) with ESMTP id 7E9638FC1B
	for <freebsd-gnats-submit@FreeBSD.org>; Wed, 17 Aug 2011 09:01:22 +0000 (UTC)
Received: from red.freebsd.org (localhost [127.0.0.1])
	by red.freebsd.org (8.14.4/8.14.4) with ESMTP id p7H91MVi005621
	for <freebsd-gnats-submit@FreeBSD.org>; Wed, 17 Aug 2011 09:01:22 GMT
	(envelope-from nobody@red.freebsd.org)
Received: (from nobody@localhost)
	by red.freebsd.org (8.14.4/8.14.4/Submit) id p7H91Leo005620;
	Wed, 17 Aug 2011 09:01:21 GMT
	(envelope-from nobody)
Message-Id: <201108170901.p7H91Leo005620@red.freebsd.org>
Date: Wed, 17 Aug 2011 09:01:21 GMT
From: Steven Hartland <killing@multiplay.co.uk>
To: freebsd-gnats-submit@FreeBSD.org
Subject: Patch that adds ATA security options to camcontrol including secure erase
X-Send-Pr-Version: www-3.1
X-GNATS-Notify:

>Number:         159833
>Category:       bin
>Synopsis:       camcontrol(8): [patch] add ATA security options to camcontrol including secure erase
>Confidential:   no
>Severity:       non-critical
>Priority:       low
>Responsible:    smh
>State:          closed
>Quarter:        
>Keywords:       
>Date-Required:  
>Class:          change-request
>Submitter-Id:   current-users
>Arrival-Date:   Wed Aug 17 09:10:09 UTC 2011
>Closed-Date:    Fri Jun 07 14:58:34 UTC 2013
>Last-Modified:  Fri Jun 07 14:58:34 UTC 2013
>Originator:     Steven Hartland
>Release:        8.2-RELEASE
>Organization:
Multiplay
>Environment:
N/A
>Description:
With increase in use of both SSD's and ZFS and the lack of TRIM support under ZFS SSD's need to be secure erased periodically to return performance to that when they are new.

The attached patch adds the relavent options to camcontrol that enables an admin to perform all the ATA security options on a disk, including the ability to secure erase a disk.

Given the dangerous nature of the commands added, the patch also adds long option support to camcontrol so that command line options used are understandable instead of random letters with little or no correspondence to the options they represent.

This patch has been discussed on the fs mailing list here, where some changes where suggested an implemented:-
http://lists.freebsd.org/pipermail/freebsd-fs/2011-August/012148.html

Some more examples and info can also be found here:-
http://blog.multiplay.co.uk/2011/08/freebsd-security-support-for-ata-devices-via-camcontrol/

Much credit to Daniel Roethlisberger for his work on adding security support to atacontrol, detailed in PR bin/127918 which was the basis of this code.
http://www.roe.ch/ATA_Security
http://www.freebsd.org/cgi/query-pr.cgi?pr=bin/127918
>How-To-Repeat:
N/A
>Fix:
N/A

Patch attached with submission follows:

--- sbin/camcontrol/camcontrol.c.orig	2011-08-05 11:02:32.023288993 +0000
+++ sbin/camcontrol/camcontrol.c	2011-08-06 13:03:40.725619592 +0000
@@ -42,6 +42,7 @@
 #include <ctype.h>
 #include <err.h>
 #include <libutil.h>
+#include <getopt.h>
 
 #include <cam/cam.h>
 #include <cam/cam_debug.h>
@@ -77,7 +78,8 @@
 	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 {
@@ -120,6 +122,7 @@
 	cam_cmdmask	cmdnum;
 	cam_argmask	argnum;
 	const char	*subopt;
+	const struct option const *suboptlong;
 };
 
 #ifndef MINIMALISTIC
@@ -128,43 +131,62 @@
 static const char negotiate_opts[] = "acD:M:O:qR:T:UW:";
 #endif
 
+static const struct option no_opts[] = { { NULL, 0, NULL, 0 } };
+
 struct camcontrol_opts option_table[] = {
 #ifndef MINIMALISTIC
-	{"tur", CAM_CMD_TUR, CAM_ARG_NONE, NULL},
-	{"inquiry", CAM_CMD_INQUIRY, CAM_ARG_NONE, "DSR"},
-	{"identify", CAM_CMD_IDENTIFY, CAM_ARG_NONE, NULL},
-	{"start", CAM_CMD_STARTSTOP, CAM_ARG_START_UNIT, NULL},
-	{"stop", CAM_CMD_STARTSTOP, CAM_ARG_NONE, NULL},
-	{"load", CAM_CMD_STARTSTOP, CAM_ARG_START_UNIT | CAM_ARG_EJECT, NULL},
-	{"eject", CAM_CMD_STARTSTOP, CAM_ARG_EJECT, NULL},
-	{"reportluns", CAM_CMD_REPORTLUNS, CAM_ARG_NONE, "clr:"},
-	{"readcapacity", CAM_CMD_READCAP, CAM_ARG_NONE, "bhHNqs"},
+	{"tur", CAM_CMD_TUR, CAM_ARG_NONE, NULL, NULL},
+	{"inquiry", CAM_CMD_INQUIRY, CAM_ARG_NONE, "DSR", NULL},
+	{"identify", CAM_CMD_IDENTIFY, CAM_ARG_NONE, NULL, NULL},
+	{"start", CAM_CMD_STARTSTOP, CAM_ARG_START_UNIT, NULL, NULL},
+	{"stop", CAM_CMD_STARTSTOP, CAM_ARG_NONE, NULL, NULL},
+	{"load", CAM_CMD_STARTSTOP, CAM_ARG_START_UNIT | CAM_ARG_EJECT, NULL, NULL},
+	{"eject", CAM_CMD_STARTSTOP, CAM_ARG_EJECT, NULL, NULL},
+	{"reportluns", CAM_CMD_REPORTLUNS, CAM_ARG_NONE, "clr:", NULL},
+	{"readcapacity", CAM_CMD_READCAP, CAM_ARG_NONE, "bhHNqs", NULL},
 #endif /* MINIMALISTIC */
-	{"rescan", CAM_CMD_RESCAN, CAM_ARG_NONE, NULL},
-	{"reset", CAM_CMD_RESET, CAM_ARG_NONE, NULL},
+	{"rescan", CAM_CMD_RESCAN, CAM_ARG_NONE, NULL, NULL},
+	{"reset", CAM_CMD_RESET, CAM_ARG_NONE, NULL, NULL},
 #ifndef MINIMALISTIC
-	{"cmd", CAM_CMD_SCSI_CMD, CAM_ARG_NONE, scsicmd_opts},
-	{"command", CAM_CMD_SCSI_CMD, CAM_ARG_NONE, scsicmd_opts},
-	{"defects", CAM_CMD_READ_DEFECTS, CAM_ARG_NONE, readdefect_opts},
-	{"defectlist", CAM_CMD_READ_DEFECTS, CAM_ARG_NONE, readdefect_opts},
+	{"cmd", CAM_CMD_SCSI_CMD, CAM_ARG_NONE, scsicmd_opts, NULL},
+	{"command", CAM_CMD_SCSI_CMD, CAM_ARG_NONE, scsicmd_opts, NULL},
+	{"defects", CAM_CMD_READ_DEFECTS, CAM_ARG_NONE, readdefect_opts, NULL},
+	{"defectlist", CAM_CMD_READ_DEFECTS, CAM_ARG_NONE, readdefect_opts, NULL},
 #endif /* MINIMALISTIC */
-	{"devlist", CAM_CMD_DEVTREE, CAM_ARG_NONE, NULL},
+	{"devlist", CAM_CMD_DEVTREE, CAM_ARG_NONE, NULL, NULL},
 #ifndef MINIMALISTIC
-	{"periphlist", CAM_CMD_DEVLIST, CAM_ARG_NONE, NULL},
-	{"modepage", CAM_CMD_MODE_PAGE, CAM_ARG_NONE, "bdelm:P:"},
-	{"tags", CAM_CMD_TAG, CAM_ARG_NONE, "N:q"},
-	{"negotiate", CAM_CMD_RATE, CAM_ARG_NONE, negotiate_opts},
-	{"rate", CAM_CMD_RATE, CAM_ARG_NONE, negotiate_opts},
-	{"debug", CAM_CMD_DEBUG, CAM_ARG_NONE, "IPTSXc"},
-	{"format", CAM_CMD_FORMAT, CAM_ARG_NONE, "qrwy"},
-	{"idle", CAM_CMD_IDLE, CAM_ARG_NONE, "t:"},
-	{"standby", CAM_CMD_STANDBY, CAM_ARG_NONE, "t:"},
-	{"sleep", CAM_CMD_SLEEP, CAM_ARG_NONE, ""},
+	{"periphlist", CAM_CMD_DEVLIST, CAM_ARG_NONE, NULL, NULL},
+	{"modepage", CAM_CMD_MODE_PAGE, CAM_ARG_NONE, "bdelm:P:", NULL},
+	{"tags", CAM_CMD_TAG, CAM_ARG_NONE, "N:q", NULL},
+	{"negotiate", CAM_CMD_RATE, CAM_ARG_NONE, negotiate_opts, NULL},
+	{"rate", CAM_CMD_RATE, CAM_ARG_NONE, negotiate_opts, NULL},
+	{"debug", CAM_CMD_DEBUG, CAM_ARG_NONE, "IPTSXc", NULL},
+	{"format", CAM_CMD_FORMAT, CAM_ARG_NONE, "qrwy", NULL},
+	{"idle", CAM_CMD_IDLE, CAM_ARG_NONE, "t:", NULL},
+	{"standby", CAM_CMD_STANDBY, CAM_ARG_NONE, "t:", NULL},
+	{"sleep", CAM_CMD_SLEEP, CAM_ARG_NONE, "", NULL},
+	{"security", CAM_CMD_SECURITY, CAM_ARG_NONE, "fr:m:s:e:h:d:U:",
+		(const struct option const [])
+		{
+			{ "security-quiet", no_argument, NULL, 'q' },
+			{ "security-confirm", no_argument, NULL, 'y' },
+			{ "security-freeze", no_argument, NULL, 'f' },
+			{ "security-user", required_argument, NULL, 'r' },
+			{ "security-level", required_argument, NULL, 'l' },
+			{ "security-set-password", required_argument, NULL, 's' },
+			{ "security-disable", required_argument, NULL, 'd' },
+			{ "security-unlock", required_argument, NULL, 'U' },
+			{ "security-erase", required_argument, NULL, 'e' },
+			{ "security-erase-enhanced", required_argument, NULL, 'h' },
+			{ "security-erase-timeout", required_argument, NULL, 'i' },
+			{ NULL, 0, NULL, 0 }
+		}
+	},
 #endif /* MINIMALISTIC */
-	{"help", CAM_CMD_USAGE, CAM_ARG_NONE, NULL},
-	{"-?", CAM_CMD_USAGE, CAM_ARG_NONE, NULL},
-	{"-h", CAM_CMD_USAGE, CAM_ARG_NONE, NULL},
-	{NULL, 0, 0, NULL}
+	{"help", CAM_CMD_USAGE, CAM_ARG_NONE, NULL, NULL},
+	{"-?", CAM_CMD_USAGE, CAM_ARG_NONE, NULL, NULL},
+	{"-h", CAM_CMD_USAGE, CAM_ARG_NONE, NULL, NULL},
+	{NULL, 0, 0, NULL, NULL}
 };
 
 typedef enum {
@@ -178,7 +200,7 @@
 
 
 camcontrol_optret getoption(char *arg, cam_cmdmask *cmdnum, cam_argmask *argnum,
-			    const char **subopt);
+			    const char **subopt, const struct option **suboptlong);
 #ifndef MINIMALISTIC
 static int getdevlist(struct cam_device *device);
 #endif /* MINIMALISTIC */
@@ -225,6 +247,9 @@
 			    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);
+static int atasecurity(struct cam_device *device, int retry_count, int timeout,
+			int argc, char **argv, char *combinedopt, const struct option *combinedoptlong);
+
 #endif /* MINIMALISTIC */
 #ifndef min
 #define min(a,b) (((a)<(b))?(a):(b))
@@ -235,10 +260,11 @@
 
 camcontrol_optret
 getoption(char *arg, cam_cmdmask *cmdnum, cam_argmask *argnum,
-	  const char **subopt)
+	  const char **subopt, const struct option **suboptlong)
 {
 	struct camcontrol_opts *opts;
 	int num_matches = 0;
+	const struct option empty_optlong[] = {{NULL, 0, NULL, 0}};
 
 	for (opts = option_table; (opts != NULL) && (opts->optname != NULL);
 	     opts++) {
@@ -246,6 +272,10 @@
 			*cmdnum = opts->cmdnum;
 			*argnum = opts->argnum;
 			*subopt = opts->subopt;
+			if (NULL != opts->suboptlong)
+				*suboptlong = opts->suboptlong;
+			else
+				*suboptlong = empty_optlong;
 			if (++num_matches > 1)
 				return(CC_OR_AMBIGUOUS);
 		}
@@ -1264,33 +1294,48 @@
 }
 
 static int
-ataidentify(struct cam_device *device, int retry_count, int timeout)
+ata_cam_send(struct cam_device *device, union ccb *ccb)
 {
-	union ccb *ccb;
-	struct ata_params *ident_buf;
-	struct ccb_getdev cgd;
-	u_int i, error = 0;
-	int16_t *ptr;
+	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;
+
+	if (arglist & CAM_ARG_ERR_RECOVER)
+		ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER;
+
+	if (cam_send_ccb(device, ccb) < 0) {
+		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);
 
-	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");
+	if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
+		warnx("ATA %s failed", ata_op_string(&(ccb->ataio.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));
+	return(0);
+}
 
-	ptr = (uint16_t *)malloc(sizeof(struct ata_params));
+static int
+ata_do_identify(struct cam_device *device, int retry_count, int timeout,
+	union ccb *ccb, struct ccb_getdev *cgd, struct ata_params** ident_bufp)
+{
+	struct ata_params *ident_buf;
+	u_int i, error;
+	int16_t *ptr;
 
+	ptr = (uint16_t *)malloc(sizeof(struct ata_params));
 	if (ptr == NULL) {
-		cam_freeccb(ccb);
 		warnx("can't malloc memory for identify\n");
 		return(1);
 	}
@@ -1304,48 +1349,23 @@
 		      /*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;
-
-	if (arglist & CAM_ARG_ERR_RECOVER)
-		ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER;
 
-	if (cam_send_ccb(device, ccb) < 0) {
-		perror("error sending ATA identify");
-
-		if (arglist & CAM_ARG_VERBOSE) {
-			cam_error_print(device, ccb, CAM_ESF_ALL,
-					CAM_EPF_ALL, stderr);
-		}
+	/*
+	 * We check protocol == PROTO_ATAPI using ATA as default to enhance
+	 * compatibility with other controllers which may support passthrough
+	 */
+	ata_28bit_cmd(&ccb->ataio, (cgd->protocol == PROTO_ATAPI) ?
+	    ATA_ATAPI_IDENTIFY : ATA_ATA_IDENTIFY, 0, 0, 0);
 
+	error = ata_cam_send(device, ccb);
+	if (0 != error) {
 		free(ptr);
-		cam_freeccb(ccb);
 		return(1);
 	}
 
-	if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
-		error = 1;
-
-		if (arglist & CAM_ARG_VERBOSE) {
-			cam_error_print(device, ccb, CAM_ESF_ALL,
-					CAM_EPF_ALL, stderr);
-		}
-	}
-
-	cam_freeccb(ccb);
-
-	if (error != 0) {
-		free(ptr);
-		return(error);
-	}
-
 	for (i = 0; i < sizeof(struct ata_params) / 2; i++)
 		ptr[i] = le16toh(ptr[i]);
+
 	if (arglist & CAM_ARG_VERBOSE) {
 		fprintf(stdout, "%s%d: Raw identify data:\n",
 		    device->device_name, device->dev_unit_num);
@@ -1377,18 +1397,559 @@
 	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;
+	struct ccb_getdev cgd;
+	u_int error = 0;
+
+	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);
+	}
+
+	/* 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));
+
+	error = ata_do_identify(device, retry_count, timeout, ccb, &cgd, &ident_buf);
+	if (0 != error) {
+		cam_freeccb(ccb);
+		return(1);
+	}
+
+	fprintf(stdout, "%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);
 }
 #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 (0 == timeout)
+		return 2 * 3600 * 1000; /* default: two hours */
+	else if (255 <= timeout)
+		return (508 + 60) * 60 * 1000; /* spec says > 508 minutes */
+
+	return ((2 * timeout) + 5) * 60 * 1000; /* add a 5min margin */
+}
+
+static void
+atasecurity_notify(union ccb *ccb, struct ata_security_password *pwd)
+{
+	fprintf(stdout, "Issuing %s", ata_op_string(&(ccb->ataio.cmd)));
+
+	if (NULL != pwd) {
+		fprintf(stdout, " password='%s', user='%s'",
+		    pwd->password,
+		    (pwd->ctrl & ATA_SECURITY_PASSWORD_MASTER) ? "master" : "user");
+
+		if(ATA_SECURITY_SET_PASSWORD == ccb->ataio.cmd.command)
+			fprintf(stdout, ", mode='%s'",
+			    (pwd->ctrl & ATA_SECURITY_LEVEL_MAXIMUM) ?  "maximum" : "high");
+	}
+
+	fprintf(stdout, "\n");
+}
+
+static int
+atasecurity_freeze(struct cam_device *device, union ccb *ccb, int retry_count,
+	u_int32_t timeout, int quiet)
+{
+	cam_fill_ataio(&ccb->ataio,
+		    retry_count,
+		    NULL,
+		    /*flags*/CAM_DIR_NONE,
+		    MSG_SIMPLE_Q_TAG,
+		    /*data_ptr*/NULL,
+		    /*dxfer_len*/0,
+		    timeout);
+
+	ata_28bit_cmd(&ccb->ataio, ATA_SECURITY_FREEZE_LOCK, 0, 0, 0);
+
+	if (0 == quiet)
+		atasecurity_notify(ccb, NULL);
+
+	return ata_cam_send(device, ccb);
+}
+
+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)
+{
+	cam_fill_ataio(&ccb->ataio,
+		    retry_count,
+		    NULL,
+		    /*flags*/CAM_DIR_OUT,
+		    MSG_SIMPLE_Q_TAG,
+		    /*data_ptr*/(u_int8_t *)pwd,
+		    /*dxfer_len*/sizeof(struct ata_security_password),
+		    timeout);
+
+	ata_28bit_cmd(&ccb->ataio, ATA_SECURITY_UNLOCK, 0, 0, 0);
+
+	if (0 == quiet)
+		atasecurity_notify(ccb, pwd);
+
+	return ata_cam_send(device, ccb);
+}
+
+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)
+{
+	cam_fill_ataio(&ccb->ataio,
+		    retry_count,
+		    NULL,
+		    /*flags*/CAM_DIR_OUT,
+		    MSG_SIMPLE_Q_TAG,
+		    /*data_ptr*/(u_int8_t *)pwd,
+		    /*dxfer_len*/sizeof(struct ata_security_password),
+		    timeout);
+
+	ata_28bit_cmd(&ccb->ataio, ATA_SECURITY_DISABLE_PASSWORD, 0, 0, 0);
+
+	if (0 == quiet)
+		atasecurity_notify(ccb, pwd);
+
+	return ata_cam_send(device, ccb);
+}
+
+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 confirm, int quiet, struct ata_params* ident_buf)
+{
+	int error = 0, response = 0;
+	if (0 == quiet) {
+        fprintf(stdout, "\nYou are about to ERASE ALL DATA from the "
+            "following device:\n");
+		fprintf(stdout, "%s%d: ", device->device_name, device->dev_unit_num);
+		ata_print_ident(ident_buf);
+	}
+
+	if (0 == confirm) {
+		do {
+			char str[50];
+			fprintf(stdout, "Are you SURE you want to do this? (yes/no) ");
+
+			if (NULL != fgets(str, sizeof(str), stdin)) {
+				if (0 == strncasecmp(str, "yes", 3))
+					response = 1;
+				else if (0 == strncasecmp(str, "no", 2))
+					response = -1;
+				else
+					fprintf(stdout, "Please answer \"yes\" or \"no\"\n");
+			}
+		} while (0 == response);
+
+		if (-1 == response)
+			return(1);
+	}
+
+	cam_fill_ataio(&ccb->ataio,
+		    retry_count,
+		    NULL,
+		    /*flags*/CAM_DIR_NONE,
+		    MSG_SIMPLE_Q_TAG,
+		    /*data_ptr*/NULL,
+		    /*dxfer_len*/0,
+		    timeout);
+
+	ata_28bit_cmd(&ccb->ataio, ATA_SECURITY_ERASE_PREPARE, 0, 0, 0);
+
+	error = ata_cam_send(device, ccb);
+	if (0 != error)
+		return error;
+
+	/* 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));
+
+	cam_fill_ataio(&ccb->ataio,
+		    retry_count,
+		    NULL,
+		    /*flags*/CAM_DIR_OUT,
+		    MSG_SIMPLE_Q_TAG,
+		    /*data_ptr*/(u_int8_t *)pwd,
+		    /*dxfer_len*/sizeof(struct ata_security_password),
+		    erase_timeout);
+
+	ata_28bit_cmd(&ccb->ataio, ATA_SECURITY_ERASE_UNIT, 0, 0, 0);
+
+	if (0 == quiet)
+		atasecurity_notify(ccb, pwd);
+
+	error = ata_cam_send(device, ccb);
+
+	if (0 == error && 0 == quiet)
+		fprintf(stdout, "\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)
+{
+	cam_fill_ataio(&ccb->ataio,
+		    retry_count,
+		    NULL,
+		    /*flags*/CAM_DIR_OUT,
+		    MSG_SIMPLE_Q_TAG,
+		    /*data_ptr*/(u_int8_t *)pwd,
+		    /*dxfer_len*/sizeof(struct ata_security_password),
+		    timeout);
+
+	ata_28bit_cmd(&ccb->ataio, ATA_SECURITY_SET_PASSWORD, 0, 0, 0);
+
+	if (0 == quiet)
+		atasecurity_notify(ccb, pwd);
+
+	return ata_cam_send(device, ccb);
+}
+
+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)" : "");
+}
+
+static int
+atasecurity(struct cam_device *device, int retry_count, int timeout,
+	int argc, char **argv, char *combinedopt, const struct option *combinedoptlong)
+{
+	union ccb *ccb;
+	struct ata_params *ident_buf;
+	u_int error = 0, confirm = 0, quiet = 0;
+	int c = -1;
+	int action = ATA_SECURITY_ACTION_PRINT;
+	int actions = 0, setpwd = 0, enabled = 0, erase_timeout = 0;
+	struct ata_security_password pwd;
+	struct ccb_getdev cgd;
+
+	memset(&pwd, 0, sizeof(pwd));
+	pwd.ctrl |= ATA_SECURITY_PASSWORD_MASTER; /* user is master by default as its safer that way */
+
+	while ((c = getopt_long(argc, argv, combinedopt, combinedoptlong, NULL)) != -1) {
+        switch(c){
+        case 'f':
+			action = ATA_SECURITY_ACTION_FREEZE;
+			actions++;
+			break;
+
+		case 'r':
+			if (0 == strcasecmp(optarg, "user")) {
+				pwd.ctrl ^= ATA_SECURITY_PASSWORD_MASTER;
+				pwd.ctrl |= ATA_SECURITY_PASSWORD_USER;
+			} else if (0 != strcasecmp(optarg, "master")) {
+				warnx("--security-user argument '%s' is unknown, must be 'user' or 'master'", optarg);
+				return(1);
+			}
+			break;
+
+		case 'l':
+			if (0 == strcasecmp(optarg, "high"))
+				pwd.ctrl |= ATA_SECURITY_LEVEL_HIGH;
+			else if (0 == strcasecmp(optarg, "maximum"))
+				pwd.ctrl |= ATA_SECURITY_LEVEL_MAXIMUM;
+			else {
+				warnx("--security-level argument '%s' is unknown, must be 'high' or 'maximum'", optarg);
+				return(1);
+			}
+			break;
+
+		case 'U':
+			if (sizeof(pwd.password) < strlen(optarg)) {
+				warnx("--security-unlock password is too long");
+				return(1);
+			}
+			strlcpy(pwd.password, optarg, sizeof(pwd.password));
+			action = ATA_SECURITY_ACTION_UNLOCK;
+			actions++;
+			break;
+
+		case 'd':
+			if (sizeof(pwd.password) < strlen(optarg)) {
+				warnx("--security-disable password is too long");
+				return(1);
+			}
+			strlcpy(pwd.password, optarg, sizeof(pwd.password));
+			action = ATA_SECURITY_ACTION_DISABLE;
+			actions++;
+			break;
+
+		case 'e':
+			if (sizeof(pwd.password) < strlen(optarg)) {
+				warnx("--security-erase password is too long");
+				return(1);
+			}
+			strlcpy(pwd.password, optarg, sizeof(pwd.password));
+			action = ATA_SECURITY_ACTION_ERASE;
+			actions++;
+			break;
+
+		case 'h':
+			if (sizeof(pwd.password) < strlen(optarg)) {
+				warnx("--security-erase-enhanced password is too long");
+				return(1);
+			}
+			strlcpy(pwd.password, optarg, sizeof(pwd.password));
+			pwd.ctrl |= ATA_SECURITY_ERASE_ENHANCED;
+			action = ATA_SECURITY_ACTION_ERASE_ENHANCED;
+			actions++;
+			break;
+
+		case 's':
+			if (sizeof(pwd.password) < strlen(optarg)) {
+				warnx("--security-set-password password is too long");
+				return(1);
+			}
+			strlcpy(pwd.password, optarg, sizeof(pwd.password));
+			setpwd = 1;
+			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 'i':
+			erase_timeout = atoi(optarg) * 1000;
+			break;
+		}
+	}
+	argc -= optind;
+	argv += optind;
+
+	if (1 < actions) {
+		warnx("too many security 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);
+	}
+
+	/* 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));
+
+	error = ata_do_identify(device, retry_count, timeout, ccb, &cgd, &ident_buf);
+	if (0 != error) {
+		cam_freeccb(ccb);
+		return(1);
+	}
+
+	if (0 == quiet) {
+		fprintf(stdout, "%s%d: ", device->device_name, device->dev_unit_num);
+		ata_print_ident(ident_buf);
+		camxferrate(device);
+	}
+
+	if (action == ATA_SECURITY_ACTION_PRINT) {
+		if (0 != quiet) {
+			fprintf(stdout, "%s%d: ", device->device_name, device->dev_unit_num);
+			ata_print_ident(ident_buf);
+			camxferrate(device);
+		}
+		atasecurity_print(ident_buf);
+		free(ident_buf);
+		cam_freeccb(ccb);
+		return 0;
+	}
+
+	if (!(ident_buf->support.command1 & ATA_SUPPORT_SECURITY)) {
+		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;
+
+	/* 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));
+
+	enabled = ident_buf->security_status & ATA_SECURITY_ENABLED;
+
+	/* first set the password if requested */
+	if (setpwd) {
+		/* prepare pwd details */
+		if (pwd.ctrl & ATA_SECURITY_PASSWORD_MASTER) {
+			pwd.revision = ident_buf->master_passwd_revision;
+			if (0 != pwd.revision && 0xffff != pwd.revision && 0 == --pwd.revision)
+				pwd.revision = 0xfffe;
+		}
+		error = atasecurity_set_password(device, ccb, retry_count, timeout, &pwd, quiet);
+		if (0 != error) {
+			cam_freeccb(ccb);
+			free(ident_buf);
+			return error;
+		}
+		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 (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 (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 (0 == error) {
+				/* 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));
+				error = atasecurity_disable(device, ccb, retry_count, timeout, &pwd, quiet);
+			}
+		} else {
+			warnx("Can't disable security, security is already disabled");
+			error = 1;
+		}
+		break;
+
+	case ATA_SECURITY_ACTION_ERASE:
+		if (enabled) {
+			if (0 == erase_timeout)
+				erase_timeout = atasecurity_erase_timeout_msecs(ident_buf->erase_time);
+
+			error = atasecurity_erase(device, ccb, retry_count, timeout,
+			    erase_timeout, &pwd, confirm, quiet, ident_buf);
+		} else {
+			warnx("Can't secure erase, security is disabled");
+			error = 1;
+		}
+		break;
+
+	case ATA_SECURITY_ACTION_ERASE_ENHANCED:
+		if (enabled) {
+			if (ident_buf->security_status & ATA_SECURITY_ENH_SUPP) {
+				if (0 == erase_timeout)
+					erase_timeout = atasecurity_erase_timeout_msecs(ident_buf->enhanced_erase_time);
+
+				error = atasecurity_erase(device, ccb, retry_count, timeout,
+				    erase_timeout, &pwd, confirm, quiet, ident_buf);
+			} 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:
@@ -4393,6 +4954,17 @@
 "        camcontrol idle       [dev_id][generic args][-t time]\n"
 "        camcontrol standby    [dev_id][generic args][-t time]\n"
 "        camcontrol sleep      [dev_id][generic args]\n"
+"        camcontrol security   [dev_id][generic args]\n"
+"                              [--security-freeze] [--security-quiet]\n"
+"                              [--security-confirm]\n"
+"                              [--security-user <user|master>]\n"
+"                              [--security-level <high|maximum>]\n"
+"                              [--security-set-password <pwd>]\n"
+"                              [--security-unlock <pwd>]\n"
+"                              [--security-disable <pwd>]\n"
+"                              [--security-erase <pwd>]\n"
+"                              [--security-erase-enhanced <pwd>]\n"
+"                              [--security-erase-timeout <timeout>]\n"
 #endif /* MINIMALISTIC */
 "        camcontrol help\n");
 	if (!verbose)
@@ -4423,6 +4995,7 @@
 "idle        send the ATA IDLE command to the named device\n"
 "standby     send the ATA STANDBY command to the named device\n"
 "sleep       send the ATA SLEEP command to the named device\n"
+"security    report / 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"
@@ -4492,7 +5065,17 @@
 "-w                don't send immediate format command\n"
 "-y                don't ask any questions\n"
 "idle/standby arguments:\n"
-"-t <arg>          number of seconds before respective state.\n");
+"-t <arg>          number of seconds before respective state.\n"
+"security arguments:\n"
+"--security-freeze                   freeze the security configuration of the specified device\n"
+"--security-user user|master         specifies which user to set: user or master\n"
+"--security-level high|maximum       specifies which security level to set: high or maximum\n"
+"--security-set-password <pwd>       password the device (enable security) using the given password for the selected user\n"
+"--security-unlock <pwd>             unlock the device using the given password for the selected user\n"
+"--security-disable <pwd>            disable security using the given password for the selected user\n"
+"--security-erase <pwd>              erase the device using the given password for the selected user\n"
+"--security-erase-enhanced <pwd>     enhanced erase the device using the given password for the selected user\n"
+"--security-erase-timeout <timeout>  overrides the timeout (in seconds) used for security erase operation\n");
 #endif /* MINIMALISTIC */
 }
 
@@ -4511,6 +5094,7 @@
 	char combinedopt[256];
 	int error = 0, optstart = 2;
 	int devopen = 1;
+	const struct option *longopts = NULL;
 #ifndef MINIMALISTIC
 	int bus, target, lun;
 #endif /* MINIMALISTIC */
@@ -4526,7 +5110,7 @@
 	/*
 	 * Get the base option.
 	 */
-	optreturn = getoption(argv[1], &cmdlist, &arglist, &subopt);
+	optreturn = getoption(argv[1], &cmdlist, &arglist, &subopt, &longopts);
 
 	if (optreturn == CC_OR_AMBIGUOUS) {
 		warnx("ambiguous option %s", argv[1]);
@@ -4642,7 +5226,7 @@
 	 * options, and ignoring options that possibly belong to
 	 * subfunctions.
 	 */
-	while ((c = getopt(argc, argv, combinedopt))!= -1){
+	while ((c = getopt_long(argc, argv, combinedopt, longopts, NULL)) != -1) {
 		switch(c) {
 			case 'C':
 				retry_count = strtol(optarg, NULL, 0);
@@ -4787,6 +5371,9 @@
 						 combinedopt, retry_count,
 						 timeout);
 			break;
+		case CAM_CMD_SECURITY:
+			error = atasecurity(cam_dev, retry_count, timeout, argc, argv, combinedopt, longopts);
+			break;
 #endif /* MINIMALISTIC */
 		case CAM_CMD_USAGE:
 			usage(1);
--- sbin/camcontrol/camcontrol.8.orig	2011-08-05 11:32:53.897577205 +0000
+++ sbin/camcontrol/camcontrol.8	2011-08-06 01:12:14.602310347 +0000
@@ -183,6 +183,21 @@
 .Op device id
 .Op generic args
 .Nm
+.Ic security
+.Op device id
+.Op generic args
+.Op Fl -security-quiet
+.Op Fl -security-confirm
+.Op Fl -security-freeze
+.Op Fl -security-user Ar user|master
+.Op Fl -security-level Ar high|maximum
+.Op Fl -security-set-password Ar pwd
+.Op Fl -security-unlock Ar pwd
+.Op Fl -security-disable Ar pwd
+.Op Fl -security-erase Ar pwd
+.Op Fl -security-enhanced-erase Ar pwd
+.Op Fl -security-erase-timeout Ar timeout
+.Nm
 .Ic help
 .Sh DESCRIPTION
 The
@@ -853,6 +868,122 @@
 .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 -security-quiet
+.Pp
+Be quiet, do not print any status messages.
+This option will not disable the questions, however.
+To disable questions, use the
+.Fl -security-confirm
+argument, below.
+.It Fl -security-confirm
+.Pp
+Confirm yes to dangerous options such as
+.Fl -security-erase
+without prompting for confirmation
+.It Fl -security-freeze
+.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 -security-user Ar user|master
+.Pp
+Specifies which user to set / use for the running action command, valid values
+are user or master.
+.Pp
+This option must be used in conjunction with one of the security action commands.
+.Pp
+Defaults to
+.Em master
+.It Fl -security-level Ar high|maximum
+.Pp
+Specifies which security level to set when issuing a
+.Fl -security-set-password
+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 -security-set-password Ar pwd
+.Pp
+Password the device (enable security) using the given password for the selected
+user.
+.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 -security-unlock Ar pwd
+.Pp
+Unlock the device using the given password for the selected user according to
+the devices configured security level.
+.It Fl -security-disable Ar pwd
+.Pp
+Disable device security using the given password for the selected user according
+to the devices configured security level.
+.It Fl -security-erase 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 -security-erase-enhanced 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 -security-erase-timeout Ar timeout
+.Pp
+Overrides the default timeout, specified in seconds, used for both
+.Fl -security-erase
+and
+.Fl -security-erase-enhanced
+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.
+.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
@@ -971,6 +1102,33 @@
 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.
+.Pp
+.Bd -literal -offset indent
+camcontrol security ada0
+.Ed
+.Pp
+Report security support and settings for ada0
+.Pp
+.Bd -literal -offset indent
+camcontrol security ada0 --security-user user --security-set-password MyPass 
+.Ed
+.Pp
+Enable security on device ada0 with the password MyPass
+.Pp
+.Bd -literal -offset indent
+camcontrol security ada0 --security-user user --security-erase MyPass
+.Ed
+.Pp
+Secure erase ada0
+.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 ,


>Release-Note:
>Audit-Trail:
From: "Steven Hartland" <killing@multiplay.co.uk>
To: <bug-followup@freebsd.org>
Subject: Re: bin/159833: camcontrol(8): [patch] add ATA security options to camcontrol including secure erase
Date: Sat, 22 Oct 2011 14:35:58 +0100
MIME-Version: 1.0
Content-Type: multipart/mixed;
	boundary="----=_NextPart_000_13E8_01CC90C7.E9C4EB30"

This is a multi-part message in MIME format.

------=_NextPart_000_13E8_01CC90C7.E9C4EB30
Content-Type: text/plain;
	format=flowed;
	charset="Windows-1252";
	reply-type=original
Content-Transfer-Encoding: 7bit

Latest version of the patch is attached, this fixes missing changes from
sys/sys/ata.h in the original version.

    Regards
    Steve

================================================
This e.mail is private and confidential between Multiplay (UK) Ltd. and the person or entity to whom it is addressed. In the event of misdirection, the recipient is prohibited from using, copying, printing or otherwise disseminating it or any information contained in it. 

In the event of misdirection, illegible or incomplete transmission please telephone +44 845 868 1337
or return the E.mail to postmaster@multiplay.co.uk.
------=_NextPart_000_13E8_01CC90C7.E9C4EB30
Content-Type: text/plain;
	format=flowed;
	name="ata_security_cam-v2.txt";
	reply-type=original
Content-Transfer-Encoding: quoted-printable
Content-Disposition: attachment;
	filename="ata_security_cam-v2.txt"

--- sbin/camcontrol/camcontrol.c.orig	2011-08-05 11:02:32.023288993 +0000=0A=
+++ sbin/camcontrol/camcontrol.c	2011-08-06 13:03:40.725619592 +0000=0A=
@@ -42,6 +42,7 @@=0A=
 #include <ctype.h>=0A=
 #include <err.h>=0A=
 #include <libutil.h>=0A=
+#include <getopt.h>=0A=
 =0A=
 #include <cam/cam.h>=0A=
 #include <cam/cam_debug.h>=0A=
@@ -77,7 +78,8 @@=0A=
 	CAM_CMD_IDENTIFY	=3D 0x00000013,=0A=
 	CAM_CMD_IDLE		=3D 0x00000014,=0A=
 	CAM_CMD_STANDBY		=3D 0x00000015,=0A=
-	CAM_CMD_SLEEP		=3D 0x00000016=0A=
+	CAM_CMD_SLEEP		=3D 0x00000016,=0A=
+	CAM_CMD_SECURITY	=3D 0x00000017=0A=
 } cam_cmdmask;=0A=
 =0A=
 typedef enum {=0A=
@@ -120,6 +122,7 @@=0A=
 	cam_cmdmask	cmdnum;=0A=
 	cam_argmask	argnum;=0A=
 	const char	*subopt;=0A=
+	const struct option const *suboptlong;=0A=
 };=0A=
 =0A=
 #ifndef MINIMALISTIC=0A=
@@ -128,43 +131,62 @@=0A=
 static const char negotiate_opts[] =3D "acD:M:O:qR:T:UW:";=0A=
 #endif=0A=
 =0A=
+static const struct option no_opts[] =3D { { NULL, 0, NULL, 0 } };=0A=
+=0A=
 struct camcontrol_opts option_table[] =3D {=0A=
 #ifndef MINIMALISTIC=0A=
-	{"tur", CAM_CMD_TUR, CAM_ARG_NONE, NULL},=0A=
-	{"inquiry", CAM_CMD_INQUIRY, CAM_ARG_NONE, "DSR"},=0A=
-	{"identify", CAM_CMD_IDENTIFY, CAM_ARG_NONE, NULL},=0A=
-	{"start", CAM_CMD_STARTSTOP, CAM_ARG_START_UNIT, NULL},=0A=
-	{"stop", CAM_CMD_STARTSTOP, CAM_ARG_NONE, NULL},=0A=
-	{"load", CAM_CMD_STARTSTOP, CAM_ARG_START_UNIT | CAM_ARG_EJECT, NULL},=0A=
-	{"eject", CAM_CMD_STARTSTOP, CAM_ARG_EJECT, NULL},=0A=
-	{"reportluns", CAM_CMD_REPORTLUNS, CAM_ARG_NONE, "clr:"},=0A=
-	{"readcapacity", CAM_CMD_READCAP, CAM_ARG_NONE, "bhHNqs"},=0A=
+	{"tur", CAM_CMD_TUR, CAM_ARG_NONE, NULL, NULL},=0A=
+	{"inquiry", CAM_CMD_INQUIRY, CAM_ARG_NONE, "DSR", NULL},=0A=
+	{"identify", CAM_CMD_IDENTIFY, CAM_ARG_NONE, NULL, NULL},=0A=
+	{"start", CAM_CMD_STARTSTOP, CAM_ARG_START_UNIT, NULL, NULL},=0A=
+	{"stop", CAM_CMD_STARTSTOP, CAM_ARG_NONE, NULL, NULL},=0A=
+	{"load", CAM_CMD_STARTSTOP, CAM_ARG_START_UNIT | CAM_ARG_EJECT, NULL, =
NULL},=0A=
+	{"eject", CAM_CMD_STARTSTOP, CAM_ARG_EJECT, NULL, NULL},=0A=
+	{"reportluns", CAM_CMD_REPORTLUNS, CAM_ARG_NONE, "clr:", NULL},=0A=
+	{"readcapacity", CAM_CMD_READCAP, CAM_ARG_NONE, "bhHNqs", NULL},=0A=
 #endif /* MINIMALISTIC */=0A=
-	{"rescan", CAM_CMD_RESCAN, CAM_ARG_NONE, NULL},=0A=
-	{"reset", CAM_CMD_RESET, CAM_ARG_NONE, NULL},=0A=
+	{"rescan", CAM_CMD_RESCAN, CAM_ARG_NONE, NULL, NULL},=0A=
+	{"reset", CAM_CMD_RESET, CAM_ARG_NONE, NULL, NULL},=0A=
 #ifndef MINIMALISTIC=0A=
-	{"cmd", CAM_CMD_SCSI_CMD, CAM_ARG_NONE, scsicmd_opts},=0A=
-	{"command", CAM_CMD_SCSI_CMD, CAM_ARG_NONE, scsicmd_opts},=0A=
-	{"defects", CAM_CMD_READ_DEFECTS, CAM_ARG_NONE, readdefect_opts},=0A=
-	{"defectlist", CAM_CMD_READ_DEFECTS, CAM_ARG_NONE, readdefect_opts},=0A=
+	{"cmd", CAM_CMD_SCSI_CMD, CAM_ARG_NONE, scsicmd_opts, NULL},=0A=
+	{"command", CAM_CMD_SCSI_CMD, CAM_ARG_NONE, scsicmd_opts, NULL},=0A=
+	{"defects", CAM_CMD_READ_DEFECTS, CAM_ARG_NONE, readdefect_opts, NULL},=0A=
+	{"defectlist", CAM_CMD_READ_DEFECTS, CAM_ARG_NONE, readdefect_opts, =
NULL},=0A=
 #endif /* MINIMALISTIC */=0A=
-	{"devlist", CAM_CMD_DEVTREE, CAM_ARG_NONE, NULL},=0A=
+	{"devlist", CAM_CMD_DEVTREE, CAM_ARG_NONE, NULL, NULL},=0A=
 #ifndef MINIMALISTIC=0A=
-	{"periphlist", CAM_CMD_DEVLIST, CAM_ARG_NONE, NULL},=0A=
-	{"modepage", CAM_CMD_MODE_PAGE, CAM_ARG_NONE, "bdelm:P:"},=0A=
-	{"tags", CAM_CMD_TAG, CAM_ARG_NONE, "N:q"},=0A=
-	{"negotiate", CAM_CMD_RATE, CAM_ARG_NONE, negotiate_opts},=0A=
-	{"rate", CAM_CMD_RATE, CAM_ARG_NONE, negotiate_opts},=0A=
-	{"debug", CAM_CMD_DEBUG, CAM_ARG_NONE, "IPTSXc"},=0A=
-	{"format", CAM_CMD_FORMAT, CAM_ARG_NONE, "qrwy"},=0A=
-	{"idle", CAM_CMD_IDLE, CAM_ARG_NONE, "t:"},=0A=
-	{"standby", CAM_CMD_STANDBY, CAM_ARG_NONE, "t:"},=0A=
-	{"sleep", CAM_CMD_SLEEP, CAM_ARG_NONE, ""},=0A=
+	{"periphlist", CAM_CMD_DEVLIST, CAM_ARG_NONE, NULL, NULL},=0A=
+	{"modepage", CAM_CMD_MODE_PAGE, CAM_ARG_NONE, "bdelm:P:", NULL},=0A=
+	{"tags", CAM_CMD_TAG, CAM_ARG_NONE, "N:q", NULL},=0A=
+	{"negotiate", CAM_CMD_RATE, CAM_ARG_NONE, negotiate_opts, NULL},=0A=
+	{"rate", CAM_CMD_RATE, CAM_ARG_NONE, negotiate_opts, NULL},=0A=
+	{"debug", CAM_CMD_DEBUG, CAM_ARG_NONE, "IPTSXc", NULL},=0A=
+	{"format", CAM_CMD_FORMAT, CAM_ARG_NONE, "qrwy", NULL},=0A=
+	{"idle", CAM_CMD_IDLE, CAM_ARG_NONE, "t:", NULL},=0A=
+	{"standby", CAM_CMD_STANDBY, CAM_ARG_NONE, "t:", NULL},=0A=
+	{"sleep", CAM_CMD_SLEEP, CAM_ARG_NONE, "", NULL},=0A=
+	{"security", CAM_CMD_SECURITY, CAM_ARG_NONE, "fr:m:s:e:h:d:U:",=0A=
+		(const struct option const [])=0A=
+		{=0A=
+			{ "security-quiet", no_argument, NULL, 'q' },=0A=
+			{ "security-confirm", no_argument, NULL, 'y' },=0A=
+			{ "security-freeze", no_argument, NULL, 'f' },=0A=
+			{ "security-user", required_argument, NULL, 'r' },=0A=
+			{ "security-level", required_argument, NULL, 'l' },=0A=
+			{ "security-set-password", required_argument, NULL, 's' },=0A=
+			{ "security-disable", required_argument, NULL, 'd' },=0A=
+			{ "security-unlock", required_argument, NULL, 'U' },=0A=
+			{ "security-erase", required_argument, NULL, 'e' },=0A=
+			{ "security-erase-enhanced", required_argument, NULL, 'h' },=0A=
+			{ "security-erase-timeout", required_argument, NULL, 'i' },=0A=
+			{ NULL, 0, NULL, 0 }=0A=
+		}=0A=
+	},=0A=
 #endif /* MINIMALISTIC */=0A=
-	{"help", CAM_CMD_USAGE, CAM_ARG_NONE, NULL},=0A=
-	{"-?", CAM_CMD_USAGE, CAM_ARG_NONE, NULL},=0A=
-	{"-h", CAM_CMD_USAGE, CAM_ARG_NONE, NULL},=0A=
-	{NULL, 0, 0, NULL}=0A=
+	{"help", CAM_CMD_USAGE, CAM_ARG_NONE, NULL, NULL},=0A=
+	{"-?", CAM_CMD_USAGE, CAM_ARG_NONE, NULL, NULL},=0A=
+	{"-h", CAM_CMD_USAGE, CAM_ARG_NONE, NULL, NULL},=0A=
+	{NULL, 0, 0, NULL, NULL}=0A=
 };=0A=
 =0A=
 typedef enum {=0A=
@@ -178,7 +200,7 @@=0A=
 =0A=
 =0A=
 camcontrol_optret getoption(char *arg, cam_cmdmask *cmdnum, cam_argmask =
*argnum,=0A=
-			    const char **subopt);=0A=
+			    const char **subopt, const struct option **suboptlong);=0A=
 #ifndef MINIMALISTIC=0A=
 static int getdevlist(struct cam_device *device);=0A=
 #endif /* MINIMALISTIC */=0A=
@@ -225,6 +247,9 @@=0A=
 			    char *combinedopt, int retry_count, int timeout);=0A=
 static int atapm(struct cam_device *device, int argc, char **argv,=0A=
 			    char *combinedopt, int retry_count, int timeout);=0A=
+static int atasecurity(struct cam_device *device, int retry_count, int =
timeout,=0A=
+			int argc, char **argv, char *combinedopt, const struct option =
*combinedoptlong);=0A=
+=0A=
 #endif /* MINIMALISTIC */=0A=
 #ifndef min=0A=
 #define min(a,b) (((a)<(b))?(a):(b))=0A=
@@ -235,10 +260,11 @@=0A=
 =0A=
 camcontrol_optret=0A=
 getoption(char *arg, cam_cmdmask *cmdnum, cam_argmask *argnum,=0A=
-	  const char **subopt)=0A=
+	  const char **subopt, const struct option **suboptlong)=0A=
 {=0A=
 	struct camcontrol_opts *opts;=0A=
 	int num_matches =3D 0;=0A=
+	const struct option empty_optlong[] =3D {{NULL, 0, NULL, 0}};=0A=
 =0A=
 	for (opts =3D option_table; (opts !=3D NULL) && (opts->optname !=3D =
NULL);=0A=
 	     opts++) {=0A=
@@ -246,6 +272,10 @@=0A=
 			*cmdnum =3D opts->cmdnum;=0A=
 			*argnum =3D opts->argnum;=0A=
 			*subopt =3D opts->subopt;=0A=
+			if (NULL !=3D opts->suboptlong)=0A=
+				*suboptlong =3D opts->suboptlong;=0A=
+			else=0A=
+				*suboptlong =3D empty_optlong;=0A=
 			if (++num_matches > 1)=0A=
 				return(CC_OR_AMBIGUOUS);=0A=
 		}=0A=
@@ -1264,33 +1294,48 @@=0A=
 }=0A=
 =0A=
 static int=0A=
-ataidentify(struct cam_device *device, int retry_count, int timeout)=0A=
+ata_cam_send(struct cam_device *device, union ccb *ccb)=0A=
 {=0A=
-	union ccb *ccb;=0A=
-	struct ata_params *ident_buf;=0A=
-	struct ccb_getdev cgd;=0A=
-	u_int i, error =3D 0;=0A=
-	int16_t *ptr;=0A=
+	if (arglist & CAM_ARG_VERBOSE)=0A=
+		warnx("sending ATA %s with timeout of %u msecs", =
ata_op_string(&(ccb->ataio.cmd)),=0A=
+		    ccb->ataio.ccb_h.timeout);=0A=
+=0A=
+	/* Disable freezing the device queue */=0A=
+	ccb->ccb_h.flags |=3D CAM_DEV_QFRZDIS;=0A=
+=0A=
+	if (arglist & CAM_ARG_ERR_RECOVER)=0A=
+		ccb->ccb_h.flags |=3D CAM_PASS_ERR_RECOVER;=0A=
+=0A=
+	if (cam_send_ccb(device, ccb) < 0) {=0A=
+		warn("error sending ATA %s", ata_op_string(&(ccb->ataio.cmd)));=0A=
+=0A=
+		if (arglist & CAM_ARG_VERBOSE)=0A=
+			cam_error_print(device, ccb, CAM_ESF_ALL, CAM_EPF_ALL, stderr);=0A=
 =0A=
-	if (get_cgd(device, &cgd) !=3D 0) {=0A=
-		warnx("couldn't get CGD");=0A=
 		return(1);=0A=
 	}=0A=
-	ccb =3D cam_getccb(device);=0A=
 =0A=
-	if (ccb =3D=3D NULL) {=0A=
-		warnx("couldn't allocate CCB");=0A=
+	if ((ccb->ccb_h.status & CAM_STATUS_MASK) !=3D CAM_REQ_CMP) {=0A=
+		warnx("ATA %s failed", ata_op_string(&(ccb->ataio.cmd)));=0A=
+		if (arglist & CAM_ARG_VERBOSE)=0A=
+			cam_error_print(device, ccb, CAM_ESF_ALL, CAM_EPF_ALL, stderr);=0A=
+=0A=
 		return(1);=0A=
 	}=0A=
 =0A=
-	/* cam_getccb cleans up the header, caller has to zero the payload */=0A=
-	bzero(&(&ccb->ccb_h)[1],=0A=
-	      sizeof(struct ccb_ataio) - sizeof(struct ccb_hdr));=0A=
+	return(0);=0A=
+}=0A=
 =0A=
-	ptr =3D (uint16_t *)malloc(sizeof(struct ata_params));=0A=
+static int=0A=
+ata_do_identify(struct cam_device *device, int retry_count, int timeout,=0A=
+	union ccb *ccb, struct ccb_getdev *cgd, struct ata_params** ident_bufp)=0A=
+{=0A=
+	struct ata_params *ident_buf;=0A=
+	u_int i, error;=0A=
+	int16_t *ptr;=0A=
 =0A=
+	ptr =3D (uint16_t *)malloc(sizeof(struct ata_params));=0A=
 	if (ptr =3D=3D NULL) {=0A=
-		cam_freeccb(ccb);=0A=
 		warnx("can't malloc memory for identify\n");=0A=
 		return(1);=0A=
 	}=0A=
@@ -1304,48 +1349,23 @@=0A=
 		      /*data_ptr*/(u_int8_t *)ptr,=0A=
 		      /*dxfer_len*/sizeof(struct ata_params),=0A=
 		      timeout ? timeout : 30 * 1000);=0A=
-	if (cgd.protocol =3D=3D PROTO_ATA)=0A=
-		ata_28bit_cmd(&ccb->ataio, ATA_ATA_IDENTIFY, 0, 0, 0);=0A=
-	else=0A=
-		ata_28bit_cmd(&ccb->ataio, ATA_ATAPI_IDENTIFY, 0, 0, 0);=0A=
-=0A=
-	/* Disable freezing the device queue */=0A=
-	ccb->ccb_h.flags |=3D CAM_DEV_QFRZDIS;=0A=
-=0A=
-	if (arglist & CAM_ARG_ERR_RECOVER)=0A=
-		ccb->ccb_h.flags |=3D CAM_PASS_ERR_RECOVER;=0A=
 =0A=
-	if (cam_send_ccb(device, ccb) < 0) {=0A=
-		perror("error sending ATA identify");=0A=
-=0A=
-		if (arglist & CAM_ARG_VERBOSE) {=0A=
-			cam_error_print(device, ccb, CAM_ESF_ALL,=0A=
-					CAM_EPF_ALL, stderr);=0A=
-		}=0A=
+	/*=0A=
+	 * We check protocol =3D=3D PROTO_ATAPI using ATA as default to enhance=0A=
+	 * compatibility with other controllers which may support passthrough=0A=
+	 */=0A=
+	ata_28bit_cmd(&ccb->ataio, (cgd->protocol =3D=3D PROTO_ATAPI) ?=0A=
+	    ATA_ATAPI_IDENTIFY : ATA_ATA_IDENTIFY, 0, 0, 0);=0A=
 =0A=
+	error =3D ata_cam_send(device, ccb);=0A=
+	if (0 !=3D error) {=0A=
 		free(ptr);=0A=
-		cam_freeccb(ccb);=0A=
 		return(1);=0A=
 	}=0A=
 =0A=
-	if ((ccb->ccb_h.status & CAM_STATUS_MASK) !=3D CAM_REQ_CMP) {=0A=
-		error =3D 1;=0A=
-=0A=
-		if (arglist & CAM_ARG_VERBOSE) {=0A=
-			cam_error_print(device, ccb, CAM_ESF_ALL,=0A=
-					CAM_EPF_ALL, stderr);=0A=
-		}=0A=
-	}=0A=
-=0A=
-	cam_freeccb(ccb);=0A=
-=0A=
-	if (error !=3D 0) {=0A=
-		free(ptr);=0A=
-		return(error);=0A=
-	}=0A=
-=0A=
 	for (i =3D 0; i < sizeof(struct ata_params) / 2; i++)=0A=
 		ptr[i] =3D le16toh(ptr[i]);=0A=
+=0A=
 	if (arglist & CAM_ARG_VERBOSE) {=0A=
 		fprintf(stdout, "%s%d: Raw identify data:\n",=0A=
 		    device->device_name, device->dev_unit_num);=0A=
@@ -1377,18 +1397,559 @@=0A=
 	ata_bpack(ident_buf->media_serial, ident_buf->media_serial,=0A=
 	    sizeof(ident_buf->media_serial));=0A=
 =0A=
-	fprintf(stdout, "%s%d: ", device->device_name,=0A=
-		device->dev_unit_num);=0A=
+	*ident_bufp =3D ident_buf;=0A=
+=0A=
+	return 0;=0A=
+}=0A=
+=0A=
+=0A=
+static int=0A=
+ataidentify(struct cam_device *device, int retry_count, int timeout)=0A=
+{=0A=
+	union ccb *ccb;=0A=
+	struct ata_params *ident_buf;=0A=
+	struct ccb_getdev cgd;=0A=
+	u_int error =3D 0;=0A=
+=0A=
+	if (get_cgd(device, &cgd) !=3D 0) {=0A=
+		warnx("couldn't get CGD");=0A=
+		return(1);=0A=
+	}=0A=
+=0A=
+	ccb =3D cam_getccb(device);=0A=
+	if (ccb =3D=3D NULL) {=0A=
+		warnx("couldn't allocate CCB");=0A=
+		return(1);=0A=
+	}=0A=
+=0A=
+	/* cam_getccb cleans up the header, caller has to zero the payload */=0A=
+	bzero(&(&ccb->ccb_h)[1], sizeof(struct ccb_ataio) - sizeof(struct =
ccb_hdr));=0A=
+=0A=
+	error =3D ata_do_identify(device, retry_count, timeout, ccb, &cgd, =
&ident_buf);=0A=
+	if (0 !=3D error) {=0A=
+		cam_freeccb(ccb);=0A=
+		return(1);=0A=
+	}=0A=
+=0A=
+	fprintf(stdout, "%s%d: ", device->device_name, device->dev_unit_num);=0A=
 	ata_print_ident(ident_buf);=0A=
 	camxferrate(device);=0A=
 	atacapprint(ident_buf);=0A=
 =0A=
 	free(ident_buf);=0A=
+	cam_freeccb(ccb);=0A=
 =0A=
 	return(0);=0A=
 }=0A=
 #endif /* MINIMALISTIC */=0A=
 =0A=
+=0A=
+#ifndef MINIMALISTIC=0A=
+enum {=0A=
+	ATA_SECURITY_ACTION_PRINT,=0A=
+	ATA_SECURITY_ACTION_FREEZE,=0A=
+	ATA_SECURITY_ACTION_UNLOCK,=0A=
+	ATA_SECURITY_ACTION_DISABLE,=0A=
+	ATA_SECURITY_ACTION_ERASE,=0A=
+	ATA_SECURITY_ACTION_ERASE_ENHANCED,=0A=
+	ATA_SECURITY_ACTION_SET_PASSWORD=0A=
+} atasecurity_action;=0A=
+=0A=
+static void=0A=
+atasecurity_print_time(u_int16_t tw)=0A=
+{=0A=
+	if (tw =3D=3D 0)=0A=
+		printf("unspecified");=0A=
+	else if (tw >=3D 255)=0A=
+		printf("> 508 min");=0A=
+	else=0A=
+		printf("%i min", 2 * tw);=0A=
+}=0A=
+=0A=
+static u_int32_t=0A=
+atasecurity_erase_timeout_msecs(u_int16_t timeout)=0A=
+{=0A=
+	if (0 =3D=3D timeout)=0A=
+		return 2 * 3600 * 1000; /* default: two hours */=0A=
+	else if (255 <=3D timeout)=0A=
+		return (508 + 60) * 60 * 1000; /* spec says > 508 minutes */=0A=
+=0A=
+	return ((2 * timeout) + 5) * 60 * 1000; /* add a 5min margin */=0A=
+}=0A=
+=0A=
+static void=0A=
+atasecurity_notify(union ccb *ccb, struct ata_security_password *pwd)=0A=
+{=0A=
+	fprintf(stdout, "Issuing %s", ata_op_string(&(ccb->ataio.cmd)));=0A=
+=0A=
+	if (NULL !=3D pwd) {=0A=
+		fprintf(stdout, " password=3D'%s', user=3D'%s'",=0A=
+		    pwd->password,=0A=
+		    (pwd->ctrl & ATA_SECURITY_PASSWORD_MASTER) ? "master" : "user");=0A=
+=0A=
+		if(ATA_SECURITY_SET_PASSWORD =3D=3D ccb->ataio.cmd.command)=0A=
+			fprintf(stdout, ", mode=3D'%s'",=0A=
+			    (pwd->ctrl & ATA_SECURITY_LEVEL_MAXIMUM) ?  "maximum" : "high");=0A=
+	}=0A=
+=0A=
+	fprintf(stdout, "\n");=0A=
+}=0A=
+=0A=
+static int=0A=
+atasecurity_freeze(struct cam_device *device, union ccb *ccb, int =
retry_count,=0A=
+	u_int32_t timeout, int quiet)=0A=
+{=0A=
+	cam_fill_ataio(&ccb->ataio,=0A=
+		    retry_count,=0A=
+		    NULL,=0A=
+		    /*flags*/CAM_DIR_NONE,=0A=
+		    MSG_SIMPLE_Q_TAG,=0A=
+		    /*data_ptr*/NULL,=0A=
+		    /*dxfer_len*/0,=0A=
+		    timeout);=0A=
+=0A=
+	ata_28bit_cmd(&ccb->ataio, ATA_SECURITY_FREEZE_LOCK, 0, 0, 0);=0A=
+=0A=
+	if (0 =3D=3D quiet)=0A=
+		atasecurity_notify(ccb, NULL);=0A=
+=0A=
+	return ata_cam_send(device, ccb);=0A=
+}=0A=
+=0A=
+static int=0A=
+atasecurity_unlock(struct cam_device *device, union ccb *ccb, int =
retry_count,=0A=
+	u_int32_t timeout, struct ata_security_password *pwd, int quiet)=0A=
+{=0A=
+	cam_fill_ataio(&ccb->ataio,=0A=
+		    retry_count,=0A=
+		    NULL,=0A=
+		    /*flags*/CAM_DIR_OUT,=0A=
+		    MSG_SIMPLE_Q_TAG,=0A=
+		    /*data_ptr*/(u_int8_t *)pwd,=0A=
+		    /*dxfer_len*/sizeof(struct ata_security_password),=0A=
+		    timeout);=0A=
+=0A=
+	ata_28bit_cmd(&ccb->ataio, ATA_SECURITY_UNLOCK, 0, 0, 0);=0A=
+=0A=
+	if (0 =3D=3D quiet)=0A=
+		atasecurity_notify(ccb, pwd);=0A=
+=0A=
+	return ata_cam_send(device, ccb);=0A=
+}=0A=
+=0A=
+static int=0A=
+atasecurity_disable(struct cam_device *device, union ccb *ccb, int =
retry_count,=0A=
+	u_int32_t timeout, struct ata_security_password *pwd, int quiet)=0A=
+{=0A=
+	cam_fill_ataio(&ccb->ataio,=0A=
+		    retry_count,=0A=
+		    NULL,=0A=
+		    /*flags*/CAM_DIR_OUT,=0A=
+		    MSG_SIMPLE_Q_TAG,=0A=
+		    /*data_ptr*/(u_int8_t *)pwd,=0A=
+		    /*dxfer_len*/sizeof(struct ata_security_password),=0A=
+		    timeout);=0A=
+=0A=
+	ata_28bit_cmd(&ccb->ataio, ATA_SECURITY_DISABLE_PASSWORD, 0, 0, 0);=0A=
+=0A=
+	if (0 =3D=3D quiet)=0A=
+		atasecurity_notify(ccb, pwd);=0A=
+=0A=
+	return ata_cam_send(device, ccb);=0A=
+}=0A=
+=0A=
+static int=0A=
+atasecurity_erase(struct cam_device *device, union ccb *ccb, int =
retry_count,=0A=
+	u_int32_t timeout, u_int32_t erase_timeout, struct =
ata_security_password *pwd,=0A=
+	int confirm, int quiet, struct ata_params* ident_buf)=0A=
+{=0A=
+	int error =3D 0, response =3D 0;=0A=
+	if (0 =3D=3D quiet) {=0A=
+        fprintf(stdout, "\nYou are about to ERASE ALL DATA from the "=0A=
+            "following device:\n");=0A=
+		fprintf(stdout, "%s%d: ", device->device_name, device->dev_unit_num);=0A=
+		ata_print_ident(ident_buf);=0A=
+	}=0A=
+=0A=
+	if (0 =3D=3D confirm) {=0A=
+		do {=0A=
+			char str[50];=0A=
+			fprintf(stdout, "Are you SURE you want to do this? (yes/no) ");=0A=
+=0A=
+			if (NULL !=3D fgets(str, sizeof(str), stdin)) {=0A=
+				if (0 =3D=3D strncasecmp(str, "yes", 3))=0A=
+					response =3D 1;=0A=
+				else if (0 =3D=3D strncasecmp(str, "no", 2))=0A=
+					response =3D -1;=0A=
+				else=0A=
+					fprintf(stdout, "Please answer \"yes\" or \"no\"\n");=0A=
+			}=0A=
+		} while (0 =3D=3D response);=0A=
+=0A=
+		if (-1 =3D=3D response)=0A=
+			return(1);=0A=
+	}=0A=
+=0A=
+	cam_fill_ataio(&ccb->ataio,=0A=
+		    retry_count,=0A=
+		    NULL,=0A=
+		    /*flags*/CAM_DIR_NONE,=0A=
+		    MSG_SIMPLE_Q_TAG,=0A=
+		    /*data_ptr*/NULL,=0A=
+		    /*dxfer_len*/0,=0A=
+		    timeout);=0A=
+=0A=
+	ata_28bit_cmd(&ccb->ataio, ATA_SECURITY_ERASE_PREPARE, 0, 0, 0);=0A=
+=0A=
+	error =3D ata_cam_send(device, ccb);=0A=
+	if (0 !=3D error)=0A=
+		return error;=0A=
+=0A=
+	/* cam_getccb cleans up the header, caller has to zero the payload */=0A=
+	bzero(&(&ccb->ccb_h)[1], sizeof(struct ccb_ataio) - sizeof(struct =
ccb_hdr));=0A=
+=0A=
+	cam_fill_ataio(&ccb->ataio,=0A=
+		    retry_count,=0A=
+		    NULL,=0A=
+		    /*flags*/CAM_DIR_OUT,=0A=
+		    MSG_SIMPLE_Q_TAG,=0A=
+		    /*data_ptr*/(u_int8_t *)pwd,=0A=
+		    /*dxfer_len*/sizeof(struct ata_security_password),=0A=
+		    erase_timeout);=0A=
+=0A=
+	ata_28bit_cmd(&ccb->ataio, ATA_SECURITY_ERASE_UNIT, 0, 0, 0);=0A=
+=0A=
+	if (0 =3D=3D quiet)=0A=
+		atasecurity_notify(ccb, pwd);=0A=
+=0A=
+	error =3D ata_cam_send(device, ccb);=0A=
+=0A=
+	if (0 =3D=3D error && 0 =3D=3D quiet)=0A=
+		fprintf(stdout, "\nErase Complete\n");=0A=
+=0A=
+	return error;=0A=
+}=0A=
+=0A=
+static int=0A=
+atasecurity_set_password(struct cam_device *device, union ccb *ccb, int =
retry_count,=0A=
+	u_int32_t timeout, struct ata_security_password *pwd, int quiet)=0A=
+{=0A=
+	cam_fill_ataio(&ccb->ataio,=0A=
+		    retry_count,=0A=
+		    NULL,=0A=
+		    /*flags*/CAM_DIR_OUT,=0A=
+		    MSG_SIMPLE_Q_TAG,=0A=
+		    /*data_ptr*/(u_int8_t *)pwd,=0A=
+		    /*dxfer_len*/sizeof(struct ata_security_password),=0A=
+		    timeout);=0A=
+=0A=
+	ata_28bit_cmd(&ccb->ataio, ATA_SECURITY_SET_PASSWORD, 0, 0, 0);=0A=
+=0A=
+	if (0 =3D=3D quiet)=0A=
+		atasecurity_notify(ccb, pwd);=0A=
+=0A=
+	return ata_cam_send(device, ccb);=0A=
+}=0A=
+=0A=
+static void=0A=
+atasecurity_print(struct ata_params *parm)=0A=
+{=0A=
+=0A=
+	printf("\nSecurity Option           Value\n");=0A=
+	if (arglist & CAM_ARG_VERBOSE) {=0A=
+		printf("status                    %04x\n", parm->security_status);=0A=
+	}=0A=
+	printf("supported                 %s\n",=0A=
+		parm->security_status & ATA_SECURITY_SUPPORTED ? "yes" : "no");=0A=
+	if (!(parm->security_status & ATA_SECURITY_SUPPORTED))=0A=
+		return;=0A=
+	printf("enabled                   %s\n",=0A=
+		parm->security_status & ATA_SECURITY_ENABLED ? "yes" : "no");=0A=
+	printf("drive locked              %s\n",=0A=
+		parm->security_status & ATA_SECURITY_LOCKED ? "yes" : "no");=0A=
+	printf("security config frozen    %s\n",=0A=
+		parm->security_status & ATA_SECURITY_FROZEN ? "yes" : "no");=0A=
+	printf("count expired             %s\n",=0A=
+		parm->security_status & ATA_SECURITY_COUNT_EXP ? "yes" : "no");=0A=
+	printf("security level            %s\n",=0A=
+		parm->security_status & ATA_SECURITY_LEVEL ? "maximum" : "high");=0A=
+	printf("enhanced erase supported  %s\n",=0A=
+		parm->security_status & ATA_SECURITY_ENH_SUPP ? "yes" : "no");=0A=
+	printf("erase time                ");=0A=
+	atasecurity_print_time(parm->erase_time);=0A=
+	printf("\n");=0A=
+	printf("enhanced erase time       ");=0A=
+	atasecurity_print_time(parm->enhanced_erase_time);=0A=
+	printf("\n");=0A=
+	printf("master password rev       %04x%s\n",=0A=
+			parm->master_passwd_revision,=0A=
+			parm->master_passwd_revision =3D=3D 0x0000 ||=0A=
+			parm->master_passwd_revision =3D=3D 0xFFFF ?=0A=
+			" (unsupported)" : "");=0A=
+}=0A=
+=0A=
+static int=0A=
+atasecurity(struct cam_device *device, int retry_count, int timeout,=0A=
+	int argc, char **argv, char *combinedopt, const struct option =
*combinedoptlong)=0A=
+{=0A=
+	union ccb *ccb;=0A=
+	struct ata_params *ident_buf;=0A=
+	u_int error =3D 0, confirm =3D 0, quiet =3D 0;=0A=
+	int c =3D -1;=0A=
+	int action =3D ATA_SECURITY_ACTION_PRINT;=0A=
+	int actions =3D 0, setpwd =3D 0, enabled =3D 0, erase_timeout =3D 0;=0A=
+	struct ata_security_password pwd;=0A=
+	struct ccb_getdev cgd;=0A=
+=0A=
+	memset(&pwd, 0, sizeof(pwd));=0A=
+	pwd.ctrl |=3D ATA_SECURITY_PASSWORD_MASTER; /* user is master by =
default as its safer that way */=0A=
+=0A=
+	while ((c =3D getopt_long(argc, argv, combinedopt, combinedoptlong, =
NULL)) !=3D -1) {=0A=
+        switch(c){=0A=
+        case 'f':=0A=
+			action =3D ATA_SECURITY_ACTION_FREEZE;=0A=
+			actions++;=0A=
+			break;=0A=
+=0A=
+		case 'r':=0A=
+			if (0 =3D=3D strcasecmp(optarg, "user")) {=0A=
+				pwd.ctrl ^=3D ATA_SECURITY_PASSWORD_MASTER;=0A=
+				pwd.ctrl |=3D ATA_SECURITY_PASSWORD_USER;=0A=
+			} else if (0 !=3D strcasecmp(optarg, "master")) {=0A=
+				warnx("--security-user argument '%s' is unknown, must be 'user' or =
'master'", optarg);=0A=
+				return(1);=0A=
+			}=0A=
+			break;=0A=
+=0A=
+		case 'l':=0A=
+			if (0 =3D=3D strcasecmp(optarg, "high"))=0A=
+				pwd.ctrl |=3D ATA_SECURITY_LEVEL_HIGH;=0A=
+			else if (0 =3D=3D strcasecmp(optarg, "maximum"))=0A=
+				pwd.ctrl |=3D ATA_SECURITY_LEVEL_MAXIMUM;=0A=
+			else {=0A=
+				warnx("--security-level argument '%s' is unknown, must be 'high' or =
'maximum'", optarg);=0A=
+				return(1);=0A=
+			}=0A=
+			break;=0A=
+=0A=
+		case 'U':=0A=
+			if (sizeof(pwd.password) < strlen(optarg)) {=0A=
+				warnx("--security-unlock password is too long");=0A=
+				return(1);=0A=
+			}=0A=
+			strlcpy(pwd.password, optarg, sizeof(pwd.password));=0A=
+			action =3D ATA_SECURITY_ACTION_UNLOCK;=0A=
+			actions++;=0A=
+			break;=0A=
+=0A=
+		case 'd':=0A=
+			if (sizeof(pwd.password) < strlen(optarg)) {=0A=
+				warnx("--security-disable password is too long");=0A=
+				return(1);=0A=
+			}=0A=
+			strlcpy(pwd.password, optarg, sizeof(pwd.password));=0A=
+			action =3D ATA_SECURITY_ACTION_DISABLE;=0A=
+			actions++;=0A=
+			break;=0A=
+=0A=
+		case 'e':=0A=
+			if (sizeof(pwd.password) < strlen(optarg)) {=0A=
+				warnx("--security-erase password is too long");=0A=
+				return(1);=0A=
+			}=0A=
+			strlcpy(pwd.password, optarg, sizeof(pwd.password));=0A=
+			action =3D ATA_SECURITY_ACTION_ERASE;=0A=
+			actions++;=0A=
+			break;=0A=
+=0A=
+		case 'h':=0A=
+			if (sizeof(pwd.password) < strlen(optarg)) {=0A=
+				warnx("--security-erase-enhanced password is too long");=0A=
+				return(1);=0A=
+			}=0A=
+			strlcpy(pwd.password, optarg, sizeof(pwd.password));=0A=
+			pwd.ctrl |=3D ATA_SECURITY_ERASE_ENHANCED;=0A=
+			action =3D ATA_SECURITY_ACTION_ERASE_ENHANCED;=0A=
+			actions++;=0A=
+			break;=0A=
+=0A=
+		case 's':=0A=
+			if (sizeof(pwd.password) < strlen(optarg)) {=0A=
+				warnx("--security-set-password password is too long");=0A=
+				return(1);=0A=
+			}=0A=
+			strlcpy(pwd.password, optarg, sizeof(pwd.password));=0A=
+			setpwd =3D 1;=0A=
+			action =3D ATA_SECURITY_ACTION_SET_PASSWORD;=0A=
+			/* don't increment action as this can be combined with other actions =
*/=0A=
+			break;=0A=
+=0A=
+		case 'y':=0A=
+			confirm++;=0A=
+			break;=0A=
+=0A=
+		case 'q':=0A=
+			quiet++;=0A=
+			break;=0A=
+=0A=
+		case 'i':=0A=
+			erase_timeout =3D atoi(optarg) * 1000;=0A=
+			break;=0A=
+		}=0A=
+	}=0A=
+	argc -=3D optind;=0A=
+	argv +=3D optind;=0A=
+=0A=
+	if (1 < actions) {=0A=
+		warnx("too many security actions specified");=0A=
+		return(1);=0A=
+	}=0A=
+=0A=
+	if (get_cgd(device, &cgd) !=3D 0) {=0A=
+		warnx("couldn't get CGD");=0A=
+		return(1);=0A=
+	}=0A=
+=0A=
+	ccb =3D cam_getccb(device);=0A=
+	if (ccb =3D=3D NULL) {=0A=
+		warnx("couldn't allocate CCB");=0A=
+		return(1);=0A=
+	}=0A=
+=0A=
+	/* cam_getccb cleans up the header, caller has to zero the payload */=0A=
+	bzero(&(&ccb->ccb_h)[1], sizeof(struct ccb_ataio) - sizeof(struct =
ccb_hdr));=0A=
+=0A=
+	error =3D ata_do_identify(device, retry_count, timeout, ccb, &cgd, =
&ident_buf);=0A=
+	if (0 !=3D error) {=0A=
+		cam_freeccb(ccb);=0A=
+		return(1);=0A=
+	}=0A=
+=0A=
+	if (0 =3D=3D quiet) {=0A=
+		fprintf(stdout, "%s%d: ", device->device_name, device->dev_unit_num);=0A=
+		ata_print_ident(ident_buf);=0A=
+		camxferrate(device);=0A=
+	}=0A=
+=0A=
+	if (action =3D=3D ATA_SECURITY_ACTION_PRINT) {=0A=
+		if (0 !=3D quiet) {=0A=
+			fprintf(stdout, "%s%d: ", device->device_name, device->dev_unit_num);=0A=
+			ata_print_ident(ident_buf);=0A=
+			camxferrate(device);=0A=
+		}=0A=
+		atasecurity_print(ident_buf);=0A=
+		free(ident_buf);=0A=
+		cam_freeccb(ccb);=0A=
+		return 0;=0A=
+	}=0A=
+=0A=
+	if (!(ident_buf->support.command1 & ATA_SUPPORT_SECURITY)) {=0A=
+		warnx("Security not supported");=0A=
+		free(ident_buf);=0A=
+		cam_freeccb(ccb);=0A=
+		return(1);=0A=
+	}=0A=
+=0A=
+	/* default timeout 15 seconds the same as linux hdparm */=0A=
+	timeout =3D timeout ? timeout : 15 * 1000;=0A=
+=0A=
+	/* cam_getccb cleans up the header, caller has to zero the payload */=0A=
+	bzero(&(&ccb->ccb_h)[1], sizeof(struct ccb_ataio) - sizeof(struct =
ccb_hdr));=0A=
+=0A=
+	enabled =3D ident_buf->security_status & ATA_SECURITY_ENABLED;=0A=
+=0A=
+	/* first set the password if requested */=0A=
+	if (setpwd) {=0A=
+		/* prepare pwd details */=0A=
+		if (pwd.ctrl & ATA_SECURITY_PASSWORD_MASTER) {=0A=
+			pwd.revision =3D ident_buf->master_passwd_revision;=0A=
+			if (0 !=3D pwd.revision && 0xffff !=3D pwd.revision && 0 =3D=3D =
--pwd.revision)=0A=
+				pwd.revision =3D 0xfffe;=0A=
+		}=0A=
+		error =3D atasecurity_set_password(device, ccb, retry_count, timeout, =
&pwd, quiet);=0A=
+		if (0 !=3D error) {=0A=
+			cam_freeccb(ccb);=0A=
+			free(ident_buf);=0A=
+			return error;=0A=
+		}=0A=
+		enabled =3D 1;=0A=
+	}=0A=
+=0A=
+	switch(action) {=0A=
+	case ATA_SECURITY_ACTION_FREEZE:=0A=
+		error =3D atasecurity_freeze(device, ccb, retry_count, timeout, =
quiet);=0A=
+		break;=0A=
+=0A=
+	case ATA_SECURITY_ACTION_UNLOCK:=0A=
+		if (enabled) {=0A=
+			if (ident_buf->security_status & ATA_SECURITY_LOCKED)=0A=
+				error =3D atasecurity_unlock(device, ccb, retry_count, timeout, =
&pwd, quiet);=0A=
+			else {=0A=
+				warnx("Can't unlock, drive is not locked");=0A=
+				error =3D 1;=0A=
+			}=0A=
+		} else {=0A=
+			warnx("Can't unlock, security is disabled");=0A=
+			error =3D 1;=0A=
+		}=0A=
+		break;=0A=
+=0A=
+	case ATA_SECURITY_ACTION_DISABLE:=0A=
+		if (enabled) {=0A=
+			/* First unlock the drive if its locked */=0A=
+			if (ident_buf->security_status & ATA_SECURITY_LOCKED)=0A=
+				error =3D atasecurity_unlock(device, ccb, retry_count, timeout, =
&pwd, quiet);=0A=
+=0A=
+			if (0 =3D=3D error) {=0A=
+				/* cam_getccb cleans up the header, caller has to zero the payload =
*/=0A=
+				bzero(&(&ccb->ccb_h)[1], sizeof(struct ccb_ataio) - sizeof(struct =
ccb_hdr));=0A=
+				error =3D atasecurity_disable(device, ccb, retry_count, timeout, =
&pwd, quiet);=0A=
+			}=0A=
+		} else {=0A=
+			warnx("Can't disable security, security is already disabled");=0A=
+			error =3D 1;=0A=
+		}=0A=
+		break;=0A=
+=0A=
+	case ATA_SECURITY_ACTION_ERASE:=0A=
+		if (enabled) {=0A=
+			if (0 =3D=3D erase_timeout)=0A=
+				erase_timeout =3D =
atasecurity_erase_timeout_msecs(ident_buf->erase_time);=0A=
+=0A=
+			error =3D atasecurity_erase(device, ccb, retry_count, timeout,=0A=
+			    erase_timeout, &pwd, confirm, quiet, ident_buf);=0A=
+		} else {=0A=
+			warnx("Can't secure erase, security is disabled");=0A=
+			error =3D 1;=0A=
+		}=0A=
+		break;=0A=
+=0A=
+	case ATA_SECURITY_ACTION_ERASE_ENHANCED:=0A=
+		if (enabled) {=0A=
+			if (ident_buf->security_status & ATA_SECURITY_ENH_SUPP) {=0A=
+				if (0 =3D=3D erase_timeout)=0A=
+					erase_timeout =3D =
atasecurity_erase_timeout_msecs(ident_buf->enhanced_erase_time);=0A=
+=0A=
+				error =3D atasecurity_erase(device, ccb, retry_count, timeout,=0A=
+				    erase_timeout, &pwd, confirm, quiet, ident_buf);=0A=
+			} else {=0A=
+				warnx("Enhanced erase is not supported");=0A=
+				error =3D 1;=0A=
+			}=0A=
+		} else {=0A=
+			warnx("Can't secure erase (enhanced), security is disabled");=0A=
+			error =3D 1;=0A=
+		}=0A=
+		break;=0A=
+	}=0A=
+=0A=
+	cam_freeccb(ccb);=0A=
+	free(ident_buf);=0A=
+=0A=
+	return error;=0A=
+}=0A=
+#endif /* MINIMALISTIC */=0A=
+=0A=
 /*=0A=
  * Parse out a bus, or a bus, target and lun in the following=0A=
  * format:=0A=
@@ -4393,6 +4954,17 @@=0A=
 "        camcontrol idle       [dev_id][generic args][-t time]\n"=0A=
 "        camcontrol standby    [dev_id][generic args][-t time]\n"=0A=
 "        camcontrol sleep      [dev_id][generic args]\n"=0A=
+"        camcontrol security   [dev_id][generic args]\n"=0A=
+"                              [--security-freeze] [--security-quiet]\n"=0A=
+"                              [--security-confirm]\n"=0A=
+"                              [--security-user <user|master>]\n"=0A=
+"                              [--security-level <high|maximum>]\n"=0A=
+"                              [--security-set-password <pwd>]\n"=0A=
+"                              [--security-unlock <pwd>]\n"=0A=
+"                              [--security-disable <pwd>]\n"=0A=
+"                              [--security-erase <pwd>]\n"=0A=
+"                              [--security-erase-enhanced <pwd>]\n"=0A=
+"                              [--security-erase-timeout <timeout>]\n"=0A=
 #endif /* MINIMALISTIC */=0A=
 "        camcontrol help\n");=0A=
 	if (!verbose)=0A=
@@ -4423,6 +4995,7 @@=0A=
 "idle        send the ATA IDLE command to the named device\n"=0A=
 "standby     send the ATA STANDBY command to the named device\n"=0A=
 "sleep       send the ATA SLEEP command to the named device\n"=0A=
+"security    report / send ATA security commands to the named device\n"=0A=
 "help        this message\n"=0A=
 "Device Identifiers:\n"=0A=
 "bus:target        specify the bus and target, lun defaults to 0\n"=0A=
@@ -4492,7 +5065,17 @@=0A=
 "-w                don't send immediate format command\n"=0A=
 "-y                don't ask any questions\n"=0A=
 "idle/standby arguments:\n"=0A=
-"-t <arg>          number of seconds before respective state.\n");=0A=
+"-t <arg>          number of seconds before respective state.\n"=0A=
+"security arguments:\n"=0A=
+"--security-freeze                   freeze the security configuration =
of the specified device\n"=0A=
+"--security-user user|master         specifies which user to set: user =
or master\n"=0A=
+"--security-level high|maximum       specifies which security level to =
set: high or maximum\n"=0A=
+"--security-set-password <pwd>       password the device (enable =
security) using the given password for the selected user\n"=0A=
+"--security-unlock <pwd>             unlock the device using the given =
password for the selected user\n"=0A=
+"--security-disable <pwd>            disable security using the given =
password for the selected user\n"=0A=
+"--security-erase <pwd>              erase the device using the given =
password for the selected user\n"=0A=
+"--security-erase-enhanced <pwd>     enhanced erase the device using =
the given password for the selected user\n"=0A=
+"--security-erase-timeout <timeout>  overrides the timeout (in seconds) =
used for security erase operation\n");=0A=
 #endif /* MINIMALISTIC */=0A=
 }=0A=
 =0A=
@@ -4511,6 +5094,7 @@=0A=
 	char combinedopt[256];=0A=
 	int error =3D 0, optstart =3D 2;=0A=
 	int devopen =3D 1;=0A=
+	const struct option *longopts =3D NULL;=0A=
 #ifndef MINIMALISTIC=0A=
 	int bus, target, lun;=0A=
 #endif /* MINIMALISTIC */=0A=
@@ -4526,7 +5110,7 @@=0A=
 	/*=0A=
 	 * Get the base option.=0A=
 	 */=0A=
-	optreturn =3D getoption(argv[1], &cmdlist, &arglist, &subopt);=0A=
+	optreturn =3D getoption(argv[1], &cmdlist, &arglist, &subopt, =
&longopts);=0A=
 =0A=
 	if (optreturn =3D=3D CC_OR_AMBIGUOUS) {=0A=
 		warnx("ambiguous option %s", argv[1]);=0A=
@@ -4642,7 +5226,7 @@=0A=
 	 * options, and ignoring options that possibly belong to=0A=
 	 * subfunctions.=0A=
 	 */=0A=
-	while ((c =3D getopt(argc, argv, combinedopt))!=3D -1){=0A=
+	while ((c =3D getopt_long(argc, argv, combinedopt, longopts, NULL)) =
!=3D -1) {=0A=
 		switch(c) {=0A=
 			case 'C':=0A=
 				retry_count =3D strtol(optarg, NULL, 0);=0A=
@@ -4787,6 +5371,9 @@=0A=
 						 combinedopt, retry_count,=0A=
 						 timeout);=0A=
 			break;=0A=
+		case CAM_CMD_SECURITY:=0A=
+			error =3D atasecurity(cam_dev, retry_count, timeout, argc, argv, =
combinedopt, longopts);=0A=
+			break;=0A=
 #endif /* MINIMALISTIC */=0A=
 		case CAM_CMD_USAGE:=0A=
 			usage(1);=0A=
--- sbin/camcontrol/camcontrol.8.orig	2011-08-05 11:32:53.897577205 +0000=0A=
+++ sbin/camcontrol/camcontrol.8	2011-08-06 01:12:14.602310347 +0000=0A=
@@ -183,6 +183,21 @@=0A=
 .Op device id=0A=
 .Op generic args=0A=
 .Nm=0A=
+.Ic security=0A=
+.Op device id=0A=
+.Op generic args=0A=
+.Op Fl -security-quiet=0A=
+.Op Fl -security-confirm=0A=
+.Op Fl -security-freeze=0A=
+.Op Fl -security-user Ar user|master=0A=
+.Op Fl -security-level Ar high|maximum=0A=
+.Op Fl -security-set-password Ar pwd=0A=
+.Op Fl -security-unlock Ar pwd=0A=
+.Op Fl -security-disable Ar pwd=0A=
+.Op Fl -security-erase Ar pwd=0A=
+.Op Fl -security-enhanced-erase Ar pwd=0A=
+.Op Fl -security-erase-timeout Ar timeout=0A=
+.Nm=0A=
 .Ic help=0A=
 .Sh DESCRIPTION=0A=
 The=0A=
@@ -853,6 +868,122 @@=0A=
 .It Ic sleep=0A=
 Put ATA device into SLEEP state. Note that the only way get device out =
of=0A=
 this state may be reset.=0A=
+.It Ic security=0A=
+Update or report security settings, using an ATA identify command =
(0xec).=0A=
+By default,=0A=
+.Nm=0A=
+will print out the security support and associated settings of the =
device.=0A=
+The=0A=
+.Ic security=0A=
+command takes several arguments:=0A=
+.Bl -tag -width 0n=0A=
+.It Fl -security-quiet=0A=
+.Pp=0A=
+Be quiet, do not print any status messages.=0A=
+This option will not disable the questions, however.=0A=
+To disable questions, use the=0A=
+.Fl -security-confirm=0A=
+argument, below.=0A=
+.It Fl -security-confirm=0A=
+.Pp=0A=
+Confirm yes to dangerous options such as=0A=
+.Fl -security-erase=0A=
+without prompting for confirmation=0A=
+.It Fl -security-freeze=0A=
+.Pp=0A=
+Freeze the security configuration of the specified device.=0A=
+.Pp=0A=
+After command completion any other commands that update the device lock =
mode=0A=
+shall be command aborted. Frozen mode is disabled by power-off or =
hardware reset. =0A=
+.It Fl -security-user Ar user|master=0A=
+.Pp=0A=
+Specifies which user to set / use for the running action command, valid =
values=0A=
+are user or master.=0A=
+.Pp=0A=
+This option must be used in conjunction with one of the security action =
commands.=0A=
+.Pp=0A=
+Defaults to=0A=
+.Em master=0A=
+.It Fl -security-level Ar high|maximum=0A=
+.Pp=0A=
+Specifies which security level to set when issuing a=0A=
+.Fl -security-set-password=0A=
+command. The security level determines device behavior when the master =
password=0A=
+is used to unlock the device. When the security level is set to high =
the device=0A=
+requires the unlock command and the master password to unlock.=0A=
+When the security level is set to maximum the device requires a secure =
erase=0A=
+with the master password to unlock.=0A=
+.Pp=0A=
+This option must be used in conjunction with one of the security action =
commands.=0A=
+.Pp=0A=
+Defaults to=0A=
+.Em high=0A=
+.It Fl -security-set-password Ar pwd=0A=
+.Pp=0A=
+Password the device (enable security) using the given password for the =
selected=0A=
+user.=0A=
+.Pp=0A=
+A master password may be set in a addition to the user password. The =
purpose of=0A=
+the master password is to allow an administrator to establish a =
password that=0A=
+is kept secret from the user, and which may be used to unlock the =
device if the=0A=
+user password is lost.=0A=
+.Pp=0A=
+.Em Note:=0A=
+Setting the master password does not enable device security.=0A=
+.Pp=0A=
+If the master password is set and the drive supports a Master Revision =
Code=0A=
+feature the Master Password Revision Code will be decremented.=0A=
+.It Fl -security-unlock Ar pwd=0A=
+.Pp=0A=
+Unlock the device using the given password for the selected user =
according to=0A=
+the devices configured security level.=0A=
+.It Fl -security-disable Ar pwd=0A=
+.Pp=0A=
+Disable device security using the given password for the selected user =
according=0A=
+to the devices configured security level.=0A=
+.It Fl -security-erase Ar pwd=0A=
+.Pp=0A=
+Erase the device using the given password for the selected user.=0A=
+.Pp=0A=
+.Em WARNING! WARNING! WARNING!=0A=
+.Pp=0A=
+Issuing a secure erase will=0A=
+.Em ERASE ALL=0A=
+user data on the device and may take several hours to complete.=0A=
+.Pp=0A=
+When this command is used against an SSD drive all its cells will be =
marked as=0A=
+empty, restoring it to factory default write performance. For SSD's =
this action=0A=
+usually takes just a few seconds.=0A=
+.It Fl -security-erase-enhanced Ar pwd=0A=
+.Pp=0A=
+Enhanced erase the device using the given password for the selected =
user.=0A=
+.Pp=0A=
+.Em WARNING! WARNING! WARNING!=0A=
+.Pp=0A=
+Issuing an enhanced secure erase will =0A=
+.Em ERASE ALL=0A=
+user data on the device and may take several hours to complete.=0A=
+.Pp=0A=
+An enhanced erase writes predetermined data patterns to all user data =
areas,=0A=
+all previously written user data shall be overwritten, including =
sectors that=0A=
+are no longer in use due to reallocation.=0A=
+.It Fl -security-erase-timeout Ar timeout=0A=
+.Pp=0A=
+Overrides the default timeout, specified in seconds, used for both=0A=
+.Fl -security-erase=0A=
+and=0A=
+.Fl -security-erase-enhanced=0A=
+this is useful if your system has problems processing long timeouts =
correctly.=0A=
+.Pp=0A=
+Usually the timeout is calculated from the information stored on the =
drive if=0A=
+present, otherwise it defaults to 2 hours.=0A=
+.Pp=0A=
+.El=0A=
+If the password specified for any action commands doesn't match the =
configured=0A=
+password for the specified user the command will fail.=0A=
+.Pp=0A=
+The password in all cases is limited to 32 characters, longer passwords =
will=0A=
+fail.=0A=
 .It Ic help=0A=
 Print out verbose usage information.=0A=
 .El=0A=
@@ -971,6 +1102,33 @@=0A=
 Negotiate a sync rate of 20MHz and an offset of 15 with da3.=0A=
 Then send a=0A=
 Test Unit Ready command to make the settings take effect.=0A=
+.Pp=0A=
+.Bd -literal -offset indent=0A=
+camcontrol security ada0=0A=
+.Ed=0A=
+.Pp=0A=
+Report security support and settings for ada0=0A=
+.Pp=0A=
+.Bd -literal -offset indent=0A=
+camcontrol security ada0 --security-user user --security-set-password =
MyPass =0A=
+.Ed=0A=
+.Pp=0A=
+Enable security on device ada0 with the password MyPass=0A=
+.Pp=0A=
+.Bd -literal -offset indent=0A=
+camcontrol security ada0 --security-user user --security-erase MyPass=0A=
+.Ed=0A=
+.Pp=0A=
+Secure erase ada0=0A=
+.Pp=0A=
+.Em WARNING! WARNING! WARNING=0A=
+.Pp=0A=
+This will=0A=
+.Em ERASE ALL=0A=
+data from the device, so backup your data before using!=0A=
+.Pp=0A=
+This command can be used used against an SSD drive to restoring it to=0A=
+factory default write performance=0A=
 .Sh SEE ALSO=0A=
 .Xr cam 3 ,=0A=
 .Xr cam_cdbparse 3 ,=0A=
--- sys/sys/ata.h.orig	2010-12-21 17:09:25.000000000 +0000=0A=
+++ sys/sys/ata.h	2011-08-05 11:14:15.313867265 +0000=0A=
@@ -188,10 +188,10 @@=0A=
 	} __packed support, enabled;=0A=
 =0A=
 /*088*/ u_int16_t       udmamodes;              /* UltraDMA modes */=0A=
-/*089*/ u_int16_t       erase_time;=0A=
-/*090*/ u_int16_t       enhanced_erase_time;=0A=
+/*089*/ u_int16_t       erase_time;             /* time req'd in 2min =
units */=0A=
+/*090*/ u_int16_t       enhanced_erase_time;    /* time req'd in 2min =
units */=0A=
 /*091*/ u_int16_t       apm_value;=0A=
-/*092*/ u_int16_t       master_passwd_revision;=0A=
+/*092*/ u_int16_t       master_passwd_revision; /* password revision =
code */=0A=
 /*093*/ u_int16_t       hwres;=0A=
 #define ATA_CABLE_ID                    0x2000=0A=
 =0A=
@@ -228,6 +228,14 @@=0A=
 	u_int16_t       reserved121[6];=0A=
 /*127*/ u_int16_t       removable_status;=0A=
 /*128*/ u_int16_t       security_status;=0A=
+#define ATA_SECURITY_LEVEL		0x0100	/* 0: high, 1: maximum */=0A=
+#define ATA_SECURITY_ENH_SUPP		0x0020	/* enhanced erase supported */=0A=
+#define ATA_SECURITY_COUNT_EXP		0x0010	/* count expired */=0A=
+#define ATA_SECURITY_FROZEN		0x0008	/* security config is frozen */=0A=
+#define ATA_SECURITY_LOCKED		0x0004	/* drive is locked */=0A=
+#define ATA_SECURITY_ENABLED		0x0002	/* ATA Security is enabled */=0A=
+#define ATA_SECURITY_SUPPORTED		0x0001	/* ATA Security is supported */=0A=
+=0A=
 	u_int16_t       reserved129[31];=0A=
 /*160*/ u_int16_t       cfa_powermode1;=0A=
 	u_int16_t       reserved161;=0A=
@@ -355,7 +363,12 @@=0A=
 #define         ATA_SF_DIS_RELIRQ       0xdd    /* disable release =
interrupt */=0A=
 #define         ATA_SF_ENAB_SRVIRQ      0x5e    /* enable service =
interrupt */=0A=
 #define         ATA_SF_DIS_SRVIRQ       0xde    /* disable service =
interrupt */=0A=
-#define ATA_SECURITY_FREEE_LOCK         0xf5    /* freeze security =
config */=0A=
+#define ATA_SECURITY_SET_PASSWORD       0xf1    /* set drive password */=0A=
+#define ATA_SECURITY_UNLOCK             0xf2    /* unlock drive using =
passwd */=0A=
+#define ATA_SECURITY_ERASE_PREPARE      0xf3    /* prepare to erase =
drive */=0A=
+#define ATA_SECURITY_ERASE_UNIT         0xf4    /* erase all blocks on =
drive */=0A=
+#define ATA_SECURITY_FREEZE_LOCK        0xf5    /* freeze security =
config */=0A=
+#define ATA_SECURITY_DISABLE_PASSWORD   0xf6    /* disable drive =
password */=0A=
 #define ATA_READ_NATIVE_MAX_ADDRESS     0xf8    /* read native max =
address */=0A=
 #define ATA_SET_MAX_ADDRESS             0xf9    /* set max address */=0A=
 =0A=
@@ -501,6 +514,20 @@=0A=
     int                 error;=0A=
 };=0A=
 =0A=
+struct ata_security_password {=0A=
+	u_int16_t		ctrl;=0A=
+#define ATA_SECURITY_PASSWORD_USER	0x0000=0A=
+#define ATA_SECURITY_PASSWORD_MASTER	0x0001=0A=
+#define ATA_SECURITY_ERASE_NORMAL	0x0000=0A=
+#define ATA_SECURITY_ERASE_ENHANCED	0x0002=0A=
+#define ATA_SECURITY_LEVEL_HIGH		0x0000=0A=
+#define ATA_SECURITY_LEVEL_MAXIMUM	0x0100=0A=
+=0A=
+	u_int8_t		password[32];=0A=
+	u_int16_t		revision;=0A=
+	u_int16_t		reserved[238];=0A=
+};=0A=
+=0A=
 /* pr device ATA ioctl calls */=0A=
 #define IOCATAREQUEST           _IOWR('a', 100, struct ata_ioc_request)=0A=
 #define IOCATAGPARM             _IOR('a', 101, struct ata_params)=0A=

------=_NextPart_000_13E8_01CC90C7.E9C4EB30--


From: Jeremy Chadwick <jdc@koitsu.org>
To: bug-followup@FreeBSD.org, killing@multiplay.co.uk
Cc: mav@freebsd.org, avg@freebsd.org
Subject: Re: bin/159833: camcontrol(8): [patch] add ATA security options to
 camcontrol including secure erase
Date: Thu, 8 Nov 2012 10:54:44 -0800

 This incredibly useful PR/patch has been neglected for over a year.
 
 SSDs are mainstream now and being used commonly by FreeBSD users.  This
 greatly increases the want for secure erase.  Sure, we have TRIM for
 UFS, and HEAD/CURRENT has TRIM for ZFS, and that's good at keeping
 the FTL optimal on an SSD, but let's be reasonable here.
 
 Alexander and Andriy, I know you're both busy, but can either of you
 review this?
 
 If you need/want an updated patch (specifically for 8.3 and 9.x), I can
 provide updated ones (will take me some time of course).
 
 Thanks guys.
 
 -- 
 | Jeremy Chadwick                                   jdc@koitsu.org |
 | UNIX Systems Administrator                http://jdc.koitsu.org/ |
 | Mountain View, CA, US                                            |
 | Making life hard for others since 1977.             PGP 4BD6C0CB |
 

From: "Steven Hartland" <killing@multiplay.co.uk>
To: "Jeremy Chadwick" <jdc@koitsu.org>,
	<bug-followup@FreeBSD.org>
Cc: <mav@freebsd.org>,
	<avg@freebsd.org>
Subject: Re: bin/159833: camcontrol(8): [patch] add ATA security options to camcontrol including secure erase
Date: Fri, 9 Nov 2012 00:19:48 -0000

 ----- Original Message ----- 
 From: "Jeremy Chadwick" <jdc@koitsu.org>
 
 
 > This incredibly useful PR/patch has been neglected for over a year.
 > 
 > SSDs are mainstream now and being used commonly by FreeBSD users.  This
 > greatly increases the want for secure erase.  Sure, we have TRIM for
 > UFS, and HEAD/CURRENT has TRIM for ZFS, and that's good at keeping
 > the FTL optimal on an SSD, but let's be reasonable here.
 > 
 > Alexander and Andriy, I know you're both busy, but can either of you
 > review this?
 > 
 > If you need/want an updated patch (specifically for 8.3 and 9.x), I can
 > provide updated ones (will take me some time of course).
 > 
 > Thanks guys.
 
 I have an updated version of this patch which is waiting on finishing
 off some cam related patches that I'm working with mav@ on.
 
 So for now lets hold off on this one and I'll get the updated across once
 the dependencies are all place :)
 
     Regards
     Steve
 
 ================================================
 This e.mail is private and confidential between Multiplay (UK) Ltd. and the person or entity to whom it is addressed. In the event of misdirection, the recipient is prohibited from using, copying, printing or otherwise disseminating it or any information contained in it. 
 
 In the event of misdirection, illegible or incomplete transmission please telephone +44 845 868 1337
 or return the E.mail to postmaster@multiplay.co.uk.
 
Responsible-Changed-From-To: freebsd-bugs->smh 
Responsible-Changed-By: smh 
Responsible-Changed-When: Tue Dec 11 14:12:28 UTC 2012 
Responsible-Changed-Why:  
I'll take it. 

http://www.freebsd.org/cgi/query-pr.cgi?pr=159833 
State-Changed-From-To: open->patched 
State-Changed-By: smh 
State-Changed-When: Thu Apr 25 16:24:11 UTC 2013 
State-Changed-Why:  
Awaiting MFC of r249115 

http://www.freebsd.org/cgi/query-pr.cgi?pr=159833 
State-Changed-From-To: patched->closed 
State-Changed-By: smh 
State-Changed-When: Fri Jun 7 14:58:33 UTC 2013 
State-Changed-Why:  
Committed. Thanks! 

http://www.freebsd.org/cgi/query-pr.cgi?pr=159833 
>Unformatted:
