From owner-svn-src-all@freebsd.org Fri Jul 19 19:15:10 2019 Return-Path: Delivered-To: svn-src-all@mailman.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mailman.nyi.freebsd.org (Postfix) with ESMTP id 11372A9A25; Fri, 19 Jul 2019 19:15:10 +0000 (UTC) (envelope-from mav@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) server-signature RSA-PSS (4096 bits) client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "Let's Encrypt Authority X3" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id ECE8B82B24; Fri, 19 Jul 2019 19:15:09 +0000 (UTC) (envelope-from mav@FreeBSD.org) Received: from repo.freebsd.org (repo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by mxrelay.nyi.freebsd.org (Postfix) with ESMTPS id C1D9B25B90; Fri, 19 Jul 2019 19:15:09 +0000 (UTC) (envelope-from mav@FreeBSD.org) Received: from repo.freebsd.org ([127.0.1.37]) by repo.freebsd.org (8.15.2/8.15.2) with ESMTP id x6JJF9NS000395; Fri, 19 Jul 2019 19:15:09 GMT (envelope-from mav@FreeBSD.org) Received: (from mav@localhost) by repo.freebsd.org (8.15.2/8.15.2/Submit) id x6JJF8TW000391; Fri, 19 Jul 2019 19:15:08 GMT (envelope-from mav@FreeBSD.org) Message-Id: <201907191915.x6JJF8TW000391@repo.freebsd.org> X-Authentication-Warning: repo.freebsd.org: mav set sender to mav@FreeBSD.org using -f From: Alexander Motin Date: Fri, 19 Jul 2019 19:15:08 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r350149 - in head: sbin/camcontrol sys/cam/ata sys/sys X-SVN-Group: head X-SVN-Commit-Author: mav X-SVN-Commit-Paths: in head: sbin/camcontrol sys/cam/ata sys/sys X-SVN-Commit-Revision: 350149 X-SVN-Commit-Repository: base MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-Rspamd-Queue-Id: ECE8B82B24 X-Spamd-Bar: -- Authentication-Results: mx1.freebsd.org X-Spamd-Result: default: False [-2.97 / 15.00]; local_wl_from(0.00)[FreeBSD.org]; NEURAL_HAM_MEDIUM(-1.00)[-1.000,0]; NEURAL_HAM_LONG(-1.00)[-1.000,0]; NEURAL_HAM_SHORT(-0.97)[-0.969,0]; ASN(0.00)[asn:11403, ipnet:2610:1c1:1::/48, country:US] X-BeenThere: svn-src-all@freebsd.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: "SVN commit messages for the entire src tree \(except for " user" and " projects" \)" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Fri, 19 Jul 2019 19:15:10 -0000 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 ] [-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 */