diff --git a/Documentation/nvme-get-reg.txt b/Documentation/nvme-get-reg.txt new file mode 100644 index 0000000000..b0d133e7ae --- /dev/null +++ b/Documentation/nvme-get-reg.txt @@ -0,0 +1,138 @@ +nvme-get-reg(1) +=============== + +NAME +---- +nvme-get-reg - Read and show the defined NVMe controller register + +SYNOPSIS +-------- +[verse] +'nvme get-reg' [--offset=, -O ] [--human-readable | -H] + [--cap] [--vs] [--cmbloc] [--cmbsz] [--bpinfo] + [--cmbsts] [--cmbebs] [--cmbswtp] [--crto] [--pmrcap] + [--pmrsts] [--pmrebs] [--pmrswtp] [--intms] [--intmc] + [--cc] [--csts] [--nssr] [--aqa] [--asq] [--acq] + [--bprsel] [--bpmbl] [--cmbmsc] [--nssd] [--pmrctl] + [--pmrmscl] [--pmrmscu] + [--output-format= | -o ] [--verbose | -v] + +DESCRIPTION +----------- +Read and show the defined NVMe controller register. + +OPTIONS +------- +-O :: +--offset=:: + offset of the requested register + +-H:: +--human-readable:: + show register in readable format + +--cap:: + CAP=0x0 register offset + +--vs:: + VS=0x8 register offset + +--cmbloc:: + CMBLOC=0x38 register offset + +--cmbsz:: + CMBSZ=0x3c register offset + +--bpinfo:: + BPINFO=0x40 register offset + +--cmbsts:: + CMBSTS=0x58 register offset + +--cmbebs:: + CMBEBS=0x5c register offset + +--cmbswtp:: + CMBSWTP=0x60 register offset + +--crto:: + CRTO=0x68 register offset + +--pmrcap:: + PMRCAP=0xe00 register offset + +--pmrsts:: + PMRSTS=0xe08 register offset + +--pmrebs:: + PMREBS=0xe0c register offset + +--pmrswtp:: + PMRSWTP=0xe10 register offset + +--intms:: + INTMS=0xc register offset + +--intmc:: + INTMC=0x10 register offset + +--cc:: + CC=0x14 register offset + +--csts:: + CSTS=0x1c register offset + +--nssr:: + NSSR=0x20 register offset + +--aqa:: + AQA=0x24 register offset + +--asq:: + ASQ=0x28 register offset + +--acq:: + ACQ=0x30 register offset + +--bprsel:: + BPRSEL=0x44 register offset + +--bpmbl:: + BPMBL=0x48 register offset + +--cmbmsc:: + CMBMSC=0x50 register offset + +--nssd:: + NSSD=0x64 register offset + +--pmrctl:: + PMRCTL=0xe04 register offset + +--pmrmscl:: + PMRMSCL=0xe14 register offset + +--pmrmscu:: + PMRMSCU=0xe18 register offset + +-o :: +--output-format=:: + Set the reporting format to 'normal', 'json' or 'binary'. Only one + output format can be used at a time. + +-v:: +--verbose:: + Increase the information detail in the output. + +EXAMPLES +-------- +* The following will run the get-reg command with CC=0x14 register offset ++ +------------ +# nvme get-reg /dev/nvme0 --cc +register: 0x0014 (Controller Configuration), value: 0x460001 +------------ + +NVME +---- +Part of the nvme-user suite. diff --git a/Documentation/nvme-set-reg.txt b/Documentation/nvme-set-reg.txt new file mode 100644 index 0000000000..6fd6a4920c --- /dev/null +++ b/Documentation/nvme-set-reg.txt @@ -0,0 +1,102 @@ +nvme-set-reg(1) +=============== + +NAME +---- +nvme-set-reg - Writes and shows the defined NVMe controller register + +SYNOPSIS +-------- +[verse] +'nvme set-reg' [--offset=, -O ] + [--value= | -V ] [--mmio32 | -m] + [--intms=] [--intmc=] [--cc=] + [--csts=] [--nssr=] [--aqa=] + [--asq=] [--acq=] [--bprsel=] + [--bpmbl=] [--cmbmsc=] [--nssd=] + [--pmrctl=] [--pmrmscl=] [--pmrmscu=] + [--output-format= | -o ] [--verbose | -v] + +DESCRIPTION +----------- +Writes and shows the defined NVMe controller register. + +OPTIONS +------- +-O :: +--offset=:: + offset of the requested register + +-V :: +--value=:: + the value of the register to be set + +--mmio32:: +-m:: + Access 64-bit registers as 2 32-bit + +--intms=:: + INTMS=0xc register offset + +--intmc=:: + INTMC=0x10 register offset + +--cc=:: + CC=0x14 register offset + +--csts=:: + CSTS=0x1c register offset + +--nssr=:: + NSSR=0x20 register offset + +--aqa=:: + AQA=0x24 register offset + +--asq=:: + ASQ=0x28 register offset + +--acq=:: + ACQ=0x30 register offset + +--bprsel=:: + BPRSEL=0x44 register offset + +--bpmbl=:: + BPMBL=0x48 register offset + +--cmbmsc=:: + CMBMSC=0x50 register offset + +--nssd=:: + NSSD=0x64 register offset + +--pmrctl=:: + PMRCTL=0xe04 register offset + +--pmrmscl=:: + PMRMSCL=0xe14 register offset + +--pmrmscu=:: + PMRMSCU=0xe18 register offset + +-o :: +--output-format=:: + Set the reporting format to 'normal', 'json' or 'binary'. Only one + output format can be used at a time. + +-v:: +--verbose:: + Increase the information detail in the output. + +EXAMPLES +-------- +* The following will run the set-reg command with offset 0x14 and value 0x460000 ++ +------------ +# nvme set-reg /dev/nvme0 -O 0x14 -V 0x460000 +------------ + +NVME +---- +Part of the nvme-user suite. diff --git a/common.h b/common.h index b1161189b3..a4f4f99de2 100644 --- a/common.h +++ b/common.h @@ -3,6 +3,7 @@ #define _COMMON_H #include +#include #include "ccan/endian/endian.h" @@ -35,4 +36,24 @@ static inline uint64_t mmio_read64(void *addr) return ((uint64_t)high << 32) | low; } +static inline void mmio_write32(void *addr, uint32_t value) +{ + leint32_t *p = addr; + + *p = cpu_to_le32(value); +} + +/* Access 64-bit registers as 2 32-bit if write32 flag set; Some devices fail 64-bit MMIO. */ +static inline void mmio_write64(void *addr, uint64_t value, bool write32) +{ + uint64_t *p = addr; + + if (write32) { + mmio_write32(addr, value); + mmio_write32((uint32_t *)addr + 1, value >> 32); + return; + } + + *p = cpu_to_le64(value); +} #endif diff --git a/completions/_nvme b/completions/_nvme index 52aaebf923..40e2c1df11 100644 --- a/completions/_nvme +++ b/completions/_nvme @@ -104,6 +104,8 @@ _nvme () { 'show-topology:show subsystem topology' 'nvme-mi-recv:send a NVMe-MI receive command' 'nvme-mi-send:send a NVMe-MI send command' + 'get-reg:read and show the defined NVMe controller register' + 'set-seg:write and show the defined NVMe controller register' 'version:show the program version' 'ocp:OCP cloud SSD extensions' 'solidigm:Solidigm plug-in extensions' @@ -2244,6 +2246,81 @@ _nvme () { _arguments '*:: :->subcmds' _describe -t commands "nvme nvme-mi-send options" _nvme_mi_send ;; + (get-reg) + local _get_reg + _get_reg=( + --offset=':offset of the requested register' + -O':alias for --offset' + --human-readable':show register in readable format' + -H':alias for --human-readable' + --cap':CAP=0x0 register offset' + --vs':VS=0x8 register offset' + --cmbloc':CMBLOC=0x38 register offset' + --cmbsz':CMBSZ=0x3c register offset' + --bpinfo':BPINFO=0x40 register offset' + --cmbsts':CMBSTS=0x58 register offset' + --cmbebs':CMBEBS=0x5c register offset' + --cmbswtp':CMBSWTP=0x60 register offset' + --crto':CRTO=0x68 register offset' + --pmrcap':PMRCAP=0xe00 register offset' + --pmrsts':PMRSTS=0xe08 register offset' + --pmrebs':PMREBS=0xe0c register offset' + --pmrswtp':PMRSWTP=0xe10 register offset' + --intms':INTMS=0xc register offset' + --intmc':INTMC=0x10 register offset' + --cc':CC=0x14 register offset' + --csts':CSTS=0x1c register offset' + --nssr':NSSR=0x20 register offset' + --aqa':AQA=0x24 register offset' + --asq':ASQ=0x28 register offset' + --acq':ACQ=0x30 register offset' + --bprsel':BPRSEL=0x44 register offset' + --bpmbl':BPMBL=0x48 register offset' + --cmbmsc':CMBMSC=0x50 register offset' + --nssd':NSSD=0x64 register offset' + --pmrctl':PMRCTL=0xe04 register offset' + --pmrmscl':PMRMSCL=0xe14 register offset' + --pmrmscu':PMRMSCU=0xe18 register offset' + --output-format=':Output format: normal|json|binary' + -o ':alias for --output-format' + --verbose':Increase the information detail in the output.' + -v':alias for --verbose' + ) + _arguments '*:: :->subcmds' + _describe -t commands "nvme get-reg options" _get_reg + ;; + (set-reg) + local _set_reg + _set_reg=( + --offset=':offset of the requested register' + -O':alias for --offset' + --value=':the value of the register to be set' + -V':alias for --value' + --mmio32':Access 64-bit registers as 2 32-bit' + -m':alias for --mmio32' + --intms=':INTMS=0xc register offset' + --intmc=':INTMC=0x10 register offset' + --cc=':CC=0x14 register offset' + --csts=':CSTS=0x1c register offset' + --nssr=':NSSR=0x20 register offset' + --aqa=':AQA=0x24 register offset' + --asq=':ASQ=0x28 register offset' + --acq=':ACQ=0x30 register offset' + --bprsel=':BPRSEL=0x44 register offset' + --bpmbl=':BPMBL=0x48 register offset' + --cmbmsc=':CMBMSC=0x50 register offset' + --nssd=':NSSD=0x64 register offset' + --pmrctl=':PMRCTL=0xe04 register offset' + --pmrmscl=':PMRMSCL=0xe14 register offset' + --pmrmscu=':PMRMSCU=0xe18 register offset' + --output-format=':Output format: normal|json|binary' + -o ':alias for --output-format' + --verbose':Increase the information detail in the output.' + -v':alias for --verbose' + ) + _arguments '*:: :->subcmds' + _describe -t commands "nvme set-reg options" _set_reg + ;; (version) local _version _version=( diff --git a/completions/bash-nvme-completion.sh b/completions/bash-nvme-completion.sh index 2cf99b4535..0a4305eb5c 100644 --- a/completions/bash-nvme-completion.sh +++ b/completions/bash-nvme-completion.sh @@ -453,6 +453,20 @@ nvme_list_opts () { opts+=" --opcode= -O --namespace-id= -n --data-len= -l \ --nmimt= -m --nmd0= -0 --nmd1= -1 --input-file= -i" ;; + "get-reg") + opts+=" --offset, -O --human-readable -H --cap --vs --cmbloc \ + --cmbsz --bpinfo --cmbsts --cmbebs --cmbswtp --crto \ + --pmrcap --pmrsts --pmrebs --pmrswtp --intms --intmc \ + --cc --csts --nssr --aqa --asq --acq --bprsel --bpmbl \ + --cmbmsc --nssd --pmrctl --pmrmscl --pmrmscu \ + --output-format -o --verbose -v" + ;; + "set-reg") + opts+=" --offset, -O --value= -V --mmio32 -m --intms= --intmc= \ + --cc= --csts= --nssr= --aqa= --asq= --acq= --bprsel= \ + --bpmbl= --cmbmsc= --nssd= --pmrctl= --pmrmscl= \ + --pmrmscu= --output-format= -o --verbose= -v" + ;; "version") opts+=$NO_OPTS ;; @@ -1575,7 +1589,7 @@ _nvme_subcmds () { rpmb boot-part-log fid-support-effects-log \ supported-log-pages lockdown media-unit-stat-log \ supported-cap-config-log dim show-topology list-endgrp \ - nvme-mi-recv nvme-mi-send" + nvme-mi-recv nvme-mi-send get-reg set-reg" # Add plugins: for plugin in "${!_plugin_subcmds[@]}"; do diff --git a/nvme-builtin.h b/nvme-builtin.h index 2d2bead38b..d95b12ce41 100644 --- a/nvme-builtin.h +++ b/nvme-builtin.h @@ -89,6 +89,8 @@ COMMAND_LIST( ENTRY("subsystem-reset", "Resets the subsystem", subsystem_reset) ENTRY("ns-rescan", "Rescans the NVME namespaces", ns_rescan) ENTRY("show-regs", "Shows the controller registers or properties. Requires character device", show_registers) + ENTRY("set-reg", "Set a register and show the resulting value", set_register) + ENTRY("get-reg", "Get a register and show the resulting value", get_register) ENTRY("discover", "Discover NVMeoF subsystems", discover_cmd) ENTRY("connect-all", "Discover and Connect to NVMeoF subsystems", connect_all_cmd) ENTRY("connect", "Connect to NVMeoF subsystem", connect_cmd) diff --git a/nvme-print-json.c b/nvme-print-json.c index 3e47cd506d..a743973c4e 100644 --- a/nvme-print-json.c +++ b/nvme-print-json.c @@ -1049,13 +1049,11 @@ static void json_registers_acq(uint64_t acq, struct json_object *r) obj_add_prix64(r, "Admin Completion Queue Base (ACQB)", acq); } -static void json_registers_cmbloc(uint32_t cmbloc, void *bar, struct json_object *r) +static void json_registers_cmbloc(uint32_t cmbloc, bool support, struct json_object *r) { - uint32_t cmbsz = mmio_read32(bar + NVME_REG_CMBSZ); - obj_add_uint_x(r, "cmbloc", cmbloc); - if (!cmbsz) { + if (!support) { obj_add_result(r, "Controller Memory Buffer feature is not supported"); return; } @@ -1184,17 +1182,15 @@ static void json_registers_pmrctl(uint32_t pmrctl, struct json_object *r) obj_add_str(r, "Enable (EN)", pmrctl & 1 ? "Ready" : "Disabled"); } -static void json_registers_pmrsts(uint32_t pmrsts, void *bar, struct json_object *r) +static void json_registers_pmrsts(uint32_t pmrsts, bool ready, struct json_object *r) { - uint32_t pmrctl = mmio_read32(bar + NVME_REG_PMRCTL); - obj_add_uint_x(r, "pmrsts", pmrsts); obj_add_uint_x(r, "Controller Base Address Invalid (CBAI)", (pmrsts & 0x1000) >> 12); obj_add_str(r, "Health Status (HSTS)", - nvme_register_pmr_hsts_to_string((pmrsts & 0xe00) >> 9)); + nvme_register_pmr_hsts_to_string((pmrsts & 0xe00) >> 9)); obj_add_str(r, "Not Ready (NRDY)", - !(pmrsts & 0x100) && (pmrctl & 1) ? "Ready" : "Not ready"); + !(pmrsts & 0x100) && ready ? "Ready" : "Not ready"); obj_add_uint_x(r, "Error (ERR)", pmrsts & 0xff); } @@ -1205,7 +1201,7 @@ static void json_registers_pmrebs(uint32_t pmrebs, struct json_object *r) obj_add_uint_x(r, "PMR Elasticity Buffer Size Base (PMRWBZ)", (pmrebs & 0xffffff00) >> 8); obj_add_str(r, "Read Bypass Behavior", pmrebs & 0x10 ? "Shall" : "May"); obj_add_str(r, "PMR Elasticity Buffer Size Units (PMRSZU)", - nvme_register_pmr_pmrszu_to_string(pmrebs & 0xf)); + nvme_register_unit_to_string(pmrebs & 0xf)); } static void json_registers_pmrswtp(uint32_t pmrswtp, struct json_object *r) @@ -1214,7 +1210,7 @@ static void json_registers_pmrswtp(uint32_t pmrswtp, struct json_object *r) obj_add_uint_x(r, "PMR Sustained Write Throughput (PMRSWTV)", (pmrswtp & 0xffffff00) >> 8); obj_add_key(r, "PMR Sustained Write Throughput Units (PMRSWTU)", "%s/second", - nvme_register_pmr_pmrszu_to_string(pmrswtp & 0xf)); + nvme_register_unit_to_string(pmrswtp & 0xf)); } static void json_registers_pmrmscl(uint32_t pmrmscl, struct json_object *r) @@ -2543,11 +2539,16 @@ static void json_ctrl_registers_acq(void *bar, struct json_object *r) static void json_ctrl_registers_cmbloc(void *bar, struct json_object *r) { uint32_t cmbloc = mmio_read32(bar + NVME_REG_CMBLOC); + uint32_t cmbsz; + bool support; - if (human()) - json_registers_cmbloc(cmbloc, bar, obj_create_array_obj(r, "cmbloc")); - else + if (human()) { + cmbsz = mmio_read32(bar + NVME_REG_CMBSZ); + support = nvme_registers_cmbloc_support(cmbsz); + json_registers_cmbloc(cmbloc, support, obj_create_array_obj(r, "cmbloc")); + } else { obj_add_int(r, "cmbloc", cmbloc); + } } static void json_ctrl_registers_cmbsz(void *bar, struct json_object *r) @@ -2633,11 +2634,16 @@ static void json_ctrl_registers_pmrctl(void *bar, struct json_object *r) static void json_ctrl_registers_pmrsts(void *bar, struct json_object *r) { uint32_t pmrsts = mmio_read32(bar + NVME_REG_PMRSTS); + uint32_t pmrctl; + bool ready; - if (human()) - json_registers_pmrsts(pmrsts, bar, obj_create_array_obj(r, "pmrsts")); - else + if (human()) { + pmrctl = mmio_read32(bar + NVME_REG_PMRCTL); + ready = nvme_registers_pmrctl_ready(pmrctl); + json_registers_pmrsts(pmrsts, ready, obj_create_array_obj(r, "pmrsts")); + } else { obj_add_int(r, "pmrsts", pmrsts); + } } static void json_ctrl_registers_pmrebs(void *bar, struct json_object *r) @@ -2714,6 +2720,149 @@ static void json_ctrl_registers(void *bar, bool fabrics) json_print(r); } +static void json_registers_cmbebs(__u32 cmbebs, struct json_object *r) +{ + char buffer[BUF_LEN]; + + obj_add_uint_nx(r, "cmbebs", cmbebs); + + obj_add_uint_nx(r, "CMB Elasticity Buffer Size Base (CMBWBZ)", cmbebs >> 8); + sprintf(buffer, "%s", cmbebs & 0x10 ? "shall" : "may"); + obj_add_str(r, "Read Bypass Behavior", buffer); + obj_add_str(r, "CMB Elasticity Buffer Size Units (CMBSZU)", + nvme_register_unit_to_string(cmbebs & 0xf)); +} + +static void json_registers_cmbswtp(__u32 cmbswtp, struct json_object *r) +{ + char str[STR_LEN]; + + obj_add_uint_nx(r, "cmbswtp", cmbswtp); + + obj_add_uint_nx(r, "CMB Sustained Write Throughput (CMBSWTV)", cmbswtp >> 8); + sprintf(str, "%s", nvme_register_unit_to_string(cmbswtp & 0xf)); + obj_add_str(r, "CMB Sustained Write Throughput Units (CMBSWTU)", str); +} + +static void json_ctrl_register_human(int offset, uint64_t value, struct json_object *r) +{ + char buffer[BUF_LEN]; + struct json_object *array_obj = NULL; + + switch (offset) { + case NVME_REG_CAP: + array_obj = obj_create_array_obj(r, "cap"); + break; + case NVME_REG_VS: + array_obj = obj_create_array_obj(r, "vs"); + break; + case NVME_REG_INTMS: + obj_add_nprix64(r, "Interrupt Vector Mask Set (IVMS)", value); + break; + case NVME_REG_INTMC: + obj_add_nprix64(r, "Interrupt Vector Mask Clear (IVMC)", value); + break; + case NVME_REG_CC: + array_obj = obj_create_array_obj(r, "cc"); + break; + case NVME_REG_CSTS: + array_obj = obj_create_array_obj(r, "csts"); + break; + case NVME_REG_NSSR: + obj_add_uint64(r, "NVM Subsystem Reset Control (NSSRC)", value); + break; + case NVME_REG_AQA: + json_registers_aqa(value, obj_create_array_obj(r, "aqa")); + break; + case NVME_REG_ASQ: + obj_add_nprix64(r, "Admin Submission Queue Base (ASQB)", value); + break; + case NVME_REG_ACQ: + obj_add_nprix64(r, "Admin Completion Queue Base (ACQB)", value); + break; + case NVME_REG_CMBLOC: + json_registers_cmbloc(value, true, obj_create_array_obj(r, "cmbloc")); + break; + case NVME_REG_CMBSZ: + json_registers_cmbsz(value, obj_create_array_obj(r, "cmbsz")); + break; + case NVME_REG_BPINFO: + json_registers_bpinfo(value, obj_create_array_obj(r, "bpinfo")); + break; + case NVME_REG_BPRSEL: + json_registers_bprsel(value, obj_create_array_obj(r, "bprsel")); + break; + case NVME_REG_BPMBL: + json_registers_bpmbl(value, obj_create_array_obj(r, "bpmbl")); + break; + case NVME_REG_CMBMSC: + json_registers_cmbmsc(value, obj_create_array_obj(r, "cmbmsc")); + break; + case NVME_REG_CMBSTS: + json_registers_cmbsts(value, obj_create_array_obj(r, "cmbsts")); + break; + case NVME_REG_CMBEBS: + json_registers_cmbebs(value, obj_create_array_obj(r, "cmbebs")); + break; + case NVME_REG_CMBSWTP: + json_registers_cmbswtp(value, obj_create_array_obj(r, "cmbswtp")); + break; + case NVME_REG_NSSD: + json_registers_nssd(value, obj_create_array_obj(r, "nssd")); + break; + case NVME_REG_CRTO: + array_obj = obj_create_array_obj(r, "crto"); + break; + case NVME_REG_PMRCAP: + json_registers_pmrcap(value, obj_create_array_obj(r, "pmrcap")); + break; + case NVME_REG_PMRCTL: + json_registers_pmrctl(value, obj_create_array_obj(r, "pmrctl")); + break; + case NVME_REG_PMRSTS: + json_registers_pmrsts(value, true, obj_create_array_obj(r, "pmrsts")); + break; + case NVME_REG_PMREBS: + json_registers_pmrebs(value, obj_create_array_obj(r, "pmrebs")); + break; + case NVME_REG_PMRSWTP: + json_registers_pmrswtp(value, obj_create_array_obj(r, "pmrswtp")); + break; + case NVME_REG_PMRMSCL: + json_registers_pmrmscl(value, obj_create_array_obj(r, "pmrmscl")); + break; + case NVME_REG_PMRMSCU: + json_registers_pmrmscu(value, obj_create_array_obj(r, "pmrmscu")); + break; + default: + sprintf(buffer, "%#04x (%s)", offset, nvme_register_to_string(offset)); + obj_add_str(r, "register", buffer); + obj_add_nprix64(r, "value", value); + break; + } + + if (array_obj) + json_single_property_human(offset, value, array_obj); +} + +static void json_ctrl_register(int offset, uint64_t value) +{ + bool human = json_print_ops.flags & VERBOSE; + struct json_object *r; + char json_str[STR_LEN]; + + sprintf(json_str, "register: %#04x", offset); + r = obj_create(json_str); + + if (human) { + obj_add_uint64(r, nvme_register_to_string(offset), value); + json_ctrl_register_human(offset, value, r); + } else { + obj_add_str(r, "name", nvme_register_symbol_to_string(offset)); + obj_add_uint64(r, "value", value); + } +} + static void json_nvme_cmd_set_independent_id_ns(struct nvme_id_independent_id_ns *ns, unsigned int nsid) { @@ -4455,6 +4604,7 @@ static struct print_ops json_print_ops = { .phy_rx_eom_log = json_phy_rx_eom_log, .ctrl_list = json_nvme_list_ctrl, .ctrl_registers = json_ctrl_registers, + .ctrl_register = json_ctrl_register, .directive = json_directive_show, .discovery_log = json_discovery_log, .effects_log_list = json_effects_log_list, diff --git a/nvme-print-stdout.c b/nvme-print-stdout.c index 0c27b8313a..e917ada16e 100644 --- a/nvme-print-stdout.c +++ b/nvme-print-stdout.c @@ -1234,14 +1234,15 @@ static void stdout_registers_aqa(__u32 aqa) } -static void stdout_registers_cmbloc(__u32 cmbloc, __u32 cmbsz) +static void stdout_registers_cmbloc(__u32 cmbloc, bool support) { static const char * const enforced[] = { "Enforced", "Not Enforced" }; - if (cmbsz == 0) { + if (!support) { printf("\tController Memory Buffer feature is not supported\n\n"); return; } + printf("\tOffset (OFST): "); printf("%#x (See cmbsz.szu for granularity)\n", (cmbloc & 0xfffff000) >> 12); @@ -1352,6 +1353,24 @@ static void stdout_registers_cmbsts(__u32 cmbsts) (cmbsts & 0x00000001)); } +static void stdout_registers_cmbebs(__u32 cmbebs) +{ + printf("\tCMB Elasticity Buffer Size Base (CMBWBZ): %#x\n", cmbebs >> 8); + printf("\tRead Bypass Behavior : "); + printf("memory reads not conflicting with memory writes in the CMB Elasticity Buffer "); + printf("%s", cmbebs & 0x10 ? "SHALL" : "MAY"); + printf(" bypass those memory writes\n"); + printf("\tCMB Elasticity Buffer Size Units (CMBSZU): %s\n\n", + nvme_register_unit_to_string(cmbebs & 0xf)); +} + +static void stdout_registers_cmbswtp(__u32 cmbswtp) +{ + printf("\tCMB Sustained Write Throughput (CMBSWTV): %#x\n", cmbswtp >> 8); + printf("\tCMB Sustained Write Throughput Units (CMBSWTU): %s/second\n\n", + nvme_register_unit_to_string(cmbswtp & 0xf)); +} + static void stdout_registers_pmrcap(__u32 pmrcap) { printf("\tController Memory Space Supported (CMSS): "); @@ -1376,14 +1395,14 @@ static void stdout_registers_pmrctl(__u32 pmrctl) printf("\tEnable (EN): PMR is %s\n", NVME_PMRCTL_EN(pmrctl) ? "READY" : "Disabled"); } -static void stdout_registers_pmrsts(__u32 pmrsts, __u32 pmrctl) +static void stdout_registers_pmrsts(__u32 pmrsts, bool ready) { printf("\tController Base Address Invalid (CBAI): %x\n", NVME_PMRSTS_CBAI(pmrsts)); printf("\tHealth Status (HSTS): %s\n", nvme_register_pmr_hsts_to_string(NVME_PMRSTS_HSTS(pmrsts))); printf("\tNot Ready (NRDY): "); printf("The Persistent Memory Region is %s to process ", - !NVME_PMRSTS_NRDY(pmrsts) && NVME_PMRCTL_EN(pmrctl) ? "READY" : "Not Ready"); + !NVME_PMRSTS_NRDY(pmrsts) && ready ? "READY" : "Not Ready"); printf("PCI Express memory read and write requests\n"); printf("\tError (ERR): %x\n", NVME_PMRSTS_ERR(pmrsts)); } @@ -1396,7 +1415,7 @@ static void stdout_registers_pmrebs(__u32 pmrebs) printf("in the PMR Elasticity Buffer %s bypass those memory writes\n", NVME_PMREBS_RBB(pmrebs) ? "SHALL" : "MAY"); printf("\tPMR Elasticity Buffer Size Units (PMRSZU): %s\n", - nvme_register_pmr_pmrszu_to_string(NVME_PMREBS_PMRSZU(pmrebs))); + nvme_register_unit_to_string(NVME_PMREBS_PMRSZU(pmrebs))); } static void stdout_registers_pmrswtp(__u32 pmrswtp) @@ -1404,7 +1423,7 @@ static void stdout_registers_pmrswtp(__u32 pmrswtp) printf("\tPMR Sustained Write Throughput (PMRSWTV): %x\n", NVME_PMRSWTP_PMRSWTV(pmrswtp)); printf("\tPMR Sustained Write Throughput Units (PMRSWTU): %s/second\n", - nvme_register_pmr_pmrszu_to_string(NVME_PMRSWTP_PMRSWTU(pmrswtp))); + nvme_register_unit_to_string(NVME_PMRSWTP_PMRSWTU(pmrswtp))); } static void stdout_registers_pmrmscl(uint32_t pmrmscl) @@ -1420,215 +1439,170 @@ static void stdout_registers_pmrmscu(uint32_t pmrmscu) pmrmscu); } -void stdout_ctrl_registers(void *bar, bool fabrics) +static void stdout_ctrl_register_human(int offset, uint64_t value, bool support) { - uint64_t cap, asq, acq, bpmbl, cmbmsc; - uint32_t vs, intms, intmc, cc, csts, nssr, crto, aqa, cmbsz, cmbloc, bpinfo, - bprsel, cmbsts, pmrcap, pmrctl, pmrsts, pmrebs, pmrswtp, - pmrmscl, pmrmscu; - int human = stdout_print_ops.flags & VERBOSE; - - cap = mmio_read64(bar + NVME_REG_CAP); - vs = mmio_read32(bar + NVME_REG_VS); - intms = mmio_read32(bar + NVME_REG_INTMS); - intmc = mmio_read32(bar + NVME_REG_INTMC); - cc = mmio_read32(bar + NVME_REG_CC); - csts = mmio_read32(bar + NVME_REG_CSTS); - nssr = mmio_read32(bar + NVME_REG_NSSR); - crto = mmio_read32(bar + NVME_REG_CRTO); - aqa = mmio_read32(bar + NVME_REG_AQA); - asq = mmio_read64(bar + NVME_REG_ASQ); - acq = mmio_read64(bar + NVME_REG_ACQ); - cmbloc = mmio_read32(bar + NVME_REG_CMBLOC); - cmbsz = mmio_read32(bar + NVME_REG_CMBSZ); - bpinfo = mmio_read32(bar + NVME_REG_BPINFO); - bprsel = mmio_read32(bar + NVME_REG_BPRSEL); - bpmbl = mmio_read64(bar + NVME_REG_BPMBL); - cmbmsc = mmio_read64(bar + NVME_REG_CMBMSC); - cmbsts = mmio_read32(bar + NVME_REG_CMBSTS); - pmrcap = mmio_read32(bar + NVME_REG_PMRCAP); - pmrctl = mmio_read32(bar + NVME_REG_PMRCTL); - pmrsts = mmio_read32(bar + NVME_REG_PMRSTS); - pmrebs = mmio_read32(bar + NVME_REG_PMREBS); - pmrswtp = mmio_read32(bar + NVME_REG_PMRSWTP); - pmrmscl = mmio_read32(bar + NVME_REG_PMRMSCL); - pmrmscu = mmio_read32(bar + NVME_REG_PMRMSCU); - - if (human) { - if (cap != 0xffffffff) { - printf("cap : %"PRIx64"\n", cap); - stdout_registers_cap((struct nvme_bar_cap *)&cap); - } - if (vs != 0xffffffff) { - printf("version : %x\n", vs); - stdout_registers_version(vs); - } - if (cc != 0xffffffff) { - printf("cc : %x\n", cc); - stdout_registers_cc(cc); - } - if (csts != 0xffffffff) { - printf("csts : %x\n", csts); - stdout_registers_csts(csts); - } - if (nssr != 0xffffffff) { - printf("nssr : %x\n", nssr); - printf("\tNVM Subsystem Reset Control (NSSRC): %u\n\n", - nssr); - } - if (crto != 0xffffffff) { - printf("crto : %x\n", crto); - stdout_registers_crto(crto); - } - if (!fabrics) { - printf("intms : %x\n", intms); - printf("\tInterrupt Vector Mask Set (IVMS): %x\n\n", - intms); - - printf("intmc : %x\n", intmc); - printf("\tInterrupt Vector Mask Clear (IVMC): %x\n\n", - intmc); - printf("aqa : %x\n", aqa); - stdout_registers_aqa(aqa); - - printf("asq : %"PRIx64"\n", asq); - printf("\tAdmin Submission Queue Base (ASQB): %"PRIx64"\n\n", - asq); - - printf("acq : %"PRIx64"\n", acq); - printf("\tAdmin Completion Queue Base (ACQB): %"PRIx64"\n\n", - acq); - - printf("cmbloc : %x\n", cmbloc); - stdout_registers_cmbloc(cmbloc, cmbsz); - - printf("cmbsz : %x\n", cmbsz); - stdout_registers_cmbsz(cmbsz); - - printf("bpinfo : %x\n", bpinfo); - stdout_registers_bpinfo(bpinfo); - - printf("bprsel : %x\n", bprsel); - stdout_registers_bprsel(bprsel); - - printf("bpmbl : %"PRIx64"\n", bpmbl); - stdout_registers_bpmbl(bpmbl); - - printf("cmbmsc : %"PRIx64"\n", cmbmsc); - stdout_registers_cmbmsc(cmbmsc); - - printf("cmbsts : %x\n", cmbsts); - stdout_registers_cmbsts(cmbsts); - - printf("pmrcap : %x\n", pmrcap); - stdout_registers_pmrcap(pmrcap); - - printf("pmrctl : %x\n", pmrctl); - stdout_registers_pmrctl(pmrctl); - - printf("pmrsts : %x\n", pmrsts); - stdout_registers_pmrsts(pmrsts, pmrctl); - - printf("pmrebs : %x\n", pmrebs); - stdout_registers_pmrebs(pmrebs); - - printf("pmrswtp : %x\n", pmrswtp); - stdout_registers_pmrswtp(pmrswtp); - - printf("pmrmscl : %#x\n", pmrmscl); - stdout_registers_pmrmscl(pmrmscl); - - printf("pmrmscu : %#x\n", pmrmscu); - stdout_registers_pmrmscu(pmrmscu); - } - } else { - if (cap != 0xffffffff) - printf("cap : %"PRIx64"\n", cap); - if (vs != 0xffffffff) - printf("version : %x\n", vs); - if (cc != 0xffffffff) - printf("cc : %x\n", cc); - if (csts != 0xffffffff) - printf("csts : %x\n", csts); - if (nssr != 0xffffffff) - printf("nssr : %x\n", nssr); - if (crto != 0xffffffff) - printf("crto : %x\n", crto); - if (!fabrics) { - printf("intms : %x\n", intms); - printf("intmc : %x\n", intmc); - printf("aqa : %x\n", aqa); - printf("asq : %"PRIx64"\n", asq); - printf("acq : %"PRIx64"\n", acq); - printf("cmbloc : %x\n", cmbloc); - printf("cmbsz : %x\n", cmbsz); - printf("bpinfo : %x\n", bpinfo); - printf("bprsel : %x\n", bprsel); - printf("bpmbl : %"PRIx64"\n", bpmbl); - printf("cmbmsc : %"PRIx64"\n", cmbmsc); - printf("cmbsts : %x\n", cmbsts); - printf("pmrcap : %x\n", pmrcap); - printf("pmrctl : %x\n", pmrctl); - printf("pmrsts : %x\n", pmrsts); - printf("pmrebs : %x\n", pmrebs); - printf("pmrswtp : %x\n", pmrswtp); - printf("pmrmscl : %#x\n", pmrmscl); - printf("pmrmscu : %#x\n", pmrmscu); - } - } -} - -static void stdout_single_property(int offset, uint64_t value64) -{ - int human = stdout_print_ops.flags & VERBOSE; - uint32_t value32 = (uint32_t)value64; - - if (!human) { - if (nvme_is_64bit_reg(offset)) - printf("property: %#02x (%s), value: %#"PRIx64"\n", - offset, nvme_register_to_string(offset), value64); - else - printf("property: %#02x (%s), value: %#x\n", offset, - nvme_register_to_string(offset), value32); - return; - } - switch (offset) { case NVME_REG_CAP: - printf("cap : %"PRIx64"\n", value64); - stdout_registers_cap((struct nvme_bar_cap *)&value64); + stdout_registers_cap((struct nvme_bar_cap *)&value); break; case NVME_REG_VS: - printf("version : %x\n", value32); - stdout_registers_version(value32); + stdout_registers_version(value); + break; + case NVME_REG_INTMS: + printf("\tInterrupt Vector Mask Set (IVMS): %#"PRIx64"\n\n", value); + break; + case NVME_REG_INTMC: + printf("\tInterrupt Vector Mask Clear (IVMC): %#"PRIx64"\n\n", value); break; case NVME_REG_CC: - printf("cc : %x\n", value32); - stdout_registers_cc(value32); + stdout_registers_cc(value); break; case NVME_REG_CSTS: - printf("csts : %x\n", value32); - stdout_registers_csts(value32); + stdout_registers_csts(value); break; case NVME_REG_NSSR: - printf("nssr : %x\n", value32); - printf("\tNVM Subsystem Reset Control (NSSRC): %u\n\n", value32); + printf("\tNVM Subsystem Reset Control (NSSRC): %"PRIu64"\n\n", value); + break; + case NVME_REG_AQA: + stdout_registers_aqa(value); + break; + case NVME_REG_ASQ: + printf("\tAdmin Submission Queue Base (ASQB): %#"PRIx64"\n\n", value); + break; + case NVME_REG_ACQ: + printf("\tAdmin Completion Queue Base (ACQB): %#"PRIx64"\n\n", value); + break; + case NVME_REG_CMBLOC: + stdout_registers_cmbloc(value, support); + break; + case NVME_REG_CMBSZ: + stdout_registers_cmbsz(value); + break; + case NVME_REG_BPINFO: + stdout_registers_bpinfo(value); + break; + case NVME_REG_BPRSEL: + stdout_registers_bprsel(value); + break; + case NVME_REG_BPMBL: + stdout_registers_bpmbl(value); + break; + case NVME_REG_CMBMSC: + stdout_registers_cmbmsc(value); + break; + case NVME_REG_CMBSTS: + stdout_registers_cmbsts(value); + break; + case NVME_REG_CMBEBS: + stdout_registers_cmbebs(value); + break; + case NVME_REG_CMBSWTP: + stdout_registers_cmbswtp(value); break; case NVME_REG_NSSD: - printf("nssd : %x\n", value32); - stdout_registers_nssd(value32); + stdout_registers_nssd(value); break; case NVME_REG_CRTO: - printf("crto : %x\n", value32); - stdout_registers_crto(value32); + stdout_registers_crto(value); + break; + case NVME_REG_PMRCAP: + stdout_registers_pmrcap(value); + break; + case NVME_REG_PMRCTL: + stdout_registers_pmrctl(value); + break; + case NVME_REG_PMRSTS: + stdout_registers_pmrsts(value, support); + break; + case NVME_REG_PMREBS: + stdout_registers_pmrebs(value); + break; + case NVME_REG_PMRSWTP: + stdout_registers_pmrswtp(value); + break; + case NVME_REG_PMRMSCL: + stdout_registers_pmrmscl(value); + break; + case NVME_REG_PMRMSCU: + stdout_registers_pmrmscu(value); break; default: - printf("unknown property: %#02x (%s), value: %"PRIx64"\n", - offset, nvme_register_to_string(offset), value64); + printf("unknown register: %#04x (%s), value: %#"PRIx64"\n", + offset, nvme_register_to_string(offset), value); break; } } +static void stdout_ctrl_register_common(int offset, uint64_t value, bool fabrics) +{ + bool human = !!(stdout_print_ops.flags & VERBOSE); + const char *name = nvme_register_to_string(offset); + const char *type = fabrics ? "property" : "register"; + + if (human) { + printf("%s: %#"PRIx64"\n", name, value); + stdout_ctrl_register_human(offset, value, true); + return; + } + + printf("%s: %#04x (%s), value: %#"PRIx64"\n", type, offset, + name, value); +} + +static void stdout_ctrl_register(int offset, uint64_t value) +{ + stdout_ctrl_register_common(offset, value, false); +} + +static void stdout_ctrl_register_support(void *bar, bool fabrics, int offset, bool human, + bool support) +{ + uint64_t value = nvme_is_64bit_reg(offset) ? mmio_read64(bar + offset) : + mmio_read32(bar + offset); + + if (fabrics && value == -1) + return; + + printf("%-8s: ", nvme_register_symbol_to_string(offset)); + + printf("%#"PRIx64"\n", value); + + if (human) + stdout_ctrl_register_human(offset, value, support); +} + +void stdout_ctrl_registers(void *bar, bool fabrics) +{ + uint32_t value; + bool human = !!(stdout_print_ops.flags & VERBOSE); + int offset; + bool support; + + for (offset = NVME_REG_CAP; offset <= NVME_REG_PMRMSCU; offset += get_reg_size(offset)) { + if (!nvme_is_ctrl_reg(offset) || (fabrics && !nvme_is_fabrics_reg(offset))) + continue; + switch (offset) { + case NVME_REG_CMBLOC: + value = mmio_read32(bar + NVME_REG_CMBSZ); + support = nvme_registers_cmbloc_support(value); + break; + case NVME_REG_PMRSTS: + value = mmio_read32(bar + NVME_REG_PMRCTL); + support = nvme_registers_pmrctl_ready(value); + break; + default: + support = true; + break; + } + stdout_ctrl_register_support(bar, fabrics, offset, human, support); + } +} + +static void stdout_single_property(int offset, uint64_t value) +{ + stdout_ctrl_register_common(offset, value, true); +} + static void stdout_status(int status) { int val; @@ -5120,6 +5094,7 @@ static struct print_ops stdout_print_ops = { .phy_rx_eom_log = stdout_phy_rx_eom_log, .ctrl_list = stdout_list_ctrl, .ctrl_registers = stdout_ctrl_registers, + .ctrl_register = stdout_ctrl_register, .directive = stdout_directive_show, .discovery_log = stdout_discovery_log, .effects_log_list = stdout_effects_log_pages, diff --git a/nvme-print.c b/nvme-print.c index 0a8deb2a8b..ef4b65342c 100644 --- a/nvme-print.c +++ b/nvme-print.c @@ -351,15 +351,67 @@ const char *nvme_register_pmr_hsts_to_string(__u8 hsts) } } -const char *nvme_register_pmr_pmrszu_to_string(__u8 pmrszu) +const char *nvme_register_unit_to_string(__u8 unit) { - switch (pmrszu) { - case 0: return "Bytes"; - case 1: return "One KB"; - case 2: return "One MB"; - case 3: return "One GB"; - default: return "Reserved"; + switch (unit) { + case 0: + return "Bytes"; + case 1: + return "One KB"; + case 2: + return "One MB"; + case 3: + return "One GB"; + default: + break; + } + + return "Reserved"; +} + +bool nvme_is_fabrics_reg(int offset) +{ + switch (offset) { + case NVME_REG_CAP: + case NVME_REG_VS: + case NVME_REG_CC: + case NVME_REG_CSTS: + case NVME_REG_NSSR: + case NVME_REG_CRTO: + return true; + default: + break; } + + return false; +} + +bool nvme_registers_cmbloc_support(__u32 cmbsz) +{ + return !!cmbsz; +} + +bool nvme_registers_pmrctl_ready(__u32 pmrctl) +{ + return NVME_PMRCTL_EN(pmrctl); +} + +void nvme_show_ctrl_register(void *bar, bool fabrics, int offset, enum nvme_print_flags flags) +{ + uint64_t value; + + if (fabrics && !nvme_is_fabrics_reg(offset)) { + printf("register: %#04x (%s) not fabrics\n", offset, + nvme_register_to_string(offset)); + return; + } + + if (nvme_is_64bit_reg(offset)) + value = mmio_read64(bar + offset); + else + value = mmio_read32(bar + offset); + + nvme_print(ctrl_register, flags, offset, value); } void nvme_show_ctrl_registers(void *bar, bool fabrics, enum nvme_print_flags flags) @@ -761,20 +813,67 @@ const char *nvme_feature_to_string(enum nvme_features_id feature) const char *nvme_register_to_string(int reg) { switch (reg) { - case NVME_REG_CAP: return "Controller Capabilities"; - case NVME_REG_VS: return "Version"; - case NVME_REG_INTMS: return "Interrupt Vector Mask Set"; - case NVME_REG_INTMC: return "Interrupt Vector Mask Clear"; - case NVME_REG_CC: return "Controller Configuration"; - case NVME_REG_CSTS: return "Controller Status"; - case NVME_REG_NSSR: return "NVM Subsystem Reset"; - case NVME_REG_AQA: return "Admin Queue Attributes"; - case NVME_REG_ASQ: return "Admin Submission Queue Base Address"; - case NVME_REG_ACQ: return "Admin Completion Queue Base Address"; - case NVME_REG_CMBLOC: return "Controller Memory Buffer Location"; - case NVME_REG_CMBSZ: return "Controller Memory Buffer Size"; - default: return "Unknown"; + case NVME_REG_CAP: + return "Controller Capabilities"; + case NVME_REG_VS: + return "Version"; + case NVME_REG_INTMS: + return "Interrupt Vector Mask Set"; + case NVME_REG_INTMC: + return "Interrupt Vector Mask Clear"; + case NVME_REG_CC: + return "Controller Configuration"; + case NVME_REG_CSTS: + return "Controller Status"; + case NVME_REG_NSSR: + return "NVM Subsystem Reset"; + case NVME_REG_AQA: + return "Admin Queue Attributes"; + case NVME_REG_ASQ: + return "Admin Submission Queue Base Address"; + case NVME_REG_ACQ: + return "Admin Completion Queue Base Address"; + case NVME_REG_CMBLOC: + return "Controller Memory Buffer Location"; + case NVME_REG_CMBSZ: + return "Controller Memory Buffer Size"; + case NVME_REG_BPINFO: + return "Boot Partition Information"; + case NVME_REG_BPRSEL: + return "Boot Partition Read Select"; + case NVME_REG_BPMBL: + return "Boot Partition Memory Buffer Location"; + case NVME_REG_CMBMSC: + return "Controller Memory Buffer Memory Space Control"; + case NVME_REG_CMBSTS: + return "Controller Memory Buffer Status"; + case NVME_REG_CMBEBS: + return "Controller Memory Buffer Elasticity Buffer Size"; + case NVME_REG_CMBSWTP: + return "Controller Memory Buffer Sustained Write Throughput"; + case NVME_REG_NSSD: + return "NVM Subsystem Shutdown"; + case NVME_REG_CRTO: + return "Controller Ready Timeouts"; + case NVME_REG_PMRCAP: + return "Persistent Memory Region Capabilities"; + case NVME_REG_PMRCTL: + return "Persistent Memory Region Control"; + case NVME_REG_PMRSTS: + return "Persistent Memory Region Status"; + case NVME_REG_PMREBS: + return "Persistent Memory Region Elasticity Buffer Size"; + case NVME_REG_PMRSWTP: + return "Persistent Memory Region Sustained Write Throughput"; + case NVME_REG_PMRMSCL: + return "Persistent Memory Region Memory Space Control Lower"; + case NVME_REG_PMRMSCU: + return "Persistent Memory Region Memory Space Control Upper"; + default: + break; } + + return "Unknown"; } const char *nvme_select_to_string(int sel) @@ -981,6 +1080,72 @@ const char *nvme_pel_ehai_pit_to_string(enum nvme_pel_ehai_pit pit) return "Reserved"; } +const char *nvme_register_symbol_to_string(int offset) +{ + switch (offset) { + case NVME_REG_CAP: + return "cap"; + case NVME_REG_VS: + return "version"; + case NVME_REG_INTMS: + return "intms"; + case NVME_REG_INTMC: + return "intmc"; + case NVME_REG_CC: + return "cc"; + case NVME_REG_CSTS: + return "csts"; + case NVME_REG_NSSR: + return "nssr"; + case NVME_REG_AQA: + return "aqa"; + case NVME_REG_ASQ: + return "asq"; + case NVME_REG_ACQ: + return "acq"; + case NVME_REG_CMBLOC: + return "cmbloc"; + case NVME_REG_CMBSZ: + return "cmbsz"; + case NVME_REG_BPINFO: + return "bpinfo"; + case NVME_REG_BPRSEL: + return "bprsel"; + case NVME_REG_BPMBL: + return "bpmbl"; + case NVME_REG_CMBMSC: + return "cmbmsc"; + case NVME_REG_CMBSTS: + return "cmbsts"; + case NVME_REG_CMBEBS: + return "cmbebs"; + case NVME_REG_CMBSWTP: + return "cmbswtp"; + case NVME_REG_NSSD: + return "nssd"; + case NVME_REG_CRTO: + return "crto"; + case NVME_REG_PMRCAP: + return "pmrcap"; + case NVME_REG_PMRCTL: + return "pmrctl"; + case NVME_REG_PMRSTS: + return "pmrsts"; + case NVME_REG_PMREBS: + return "pmrebs"; + case NVME_REG_PMRSWTP: + return "pmrswtp"; + case NVME_REG_PMRMSCL: + return "pmrmscl"; + case NVME_REG_PMRMSCU: + return "pmrmscu"; + default: + break; + } + + return "unknown"; +} + void nvme_feature_show(enum nvme_features_id fid, int sel, unsigned int result) { nvme_print(show_feature, NORMAL, fid, sel, result); diff --git a/nvme-print.h b/nvme-print.h index d2a32f5a05..71aa0bfb71 100644 --- a/nvme-print.h +++ b/nvme-print.h @@ -26,6 +26,7 @@ struct print_ops { void (*phy_rx_eom_log)(struct nvme_phy_rx_eom_log *log, __u16 controller); void (*ctrl_list)(struct nvme_ctrl_list *ctrl_list); void (*ctrl_registers)(void *bar, bool fabrics); + void (*ctrl_register)(int offset, uint64_t value); void (*directive)(__u8 type, __u8 oper, __u16 spec, __u32 nsid, __u32 result, void *buf, __u32 len); void (*discovery_log)(struct nvmf_discovery_log *log, int numrec); void (*effects_log_list)(struct list_head *list); @@ -205,6 +206,7 @@ void nvme_show_media_unit_stat_log(struct nvme_media_unit_stat_log *mus, void nvme_show_supported_cap_config_log(struct nvme_supported_cap_config_list_log *caplog, enum nvme_print_flags flags); void nvme_show_ctrl_registers(void *bar, bool fabrics, enum nvme_print_flags flags); +void nvme_show_ctrl_register(void *bar, bool fabrics, int offset, enum nvme_print_flags flags); void nvme_show_single_property(int offset, uint64_t prop, enum nvme_print_flags flags); void nvme_show_id_ns_descs(void *data, unsigned nsid, enum nvme_print_flags flags); void nvme_show_lba_status(struct nvme_lba_status *list, unsigned long len, @@ -291,9 +293,10 @@ const char *nvme_log_to_string(__u8 lid); const char *nvme_nss_hw_error_to_string(__u16 error_code); const char *nvme_pel_event_to_string(int type); const char *nvme_register_pmr_hsts_to_string(__u8 hsts); -const char *nvme_register_pmr_pmrszu_to_string(__u8 pmrszu); +const char *nvme_register_unit_to_string(__u8 unit); const char *nvme_register_szu_to_string(__u8 szu); const char *nvme_register_to_string(int reg); +const char *nvme_register_symbol_to_string(int offset); const char *nvme_resv_notif_to_string(__u8 type); const char *nvme_select_to_string(int sel); const char *nvme_sstat_status_to_string(__u16 status); @@ -312,4 +315,7 @@ void nvme_show_perror(const char *msg); void nvme_show_error_status(int status, const char *msg, ...); void nvme_show_init(void); void nvme_show_finish(void); +bool nvme_is_fabrics_reg(int offset); +bool nvme_registers_cmbloc_support(__u32 cmbsz); +bool nvme_registers_pmrctl_ready(__u32 pmrctl); #endif /* NVME_PRINT_H */ diff --git a/nvme.c b/nvme.c index c13695e8e3..3fa4a58b65 100644 --- a/nvme.c +++ b/nvme.c @@ -108,6 +108,61 @@ struct passthru_config { bool latency; }; +struct get_reg_config { + int offset; + bool human_readable; + bool cap; + bool vs; + bool intms; + bool intmc; + bool cc; + bool csts; + bool nssr; + bool aqa; + bool asq; + bool acq; + bool cmbloc; + bool cmbsz; + bool bpinfo; + bool bprsel; + bool bpmbl; + bool cmbmsc; + bool cmbsts; + bool cmbebs; + bool cmbswtp; + bool nssd; + bool crto; + bool pmrcap; + bool pmrctl; + bool pmrsts; + bool pmrebs; + bool pmrswtp; + bool pmrmscl; + bool pmrmscu; + bool fabrics; +}; + +struct set_reg_config { + int offset; + bool mmio32; + __u64 value; + __u32 intms; + __u32 intmc; + __u32 cc; + __u32 csts; + __u32 nssr; + __u32 aqa; + __u64 asq; + __u64 acq; + __u32 bprsel; + __u64 bpmbl; + __u64 cmbmsc; + __u32 nssd; + __u32 pmrctl; + __u32 pmrmscl; + __u32 pmrmscu; +}; + #define NVME_ARGS(n, ...) \ struct argconfig_commandline_options n[] = { \ OPT_INCR("verbose", 'v', &verbose_level, verbose), \ @@ -186,11 +241,27 @@ static const char *uuid_index_specify = "specify uuid index"; static const char *verbose = "Increase output verbosity"; static const char dash[51] = {[0 ... 49] = '=', '\0'}; static const char space[51] = {[0 ... 49] = ' ', '\0'}; +static const char *offset = "offset of the requested register"; +static const char *intms = "INTMS=0xc register offset"; +static const char *intmc = "INTMC=0x10 register offset"; +static const char *cc = "CC=0x14 register offset"; +static const char *csts = "CSTS=0x1c register offset"; +static const char *nssr = "NSSR=0x20 register offset"; +static const char *aqa = "AQA=0x24 register offset"; +static const char *asq = "ASQ=0x28 register offset"; +static const char *acq = "ACQ=0x30 register offset"; +static const char *bprsel = "BPRSEL=0x44 register offset"; +static const char *bpmbl = "BPMBL=0x48 register offset"; +static const char *cmbmsc = "CMBMSC=0x50 register offset"; +static const char *nssd = "NSSD=0x64 register offset"; +static const char *pmrctl = "PMRCTL=0xe04 register offset"; +static const char *pmrmscl = "PMRMSCL=0xe14 register offset"; +static const char *pmrmscu = "PMRMSCU=0xe18 register offset"; static char *output_format_val = "normal"; int verbose_level; -static void *mmap_registers(struct nvme_dev *dev); +static void *mmap_registers(struct nvme_dev *dev, bool writable); const char *nvme_strerror(int errnum) { @@ -983,7 +1054,7 @@ static int get_effects_log(int argc, char **argv, struct command *cmd, struct pl if (cfg.csi < 0) { __u64 cap; - bar = mmap_registers(dev); + bar = mmap_registers(dev, false); if (bar) { cap = mmio_read64(bar + NVME_REG_CAP); @@ -5201,42 +5272,62 @@ static int sanitize_cmd(int argc, char **argv, struct command *cmd, struct plugi return err; } -static int nvme_get_properties(int fd, void **pbar) +static int nvme_get_single_property(int fd, struct get_reg_config *cfg, __u64 *value) +{ + int err; + struct nvme_get_property_args args = { + .args_size = sizeof(args), + .fd = fd, + .offset = cfg->offset, + .value = value, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + }; + + err = nvme_get_property(&args); + if (!err) + return 0; + + if (!cfg->fabrics && + nvme_status_equals(err, NVME_STATUS_TYPE_NVME, NVME_SC_INVALID_FIELD)) { + *value = -1; + return 0; + } + + if (cfg->fabrics && err > 0) + nvme_show_status(err); + else + nvme_show_error("get-property: %s", nvme_strerror(errno)); + + return err; +} + +static int nvme_get_properties(int fd, void **pbar, struct get_reg_config *cfg) { - int offset, err, size = getpagesize(); + int err, size = getpagesize(); + bool is_64bit = false; __u64 value; - void *bar = malloc(size); + void *bar; + int offset; + bar = malloc(size); if (!bar) { nvme_show_error("malloc: %s", strerror(errno)); - return -1; + return -errno; } memset(bar, 0xff, size); - for (offset = NVME_REG_CAP; offset <= NVME_REG_CMBSZ;) { - struct nvme_get_property_args args = { - .args_size = sizeof(args), - .fd = fd, - .offset = offset, - .value = &value, - .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, - }; - - err = nvme_get_property(&args); - if (nvme_status_equals(err, NVME_STATUS_TYPE_NVME, NVME_SC_INVALID_FIELD)) { - err = 0; - value = -1; - } else if (err) { - nvme_show_error("get-property: %s", nvme_strerror(errno)); + for (offset = NVME_REG_CAP; offset <= NVME_REG_CMBSZ; + offset += is_64bit ? sizeof(uint64_t) : sizeof(uint32_t)) { + cfg->offset = offset; + err = nvme_get_single_property(fd, cfg, &value); + if (err) break; - } - if (nvme_is_64bit_reg(offset)) { - *(uint64_t *)(bar + offset) = value; - offset += 8; - } else { - *(uint32_t *)(bar + offset) = value; - offset += 4; - } + + is_64bit = nvme_is_64bit_reg(cfg->offset); + if (is_64bit) + *(uint64_t *)(bar + cfg->offset) = value; + else + *(uint32_t *)(bar + cfg->offset) = value; } if (err) @@ -5247,14 +5338,18 @@ static int nvme_get_properties(int fd, void **pbar) return err; } -static void *mmap_registers(struct nvme_dev *dev) +static void *mmap_registers(struct nvme_dev *dev, bool writable) { char path[512]; void *membase; int fd; + int prot = PROT_READ; + + if (writable) + prot |= PROT_WRITE; sprintf(path, "/sys/class/nvme/%s/device/resource0", dev->name); - fd = open(path, O_RDONLY); + fd = open(path, writable ? O_RDWR : O_RDONLY); if (fd < 0) { if (log_level >= LOG_DEBUG) nvme_show_error("%s did not find a pci resource, open failed %s", @@ -5262,7 +5357,7 @@ static void *mmap_registers(struct nvme_dev *dev) return NULL; } - membase = mmap(NULL, getpagesize(), PROT_READ, MAP_SHARED, fd, 0); + membase = mmap(NULL, getpagesize(), prot, MAP_SHARED, fd, 0); if (membase == MAP_FAILED) { if (log_level >= LOG_DEBUG) { fprintf(stderr, "%s failed to map. ", dev->name); @@ -5288,11 +5383,7 @@ static int show_registers(int argc, char **argv, struct command *cmd, struct plu void *bar; int err; - struct config { - bool human_readable; - }; - - struct config cfg = { + struct get_reg_config cfg = { .human_readable = false, }; @@ -5312,9 +5403,9 @@ static int show_registers(int argc, char **argv, struct command *cmd, struct plu if (cfg.human_readable) flags |= VERBOSE; - bar = mmap_registers(dev); + bar = mmap_registers(dev, false); if (!bar) { - err = nvme_get_properties(dev_fd(dev), &bar); + err = nvme_get_properties(dev_fd(dev), &bar, &cfg); if (err) return err; fabrics = true; @@ -5329,6 +5420,574 @@ static int show_registers(int argc, char **argv, struct command *cmd, struct plu return 0; } +int get_reg_size(int offset) +{ + return nvme_is_64bit_reg(offset) ? sizeof(uint64_t) : sizeof(uint32_t); +} + +static bool is_reg_selected(struct get_reg_config *cfg, int offset) +{ + switch (offset) { + case NVME_REG_CAP: + return cfg->cap; + case NVME_REG_VS: + return cfg->vs; + case NVME_REG_INTMS: + return cfg->intms; + case NVME_REG_INTMC: + return cfg->intmc; + case NVME_REG_CC: + return cfg->cc; + case NVME_REG_CSTS: + return cfg->csts; + case NVME_REG_NSSR: + return cfg->nssr; + case NVME_REG_AQA: + return cfg->aqa; + case NVME_REG_ASQ: + return cfg->asq; + case NVME_REG_ACQ: + return cfg->acq; + case NVME_REG_CMBLOC: + return cfg->cmbloc; + case NVME_REG_CMBSZ: + return cfg->cmbsz; + case NVME_REG_BPINFO: + return cfg->bpinfo; + case NVME_REG_BPRSEL: + return cfg->bprsel; + case NVME_REG_BPMBL: + return cfg->bpmbl; + case NVME_REG_CMBMSC: + return cfg->cmbmsc; + case NVME_REG_CMBSTS: + return cfg->cmbsts; + case NVME_REG_CMBEBS: + return cfg->cmbebs; + case NVME_REG_CMBSWTP: + return cfg->cmbswtp; + case NVME_REG_NSSD: + return cfg->nssd; + case NVME_REG_CRTO: + return cfg->crto; + case NVME_REG_PMRCAP: + return cfg->pmrcap; + case NVME_REG_PMRCTL: + return cfg->pmrctl; + case NVME_REG_PMRSTS: + return cfg->pmrsts; + case NVME_REG_PMREBS: + return cfg->pmrebs; + case NVME_REG_PMRSWTP: + return cfg->pmrswtp; + case NVME_REG_PMRMSCL: + return cfg->pmrmscl; + case NVME_REG_PMRMSCU: + return cfg->pmrmscu; + default: + break; + } + + return false; +} + +static int get_register_properties(int fd, void **pbar, struct get_reg_config *cfg) +{ + int offset = NVME_REG_CRTO; + __u64 value; + int size; + int err; + void *bar; + struct nvme_get_property_args args = { + .args_size = sizeof(args), + .fd = fd, + .value = &value, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + }; + + size = offset + get_reg_size(offset); + bar = malloc(size); + if (!bar) { + nvme_show_error("malloc: %s", strerror(errno)); + return -1; + } + + for (offset = NVME_REG_CAP; offset <= NVME_REG_CRTO; offset += get_reg_size(offset)) { + if ((cfg->offset != offset && !is_reg_selected(cfg, offset)) || + !nvme_is_fabrics_reg(offset)) + continue; + + args.offset = offset; + err = nvme_get_property(&args); + if (nvme_status_equals(err, NVME_STATUS_TYPE_NVME, NVME_SC_INVALID_FIELD)) { + value = -1; + } else if (err) { + nvme_show_error("get-property: %s", nvme_strerror(errno)); + free(bar); + return err; + } + + if (nvme_is_64bit_reg(offset)) + *(uint64_t *)(bar + offset) = value; + else + *(uint32_t *)(bar + offset) = value; + } + + *pbar = bar; + + return 0; +} + +bool nvme_is_ctrl_reg(int offset) +{ + switch (offset) { + case NVME_REG_CAP: + case NVME_REG_VS: + case NVME_REG_INTMS: + case NVME_REG_INTMC: + case NVME_REG_CC: + case NVME_REG_CSTS: + case NVME_REG_NSSR: + case NVME_REG_AQA: + case NVME_REG_ASQ: + case NVME_REG_ACQ: + case NVME_REG_CMBLOC: + case NVME_REG_CMBSZ: + case NVME_REG_BPINFO: + case NVME_REG_BPRSEL: + case NVME_REG_BPMBL: + case NVME_REG_CMBMSC: + case NVME_REG_CMBSTS: + case NVME_REG_CMBEBS: + case NVME_REG_CMBSWTP: + case NVME_REG_NSSD: + case NVME_REG_CRTO: + case NVME_REG_PMRCAP: + case NVME_REG_PMRCTL: + case NVME_REG_PMRSTS: + case NVME_REG_PMREBS: + case NVME_REG_PMRSWTP: + case NVME_REG_PMRMSCL: + case NVME_REG_PMRMSCU: + return true; + default: + break; + } + + return false; +} + +static bool get_register_offset(void *bar, bool fabrics, struct get_reg_config *cfg, + enum nvme_print_flags flags) +{ + bool offset_matched = cfg->offset >= 0; + int offset; + + if (offset_matched) + nvme_show_ctrl_register(bar, fabrics, cfg->offset, flags); + + for (offset = NVME_REG_CAP; offset <= NVME_REG_PMRMSCU; offset += get_reg_size(offset)) { + if (!nvme_is_ctrl_reg(offset) || offset == cfg->offset || !is_reg_selected(cfg, offset)) + continue; + nvme_show_ctrl_register(bar, fabrics, offset, flags); + if (!offset_matched) + offset_matched = true; + } + + return offset_matched; +} + +static int get_register(int argc, char **argv, struct command *cmd, struct plugin *plugin) +{ + const char *desc = "Reads and shows the defined NVMe controller register.\n" + "Register offset must be one of:\n" + "CAP=0x0, VS=0x8, INTMS=0xc, INTMC=0x10, CC=0x14, CSTS=0x1c,\n" + "NSSR=0x20, AQA=0x24, ASQ=0x28, ACQ=0x30, CMBLOC=0x38,\n" + "CMBSZ=0x3c, BPINFO=0x40, BPRSEL=0x44, BPMBL=0x48, CMBMSC=0x50,\n" + "CMBSTS=0x58, CRTO=0x68, PMRCAP=0xe00, PMRCTL=0xe04,\n" + "PMRSTS=0xe08, PMREBS=0xe0c, PMRSWTP=0xe10, PMRMSCL=0xe14, PMRMSCU=0xe18"; + const char *human_readable = "show register in readable format"; + const char *cap = "CAP=0x0 register offset"; + const char *vs = "VS=0x8 register offset"; + const char *cmbloc = "CMBLOC=0x38 register offset"; + const char *cmbsz = "CMBSZ=0x3c register offset"; + const char *bpinfo = "BPINFO=0x40 register offset"; + const char *cmbsts = "CMBSTS=0x58 register offset"; + const char *cmbebs = "CMBEBS=0x5c register offset"; + const char *cmbswtp = "CMBSWTP=0x60 register offset"; + const char *crto = "CRTO=0x68 register offset"; + const char *pmrcap = "PMRCAP=0xe00 register offset"; + const char *pmrsts = "PMRSTS=0xe08 register offset"; + const char *pmrebs = "PMREBS=0xe0c register offset"; + const char *pmrswtp = "PMRSWTP=0xe10 register offset"; + const char *pmrmscl = "PMRMSCL=0xe14 register offset"; + const char *pmrmscu = "PMRMSCU=0xe18 register offset"; + + _cleanup_nvme_dev_ struct nvme_dev *dev = NULL; + int err; + enum nvme_print_flags flags; + bool fabrics = false; + + void *bar; + + struct get_reg_config cfg = { + .offset = -1, + }; + + NVME_ARGS(opts, + OPT_UINT("offset", 'O', &cfg.offset, offset), + OPT_FLAG("human-readable", 'H', &cfg.human_readable, human_readable), + OPT_FLAG("cap", 0, &cfg.cap, cap), + OPT_FLAG("vs", 0, &cfg.vs, vs), + OPT_FLAG("cmbloc", 0, &cfg.cmbloc, cmbloc), + OPT_FLAG("cmbsz", 0, &cfg.cmbsz, cmbsz), + OPT_FLAG("bpinfo", 0, &cfg.bpinfo, bpinfo), + OPT_FLAG("cmbsts", 0, &cfg.cmbsts, cmbsts), + OPT_FLAG("cmbebs", 0, &cfg.cmbebs, cmbebs), + OPT_FLAG("cmbswtp", 0, &cfg.cmbswtp, cmbswtp), + OPT_FLAG("crto", 0, &cfg.crto, crto), + OPT_FLAG("pmrcap", 0, &cfg.pmrcap, pmrcap), + OPT_FLAG("pmrsts", 0, &cfg.pmrsts, pmrsts), + OPT_FLAG("pmrebs", 0, &cfg.pmrebs, pmrebs), + OPT_FLAG("pmrswtp", 0, &cfg.pmrswtp, pmrswtp), + OPT_FLAG("intms", 0, &cfg.intms, intms), + OPT_FLAG("intmc", 0, &cfg.intmc, intmc), + OPT_FLAG("cc", 0, &cfg.cc, cc), + OPT_FLAG("csts", 0, &cfg.csts, csts), + OPT_FLAG("nssr", 0, &cfg.nssr, nssr), + OPT_FLAG("aqa", 0, &cfg.aqa, aqa), + OPT_FLAG("asq", 0, &cfg.asq, asq), + OPT_FLAG("acq", 0, &cfg.acq, acq), + OPT_FLAG("bprsel", 0, &cfg.bprsel, bprsel), + OPT_FLAG("bpmbl", 0, &cfg.bpmbl, bpmbl), + OPT_FLAG("cmbmsc", 0, &cfg.cmbmsc, cmbmsc), + OPT_FLAG("nssd", 0, &cfg.nssd, nssd), + OPT_FLAG("pmrctl", 0, &cfg.pmrctl, pmrctl), + OPT_FLAG("pmrmscl", 0, &cfg.pmrmscl, pmrmscl), + OPT_FLAG("pmrmscu", 0, &cfg.pmrmscu, pmrmscu)); + + err = parse_and_open(&dev, argc, argv, desc, opts); + if (err) + return err; + + err = validate_output_format(output_format_val, &flags); + if (err < 0) { + nvme_show_error("Invalid output format"); + return err; + } + + if (cfg.human_readable) + flags |= VERBOSE; + + bar = mmap_registers(dev, false); + if (!bar) { + err = get_register_properties(dev_fd(dev), &bar, &cfg); + if (err) + return err; + fabrics = true; + } + + nvme_show_init(); + + if (!get_register_offset(bar, fabrics, &cfg, flags)) { + nvme_show_error("offset required param"); + err = -EINVAL; + } + + nvme_show_finish(); + + if (fabrics) + free(bar); + else + munmap(bar, getpagesize()); + + return err; +} + +static int nvme_set_single_property(int fd, int offset, uint64_t value) +{ + struct nvme_set_property_args args = { + .args_size = sizeof(args), + .fd = fd, + .offset = offset, + .value = value, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .result = NULL, + }; + int err = nvme_set_property(&args); + + if (err < 0) + nvme_show_error("set-property: %s", nvme_strerror(errno)); + else if (!err) + printf("set-property: %#02x (%s), value: %#"PRIx64"\n", offset, + nvme_register_to_string(offset), value); + else if (err > 0) + nvme_show_status(err); + + return err; +} + +static int set_register_property(int fd, int offset, uint64_t value) +{ + if (!nvme_is_fabrics_reg(offset)) { + printf("register: %#04x (%s) not fabrics\n", offset, + nvme_register_to_string(offset)); + return -EINVAL; + } + + return nvme_set_single_property(fd, offset, value); +} + +static int nvme_set_register(int fd, void *bar, int offset, uint64_t value, bool mmio32) +{ + if (!bar) + return set_register_property(fd, offset, value); + + if (nvme_is_64bit_reg(offset)) + mmio_write64(bar + offset, value, mmio32); + else + mmio_write32(bar + offset, value); + + printf("set-register: %#02x (%s), value: %#"PRIx64"\n", offset, + nvme_register_to_string(offset), value); + + return 0; +} + +static inline int set_register_names_check(struct argconfig_commandline_options *opts, int offset) +{ + switch (offset) { + case NVME_REG_INTMS: + if (argconfig_parse_seen(opts, "intms")) + return -EINVAL; + break; + case NVME_REG_INTMC: + if (argconfig_parse_seen(opts, "intmc")) + return -EINVAL; + break; + case NVME_REG_CC: + if (argconfig_parse_seen(opts, "cc")) + return -EINVAL; + break; + case NVME_REG_CSTS: + if (argconfig_parse_seen(opts, "csts")) + return -EINVAL; + break; + case NVME_REG_NSSR: + if (argconfig_parse_seen(opts, "nssr")) + return -EINVAL; + break; + case NVME_REG_AQA: + if (argconfig_parse_seen(opts, "aqa")) + return -EINVAL; + break; + case NVME_REG_ASQ: + if (argconfig_parse_seen(opts, "asq")) + return -EINVAL; + break; + case NVME_REG_ACQ: + if (argconfig_parse_seen(opts, "acq")) + return -EINVAL; + break; + case NVME_REG_BPRSEL: + if (argconfig_parse_seen(opts, "bprsel")) + return -EINVAL; + break; + case NVME_REG_CMBMSC: + if (argconfig_parse_seen(opts, "cmbmsc")) + return -EINVAL; + break; + case NVME_REG_NSSD: + if (argconfig_parse_seen(opts, "nssd")) + return -EINVAL; + break; + case NVME_REG_PMRCTL: + if (argconfig_parse_seen(opts, "pmrctl")) + return -EINVAL; + break; + case NVME_REG_PMRMSCL: + if (argconfig_parse_seen(opts, "pmrmscl")) + return -EINVAL; + break; + case NVME_REG_PMRMSCU: + if (argconfig_parse_seen(opts, "pmrmscu")) + return -EINVAL; + break; + default: + break; + } + + return 0; +} + +static int set_register_offset(int fd, void *bar, struct argconfig_commandline_options *opts, + struct set_reg_config *cfg) +{ + int err; + + if (!argconfig_parse_seen(opts, "value")) { + nvme_show_error("value required param"); + return -EINVAL; + } + + err = set_register_names_check(opts, cfg->offset); + if (err) { + nvme_show_error("offset duplicated param"); + return err; + } + + err = nvme_set_register(fd, bar, cfg->offset, cfg->value, cfg->mmio32); + if (err) + return err; + + return 0; +} + +static int set_register_names(int fd, void *bar, struct argconfig_commandline_options *opts, + struct set_reg_config *cfg) +{ + int err; + + if (argconfig_parse_seen(opts, "intms")) { + err = nvme_set_register(fd, bar, NVME_REG_INTMS, cfg->intms, cfg->mmio32); + if (err) + return err; + } + + if (argconfig_parse_seen(opts, "intmc")) { + err = nvme_set_register(fd, bar, NVME_REG_INTMC, cfg->intmc, cfg->mmio32); + if (err) + return err; + } + + if (argconfig_parse_seen(opts, "cc")) { + err = nvme_set_register(fd, bar, NVME_REG_CC, cfg->cc, cfg->mmio32); + if (err) + return err; + } + + if (argconfig_parse_seen(opts, "csts")) { + err = nvme_set_register(fd, bar, NVME_REG_CSTS, cfg->csts, cfg->mmio32); + if (err) + return err; + } + + if (argconfig_parse_seen(opts, "nssr")) { + err = nvme_set_register(fd, bar, NVME_REG_NSSR, cfg->nssr, cfg->mmio32); + if (err) + return err; + } + + if (argconfig_parse_seen(opts, "aqa")) { + err = nvme_set_register(fd, bar, NVME_REG_AQA, cfg->aqa, cfg->mmio32); + if (err) + return err; + } + + if (argconfig_parse_seen(opts, "asq")) { + err = nvme_set_register(fd, bar, NVME_REG_ASQ, cfg->asq, cfg->mmio32); + if (err) + return err; + } + + if (argconfig_parse_seen(opts, "acq")) { + err = nvme_set_register(fd, bar, NVME_REG_ACQ, cfg->acq, cfg->mmio32); + if (err) + return err; + } + + if (argconfig_parse_seen(opts, "bprsel")) { + err = nvme_set_register(fd, bar, NVME_REG_BPRSEL, cfg->bprsel, cfg->mmio32); + if (err) + return err; + } + + if (argconfig_parse_seen(opts, "cmbmsc")) { + err = nvme_set_register(fd, bar, NVME_REG_CMBMSC, cfg->cmbmsc, cfg->mmio32); + if (err) + return err; + } + + if (argconfig_parse_seen(opts, "nssd")) { + err = nvme_set_register(fd, bar, NVME_REG_NSSD, cfg->nssd, cfg->mmio32); + if (err) + return err; + } + + if (argconfig_parse_seen(opts, "pmrctl")) { + err = nvme_set_register(fd, bar, NVME_REG_PMRCTL, cfg->pmrctl, cfg->mmio32); + if (err) + return err; + } + + if (argconfig_parse_seen(opts, "pmrmscl")) { + err = nvme_set_register(fd, bar, NVME_REG_PMRMSCL, cfg->pmrmscl, cfg->mmio32); + if (err) + return err; + } + + if (argconfig_parse_seen(opts, "pmrmscu")) { + err = nvme_set_register(fd, bar, NVME_REG_PMRMSCU, cfg->pmrmscu, cfg->mmio32); + if (err) + return err; + } + + return 0; +} + +static int set_register(int argc, char **argv, struct command *cmd, struct plugin *plugin) +{ + const char *desc = "Writes and shows the defined NVMe controller register"; + const char *value = "the value of the register to be set"; + const char *mmio32 = "Access 64-bit registers as 2 32-bit"; + + _cleanup_nvme_dev_ struct nvme_dev *dev = NULL; + int err; + + void *bar; + + struct set_reg_config cfg = { + .offset = -1, + }; + + NVME_ARGS(opts, + OPT_UINT("offset", 'O', &cfg.offset, offset), + OPT_SUFFIX("value", 'V', &cfg.value, value), + OPT_FLAG("mmio32", 'm', &cfg.mmio32, mmio32), + OPT_UINT("intms", 0, &cfg.intms, intms), + OPT_UINT("intmc", 0, &cfg.intmc, intmc), + OPT_UINT("cc", 0, &cfg.cc, cc), + OPT_UINT("csts", 0, &cfg.csts, csts), + OPT_UINT("nssr", 0, &cfg.nssr, nssr), + OPT_UINT("aqa", 0, &cfg.aqa, aqa), + OPT_SUFFIX("asq", 0, &cfg.asq, asq), + OPT_SUFFIX("acq", 0, &cfg.acq, acq), + OPT_UINT("bprsel", 0, &cfg.bprsel, bprsel), + OPT_SUFFIX("bpmbl", 0, &cfg.bpmbl, bpmbl), + OPT_SUFFIX("cmbmsc", 0, &cfg.cmbmsc, cmbmsc), + OPT_UINT("nssd", 0, &cfg.nssd, nssd), + OPT_UINT("pmrctl", 0, &cfg.pmrctl, pmrctl), + OPT_UINT("pmrmscl", 0, &cfg.pmrmscl, pmrmscl), + OPT_UINT("pmrmscu", 0, &cfg.pmrmscu, pmrmscu)); + + err = parse_and_open(&dev, argc, argv, desc, opts); + if (err) + return err; + + bar = mmap_registers(dev, true); + + if (argconfig_parse_seen(opts, "offset")) + err = set_register_offset(dev_fd(dev), bar, opts, &cfg); + + if (!err) + err = set_register_names(dev_fd(dev), bar, opts, &cfg); + + if (bar) + munmap(bar, getpagesize()); + + return err; +} + static int get_property(int argc, char **argv, struct command *cmd, struct plugin *plugin) { const char *desc = "Reads and shows the defined NVMe controller property\n" @@ -5341,14 +6000,10 @@ static int get_property(int argc, char **argv, struct command *cmd, struct plugi __u64 value; int err; - struct config { - int offset; - bool human_readable; - }; - - struct config cfg = { + struct get_reg_config cfg = { .offset = -1, .human_readable = false, + .fabrics = true, }; NVME_ARGS(opts, @@ -5364,20 +6019,9 @@ static int get_property(int argc, char **argv, struct command *cmd, struct plugi return -EINVAL; } - struct nvme_get_property_args args = { - .args_size = sizeof(args), - .fd = dev_fd(dev), - .offset = cfg.offset, - .value = &value, - .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, - }; - err = nvme_get_property(&args); - if (err < 0) - nvme_show_error("get-property: %s", nvme_strerror(errno)); - else if (!err) + err = nvme_get_single_property(dev_fd(dev), &cfg, &value); + if (!err) nvme_show_single_property(cfg.offset, value, cfg.human_readable); - else if (err > 0) - nvme_show_status(err); return err; } @@ -5392,12 +6036,7 @@ static int set_property(int argc, char **argv, struct command *cmd, struct plugi _cleanup_nvme_dev_ struct nvme_dev *dev = NULL; int err; - struct config { - int offset; - int value; - }; - - struct config cfg = { + struct set_reg_config cfg = { .offset = -1, .value = -1, }; @@ -5419,24 +6058,7 @@ static int set_property(int argc, char **argv, struct command *cmd, struct plugi return -EINVAL; } - struct nvme_set_property_args args = { - .args_size = sizeof(args), - .fd = dev_fd(dev), - .offset = cfg.offset, - .value = cfg.value, - .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, - .result = NULL, - }; - err = nvme_set_property(&args); - if (err < 0) - nvme_show_error("set-property: %s", nvme_strerror(errno)); - else if (!err) - printf("set-property: %02x (%s), value: %#08x\n", cfg.offset, - nvme_register_to_string(cfg.offset), cfg.value); - else if (err > 0) - nvme_show_status(err); - - return err; + return nvme_set_single_property(dev_fd(dev), cfg.offset, cfg.value); } static int format_cmd(int argc, char **argv, struct command *cmd, struct plugin *plugin) diff --git a/nvme.h b/nvme.h index d0aca2aee9..7e86afc5d0 100644 --- a/nvme.h +++ b/nvme.h @@ -125,4 +125,6 @@ void d(unsigned char *buf, int len, int width, int group); void d_raw(unsigned char *buf, unsigned len); uint64_t int48_to_long(uint8_t *data); +int get_reg_size(int offset); +bool nvme_is_ctrl_reg(int offset); #endif /* _NVME_H */