diff --git a/Documentation/nvme-id-ns-granularity.txt b/Documentation/nvme-id-ns-granularity.txt old mode 100755 new mode 100644 diff --git a/Documentation/nvme-id-uuid.txt b/Documentation/nvme-id-uuid.txt old mode 100755 new mode 100644 diff --git a/Documentation/nvme-list-secondary.txt b/Documentation/nvme-list-secondary.txt old mode 100755 new mode 100644 diff --git a/Documentation/nvme-nvme-mi-recv.txt b/Documentation/nvme-nvme-mi-recv.txt old mode 100755 new mode 100644 diff --git a/Documentation/nvme-nvme-mi-send.txt b/Documentation/nvme-nvme-mi-send.txt old mode 100755 new mode 100644 diff --git a/Documentation/nvme-set-property.txt b/Documentation/nvme-set-property.txt index 22a513c8b2..ddc267d6b0 100644 --- a/Documentation/nvme-set-property.txt +++ b/Documentation/nvme-set-property.txt @@ -23,8 +23,8 @@ OPTIONS --offset:: The offset of the property. --V:: ---value: +-V :: +--value=:: The value of the property to be set. -o :: diff --git a/Documentation/nvme-virt-mgmt.txt b/Documentation/nvme-virt-mgmt.txt old mode 100755 new mode 100644 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 e90fc4288b..69955f0d04 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' @@ -2234,6 +2236,124 @@ _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' + -c':alias for --cap' + --vs':VS=0x8 register offset' + -V':alias for --vs' + --cmbloc':CMBLOC=0x38 register offset' + -m':alias for --cmbloc' + --cmbsz':CMBSZ=0x3c register offset' + -M':alias for --cmbsz' + --bpinfo':BPINFO=0x40 register offset' + -b':alias for --bpinfo' + --cmbsts':CMBSTS=0x58 register offset' + -S':alias for --cmbsts' + --cmbebs':CMBEBS=0x5c register offset' + -E':alias for --cmbebs' + --cmbswtp':CMBSWTP=0x60 register offset' + -W':alias for --cmbswtp' + --crto':CRTO=0x68 register offset' + -r':alias for --crto' + --pmrcap':PMRCAP=0xe00 register offset' + -P':alias for --pmrcap' + --pmrsts':PMRSTS=0xe08 register offset' + -t':alias for --pmrsts' + --pmrebs':PMREBS=0xe0c register offset' + -e':alias for --pmrebs' + --pmrswtp':PMRSWTP=0xe10 register offset' + -w':alias for --pmrswtp' + --intms':INTMS=0xc register offset' + -i':alias for --intms' + --intmc':INTMC=0x10 register offset' + -I':alias for --intmc' + --cc':CC=0x14 register offset' + -C':alias for --cc' + --csts':CSTS=0x1c register offset' + -T':alias for --csts' + --nssr':NSSR=0x20 register offset' + -n':alias for --nssr' + --aqa':AQA=0x24 register offset' + -a':alias for --aqa' + --asq':ASQ=0x28 register offset' + -A':alias for --asq' + --acq':ACQ=0x30 register offset' + -q':alias for --acq' + --bprsel':BPRSEL=0x44 register offset' + -B':alias for --bprsel' + --bpmbl':BPMBL=0x48 register offset' + -p':alias for --bpmbl' + --cmbmsc':CMBMSC=0x50 register offset' + -s':alias for --cmbmsc' + --nssd':NSSD=0x64 register offset' + -N':alias for --nssd' + --pmrctl':PMRCTL=0xe04 register offset' + -R':alias for --pmrctl' + --pmrmscl':PMRMSCL=0xe14 register offset' + -l':alias for --pmrmscl' + --pmrmscu':PMRMSCU=0xe18 register offset' + -u':alias for --pmrmscu' + --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' + -i':alias for --intms' + --intmc=':INTMC=0x10 register offset' + -I':alias for --intmc' + --cc=':CC=0x14 register offset' + -C':alias for --cc' + --csts=':CSTS=0x1c register offset' + -T':alias for --csts' + --nssr=':NSSR=0x20 register offset' + -n':alias for --nssr' + --aqa=':AQA=0x24 register offset' + -a':alias for --aqa' + --asq=':ASQ=0x28 register offset' + -A':alias for --asq' + --acq=':ACQ=0x30 register offset' + -q':alias for --acq' + --bprsel=':BPRSEL=0x44 register offset' + -B':alias for --bprsel' + --bpmbl=':BPMBL=0x48 register offset' + -p':alias for --bpmbl' + --cmbmsc=':CMBMSC=0x50 register offset' + -s':alias for --cmbmsc' + --nssd=':NSSD=0x64 register offset' + -N':alias for --nssd' + --pmrctl=':PMRCTL=0xe04 register offset' + -R':alias for --pmrctl' + --pmrmscl=':PMRMSCL=0xe14 register offset' + -l':alias for --pmrmscl' + --pmrmscu=':PMRMSCU=0xe18 register offset' + -u':alias for --pmrmscu' + --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 d862aab0ab..35b8415d77 100644 --- a/completions/bash-nvme-completion.sh +++ b/completions/bash-nvme-completion.sh @@ -453,6 +453,23 @@ 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 -c --vs -V \ + --cmbloc -m --cmbsz -M --bpinfo -b --cmbsts -S \ + --cmbebs -E --cmbswtp -W --crto -r --pmrcap -P \ + --pmrsts -t --pmrebs -e --pmrswtp -w --intms -i \ + --intmc -I --cc -C --csts -T --nssr -n --aqa -a \ + --asq -A --acq -q --bprsel -B --bpmbl -p --cmbmsc -s \ + --nssd -N --pmrctl -R --pmrmscl -l --pmrmscu -u \ + --output-format -o --verbose -v" + ;; + "set-reg") + opts+=" --offset, -O --value= -V --mmio32 -M --intms= -i \ + --intmc= -I --cc= -C --csts= -T --nssr= -n --aqa= -a \ + --asq= -A --acq= -q --bprsel= -B --bpmbl= -p \ + --cmbmsc= -s --nssd= -N --pmrctl= -R --pmrmscl= -l \ + --pmrmscu= -u --output-format= -o --verbose= -v" + ;; "version") opts+=$NO_OPTS ;; @@ -1571,7 +1588,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-stdout.c b/nvme-print-stdout.c index 51166d9ef1..03516d4025 100644 --- a/nvme-print-stdout.c +++ b/nvme-print-stdout.c @@ -1227,14 +1227,23 @@ static void stdout_registers_aqa(__u32 aqa) } -static void stdout_registers_cmbloc(__u32 cmbloc, __u32 cmbsz) +static bool stdout_registers_cmbloc_support(__u32 cmbsz) { - static const char *enforced[] = { "Enforced", "Not Enforced" }; + if (cmbsz) + return true; - if (cmbsz == 0) { + return false; +} + +static void stdout_registers_cmbloc(__u32 cmbloc, bool support) +{ + static const char * const enforced[] = { "Enforced", "Not Enforced" }; + + if (!support) { printf("\tController Memory Buffer feature is not supported\n\n"); return; } + printf("\tOffset (OFST): 0x%x (See cmbsz.szu for granularity)\n", (cmbloc & 0xfffff000) >> 12); @@ -1345,11 +1354,34 @@ 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 : %s%s%s", + "memory reads not conflicting with memory writes in the CMB Elasticity Buffer ", + (cmbebs & 0x10) ? "SHALL" : "MAY", + " bypass those memory writes\n"); + printf("\tCMB Elasticity Buffer Size Units (CMBSZU): %s\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", + nvme_register_unit_to_string(cmbswtp & 0xf)); +} + +static void stdout_registers_nssd(__u32 nssd) +{ + printf("\tNVM Subsystem Shutdown Control (NSSC): %x\n\n", nssd); +} + static void stdout_registers_pmrcap(__u32 pmrcap) { - printf("\tController Memory Space Supported (CMSS): "\ - "Referencing PMR with host supplied addresses is %s\n", - ((pmrcap & 0x01000000) >> 24) ? "Supported" : "Not Supported"); + printf("\tController Memory Space Supported (CMSS): %s%s%s", + "Referencing PMR with host supplied addresses is ", + (pmrcap & 0x01000000) >> 24 ? "Supported" : "Not Supported", "\n"); printf("\tPersistent Memory Region Timeout (PMRTO): %x\n", (pmrcap & 0x00ff0000) >> 16); printf("\tPersistent Memory Region Write Barrier Mechanisms (PMRWBM): %x\n", @@ -1364,34 +1396,41 @@ static void stdout_registers_pmrcap(__u32 pmrcap) (pmrcap & 0x00000008) ? "supported" : "not supported"); } +static bool stdout_registers_pmrctl_ready(__u32 pmrctl) +{ + if (pmrctl & 1) + return true; + + return false; +} + static void stdout_registers_pmrctl(__u32 pmrctl) { - printf("\tEnable (EN): PMR is %s\n", (pmrctl & 0x00000001) ? - "READY" : "Disabled"); + printf("\tEnable (EN): PMR is %s\n", + stdout_registers_pmrctl_ready(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", - (pmrsts & 0x00001000) >> 12); + printf("\tController Base Address Invalid (CBAI): %x\n", (pmrsts & 0x1000) >> 12); printf("\tHealth Status (HSTS): %s\n", - nvme_register_pmr_hsts_to_string((pmrsts & 0x00000e00) >> 9)); - printf("\tNot Ready (NRDY): "\ - "The Persistent Memory Region is %s to process "\ - "PCI Express memory read and write requests\n", - (pmrsts & 0x00000100) == 0 && (pmrctl & 0x00000001) ? - "READY" : "Not Ready"); - printf("\tError (ERR): %x\n", (pmrsts & 0x000000ff)); + nvme_register_pmr_hsts_to_string((pmrsts & 0xe00) >> 9)); + printf("\tNot Ready (NRDY): %s%s%s%s", + "The Persistent Memory Region is ", + !(pmrsts & 0x100) && ready ? "READY" : "Not Ready", " to process ", + "PCI Express memory read and write requests\n"); + printf("\tError (ERR): %x\n", (pmrsts & 0xff)); } static void stdout_registers_pmrebs(__u32 pmrebs) { printf("\tPMR Elasticity Buffer Size Base (PMRWBZ): %x\n", (pmrebs & 0xffffff00) >> 8); - printf("\tRead Bypass Behavior : memory reads not conflicting with memory writes "\ - "in the PMR Elasticity Buffer %s bypass those memory writes\n", - (pmrebs & 0x00000010) ? "SHALL" : "MAY"); + printf("\tRead Bypass Behavior : %s%s%s%s", + "memory reads not conflicting with memory writes ", + "in the PMR Elasticity Buffer ", (pmrebs & 0x00000010) ? "SHALL" : "MAY", + " bypass those memory writes\n"); printf("\tPMR Elasticity Buffer Size Units (PMRSZU): %s\n", - nvme_register_pmr_pmrszu_to_string(pmrebs & 0x0000000f)); + nvme_register_pmr_pmrszu_to_string(pmrebs & 0x0000000f)); } static void stdout_registers_pmrswtp(__u32 pmrswtp) @@ -1404,174 +1443,200 @@ static void stdout_registers_pmrswtp(__u32 pmrswtp) static void stdout_registers_pmrmscl(uint32_t pmrmscl) { - printf("\tController Base Address (CBA): %#x\n", - (pmrmscl & 0xfffff000) >> 12); - printf("\tController Memory Space Enable (CMSE): %#x\n\n", - (pmrmscl & 0x00000002) >> 1); + printf("\tController Base Address (CBA): %#x\n", (pmrmscl & 0xfffff000) >> 12); + printf("\tController Memory Space Enable (CMSE): %#x\n\n", (pmrmscl & 0x00000002) >> 1); } static void stdout_registers_pmrmscu(uint32_t pmrmscu) { - printf("\tController Base Address (CBA): %#x\n", - pmrmscu); + printf("\tController Base Address (CBA): %#x\n", pmrmscu); } -void stdout_ctrl_registers(void *bar, bool fabrics) +static void stdout_ctrl_register_human(int offset, uint64_t value64, 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); + uint32_t value32 = (uint32_t)value64; - 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); + switch (offset) { + case NVME_REG_CAP: + stdout_registers_cap((struct nvme_bar_cap *)&value64); + break; + case NVME_REG_VS: + stdout_registers_version(value64); + break; + case NVME_REG_INTMS: + printf("\tInterrupt Vector Mask Set (IVMS): %x\n\n", value32); + break; + case NVME_REG_INTMC: + printf("\tInterrupt Vector Mask Clear (IVMC): %x\n\n", value32); + break; + case NVME_REG_CC: + stdout_registers_cc(value32); + break; + case NVME_REG_CSTS: + stdout_registers_csts(value32); + break; + case NVME_REG_NSSR: + printf("\tNVM Subsystem Reset Control (NSSRC): %u\n\n", value32); + break; + case NVME_REG_AQA: + stdout_registers_aqa(value32); + break; + case NVME_REG_ASQ: + printf("\tAdmin Submission Queue Base (ASQB): %"PRIx64"\n\n", value64); + break; + case NVME_REG_ACQ: + printf("\tAdmin Completion Queue Base (ACQB): %"PRIx64"\n\n", value64); + break; + case NVME_REG_CMBLOC: + stdout_registers_cmbloc(value32, support ? *support : true); + break; + case NVME_REG_CMBSZ: + stdout_registers_cmbsz(value32); + break; + case NVME_REG_BPINFO: + stdout_registers_bpinfo(value32); + break; + case NVME_REG_BPRSEL: + stdout_registers_bprsel(value32); + break; + case NVME_REG_BPMBL: + stdout_registers_bpmbl(value64); + break; + case NVME_REG_CMBMSC: + stdout_registers_cmbmsc(value64); + break; + case NVME_REG_CMBSTS: + stdout_registers_cmbsts(value32); + break; + case NVME_REG_CMBEBS: + stdout_registers_cmbebs(value32); + break; + case NVME_REG_CMBSWTP: + stdout_registers_cmbswtp(value32); + break; + case NVME_REG_NSSD: + stdout_registers_nssd(value32); + break; + case NVME_REG_CRTO: + stdout_registers_crto(value32); + break; + case NVME_REG_PMRCAP: + stdout_registers_pmrcap(value32); + break; + case NVME_REG_PMRCTL: + stdout_registers_pmrctl(value32); + break; + case NVME_REG_PMRSTS: + stdout_registers_pmrsts(value32, support ? *support : true); + break; + case NVME_REG_PMREBS: + stdout_registers_pmrebs(value32); + break; + case NVME_REG_PMRSWTP: + stdout_registers_pmrswtp(value32); + break; + case NVME_REG_PMRMSCL: + stdout_registers_pmrmscl(value32); + break; + case NVME_REG_PMRMSCU: + stdout_registers_pmrmscu(value32); + break; + default: + printf("unknown register: 0x%04x (%s), value: 0x%"PRIx64"\n", + offset, nvme_register_to_string(offset), value64); + break; + } +} - printf("intmc : %x\n", intmc); - printf("\tInterrupt Vector Mask Clear (IVMC): %x\n\n", - intmc); - printf("aqa : %x\n", aqa); - stdout_registers_aqa(aqa); +static void stdout_ctrl_register(int offset, uint64_t value64) +{ + bool human = stdout_print_ops.flags & VERBOSE; + uint32_t value32 = (uint32_t)value64; - printf("asq : %"PRIx64"\n", asq); - printf("\tAdmin Submission Queue Base (ASQB): %"PRIx64"\n\n", - asq); + if (human) + printf("%s: ", nvme_register_to_string(offset)); + else + printf("register: 0x%04x (%s), value: ", offset, nvme_register_to_string(offset)); - printf("acq : %"PRIx64"\n", acq); - printf("\tAdmin Completion Queue Base (ACQB): %"PRIx64"\n\n", - acq); + if (nvme_is_64bit_reg(offset)) + printf("0x%"PRIx64"\n", value64); + else + printf("0x%x\n", value32); - printf("cmbloc : %x\n", cmbloc); - stdout_registers_cmbloc(cmbloc, cmbsz); + if (human) + stdout_ctrl_register_human(offset, value64, NULL); +} - printf("cmbsz : %x\n", cmbsz); - stdout_registers_cmbsz(cmbsz); +static void stdout_ctrl_register_support(void *bar, int offset, bool human, bool support) +{ + bool reg64 = nvme_is_64bit_reg(offset); + uint64_t value = reg64 ? mmio_read64(bar + offset) : mmio_read32(bar + offset); - printf("bpinfo : %x\n", bpinfo); - stdout_registers_bpinfo(bpinfo); + printf("%-8s: ", nvme_register_symbol_to_string(offset)); - printf("bprsel : %x\n", bprsel); - stdout_registers_bprsel(bprsel); + if (reg64) + printf("%"PRIx64"\n", value); + else + printf("%x\n", (uint32_t)value); - printf("bpmbl : %"PRIx64"\n", bpmbl); - stdout_registers_bpmbl(bpmbl); + if (human) + stdout_ctrl_register_human(offset, value, &support); +} - printf("cmbmsc : %"PRIx64"\n", cmbmsc); - stdout_registers_cmbmsc(cmbmsc); +void stdout_ctrl_registers(void *bar, bool fabrics) +{ + uint64_t cap = mmio_read64(bar + NVME_REG_CAP); + uint32_t vs = mmio_read32(bar + NVME_REG_VS); + uint32_t cc = mmio_read32(bar + NVME_REG_CC); + uint32_t csts = mmio_read32(bar + NVME_REG_CSTS); + uint32_t nssr = mmio_read32(bar + NVME_REG_NSSR); + uint32_t crto = mmio_read32(bar + NVME_REG_CRTO); + uint32_t cmbsz = mmio_read32(bar + NVME_REG_CMBSZ); + uint32_t pmrctl = mmio_read32(bar + NVME_REG_PMRCTL); + bool human = stdout_print_ops.flags & VERBOSE ? true : false; - printf("cmbsts : %x\n", cmbsts); - stdout_registers_cmbsts(cmbsts); + if (cap != -1) + stdout_ctrl_register_support(bar, NVME_REG_CAP, human, true); - printf("pmrcap : %x\n", pmrcap); - stdout_registers_pmrcap(pmrcap); + if (vs != -1) + stdout_ctrl_register_support(bar, NVME_REG_VS, human, true); - printf("pmrctl : %x\n", pmrctl); - stdout_registers_pmrctl(pmrctl); + if (cc != -1) + stdout_ctrl_register_support(bar, NVME_REG_CC, human, true); - printf("pmrsts : %x\n", pmrsts); - stdout_registers_pmrsts(pmrsts, pmrctl); + if (csts != -1) + stdout_ctrl_register_support(bar, NVME_REG_CSTS, human, true); - printf("pmrebs : %x\n", pmrebs); - stdout_registers_pmrebs(pmrebs); + if (nssr != -1) + stdout_ctrl_register_support(bar, NVME_REG_NSSR, human, true); - printf("pmrswtp : %x\n", pmrswtp); - stdout_registers_pmrswtp(pmrswtp); + if (crto != -1) + stdout_ctrl_register_support(bar, NVME_REG_CRTO, human, true); - printf("pmrmscl : %#x\n", pmrmscl); - stdout_registers_pmrmscl(pmrmscl); + if (fabrics) + return; - 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); - } - } + stdout_ctrl_register_support(bar, NVME_REG_INTMS, human, true); + stdout_ctrl_register_support(bar, NVME_REG_INTMC, human, true); + stdout_ctrl_register_support(bar, NVME_REG_AQA, human, true); + stdout_ctrl_register_support(bar, NVME_REG_ASQ, human, true); + stdout_ctrl_register_support(bar, NVME_REG_ACQ, human, true); + stdout_ctrl_register_support(bar, NVME_REG_CMBLOC, human, + stdout_registers_cmbloc_support(cmbsz)); + stdout_ctrl_register_support(bar, NVME_REG_CMBSZ, human, true); + stdout_ctrl_register_support(bar, NVME_REG_BPINFO, human, true); + stdout_ctrl_register_support(bar, NVME_REG_BPRSEL, human, true); + stdout_ctrl_register_support(bar, NVME_REG_BPMBL, human, true); + stdout_ctrl_register_support(bar, NVME_REG_CMBMSC, human, true); + stdout_ctrl_register_support(bar, NVME_REG_CMBSTS, human, true); + stdout_ctrl_register_support(bar, NVME_REG_PMRCAP, human, true); + stdout_ctrl_register_support(bar, NVME_REG_PMRCTL, human, true); + stdout_ctrl_register_support(bar, NVME_REG_PMRSTS, human, + stdout_registers_pmrctl_ready(pmrctl)); + stdout_ctrl_register_support(bar, NVME_REG_PMREBS, human, true); + stdout_ctrl_register_support(bar, NVME_REG_PMRSWTP, human, true); + stdout_ctrl_register_support(bar, NVME_REG_PMRMSCL, human, true); + stdout_ctrl_register_support(bar, NVME_REG_PMRMSCU, human, true); } static void stdout_single_property(int offset, uint64_t value64) @@ -1581,44 +1646,33 @@ static void stdout_single_property(int offset, uint64_t value64) if (!human) { if (nvme_is_64bit_reg(offset)) - printf("property: 0x%02x (%s), value: %"PRIx64"\n", + printf("property: 0x%04x (%s), value: %"PRIx64"\n", offset, nvme_register_to_string(offset), value64); else - printf("property: 0x%02x (%s), value: %x\n", offset, + printf("property: 0x%04x (%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); - break; - case NVME_REG_VS: - printf("version : %x\n", value32); - stdout_registers_version(value32); - break; - case NVME_REG_CC: - printf("cc : %x\n", value32); - stdout_registers_cc(value32); - break; - case NVME_REG_CSTS: - printf("csts : %x\n", value32); - stdout_registers_csts(value32); - break; - case NVME_REG_NSSR: - printf("nssr : %x\n", value32); - printf("\tNVM Subsystem Reset Control (NSSRC): %u\n\n", value32); - break; - case NVME_REG_CRTO: - printf("crto : %x\n", value32); - stdout_registers_crto(value32); - break; - default: - printf("unknown property: 0x%02x (%s), value: %"PRIx64"\n", - offset, nvme_register_to_string(offset), value64); - break; + if (nvme_is_fabrics_reg(offset)) { + printf("%s : ", nvme_register_symbol_to_string(offset)); + switch (offset) { + case NVME_REG_CAP: + printf("%"PRIx64"\n", value64); + break; + case NVME_REG_VS: + case NVME_REG_CC: + case NVME_REG_CSTS: + case NVME_REG_NSSR: + case NVME_REG_CRTO: + printf("%x\n", value32); + break; + default: + break; + } } + + stdout_ctrl_register_human(offset, value64, NULL); } static void stdout_status(int status) @@ -5082,6 +5136,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 2c7802be71..47dc67ce25 100644 --- a/nvme-print.c +++ b/nvme-print.c @@ -351,15 +351,69 @@ const char *nvme_register_pmr_hsts_to_string(__u8 hsts) } } +const char *nvme_register_unit_to_string(__u8 unit) +{ + 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"; +} + const char *nvme_register_pmr_pmrszu_to_string(__u8 pmrszu) { - switch (pmrszu) { - case 0: return "Bytes"; - case 1: return "One KB"; - case 2: return "One MB"; - case 3: return "One GB"; - default: return "Reserved"; + return nvme_register_unit_to_string(pmrszu); +} + +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; +} + +void nvme_show_ctrl_register(void *bar, bool fabrics, int offset, enum nvme_print_flags flags) +{ + uint64_t value; + + if (nvme_is_64bit_reg(offset)) + value = mmio_read64(bar + offset); + else + value = mmio_read32(bar + offset); + + if (!nvme_is_fabrics_reg(offset)) { + if (fabrics) { + printf("register: 0x%04x (%s) not fabrics\n", offset, + nvme_register_to_string(offset)); + return; + } + if (value == -1) { + printf("register: 0x%04x (%s), value: 0x%"PRIx64" not valid\n", offset, + nvme_register_to_string(offset), value); + return; + } } + + nvme_print(ctrl_register, flags, offset, value); } void nvme_show_ctrl_registers(void *bar, bool fabrics, enum nvme_print_flags flags) @@ -761,20 +815,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) @@ -950,6 +1051,72 @@ const char *nvme_host_metadata_type_to_string(enum nvme_features_id fid, } } +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 4533474eb3..c503ca798f 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,11 @@ 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_unit_to_string(__u8 ebszu); const char *nvme_register_pmr_pmrszu_to_string(__u8 pmrszu); 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); @@ -310,4 +314,5 @@ 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); #endif /* NVME_PRINT_H */ diff --git a/nvme.c b/nvme.c index 64aa556000..a411212993 100644 --- a/nvme.c +++ b/nvme.c @@ -106,6 +106,60 @@ 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; +}; + +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_FLAG("verbose", 'v', NULL, verbose), \ @@ -114,6 +168,11 @@ struct passthru_config { OPT_END() \ } +#define REG_ARGS(opts, cfg, ...) \ + NVME_ARGS(opts, \ + OPT_UINT("offset", 'O', &cfg.offset, offset), \ + ##__VA_ARGS__) + static const char nvme_version_string[] = NVME_VERSION; static struct plugin builtin = { @@ -184,10 +243,26 @@ 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"; -static void *mmap_registers(nvme_root_t r, struct nvme_dev *dev); +static void *mmap_registers(nvme_root_t r, struct nvme_dev *dev, bool writable); const char *nvme_strerror(int errnum) { @@ -469,6 +544,7 @@ static int get_smart_log(int argc, char **argv, struct command *cmd, struct plug "(default) or binary."; _cleanup_free_ struct nvme_smart_log *smart_log = NULL; + _cleanup_nvme_dev_ struct nvme_dev *dev = NULL; const char *namespace = "(optional) desired namespace"; enum nvme_print_flags flags; @@ -531,7 +607,7 @@ static int get_ana_log(int argc, char **argv, struct command *cmd, "decoded format (default), json or binary."; const char *groups = "Return ANA groups only."; - _cleanup_nvme_dev_ struct nvme_dev *dev= NULL; + _cleanup_nvme_dev_ struct nvme_dev *dev = NULL; _cleanup_free_ struct nvme_id_ctrl *ctrl = NULL; _cleanup_free_ void *ana_log = NULL; size_t ana_log_len; @@ -1011,7 +1087,7 @@ static int get_effects_log(int argc, char **argv, struct command *cmd, struct pl __u64 cap; r = nvme_scan(NULL); - bar = mmap_registers(r, dev); + bar = mmap_registers(r, dev, false); nvme_free_tree(r); if (bar) { @@ -1419,6 +1495,7 @@ static int get_persistent_event_log(int argc, char **argv, _cleanup_free_ struct nvme_persistent_event_log *pevent_collected = NULL; _cleanup_free_ struct nvme_persistent_event_log *pevent = NULL; + _cleanup_huge_ struct nvme_mem_huge mh = { 0, }; _cleanup_nvme_dev_ struct nvme_dev *dev = NULL; enum nvme_print_flags flags; @@ -5214,7 +5291,7 @@ static int nvme_get_properties(int fd, void **pbar) return err; } -static void *mmap_registers(nvme_root_t r, struct nvme_dev *dev) +static void *mmap_registers(nvme_root_t r, struct nvme_dev *dev, bool writable) { nvme_ctrl_t c = NULL; nvme_ns_t n = NULL; @@ -5222,6 +5299,10 @@ static void *mmap_registers(nvme_root_t r, struct nvme_dev *dev) char path[512]; void *membase; int fd; + int prot = PROT_READ; + + if (writable) + prot |= PROT_WRITE; c = nvme_scan_ctrl(r, dev->name); if (c) { @@ -5239,7 +5320,7 @@ static void *mmap_registers(nvme_root_t r, struct nvme_dev *dev) nvme_free_ns(n); } - fd = open(path, O_RDONLY); + fd = open(path, writable ? O_RDWR : O_RDONLY); if (fd < 0) { if (map_log_level(0, false) >= LOG_DEBUG) nvme_show_error("%s did not find a pci resource, open failed %s", @@ -5247,7 +5328,7 @@ static void *mmap_registers(nvme_root_t r, 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 (map_log_level(0, false) >= LOG_DEBUG) { fprintf(stderr, "%s failed to map. ", dev->name); @@ -5299,7 +5380,7 @@ static int show_registers(int argc, char **argv, struct command *cmd, struct plu flags |= VERBOSE; r = nvme_scan(NULL); - bar = mmap_registers(r, dev); + bar = mmap_registers(r, dev, false); if (!bar) { err = nvme_get_properties(dev_fd(dev), &bar); if (err) @@ -5317,6 +5398,629 @@ static int show_registers(int argc, char **argv, struct command *cmd, struct plu return err; } +static int get_register_property(int fd, void **pbar, int offset) +{ + int err; + int size = offset + 1 + (nvme_is_64bit_reg(offset) ? sizeof(uint64_t) : sizeof(uint32_t)); + __u64 value; + void *bar = malloc(size); + struct nvme_get_property_args args = { + .args_size = sizeof(args), + .fd = fd, + .offset = offset, + .value = &value, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + }; + + if (!bar) { + nvme_show_error("malloc: %s", strerror(errno)); + return -1; + } + + memset(bar, 0xff, size); + + 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; +} + +static bool get_register_flag(void *bar, bool fabrics, struct get_reg_config *cfg, int offset, + enum nvme_print_flags flags) +{ + bool flag = false; + + if (cfg->offset == offset) + return false; + + switch (offset) { + case NVME_REG_CAP: + if (cfg->cap) + flag = true; + break; + case NVME_REG_VS: + if (cfg->vs) + flag = true; + break; + case NVME_REG_INTMS: + if (cfg->intms) + flag = true; + break; + case NVME_REG_INTMC: + if (cfg->intmc) + flag = true; + break; + case NVME_REG_CC: + if (cfg->cc) + flag = true; + break; + case NVME_REG_CSTS: + if (cfg->csts) + flag = true; + break; + case NVME_REG_NSSR: + if (cfg->nssr) + flag = true; + break; + case NVME_REG_AQA: + if (cfg->aqa) + flag = true; + break; + case NVME_REG_ASQ: + if (cfg->asq) + flag = true; + break; + case NVME_REG_ACQ: + if (cfg->acq) + flag = true; + break; + case NVME_REG_CMBLOC: + if (cfg->cmbloc) + flag = true; + break; + case NVME_REG_CMBSZ: + if (cfg->cmbsz) + flag = true; + break; + case NVME_REG_BPINFO: + if (cfg->bpinfo) + flag = true; + break; + case NVME_REG_BPRSEL: + if (cfg->bprsel) + flag = true; + break; + case NVME_REG_BPMBL: + if (cfg->bpmbl) + flag = true; + break; + case NVME_REG_CMBMSC: + if (cfg->cmbmsc) + flag = true; + break; + case NVME_REG_CMBSTS: + if (cfg->cmbsts) + flag = true; + break; + case NVME_REG_CMBEBS: + if (cfg->cmbebs) + flag = true; + break; + case NVME_REG_CMBSWTP: + if (cfg->cmbswtp) + flag = true; + break; + case NVME_REG_NSSD: + if (cfg->nssd) + flag = true; + break; + case NVME_REG_CRTO: + if (cfg->crto) + flag = true; + break; + case NVME_REG_PMRCAP: + if (cfg->pmrcap) + flag = true; + break; + case NVME_REG_PMRCTL: + if (cfg->pmrctl) + flag = true; + break; + case NVME_REG_PMRSTS: + if (cfg->pmrsts) + flag = true; + break; + case NVME_REG_PMREBS: + if (cfg->pmrebs) + flag = true; + break; + case NVME_REG_PMRSWTP: + if (cfg->pmrswtp) + flag = true; + break; + case NVME_REG_PMRMSCL: + if (cfg->pmrmscl) + flag = true; + break; + case NVME_REG_PMRMSCU: + if (cfg->pmrmscu) + flag = true; + break; + default: + break; + } + + if (flag) + nvme_show_ctrl_register(bar, fabrics, offset, flags); + + return flag; +} + +static bool get_register_offset(void *bar, bool fabrics, struct get_reg_config *cfg, + enum nvme_print_flags flags) +{ + bool flag = cfg->offset >= 0; + + if (flag) + nvme_show_ctrl_register(bar, fabrics, cfg->offset, flags); + + flag = get_register_flag(bar, fabrics, cfg, NVME_REG_CAP, flags) || flag; + flag = get_register_flag(bar, fabrics, cfg, NVME_REG_VS, flags) || flag; + flag = get_register_flag(bar, fabrics, cfg, NVME_REG_INTMS, flags) || flag; + flag = get_register_flag(bar, fabrics, cfg, NVME_REG_INTMC, flags) || flag; + flag = get_register_flag(bar, fabrics, cfg, NVME_REG_CC, flags) || flag; + flag = get_register_flag(bar, fabrics, cfg, NVME_REG_CSTS, flags) || flag; + flag = get_register_flag(bar, fabrics, cfg, NVME_REG_NSSR, flags) || flag; + flag = get_register_flag(bar, fabrics, cfg, NVME_REG_AQA, flags) || flag; + flag = get_register_flag(bar, fabrics, cfg, NVME_REG_ASQ, flags) || flag; + flag = get_register_flag(bar, fabrics, cfg, NVME_REG_ACQ, flags) || flag; + flag = get_register_flag(bar, fabrics, cfg, NVME_REG_CMBLOC, flags) || flag; + flag = get_register_flag(bar, fabrics, cfg, NVME_REG_CMBSZ, flags) || flag; + flag = get_register_flag(bar, fabrics, cfg, NVME_REG_BPINFO, flags) || flag; + flag = get_register_flag(bar, fabrics, cfg, NVME_REG_BPRSEL, flags) || flag; + flag = get_register_flag(bar, fabrics, cfg, NVME_REG_BPMBL, flags) || flag; + flag = get_register_flag(bar, fabrics, cfg, NVME_REG_CMBMSC, flags) || flag; + flag = get_register_flag(bar, fabrics, cfg, NVME_REG_CMBSTS, flags) || flag; + flag = get_register_flag(bar, fabrics, cfg, NVME_REG_CMBEBS, flags) || flag; + flag = get_register_flag(bar, fabrics, cfg, NVME_REG_CMBSWTP, flags) || flag; + flag = get_register_flag(bar, fabrics, cfg, NVME_REG_NSSD, flags) || flag; + flag = get_register_flag(bar, fabrics, cfg, NVME_REG_CRTO, flags) || flag; + flag = get_register_flag(bar, fabrics, cfg, NVME_REG_PMRCAP, flags) || flag; + flag = get_register_flag(bar, fabrics, cfg, NVME_REG_PMRCTL, flags) || flag; + flag = get_register_flag(bar, fabrics, cfg, NVME_REG_PMRSTS, flags) || flag; + flag = get_register_flag(bar, fabrics, cfg, NVME_REG_PMREBS, flags) || flag; + flag = get_register_flag(bar, fabrics, cfg, NVME_REG_PMRSWTP, flags) || flag; + flag = get_register_flag(bar, fabrics, cfg, NVME_REG_PMRMSCL, flags) || flag; + flag = get_register_flag(bar, fabrics, cfg, NVME_REG_PMRMSCU, flags) || flag; + + return flag; +} + +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; + + _cleanup_nvme_root_ nvme_root_t r = NULL; + void *bar; + + struct get_reg_config cfg = { + .offset = -1, + }; + + REG_ARGS(opts, cfg, + OPT_FLAG("human-readable", 'H', &cfg.human_readable, human_readable), + OPT_FLAG("cap", 'c', &cfg.cap, cap), + OPT_FLAG("vs", 'V', &cfg.vs, vs), + OPT_FLAG("cmbloc", 'm', &cfg.cmbloc, cmbloc), + OPT_FLAG("cmbsz", 'M', &cfg.cmbsz, cmbsz), + OPT_FLAG("bpinfo", 'b', &cfg.bpinfo, bpinfo), + OPT_FLAG("cmbsts", 'S', &cfg.cmbsts, cmbsts), + OPT_FLAG("cmbebs", 'E', &cfg.cmbebs, cmbebs), + OPT_FLAG("cmbswtp", 'W', &cfg.cmbswtp, cmbswtp), + OPT_FLAG("crto", 'r', &cfg.crto, crto), + OPT_FLAG("pmrcap", 'P', &cfg.pmrcap, pmrcap), + OPT_FLAG("pmrsts", 't', &cfg.pmrsts, pmrsts), + OPT_FLAG("pmrebs", 'e', &cfg.pmrebs, pmrebs), + OPT_FLAG("pmrswtp", 'w', &cfg.pmrswtp, pmrswtp), + OPT_FLAG("intms", 'i', &cfg.intms, intms), + OPT_FLAG("intmc", 'I', &cfg.intmc, intmc), + OPT_FLAG("cc", 'C', &cfg.cc, cc), + OPT_FLAG("csts", 'T', &cfg.csts, csts), + OPT_FLAG("nssr", 'n', &cfg.nssr, nssr), + OPT_FLAG("aqa", 'a', &cfg.aqa, aqa), + OPT_FLAG("asq", 'A', &cfg.asq, asq), + OPT_FLAG("acq", 'q', &cfg.acq, acq), + OPT_FLAG("bprsel", 'B', &cfg.bprsel, bprsel), + OPT_FLAG("bpmbl", 'p', &cfg.bpmbl, bpmbl), + OPT_FLAG("cmbmsc", 's', &cfg.cmbmsc, cmbmsc), + OPT_FLAG("nssd", 'N', &cfg.nssd, nssd), + OPT_FLAG("pmrctl", 'R', &cfg.pmrctl, pmrctl), + OPT_FLAG("pmrmscl", 'l', &cfg.pmrmscl, pmrmscl), + OPT_FLAG("pmrmscu", 'u', &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; + + r = nvme_scan(NULL); + bar = mmap_registers(r, dev, false); + if (!bar) { + err = get_register_property(dev_fd(dev), &bar, NVME_REG_PMRMSCU); + if (err) + return err; + fabrics = true; + } + + if (!get_register_offset(bar, fabrics, &cfg, flags)) { + nvme_show_error("offset required param"); + err = -EINVAL; + } + + if (fabrics) + free(bar); + else + munmap(bar, getpagesize()); + + return err; +} + +static void nvme_set_register(void *bar, int offset, uint64_t value, bool mmio32) +{ + 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); +} + +static void set_register_value(void *bar, struct argconfig_commandline_options *opts, + struct set_reg_config *cfg, int offset) +{ + switch (offset) { + case NVME_REG_INTMS: + if (argconfig_parse_seen(opts, "intms")) + nvme_set_register(bar, offset, cfg->intms, cfg->mmio32); + break; + case NVME_REG_INTMC: + if (argconfig_parse_seen(opts, "intmc")) + nvme_set_register(bar, offset, cfg->intmc, cfg->mmio32); + break; + case NVME_REG_CC: + if (argconfig_parse_seen(opts, "cc")) + nvme_set_register(bar, offset, cfg->cc, cfg->mmio32); + break; + case NVME_REG_CSTS: + if (argconfig_parse_seen(opts, "csts")) + nvme_set_register(bar, offset, cfg->csts, cfg->mmio32); + break; + case NVME_REG_NSSR: + if (argconfig_parse_seen(opts, "nssr")) + nvme_set_register(bar, offset, cfg->nssr, cfg->mmio32); + break; + case NVME_REG_AQA: + if (argconfig_parse_seen(opts, "aqa")) + nvme_set_register(bar, offset, cfg->aqa, cfg->mmio32); + break; + case NVME_REG_ASQ: + if (argconfig_parse_seen(opts, "asq")) + nvme_set_register(bar, offset, cfg->asq, cfg->mmio32); + break; + case NVME_REG_ACQ: + if (argconfig_parse_seen(opts, "acq")) + nvme_set_register(bar, offset, cfg->acq, cfg->mmio32); + break; + case NVME_REG_BPRSEL: + if (argconfig_parse_seen(opts, "bprsel")) + nvme_set_register(bar, offset, cfg->bprsel, cfg->mmio32); + break; + case NVME_REG_CMBMSC: + if (argconfig_parse_seen(opts, "cmbmsc")) + nvme_set_register(bar, offset, cfg->cmbmsc, cfg->mmio32); + break; + case NVME_REG_NSSD: + if (argconfig_parse_seen(opts, "nssd")) + nvme_set_register(bar, offset, cfg->nssd, cfg->mmio32); + break; + case NVME_REG_PMRCTL: + if (argconfig_parse_seen(opts, "pmrctl")) + nvme_set_register(bar, offset, cfg->pmrctl, cfg->mmio32); + break; + case NVME_REG_PMRMSCL: + if (argconfig_parse_seen(opts, "pmrmscl")) + nvme_set_register(bar, offset, cfg->pmrmscl, cfg->mmio32); + break; + case NVME_REG_PMRMSCU: + if (argconfig_parse_seen(opts, "pmrmscu")) + nvme_set_register(bar, offset, cfg->pmrmscu, cfg->mmio32); + break; + default: + break; + } +} + +static int set_register_check(struct argconfig_commandline_options *opts, + struct set_reg_config *cfg, int offset) +{ + bool flag = false; + + switch (offset) { + case NVME_REG_INTMS: + if (argconfig_parse_seen(opts, "intms")) + flag = true; + break; + case NVME_REG_INTMC: + if (argconfig_parse_seen(opts, "intmc")) + flag = true; + break; + case NVME_REG_CC: + if (argconfig_parse_seen(opts, "cc")) + flag = true; + break; + case NVME_REG_CSTS: + if (argconfig_parse_seen(opts, "csts")) + flag = true; + break; + case NVME_REG_NSSR: + if (argconfig_parse_seen(opts, "nssr")) + flag = true; + break; + case NVME_REG_AQA: + if (argconfig_parse_seen(opts, "aqa")) + flag = true; + break; + case NVME_REG_ASQ: + if (argconfig_parse_seen(opts, "asq")) + flag = true; + break; + case NVME_REG_ACQ: + if (argconfig_parse_seen(opts, "acq")) + flag = true; + break; + case NVME_REG_BPRSEL: + if (argconfig_parse_seen(opts, "bprsel")) + flag = true; + break; + case NVME_REG_CMBMSC: + if (argconfig_parse_seen(opts, "cmbmsc")) + flag = true; + break; + case NVME_REG_NSSD: + if (argconfig_parse_seen(opts, "nssd")) + flag = true; + break; + case NVME_REG_PMRCTL: + if (argconfig_parse_seen(opts, "pmrctl")) + flag = true; + break; + case NVME_REG_PMRMSCL: + if (argconfig_parse_seen(opts, "pmrmscl")) + flag = true; + break; + case NVME_REG_PMRMSCU: + if (argconfig_parse_seen(opts, "pmrmscu")) + flag = true; + break; + default: + break; + } + + if (flag && cfg->offset == offset) { + nvme_show_error("offset duplicated param"); + return -EINVAL; + } + + return 0; +} + +static int set_register_offset_check(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_check(opts, cfg, NVME_REG_INTMS); + if (err) + return err; + + err = set_register_check(opts, cfg, NVME_REG_INTMC); + if (err) + return err; + + err = set_register_check(opts, cfg, NVME_REG_CC); + if (err) + return err; + + err = set_register_check(opts, cfg, NVME_REG_CSTS); + if (err) + return err; + + err = set_register_check(opts, cfg, NVME_REG_NSSR); + if (err) + return err; + + err = set_register_check(opts, cfg, NVME_REG_AQA); + if (err) + return err; + + err = set_register_check(opts, cfg, NVME_REG_ASQ); + if (err) + return err; + + err = set_register_check(opts, cfg, NVME_REG_ACQ); + if (err) + return err; + + err = set_register_check(opts, cfg, NVME_REG_BPRSEL); + if (err) + return err; + + err = set_register_check(opts, cfg, NVME_REG_BPMBL); + if (err) + return err; + + err = set_register_check(opts, cfg, NVME_REG_CMBMSC); + if (err) + return err; + + err = set_register_check(opts, cfg, NVME_REG_NSSD); + if (err) + return err; + + err = set_register_check(opts, cfg, NVME_REG_PMRCTL); + if (err) + return err; + + err = set_register_check(opts, cfg, NVME_REG_PMRMSCL); + if (err) + return err; + + err = set_register_check(opts, cfg, NVME_REG_PMRMSCU); + if (err) + return err; + + return 0; +} + +static int set_register_offset(void *bar, struct argconfig_commandline_options *opts, + struct set_reg_config *cfg) +{ + bool set = cfg->offset >= 0; + int err; + + if (set) { + err = set_register_offset_check(opts, cfg); + if (err) + return err; + nvme_set_register(bar, cfg->offset, cfg->value, cfg->mmio32); + } + + set_register_value(bar, opts, cfg, NVME_REG_INTMS); + set_register_value(bar, opts, cfg, NVME_REG_INTMC); + set_register_value(bar, opts, cfg, NVME_REG_CC); + set_register_value(bar, opts, cfg, NVME_REG_CSTS); + set_register_value(bar, opts, cfg, NVME_REG_NSSR); + set_register_value(bar, opts, cfg, NVME_REG_AQA); + set_register_value(bar, opts, cfg, NVME_REG_ASQ); + set_register_value(bar, opts, cfg, NVME_REG_ACQ); + set_register_value(bar, opts, cfg, NVME_REG_BPRSEL); + set_register_value(bar, opts, cfg, NVME_REG_BPMBL); + set_register_value(bar, opts, cfg, NVME_REG_CMBMSC); + set_register_value(bar, opts, cfg, NVME_REG_NSSD); + set_register_value(bar, opts, cfg, NVME_REG_PMRCTL); + set_register_value(bar, opts, cfg, NVME_REG_PMRMSCL); + set_register_value(bar, opts, cfg, NVME_REG_PMRMSCU); + + 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; + + _cleanup_nvme_root_ nvme_root_t r = NULL; + void *bar; + + struct set_reg_config cfg = { + .offset = -1, + }; + + REG_ARGS(opts, cfg, + OPT_SUFFIX("value", 'V', &cfg.value, value), + OPT_FLAG("mmio32", 'M', &cfg.mmio32, mmio32), + OPT_UINT("intms", 'i', &cfg.intms, intms), + OPT_UINT("intmc", 'I', &cfg.intmc, intmc), + OPT_UINT("cc", 'C', &cfg.cc, cc), + OPT_UINT("csts", 'T', &cfg.csts, csts), + OPT_UINT("nssr", 'n', &cfg.nssr, nssr), + OPT_UINT("aqa", 'a', &cfg.aqa, aqa), + OPT_SUFFIX("asq", 'A', &cfg.asq, asq), + OPT_SUFFIX("acq", 'q', &cfg.acq, acq), + OPT_UINT("bprsel", 'B', &cfg.bprsel, bprsel), + OPT_SUFFIX("bpmbl", 'p', &cfg.bpmbl, bpmbl), + OPT_SUFFIX("cmbmsc", 's', &cfg.cmbmsc, cmbmsc), + OPT_UINT("nssd", 'N', &cfg.nssd, nssd), + OPT_UINT("pmrctl", 'R', &cfg.pmrctl, pmrctl), + OPT_UINT("pmrmscl", 'l', &cfg.pmrmscl, pmrmscl), + OPT_UINT("pmrmscu", 'u', &cfg.pmrmscu, pmrmscu)); + + err = parse_and_open(&dev, argc, argv, desc, opts); + if (err) + return err; + + r = nvme_scan(NULL); + bar = mmap_registers(r, dev, true); + if (!bar) { + nvme_show_error("Failed to map"); + return -EINVAL; + } + + err = set_register_offset(bar, opts, &cfg); + + 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" diff --git a/nvme.h b/nvme.h index 3915698599..366a3de976 100644 --- a/nvme.h +++ b/nvme.h @@ -106,6 +106,10 @@ static inline DEFINE_CLEANUP_FUNC( cleanup_nvme_dev, struct nvme_dev *, dev_close) #define _cleanup_nvme_dev_ __cleanup__(cleanup_nvme_dev) +static inline DEFINE_CLEANUP_FUNC( + cleanup_nvme_root, nvme_root_t, nvme_free_tree) +#define _cleanup_nvme_root_ __cleanup__(cleanup_nvme_root) + extern const char *output_format; int validate_output_format(const char *format, enum nvme_print_flags *flags); diff --git a/subprojects/libnvme.wrap b/subprojects/libnvme.wrap index 7aa3c43009..411c95d971 100644 --- a/subprojects/libnvme.wrap +++ b/subprojects/libnvme.wrap @@ -1,6 +1,6 @@ [wrap-git] url = https://github.com/linux-nvme/libnvme.git -revision = b5122474cc0f545e7a8868f9c0177a41428acb2b +revision = 24a5580e13ff3aed1e4a872bd7cb329a1bb9273a [provide] libnvme = libnvme_dep