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,674 @@ +/*- + * 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 "nvmecontrol.h" + +/* Tables for command line parsing */ + +static cmd_fn_t feat; +static cmd_fn_t featget; +static cmd_fn_t featset; +static cmd_fn_t featsummary; + +#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) + +#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_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_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_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_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 (0xFF) +#define NVME_FEAT_TEMPERATURE_THRESHOLD_TMPTH_SHIFT (0) +#define NVME_FEAT_TEMPERATURE_THRESHOLD_TMPTH_MASK (0xFFFF) + +#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_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_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_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_WRITE_ATOMICITY_DN_SHIFT (0) +#define NVME_FEAT_WRITE_ATOMICITY_DN_MASK (0x1) + +#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) + +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", +}; + +typedef void (*output_fn_t)(const uint8_t fid, uint32_t cdw0); + +static const char * +feat_name_res[] = { + [0] = "Reserved", + [1] = "Command Arbitration", + [2] = "Power Management", + [3] = "LBA Range Type", + [4] = "Temperature Threshold", + [5] = "Error Recovery", + [6] = "Volatile Write Cache", + [7] = "Number of Queues", + [8] = "Interrupt Coalescing", + [9] = "Interrupt Vector Configuration", + [10] = "Write Atomicity Normal", + [11] = "Asynchronous Event Configuration", + [12] = "Autonomous Power State Transition", + [13] = "Host Memory Buffer", + [14] = "Timestamp", + [15] = "Keep Alive Timer", + [16] = "Host Controlled Thermal Management", + [17] = "Non-Operational Power State Config", +}; +static uint32_t feat_name_res_max = nitems(feat_name_res); + +struct summary_data { + uint8_t fid; + uint8_t sel; + uint32_t cdw11; +}; + +static const struct summary_data feat_summary_list[] = { + { 0x1, 0x0, 0x0 }, + { 0x2, 0x0, 0x0 }, + { 0x4, 0x0, 0x0 }, + { 0x5, 0x0, 0x0 }, + { 0x7, 0x0, 0x0 }, + { 0x8, 0x0, 0x0 }, + { 0x9, 0x0, 0x0 }, + { 0xa, 0x0, 0x0 }, + { 0xb, 0x0, 0x0 }, +}; +static uint32_t feat_summary_list_max = nitems(feat_summary_list); + +static struct cmd feat_cmd = { + .name = "feature", + .fn = feat, + .descr = "Get Feature & Set Feature for specified device", + .ctx_size = 0, + .opts = NULL, + .args = NULL, +}; + +CMD_COMMAND(feat_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 = featget, + .descr = "Get Feature for specified device", + .ctx_size = sizeof(get_opt), + .opts = get_opts, + .args = get_args, +}; + +CMD_SUBCOMMAND(feat_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 *dev; +} set_opt = { + .fid = NONE8, + .save = false, + .cdw11 = 0, + .cdw12 = 0, + .cdw13 = 0, + .cdw14 = 0, + .cdw15 = 0, + .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_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 = featset, + .descr = "Set Feature for specified device", + .ctx_size = sizeof(set_opt), + .opts = set_opts, + .args = set_args, +}; + +CMD_SUBCOMMAND(feat_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 = featsummary, + .descr = "Show Feature summary for specified device", + .ctx_size = sizeof(summary_opt), + .opts = NULL, + .args = summary_args, +}; + +CMD_SUBCOMMAND(feat_cmd, summary_cmd); + +static void +print_feat_arbitration(uint32_t cdw0) +{ + + printf(" Arbitration Burst (AB): "); + if ((cdw0 & NVME_FEAT_ARBITRATION_AB_MASK) == NVME_FEAT_ARBITRATION_AB_MASK) + printf("no limit\n"); + else + printf("%d\n", 1 << (cdw0 & NVME_FEAT_ARBITRATION_AB_MASK)); + printf(" Low Priority Weight (LPW): %d\n", + ONE_BASED((cdw0 >> NVME_FEAT_ARBITRATION_LPW_SHIFT) & + NVME_FEAT_ARBITRATION_LPW_MASK)); + printf(" Medium Priority Weight (MPW): %d\n", + ONE_BASED((cdw0 >> NVME_FEAT_ARBITRATION_MPW_SHIFT) & + NVME_FEAT_ARBITRATION_MPW_MASK)); + printf(" High Priority Weight (HPW): %d\n", + ONE_BASED((cdw0 >> NVME_FEAT_ARBITRATION_HPW_SHIFT) & + NVME_FEAT_ARBITRATION_HPW_MASK)); +} + +static void +print_feat_power_management(uint32_t cdw0) +{ + + printf(" Workload Hint (WH): %#x\n", + (cdw0 >> NVME_FEAT_POWER_MANAGEMENT_WH_SHIFT) & + NVME_FEAT_POWER_MANAGEMENT_WH_MASK); + printf(" Power State (PS): %#x\n", + (cdw0 >> NVME_FEAT_POWER_MANAGEMENT_PS_SHIFT) & + NVME_FEAT_POWER_MANAGEMENT_PS_MASK); +} + +static void +print_feat_temperature_threshold(uint32_t cdw0) +{ + + printf(" Threshold Type Select (THSEL): %#x\n", + (cdw0 >> NVME_FEAT_TEMPERATURE_THRESHOLD_THSEL_SHIFT) & + NVME_FEAT_TEMPERATURE_THRESHOLD_THSEL_MASK); + printf(" Threshold Temperature Select (TMPSEL): %#x\n", + (cdw0 >> NVME_FEAT_TEMPERATURE_THRESHOLD_TMPSEL_SHIFT) & + NVME_FEAT_TEMPERATURE_THRESHOLD_TMPSEL_MASK); + printf(" Temperature Threshold (TMPTH): "); + print_temp((cdw0 >> NVME_FEAT_TEMPERATURE_THRESHOLD_TMPTH_SHIFT) & + NVME_FEAT_TEMPERATURE_THRESHOLD_TMPTH_MASK); +} + +static void +print_feat_error_recovery(uint32_t cdw0) +{ + + printf(" Deallocated or Unwritten Logical Block Error Enable (DULBE): %d\n", + (cdw0 >> NVME_FEAT_ERROR_RECOVERY_DULBE_SHIFT) & + NVME_FEAT_ERROR_RECOVERY_DULBE_MASK); + printf(" Time Limited Error Recovery (TLER): %d of 100ms\n", + (cdw0 >> NVME_FEAT_ERROR_RECOVERY_TLER_SHIFT) & + NVME_FEAT_ERROR_RECOVERY_TLER_MASK); +} + +static void +print_feat_number_of_queues(uint32_t cdw0) +{ + + printf(" Number of I/O Completion Queues Allocated (NCQA): %d\n", + ONE_BASED((cdw0 >> NVME_FEAT_NUMBER_OF_QUEUES_NCQA_SHIFT) & + NVME_FEAT_NUMBER_OF_QUEUES_NCQA_MASK)); + printf(" Number of I/O Submission Queues Allocated (NSQA): %d\n", + ONE_BASED((cdw0 >> NVME_FEAT_NUMBER_OF_QUEUES_NSQA_SHIFT) & + NVME_FEAT_NUMBER_OF_QUEUES_NSQA_MASK)); +} + +static void +print_feat_interrupt_coalescing(uint32_t cdw0) +{ + + printf(" Aggregation Time (TIME): %d\n", + (cdw0 >> NVME_FEAT_INTERRUPT_COALESCING_TIME_SHIFT) & + NVME_FEAT_INTERRUPT_COALESCING_TIME_MASK); + printf(" Aggregation Threshold (THR): %d\n", + (cdw0 >> NVME_FEAT_INTERRUPT_COALESCING_THR_SHIFT) & + NVME_FEAT_INTERRUPT_COALESCING_THR_MASK); +} + +static void +print_feat_interrupt_vector_configuration(uint32_t cdw0) +{ + + printf(" Coalescing Disable (CD): %d\n", + (cdw0 >> NVME_FEAT_INTERRUPT_VECTOR_CONFIGURATION_CD_SHIFT) & + NVME_FEAT_INTERRUPT_VECTOR_CONFIGURATION_CD_MASK); + printf(" Interrupt Vector (IV): %d\n", + (cdw0 >> NVME_FEAT_INTERRUPT_VECTOR_CONFIGURATION_IV_SHIFT) & + NVME_FEAT_INTERRUPT_VECTOR_CONFIGURATION_IV_MASK); +} + +static void +print_feat_write_atomicity(uint32_t cdw0) +{ + + printf(" Disable Normal (DN): %d\n", + (cdw0 >> NVME_FEAT_WRITE_ATOMICITY_DN_SHIFT) & + NVME_FEAT_WRITE_ATOMICITY_DN_MASK); +} + +static void +print_feat_async_event_configuration(uint32_t cdw0) +{ + + printf(" Telemetry Log Notices (TLN): %d\n", + (cdw0 >> NVME_FEAT_ASYNC_EVENT_CONFIGURATION_TLN_SHIFT) & + NVME_FEAT_ASYNC_EVENT_CONFIGURATION_TLN_MASK); + printf(" Firmware Activation Notices (FAN): %d\n", + (cdw0 >> NVME_FEAT_ASYNC_EVENT_CONFIGURATION_FAN_SHIFT) & + NVME_FEAT_ASYNC_EVENT_CONFIGURATION_FAN_MASK); + printf(" Namespace Attribute Notices (NAN): %d\n", + (cdw0 >> NVME_FEAT_ASYNC_EVENT_CONFIGURATION_NAN_SHIFT) & + NVME_FEAT_ASYNC_EVENT_CONFIGURATION_NAN_MASK); + printf(" SMART / Health Critical Warnings (HCW): 0x%x\n", + (cdw0 >> NVME_FEAT_ASYNC_EVENT_CONFIGURATION_HCW_SHIFT) & + NVME_FEAT_ASYNC_EVENT_CONFIGURATION_HCW_MASK); +} + +static void +output_human(const uint8_t fid, uint32_t cdw0) +{ + + switch (fid) { + case NVME_FEAT_ARBITRATION: + print_feat_arbitration(cdw0); + break; + case NVME_FEAT_POWER_MANAGEMENT: + print_feat_power_management(cdw0); + break; + case NVME_FEAT_LBA_RANGE_TYPE: + printf(" cdw0 = 0x%#08x\n", cdw0); + break; + case NVME_FEAT_TEMPERATURE_THRESHOLD: + print_feat_temperature_threshold(cdw0); + break; + case NVME_FEAT_ERROR_RECOVERY: + print_feat_error_recovery(cdw0); + break; + case NVME_FEAT_VOLATILE_WRITE_CACHE: + printf(" cdw0 = 0x%#08x\n", cdw0); + break; + case NVME_FEAT_NUMBER_OF_QUEUES: + print_feat_number_of_queues(cdw0); + break; + case NVME_FEAT_INTERRUPT_COALESCING: + print_feat_interrupt_coalescing(cdw0); + break; + case NVME_FEAT_INTERRUPT_VECTOR_CONFIGURATION: + print_feat_interrupt_vector_configuration(cdw0); + break; + case NVME_FEAT_WRITE_ATOMICITY: + print_feat_write_atomicity(cdw0); + break; + case NVME_FEAT_ASYNC_EVENT_CONFIGURATION: + print_feat_async_event_configuration(cdw0); + break; + case NVME_FEAT_AUTONOMOUS_POWER_STATE_TRANSITION: + case NVME_FEAT_HOST_MEMORY_BUFFER: + case NVME_FEAT_TIMESTAMP: + case NVME_FEAT_KEEP_ALIVE_TIMER: + case NVME_FEAT_HOST_CONTROLLED_THERMAL_MGMT: + case NVME_FEAT_NON_OP_POWER_STATE_CONFIG: + case NVME_FEAT_READ_RECOVERY_LEVEL_CONFIG: + case NVME_FEAT_PREDICTABLE_LATENCY_MODE_WINDOW: + case NVME_FEAT_LBA_STATUS_INFORMATION_ATTRIBUTES: + case NVME_FEAT_HOST_BEHAVIOR_SUPPORT: + case NVME_FEAT_SANITIZE_CONFIG: + case NVME_FEAT_ENDURANCE_GROUP_EVENT_CONFIGURATION: + case NVME_FEAT_SOFTWARE_PROGRESS_MARKER: + case NVME_FEAT_HOST_IDENTIFIER: + case NVME_FEAT_RESERVATION_NOTIFICATION_MASK: + case NVME_FEAT_RESERVATION_PERSISTENCE: + case NVME_FEAT_NAMESPACE_WRITE_PROTECTION_CONFIG: + default: + printf(" cdw0 = 0x%#08x\n", cdw0); + break; + } +} + +static void +output_binary_uint4(uint8_t v){ + printf("%s", bits[v & 0x0F]); +} + +static void +output_binary(const uint8_t fid __unused, uint32_t cdw0) +{ + uint32_t i; + + printf("cdw0 = "); + for(i = 0; i < sizeof(cdw0) * 2; i++) { + output_binary_uint4(cdw0 >> i*4); + printf(" "); + } + printf("\n"); +} + +static void +featget(const struct cmd *f, int argc, char *argv[]) +{ + struct nvme_pt_command pt; + uint8_t fid, sel; + uint32_t cdw0, cdw11; + int fd; + output_fn_t output_fn; + + 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 > 0x3) { + fprintf(stderr, + "select value over range\n"); + arg_help(argc, argv, f); + } + + open_dev(get_opt.dev, &fd, 1, 1); + + fid = get_opt.fid; + sel = get_opt.sel; + cdw11 = get_opt.cdw11; + + 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) + err(EX_IOERR, "passthrough request failed"); + + printf("Get Feature: %s(0x%x)\n", + (fid < feat_name_res_max ? feat_name_res[fid] : "\0"), fid); + + if (nvme_completion_is_error(&pt.cpl)) + errx(EX_IOERR, "get feature returned error"); + + cdw0 = pt.cpl.cdw0; + + output_fn = output_human; + if ( get_opt.binary ) + output_fn = output_binary; + + output_fn(fid, cdw0); + + close(fd); + exit(0); +} + +static void +featset(const struct cmd *f, int argc, char *argv[]) +{ + struct nvme_pt_command pt; + uint8_t fid; + bool save; + uint32_t cdw11, cdw12, cdw13, cdw14, cdw15; + int fd; + + if (arg_parse(argc, argv, f)) + return; + + if (set_opt.fid == NONE8) { + fprintf(stderr, + "feature id not specified\n"); + arg_help(argc, argv, f); + } + + fid = set_opt.fid; + save = set_opt.save; + cdw11 = set_opt.cdw11; + cdw12 = set_opt.cdw12; + cdw13 = set_opt.cdw13; + cdw14 = set_opt.cdw14; + cdw15 = set_opt.cdw15; + + open_dev(set_opt.dev, &fd, 1, 1); + + memset(&pt, 0, sizeof(pt)); + pt.cmd.opc = NVME_OPC_SET_FEATURES; + pt.cmd.cdw10 = htole32((save << NVME_FEAT_SET_SV_SHIFT) | + (fid << NVME_FEAT_SET_FID_SHIFT)); + pt.cmd.cdw11 = htole32(cdw11); + pt.cmd.cdw12 = htole32(cdw12); + pt.cmd.cdw13 = htole32(cdw13); + pt.cmd.cdw14 = htole32(cdw14); + pt.cmd.cdw15 = htole32(cdw15); + pt.buf = NULL; + pt.len = 0; + pt.is_read = 0; + + if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0) + err(EX_IOERR, "passthrough request failed"); + + printf("Set Feature: %s(0x%x)\n", + (fid < feat_name_res_max ? feat_name_res[fid] : "\0"), fid); + + if (nvme_completion_is_error(&pt.cpl)) + errx(EX_IOERR, "set feature returned error"); + else + printf("set feature success\n"); + + close(fd); + exit(0); +} + +static void +featsummary(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 < feat_summary_list_max; i++) { + fid = feat_summary_list[i].fid; + sel = feat_summary_list[i].sel; + cdw11 = feat_summary_list[i].cdw11; + + 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)\n", + (fid < feat_name_res_max ? feat_name_res[fid] : "\0"), fid); + + if (nvme_completion_is_error(&pt.cpl)) + continue; + + cdw0 = pt.cpl.cdw0; + output_human(fid, cdw0); + } + + close(fd); + exit(0); +} + +static void +feat(const struct cmd *nf __unused, int argc, char *argv[]) +{ + + cmd_dispatch(argc, argv, &feat_cmd); +}