diff options
author | Alexander Motin <mav@FreeBSD.org> | 2019-07-19 19:15:08 +0000 |
---|---|---|
committer | Alexander Motin <mav@FreeBSD.org> | 2019-07-19 19:15:08 +0000 |
commit | 89b35a5274e6b6b09a172061c1d571069afe0361 (patch) | |
tree | caa824ed5b6dff37ec9dc45c75f92f636ab2d45d /sbin/camcontrol | |
parent | 4ca9bcd650b8f8956370e931d243a6689e53b9b6 (diff) | |
download | src-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.8 | 43 | ||||
-rw-r--r-- | sbin/camcontrol/camcontrol.c | 278 |
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; |