Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 5 Sep 2013 10:14:17 -0500 (CDT)
From:      Bryan Venteicher <bryanv@daemoninthecloset.org>
To:        freebsd-scsi@freebsd.org
Subject:   camcontrol SCSI sanitize
Message-ID:  <1292769639.3059.1378394057334.JavaMail.root@daemoninthecloset.org>
In-Reply-To: <1895443574.3014.1378393339664.JavaMail.root@daemoninthecloset.org>

next in thread | previous in thread | raw e-mail | index | archive | help

[-- Attachment #1 --]
Hi,

Anybody with more SCSI experience than myself interested in
reviewing the attached patch that adds SCSI sanitize support
to camcontrol? It has been compiled tested on -current, but 
a very similar patch works on 8.x.

The time is short, but I wouldn't mind sneaking this into 10
before the code freeze so I have one less local patch to deal
with later.

http://people.freebsd.org/~bryanv/patches/camcontrol_sanitize.diff

Thanks.
[-- Attachment #2 --]
commit 94f7c8d11a320e36f59edb77fce593b6c892aa3c
Author: Bryan Venteicher <bryanv@daemoninthecloset.org>
Date:   Thu Sep 5 00:14:08 2013 -0500

    Add camcontrol sanitize support

diff --git a/sbin/camcontrol/camcontrol.8 b/sbin/camcontrol/camcontrol.8
index df0b3e7..0a2be64 100644
--- a/sbin/camcontrol/camcontrol.8
+++ b/sbin/camcontrol/camcontrol.8
@@ -207,6 +207,19 @@
 .Op Fl w
 .Op Fl y
 .Nm
+.Ic sanitize
+.Op device id
+.Op generic args
+.Aq Fl a Ar overwrite|block|crypto|exitfailure
+.Op Fl c count
+.Op Fl P file
+.Op Fl I
+.Op Fl U
+.Op Fl q
+.Op Fl r
+.Op Fl w
+.Op Fl y
+.Nm
 .Ic idle
 .Op device id
 .Op generic args
@@ -1088,6 +1101,117 @@ The user
 will not be asked about the timeout if a timeout is specified on the
 command line.
 .El
+.It Ic sanitize
+Issue the
+.Tn SCSI
+SANITIZE command to the named device.
+.Pp
+.Em WARNING! WARNING! WARNING!
+.Pp
+ALL existing data in the cache and on the disk will be
+destroyed or made inaccessible.
+Recovery of the data is not possible.
+Use extreme caution when issuing this command.
+.Pp
+The
+.Sq sanitize
+subcommand takes several arguments that modify its default behavior.
+The
+.Fl q
+and
+.Fl y
+arguments can be useful for scripts.
+.Bl -tag -width 6n
+.It Fl q
+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 a Ar action
+Specify the sanitize operation to perform. Only one
+.Fl a
+action may be specified.
+.Bl -tag -width 16n
+.It overwrite
+Perform an overwrite operation by writing a user supplied
+data pattern to the device one or more times.
+The pattern is given by the
+.Fl P
+argument.
+The count is given by the
+.Fl c
+argument.
+.It block
+Perform a block erase operation.
+The physical blocks are set to a vendor defined value, typically all zeros.
+.It crypto
+Perform a cryptographic erase operation.
+The encryption keys are changed.
+.It exitfailure
+Clears a failed sanitize operation.
+A failed sanitize can only be cleared if it was run in
+the unrestricted completion mode with the
+.Fl U
+argument.
+.El
+.It Fl c Ar count
+The number of passes to perform when performing an
+.Sq overwrite
+operation.
+Valid values are between 1 and 31.  The default is 1.
+.It Fl P Ar file
+Path to the file containing the pattern to use when
+performing an
+.Sq overwrite
+operation.
+The pattern is repeated as needed to fill each logical block.
+.It Fl I
+When performing an
+.Sq overwrite
+operation, this causes the pattern to be inverted between consecutive passes.
+.It Fl U
+Perform the sanitize in the unrestricted completion mode.
+A failed sanitize operation can later be cleared with the
+.Sq exitfailure
+action.
+.It Fl r
+Run in
+.Dq report only
+mode.
+This will report status on a sanitize that is already running on the drive.
+.It Fl w
+Issue a non-immediate sanitize command.
+By default,
+.Nm
+issues the SANITIZE command with the immediate bit set.
+This tells the
+device to immediately return the sanitize command, before
+the sanitize has actually completed.
+Then,
+.Nm
+gathers
+.Tn SCSI
+sense information from the device every second to determine how far along
+in the sanitize process it is.
+If the
+.Fl w
+argument is specified,
+.Nm
+will issue a non-immediate sanitize command, and will be unable to print any
+information to let the user know what percentage of the disk has been
+sanitized.
+.It Fl y
+Do not ask any questions.
+By default,
+.Nm
+will ask the user if he/she really wants to sanitize the disk in question,
+and also if the default sanitize command timeout is acceptable.
+The user
+will not be asked about the timeout if a timeout is specified on the
+command line.
+.El
 .It Ic idle
 Put ATA device into IDLE state. Optional parameter
 .Pq Fl t
diff --git a/sbin/camcontrol/camcontrol.c b/sbin/camcontrol/camcontrol.c
index 76b3939..6531d54 100644
--- a/sbin/camcontrol/camcontrol.c
+++ b/sbin/camcontrol/camcontrol.c
@@ -32,6 +32,7 @@ __FBSDID("$FreeBSD$");
 #include <sys/ioctl.h>
 #include <sys/stdint.h>
 #include <sys/types.h>
+#include <sys/stat.h>
 #include <sys/endian.h>
 #include <sys/sbuf.h>
 
@@ -93,7 +94,8 @@ typedef enum {
 	CAM_CMD_SMP_MANINFO	= 0x0000001b,
 	CAM_CMD_DOWNLOAD_FW	= 0x0000001c,
 	CAM_CMD_SECURITY	= 0x0000001d,
-	CAM_CMD_HPA		= 0x0000001e
+	CAM_CMD_HPA		= 0x0000001e,
+	CAM_CMD_SANITIZE	= 0x0000001f,
 } cam_cmdmask;
 
 typedef enum {
@@ -215,6 +217,7 @@ static struct camcontrol_opts option_table[] = {
 	{"fwdownload", CAM_CMD_DOWNLOAD_FW, CAM_ARG_NONE, "f:ys"},
 	{"security", CAM_CMD_SECURITY, CAM_ARG_NONE, "d:e:fh:k:l:qs:T:U:y"},
 	{"hpa", CAM_CMD_HPA, CAM_ARG_NONE, "Pflp:qs:U:y"},
+	{"sanitize", CAM_CMD_SANITIZE, CAM_ARG_NONE, "a:c:IP:qrUwy"},
 #endif /* MINIMALISTIC */
 	{"help", CAM_CMD_USAGE, CAM_ARG_NONE, NULL},
 	{"-?", CAM_CMD_USAGE, CAM_ARG_NONE, NULL},
@@ -301,6 +304,8 @@ static int ratecontrol(struct cam_device *device, int retry_count,
 		       int timeout, int argc, char **argv, char *combinedopt);
 static int scsiformat(struct cam_device *device, int argc, char **argv,
 		      char *combinedopt, int retry_count, int timeout);
+static int scsisanitize(struct cam_device *device, int argc, char **argv,
+			char *combinedopt, int retry_count, int timeout);
 static int scsireportluns(struct cam_device *device, int argc, char **argv,
 			  char *combinedopt, int retry_count, int timeout);
 static int scsireadcapacity(struct cam_device *device, int argc, char **argv,
@@ -5537,6 +5542,401 @@ scsiformat_bailout:
 }
 
 static int
+scsisanitize(struct cam_device *device, int argc, char **argv,
+	     char *combinedopt, int retry_count, int timeout)
+{
+	union ccb *ccb;
+	u_int8_t action = 0;
+	int c;
+	int ycount = 0, quiet = 0;
+	int error = 0, retval = 0;
+	int use_timeout = 10800 * 1000;
+	int immediate = 1;
+	int invert = 0;
+	int passes = 0;
+	int ause = 0;
+	int fd = -1;
+	const char *pattern = NULL;
+	u_int8_t *data_ptr = NULL;
+	u_int32_t dxfer_len = 0;
+	u_int8_t byte2 = 0;
+	int num_warnings = 0;
+	int reportonly = 0;
+
+	ccb = cam_getccb(device);
+
+	if (ccb == NULL) {
+		warnx("scsisanitize: error allocating ccb");
+		return(1);
+	}
+
+	bzero(&(&ccb->ccb_h)[1],
+	      sizeof(struct ccb_scsiio) - sizeof(struct ccb_hdr));
+
+	while ((c = getopt(argc, argv, combinedopt)) != -1) {
+		switch(c) {
+		case 'a':
+			if (strcasecmp(optarg, "overwrite") == 0)
+				action = SSZ_SERVICE_ACTION_OVERWRITE;
+			else if (strcasecmp(optarg, "block") == 0)
+				action = SSZ_SERVICE_ACTION_BLOCK_ERASE;
+			else if (strcasecmp(optarg, "crypto") == 0)
+				action = SSZ_SERVICE_ACTION_CRYPTO_ERASE;
+			else if (strcasecmp(optarg, "exitfailure") == 0)
+				action = SSZ_SERVICE_ACTION_EXIT_MODE_FAILURE;
+			else {
+				warnx("invalid service action mode \"%s\"",
+				      optarg);
+				error = 1;
+				goto scsisanitize_bailout;
+			}
+			break;
+		case 'c':
+			passes = strtol(optarg, NULL, 0);
+			if (passes < 1 || passes > 31) {
+				warnx("invalid passes value %d", passes);
+				error = 1;
+				goto scsisanitize_bailout;
+			}
+			break;
+		case 'I':
+			invert = 1;
+			break;
+		case 'P':
+			pattern = optarg;
+			break;
+		case 'q':
+			quiet++;
+			break;
+		case 'r':
+			reportonly = 1;
+			break;
+		case 'U':
+			ause = 1;
+			break;
+		case 'w':
+			immediate = 0;
+			break;
+		case 'y':
+			ycount++;
+			break;
+		}
+	}
+
+	if (reportonly)
+		goto doreport;
+
+	if (action == 0) {
+		warnx("an action is required");
+		error = 1;
+		goto scsisanitize_bailout;
+	} else if (action == SSZ_SERVICE_ACTION_OVERWRITE) {
+		struct scsi_sanitize_parameter_list *pl;
+		struct stat sb;
+		ssize_t sz, amt;
+
+		if (pattern == NULL) {
+			warnx("overwrite action requires -P argument");
+			error = 1;
+			goto scsisanitize_bailout;
+		}
+		fd = open(pattern, O_RDONLY);
+		if (fd < 0) {
+			warn("cannot open pattern file %s", pattern);
+			error = 1;
+			goto scsisanitize_bailout;
+		}
+		if (fstat(fd, &sb) < 0) {
+			warn("cannot stat pattern file %s", pattern);
+			error = 1;
+			goto scsisanitize_bailout;
+		}
+		sz = sb.st_size;
+		if (sz > SSZPL_MAX_PATTERN_LENGTH) {
+			warnx("pattern file size exceeds maximum value %d",
+			      SSZPL_MAX_PATTERN_LENGTH);
+			error = 1;
+			goto scsisanitize_bailout;
+		}
+		dxfer_len = sizeof(*pl) + sz;
+		data_ptr = calloc(1, dxfer_len);
+		if (data_ptr == NULL) {
+			warnx("cannot allocate parameter list buffer");
+			error = 1;
+			goto scsisanitize_bailout;
+		}
+
+		amt = read(fd, data_ptr + sizeof(*pl), sz);
+		if (amt < 0) {
+			warn("cannot read pattern file");
+			error = 1;
+			goto scsisanitize_bailout;
+		} else if (amt != sz) {
+			warnx("short pattern file read");
+			error = 1;
+			goto scsisanitize_bailout;
+		}
+
+		pl = (struct scsi_sanitize_parameter_list *)data_ptr;
+		if (passes == 0)
+			pl->byte1 = 1;
+		else
+			pl->byte1 = passes;
+		if (invert != 0)
+			pl->byte1 |= SSZPL_INVERT;
+		scsi_ulto2b(sz, pl->length);
+	} else {
+		const char *arg;
+
+		if (passes != 0)
+			arg = "-c";
+		else if (pattern != NULL)
+			arg = "-P";
+		else if (invert != 0)
+			arg = "-I";
+		else
+			arg = NULL;
+		if (arg != NULL) {
+			warnx("%s argument only valid with overwrite action", arg);
+			error = 1;
+			goto scsisanitize_bailout;
+		}
+	}
+
+	if (quiet == 0) {
+		fprintf(stdout, "You are about to REMOVE ALL DATA from the "
+			"following device:\n");
+
+		error = scsidoinquiry(device, argc, argv, combinedopt,
+				      retry_count, timeout);
+
+		if (error != 0) {
+			warnx("scsisanitize: error sending inquiry");
+			goto scsisanitize_bailout;
+		}
+	}
+
+	if (ycount == 0) {
+		if (!get_confirmation()) {
+			error = 1;
+			goto scsisanitize_bailout;
+		}
+	}
+
+	if (timeout != 0)
+		use_timeout = timeout;
+
+	if (quiet == 0) {
+		fprintf(stdout, "Current sanitize timeout is %d seconds\n",
+			use_timeout / 1000);
+	}
+
+	/*
+	 * If the user hasn't disabled questions and didn't specify a
+	 * timeout on the command line, ask them if they want the current
+	 * timeout.
+	 */
+	if ((ycount == 0)
+	 && (timeout == 0)) {
+		char str[1024];
+		int new_timeout = 0;
+
+		fprintf(stdout, "Enter new timeout in seconds or press\n"
+			"return to keep the current timeout [%d] ",
+			use_timeout / 1000);
+
+		if (fgets(str, sizeof(str), stdin) != NULL) {
+			if (str[0] != '\0')
+				new_timeout = atoi(str);
+		}
+
+		if (new_timeout != 0) {
+			use_timeout = new_timeout * 1000;
+			fprintf(stdout, "Using new timeout value %d\n",
+				use_timeout / 1000);
+		}
+	}
+
+	byte2 = action;
+	if (ause != 0)
+		byte2 |= SSZ_UNRESTRICTED_EXIT;
+	if (immediate != 0)
+		byte2 |= SSZ_IMMED;
+
+	scsi_sanitize(&ccb->csio,
+		      /* retries */ retry_count,
+		      /* cbfcnp */ NULL,
+		      /* tag_action */ MSG_SIMPLE_Q_TAG,
+		      /* byte2 */ byte2,
+		      /* control */ 0,
+		      /* data_ptr */ data_ptr,
+		      /* dxfer_len */ dxfer_len,
+		      /* sense_len */ SSD_FULL_SIZE,
+		      /* timeout */ use_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 (((retval = cam_send_ccb(device, ccb)) < 0)
+	 || ((immediate == 0)
+	   && ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP))) {
+		const char errstr[] = "error sending sanitize command";
+
+		if (retval < 0)
+			warn(errstr);
+		else
+			warnx(errstr);
+
+		if (arglist & CAM_ARG_VERBOSE) {
+			cam_error_print(device, ccb, CAM_ESF_ALL,
+					CAM_EPF_ALL, stderr);
+		}
+		error = 1;
+		goto scsisanitize_bailout;
+	}
+
+	/*
+	 * If we ran in non-immediate mode, we already checked for errors
+	 * above and printed out any necessary information.  If we're in
+	 * immediate mode, we need to loop through and get status
+	 * information periodically.
+	 */
+	if (immediate == 0) {
+		if (quiet == 0) {
+			fprintf(stdout, "Sanitize Complete\n");
+		}
+		goto scsisanitize_bailout;
+	}
+
+doreport:
+	do {
+		cam_status status;
+
+		bzero(&(&ccb->ccb_h)[1],
+		      sizeof(struct ccb_scsiio) - sizeof(struct ccb_hdr));
+
+		/*
+		 * There's really no need to do error recovery or
+		 * retries here, since we're just going to sit in a
+		 * loop and wait for the device to finish sanitizing.
+		 */
+		scsi_test_unit_ready(&ccb->csio,
+				     /* retries */ 0,
+				     /* cbfcnp */ NULL,
+				     /* tag_action */ MSG_SIMPLE_Q_TAG,
+				     /* sense_len */ SSD_FULL_SIZE,
+				     /* timeout */ 5000);
+
+		/* Disable freezing the device queue */
+		ccb->ccb_h.flags |= CAM_DEV_QFRZDIS;
+
+		retval = cam_send_ccb(device, ccb);
+
+		/*
+		 * If we get an error from the ioctl, bail out.  SCSI
+		 * errors are expected.
+		 */
+		if (retval < 0) {
+			warn("error sending CAMIOCOMMAND ioctl");
+			if (arglist & CAM_ARG_VERBOSE) {
+				cam_error_print(device, ccb, CAM_ESF_ALL,
+						CAM_EPF_ALL, stderr);
+			}
+			error = 1;
+			goto scsisanitize_bailout;
+		}
+
+		status = ccb->ccb_h.status & CAM_STATUS_MASK;
+
+		if ((status != CAM_REQ_CMP)
+		 && (status == CAM_SCSI_STATUS_ERROR)
+		 && ((ccb->ccb_h.status & CAM_AUTOSNS_VALID) != 0)) {
+			struct scsi_sense_data *sense;
+			int error_code, sense_key, asc, ascq;
+
+			sense = &ccb->csio.sense_data;
+			scsi_extract_sense_len(sense, ccb->csio.sense_len -
+			    ccb->csio.sense_resid, &error_code, &sense_key,
+			    &asc, &ascq, /*show_errors*/ 1);
+
+			/*
+			 * According to the SCSI-3 spec, a drive that is in the
+			 * middle of a sanitize should return NOT READY with an
+			 * ASC of "logical unit not ready, sanitize in
+			 * progress". The sense key specific bytes will then
+			 * be a progress indicator.
+			 */
+			if ((sense_key == SSD_KEY_NOT_READY)
+			 && (asc == 0x04) && (ascq == 0x1b)) {
+				uint8_t sks[3];
+
+				if ((scsi_get_sks(sense, ccb->csio.sense_len -
+				     ccb->csio.sense_resid, sks) == 0)
+				 && (quiet == 0)) {
+					int val;
+					u_int64_t percentage;
+
+					val = scsi_2btoul(&sks[1]);
+					percentage = 10000 * val;
+
+					fprintf(stdout,
+						"\rSanitizing:  %ju.%02u %% "
+						"(%d/%d) done",
+						(uintmax_t)(percentage /
+						(0x10000 * 100)),
+						(unsigned)((percentage /
+						0x10000) % 100),
+						val, 0x10000);
+					fflush(stdout);
+				} else if ((quiet == 0)
+					&& (++num_warnings <= 1)) {
+					warnx("Unexpected SCSI Sense Key "
+					      "Specific value returned "
+					      "during sanitize:");
+					scsi_sense_print(device, &ccb->csio,
+							 stderr);
+					warnx("Unable to print status "
+					      "information, but sanitze will "
+					      "proceed.");
+					warnx("will exit when sanitize is "
+					      "complete");
+				}
+				sleep(1);
+			} else {
+				warnx("Unexpected SCSI error during sanitize");
+				cam_error_print(device, ccb, CAM_ESF_ALL,
+						CAM_EPF_ALL, stderr);
+				error = 1;
+				goto scsisanitize_bailout;
+			}
+
+		} else if (status != CAM_REQ_CMP) {
+			warnx("Unexpected CAM status %#x", status);
+			if (arglist & CAM_ARG_VERBOSE)
+				cam_error_print(device, ccb, CAM_ESF_ALL,
+						CAM_EPF_ALL, stderr);
+			error = 1;
+			goto scsisanitize_bailout;
+		}
+	} while((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP);
+
+	if (quiet == 0)
+		fprintf(stdout, "\nSanitize Complete\n");
+
+scsisanitize_bailout:
+	if (fd >= 0)
+		close(fd);
+	if (data_ptr != NULL)
+		free(data_ptr);
+	cam_freeccb(ccb);
+
+	return(error);
+}
+
+static int
 scsireportluns(struct cam_device *device, int argc, char **argv,
 	       char *combinedopt, int retry_count, int timeout)
 {
@@ -7371,6 +7771,9 @@ usage(int printlong)
 "                              [-U <user|master>] [-y]\n"
 "        camcontrol hpa        [dev_id][generic args] [-f] [-l] [-P] [-p pwd]\n"
 "                              [-q] [-s max_sectors] [-U pwd] [-y]\n"
+"        camcontrol sanitize   [dev_id][generic args]\n"
+"                              [-a overwrite|block|crypto|exitfailure]\n"
+"                              [-c count][-P file][-I][-U][-q][-r][-w][-y]\n"
 #endif /* MINIMALISTIC */
 "        camcontrol help\n");
 	if (!printlong)
@@ -7532,6 +7935,16 @@ usage(int printlong)
 "                  device\n"
 "-U pwd            unlock the HPA configuration of the device\n"
 "-y                don't ask any questions\n"
+"sanitize arguments:\n"
+"-a action         service action mode (overwrite, block, crypto or exitfailure)\n"
+"-c count          overwrite passes performed (1 to 31)\n"
+"-P file           overwrite initialization pattern\n"
+"-I                invert overwrite pattern after each pass\n"
+"-U                set unrestricted sanitize exit (AUSE) bit\n"
+"-q                be quiet, don't print status messages\n"
+"-r                run in report only mode\n"
+"-w                don't send immediate format command\n"
+"-y                don't ask any questions\n"
 );
 #endif /* MINIMALISTIC */
 }
@@ -7860,6 +8273,10 @@ main(int argc, char **argv)
 			    arglist & CAM_ARG_VERBOSE, retry_count, timeout,
 			    get_disk_type(cam_dev));
 			break;
+		case CAM_CMD_SANITIZE:
+			error = scsisanitize(cam_dev, argc, argv,
+					     combinedopt, retry_count, timeout);
+			break;
 #endif /* MINIMALISTIC */
 		case CAM_CMD_USAGE:
 			usage(1);
diff --git a/sys/cam/scsi/scsi_da.c b/sys/cam/scsi/scsi_da.c
index 8ee47f9..913951e 100644
--- a/sys/cam/scsi/scsi_da.c
+++ b/sys/cam/scsi/scsi_da.c
@@ -3851,4 +3851,31 @@ scsi_format_unit(struct ccb_scsiio *csio, u_int32_t retries,
 		      timeout);
 }
 
+void
+scsi_sanitize(struct ccb_scsiio *csio, u_int32_t retries,
+	      void (*cbfcnp)(struct cam_periph *, union ccb *),
+	      u_int8_t tag_action, u_int8_t byte2, u_int16_t control,
+	      u_int8_t *data_ptr, u_int32_t dxfer_len, u_int8_t sense_len,
+	      u_int32_t timeout)
+{
+	struct scsi_sanitize *scsi_cmd;
+
+	scsi_cmd = (struct scsi_sanitize *)&csio->cdb_io.cdb_bytes;
+	scsi_cmd->opcode = SANITIZE;
+	scsi_cmd->byte2 = byte2;
+	scsi_cmd->control = control;
+	scsi_ulto2b(dxfer_len, scsi_cmd->length);
+
+	cam_fill_csio(csio,
+		      retries,
+		      cbfcnp,
+		      /*flags*/ (dxfer_len > 0) ? CAM_DIR_OUT : CAM_DIR_NONE,
+		      tag_action,
+		      data_ptr,
+		      dxfer_len,
+		      sense_len,
+		      sizeof(*scsi_cmd),
+		      timeout);
+}
+
 #endif /* _KERNEL */
diff --git a/sys/cam/scsi/scsi_da.h b/sys/cam/scsi/scsi_da.h
index 5799238..4fbd725 100644
--- a/sys/cam/scsi/scsi_da.h
+++ b/sys/cam/scsi/scsi_da.h
@@ -116,6 +116,31 @@ struct scsi_read_defect_data_10
 	u_int8_t control;
 };
 
