Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 8 Aug 2019 22:08:55 +0000 (UTC)
From:      Alexander Motin <mav@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-11@freebsd.org
Subject:   svn commit: r350801 - in stable/11: sbin/camcontrol sys/cam/ata sys/sys
Message-ID:  <201908082208.x78M8tZm041843@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: mav
Date: Thu Aug  8 22:08:54 2019
New Revision: 350801
URL: https://svnweb.freebsd.org/changeset/base/350801

Log:
  MFC r350149: Add Accessible Max Address Configuration support to camcontrol.
  
  AMA replaced HPA in ACS-3 specification.  It allows to limit size of the
  disk alike to HPA, but declares inaccessible data as indeterminate.  One
  of its practical use cases is to under-provision SATA SSDs for better
  reliability and performance.
  
  While there, fix HPA Security detection/reporting.
  
  Relnotes:	yes

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

Modified: stable/11/sbin/camcontrol/camcontrol.8
==============================================================================
--- stable/11/sbin/camcontrol/camcontrol.8	Thu Aug  8 22:06:40 2019	(r350800)
+++ stable/11/sbin/camcontrol/camcontrol.8	Thu Aug  8 22:08:54 2019	(r350801)
@@ -27,7 +27,7 @@
 .\"
 .\" $FreeBSD$
 .\"
-.Dd April 22, 2019
+.Dd July 19, 2019
 .Dt CAMCONTROL 8
 .Os
 .Sh NAME
@@ -292,6 +292,13 @@
 .Op Fl U Ar pwd
 .Op Fl y
 .Nm
+.Ic ama
+.Op device id
+.Op generic args
+.Op Fl f
+.Op Fl q
+.Op Fl s Ar max_sectors
+.Nm
 .Ic persist
 .Op device id
 .Op generic args
@@ -1599,6 +1606,40 @@ without prompting for confirmation
 .Pp
 The password for all HPA commands is limited to 32 characters, longer passwords
 will fail.
+.It Ic ama
+Update or report Accessible Max Address Configuration.
+By default
+.Nm
+will print out the Accessible Max Address Configuration support and associated
+settings of the device.
+The
+.Ic ama
+command takes several optional arguments:
+.Bl -tag -width 0n
+.It Fl f
+.Pp
+Freeze the Accessible Max Address Configuration of the specified device.
+.Pp
+After command completion any other commands that update the configuration
+shall be command aborted.
+Frozen mode is disabled by power-off.
+.It Fl q
+.Pp
+Be quiet, do not print any status messages.
+.It Fl s Ar max_sectors
+.Pp
+Configures the maximum user accessible sectors of the device.
+This will change the number of sectors the device reports.
+.Pp
+.Em WARNING! WARNING! WARNING!
+.Pp
+Changing the max sectors of a device using this option will make the data on
+the device beyond the specified value indeterminate.
+.Pp
+Only one successful
+.Fl s Ar max_sectors
+call can be made without a power-on reset of the device.
+.El
 .It Ic fwdownload
 Program firmware of the named
 .Tn SCSI

