From 18fe326d98fd216dc37a88ae9e5724f496f6b647 Mon Sep 17 00:00:00 2001 From: Tokunori Ikegami Date: Sun, 21 Jan 2024 14:57:47 +0900 Subject: [PATCH] nvme: Add support for get-reg command to output single register Note: Currently only stdout print supported. Signed-off-by: Tokunori Ikegami --- nvme-builtin.h | 1 + nvme-print-stdout.c | 102 +++++++++++++++++++++++ nvme-print.c | 195 +++++++++++++++++++++++++++++++------------- nvme-print.h | 3 + nvme.c | 98 +++++++++++++++++++++- nvme.h | 1 + 6 files changed, 342 insertions(+), 58 deletions(-) diff --git a/nvme-builtin.h b/nvme-builtin.h index 2d2bead38b..0ef19ba410 100644 --- a/nvme-builtin.h +++ b/nvme-builtin.h @@ -89,6 +89,7 @@ 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("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 9d4aae7c21..acf4b68923 100644 --- a/nvme-print-stdout.c +++ b/nvme-print-stdout.c @@ -1427,6 +1427,107 @@ static void stdout_registers_pmrmscu(uint32_t pmrmscu) pmrmscu); } +static void stdout_single_register(int offset, uint64_t value64) +{ + bool human = stdout_print_ops.flags & VERBOSE; + uint32_t value32 = (uint32_t)value64; + + if (human) + printf("%s: ", nvme_register_to_string(offset)); + else + printf("register: 0x%02x (%s), value: ", offset, nvme_register_to_string(offset)); + + if (nvme_is_64bit_reg(offset)) + printf("%"PRIx64"\n", value64); + else + printf("%x\n", value32); + + if (!human) + return; + + switch (offset) { + case NVME_REG_CAP: + stdout_registers_cap((struct nvme_bar_cap *)&value64); + break; + case NVME_REG_VS: + stdout_registers_version(value32); + 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, 1); + 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_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, 1); + 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%02x (%s), value: %"PRIx64"\n", + offset, nvme_register_to_string(offset), value64); + break; + } +} + void stdout_ctrl_registers(void *bar, bool fabrics) { uint64_t cap, asq, acq, bpmbl, cmbmsc; @@ -5092,6 +5193,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, + .single_register = stdout_single_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 1086aad2a9..db7dc57ed1 100644 --- a/nvme-print.c +++ b/nvme-print.c @@ -362,6 +362,31 @@ const char *nvme_register_pmr_pmrszu_to_string(__u8 pmrszu) } } +void nvme_show_single_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%02x (%s) not fabrics\n", offset, + nvme_register_to_string(offset)); + return; + } + if (value == 0xffffffff) { + printf("register: 0x%02x (%s), value: %"PRIx64" not valid\n", offset, + nvme_register_to_string(offset), value); + return; + } + } + + nvme_print(single_register, flags, offset, value); +} + void nvme_show_ctrl_registers(void *bar, bool fabrics, enum nvme_print_flags flags) { nvme_print(ctrl_registers, flags, bar, fabrics); @@ -893,61 +918,121 @@ void nvme_show_lba_status_info(__u32 result) const char *nvme_host_metadata_type_to_string(enum nvme_features_id fid, __u8 type) { - switch (fid) { - case NVME_FEAT_FID_ENH_CTRL_METADATA: - case NVME_FEAT_FID_CTRL_METADATA: - switch (type) { - case NVME_CTRL_METADATA_OS_CTRL_NAME: - return "Operating System Controller Name"; - case NVME_CTRL_METADATA_OS_DRIVER_NAME: - return "Operating System Driver Name"; - case NVME_CTRL_METADATA_OS_DRIVER_VER: - return "Operating System Driver Version"; - case NVME_CTRL_METADATA_PRE_BOOT_CTRL_NAME: - return "Pre-boot Controller Name"; - case NVME_CTRL_METADATA_PRE_BOOT_DRIVER_NAME: - return "Pre-boot Driver Name"; - case NVME_CTRL_METADATA_PRE_BOOT_DRIVER_VER: - return "Pre-boot Driver Version"; - case NVME_CTRL_METADATA_SYS_PROC_MODEL: - return "System Processor Model"; - case NVME_CTRL_METADATA_CHIPSET_DRV_NAME: - return "Chipset Driver Name"; - case NVME_CTRL_METADATA_CHIPSET_DRV_VERSION: - return "Chipset Driver Version"; - case NVME_CTRL_METADATA_OS_NAME_AND_BUILD: - return "Operating System Name and Build"; - case NVME_CTRL_METADATA_SYS_PROD_NAME: - return "System Product Name"; - case NVME_CTRL_METADATA_FIRMWARE_VERSION: - return "Firmware Version"; - case NVME_CTRL_METADATA_OS_DRIVER_FILENAME: - return "Operating System Driver Filename"; - case NVME_CTRL_METADATA_DISPLAY_DRV_NAME: - return "Display Driver Name"; - case NVME_CTRL_METADATA_DISPLAY_DRV_VERSION: - return "Display Driver Version"; - case NVME_CTRL_METADATA_HOST_DET_FAIL_REC: - return "Host-Determined Failure Record"; - default: - return "Unknown Controller Type"; - } - case NVME_FEAT_FID_NS_METADATA: - switch (type) { - case NVME_NS_METADATA_OS_NS_NAME: - return "Operating System Namespace Name"; - case NVME_NS_METADATA_PRE_BOOT_NS_NAME: - return "Pre-boot Namespace Name"; - case NVME_NS_METADATA_OS_NS_QUAL_1: - return "Operating System Namespace Name Qualifier 1"; - case NVME_NS_METADATA_OS_NS_QUAL_2: - return "Operating System Namespace Name Qualifier 2"; - default: - return "Unknown Namespace Type"; - } - default: - return "Unknown Feature"; - } + switch (fid) { + case NVME_FEAT_FID_ENH_CTRL_METADATA: + case NVME_FEAT_FID_CTRL_METADATA: + switch (type) { + case NVME_CTRL_METADATA_OS_CTRL_NAME: + return "Operating System Controller Name"; + case NVME_CTRL_METADATA_OS_DRIVER_NAME: + return "Operating System Driver Name"; + case NVME_CTRL_METADATA_OS_DRIVER_VER: + return "Operating System Driver Version"; + case NVME_CTRL_METADATA_PRE_BOOT_CTRL_NAME: + return "Pre-boot Controller Name"; + case NVME_CTRL_METADATA_PRE_BOOT_DRIVER_NAME: + return "Pre-boot Driver Name"; + case NVME_CTRL_METADATA_PRE_BOOT_DRIVER_VER: + return "Pre-boot Driver Version"; + case NVME_CTRL_METADATA_SYS_PROC_MODEL: + return "System Processor Model"; + case NVME_CTRL_METADATA_CHIPSET_DRV_NAME: + return "Chipset Driver Name"; + case NVME_CTRL_METADATA_CHIPSET_DRV_VERSION: + return "Chipset Driver Version"; + case NVME_CTRL_METADATA_OS_NAME_AND_BUILD: + return "Operating System Name and Build"; + case NVME_CTRL_METADATA_SYS_PROD_NAME: + return "System Product Name"; + case NVME_CTRL_METADATA_FIRMWARE_VERSION: + return "Firmware Version"; + case NVME_CTRL_METADATA_OS_DRIVER_FILENAME: + return "Operating System Driver Filename"; + case NVME_CTRL_METADATA_DISPLAY_DRV_NAME: + return "Display Driver Name"; + case NVME_CTRL_METADATA_DISPLAY_DRV_VERSION: + return "Display Driver Version"; + case NVME_CTRL_METADATA_HOST_DET_FAIL_REC: + return "Host-Determined Failure Record"; + default: + return "Unknown Controller Type"; + } + case NVME_FEAT_FID_NS_METADATA: + switch (type) { + case NVME_NS_METADATA_OS_NS_NAME: + return "Operating System Namespace Name"; + case NVME_NS_METADATA_PRE_BOOT_NS_NAME: + return "Pre-boot Namespace Name"; + case NVME_NS_METADATA_OS_NS_QUAL_1: + return "Operating System Namespace Name Qualifier 1"; + case NVME_NS_METADATA_OS_NS_QUAL_2: + return "Operating System Namespace Name Qualifier 2"; + default: + return "Unknown Namespace Type"; + } + default: + return "Unknown Feature"; + } +} + +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_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) diff --git a/nvme-print.h b/nvme-print.h index 4533474eb3..75af06dda3 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 (*single_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_single_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, @@ -294,6 +296,7 @@ const char *nvme_register_pmr_hsts_to_string(__u8 hsts); 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); diff --git a/nvme.c b/nvme.c index 110442e498..572226198c 100644 --- a/nvme.c +++ b/nvme.c @@ -1172,7 +1172,7 @@ static int get_fw_log(int argc, char **argv, struct command *cmd, struct plugin const char *desc = "Retrieve the firmware log for the\n" "specified device in either decoded format (default) or binary."; - _cleanup_free_ struct nvme_firmware_slot *fw_log = NULL;; + _cleanup_free_ struct nvme_firmware_slot *fw_log = NULL; _cleanup_nvme_dev_ struct nvme_dev *dev = NULL; enum nvme_print_flags flags; int err; @@ -3528,7 +3528,7 @@ static int ns_descs(int argc, char **argv, struct command *cmd, struct plugin *p const char *raw = "show descriptors in binary format"; _cleanup_nvme_dev_ struct nvme_dev *dev = NULL; - _cleanup_free_ void *nsdescs = NULL;; + _cleanup_free_ void *nsdescs = NULL; enum nvme_print_flags flags; int err; @@ -5289,7 +5289,6 @@ static int show_registers(int argc, char **argv, struct command *cmd, struct plu if (err) return err; - r = nvme_scan(NULL); err = validate_output_format(output_format_val, &flags); if (err < 0) { nvme_show_error("Invalid output format"); @@ -5299,6 +5298,7 @@ static int show_registers(int argc, char **argv, struct command *cmd, struct plu if (cfg.human_readable) flags |= VERBOSE; + r = nvme_scan(NULL); bar = mmap_registers(r, dev); if (!bar) { err = nvme_get_properties(dev_fd(dev), &bar); @@ -5317,6 +5317,98 @@ static int show_registers(int argc, char **argv, struct command *cmd, struct plu return err; } +bool nvme_is_fabrics_reg(int offset) +{ + switch (offset) { + case NVME_REG_CAP: + return true; + case NVME_REG_VS: + return true; + case NVME_REG_CC: + return true; + case NVME_REG_CSTS: + return true; + case NVME_REG_NSSR: + return true; + case NVME_REG_CRTO: + return true; + default: + break; + } + + return false; +} + +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 *offset = "offset of the requested register"; + const char *human_readable = "show register in readable format"; + + _cleanup_nvme_dev_ struct nvme_dev *dev = NULL; + int err; + enum nvme_print_flags flags; + bool fabrics = false; + nvme_root_t r; + void *bar; + + struct config { + int offset; + bool human_readable; + }; + + struct config cfg = { + .offset = -1, + .human_readable = false, + }; + + NVME_ARGS(opts, cfg, + OPT_UINT("offset", 'O', &cfg.offset, offset), + OPT_FLAG("human-readable", 'H', &cfg.human_readable, human_readable)); + + err = parse_and_open(&dev, argc, argv, desc, opts); + if (err) + return err; + + if (!argconfig_parse_seen(opts, "offset")) { + nvme_show_error("offset required param"); + return -EINVAL; + } + + err = validate_output_format(output_format_val, &flags); + if (err < 0) { + nvme_show_error("Invalid output format"); + goto free_tree; + } + + if (cfg.human_readable) + flags |= VERBOSE; + + r = nvme_scan(NULL); + bar = mmap_registers(r, dev); + if (!bar) { + err = nvme_get_properties(dev_fd(dev), &bar); + if (err) + goto free_tree; + fabrics = true; + } + + nvme_show_single_register(bar, fabrics, cfg.offset, flags); + if (fabrics) + free(bar); + else + munmap(bar, getpagesize()); +free_tree: + nvme_free_tree(r); + 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..68b93ce410 100644 --- a/nvme.h +++ b/nvme.h @@ -122,6 +122,7 @@ unsigned long long elapsed_utime(struct timeval start_time, /* nvme-print.c */ const char *nvme_select_to_string(int sel); +bool nvme_is_fabrics_reg(int offset); void d(unsigned char *buf, int len, int width, int group); void d_raw(unsigned char *buf, unsigned len);