+struct scsi_sanitize
+{
+	u_int8_t opcode;
+	u_int8_t byte2;
+#define SSZ_SERVICE_ACTION_OVERWRITE         0x01
+#define SSZ_SERVICE_ACTION_BLOCK_ERASE       0x02
+#define SSZ_SERVICE_ACTION_CRYPTO_ERASE      0x03
+#define SSZ_SERVICE_ACTION_EXIT_MODE_FAILURE 0x1F
+#define SSZ_UNRESTRICTED_EXIT                0x20
+#define SSZ_IMMED                            0x80
+	u_int8_t reserved[5];
+	u_int8_t length[2];
+	u_int8_t control;
+};
+
+struct scsi_sanitize_parameter_list
+{
+	u_int8_t byte1;
+#define SSZPL_INVERT 0x80
+	u_int8_t reserved;
+	u_int8_t length[2];
+	/* Variable length initialization pattern. */
+#define SSZPL_MAX_PATTERN_LENGTH 65535
+};
+
 struct scsi_read_defect_data_12
 {
 	u_int8_t opcode;
@@ -156,6 +181,7 @@ struct scsi_read_defect_data_12
 #define	WRITE_AND_VERIFY	0x2e
 #define	VERIFY			0x2f
 #define READ_DEFECT_DATA_10	0x37
+#define SANITIZE		0x48
 #define READ_DEFECT_DATA_12	0xb7
 
 struct format_defect_list_header
@@ -508,6 +534,12 @@ void scsi_format_unit(struct ccb_scsiio *csio, u_int32_t retries,
 		      u_int8_t *data_ptr, u_int32_t dxfer_len,
 		      u_int8_t sense_len, u_int32_t timeout);
 
+void scsi_sanitize(struct ccb_scsiio *csio, u_int32_t retries,
+		   void (*cbfcnp)(struct cam_periph *, union ccb *),
+		   u_int8_t tag_action, u_int8_t byte2, u_int16_t control,
+		   u_int8_t *data_ptr, u_int32_t dxfer_len, u_int8_t sense_len,
+		   u_int32_t timeout);
+
 #endif /* !_KERNEL */
 __END_DECLS
 

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