Modified: stable/11/sbin/camcontrol/camcontrol.c
==============================================================================
--- stable/11/sbin/camcontrol/camcontrol.c	Thu Aug  8 22:06:40 2019	(r350800)
+++ stable/11/sbin/camcontrol/camcontrol.c	Thu Aug  8 22:08:54 2019	(r350801)
@@ -107,6 +107,7 @@ typedef enum {
 	CAM_CMD_TIMESTAMP	= 0x00000028,
 	CAM_CMD_POWER_MODE	= 0x0000002a,
 	CAM_CMD_DEVTYPE		= 0x0000002b,
+	CAM_CMD_AMA	= 0x0000002c,
 } cam_cmdmask;
 
 typedef enum {
@@ -239,6 +240,7 @@ static struct camcontrol_opts option_table[] = {
 	{"fwdownload", CAM_CMD_DOWNLOAD_FW, CAM_ARG_NONE, "f:qsy"},
 	{"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"},
+	{"ama", CAM_CMD_AMA, CAM_ARG_NONE, "fqs:"},
 	{"persist", CAM_CMD_PERSIST, CAM_ARG_NONE, "ai:I:k:K:o:ps:ST:U"},
 	{"attrib", CAM_CMD_ATTRIB, CAM_ARG_NONE, "a:ce:F:p:r:s:T:w:V:"},
 	{"opcodes", CAM_CMD_OPCODES, CAM_ARG_NONE, "No:s:T"},
@@ -358,6 +360,8 @@ static int atasecurity(struct cam_device *device, int 
 		       int argc, char **argv, char *combinedopt);
 static int atahpa(struct cam_device *device, int retry_count, int timeout,
 		  int argc, char **argv, char *combinedopt);
+static int ataama(struct cam_device *device, int retry_count, int timeout,
+		  int argc, char **argv, char *combinedopt);
 static int scsiprintoneopcode(struct cam_device *device, int req_opcode,
 			      int sa_set, int req_sa, uint8_t *buf,
 			      uint32_t valid_len);
@@ -1275,8 +1279,9 @@ atahpa_print(struct ata_params *parm, u_int64_t hpasiz
 			lba, hpasize);
 
 		printf("HPA - Security                 ");
-		if (parm->support.command1 & ATA_SUPPORT_MAXSECURITY)
-			printf("yes\n");
+		if (parm->support.command2 & ATA_SUPPORT_MAXSECURITY)
+			printf("yes      %s\n", (parm->enabled.command2 &
+			    ATA_SUPPORT_MAXSECURITY) ? "yes" : "no ");
 		else
 			printf("no\n");
 	} else {
@@ -1284,6 +1289,32 @@ atahpa_print(struct ata_params *parm, u_int64_t hpasiz
 	}
 }
 
+static void
+ataama_print(struct ata_params *parm, u_int64_t nativesize, int header)
+{
+	u_int32_t lbasize = (u_int32_t)parm->lba_size_1 |
+				((u_int32_t)parm->lba_size_2 << 16);
+
+	u_int64_t lbasize48 = ((u_int64_t)parm->lba_size48_1) |
+				((u_int64_t)parm->lba_size48_2 << 16) |
+				((u_int64_t)parm->lba_size48_3 << 32) |
+				((u_int64_t)parm->lba_size48_4 << 48);
+
+	if (header) {
+		printf("\nFeature                      "
+		       "Support  Enabled   Value\n");
+	}
+
+	printf("Accessible Max Address Config  ");
+	if (parm->support2 & ATA_SUPPORT_AMAX_ADDR) {
+		u_int64_t lba = lbasize48 ? lbasize48 : lbasize;
+		printf("yes      %s     %ju/%ju\n",
+		    (nativesize > lba) ? "yes" : "no ", lba, nativesize);
+	} else {
+		printf("no\n");
+	}
+}
+
 static int
 atasata(struct ata_params *parm)
 {
@@ -2110,7 +2141,95 @@ atahpa_freeze_lock(struct cam_device *device, int retr
 	return atahpa_proc_resp(device, ccb, is48bit, NULL);
 }
 
+static int
+ata_get_native_max(struct cam_device *device, int retry_count,
+		      u_int32_t timeout, union ccb *ccb,
+		      u_int64_t *nativesize)
+{
+	int error;
 
+	error = ata_do_cmd(device,
+			   ccb,
+			   retry_count,
+			   /*flags*/CAM_DIR_NONE,
+			   /*protocol*/AP_PROTO_NON_DATA | AP_EXTEND,
+			   /*ata_flags*/AP_FLAG_CHK_COND,
+			   /*tag_action*/MSG_SIMPLE_Q_TAG,
+			   /*command*/ATA_AMAX_ADDR,
+			   /*features*/ATA_AMAX_ADDR_GET,
+			   /*lba*/0,
+			   /*sector_count*/0,
+			   /*data_ptr*/NULL,
+			   /*dxfer_len*/0,
+			   timeout ? timeout : 30 * 1000,
+			   /*force48bit*/1);
+
+	if (error)
+		return (error);
+
+	return atahpa_proc_resp(device, ccb, /*is48bit*/1, nativesize);
+}
+
+static int
+ataama_set(struct cam_device *device, int retry_count,
+	      u_int32_t timeout, union ccb *ccb, u_int64_t maxsize)
+{
+	int error;
+
+	/* lba's are zero indexed so the max lba is requested max - 1 */
+	if (maxsize)
+		maxsize--;
+
+	error = ata_do_cmd(device,
+			   ccb,
+			   retry_count,
+			   /*flags*/CAM_DIR_NONE,
+			   /*protocol*/AP_PROTO_NON_DATA | AP_EXTEND,
+			   /*ata_flags*/AP_FLAG_CHK_COND,
+			   /*tag_action*/MSG_SIMPLE_Q_TAG,
+			   /*command*/ATA_AMAX_ADDR,
+			   /*features*/ATA_AMAX_ADDR_SET,
+			   /*lba*/maxsize,
+			   /*sector_count*/0,
+			   /*data_ptr*/NULL,
+			   /*dxfer_len*/0,
+			   timeout ? timeout : 30 * 1000,
+			   /*force48bit*/1);
+
+	if (error)
+		return (error);
+
+	return atahpa_proc_resp(device, ccb, /*is48bit*/1, NULL);
+}
+
+static int
+ataama_freeze(struct cam_device *device, int retry_count,
+		   u_int32_t timeout, union ccb *ccb)
+{
+	int error;
+
+	error = ata_do_cmd(device,
+			   ccb,
+			   retry_count,
+			   /*flags*/CAM_DIR_NONE,
+			   /*protocol*/AP_PROTO_NON_DATA | AP_EXTEND,
+			   /*ata_flags*/AP_FLAG_CHK_COND,
+			   /*tag_action*/MSG_SIMPLE_Q_TAG,
+			   /*command*/ATA_AMAX_ADDR,
+			   /*features*/ATA_AMAX_ADDR_FREEZE,
+			   /*lba*/0,
+			   /*sector_count*/0,
+			   /*data_ptr*/NULL,
+			   /*dxfer_len*/0,
+			   timeout ? timeout : 30 * 1000,
+			   /*force48bit*/1);
+
+	if (error)
+		return (error);
+
+	return atahpa_proc_resp(device, ccb, /*is48bit*/1, NULL);
+}
+
 int
 ata_do_identify(struct cam_device *device, int retry_count, int timeout,
 		union ccb *ccb, struct ata_params** ident_bufp)
@@ -2241,7 +2360,7 @@ ataidentify(struct cam_device *device, int retry_count
 {
 	union ccb *ccb;
 	struct ata_params *ident_buf;
-	u_int64_t hpasize;
+	u_int64_t hpasize, nativesize;
 
 	if ((ccb = cam_getccb(device)) == NULL) {
 		warnx("couldn't allocate CCB");
@@ -2262,12 +2381,22 @@ ataidentify(struct cam_device *device, int retry_count
 	} else {
 		hpasize = 0;
 	}
+	if (ident_buf->support2 & ATA_SUPPORT_AMAX_ADDR) {
+		if (ata_get_native_max(device, retry_count, timeout, ccb,
+					&nativesize) != 0) {
+			cam_freeccb(ccb);
+			return (1);
+		}
+	} else {
+		nativesize = 0;
+	}
 
 	printf("%s%d: ", device->device_name, device->dev_unit_num);
 	ata_print_ident(ident_buf);
 	camxferrate(device);
 	atacapprint(ident_buf);
 	atahpa_print(ident_buf, hpasize, 0);
+	ataama_print(ident_buf, nativesize, 0);
 
 	free(ident_buf);
 	cam_freeccb(ccb);
@@ -2757,7 +2886,7 @@ atahpa(struct cam_device *device, int retry_count, int
 		return (1);
 	}
 
-	if (security && !(ident_buf->support.command1 & ATA_SUPPORT_MAXSECURITY)) {
+	if (security && !(ident_buf->support.command2 & ATA_SUPPORT_MAXSECURITY)) {
 		warnx("HPA Security is not supported by this device");
 		cam_freeccb(ccb);
 		free(ident_buf);
@@ -2786,7 +2915,7 @@ atahpa(struct cam_device *device, int retry_count, int
 		if (error == 0) {
 			error = atahpa_set_max(device, retry_count, timeout,
 					       ccb, is48bit, maxsize, persist);
-			if (error == 0) {
+			if (error == 0 && quiet == 0) {
 				/* redo identify to get new lba values */
 				error = ata_do_identify(device, retry_count,
 							timeout, ccb,
@@ -2799,28 +2928,28 @@ atahpa(struct cam_device *device, int retry_count, int
 	case ATA_HPA_ACTION_SET_PWD:
 		error = atahpa_password(device, retry_count, timeout,
 					ccb, is48bit, &pwd);
-		if (error == 0)
+		if (error == 0 && quiet == 0)
 			printf("HPA password has been set\n");
 		break;
 
 	case ATA_HPA_ACTION_LOCK:
 		error = atahpa_lock(device, retry_count, timeout,
 				    ccb, is48bit);
-		if (error == 0)
+		if (error == 0 && quiet == 0)
 			printf("HPA has been locked\n");
 		break;
 
 	case ATA_HPA_ACTION_UNLOCK:
 		error = atahpa_unlock(device, retry_count, timeout,
 				      ccb, is48bit, &pwd);
-		if (error == 0)
+		if (error == 0 && quiet == 0)
 			printf("HPA has been unlocked\n");
 		break;
 
 	case ATA_HPA_ACTION_FREEZE_LOCK:
 		error = atahpa_freeze_lock(device, retry_count, timeout,
 					   ccb, is48bit);
-		if (error == 0)
+		if (error == 0 && quiet == 0)
 			printf("HPA has been frozen\n");
 		break;
 
@@ -2834,7 +2963,128 @@ atahpa(struct cam_device *device, int retry_count, int
 	return (error);
 }
 
+enum {
+	ATA_AMA_ACTION_PRINT,
+	ATA_AMA_ACTION_SET_MAX,
+	ATA_AMA_ACTION_FREEZE_LOCK
+};
+
 static int
+ataama(struct cam_device *device, int retry_count, int timeout,
+       int argc, char **argv, char *combinedopt)
+{
+	union ccb *ccb;
+	struct ata_params *ident_buf;
+	struct ccb_getdev cgd;
+	int error, quiet, c, action, actions;
+	u_int64_t nativesize, maxsize;
+
+	actions = 0;
+	quiet = 0;
+	maxsize = 0;
+
+	/* default action is to print AMA information */
+	action = ATA_AMA_ACTION_PRINT;
+
+	while ((c = getopt(argc, argv, combinedopt)) != -1) {
+		switch(c){
+		case 's':
+			action = ATA_AMA_ACTION_SET_MAX;
+			maxsize = strtoumax(optarg, NULL, 0);
+			actions++;
+			break;
+
+		case 'f':
+			action = ATA_AMA_ACTION_FREEZE_LOCK;
+			actions++;
+			break;
+
+		case 'q':
+			quiet++;
+			break;
+		}
+	}
+
+	if (actions > 1) {
+		warnx("too many AMA actions specified");
+		return (1);
+	}
+
+	if (get_cgd(device, &cgd) != 0) {
+		warnx("couldn't get CGD");
+		return (1);
+	}
+
+	ccb = cam_getccb(device);
+	if (ccb == NULL) {
+		warnx("couldn't allocate CCB");
+		return (1);
+	}
+
+	error = ata_do_identify(device, retry_count, timeout, ccb, &ident_buf);
+	if (error != 0) {
+		cam_freeccb(ccb);
+		return (1);
+	}
+
+	if (quiet == 0) {
+		printf("%s%d: ", device->device_name, device->dev_unit_num);
+		ata_print_ident(ident_buf);
+		camxferrate(device);
+	}
+
+	if (action == ATA_AMA_ACTION_PRINT) {
+		error = ata_get_native_max(device, retry_count, timeout, ccb,
+					   &nativesize);
+		if (error == 0)
+			ataama_print(ident_buf, nativesize, 1);
+
+		cam_freeccb(ccb);
+		free(ident_buf);
+		return (error);
+	}
+
+	if (!(ident_buf->support2 & ATA_SUPPORT_AMAX_ADDR)) {
+		warnx("Accessible Max Address is not supported by this device");
+		cam_freeccb(ccb);
+		free(ident_buf);
+		return (1);
+	}
+
+	switch(action) {
+	case ATA_AMA_ACTION_SET_MAX:
+		error = ata_get_native_max(device, retry_count, timeout, ccb,
+					   &nativesize);
+		if (error == 0) {
+			error = ataama_set(device, retry_count, timeout,
+				       ccb, maxsize);
+			if (error == 0 && quiet == 0) {
+				/* redo identify to get new lba values */
+				error = ata_do_identify(device, retry_count,
+				    timeout, ccb, &ident_buf);
+				ataama_print(ident_buf, nativesize, 1);
+			}
+		}
+		break;
+
+	case ATA_AMA_ACTION_FREEZE_LOCK:
+		error = ataama_freeze(device, retry_count, timeout,
+					   ccb);
+		if (error == 0 && quiet == 0)
+			printf("Accessible Max Address has been frozen\n");
+		break;
+
+	default:
+		errx(1, "Option currently not supported");
+	}
+
+	cam_freeccb(ccb);
+	free(ident_buf);
+
+	return (error);
+}
+
+static int
 atasecurity(struct cam_device *device, int retry_count, int timeout,
 	    int argc, char **argv, char *combinedopt)
 {
@@ -9191,6 +9441,7 @@ 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 ama        [dev_id][generic args] [-f] [-q] [-s max_sectors]\n"
 "        camcontrol persist    [dev_id][generic args] <-i action|-o action>\n"
 "                              [-a][-I tid][-k key][-K sa_key][-p][-R rtp]\n"
 "                              [-s scope][-S][-T type][-U]\n"
@@ -9391,6 +9642,11 @@ usage(int printlong)
 "                  device\n"
 "-U pwd            unlock the HPA configuration of the device\n"
 "-y                don't ask any questions\n"
+"ama arguments:\n"
+"-f                freeze the AMA configuration of the device\n"
+"-q                be quiet, do not print any status messages\n"
+"-s sectors        configures the maximum user accessible sectors of the\n"
+"                  device\n"
 "persist arguments:\n"
 "-i action         specify read_keys, read_reservation, report_cap, or\n"
 "                  read_full_status\n"
@@ -9721,6 +9977,10 @@ main(int argc, char **argv)
 		break;
 	case CAM_CMD_HPA:
 		error = atahpa(cam_dev, retry_count, timeout,
+			       argc, argv, combinedopt);
+		break;
+	case CAM_CMD_AMA:
+		error = ataama(cam_dev, retry_count, timeout,
 			       argc, argv, combinedopt);
 		break;
 #endif /* MINIMALISTIC */

Modified: stable/11/sys/cam/ata/ata_all.c
==============================================================================
--- stable/11/sys/cam/ata/ata_all.c	Thu Aug  8 22:06:40 2019	(r350800)
+++ stable/11/sys/cam/ata/ata_all.c	Thu Aug  8 22:08:54 2019	(r350801)
@@ -182,7 +182,13 @@ ata_op_string(struct ata_cmd *cmd)
 		return ("SEP_ATTN");
 	case 0x70: return ("SEEK");
 	case 0x77: return ("SET_DATE_TIME_EXT");
-	case 0x78: return ("ACCESSIBLE_MAX_ADDRESS_CONFIGURATION");
+	case 0x78:
+		switch (cmd->features) {
+		case 0x00: return ("GET_NATIVE_MAX_ADDRESS_EXT");
+		case 0x01: return ("SET_ACCESSIBLE_MAX_ADDRESS_EXT");
+		case 0x02: return ("FREEZE_ACCESSIBLE_MAX_ADDRESS_EXT");
+		}
+		return ("ACCESSIBLE_MAX_ADDRESS_CONFIGURATION");
 	case 0x7C: return ("REMOVE_ELEMENT_AND_TRUNCATE");
 	case 0x87: return ("CFA_TRANSLATE_SECTOR");
 	case 0x90: return ("EXECUTE_DEVICE_DIAGNOSTIC");

Modified: stable/11/sys/sys/ata.h
==============================================================================
--- stable/11/sys/sys/ata.h	Thu Aug  8 22:06:40 2019	(r350800)
+++ stable/11/sys/sys/ata.h	Thu Aug  8 22:08:54 2019	(r350801)
@@ -240,12 +240,15 @@ struct ata_params {
 #define ATA_SUPPORT_FREEFALL		0x0020
 #define ATA_SUPPORT_SENSE_REPORT	0x0040
 #define ATA_SUPPORT_EPC			0x0080
+#define ATA_SUPPORT_AMAX_ADDR		0x0100
+#define ATA_SUPPORT_DSN			0x0200
 /*120*/ u_int16_t       enabled2;
 #define ATA_ENABLED_WRITEREADVERIFY	0x0002
 #define ATA_ENABLED_WRITEUNCORREXT	0x0004
 #define ATA_ENABLED_FREEFALL		0x0020
 #define ATA_ENABLED_SENSE_REPORT	0x0040
 #define ATA_ENABLED_EPC			0x0080
+#define ATA_ENABLED_DSN			0x0200
 	u_int16_t       reserved121[6];
 /*127*/ u_int16_t       removable_status;
 /*128*/ u_int16_t       security_status;
@@ -434,6 +437,10 @@ struct ata_params {
 #define		ATA_RFPDMA_ZAC_MGMT_IN	0x02	/* NCQ ZAC mgmt in w/data */
 #define ATA_SEP_ATTN                    0x67    /* SEP request */
 #define ATA_SEEK                        0x70    /* seek */
+#define	ATA_AMAX_ADDR			0x78	/* Accessible Max Address */
+#define		ATA_AMAX_ADDR_GET	0x00	/* GET NATIVE MAX ADDRESS EXT */
+#define		ATA_AMAX_ADDR_SET	0x01	/* SET ACCESSIBLE MAX ADDRESS EXT */
+#define		ATA_AMAX_ADDR_FREEZE	0x02	/* FREEZE ACCESSIBLE MAX ADDRESS EXT */
 #define	ATA_ZAC_MANAGEMENT_OUT		0x9f	/* ZAC management out */
 #define		ATA_ZM_CLOSE_ZONE	0x01	/* close zone */
 #define		ATA_ZM_FINISH_ZONE	0x02	/* finish zone */



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