Author: mav
Date: Fri Jul 19 19:15:08 2019
New Revision: 350149
URL: https://svnweb.freebsd.org/changeset/base/350149

Log:
  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.

Modified:
  head/sbin/camcontrol/camcontrol.8
  head/sbin/camcontrol/camcontrol.c
  head/sys/cam/ata/ata_all.c
  head/sys/sys/ata.h

Modified: head/sbin/camcontrol/camcontrol.8
==============================================================================
--- head/sbin/camcontrol/camcontrol.8   Fri Jul 19 18:47:13 2019        
(r350148)
+++ head/sbin/camcontrol/camcontrol.8   Fri Jul 19 19:15:08 2019        
(r350149)
@@ -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: head/sbin/camcontrol/camcontrol.c
==============================================================================
--- head/sbin/camcontrol/camcontrol.c   Fri Jul 19 18:47:13 2019        
(r350148)
+++ head/sbin/camcontrol/camcontrol.c   Fri Jul 19 19:15:08 2019        
(r350149)
@@ -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 
                       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 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 {
@@ -1432,6 +1437,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)
 {
@@ -2258,7 +2289,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)
@@ -2371,7 +2490,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");
@@ -2392,12 +2511,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);
@@ -2917,7 +3046,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);
@@ -2946,7 +3075,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,
@@ -2959,28 +3088,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;
 
@@ -2994,7 +3123,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)
 {
@@ -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"
@@ -10169,6 +10425,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;
        case CAM_CMD_DEVTREE:

Modified: head/sys/cam/ata/ata_all.c
==============================================================================
--- head/sys/cam/ata/ata_all.c  Fri Jul 19 18:47:13 2019        (r350148)
+++ head/sys/cam/ata/ata_all.c  Fri Jul 19 19:15:08 2019        (r350149)
@@ -184,7 +184,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: head/sys/sys/ata.h
==============================================================================
--- head/sys/sys/ata.h  Fri Jul 19 18:47:13 2019        (r350148)
+++ head/sys/sys/ata.h  Fri Jul 19 19:15:08 2019        (r350149)
@@ -242,12 +242,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;
@@ -436,6 +439,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 */
_______________________________________________
[email protected] mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "[email protected]"

Reply via email to