Index: sbin/nvmecontrol/Makefile =================================================================== --- sbin/nvmecontrol/Makefile +++ sbin/nvmecontrol/Makefile @@ -10,6 +10,7 @@ SRCS+= passthru.c SRCS+= identify_ext.c nvme_util.c nc_util.c SRCS+= selftest.c +SRCS+= feature.c MAN= nvmecontrol.8 LDFLAGS+= -rdynamic LIBADD+= util Index: sbin/nvmecontrol/feature.c =================================================================== --- /dev/null +++ sbin/nvmecontrol/feature.c @@ -0,0 +1,795 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (C) 2021 Wanpeng Qian + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer, + * without modification, immediately at the beginning of the file. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "nvmecontrol.h" + +/* Tables for command line parsing */ + +static cmd_fn_t feature; +static cmd_fn_t feature_get; +static cmd_fn_t feature_set; +static cmd_fn_t feature_summary; + +#define NONE8 0xffu +#define NONE32 0xffffffffu +#define NONE64 0xffffffffffffffffull +#define OPT(l, s, t, opt, addr, desc) { l, s, t, &opt.addr, desc } +#define OPT_END { NULL, 0, arg_none, NULL, NULL } + +/* Convert a zero-based value into a one-based value */ +#define ONE_BASED(zero) ((zero) + 1) +/* Convert a one-based value into a zero-based value */ +#define ZERO_BASED(one) ((one) - 1) + +static +const char *bits[16] = { + [ 0] = "0000", [ 1] = "0001", [ 2] = "0010", [ 3] = "0011", + [ 4] = "0100", [ 5] = "0101", [ 6] = "0110", [ 7] = "0111", + [ 8] = "1000", [ 9] = "1001", [10] = "1010", [11] = "1011", + [12] = "1100", [13] = "1101", [14] = "1110", [15] = "1111", +}; + +static const char * +feature_name_res[] = { + [0] = "Reserved", + [NVME_FEAT_ARBITRATION] = "Command Arbitration", + [NVME_FEAT_POWER_MANAGEMENT] = "Power Management", + [NVME_FEAT_LBA_RANGE_TYPE] = "LBA Range Type", + [NVME_FEAT_TEMPERATURE_THRESHOLD] = "Temperature Threshold", + [NVME_FEAT_ERROR_RECOVERY] = "Error Recovery", + [NVME_FEAT_VOLATILE_WRITE_CACHE] = "Volatile Write Cache", + [NVME_FEAT_NUMBER_OF_QUEUES] = "Number of Queues", + [NVME_FEAT_INTERRUPT_COALESCING] = "Interrupt Coalescing", + [NVME_FEAT_INTERRUPT_VECTOR_CONFIGURATION] = "Interrupt Vector Configuration", + [NVME_FEAT_WRITE_ATOMICITY] = "Write Atomicity Normal", + [NVME_FEAT_ASYNC_EVENT_CONFIGURATION] = "Asynchronous Event Configuration", + [NVME_FEAT_AUTONOMOUS_POWER_STATE_TRANSITION] = "Autonomous Power State Transition", + [NVME_FEAT_HOST_MEMORY_BUFFER] = "Host Memory Buffer", + [NVME_FEAT_TIMESTAMP] = "Timestamp", + [NVME_FEAT_KEEP_ALIVE_TIMER] = "Keep Alive Timer", + [NVME_FEAT_HOST_CONTROLLED_THERMAL_MGMT] = "Host Controlled Thermal Management", + [NVME_FEAT_NON_OP_POWER_STATE_CONFIG] = "Non-Operational Power State Config", +}; +static uint32_t feature_name_res_max = nitems(feature_name_res); + +static const size_t +feature_buf_size_res[] = { + [0] = 0, + [NVME_FEAT_ARBITRATION] = 0, + [NVME_FEAT_POWER_MANAGEMENT] = 0, + [NVME_FEAT_LBA_RANGE_TYPE] = 4096, + [NVME_FEAT_TEMPERATURE_THRESHOLD] = 0, + [NVME_FEAT_ERROR_RECOVERY] = 0, + [NVME_FEAT_VOLATILE_WRITE_CACHE] = 0, + [NVME_FEAT_NUMBER_OF_QUEUES] = 0, + [NVME_FEAT_INTERRUPT_COALESCING] = 0, + [NVME_FEAT_INTERRUPT_VECTOR_CONFIGURATION] = 0, + [NVME_FEAT_WRITE_ATOMICITY] = 0, + [NVME_FEAT_ASYNC_EVENT_CONFIGURATION] = 0, + [NVME_FEAT_AUTONOMOUS_POWER_STATE_TRANSITION] = 0, + [NVME_FEAT_HOST_MEMORY_BUFFER] = 0, + [NVME_FEAT_TIMESTAMP] = 8, + [NVME_FEAT_KEEP_ALIVE_TIMER] = 0, + [NVME_FEAT_HOST_CONTROLLED_THERMAL_MGMT] = 0, + [NVME_FEAT_NON_OP_POWER_STATE_CONFIG] = 0, +}; +static uint32_t feature_buf_size_res_max = nitems(feature_buf_size_res); + +static const char * +sel_name_res[] = { + [0] = "Current", + [1] = "Default", + [2] = "Saved", + [3] = "Supported capabilities", +}; +static uint32_t sel_name_res_max = nitems(sel_name_res); + +static const char * +lba_range_type_name_res[] = { + [0] = "General Purpose", + [1] = "Filesystem", + [2] = "RAID", + [3] = "Cache", + [4] = "Page / swap file", +}; +static uint32_t lba_range_type_name_res_max = + nitems(lba_range_type_name_res); + +struct summary_data { + uint8_t fid; + uint8_t sel; + uint32_t cdw11; +}; + +struct nvme_lba_range_type { + uint8_t type; + uint8_t attribute; + uint8_t reserved1[14]; + uint64_t slba; + uint64_t nlb; + uint8_t guid[16]; + uint8_t reserved2[16]; +} __packed __aligned(4); + +static const struct summary_data feature_summary_list[] = { + { NVME_FEAT_ARBITRATION, 0x0, 0x0 }, + { NVME_FEAT_POWER_MANAGEMENT, 0x0, 0x0 }, + { NVME_FEAT_TEMPERATURE_THRESHOLD, 0x0, 0x0 }, + { NVME_FEAT_ERROR_RECOVERY, 0x0, 0x0 }, + { NVME_FEAT_NUMBER_OF_QUEUES, 0x0, 0x0 }, + { NVME_FEAT_INTERRUPT_COALESCING, 0x0, 0x0 }, + { NVME_FEAT_INTERRUPT_VECTOR_CONFIGURATION, 0x0, 0x0 }, + { NVME_FEAT_WRITE_ATOMICITY, 0x0, 0x0 }, + { NVME_FEAT_ASYNC_EVENT_CONFIGURATION, 0x0, 0x0 }, +}; +static uint32_t feature_summary_list_max = nitems(feature_summary_list); + +static struct cmd feature_cmd = { + .name = "feature", + .fn = feature, + .descr = "Get Feature & Set Feature for specified device", + .ctx_size = 0, + .opts = NULL, + .args = NULL, +}; + +CMD_COMMAND(feature_cmd); + +static struct get_options { + uint8_t fid; + uint8_t sel; + uint32_t cdw11; + bool binary; + const char *dev; +} get_opt = { + .fid = NONE8, + .sel = 0, + .cdw11 = 0, + .binary = false, + .dev = NULL, +}; + +static const struct opts get_opts[] = { + OPT("fid", 'f', arg_uint8, get_opt, fid, + "feature id (required)"), + OPT("sel", 's', arg_uint8, get_opt, sel, + "[0-3]current/default/saved/supported"), + OPT("binary", 'b', arg_none, get_opt, binary, + "print result binary"), + OPT("cdw11", '1', arg_uint32, get_opt, cdw11, + "dword 11 value"), + OPT_END +}; + +static const struct args get_args[] = { + { arg_string, &get_opt.dev, "controller-id" }, + { arg_none, NULL, NULL }, +}; + +static struct cmd get_cmd = { + .name = "get", + .fn = feature_get, + .descr = "Get Feature for specified device", + .ctx_size = sizeof(get_opt), + .opts = get_opts, + .args = get_args, +}; + +CMD_SUBCOMMAND(feature_cmd, get_cmd); + +static struct set_options { + uint8_t fid; + bool save; + uint32_t cdw11; + uint32_t cdw12; + uint32_t cdw13; + uint32_t cdw14; + uint32_t cdw15; + const char *datafile; + const char *dev; +} set_opt = { + .fid = NONE8, + .save = false, + .cdw11 = 0, + .cdw12 = 0, + .cdw13 = 0, + .cdw14 = 0, + .cdw15 = 0, + .datafile = NULL, + .dev = NULL, +}; + +static const struct opts set_opts[] = { + OPT("fid", 'f', arg_uint8, set_opt, fid, + "feature id (required)"), + OPT("save", 's', arg_none, set_opt, save, + "the controller shall save the attribute"), + OPT("cdw11", '1', arg_uint32, set_opt, cdw11, + "dword 11 value"), + OPT("cdw12", '2', arg_uint32, set_opt, cdw12, + "dword 12 value"), + OPT("cdw13", '3', arg_uint32, set_opt, cdw13, + "dword 13 value"), + OPT("cdw14", '4', arg_uint32, set_opt, cdw14, + "dword 14 value"), + OPT("cdw15", '5', arg_uint32, set_opt, cdw15, + "dword 15 value"), + OPT("datafile", 'f', arg_path, set_opt, datafile, + "data file for some special features."), + OPT_END +}; + +static const struct args set_args[] = { + { arg_string, &set_opt.dev, "controller-id" }, + { arg_none, NULL, NULL }, +}; + +static struct cmd set_cmd = { + .name = "set", + .fn = feature_set, + .descr = "Set Feature for specified device", + .ctx_size = sizeof(set_opt), + .opts = set_opts, + .args = set_args, +}; + +CMD_SUBCOMMAND(feature_cmd, set_cmd); + +static struct summary_options { + const char *dev; +} summary_opt = { + .dev = NULL, +}; + +static const struct args summary_args[] = { + { arg_string, &summary_opt.dev, "controller-id" }, + { arg_none, NULL, NULL }, +}; + +static struct cmd summary_cmd = { + .name = "summary", + .fn = feature_summary, + .descr = "Show Feature summary for specified device", + .ctx_size = sizeof(summary_opt), + .opts = NULL, + .args = summary_args, +}; + +CMD_SUBCOMMAND(feature_cmd, summary_cmd); + +static void +print_feature_arbitration(uint32_t cdw0) +{ + + printf(" Arbitration Burst (AB): "); + if ((NVME_FEAT_ARBITRATION_AB(cdw0)) == NVME_FEAT_ARBITRATION_AB_MASK) + printf("no limit\n"); + else + printf("%d\n", 1 << (NVME_FEAT_ARBITRATION_AB(cdw0))); + printf(" Low Priority Weight (LPW): %d\n", + ONE_BASED(NVME_FEAT_ARBITRATION_LPW(cdw0))); + printf(" Medium Priority Weight (MPW): %d\n", + ONE_BASED(NVME_FEAT_ARBITRATION_MPW(cdw0))); + printf(" High Priority Weight (HPW): %d\n", + ONE_BASED(NVME_FEAT_ARBITRATION_HPW(cdw0))); +} + +static void +print_feature_power_management(uint32_t cdw0) +{ + + printf(" Workload Hint (WH): %#x\n", + NVME_FEAT_POWER_MANAGEMENT_WH(cdw0)); + printf(" Power State (PS): %#x\n", + NVME_FEAT_POWER_MANAGEMENT_PS(cdw0)); +} + +static void +print_feature_lba_range_type(uint32_t cdw0 __unused, void *buf, size_t len) +{ + struct nvme_lba_range_type *lba = (struct nvme_lba_range_type*)buf; + size_t num; + size_t i, j; + uint8_t idx; + + num = MIN(ONE_BASED(NVME_FEAT_LBA_RANGE_TYPE_NUM(cdw0)), + len / sizeof(struct nvme_lba_range_type)); + + for (i = 0; i < num; i++) { + if (i == 0 ) + printf("IDX\tTYPE\t\tSLBA\tNLB\t\tGUID\n"); + if (idx > lba_range_type_name_res_max) + idx = 0; + printf("0x%04zx %s", i, lba_range_type_name_res[idx]); + printf("\t0x%lx\t0x%08lx\t", lba->slba, ONE_BASED(lba->nlb)); + for (j = 0; j < sizeof(lba->guid); j++) + printf("%02x", lba->guid[j]); + printf("\n"); + lba++; + } +} + +static void +print_feature_temperature_threshold(uint32_t cdw0) +{ + + printf(" Temperature Threshold (TMPTH): "); + print_temp(NVME_FEAT_TEMPERATURE_THRESHOLD_TMPTH(cdw0)); +} + +static void +print_feature_error_recovery(uint32_t cdw0) +{ + + printf(" Deallocated or Unwritten Logical Block Error Enable (DULBE): %d\n", + NVME_FEAT_ERROR_RECOVERY_DULBE(cdw0)); + printf(" Time Limited Error Recovery (TLER): %d of 100ms\n", + NVME_FEAT_ERROR_RECOVERY_TLER(cdw0)); +} + +static void +print_feature_volatile_write_cache(uint32_t cdw0) +{ + + printf(" Volatile Write Cache Enable (WCE): %d\n", + NVME_FEAT_VOLATILE_WRITE_CACHE_WCE(cdw0)); +} + +static void +print_feature_number_of_queues(uint32_t cdw0) +{ + + printf(" Number of I/O Completion Queues Allocated (NCQA): %d\n", + ONE_BASED(NVME_FEAT_NUMBER_OF_QUEUES_NCQA(cdw0))); + printf(" Number of I/O Submission Queues Allocated (NSQA): %d\n", + ONE_BASED(NVME_FEAT_NUMBER_OF_QUEUES_NSQA(cdw0))); +} + +static void +print_feature_interrupt_coalescing(uint32_t cdw0) +{ + + printf(" Aggregation Time (TIME): %d\n", + NVME_FEAT_INTERRUPT_COALESCING_TIME(cdw0)); + printf(" Aggregation Threshold (THR): %d\n", + NVME_FEAT_INTERRUPT_COALESCING_THR(cdw0)); +} + +static void +print_feature_interrupt_vector_configuration(uint32_t cdw0) +{ + + printf(" Coalescing Disable (CD): %d\n", + NVME_FEAT_INTERRUPT_VECTOR_CONFIGURATION_CD(cdw0)); + printf(" Interrupt Vector (IV): %d\n", + NVME_FEAT_INTERRUPT_VECTOR_CONFIGURATION_IV(cdw0)); +} + +static void +print_feature_write_atomicity(uint32_t cdw0) +{ + + printf(" Disable Normal (DN): %d\n", + NVME_FEAT_WRITE_ATOMICITY_DN(cdw0)); +} + +static void +print_feature_async_event_configuration(uint32_t cdw0) +{ + + printf(" Telemetry Log Notices: %d\n", + NVME_FEAT_ASYNC_EVENT_CONFIGURATION_TLN(cdw0)); + printf(" Firmware Activation Notices: %d\n", + NVME_FEAT_ASYNC_EVENT_CONFIGURATION_FAN(cdw0)); + printf(" Namespace Attribute Notices: %d\n", + NVME_FEAT_ASYNC_EVENT_CONFIGURATION_NAN(cdw0)); + printf(" SMART / Health Critical Warnings: 0x%x\n", + NVME_FEAT_ASYNC_EVENT_CONFIGURATION_TLN(cdw0)); +} + +static void +print_feature_autonomous_power_state_transition(uint32_t cdw0) +{ + printf(" Autonomous Power State Transition Enable (APSTE): %d\n", + NVME_FEAT_AUTONOMOUS_POWER_STATE_TRANSITION_APSTE(cdw0)); +} + +static void +print_feature_timestamp(uint32_t cdw0 __unused, void *buf, size_t len) +{ + uint8_t *data = (uint8_t*)buf; + uint64_t milliseconds; + uint8_t origin; + bool synch; + time_t seconds; + struct tm *timetm; + char tmbuf[64]; + + if (len < 8) + return; + + origin = (data[6] >> 1) & 0x07; + synch = data[6] & 0x01; + + memcpy((uint8_t*)&milliseconds, (uint8_t*)data, len); + milliseconds &= 0xffffffffffff; + + seconds = milliseconds / 1000; + timetm = localtime(&seconds); + strftime(tmbuf, sizeof tmbuf, "%Y-%m-%d %H:%M:%S", timetm); + + switch (origin) { + case 0x0: + printf(" Origin: since controller reset\n"); + break; + case 0x1: + printf(" Origin: since last set feature\n"); + break; + default: + printf(" Origin: reserved\n" ); + break; + } + if (synch) + printf(" Synch: may not continuously.\n"); + else + printf(" Synch: continuously\n"); + + printf(" Timestamp: %lu ms (%s.%03ld)\n",milliseconds, tmbuf, milliseconds % 1000); +} + +static void +print_feature_keep_alive_timer(uint32_t cdw0) +{ + + printf(" Keep Alive Timeout (KATO): %d\n", + NVME_FEAT_KEEP_ALIVE_TIMER_KATO(cdw0)); +} + +static void +print_feature_host_memory_buffer(uint32_t cdw0) +{ + + printf(" Memory Return (MR): %d\n", + NVME_FEAT_HOST_MEMORY_BUFFER_MR(cdw0)); + printf(" Enable Host Memory (EHM): %d\n", + NVME_FEAT_HOST_MEMORY_BUFFER_EHM(cdw0)); +} + +static void +print_feature_host_controlled_thermal_mgmt(uint32_t cdw0) +{ + + printf(" Thermal Management Temperature 1 (TMT1): %d\n", + NVME_FEAT_HOST_CONTROLLED_THERMAL_MGMT_TMT1(cdw0)); + printf(" Thermal Management Temperature 2 (TMT2): %d\n", + NVME_FEAT_HOST_CONTROLLED_THERMAL_MGMT_TMT2(cdw0)); +} +static void +print_feature_non_op_power_state_config(uint32_t cdw0) +{ + + printf(" Non-Operational Power State Permissive Mode Enable (NOPPME): %d\n", + NVME_FEAT_NON_OP_POWER_STATE_CONFIG_NOPPME(cdw0)); +} + +static void +output_human(const uint8_t fid, uint32_t cdw0, void *buf, size_t len) +{ + + switch (fid) { + case NVME_FEAT_ARBITRATION: + print_feature_arbitration(cdw0); + break; + case NVME_FEAT_POWER_MANAGEMENT: + print_feature_power_management(cdw0); + break; + case NVME_FEAT_LBA_RANGE_TYPE: + print_feature_lba_range_type(cdw0, buf, len); + break; + case NVME_FEAT_TEMPERATURE_THRESHOLD: + print_feature_temperature_threshold(cdw0); + break; + case NVME_FEAT_ERROR_RECOVERY: + print_feature_error_recovery(cdw0); + break; + case NVME_FEAT_VOLATILE_WRITE_CACHE: + print_feature_volatile_write_cache(cdw0); + break; + case NVME_FEAT_NUMBER_OF_QUEUES: + print_feature_number_of_queues(cdw0); + break; + case NVME_FEAT_INTERRUPT_COALESCING: + print_feature_interrupt_coalescing(cdw0); + break; + case NVME_FEAT_INTERRUPT_VECTOR_CONFIGURATION: + print_feature_interrupt_vector_configuration(cdw0); + break; + case NVME_FEAT_WRITE_ATOMICITY: + print_feature_write_atomicity(cdw0); + break; + case NVME_FEAT_ASYNC_EVENT_CONFIGURATION: + print_feature_async_event_configuration(cdw0); + break; + case NVME_FEAT_AUTONOMOUS_POWER_STATE_TRANSITION: + print_feature_autonomous_power_state_transition(cdw0); + break; + case NVME_FEAT_HOST_MEMORY_BUFFER: + print_feature_host_memory_buffer(cdw0); + break; + case NVME_FEAT_TIMESTAMP: + print_feature_timestamp(cdw0, buf, len); + break; + case NVME_FEAT_KEEP_ALIVE_TIMER: + print_feature_keep_alive_timer(cdw0); + break; + case NVME_FEAT_HOST_CONTROLLED_THERMAL_MGMT: + print_feature_host_controlled_thermal_mgmt(cdw0); + break; + case NVME_FEAT_NON_OP_POWER_STATE_CONFIG: + print_feature_non_op_power_state_config(cdw0); + break; + default: + printf(" cdw0 = 0x%#08x\n", cdw0); + break; + } +} + +static void +output_binary(const uint8_t fid __unused, uint32_t cdw0, void *buf __unused, size_t len __unused) +{ + uint32_t i; + + printf("cdw0 = "); + for(i = 0; i < sizeof(cdw0) * 2; i++) { + printf("%s", bits[(cdw0 >> i*4) & 0x0F]); + printf(" "); + } + printf("\n"); +} + +static void +read_data_file(const char *path, void **buf, int32_t *size) +{ + struct stat sb; + int32_t filesize; + int fd; + + *size = 0; + *buf = NULL; + + if ((fd = open(path, O_RDONLY)) < 0) + err(EX_NOINPUT, "unable to open '%s'", path); + if (fstat(fd, &sb) < 0) + err(EX_NOINPUT, "unable to stat '%s'", path); + + if (sb.st_size > INT32_MAX) + errx(EX_USAGE, "size of file '%s' is too large (%jd bytes)", + path, (intmax_t)sb.st_size); + filesize = (int32_t)sb.st_size; + if ((*buf = malloc(filesize)) == NULL) + errx(EX_OSERR, "unable to malloc %d bytes", filesize); + if ((*size = read(fd, *buf, filesize)) < 0) + err(EX_IOERR, "error reading '%s'", path); + /* XXX assuming no short reads */ + if (*size != filesize) + errx(EX_IOERR, + "error reading '%s' (read %d bytes, requested %d bytes)", + path, *size, filesize); + close(fd); +} + +static void +feature_get(const struct cmd *f, int argc, char *argv[]) +{ + struct nvme_pt_command pt; + int fd; + uint8_t *buf = NULL; + uint32_t size = 0; + + if (arg_parse(argc, argv, f)) + return; + + if (get_opt.fid == NONE8) { + fprintf(stderr, + "feature id not specified\n"); + arg_help(argc, argv, f); + } + + if (get_opt.sel >= sel_name_res_max) + errx(EX_IOERR, "invalid select value"); + + if (get_opt.fid < feature_buf_size_res_max) { + if ((size = feature_buf_size_res[get_opt.fid]) > 0) { + if ((buf = malloc(size)) == NULL) + errx(EX_OSERR, "unable to malloc %d bytes", size); + memset(buf, 0, size); + } + } + + open_dev(get_opt.dev, &fd, 1, 1); + + memset(&pt, 0, sizeof(pt)); + pt.cmd.opc = NVME_OPC_GET_FEATURES; + pt.cmd.cdw10 = htole32((get_opt.sel << NVME_FEAT_GET_SEL_SHIFT) | + (get_opt.fid << NVME_FEAT_GET_FID_SHIFT)); + pt.cmd.cdw11 = htole32(get_opt.cdw11); + pt.buf = buf; + pt.len = size; + pt.is_read = 1; + + if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0) + err(EX_IOERR, "passthrough request failed"); + + printf("%s(0x%x), %s value\n", + (get_opt.fid < feature_name_res_max ? feature_name_res[get_opt.fid] : "\0"), get_opt.fid, + sel_name_res[get_opt.sel]); + + if (nvme_completion_is_error(&pt.cpl)) { + errx(EX_IOERR, "%s", + get_status_string(NVME_STATUS_GET_SCT(pt.cpl.status), + NVME_STATUS_GET_SC(pt.cpl.status))); + } + + if ( get_opt.binary ) + output_binary(get_opt.fid, pt.cpl.cdw0, buf, size); + else + output_human(get_opt.fid, pt.cpl.cdw0, buf, size); + + close(fd); + exit(0); +} + +static void +feature_set(const struct cmd *f, int argc, char *argv[]) +{ + struct nvme_pt_command pt; + int fd; + void *buf = NULL; + int32_t size = 0; + + if (arg_parse(argc, argv, f)) + return; + + if (set_opt.fid == NONE8) { + fprintf(stderr, + "feature id not specified\n"); + arg_help(argc, argv, f); + } + + if (set_opt.datafile) + read_data_file(set_opt.datafile, &buf, &size); + + if (set_opt.fid == NVME_FEAT_LBA_RANGE_TYPE && + ( size > 4096 || size % 64 != 0)) { + errx(EX_USAGE, "data size of file '%s' is too large (%d bytes) or not align to 64 bytes", + set_opt.datafile, size); + } + + open_dev(set_opt.dev, &fd, 1, 1); + + memset(&pt, 0, sizeof(pt)); + pt.cmd.opc = NVME_OPC_SET_FEATURES; + pt.cmd.cdw10 = htole32((set_opt.save << NVME_FEAT_SET_SV_SHIFT) | + (set_opt.fid << NVME_FEAT_SET_FID_SHIFT)); + pt.cmd.cdw11 = htole32(set_opt.cdw11); + pt.cmd.cdw12 = htole32(set_opt.cdw12); + pt.cmd.cdw13 = htole32(set_opt.cdw13); + pt.cmd.cdw14 = htole32(set_opt.cdw14); + pt.cmd.cdw15 = htole32(set_opt.cdw15); + pt.buf = buf; + pt.len = size; + pt.is_read = 0; + + if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0) + err(EX_IOERR, "passthrough request failed"); + + + if (nvme_completion_is_error(&pt.cpl)) { + errx(EX_IOERR, "%s", + get_status_string(NVME_STATUS_GET_SCT(pt.cpl.status), + NVME_STATUS_GET_SC(pt.cpl.status))); + } + + printf("set feature success\n"); + + close(fd); + exit(0); +} + +static void +feature_summary(const struct cmd *f, int argc, char *argv[]) +{ + struct nvme_pt_command pt; + uint8_t fid, sel; + uint32_t cdw0, cdw11; + int fd; + uint32_t i; + + if (arg_parse(argc, argv, f)) + return; + + open_dev(summary_opt.dev, &fd, 1, 1); + + for(i = 0; i < feature_summary_list_max; i++) { + fid = feature_summary_list[i].fid; + sel = feature_summary_list[i].sel; + cdw11 = feature_summary_list[i].cdw11; + + if (sel >= sel_name_res_max) + continue; + + memset(&pt, 0, sizeof(pt)); + pt.cmd.opc = NVME_OPC_GET_FEATURES; + pt.cmd.cdw10 = htole32((sel << NVME_FEAT_GET_SEL_SHIFT) | + (fid << NVME_FEAT_GET_FID_SHIFT)); + pt.cmd.cdw11 = htole32(cdw11); + pt.buf = NULL; + pt.len = 0; + pt.is_read = 1; + + if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0) + continue; + + printf("%s(0x%x), %s value\n", + (fid < feature_name_res_max ? feature_name_res[fid] : "\0"), fid, + sel_name_res[sel]); + + if (nvme_completion_is_error(&pt.cpl)) + continue; + + cdw0 = pt.cpl.cdw0; + output_human(fid, cdw0, NULL, 0); + } + + close(fd); + exit(0); +} + +static void +feature(const struct cmd *nf __unused, int argc, char *argv[]) +{ + + cmd_dispatch(argc, argv, &feature_cmd); +} Index: sbin/nvmecontrol/nvmecontrol.h =================================================================== --- sbin/nvmecontrol/nvmecontrol.h +++ sbin/nvmecontrol/nvmecontrol.h @@ -64,6 +64,8 @@ const char *kv_lookup(const struct kv_name *kv, size_t kv_count, uint32_t key); +const char *get_status_string(uint16_t sct, uint16_t sc); + void logpage_register(struct logpage_function *p); #define NVME_CTRLR_PREFIX "nvme" #define NVME_NS_PREFIX "ns" Index: sbin/nvmecontrol/nvmecontrol.8 =================================================================== --- sbin/nvmecontrol/nvmecontrol.8 +++ sbin/nvmecontrol/nvmecontrol.8 @@ -153,6 +153,27 @@ .Op Fl a .Aq Ar device-id .Nm +.Ic feature get +.Aq Fl f Ar feature-id +.Op Fl s Ar select +.Op Fl b +.Op Fl 1 Ar value +.Aq Ar device-id +.Nm +.Ic feature set +.Aq Fl f Ar feature-id +.Op Fl s Ar save +.Op Fl 1 Ar value +.Op Fl 2 Ar value +.Op Fl 3 Ar value +.Op Fl 4 Ar value +.Op Fl 5 Ar value +.Op Fl f Ar path_to_datafile +.Aq Ar device-id +.Nm +.Ic feature summary +.Aq Ar device-id +.Nm .Ic format .Op Fl f Ar fmt .Op Fl m Ar mset @@ -242,6 +263,81 @@ .Dv IDENTIFY_CONTROLLER data associated with that drive. .El +.Ss feature +The feature command includes +.Ic get set +and +.Ic summary +sub-commands +to get and set feature for specific device. +.El +.Ss feature get +The feature get command can output feature settings for specific device. +.Bl -tag -width 8n +.It Fl f Ar feature-id +Feature id to get +.It Fl s +Select +.Bl -tag -compact -width 6n +.It Dv 0 +Current +.It Dv 1 +Default +.It Dv 2 +Saved +.It Dv 3 +Supported capabilities +.El +.It Fl b +Print binary data +.It Fl 1 Ar value +32-bit value for CDW11. +.El +.Ss feature set +The feature set command can set feature for specific device. +.Bl -tag -width 8n +.It Fl f Ar feature-id +Feature id to set +.It Fl s +Specifies that the controller shall save the attribute +so that the attribute persists through all power states and resets. +.It Fl 1 Ar value +32-bit value for CDW11. +.It Fl 2 Ar value +32-bit value for CDW12. +.It Fl 3 Ar value +32-bit value for CDW13. +.It Fl 4 Ar value +32-bit value for CDW14. +.It Fl 5 Ar value +32-bit value for CDW15. +.It Fl f Ar path_to_datafile +Data file for specific feature. +.El +.Ss feature summary +The feature summary command will output all mandatory features +for specific device. These features are: +.Pp +.Bl -tag -compact -width "Fid 0x00" +.It Dv Fid 0x01 +Command Arbitration +.It Dv Fid 0x02 +Power Management +.It Dv Fid 0x04 +Temperature Threshold +.It Dv Fid 0x05 +Error Recovery +.It Dv Fid 0x07 +Number of Queues +.It Dv Fid 0x08 +Interrupt Coalescing +.It Dv Fid 0x09 +Interrupt Vector Configuration +.It Dv Fid 0x0A +Write Atomicity Normal +.It Dv Fid 0x0B +Asynchronous Event Configuration +.El .Ss logpage The logpage command knows how to print log pages of various types. It also knows about vendor specific log pages from hgst/wdc and intel. @@ -670,6 +766,15 @@ standard out. Redirect it to a temporary file. .Pp +.Dl nvmecontrol feature get -f0x01 -s1 nvme0 +.Pp +Get feature default value of Command Arbitration for nvme0 controller. +.Pp +.Dl nvmecontrol feature set -f0x04 -s -10x157 nvme0 +.Pp +Set feature value of Temperature Threshold for nvme0 controller, +and tell controller to save that value. +.Pp .Dl nvmecontrol firmware -s 2 -f /tmp/nvme_firmware nvme0 .Pp Download the firmware image contained in "/tmp/nvme_firmware" to slot 2 of the Index: sbin/nvmecontrol/nvmecontrol.c =================================================================== --- sbin/nvmecontrol/nvmecontrol.c +++ sbin/nvmecontrol/nvmecontrol.c @@ -176,6 +176,154 @@ *nsid = gnsid.nsid; } +struct nvme_status_string { + uint16_t sc; + const char * str; +}; + +static struct nvme_status_string generic_status[] = { + { NVME_SC_SUCCESS, "SUCCESS" }, + { NVME_SC_INVALID_OPCODE, "INVALID OPCODE" }, + { NVME_SC_INVALID_FIELD, "INVALID_FIELD" }, + { NVME_SC_COMMAND_ID_CONFLICT, "COMMAND ID CONFLICT" }, + { NVME_SC_DATA_TRANSFER_ERROR, "DATA TRANSFER ERROR" }, + { NVME_SC_ABORTED_POWER_LOSS, "ABORTED - POWER LOSS" }, + { NVME_SC_INTERNAL_DEVICE_ERROR, "INTERNAL DEVICE ERROR" }, + { NVME_SC_ABORTED_BY_REQUEST, "ABORTED - BY REQUEST" }, + { NVME_SC_ABORTED_SQ_DELETION, "ABORTED - SQ DELETION" }, + { NVME_SC_ABORTED_FAILED_FUSED, "ABORTED - FAILED FUSED" }, + { NVME_SC_ABORTED_MISSING_FUSED, "ABORTED - MISSING FUSED" }, + { NVME_SC_INVALID_NAMESPACE_OR_FORMAT, "INVALID NAMESPACE OR FORMAT" }, + { NVME_SC_COMMAND_SEQUENCE_ERROR, "COMMAND SEQUENCE ERROR" }, + { NVME_SC_INVALID_SGL_SEGMENT_DESCR, "INVALID SGL SEGMENT DESCRIPTOR" }, + { NVME_SC_INVALID_NUMBER_OF_SGL_DESCR, "INVALID NUMBER OF SGL DESCRIPTORS" }, + { NVME_SC_DATA_SGL_LENGTH_INVALID, "DATA SGL LENGTH INVALID" }, + { NVME_SC_METADATA_SGL_LENGTH_INVALID, "METADATA SGL LENGTH INVALID" }, + { NVME_SC_SGL_DESCRIPTOR_TYPE_INVALID, "SGL DESCRIPTOR TYPE INVALID" }, + { NVME_SC_INVALID_USE_OF_CMB, "INVALID USE OF CONTROLLER MEMORY BUFFER" }, + { NVME_SC_PRP_OFFET_INVALID, "PRP OFFET INVALID" }, + { NVME_SC_ATOMIC_WRITE_UNIT_EXCEEDED, "ATOMIC WRITE UNIT EXCEEDED" }, + { NVME_SC_OPERATION_DENIED, "OPERATION DENIED" }, + { NVME_SC_SGL_OFFSET_INVALID, "SGL OFFSET INVALID" }, + { NVME_SC_HOST_ID_INCONSISTENT_FORMAT, "HOST IDENTIFIER INCONSISTENT FORMAT" }, + { NVME_SC_KEEP_ALIVE_TIMEOUT_EXPIRED, "KEEP ALIVE TIMEOUT EXPIRED" }, + { NVME_SC_KEEP_ALIVE_TIMEOUT_INVALID, "KEEP ALIVE TIMEOUT INVALID" }, + { NVME_SC_ABORTED_DUE_TO_PREEMPT, "COMMAND ABORTED DUE TO PREEMPT AND ABORT" }, + { NVME_SC_SANITIZE_FAILED, "SANITIZE FAILED" }, + { NVME_SC_SANITIZE_IN_PROGRESS, "SANITIZE IN PROGRESS" }, + { NVME_SC_SGL_DATA_BLOCK_GRAN_INVALID, "SGL_DATA_BLOCK_GRANULARITY_INVALID" }, + { NVME_SC_NOT_SUPPORTED_IN_CMB, "COMMAND NOT SUPPORTED FOR QUEUE IN CMB" }, + { NVME_SC_NAMESPACE_IS_WRITE_PROTECTED, "NAMESPACE IS WRITE PROTECTED" }, + { NVME_SC_COMMAND_INTERRUPTED, "COMMAND INTERRUPTED" }, + { NVME_SC_TRANSIENT_TRANSPORT_ERROR, "TRANSIENT TRANSPORT ERROR" }, + + { NVME_SC_LBA_OUT_OF_RANGE, "LBA OUT OF RANGE" }, + { NVME_SC_CAPACITY_EXCEEDED, "CAPACITY EXCEEDED" }, + { NVME_SC_NAMESPACE_NOT_READY, "NAMESPACE NOT READY" }, + { NVME_SC_RESERVATION_CONFLICT, "RESERVATION CONFLICT" }, + { NVME_SC_FORMAT_IN_PROGRESS, "FORMAT IN PROGRESS" }, + { 0xFFFF, "GENERIC" } +}; + +static struct nvme_status_string command_specific_status[] = { + { NVME_SC_COMPLETION_QUEUE_INVALID, "INVALID COMPLETION QUEUE" }, + { NVME_SC_INVALID_QUEUE_IDENTIFIER, "INVALID QUEUE IDENTIFIER" }, + { NVME_SC_MAXIMUM_QUEUE_SIZE_EXCEEDED, "MAX QUEUE SIZE EXCEEDED" }, + { NVME_SC_ABORT_COMMAND_LIMIT_EXCEEDED, "ABORT CMD LIMIT EXCEEDED" }, + { NVME_SC_ASYNC_EVENT_REQUEST_LIMIT_EXCEEDED, "ASYNC LIMIT EXCEEDED" }, + { NVME_SC_INVALID_FIRMWARE_SLOT, "INVALID FIRMWARE SLOT" }, + { NVME_SC_INVALID_FIRMWARE_IMAGE, "INVALID FIRMWARE IMAGE" }, + { NVME_SC_INVALID_INTERRUPT_VECTOR, "INVALID INTERRUPT VECTOR" }, + { NVME_SC_INVALID_LOG_PAGE, "INVALID LOG PAGE" }, + { NVME_SC_INVALID_FORMAT, "INVALID FORMAT" }, + { NVME_SC_FIRMWARE_REQUIRES_RESET, "FIRMWARE REQUIRES RESET" }, + { NVME_SC_INVALID_QUEUE_DELETION, "INVALID QUEUE DELETION" }, + { NVME_SC_FEATURE_NOT_SAVEABLE, "FEATURE IDENTIFIER NOT SAVEABLE" }, + { NVME_SC_FEATURE_NOT_CHANGEABLE, "FEATURE NOT CHANGEABLE" }, + { NVME_SC_FEATURE_NOT_NS_SPECIFIC, "FEATURE NOT NAMESPACE SPECIFIC" }, + { NVME_SC_FW_ACT_REQUIRES_NVMS_RESET, "FIRMWARE ACTIVATION REQUIRES NVM SUBSYSTEM RESET" }, + { NVME_SC_FW_ACT_REQUIRES_RESET, "FIRMWARE ACTIVATION REQUIRES RESET" }, + { NVME_SC_FW_ACT_REQUIRES_TIME, "FIRMWARE ACTIVATION REQUIRES MAXIMUM TIME VIOLATION" }, + { NVME_SC_FW_ACT_PROHIBITED, "FIRMWARE ACTIVATION PROHIBITED" }, + { NVME_SC_OVERLAPPING_RANGE, "OVERLAPPING RANGE" }, + { NVME_SC_NS_INSUFFICIENT_CAPACITY, "NAMESPACE INSUFFICIENT CAPACITY" }, + { NVME_SC_NS_ID_UNAVAILABLE, "NAMESPACE IDENTIFIER UNAVAILABLE" }, + { NVME_SC_NS_ALREADY_ATTACHED, "NAMESPACE ALREADY ATTACHED" }, + { NVME_SC_NS_IS_PRIVATE, "NAMESPACE IS PRIVATE" }, + { NVME_SC_NS_NOT_ATTACHED, "NS NOT ATTACHED" }, + { NVME_SC_THIN_PROV_NOT_SUPPORTED, "THIN PROVISIONING NOT SUPPORTED" }, + { NVME_SC_CTRLR_LIST_INVALID, "CONTROLLER LIST INVALID" }, + { NVME_SC_SELF_TEST_IN_PROGRESS, "DEVICE SELF-TEST IN PROGRESS" }, + { NVME_SC_BOOT_PART_WRITE_PROHIB, "BOOT PARTITION WRITE PROHIBITED" }, + { NVME_SC_INVALID_CTRLR_ID, "INVALID CONTROLLER IDENTIFIER" }, + { NVME_SC_INVALID_SEC_CTRLR_STATE, "INVALID SECONDARY CONTROLLER STATE" }, + { NVME_SC_INVALID_NUM_OF_CTRLR_RESRC, "INVALID NUMBER OF CONTROLLER RESOURCES" }, + { NVME_SC_INVALID_RESOURCE_ID, "INVALID RESOURCE IDENTIFIER" }, + { NVME_SC_SANITIZE_PROHIBITED_WPMRE, "SANITIZE PROHIBITED WRITE PERSISTENT MEMORY REGION ENABLED" }, + { NVME_SC_ANA_GROUP_ID_INVALID, "ANA GROUP IDENTIFIED INVALID" }, + { NVME_SC_ANA_ATTACH_FAILED, "ANA ATTACH FAILED" }, + + { NVME_SC_CONFLICTING_ATTRIBUTES, "CONFLICTING ATTRIBUTES" }, + { NVME_SC_INVALID_PROTECTION_INFO, "INVALID PROTECTION INFO" }, + { NVME_SC_ATTEMPTED_WRITE_TO_RO_PAGE, "WRITE TO RO PAGE" }, + { 0xFFFF, "COMMAND SPECIFIC" } +}; + +static struct nvme_status_string media_error_status[] = { + { NVME_SC_WRITE_FAULTS, "WRITE FAULTS" }, + { NVME_SC_UNRECOVERED_READ_ERROR, "UNRECOVERED READ ERROR" }, + { NVME_SC_GUARD_CHECK_ERROR, "GUARD CHECK ERROR" }, + { NVME_SC_APPLICATION_TAG_CHECK_ERROR, "APPLICATION TAG CHECK ERROR" }, + { NVME_SC_REFERENCE_TAG_CHECK_ERROR, "REFERENCE TAG CHECK ERROR" }, + { NVME_SC_COMPARE_FAILURE, "COMPARE FAILURE" }, + { NVME_SC_ACCESS_DENIED, "ACCESS DENIED" }, + { NVME_SC_DEALLOCATED_OR_UNWRITTEN, "DEALLOCATED OR UNWRITTEN LOGICAL BLOCK" }, + { 0xFFFF, "MEDIA ERROR" } +}; + +static struct nvme_status_string path_related_status[] = { + { NVME_SC_INTERNAL_PATH_ERROR, "INTERNAL PATH ERROR" }, + { NVME_SC_ASYMMETRIC_ACCESS_PERSISTENT_LOSS, "ASYMMETRIC ACCESS PERSISTENT LOSS" }, + { NVME_SC_ASYMMETRIC_ACCESS_INACCESSIBLE, "ASYMMETRIC ACCESS INACCESSIBLE" }, + { NVME_SC_ASYMMETRIC_ACCESS_TRANSITION, "ASYMMETRIC ACCESS TRANSITION" }, + { NVME_SC_CONTROLLER_PATHING_ERROR, "CONTROLLER PATHING ERROR" }, + { NVME_SC_HOST_PATHING_ERROR, "HOST PATHING ERROR" }, + { NVME_SC_COMMAND_ABOTHED_BY_HOST, "COMMAND ABOTHED BY HOST" }, + { 0xFFFF, "PATH RELATED" }, +}; + +const char * +get_status_string(uint16_t sct, uint16_t sc) +{ + struct nvme_status_string *entry; + + switch (sct) { + case NVME_SCT_GENERIC: + entry = generic_status; + break; + case NVME_SCT_COMMAND_SPECIFIC: + entry = command_specific_status; + break; + case NVME_SCT_MEDIA_ERROR: + entry = media_error_status; + break; + case NVME_SCT_PATH_RELATED: + entry = path_related_status; + break; + case NVME_SCT_VENDOR_SPECIFIC: + return ("VENDOR SPECIFIC"); + default: + return ("RESERVED"); + } + + while (entry->sc != 0xFFFF) { + if (entry->sc == sc) + return (entry->str); + entry++; + } + return (entry->str); +} + int main(int argc, char *argv[]) { Index: sys/dev/nvme/nvme.h =================================================================== --- sys/dev/nvme/nvme.h +++ sys/dev/nvme/nvme.h @@ -533,6 +533,316 @@ #define NVME_SS_PAGE_SSTAT_GDE_SHIFT (8) #define NVME_SS_PAGE_SSTAT_GDE_MASK (0x1) +/* Features */ +/* Get Features */ +#define NVME_FEAT_GET_SEL_SHIFT (8) +#define NVME_FEAT_GET_SEL_MASK (0x7) +#define NVME_FEAT_GET_FID_SHIFT (0) +#define NVME_FEAT_GET_FID_MASK (0xFF) + +#define NVME_FEAT_GET_SEL(x) \ + (((x) >> NVME_FEAT_GET_SEL_SHIFT) & \ + NVME_FEAT_GET_SEL_MASK) +#define NVME_FEAT_GET_FID(x) \ + (((x) >> NVME_FEAT_GET_FID_SHIFT) & \ + NVME_FEAT_GET_FID_MASK) + +/* Set Features */ +#define NVME_FEAT_SET_SV_SHIFT (31) +#define NVME_FEAT_SET_SV_MASK (0x1) +#define NVME_FEAT_SET_FID_SHIFT (0) +#define NVME_FEAT_SET_FID_MASK (0xFF) + +#define NVME_FEAT_SET_SV(x) \ + (((x) >> NVME_FEAT_SET_SV_SHIFT) & \ + NVME_FEAT_SET_SV_MASK) +#define NVME_FEAT_SET_FID(x) \ + (((x) >> NVME_FEAT_SET_FID_SHIFT) & \ + NVME_FEAT_SET_FID_MASK) + +/* Arbitration */ +#define NVME_FEAT_ARBITRATION_HPW_SHIFT (24) +#define NVME_FEAT_ARBITRATION_HPW_MASK (0xFF) +#define NVME_FEAT_ARBITRATION_MPW_SHIFT (16) +#define NVME_FEAT_ARBITRATION_MPW_MASK (0xFF) +#define NVME_FEAT_ARBITRATION_LPW_SHIFT (8) +#define NVME_FEAT_ARBITRATION_LPW_MASK (0xFF) +#define NVME_FEAT_ARBITRATION_AB_SHIFT (0) +#define NVME_FEAT_ARBITRATION_AB_MASK (0x7) + +#define NVME_FEAT_ARBITRATION_HPW(x) \ + (((x) >> NVME_FEAT_ARBITRATION_HPW_SHIFT) & \ + NVME_FEAT_ARBITRATION_HPW_MASK) +#define NVME_FEAT_ARBITRATION_MPW(x) \ + (((x) >> NVME_FEAT_ARBITRATION_MPW_SHIFT) & \ + NVME_FEAT_ARBITRATION_MPW_MASK) +#define NVME_FEAT_ARBITRATION_LPW(x) \ + (((x) >> NVME_FEAT_ARBITRATION_LPW_SHIFT) & \ + NVME_FEAT_ARBITRATION_LPW_MASK) +#define NVME_FEAT_ARBITRATION_AB(x) \ + (((x) >> NVME_FEAT_ARBITRATION_AB_SHIFT) & \ + NVME_FEAT_ARBITRATION_AB_MASK) + +/* Power Management */ +#define NVME_FEAT_POWER_MANAGEMENT_WH_SHIFT (5) +#define NVME_FEAT_POWER_MANAGEMENT_WH_MASK (0x7) +#define NVME_FEAT_POWER_MANAGEMENT_PS_SHIFT (0) +#define NVME_FEAT_POWER_MANAGEMENT_PS_MASK (0x1F) + +#define NVME_FEAT_POWER_MANAGEMENT_WH(x) \ + (((x) >> NVME_FEAT_POWER_MANAGEMENT_WH_SHIFT) & \ + NVME_FEAT_POWER_MANAGEMENT_WH_MASK) +#define NVME_FEAT_POWER_MANAGEMENT_PS(x) \ + (((x) >> NVME_FEAT_POWER_MANAGEMENT_PS_SHIFT) & \ + NVME_FEAT_POWER_MANAGEMENT_PS_MASK) + +/* LBA Range Type */ +#define NVME_FEAT_LBA_RANGE_TYPE_NUM_SHIFT (0) +#define NVME_FEAT_LBA_RANGE_TYPE_NUM_MASK (0x3F) + +#define NVME_FEAT_LBA_RANGE_TYPE_NUM(x) \ + (((x) >> NVME_FEAT_LBA_RANGE_TYPE_NUM_SHIFT) & \ + NVME_FEAT_LBA_RANGE_TYPE_NUM_MASK) + +/* Temperature Threshold */ +#define NVME_FEAT_TEMPERATURE_THRESHOLD_THSEL_SHIFT (20) +#define NVME_FEAT_TEMPERATURE_THRESHOLD_THSEL_MASK (0x3) +#define NVME_FEAT_TEMPERATURE_THRESHOLD_TMPSEL_SHIFT (16) +#define NVME_FEAT_TEMPERATURE_THRESHOLD_TMPSEL_MASK (0xF) +#define NVME_FEAT_TEMPERATURE_THRESHOLD_TMPTH_SHIFT (0) +#define NVME_FEAT_TEMPERATURE_THRESHOLD_TMPTH_MASK (0xFFFF) + +#define NVME_FEAT_TEMPERATURE_THRESHOLD_THSEL(x) \ + (((x) >> NVME_FEAT_TEMPERATURE_THRESHOLD_THSEL_SHIFT) & \ + NVME_FEAT_TEMPERATURE_THRESHOLD_THSEL_MASK) +#define NVME_FEAT_TEMPERATURE_THRESHOLD_TMPSEL(x) \ + (((x) >> NVME_FEAT_TEMPERATURE_THRESHOLD_TMPSEL_SHIFT) & \ + NVME_FEAT_TEMPERATURE_THRESHOLD_TMPSEL_MASK) +#define NVME_FEAT_TEMPERATURE_THRESHOLD_TMPTH(x) \ + (((x) >> NVME_FEAT_TEMPERATURE_THRESHOLD_TMPTH_SHIFT) & \ + NVME_FEAT_TEMPERATURE_THRESHOLD_TMPTH_MASK) + +/* Error Recovery */ +#define NVME_FEAT_ERROR_RECOVERY_DULBE_SHIFT (16) +#define NVME_FEAT_ERROR_RECOVERY_DULBE_MASK (0x1) +#define NVME_FEAT_ERROR_RECOVERY_TLER_SHIFT (0) +#define NVME_FEAT_ERROR_RECOVERY_TLER_MASK (0xFF) + +#define NVME_FEAT_ERROR_RECOVERY_DULBE(x) \ + (((x) >> NVME_FEAT_ERROR_RECOVERY_DULBE_SHIFT) & \ + NVME_FEAT_ERROR_RECOVERY_DULBE_MASK) +#define NVME_FEAT_ERROR_RECOVERY_TLER(x) \ + (((x) >> NVME_FEAT_ERROR_RECOVERY_TLER_SHIFT) & \ + NVME_FEAT_ERROR_RECOVERY_TLER_MASK) + +/* Volatile Write Cache */ +#define NVME_FEAT_VOLATILE_WRITE_CACHE_WCE_SHIFT (0) +#define NVME_FEAT_VOLATILE_WRITE_CACHE_WCE_MASK (0x1) + +#define NVME_FEAT_VOLATILE_WRITE_CACHE_WCE(x) \ + (((x) >> NVME_FEAT_VOLATILE_WRITE_CACHE_WCE_SHIFT) & \ + NVME_FEAT_VOLATILE_WRITE_CACHE_WCE_MASK) + +/* Number of Queues */ +#define NVME_FEAT_NUMBER_OF_QUEUES_NCQR_SHIFT (16) +#define NVME_FEAT_NUMBER_OF_QUEUES_NCQR_MASK (0xFFFF) +#define NVME_FEAT_NUMBER_OF_QUEUES_NSQR_SHIFT (0) +#define NVME_FEAT_NUMBER_OF_QUEUES_NSQR_MASK (0xFFFF) +#define NVME_FEAT_NUMBER_OF_QUEUES_NCQA_SHIFT (16) +#define NVME_FEAT_NUMBER_OF_QUEUES_NCQA_MASK (0xFFFF) +#define NVME_FEAT_NUMBER_OF_QUEUES_NSQA_SHIFT (0) +#define NVME_FEAT_NUMBER_OF_QUEUES_NSQA_MASK (0xFFFF) + +#define NVME_FEAT_NUMBER_OF_QUEUES_NCQR(x) \ + (((x) >> NVME_FEAT_NUMBER_OF_QUEUES_NCQR_SHIFT) & \ + NVME_FEAT_NUMBER_OF_QUEUES_NCQR_MASK) +#define NVME_FEAT_NUMBER_OF_QUEUES_NSQR(x) \ + (((x) >> NVME_FEAT_NUMBER_OF_QUEUES_NSQR_SHIFT) & \ + NVME_FEAT_NUMBER_OF_QUEUES_NSQR_MASK) +#define NVME_FEAT_NUMBER_OF_QUEUES_NCQA(x) \ + (((x) >> NVME_FEAT_NUMBER_OF_QUEUES_NCQA_SHIFT) & \ + NVME_FEAT_NUMBER_OF_QUEUES_NCQA_MASK) +#define NVME_FEAT_NUMBER_OF_QUEUES_NSQA(x) \ + (((x) >> NVME_FEAT_NUMBER_OF_QUEUES_NSQA_SHIFT) & \ + NVME_FEAT_NUMBER_OF_QUEUES_NSQA_MASK) + +/* Interrupt Coalescing */ +#define NVME_FEAT_INTERRUPT_COALESCING_TIME_SHIFT (8) +#define NVME_FEAT_INTERRUPT_COALESCING_TIME_MASK (0xFF) +#define NVME_FEAT_INTERRUPT_COALESCING_THR_SHIFT (0) +#define NVME_FEAT_INTERRUPT_COALESCING_THR_MASK (0xFF) + +#define NVME_FEAT_INTERRUPT_COALESCING_TIME(x) \ + (((x) >> NVME_FEAT_INTERRUPT_COALESCING_TIME_SHIFT) & \ + NVME_FEAT_INTERRUPT_COALESCING_TIME_MASK) +#define NVME_FEAT_INTERRUPT_COALESCING_THR(x) \ + (((x) >> NVME_FEAT_INTERRUPT_COALESCING_THR_SHIFT) & \ + NVME_FEAT_INTERRUPT_COALESCING_THR_MASK) + +/* Interrupt Vector Configuration */ +#define NVME_FEAT_INTERRUPT_VECTOR_CONFIGURATION_CD_SHIFT (16) +#define NVME_FEAT_INTERRUPT_VECTOR_CONFIGURATION_CD_MASK (0x1) +#define NVME_FEAT_INTERRUPT_VECTOR_CONFIGURATION_IV_SHIFT (0) +#define NVME_FEAT_INTERRUPT_VECTOR_CONFIGURATION_IV_MASK (0xFF) + +#define NVME_FEAT_INTERRUPT_VECTOR_CONFIGURATION_CD(x) \ + (((x) >> NVME_FEAT_INTERRUPT_VECTOR_CONFIGURATION_CD_SHIFT) & \ + NVME_FEAT_INTERRUPT_VECTOR_CONFIGURATION_CD_MASK) +#define NVME_FEAT_INTERRUPT_VECTOR_CONFIGURATION_IV(x) \ + (((x) >> NVME_FEAT_INTERRUPT_VECTOR_CONFIGURATION_IV_SHIFT) & \ + NVME_FEAT_INTERRUPT_VECTOR_CONFIGURATION_IV_MASK) + +/* Write Atomicity Normal */ +#define NVME_FEAT_WRITE_ATOMICITY_DN_SHIFT (0) +#define NVME_FEAT_WRITE_ATOMICITY_DN_MASK (0x1) + +#define NVME_FEAT_WRITE_ATOMICITY_DN(x) \ + (((x) >> NVME_FEAT_WRITE_ATOMICITY_DN_SHIFT) & \ + NVME_FEAT_WRITE_ATOMICITY_DN_MASK) + +/* Asynchronous Event Configuration */ +#define NVME_FEAT_ASYNC_EVENT_CONFIGURATION_TLN_SHIFT (10) +#define NVME_FEAT_ASYNC_EVENT_CONFIGURATION_TLN_MASK (0x1) +#define NVME_FEAT_ASYNC_EVENT_CONFIGURATION_FAN_SHIFT (9) +#define NVME_FEAT_ASYNC_EVENT_CONFIGURATION_FAN_MASK (0x1) +#define NVME_FEAT_ASYNC_EVENT_CONFIGURATION_NAN_SHIFT (8) +#define NVME_FEAT_ASYNC_EVENT_CONFIGURATION_NAN_MASK (0x1) +#define NVME_FEAT_ASYNC_EVENT_CONFIGURATION_HCW_SHIFT (0) +#define NVME_FEAT_ASYNC_EVENT_CONFIGURATION_HCW_MASK (0xFF) + +#define NVME_FEAT_ASYNC_EVENT_CONFIGURATION_TLN(x) \ + (((x) >> NVME_FEAT_ASYNC_EVENT_CONFIGURATION_TLN_SHIFT) & \ + NVME_FEAT_ASYNC_EVENT_CONFIGURATION_TLN_MASK) +#define NVME_FEAT_ASYNC_EVENT_CONFIGURATION_FAN(x) \ + (((x) >> NVME_FEAT_ASYNC_EVENT_CONFIGURATION_FAN_SHIFT) & \ + NVME_FEAT_ASYNC_EVENT_CONFIGURATION_FAN_MASK) +#define NVME_FEAT_ASYNC_EVENT_CONFIGURATION_NAN(x) \ + (((x) >> NVME_FEAT_ASYNC_EVENT_CONFIGURATION_NAN_SHIFT) & \ + NVME_FEAT_ASYNC_EVENT_CONFIGURATION_NAN_MASK) +#define NVME_FEAT_ASYNC_EVENT_CONFIGURATION_HCW(x) \ + (((x) >> NVME_FEAT_ASYNC_EVENT_CONFIGURATION_HCW_SHIFT) & \ + NVME_FEAT_ASYNC_EVENT_CONFIGURATION_HCW_MASK) + +/* Autonomous Power State Transition */ +#define NVME_FEAT_AUTONOMOUS_POWER_STATE_TRANSITION_APSTE_SHIFT (0) +#define NVME_FEAT_AUTONOMOUS_POWER_STATE_TRANSITION_APSTE_MASK (0x1) + +#define NVME_FEAT_AUTONOMOUS_POWER_STATE_TRANSITION_APSTE(x) \ + (((x) >> NVME_FEAT_AUTONOMOUS_POWER_STATE_TRANSITION_APSTE_SHIFT) & \ + NVME_FEAT_AUTONOMOUS_POWER_STATE_TRANSITION_APSTE_MASK) + +/* Host Memory Buffer */ +#define NVME_FEAT_HOST_MEMORY_BUFFER_MR_SHIFT (1) +#define NVME_FEAT_HOST_MEMORY_BUFFER_MR_MASK (0x1) +#define NVME_FEAT_HOST_MEMORY_BUFFER_EHM_SHIFT (0) +#define NVME_FEAT_HOST_MEMORY_BUFFER_EHM_MASK (0x1) +#define NVME_FEAT_HOST_MEMORY_BUFFER_HSIZE_SHIFT (0) +#define NVME_FEAT_HOST_MEMORY_BUFFER_HSIZE_MASK (0xFFFF) +#define NVME_FEAT_HOST_MEMORY_BUFFER_HMDLLA_SHIFT (4) +#define NVME_FEAT_HOST_MEMORY_BUFFER_HMDLLA_MASK (0xFFF) +#define NVME_FEAT_HOST_MEMORY_BUFFER_HMDLUA_SHIFT (0) +#define NVME_FEAT_HOST_MEMORY_BUFFER_HMDLUA_MASK (0xFFFF) +#define NVME_FEAT_HOST_MEMORY_BUFFER_HMDLEC_SHIFT (0) +#define NVME_FEAT_HOST_MEMORY_BUFFER_HMDLEC_MASK (0xFFFF) + +#define NVME_FEAT_HOST_MEMORY_BUFFER_MR(x) \ + (((x) >> NVME_FEAT_HOST_MEMORY_BUFFER_MR_SHIFT) & \ + NVME_FEAT_HOST_MEMORY_BUFFER_MR_MASK) +#define NVME_FEAT_HOST_MEMORY_BUFFER_EHM(x) \ + (((x) >> NVME_FEAT_HOST_MEMORY_BUFFER_EHM_SHIFT) & \ + NVME_FEAT_HOST_MEMORY_BUFFER_EHM_MASK) +#define NVME_FEAT_HOST_MEMORY_BUFFER_HSIZE(x) \ + (((x) >> NVME_FEAT_HOST_MEMORY_BUFFER_HSIZE_SHIFT) & \ + NVME_FEAT_HOST_MEMORY_BUFFER_HSIZE_MASK) +#define NVME_FEAT_HOST_MEMORY_BUFFER_HMDLLA(x) \ + (((x) >> NVME_FEAT_HOST_MEMORY_BUFFER_HMDLLA_SHIFT) & \ + NVME_FEAT_HOST_MEMORY_BUFFER_HMDLLA_MASK) +#define NVME_FEAT_HOST_MEMORY_BUFFER_HMDLUA(x) \ + (((x) >> NVME_FEAT_HOST_MEMORY_BUFFER_HMDLUA_SHIFT) & \ + NVME_FEAT_HOST_MEMORY_BUFFER_HMDLUA_MASK) +#define NVME_FEAT_HOST_MEMORY_BUFFER_HMDLEC(x) \ + (((x) >> NVME_FEAT_HOST_MEMORY_BUFFER_HMDLEC_SHIFT) & \ + NVME_FEAT_HOST_MEMORY_BUFFER_HMDLEC_MASK) + +/* Timestamp */ +#define NVME_FEAT_TIMESTAMP_TS_SHIFT (0x0) +#define NVME_FEAT_TIMESTAMP_TS_MASK (0x3F) + +#define NVME_FEAT_TIMESTAMP_TS(x) \ + (((x) >> NVME_FEAT_TIMESTAMP_TS_SHIFT) & \ + NVME_FEAT_TIMESTAMP_TS_MASK) + +/* Keep Alive Timer */ +#define NVME_FEAT_KEEP_ALIVE_TIMER_KATO_SHIFT (0x0) +#define NVME_FEAT_KEEP_ALIVE_TIMER_KATO_MASK (0xFFFF) + +#define NVME_FEAT_KEEP_ALIVE_TIMER_KATO(x) \ + (((x) >> NVME_FEAT_KEEP_ALIVE_TIMER_KATO_SHIFT) & \ + NVME_FEAT_KEEP_ALIVE_TIMER_KATO_MASK) + +/* Host Controlled Thermal Management */ +#define NVME_FEAT_HOST_CONTROLLED_THERMAL_MGMT_TMT1_SHIFT (16) +#define NVME_FEAT_HOST_CONTROLLED_THERMAL_MGMT_TMT1_MASK (0xFF) +#define NVME_FEAT_HOST_CONTROLLED_THERMAL_MGMT_TMT2_SHIFT (0) +#define NVME_FEAT_HOST_CONTROLLED_THERMAL_MGMT_TMT2_MASK (0xFF) + +#define NVME_FEAT_HOST_CONTROLLED_THERMAL_MGMT_TMT1(x) \ + (((x) >> NVME_FEAT_HOST_CONTROLLED_THERMAL_MGMT_TMT1_SHIFT) & \ + NVME_FEAT_HOST_CONTROLLED_THERMAL_MGMT_TMT1_MASK) +#define NVME_FEAT_HOST_CONTROLLED_THERMAL_MGMT_TMT2(x) \ + (((x) >> NVME_FEAT_HOST_CONTROLLED_THERMAL_MGMT_TMT2_SHIFT) & \ + NVME_FEAT_HOST_CONTROLLED_THERMAL_MGMT_TMT2_MASK) + +/* Non-Operational Power State Config */ +#define NVME_FEAT_NON_OP_POWER_STATE_CONFIG_NOPPME_SHIFT (0) +#define NVME_FEAT_NON_OP_POWER_STATE_CONFIG_NOPPME_MASK (0x1) + +#define NVME_FEAT_NON_OP_POWER_STATE_CONFIG_NOPPME(x) \ + (((x) >> NVME_FEAT_NON_OP_POWER_STATE_CONFIG_NOPPME_SHIFT) & \ + NVME_FEAT_NON_OP_POWER_STATE_CONFIG_NOPPME_MASK) + +/* Software Progress Marker */ +#define NVME_FEAT_SOFTWARE_PROGRESS_MARKER_PBSLC_SHIFT (0) +#define NVME_FEAT_SOFTWARE_PROGRESS_MARKER_PBSLC_MASK (0xFF) + +#define NVME_FEAT_SOFTWARE_PROGRESS_MARKER_PBSLC(x) \ + (((x) >> NVME_FEAT_SOFTWARE_PROGRESS_MARKER_PBSLC_SHIFT) & \ + NVME_FEAT_SOFTWARE_PROGRESS_MARKER_PBSLC_MASK) + +/* Host Identifier */ +#define NVME_FEAT_HOST_IDENTIFIER_EXHID_SHIFT (0) +#define NVME_FEAT_HOST_IDENTIFIER_EXHID_MASK (0x1) + +#define NVME_FEAT_HOST_IDENTIFIER_EXHID(x) \ + (((x) >> NVME_FEAT_HOST_IDENTIFIER_EXHID_SHIFT) & \ + NVME_FEAT_HOST_IDENTIFIER_EXHID_MASK) + +/* Reservation Notification Mask */ +#define NVME_FEAT_RESERVATION_NOTIFICATION_MASK_RESPRE_SHIFT (3) +#define NVME_FEAT_RESERVATION_NOTIFICATION_MASK_RESPRE_MASK (0x1) +#define NVME_FEAT_RESERVATION_NOTIFICATION_MASK_RESREL_SHIFT (2) +#define NVME_FEAT_RESERVATION_NOTIFICATION_MASK_RESREL_MASK (0x1) +#define NVME_FEAT_RESERVATION_NOTIFICATION_MASK_REGPRE_SHIFT (1) +#define NVME_FEAT_RESERVATION_NOTIFICATION_MASK_REGPRE_MASK (0x1) + +#define NVME_FEAT_RESERVATION_NOTIFICATION_MASK_RESPRE(x) \ + (((x) >> NVME_FEAT_RESERVATION_NOTIFICATION_MASK_RESPRE_SHIFT) & \ + NVME_FEAT_RESERVATION_NOTIFICATION_MASK_RESPRE_MASK) +#define NVME_FEAT_RESERVATION_NOTIFICATION_MASK_RESREL(x) \ + (((x) >> NVME_FEAT_RESERVATION_NOTIFICATION_MASK_RESREL_SHIFT) & \ + NVME_FEAT_RESERVATION_NOTIFICATION_MASK_RESREL_MASK) +#define NVME_FEAT_RESERVATION_NOTIFICATION_MASK_REGPRE(x) \ + (((x) >> NVME_FEAT_RESERVATION_NOTIFICATION_MASK_REGPRE_SHIFT) & \ + NVME_FEAT_RESERVATION_NOTIFICATION_MASK_REGPRE_MASK) + +/* Reservation Persistence */ +#define NVME_FEAT_RESERVATION_PERSISTENCE_PTPL_SHIFT (0) +#define NVME_FEAT_RESERVATION_PERSISTENCE_PTPL_MASK (0x1) + +#define NVME_FEAT_RESERVATION_PERSISTENCE_PTPL(x) \ + (((x) >> NVME_FEAT_RESERVATION_PERSISTENCE_PTPL_SHIFT) & \ + NVME_FEAT_RESERVATION_PERSISTENCE_PTPL_MASK) + /* CC register SHN field values */ enum shn_value { NVME_SHN_NORMAL = 0x1,