aboutsummaryrefslogtreecommitdiff
path: root/sbin/camcontrol
diff options
context:
space:
mode:
authorAlexander Motin <mav@FreeBSD.org>2019-07-19 19:15:08 +0000
committerAlexander Motin <mav@FreeBSD.org>2019-07-19 19:15:08 +0000
commit89b35a5274e6b6b09a172061c1d571069afe0361 (patch)
treecaa824ed5b6dff37ec9dc45c75f92f636ab2d45d /sbin/camcontrol
parent4ca9bcd650b8f8956370e931d243a6689e53b9b6 (diff)
downloadsrc-89b35a5274e6b6b09a172061c1d571069afe0361.tar.gz
src-89b35a5274e6b6b09a172061c1d571069afe0361.zip
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. MFC after: 2 weeks Relnotes: yes Sponsored by: iXsystems, Inc.
Notes
Notes: svn path=/head/; revision=350149
Diffstat (limited to 'sbin/camcontrol')
-rw-r--r--sbin/camcontrol/camcontrol.843
-rw-r--r--sbin/camcontrol/camcontrol.c278
2 files changed, 311 insertions, 10 deletions
diff --git a/sbin/camcontrol/camcontrol.8 b/sbin/camcontrol/camcontrol.8
index 8d7207467482..7940e1633614 100644
--- a/sbin/camcontrol/camcontrol.8
+++ b/sbin/camcontrol/camcontrol.8
@@ -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
diff --git a/sbin/camcontrol/camcontrol.c b/sbin/camcontrol/camcontrol.c
index d24ec7d4f23c..529de2e16ee0 100644
--- a/sbin/camcontrol/camcontrol.c
+++ b/sbin/camcontrol/camcontrol.c
@@ -110,6 +110,7 @@ typedef enum {
CAM_CMD_MMCSD_CMD = 0x00000029,
CAM_CMD_POWER_MODE = 0x0000002a,
CAM_CMD_DEVTYPE = 0x0000002b,
+ CAM_CMD_AMA = 0x0000002c,
} cam_cmdmask;
typedef enum {
@@ -236,6 +237,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"},
@@ -359,6 +361,8 @@ static int atasecurity(struct cam_device *device, int retry_count, int timeout,
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);
@@ -1423,8 +1427,9 @@ atahpa_print(struct ata_params *parm, u_int64_t hpasize, int header)
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 {
@@ -1432,6 +1437,32 @@ atahpa_print(struct ata_params *parm, u_int64_t hpasize, int header)
}
}
+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)
{
@@ -2258,6 +2289,94 @@ atahpa_freeze_lock(struct cam_device *device, int retry_count,
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,
@@ -2371,7 +2490,7 @@ ataidentify(struct cam_device *device, int retry_count, int timeout)
{
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");
@@ -2392,12 +2511,22 @@ ataidentify(struct cam_device *device, int retry_count, int timeout)
} 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);
@@ -2917,7 +3046,7 @@ atahpa(struct cam_device *device, int retry_count, int timeout,
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);
@@ -2946,7 +3075,7 @@ atahpa(struct cam_device *device, int retry_count, int timeout,
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,
@@ -2959,28 +3088,28 @@ atahpa(struct cam_device *device, int retry_count, int timeout,
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;
@@ -2994,6 +3123,127 @@ atahpa(struct cam_device *device, int retry_count, int timeout,
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)
@@ -9649,6 +9899,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"
@@ -9847,6 +10098,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"
@@ -10171,6 +10427,10 @@ main(int argc, char **argv)
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;
case CAM_CMD_DEVTREE:
error = getdevtree(argc, argv, combinedopt);
break;