1ecee5a1fSHans Rosenfeld /* 2ecee5a1fSHans Rosenfeld * This file and its contents are supplied under the terms of the 3ecee5a1fSHans Rosenfeld * Common Development and Distribution License ("CDDL"), version 1.0. 4ecee5a1fSHans Rosenfeld * You may only use this file in accordance with the terms of version 5ecee5a1fSHans Rosenfeld * 1.0 of the CDDL. 6ecee5a1fSHans Rosenfeld * 7ecee5a1fSHans Rosenfeld * A full copy of the text of the CDDL should have accompanied this 8ecee5a1fSHans Rosenfeld * source. A copy of the CDDL is also available via the Internet at 9ecee5a1fSHans Rosenfeld * http://www.illumos.org/license/CDDL. 10ecee5a1fSHans Rosenfeld */ 11ecee5a1fSHans Rosenfeld 12ecee5a1fSHans Rosenfeld /* 13ecee5a1fSHans Rosenfeld * Copyright 2016 Nexenta Systems, Inc. 140b68f179SHans Rosenfeld * Copyright 2017 Joyent, Inc. 15ecee5a1fSHans Rosenfeld */ 16ecee5a1fSHans Rosenfeld 17ecee5a1fSHans Rosenfeld /* 18ecee5a1fSHans Rosenfeld * nvmeadm -- NVMe administration utility 19ecee5a1fSHans Rosenfeld * 20ecee5a1fSHans Rosenfeld * nvmeadm [-v] [-d] [-h] <command> [<ctl>[/<ns>][,...]] [args] 21ecee5a1fSHans Rosenfeld * commands: list 22ecee5a1fSHans Rosenfeld * identify 23ecee5a1fSHans Rosenfeld * get-logpage <logpage name> 24ecee5a1fSHans Rosenfeld * get-features <feature>[,...] 25ecee5a1fSHans Rosenfeld * format ... 26ecee5a1fSHans Rosenfeld * secure-erase ... 27ecee5a1fSHans Rosenfeld * detach ... 28ecee5a1fSHans Rosenfeld * attach ... 29ecee5a1fSHans Rosenfeld * get-param ... 30ecee5a1fSHans Rosenfeld * set-param ... 31ecee5a1fSHans Rosenfeld * load-firmware ... 32ecee5a1fSHans Rosenfeld * activate-firmware ... 33ecee5a1fSHans Rosenfeld * write-uncorrectable ... 34ecee5a1fSHans Rosenfeld * compare ... 35ecee5a1fSHans Rosenfeld * compare-and-write ... 36ecee5a1fSHans Rosenfeld */ 37ecee5a1fSHans Rosenfeld 38ecee5a1fSHans Rosenfeld #include <stdio.h> 39ecee5a1fSHans Rosenfeld #include <stdlib.h> 40ecee5a1fSHans Rosenfeld #include <strings.h> 41ecee5a1fSHans Rosenfeld #include <ctype.h> 42ecee5a1fSHans Rosenfeld #include <err.h> 43ecee5a1fSHans Rosenfeld #include <sys/sunddi.h> 44ecee5a1fSHans Rosenfeld #include <libdevinfo.h> 45ecee5a1fSHans Rosenfeld 46ecee5a1fSHans Rosenfeld #include <sys/nvme.h> 47ecee5a1fSHans Rosenfeld 48ecee5a1fSHans Rosenfeld #include "nvmeadm.h" 49ecee5a1fSHans Rosenfeld 50ecee5a1fSHans Rosenfeld typedef struct nvme_process_arg nvme_process_arg_t; 51ecee5a1fSHans Rosenfeld typedef struct nvme_feature nvme_feature_t; 52ecee5a1fSHans Rosenfeld typedef struct nvmeadm_cmd nvmeadm_cmd_t; 53ecee5a1fSHans Rosenfeld 54ecee5a1fSHans Rosenfeld struct nvme_process_arg { 55ecee5a1fSHans Rosenfeld int npa_argc; 56ecee5a1fSHans Rosenfeld char **npa_argv; 57ecee5a1fSHans Rosenfeld char *npa_name; 580b68f179SHans Rosenfeld char *npa_nsid; 590b68f179SHans Rosenfeld int npa_found; 60ecee5a1fSHans Rosenfeld boolean_t npa_isns; 61ecee5a1fSHans Rosenfeld const nvmeadm_cmd_t *npa_cmd; 62ecee5a1fSHans Rosenfeld di_node_t npa_node; 63ecee5a1fSHans Rosenfeld di_minor_t npa_minor; 64ecee5a1fSHans Rosenfeld char *npa_path; 65ecee5a1fSHans Rosenfeld char *npa_dsk; 66ecee5a1fSHans Rosenfeld nvme_identify_ctrl_t *npa_idctl; 67ecee5a1fSHans Rosenfeld nvme_identify_nsid_t *npa_idns; 68ecee5a1fSHans Rosenfeld nvme_version_t *npa_version; 69ecee5a1fSHans Rosenfeld }; 70ecee5a1fSHans Rosenfeld 71ecee5a1fSHans Rosenfeld struct nvme_feature { 72ecee5a1fSHans Rosenfeld char *f_name; 73ecee5a1fSHans Rosenfeld char *f_short; 74ecee5a1fSHans Rosenfeld uint8_t f_feature; 75ecee5a1fSHans Rosenfeld size_t f_bufsize; 76ecee5a1fSHans Rosenfeld uint_t f_getflags; 77ecee5a1fSHans Rosenfeld int (*f_get)(int, const nvme_feature_t *, nvme_identify_ctrl_t *); 78ecee5a1fSHans Rosenfeld void (*f_print)(uint64_t, void *, size_t, nvme_identify_ctrl_t *); 79ecee5a1fSHans Rosenfeld }; 80ecee5a1fSHans Rosenfeld 81ecee5a1fSHans Rosenfeld #define NVMEADM_CTRL 1 82ecee5a1fSHans Rosenfeld #define NVMEADM_NS 2 83ecee5a1fSHans Rosenfeld #define NVMEADM_BOTH (NVMEADM_CTRL | NVMEADM_NS) 84ecee5a1fSHans Rosenfeld 85ecee5a1fSHans Rosenfeld struct nvmeadm_cmd { 86ecee5a1fSHans Rosenfeld char *c_name; 87ecee5a1fSHans Rosenfeld char *c_desc; 88ecee5a1fSHans Rosenfeld char *c_flagdesc; 89ecee5a1fSHans Rosenfeld int (*c_func)(int, const nvme_process_arg_t *); 90ecee5a1fSHans Rosenfeld void (*c_usage)(const char *); 91ecee5a1fSHans Rosenfeld boolean_t c_multi; 92ecee5a1fSHans Rosenfeld }; 93ecee5a1fSHans Rosenfeld 94ecee5a1fSHans Rosenfeld 95ecee5a1fSHans Rosenfeld static void usage(const nvmeadm_cmd_t *); 96ecee5a1fSHans Rosenfeld static void nvme_walk(nvme_process_arg_t *, di_node_t); 97ecee5a1fSHans Rosenfeld static boolean_t nvme_match(nvme_process_arg_t *); 98ecee5a1fSHans Rosenfeld 99ecee5a1fSHans Rosenfeld static int nvme_process(di_node_t, di_minor_t, void *); 100ecee5a1fSHans Rosenfeld 101ecee5a1fSHans Rosenfeld static int do_list(int, const nvme_process_arg_t *); 102ecee5a1fSHans Rosenfeld static int do_identify(int, const nvme_process_arg_t *); 103ecee5a1fSHans Rosenfeld static int do_get_logpage_error(int, const nvme_process_arg_t *); 104ecee5a1fSHans Rosenfeld static int do_get_logpage_health(int, const nvme_process_arg_t *); 105ecee5a1fSHans Rosenfeld static int do_get_logpage_fwslot(int, const nvme_process_arg_t *); 106ecee5a1fSHans Rosenfeld static int do_get_logpage(int, const nvme_process_arg_t *); 107ecee5a1fSHans Rosenfeld static int do_get_feat_common(int, const nvme_feature_t *, 108ecee5a1fSHans Rosenfeld nvme_identify_ctrl_t *); 109ecee5a1fSHans Rosenfeld static int do_get_feat_intr_vect(int, const nvme_feature_t *, 110ecee5a1fSHans Rosenfeld nvme_identify_ctrl_t *); 111ecee5a1fSHans Rosenfeld static int do_get_features(int, const nvme_process_arg_t *); 112ecee5a1fSHans Rosenfeld static int do_format(int, const nvme_process_arg_t *); 113ecee5a1fSHans Rosenfeld static int do_secure_erase(int, const nvme_process_arg_t *); 114ecee5a1fSHans Rosenfeld static int do_attach_detach(int, const nvme_process_arg_t *); 115ecee5a1fSHans Rosenfeld 116ecee5a1fSHans Rosenfeld static void usage_list(const char *); 117ecee5a1fSHans Rosenfeld static void usage_identify(const char *); 118ecee5a1fSHans Rosenfeld static void usage_get_logpage(const char *); 119ecee5a1fSHans Rosenfeld static void usage_get_features(const char *); 120ecee5a1fSHans Rosenfeld static void usage_format(const char *); 121ecee5a1fSHans Rosenfeld static void usage_secure_erase(const char *); 122ecee5a1fSHans Rosenfeld static void usage_attach_detach(const char *); 123ecee5a1fSHans Rosenfeld 124ecee5a1fSHans Rosenfeld int verbose; 125ecee5a1fSHans Rosenfeld int debug; 126ecee5a1fSHans Rosenfeld static int exitcode; 127ecee5a1fSHans Rosenfeld 128ecee5a1fSHans Rosenfeld static const nvmeadm_cmd_t nvmeadm_cmds[] = { 129ecee5a1fSHans Rosenfeld { 130ecee5a1fSHans Rosenfeld "list", 131ecee5a1fSHans Rosenfeld "list controllers and namespaces", 132ecee5a1fSHans Rosenfeld NULL, 133ecee5a1fSHans Rosenfeld do_list, usage_list, B_TRUE 134ecee5a1fSHans Rosenfeld }, 135ecee5a1fSHans Rosenfeld { 136ecee5a1fSHans Rosenfeld "identify", 137ecee5a1fSHans Rosenfeld "identify controllers and/or namespaces", 138ecee5a1fSHans Rosenfeld NULL, 139ecee5a1fSHans Rosenfeld do_identify, usage_identify, B_TRUE 140ecee5a1fSHans Rosenfeld }, 141ecee5a1fSHans Rosenfeld { 142ecee5a1fSHans Rosenfeld "get-logpage", 143ecee5a1fSHans Rosenfeld "get a log page from controllers and/or namespaces", 144ecee5a1fSHans Rosenfeld NULL, 145ecee5a1fSHans Rosenfeld do_get_logpage, usage_get_logpage, B_TRUE 146ecee5a1fSHans Rosenfeld }, 147ecee5a1fSHans Rosenfeld { 148ecee5a1fSHans Rosenfeld "get-features", 149ecee5a1fSHans Rosenfeld "get features from controllers and/or namespaces", 150ecee5a1fSHans Rosenfeld NULL, 151ecee5a1fSHans Rosenfeld do_get_features, usage_get_features, B_TRUE 152ecee5a1fSHans Rosenfeld }, 153ecee5a1fSHans Rosenfeld { 154ecee5a1fSHans Rosenfeld "format", 155ecee5a1fSHans Rosenfeld "format namespace(s) of a controller", 156ecee5a1fSHans Rosenfeld NULL, 157ecee5a1fSHans Rosenfeld do_format, usage_format, B_FALSE 158ecee5a1fSHans Rosenfeld }, 159ecee5a1fSHans Rosenfeld { 160ecee5a1fSHans Rosenfeld "secure-erase", 161ecee5a1fSHans Rosenfeld "secure erase namespace(s) of a controller", 162ecee5a1fSHans Rosenfeld " -c Do a cryptographic erase.", 163ecee5a1fSHans Rosenfeld do_secure_erase, usage_secure_erase, B_FALSE 164ecee5a1fSHans Rosenfeld }, 165ecee5a1fSHans Rosenfeld { 166ecee5a1fSHans Rosenfeld "detach", 167ecee5a1fSHans Rosenfeld "detach blkdev(7d) from namespace(s) of a controller", 168ecee5a1fSHans Rosenfeld NULL, 169ecee5a1fSHans Rosenfeld do_attach_detach, usage_attach_detach, B_FALSE 170ecee5a1fSHans Rosenfeld }, 171ecee5a1fSHans Rosenfeld { 172ecee5a1fSHans Rosenfeld "attach", 173ecee5a1fSHans Rosenfeld "attach blkdev(7d) to namespace(s) of a controller", 174ecee5a1fSHans Rosenfeld NULL, 175ecee5a1fSHans Rosenfeld do_attach_detach, usage_attach_detach, B_FALSE 176ecee5a1fSHans Rosenfeld }, 177ecee5a1fSHans Rosenfeld { 178ecee5a1fSHans Rosenfeld NULL, NULL, NULL, 179ecee5a1fSHans Rosenfeld NULL, NULL, B_FALSE 180ecee5a1fSHans Rosenfeld } 181ecee5a1fSHans Rosenfeld }; 182ecee5a1fSHans Rosenfeld 183ecee5a1fSHans Rosenfeld static const nvme_feature_t features[] = { 184ecee5a1fSHans Rosenfeld { "Arbitration", "", 185ecee5a1fSHans Rosenfeld NVME_FEAT_ARBITRATION, 0, NVMEADM_CTRL, 186ecee5a1fSHans Rosenfeld do_get_feat_common, nvme_print_feat_arbitration }, 187ecee5a1fSHans Rosenfeld { "Power Management", "", 188ecee5a1fSHans Rosenfeld NVME_FEAT_POWER_MGMT, 0, NVMEADM_CTRL, 189ecee5a1fSHans Rosenfeld do_get_feat_common, nvme_print_feat_power_mgmt }, 190ecee5a1fSHans Rosenfeld { "LBA Range Type", "range", 191ecee5a1fSHans Rosenfeld NVME_FEAT_LBA_RANGE, NVME_LBA_RANGE_BUFSIZE, NVMEADM_NS, 192ecee5a1fSHans Rosenfeld do_get_feat_common, nvme_print_feat_lba_range }, 193ecee5a1fSHans Rosenfeld { "Temperature Threshold", "", 194ecee5a1fSHans Rosenfeld NVME_FEAT_TEMPERATURE, 0, NVMEADM_CTRL, 195ecee5a1fSHans Rosenfeld do_get_feat_common, nvme_print_feat_temperature }, 196ecee5a1fSHans Rosenfeld { "Error Recovery", "", 197ecee5a1fSHans Rosenfeld NVME_FEAT_ERROR, 0, NVMEADM_CTRL, 198ecee5a1fSHans Rosenfeld do_get_feat_common, nvme_print_feat_error }, 199ecee5a1fSHans Rosenfeld { "Volatile Write Cache", "cache", 200ecee5a1fSHans Rosenfeld NVME_FEAT_WRITE_CACHE, 0, NVMEADM_CTRL, 201ecee5a1fSHans Rosenfeld do_get_feat_common, nvme_print_feat_write_cache }, 202ecee5a1fSHans Rosenfeld { "Number of Queues", "queues", 203ecee5a1fSHans Rosenfeld NVME_FEAT_NQUEUES, 0, NVMEADM_CTRL, 204ecee5a1fSHans Rosenfeld do_get_feat_common, nvme_print_feat_nqueues }, 205ecee5a1fSHans Rosenfeld { "Interrupt Coalescing", "coalescing", 206ecee5a1fSHans Rosenfeld NVME_FEAT_INTR_COAL, 0, NVMEADM_CTRL, 207ecee5a1fSHans Rosenfeld do_get_feat_common, nvme_print_feat_intr_coal }, 208ecee5a1fSHans Rosenfeld { "Interrupt Vector Configuration", "vector", 209ecee5a1fSHans Rosenfeld NVME_FEAT_INTR_VECT, 0, NVMEADM_CTRL, 210ecee5a1fSHans Rosenfeld do_get_feat_intr_vect, nvme_print_feat_intr_vect }, 211ecee5a1fSHans Rosenfeld { "Write Atomicity", "atomicity", 212ecee5a1fSHans Rosenfeld NVME_FEAT_WRITE_ATOM, 0, NVMEADM_CTRL, 213ecee5a1fSHans Rosenfeld do_get_feat_common, nvme_print_feat_write_atom }, 214ecee5a1fSHans Rosenfeld { "Asynchronous Event Configuration", "event", 215ecee5a1fSHans Rosenfeld NVME_FEAT_ASYNC_EVENT, 0, NVMEADM_CTRL, 216ecee5a1fSHans Rosenfeld do_get_feat_common, nvme_print_feat_async_event }, 217ecee5a1fSHans Rosenfeld { "Autonomous Power State Transition", "", 218ecee5a1fSHans Rosenfeld NVME_FEAT_AUTO_PST, NVME_AUTO_PST_BUFSIZE, NVMEADM_CTRL, 219ecee5a1fSHans Rosenfeld do_get_feat_common, nvme_print_feat_auto_pst }, 220ecee5a1fSHans Rosenfeld { "Software Progress Marker", "progress", 221ecee5a1fSHans Rosenfeld NVME_FEAT_PROGRESS, 0, NVMEADM_CTRL, 222ecee5a1fSHans Rosenfeld do_get_feat_common, nvme_print_feat_progress }, 223ecee5a1fSHans Rosenfeld { NULL, NULL, 0, 0, B_FALSE, NULL } 224ecee5a1fSHans Rosenfeld }; 225ecee5a1fSHans Rosenfeld 226ecee5a1fSHans Rosenfeld 227ecee5a1fSHans Rosenfeld int 228ecee5a1fSHans Rosenfeld main(int argc, char **argv) 229ecee5a1fSHans Rosenfeld { 230ecee5a1fSHans Rosenfeld int c; 231ecee5a1fSHans Rosenfeld extern int optind; 232ecee5a1fSHans Rosenfeld const nvmeadm_cmd_t *cmd; 233ecee5a1fSHans Rosenfeld di_node_t node; 234ecee5a1fSHans Rosenfeld nvme_process_arg_t npa = { 0 }; 235ecee5a1fSHans Rosenfeld int help = 0; 236ecee5a1fSHans Rosenfeld char *tmp, *lasts = NULL; 237ecee5a1fSHans Rosenfeld 238ecee5a1fSHans Rosenfeld while ((c = getopt(argc, argv, "dhv")) != -1) { 239ecee5a1fSHans Rosenfeld switch (c) { 240ecee5a1fSHans Rosenfeld case 'd': 241ecee5a1fSHans Rosenfeld debug++; 242ecee5a1fSHans Rosenfeld break; 243ecee5a1fSHans Rosenfeld case 'v': 244ecee5a1fSHans Rosenfeld verbose++; 245ecee5a1fSHans Rosenfeld break; 246ecee5a1fSHans Rosenfeld case 'h': 247ecee5a1fSHans Rosenfeld help++; 248ecee5a1fSHans Rosenfeld break; 249ecee5a1fSHans Rosenfeld case '?': 250ecee5a1fSHans Rosenfeld usage(NULL); 251ecee5a1fSHans Rosenfeld exit(-1); 252ecee5a1fSHans Rosenfeld } 253ecee5a1fSHans Rosenfeld } 254ecee5a1fSHans Rosenfeld 255ecee5a1fSHans Rosenfeld if (optind == argc) { 256ecee5a1fSHans Rosenfeld usage(NULL); 257ecee5a1fSHans Rosenfeld if (help) 258ecee5a1fSHans Rosenfeld exit(0); 259ecee5a1fSHans Rosenfeld else 260ecee5a1fSHans Rosenfeld exit(-1); 261ecee5a1fSHans Rosenfeld } 262ecee5a1fSHans Rosenfeld 263ecee5a1fSHans Rosenfeld /* Look up the specified command in the command table. */ 264ecee5a1fSHans Rosenfeld for (cmd = &nvmeadm_cmds[0]; cmd->c_name != NULL; cmd++) 265ecee5a1fSHans Rosenfeld if (strcmp(cmd->c_name, argv[optind]) == 0) 266ecee5a1fSHans Rosenfeld break; 267ecee5a1fSHans Rosenfeld 268ecee5a1fSHans Rosenfeld if (cmd->c_name == NULL) { 269ecee5a1fSHans Rosenfeld usage(NULL); 270ecee5a1fSHans Rosenfeld exit(-1); 271ecee5a1fSHans Rosenfeld } 272ecee5a1fSHans Rosenfeld 273ecee5a1fSHans Rosenfeld if (help) { 274ecee5a1fSHans Rosenfeld usage(cmd); 275ecee5a1fSHans Rosenfeld exit(0); 276ecee5a1fSHans Rosenfeld } 277ecee5a1fSHans Rosenfeld 278ecee5a1fSHans Rosenfeld npa.npa_cmd = cmd; 279ecee5a1fSHans Rosenfeld 280ecee5a1fSHans Rosenfeld optind++; 281ecee5a1fSHans Rosenfeld 282ecee5a1fSHans Rosenfeld /* 283ecee5a1fSHans Rosenfeld * All commands but "list" require a ctl/ns argument. 284ecee5a1fSHans Rosenfeld */ 285ecee5a1fSHans Rosenfeld if ((optind == argc || (strncmp(argv[optind], "nvme", 4) != 0)) && 286ecee5a1fSHans Rosenfeld cmd->c_func != do_list) { 287ecee5a1fSHans Rosenfeld warnx("missing controller/namespace name"); 288ecee5a1fSHans Rosenfeld usage(cmd); 289ecee5a1fSHans Rosenfeld exit(-1); 290ecee5a1fSHans Rosenfeld } 291ecee5a1fSHans Rosenfeld 292ecee5a1fSHans Rosenfeld 293ecee5a1fSHans Rosenfeld /* Store the remaining arguments for use by the command. */ 294ecee5a1fSHans Rosenfeld npa.npa_argc = argc - optind - 1; 295ecee5a1fSHans Rosenfeld npa.npa_argv = &argv[optind + 1]; 296ecee5a1fSHans Rosenfeld 297ecee5a1fSHans Rosenfeld /* 298ecee5a1fSHans Rosenfeld * Make sure we're not running commands on multiple controllers that 299ecee5a1fSHans Rosenfeld * aren't allowed to do that. 300ecee5a1fSHans Rosenfeld */ 301ecee5a1fSHans Rosenfeld if (argv[optind] != NULL && strchr(argv[optind], ',') != NULL && 302ecee5a1fSHans Rosenfeld cmd->c_multi == B_FALSE) { 303ecee5a1fSHans Rosenfeld warnx("%s not allowed on multiple controllers", 304ecee5a1fSHans Rosenfeld cmd->c_name); 305ecee5a1fSHans Rosenfeld usage(cmd); 306ecee5a1fSHans Rosenfeld exit(-1); 307ecee5a1fSHans Rosenfeld } 308ecee5a1fSHans Rosenfeld 309ecee5a1fSHans Rosenfeld /* 310ecee5a1fSHans Rosenfeld * Get controller/namespace arguments and run command. 311ecee5a1fSHans Rosenfeld */ 312ecee5a1fSHans Rosenfeld npa.npa_name = strtok_r(argv[optind], ",", &lasts); 313ecee5a1fSHans Rosenfeld do { 314ecee5a1fSHans Rosenfeld if (npa.npa_name != NULL) { 315ecee5a1fSHans Rosenfeld tmp = strchr(npa.npa_name, '/'); 316ecee5a1fSHans Rosenfeld if (tmp != NULL) { 317ecee5a1fSHans Rosenfeld *tmp++ = '\0'; 3180b68f179SHans Rosenfeld npa.npa_nsid = tmp; 319ecee5a1fSHans Rosenfeld npa.npa_isns = B_TRUE; 320ecee5a1fSHans Rosenfeld } 321ecee5a1fSHans Rosenfeld } 322ecee5a1fSHans Rosenfeld 323ecee5a1fSHans Rosenfeld if ((node = di_init("/", DINFOSUBTREE | DINFOMINOR)) == NULL) 324ecee5a1fSHans Rosenfeld err(-1, "failed to initialize libdevinfo"); 325ecee5a1fSHans Rosenfeld nvme_walk(&npa, node); 326ecee5a1fSHans Rosenfeld di_fini(node); 327ecee5a1fSHans Rosenfeld 3280b68f179SHans Rosenfeld if (npa.npa_found == 0) { 329ecee5a1fSHans Rosenfeld if (npa.npa_name != NULL) { 3300b68f179SHans Rosenfeld warnx("%s%.*s%.*s: no such controller or " 331ecee5a1fSHans Rosenfeld "namespace", npa.npa_name, 3320b68f179SHans Rosenfeld npa.npa_isns ? -1 : 0, "/", 3330b68f179SHans Rosenfeld npa.npa_isns ? -1 : 0, npa.npa_nsid); 334ecee5a1fSHans Rosenfeld } else { 335ecee5a1fSHans Rosenfeld warnx("no controllers found"); 336ecee5a1fSHans Rosenfeld } 337ecee5a1fSHans Rosenfeld exitcode--; 338ecee5a1fSHans Rosenfeld } 3390b68f179SHans Rosenfeld npa.npa_found = 0; 340ecee5a1fSHans Rosenfeld npa.npa_name = strtok_r(NULL, ",", &lasts); 341ecee5a1fSHans Rosenfeld } while (npa.npa_name != NULL); 342ecee5a1fSHans Rosenfeld 343ecee5a1fSHans Rosenfeld exit(exitcode); 344ecee5a1fSHans Rosenfeld } 345ecee5a1fSHans Rosenfeld 346ecee5a1fSHans Rosenfeld static void 347ecee5a1fSHans Rosenfeld usage(const nvmeadm_cmd_t *cmd) 348ecee5a1fSHans Rosenfeld { 349ecee5a1fSHans Rosenfeld (void) fprintf(stderr, "usage:\n"); 350ecee5a1fSHans Rosenfeld (void) fprintf(stderr, " %s -h %s\n", getprogname(), 351ecee5a1fSHans Rosenfeld cmd != NULL ? cmd->c_name : "[<command>]"); 352ecee5a1fSHans Rosenfeld (void) fprintf(stderr, " %s [-dv] ", getprogname()); 353ecee5a1fSHans Rosenfeld 354ecee5a1fSHans Rosenfeld if (cmd != NULL) { 355ecee5a1fSHans Rosenfeld cmd->c_usage(cmd->c_name); 356ecee5a1fSHans Rosenfeld } else { 357ecee5a1fSHans Rosenfeld (void) fprintf(stderr, 358ecee5a1fSHans Rosenfeld "<command> <ctl>[/<ns>][,...] [<args>]\n"); 359ecee5a1fSHans Rosenfeld (void) fprintf(stderr, 360ecee5a1fSHans Rosenfeld "\n Manage NVMe controllers and namespaces.\n"); 361ecee5a1fSHans Rosenfeld (void) fprintf(stderr, "\ncommands:\n"); 362ecee5a1fSHans Rosenfeld 363ecee5a1fSHans Rosenfeld for (cmd = &nvmeadm_cmds[0]; cmd->c_name != NULL; cmd++) 364ecee5a1fSHans Rosenfeld (void) fprintf(stderr, " %-15s - %s\n", 365ecee5a1fSHans Rosenfeld cmd->c_name, cmd->c_desc); 366ecee5a1fSHans Rosenfeld } 367ecee5a1fSHans Rosenfeld (void) fprintf(stderr, "\nflags:\n" 368ecee5a1fSHans Rosenfeld " -h print usage information\n" 369ecee5a1fSHans Rosenfeld " -d print information useful for debugging %s\n" 370ecee5a1fSHans Rosenfeld " -v print verbose information\n", getprogname()); 371ecee5a1fSHans Rosenfeld if (cmd != NULL && cmd->c_flagdesc != NULL) 372ecee5a1fSHans Rosenfeld (void) fprintf(stderr, "%s\n", cmd->c_flagdesc); 373ecee5a1fSHans Rosenfeld } 374ecee5a1fSHans Rosenfeld 375ecee5a1fSHans Rosenfeld static boolean_t 376ecee5a1fSHans Rosenfeld nvme_match(nvme_process_arg_t *npa) 377ecee5a1fSHans Rosenfeld { 378ecee5a1fSHans Rosenfeld char *name; 3790b68f179SHans Rosenfeld char *nsid = NULL; 380ecee5a1fSHans Rosenfeld 381ecee5a1fSHans Rosenfeld if (npa->npa_name == NULL) 382ecee5a1fSHans Rosenfeld return (B_TRUE); 383ecee5a1fSHans Rosenfeld 384ecee5a1fSHans Rosenfeld if (asprintf(&name, "%s%d", di_driver_name(npa->npa_node), 385ecee5a1fSHans Rosenfeld di_instance(npa->npa_node)) < 0) 386ecee5a1fSHans Rosenfeld err(-1, "nvme_match()"); 387ecee5a1fSHans Rosenfeld 388ecee5a1fSHans Rosenfeld if (strcmp(name, npa->npa_name) != 0) { 389ecee5a1fSHans Rosenfeld free(name); 390ecee5a1fSHans Rosenfeld return (B_FALSE); 391ecee5a1fSHans Rosenfeld } 392ecee5a1fSHans Rosenfeld 393ecee5a1fSHans Rosenfeld free(name); 394ecee5a1fSHans Rosenfeld 395ecee5a1fSHans Rosenfeld if (npa->npa_isns) { 3960b68f179SHans Rosenfeld if (npa->npa_nsid == NULL) 397ecee5a1fSHans Rosenfeld return (B_TRUE); 398ecee5a1fSHans Rosenfeld 3990b68f179SHans Rosenfeld nsid = di_minor_name(npa->npa_minor); 4000b68f179SHans Rosenfeld 4010b68f179SHans Rosenfeld if (nsid == NULL || strcmp(npa->npa_nsid, nsid) != 0) 402ecee5a1fSHans Rosenfeld return (B_FALSE); 4030b68f179SHans Rosenfeld } 404ecee5a1fSHans Rosenfeld 405ecee5a1fSHans Rosenfeld return (B_TRUE); 406ecee5a1fSHans Rosenfeld } 407ecee5a1fSHans Rosenfeld 408ecee5a1fSHans Rosenfeld char * 409ecee5a1fSHans Rosenfeld nvme_dskname(const nvme_process_arg_t *npa) 410ecee5a1fSHans Rosenfeld { 411ecee5a1fSHans Rosenfeld char *path = NULL; 412ecee5a1fSHans Rosenfeld di_node_t child; 413ecee5a1fSHans Rosenfeld di_dim_t dim; 414ecee5a1fSHans Rosenfeld char *addr; 415ecee5a1fSHans Rosenfeld 416ecee5a1fSHans Rosenfeld dim = di_dim_init(); 417ecee5a1fSHans Rosenfeld 418ecee5a1fSHans Rosenfeld for (child = di_child_node(npa->npa_node); 419ecee5a1fSHans Rosenfeld child != DI_NODE_NIL; 420ecee5a1fSHans Rosenfeld child = di_sibling_node(child)) { 421ecee5a1fSHans Rosenfeld addr = di_bus_addr(child); 422ecee5a1fSHans Rosenfeld if (addr == NULL) 423ecee5a1fSHans Rosenfeld continue; 424ecee5a1fSHans Rosenfeld 425ecee5a1fSHans Rosenfeld if (addr[0] == 'w') 426ecee5a1fSHans Rosenfeld addr++; 427ecee5a1fSHans Rosenfeld 428ecee5a1fSHans Rosenfeld if (strncasecmp(addr, di_minor_name(npa->npa_minor), 429ecee5a1fSHans Rosenfeld strchrnul(addr, ',') - addr) != 0) 430ecee5a1fSHans Rosenfeld continue; 431ecee5a1fSHans Rosenfeld 432ecee5a1fSHans Rosenfeld path = di_dim_path_dev(dim, di_driver_name(child), 433ecee5a1fSHans Rosenfeld di_instance(child), "c"); 434ecee5a1fSHans Rosenfeld 435*a856e1a6SHans Rosenfeld /* 436*a856e1a6SHans Rosenfeld * Error out if we didn't get a path, or if it's too short for 437*a856e1a6SHans Rosenfeld * the following operations to be safe. 438*a856e1a6SHans Rosenfeld */ 439*a856e1a6SHans Rosenfeld if (path == NULL || strlen(path) < 2) 440*a856e1a6SHans Rosenfeld goto fail; 441*a856e1a6SHans Rosenfeld 442*a856e1a6SHans Rosenfeld /* Chop off 's0' and get everything past the last '/' */ 443ecee5a1fSHans Rosenfeld path[strlen(path) - 2] = '\0'; 444*a856e1a6SHans Rosenfeld path = strrchr(path, '/'); 445ecee5a1fSHans Rosenfeld if (path == NULL) 446*a856e1a6SHans Rosenfeld goto fail; 447*a856e1a6SHans Rosenfeld path++; 448ecee5a1fSHans Rosenfeld 449ecee5a1fSHans Rosenfeld break; 450ecee5a1fSHans Rosenfeld } 451ecee5a1fSHans Rosenfeld 452ecee5a1fSHans Rosenfeld di_dim_fini(dim); 453*a856e1a6SHans Rosenfeld 454ecee5a1fSHans Rosenfeld return (path); 455*a856e1a6SHans Rosenfeld 456*a856e1a6SHans Rosenfeld fail: 457*a856e1a6SHans Rosenfeld err(-1, "nvme_dskname"); 458ecee5a1fSHans Rosenfeld } 459ecee5a1fSHans Rosenfeld 460ecee5a1fSHans Rosenfeld static int 461ecee5a1fSHans Rosenfeld nvme_process(di_node_t node, di_minor_t minor, void *arg) 462ecee5a1fSHans Rosenfeld { 463ecee5a1fSHans Rosenfeld nvme_process_arg_t *npa = arg; 464ecee5a1fSHans Rosenfeld int fd; 465ecee5a1fSHans Rosenfeld 466ecee5a1fSHans Rosenfeld npa->npa_node = node; 467ecee5a1fSHans Rosenfeld npa->npa_minor = minor; 468ecee5a1fSHans Rosenfeld 469ecee5a1fSHans Rosenfeld if (!nvme_match(npa)) 470ecee5a1fSHans Rosenfeld return (DI_WALK_CONTINUE); 471ecee5a1fSHans Rosenfeld 472ecee5a1fSHans Rosenfeld if ((fd = nvme_open(minor)) < 0) 473ecee5a1fSHans Rosenfeld return (DI_WALK_CONTINUE); 474ecee5a1fSHans Rosenfeld 4750b68f179SHans Rosenfeld npa->npa_found++; 476ecee5a1fSHans Rosenfeld 477ecee5a1fSHans Rosenfeld npa->npa_path = di_devfs_path(node); 478ecee5a1fSHans Rosenfeld if (npa->npa_path == NULL) 479ecee5a1fSHans Rosenfeld goto out; 480ecee5a1fSHans Rosenfeld 481ecee5a1fSHans Rosenfeld npa->npa_version = nvme_version(fd); 482ecee5a1fSHans Rosenfeld if (npa->npa_version == NULL) 483ecee5a1fSHans Rosenfeld goto out; 484ecee5a1fSHans Rosenfeld 485ecee5a1fSHans Rosenfeld npa->npa_idctl = nvme_identify_ctrl(fd); 486ecee5a1fSHans Rosenfeld if (npa->npa_idctl == NULL) 487ecee5a1fSHans Rosenfeld goto out; 488ecee5a1fSHans Rosenfeld 489ecee5a1fSHans Rosenfeld npa->npa_idns = nvme_identify_nsid(fd); 490ecee5a1fSHans Rosenfeld if (npa->npa_idns == NULL) 491ecee5a1fSHans Rosenfeld goto out; 492ecee5a1fSHans Rosenfeld 493ecee5a1fSHans Rosenfeld if (npa->npa_isns) 494ecee5a1fSHans Rosenfeld npa->npa_dsk = nvme_dskname(npa); 495ecee5a1fSHans Rosenfeld 496ecee5a1fSHans Rosenfeld exitcode += npa->npa_cmd->c_func(fd, npa); 497ecee5a1fSHans Rosenfeld 498ecee5a1fSHans Rosenfeld out: 499ecee5a1fSHans Rosenfeld di_devfs_path_free(npa->npa_path); 500ecee5a1fSHans Rosenfeld free(npa->npa_dsk); 501ecee5a1fSHans Rosenfeld free(npa->npa_version); 502ecee5a1fSHans Rosenfeld free(npa->npa_idctl); 503ecee5a1fSHans Rosenfeld free(npa->npa_idns); 504ecee5a1fSHans Rosenfeld 505ecee5a1fSHans Rosenfeld npa->npa_version = NULL; 506ecee5a1fSHans Rosenfeld npa->npa_idctl = NULL; 507ecee5a1fSHans Rosenfeld npa->npa_idns = NULL; 508ecee5a1fSHans Rosenfeld 509ecee5a1fSHans Rosenfeld nvme_close(fd); 510ecee5a1fSHans Rosenfeld 511ecee5a1fSHans Rosenfeld return (DI_WALK_CONTINUE); 512ecee5a1fSHans Rosenfeld } 513ecee5a1fSHans Rosenfeld 514ecee5a1fSHans Rosenfeld static void 515ecee5a1fSHans Rosenfeld nvme_walk(nvme_process_arg_t *npa, di_node_t node) 516ecee5a1fSHans Rosenfeld { 517ecee5a1fSHans Rosenfeld char *minor_nodetype = DDI_NT_NVME_NEXUS; 518ecee5a1fSHans Rosenfeld 519ecee5a1fSHans Rosenfeld if (npa->npa_isns) 520ecee5a1fSHans Rosenfeld minor_nodetype = DDI_NT_NVME_ATTACHMENT_POINT; 521ecee5a1fSHans Rosenfeld 522ecee5a1fSHans Rosenfeld (void) di_walk_minor(node, minor_nodetype, 0, npa, nvme_process); 523ecee5a1fSHans Rosenfeld } 524ecee5a1fSHans Rosenfeld 525ecee5a1fSHans Rosenfeld static void 526ecee5a1fSHans Rosenfeld usage_list(const char *c_name) 527ecee5a1fSHans Rosenfeld { 528ecee5a1fSHans Rosenfeld (void) fprintf(stderr, "%s [<ctl>[/<ns>][,...]\n\n" 529ecee5a1fSHans Rosenfeld " List NVMe controllers and their namespaces. If no " 530ecee5a1fSHans Rosenfeld "controllers and/or name-\n spaces are specified, all " 531ecee5a1fSHans Rosenfeld "controllers and namespaces in the system will be\n " 532ecee5a1fSHans Rosenfeld "listed.\n", c_name); 533ecee5a1fSHans Rosenfeld } 534ecee5a1fSHans Rosenfeld 535ecee5a1fSHans Rosenfeld static int 536ecee5a1fSHans Rosenfeld do_list_nsid(int fd, const nvme_process_arg_t *npa) 537ecee5a1fSHans Rosenfeld { 538ecee5a1fSHans Rosenfeld _NOTE(ARGUNUSED(fd)); 539111e6ba8SHans Rosenfeld const uint_t format = npa->npa_idns->id_flbas.lba_format; 540111e6ba8SHans Rosenfeld const uint_t bshift = npa->npa_idns->id_lbaf[format].lbaf_lbads; 541111e6ba8SHans Rosenfeld 542111e6ba8SHans Rosenfeld /* 543111e6ba8SHans Rosenfeld * Some devices have extra namespaces with illegal block sizes and 544111e6ba8SHans Rosenfeld * zero blocks. Don't list them when verbose operation isn't requested. 545111e6ba8SHans Rosenfeld */ 546111e6ba8SHans Rosenfeld if ((bshift < 9 || npa->npa_idns->id_nsize == 0) && verbose == 0) 547111e6ba8SHans Rosenfeld return (0); 548ecee5a1fSHans Rosenfeld 549ecee5a1fSHans Rosenfeld (void) printf(" %s/%s (%s): ", npa->npa_name, 550ecee5a1fSHans Rosenfeld di_minor_name(npa->npa_minor), 551ecee5a1fSHans Rosenfeld npa->npa_dsk != NULL ? npa->npa_dsk : "unattached"); 552ecee5a1fSHans Rosenfeld nvme_print_nsid_summary(npa->npa_idns); 553ecee5a1fSHans Rosenfeld 554ecee5a1fSHans Rosenfeld return (0); 555ecee5a1fSHans Rosenfeld } 556ecee5a1fSHans Rosenfeld 557ecee5a1fSHans Rosenfeld static int 558ecee5a1fSHans Rosenfeld do_list(int fd, const nvme_process_arg_t *npa) 559ecee5a1fSHans Rosenfeld { 560ecee5a1fSHans Rosenfeld _NOTE(ARGUNUSED(fd)); 561ecee5a1fSHans Rosenfeld 562ecee5a1fSHans Rosenfeld nvme_process_arg_t ns_npa = { 0 }; 563ecee5a1fSHans Rosenfeld nvmeadm_cmd_t cmd = { 0 }; 564ecee5a1fSHans Rosenfeld char *name; 565ecee5a1fSHans Rosenfeld 566ecee5a1fSHans Rosenfeld if (asprintf(&name, "%s%d", di_driver_name(npa->npa_node), 567ecee5a1fSHans Rosenfeld di_instance(npa->npa_node)) < 0) 568ecee5a1fSHans Rosenfeld err(-1, "do_list()"); 569ecee5a1fSHans Rosenfeld 570ecee5a1fSHans Rosenfeld (void) printf("%s: ", name); 571ecee5a1fSHans Rosenfeld nvme_print_ctrl_summary(npa->npa_idctl, npa->npa_version); 572ecee5a1fSHans Rosenfeld 573ecee5a1fSHans Rosenfeld ns_npa.npa_name = name; 574ecee5a1fSHans Rosenfeld ns_npa.npa_isns = B_TRUE; 575ecee5a1fSHans Rosenfeld ns_npa.npa_nsid = npa->npa_nsid; 576ecee5a1fSHans Rosenfeld cmd = *(npa->npa_cmd); 577ecee5a1fSHans Rosenfeld cmd.c_func = do_list_nsid; 578ecee5a1fSHans Rosenfeld ns_npa.npa_cmd = &cmd; 579ecee5a1fSHans Rosenfeld 580ecee5a1fSHans Rosenfeld nvme_walk(&ns_npa, npa->npa_node); 581ecee5a1fSHans Rosenfeld 582ecee5a1fSHans Rosenfeld free(name); 583ecee5a1fSHans Rosenfeld 584ecee5a1fSHans Rosenfeld return (exitcode); 585ecee5a1fSHans Rosenfeld } 586ecee5a1fSHans Rosenfeld 587ecee5a1fSHans Rosenfeld static void 588ecee5a1fSHans Rosenfeld usage_identify(const char *c_name) 589ecee5a1fSHans Rosenfeld { 590ecee5a1fSHans Rosenfeld (void) fprintf(stderr, "%s <ctl>[/<ns>][,...]\n\n" 591ecee5a1fSHans Rosenfeld " Print detailed information about the specified NVMe " 592ecee5a1fSHans Rosenfeld "controllers and/or name-\n spaces.\n", c_name); 593ecee5a1fSHans Rosenfeld } 594ecee5a1fSHans Rosenfeld 595ecee5a1fSHans Rosenfeld static int 596ecee5a1fSHans Rosenfeld do_identify(int fd, const nvme_process_arg_t *npa) 597ecee5a1fSHans Rosenfeld { 5980b68f179SHans Rosenfeld if (!npa->npa_isns) { 599ecee5a1fSHans Rosenfeld nvme_capabilities_t *cap; 600ecee5a1fSHans Rosenfeld 601ecee5a1fSHans Rosenfeld cap = nvme_capabilities(fd); 602ecee5a1fSHans Rosenfeld if (cap == NULL) 603ecee5a1fSHans Rosenfeld return (-1); 604ecee5a1fSHans Rosenfeld 605ecee5a1fSHans Rosenfeld (void) printf("%s: ", npa->npa_name); 606ecee5a1fSHans Rosenfeld nvme_print_identify_ctrl(npa->npa_idctl, cap, 607ecee5a1fSHans Rosenfeld npa->npa_version); 608ecee5a1fSHans Rosenfeld 609ecee5a1fSHans Rosenfeld free(cap); 610ecee5a1fSHans Rosenfeld } else { 611ecee5a1fSHans Rosenfeld (void) printf("%s/%s: ", npa->npa_name, 612ecee5a1fSHans Rosenfeld di_minor_name(npa->npa_minor)); 613ecee5a1fSHans Rosenfeld nvme_print_identify_nsid(npa->npa_idns, 614ecee5a1fSHans Rosenfeld npa->npa_version); 615ecee5a1fSHans Rosenfeld } 616ecee5a1fSHans Rosenfeld 617ecee5a1fSHans Rosenfeld return (0); 618ecee5a1fSHans Rosenfeld } 619ecee5a1fSHans Rosenfeld 620ecee5a1fSHans Rosenfeld static void 621ecee5a1fSHans Rosenfeld usage_get_logpage(const char *c_name) 622ecee5a1fSHans Rosenfeld { 623ecee5a1fSHans Rosenfeld (void) fprintf(stderr, "%s <ctl>[/<ns>][,...] <logpage>\n\n" 624ecee5a1fSHans Rosenfeld " Print the specified log page of the specified NVMe " 625ecee5a1fSHans Rosenfeld "controllers and/or name-\n spaces. Supported log pages " 626ecee5a1fSHans Rosenfeld "are error, health, and firmware.\n", c_name); 627ecee5a1fSHans Rosenfeld } 628ecee5a1fSHans Rosenfeld 629ecee5a1fSHans Rosenfeld static int 630ecee5a1fSHans Rosenfeld do_get_logpage_error(int fd, const nvme_process_arg_t *npa) 631ecee5a1fSHans Rosenfeld { 632ecee5a1fSHans Rosenfeld int nlog = npa->npa_idctl->id_elpe + 1; 633ecee5a1fSHans Rosenfeld size_t bufsize = sizeof (nvme_error_log_entry_t) * nlog; 634ecee5a1fSHans Rosenfeld nvme_error_log_entry_t *elog; 635ecee5a1fSHans Rosenfeld 6360b68f179SHans Rosenfeld if (npa->npa_isns) 637ecee5a1fSHans Rosenfeld errx(-1, "Error Log not available on a per-namespace basis"); 638ecee5a1fSHans Rosenfeld 639ecee5a1fSHans Rosenfeld elog = nvme_get_logpage(fd, NVME_LOGPAGE_ERROR, &bufsize); 640ecee5a1fSHans Rosenfeld 641ecee5a1fSHans Rosenfeld if (elog == NULL) 642ecee5a1fSHans Rosenfeld return (-1); 643ecee5a1fSHans Rosenfeld 644ecee5a1fSHans Rosenfeld nlog = bufsize / sizeof (nvme_error_log_entry_t); 645ecee5a1fSHans Rosenfeld 646ecee5a1fSHans Rosenfeld (void) printf("%s: ", npa->npa_name); 647ecee5a1fSHans Rosenfeld nvme_print_error_log(nlog, elog); 648ecee5a1fSHans Rosenfeld 649ecee5a1fSHans Rosenfeld free(elog); 650ecee5a1fSHans Rosenfeld 651ecee5a1fSHans Rosenfeld return (0); 652ecee5a1fSHans Rosenfeld } 653ecee5a1fSHans Rosenfeld 654ecee5a1fSHans Rosenfeld static int 655ecee5a1fSHans Rosenfeld do_get_logpage_health(int fd, const nvme_process_arg_t *npa) 656ecee5a1fSHans Rosenfeld { 657ecee5a1fSHans Rosenfeld size_t bufsize = sizeof (nvme_health_log_t); 658ecee5a1fSHans Rosenfeld nvme_health_log_t *hlog; 659ecee5a1fSHans Rosenfeld 6600b68f179SHans Rosenfeld if (npa->npa_isns) { 661ecee5a1fSHans Rosenfeld if (npa->npa_idctl->id_lpa.lp_smart == 0) 662ecee5a1fSHans Rosenfeld errx(-1, "SMART/Health information not available " 663ecee5a1fSHans Rosenfeld "on a per-namespace basis on this controller"); 664ecee5a1fSHans Rosenfeld } 665ecee5a1fSHans Rosenfeld 666ecee5a1fSHans Rosenfeld hlog = nvme_get_logpage(fd, NVME_LOGPAGE_HEALTH, &bufsize); 667ecee5a1fSHans Rosenfeld 668ecee5a1fSHans Rosenfeld if (hlog == NULL) 669ecee5a1fSHans Rosenfeld return (-1); 670ecee5a1fSHans Rosenfeld 671ecee5a1fSHans Rosenfeld (void) printf("%s: ", npa->npa_name); 672ecee5a1fSHans Rosenfeld nvme_print_health_log(hlog, npa->npa_idctl); 673ecee5a1fSHans Rosenfeld 674ecee5a1fSHans Rosenfeld free(hlog); 675ecee5a1fSHans Rosenfeld 676ecee5a1fSHans Rosenfeld return (0); 677ecee5a1fSHans Rosenfeld } 678ecee5a1fSHans Rosenfeld 679ecee5a1fSHans Rosenfeld static int 680ecee5a1fSHans Rosenfeld do_get_logpage_fwslot(int fd, const nvme_process_arg_t *npa) 681ecee5a1fSHans Rosenfeld { 682ecee5a1fSHans Rosenfeld size_t bufsize = sizeof (nvme_fwslot_log_t); 683ecee5a1fSHans Rosenfeld nvme_fwslot_log_t *fwlog; 684ecee5a1fSHans Rosenfeld 6850b68f179SHans Rosenfeld if (npa->npa_isns) 686ecee5a1fSHans Rosenfeld errx(-1, "Firmware Slot information not available on a " 687ecee5a1fSHans Rosenfeld "per-namespace basis"); 688ecee5a1fSHans Rosenfeld 689ecee5a1fSHans Rosenfeld fwlog = nvme_get_logpage(fd, NVME_LOGPAGE_FWSLOT, &bufsize); 690ecee5a1fSHans Rosenfeld 691ecee5a1fSHans Rosenfeld if (fwlog == NULL) 692ecee5a1fSHans Rosenfeld return (-1); 693ecee5a1fSHans Rosenfeld 694ecee5a1fSHans Rosenfeld (void) printf("%s: ", npa->npa_name); 695ecee5a1fSHans Rosenfeld nvme_print_fwslot_log(fwlog); 696ecee5a1fSHans Rosenfeld 697ecee5a1fSHans Rosenfeld free(fwlog); 698ecee5a1fSHans Rosenfeld 699ecee5a1fSHans Rosenfeld return (0); 700ecee5a1fSHans Rosenfeld } 701ecee5a1fSHans Rosenfeld 702ecee5a1fSHans Rosenfeld static int 703ecee5a1fSHans Rosenfeld do_get_logpage(int fd, const nvme_process_arg_t *npa) 704ecee5a1fSHans Rosenfeld { 705ecee5a1fSHans Rosenfeld int ret = 0; 706ecee5a1fSHans Rosenfeld int (*func)(int, const nvme_process_arg_t *); 707ecee5a1fSHans Rosenfeld 708ecee5a1fSHans Rosenfeld if (npa->npa_argc < 1) { 709ecee5a1fSHans Rosenfeld warnx("missing logpage name"); 710ecee5a1fSHans Rosenfeld usage(npa->npa_cmd); 711ecee5a1fSHans Rosenfeld exit(-1); 712ecee5a1fSHans Rosenfeld } 713ecee5a1fSHans Rosenfeld 714ecee5a1fSHans Rosenfeld if (strcmp(npa->npa_argv[0], "error") == 0) 715ecee5a1fSHans Rosenfeld func = do_get_logpage_error; 716ecee5a1fSHans Rosenfeld else if (strcmp(npa->npa_argv[0], "health") == 0) 717ecee5a1fSHans Rosenfeld func = do_get_logpage_health; 718ecee5a1fSHans Rosenfeld else if (strcmp(npa->npa_argv[0], "firmware") == 0) 719ecee5a1fSHans Rosenfeld func = do_get_logpage_fwslot; 720ecee5a1fSHans Rosenfeld else 721ecee5a1fSHans Rosenfeld errx(-1, "invalid log page: %s", npa->npa_argv[0]); 722ecee5a1fSHans Rosenfeld 723ecee5a1fSHans Rosenfeld ret = func(fd, npa); 724ecee5a1fSHans Rosenfeld return (ret); 725ecee5a1fSHans Rosenfeld } 726ecee5a1fSHans Rosenfeld 727ecee5a1fSHans Rosenfeld static void 728ecee5a1fSHans Rosenfeld usage_get_features(const char *c_name) 729ecee5a1fSHans Rosenfeld { 730ecee5a1fSHans Rosenfeld const nvme_feature_t *feat; 731ecee5a1fSHans Rosenfeld 732ecee5a1fSHans Rosenfeld (void) fprintf(stderr, "%s <ctl>[/<ns>][,...] [<feature>[,...]]\n\n" 733ecee5a1fSHans Rosenfeld " Print the specified features of the specified NVMe controllers " 734ecee5a1fSHans Rosenfeld "and/or\n namespaces. Supported features are:\n\n", c_name); 735ecee5a1fSHans Rosenfeld (void) fprintf(stderr, " %-35s %-14s %s\n", 736ecee5a1fSHans Rosenfeld "FEATURE NAME", "SHORT NAME", "CONTROLLER/NAMESPACE"); 737ecee5a1fSHans Rosenfeld for (feat = &features[0]; feat->f_feature != 0; feat++) { 738ecee5a1fSHans Rosenfeld char *type; 739ecee5a1fSHans Rosenfeld 740ecee5a1fSHans Rosenfeld if ((feat->f_getflags & NVMEADM_BOTH) == NVMEADM_BOTH) 741ecee5a1fSHans Rosenfeld type = "both"; 742ecee5a1fSHans Rosenfeld else if ((feat->f_getflags & NVMEADM_CTRL) != 0) 743ecee5a1fSHans Rosenfeld type = "controller only"; 744ecee5a1fSHans Rosenfeld else 745ecee5a1fSHans Rosenfeld type = "namespace only"; 746ecee5a1fSHans Rosenfeld 747ecee5a1fSHans Rosenfeld (void) fprintf(stderr, " %-35s %-14s %s\n", 748ecee5a1fSHans Rosenfeld feat->f_name, feat->f_short, type); 749ecee5a1fSHans Rosenfeld } 750ecee5a1fSHans Rosenfeld 751ecee5a1fSHans Rosenfeld } 752ecee5a1fSHans Rosenfeld 753ecee5a1fSHans Rosenfeld static int 754ecee5a1fSHans Rosenfeld do_get_feat_common(int fd, const nvme_feature_t *feat, 755ecee5a1fSHans Rosenfeld nvme_identify_ctrl_t *idctl) 756ecee5a1fSHans Rosenfeld { 757ecee5a1fSHans Rosenfeld void *buf = NULL; 758ecee5a1fSHans Rosenfeld size_t bufsize = feat->f_bufsize; 759ecee5a1fSHans Rosenfeld uint64_t res; 760ecee5a1fSHans Rosenfeld 761ecee5a1fSHans Rosenfeld if (nvme_get_feature(fd, feat->f_feature, 0, &res, &bufsize, &buf) 762ecee5a1fSHans Rosenfeld == B_FALSE) 763ecee5a1fSHans Rosenfeld return (EINVAL); 764ecee5a1fSHans Rosenfeld 765ecee5a1fSHans Rosenfeld nvme_print(2, feat->f_name, -1, NULL); 766ecee5a1fSHans Rosenfeld feat->f_print(res, buf, bufsize, idctl); 767ecee5a1fSHans Rosenfeld free(buf); 768ecee5a1fSHans Rosenfeld 769ecee5a1fSHans Rosenfeld return (0); 770ecee5a1fSHans Rosenfeld } 771ecee5a1fSHans Rosenfeld 772ecee5a1fSHans Rosenfeld static int 773ecee5a1fSHans Rosenfeld do_get_feat_intr_vect(int fd, const nvme_feature_t *feat, 774ecee5a1fSHans Rosenfeld nvme_identify_ctrl_t *idctl) 775ecee5a1fSHans Rosenfeld { 776ecee5a1fSHans Rosenfeld uint64_t res; 777ecee5a1fSHans Rosenfeld uint64_t arg; 778ecee5a1fSHans Rosenfeld int intr_cnt; 779ecee5a1fSHans Rosenfeld 780ecee5a1fSHans Rosenfeld intr_cnt = nvme_intr_cnt(fd); 781ecee5a1fSHans Rosenfeld 782ecee5a1fSHans Rosenfeld if (intr_cnt == -1) 783ecee5a1fSHans Rosenfeld return (EINVAL); 784ecee5a1fSHans Rosenfeld 785ecee5a1fSHans Rosenfeld nvme_print(2, feat->f_name, -1, NULL); 786ecee5a1fSHans Rosenfeld 787ecee5a1fSHans Rosenfeld for (arg = 0; arg < intr_cnt; arg++) { 788ecee5a1fSHans Rosenfeld if (nvme_get_feature(fd, feat->f_feature, arg, &res, NULL, NULL) 789ecee5a1fSHans Rosenfeld == B_FALSE) 790ecee5a1fSHans Rosenfeld return (EINVAL); 791ecee5a1fSHans Rosenfeld 792ecee5a1fSHans Rosenfeld feat->f_print(res, NULL, 0, idctl); 793ecee5a1fSHans Rosenfeld } 794ecee5a1fSHans Rosenfeld 795ecee5a1fSHans Rosenfeld return (0); 796ecee5a1fSHans Rosenfeld } 797ecee5a1fSHans Rosenfeld 798ecee5a1fSHans Rosenfeld static int 799ecee5a1fSHans Rosenfeld do_get_features(int fd, const nvme_process_arg_t *npa) 800ecee5a1fSHans Rosenfeld { 801ecee5a1fSHans Rosenfeld const nvme_feature_t *feat; 802ecee5a1fSHans Rosenfeld char *f, *flist, *lasts; 803ecee5a1fSHans Rosenfeld boolean_t header_printed = B_FALSE; 804ecee5a1fSHans Rosenfeld 805ecee5a1fSHans Rosenfeld if (npa->npa_argc > 1) 806ecee5a1fSHans Rosenfeld errx(-1, "unexpected arguments"); 807ecee5a1fSHans Rosenfeld 808ecee5a1fSHans Rosenfeld /* 809ecee5a1fSHans Rosenfeld * No feature list given, print all supported features. 810ecee5a1fSHans Rosenfeld */ 811ecee5a1fSHans Rosenfeld if (npa->npa_argc == 0) { 812ecee5a1fSHans Rosenfeld (void) printf("%s: Get Features\n", npa->npa_name); 813ecee5a1fSHans Rosenfeld for (feat = &features[0]; feat->f_feature != 0; feat++) { 8140b68f179SHans Rosenfeld if ((npa->npa_isns && 815ecee5a1fSHans Rosenfeld (feat->f_getflags & NVMEADM_NS) == 0) || 8160b68f179SHans Rosenfeld (!npa->npa_isns && 817ecee5a1fSHans Rosenfeld (feat->f_getflags & NVMEADM_CTRL) == 0)) 818ecee5a1fSHans Rosenfeld continue; 819ecee5a1fSHans Rosenfeld 820ecee5a1fSHans Rosenfeld (void) feat->f_get(fd, feat, npa->npa_idctl); 821ecee5a1fSHans Rosenfeld } 822ecee5a1fSHans Rosenfeld 823ecee5a1fSHans Rosenfeld return (0); 824ecee5a1fSHans Rosenfeld } 825ecee5a1fSHans Rosenfeld 826ecee5a1fSHans Rosenfeld /* 827ecee5a1fSHans Rosenfeld * Process feature list. 828ecee5a1fSHans Rosenfeld */ 829ecee5a1fSHans Rosenfeld flist = strdup(npa->npa_argv[0]); 830ecee5a1fSHans Rosenfeld if (flist == NULL) 831ecee5a1fSHans Rosenfeld err(-1, "do_get_features"); 832ecee5a1fSHans Rosenfeld 833ecee5a1fSHans Rosenfeld for (f = strtok_r(flist, ",", &lasts); 834ecee5a1fSHans Rosenfeld f != NULL; 835ecee5a1fSHans Rosenfeld f = strtok_r(NULL, ",", &lasts)) { 836ecee5a1fSHans Rosenfeld while (isspace(*f)) 837ecee5a1fSHans Rosenfeld f++; 838ecee5a1fSHans Rosenfeld 839ecee5a1fSHans Rosenfeld for (feat = &features[0]; feat->f_feature != 0; feat++) { 840ecee5a1fSHans Rosenfeld if (strncasecmp(feat->f_name, f, strlen(f)) == 0 || 841ecee5a1fSHans Rosenfeld strncasecmp(feat->f_short, f, strlen(f)) == 0) 842ecee5a1fSHans Rosenfeld break; 843ecee5a1fSHans Rosenfeld } 844ecee5a1fSHans Rosenfeld 845ecee5a1fSHans Rosenfeld if (feat->f_feature == 0) { 846ecee5a1fSHans Rosenfeld warnx("unknown feature %s", f); 847ecee5a1fSHans Rosenfeld continue; 848ecee5a1fSHans Rosenfeld } 849ecee5a1fSHans Rosenfeld 8500b68f179SHans Rosenfeld if ((npa->npa_isns && 851ecee5a1fSHans Rosenfeld (feat->f_getflags & NVMEADM_NS) == 0) || 8520b68f179SHans Rosenfeld (!npa->npa_isns && 853ecee5a1fSHans Rosenfeld (feat->f_getflags & NVMEADM_CTRL) == 0)) { 854ecee5a1fSHans Rosenfeld warnx("feature %s %s supported for namespaces", 855ecee5a1fSHans Rosenfeld feat->f_name, (feat->f_getflags & NVMEADM_NS) != 0 ? 856ecee5a1fSHans Rosenfeld "only" : "not"); 857ecee5a1fSHans Rosenfeld continue; 858ecee5a1fSHans Rosenfeld } 859ecee5a1fSHans Rosenfeld 860ecee5a1fSHans Rosenfeld if (!header_printed) { 861ecee5a1fSHans Rosenfeld (void) printf("%s: Get Features\n", npa->npa_name); 862ecee5a1fSHans Rosenfeld header_printed = B_TRUE; 863ecee5a1fSHans Rosenfeld } 864ecee5a1fSHans Rosenfeld 865ecee5a1fSHans Rosenfeld if (feat->f_get(fd, feat, npa->npa_idctl) != 0) { 866ecee5a1fSHans Rosenfeld warnx("unsupported feature: %s", feat->f_name); 867ecee5a1fSHans Rosenfeld continue; 868ecee5a1fSHans Rosenfeld } 869ecee5a1fSHans Rosenfeld } 870ecee5a1fSHans Rosenfeld 871ecee5a1fSHans Rosenfeld free(flist); 872ecee5a1fSHans Rosenfeld return (0); 873ecee5a1fSHans Rosenfeld } 874ecee5a1fSHans Rosenfeld 875ecee5a1fSHans Rosenfeld static int 876ecee5a1fSHans Rosenfeld do_format_common(int fd, const nvme_process_arg_t *npa, unsigned long lbaf, 877ecee5a1fSHans Rosenfeld unsigned long ses) 878ecee5a1fSHans Rosenfeld { 879ecee5a1fSHans Rosenfeld nvme_process_arg_t ns_npa = { 0 }; 880ecee5a1fSHans Rosenfeld nvmeadm_cmd_t cmd = { 0 }; 881ecee5a1fSHans Rosenfeld 882ecee5a1fSHans Rosenfeld cmd = *(npa->npa_cmd); 883ecee5a1fSHans Rosenfeld cmd.c_func = do_attach_detach; 884ecee5a1fSHans Rosenfeld cmd.c_name = "detach"; 885ecee5a1fSHans Rosenfeld ns_npa = *npa; 886ecee5a1fSHans Rosenfeld ns_npa.npa_cmd = &cmd; 887ecee5a1fSHans Rosenfeld 888ecee5a1fSHans Rosenfeld if (do_attach_detach(fd, &ns_npa) != 0) 889ecee5a1fSHans Rosenfeld return (exitcode); 890ecee5a1fSHans Rosenfeld if (nvme_format_nvm(fd, lbaf, ses) == B_FALSE) { 891ecee5a1fSHans Rosenfeld warn("%s failed", npa->npa_cmd->c_name); 892ecee5a1fSHans Rosenfeld exitcode += -1; 893ecee5a1fSHans Rosenfeld } 894ecee5a1fSHans Rosenfeld cmd.c_name = "attach"; 895ecee5a1fSHans Rosenfeld exitcode += do_attach_detach(fd, &ns_npa); 896ecee5a1fSHans Rosenfeld 897ecee5a1fSHans Rosenfeld return (exitcode); 898ecee5a1fSHans Rosenfeld } 899ecee5a1fSHans Rosenfeld 900ecee5a1fSHans Rosenfeld static void 901ecee5a1fSHans Rosenfeld usage_format(const char *c_name) 902ecee5a1fSHans Rosenfeld { 903ecee5a1fSHans Rosenfeld (void) fprintf(stderr, "%s <ctl>[/<ns>] [<lba-format>]\n\n" 904ecee5a1fSHans Rosenfeld " Format one or all namespaces of the specified NVMe " 905ecee5a1fSHans Rosenfeld "controller. Supported LBA\n formats can be queried with " 906ecee5a1fSHans Rosenfeld "the \"%s identify\" command on the namespace\n to be " 907ecee5a1fSHans Rosenfeld "formatted.\n", c_name, getprogname()); 908ecee5a1fSHans Rosenfeld } 909ecee5a1fSHans Rosenfeld 910ecee5a1fSHans Rosenfeld static int 911ecee5a1fSHans Rosenfeld do_format(int fd, const nvme_process_arg_t *npa) 912ecee5a1fSHans Rosenfeld { 913ecee5a1fSHans Rosenfeld unsigned long lbaf; 914ecee5a1fSHans Rosenfeld 915ecee5a1fSHans Rosenfeld if (npa->npa_idctl->id_oacs.oa_format == 0) 916ecee5a1fSHans Rosenfeld errx(-1, "%s not supported", npa->npa_cmd->c_name); 917ecee5a1fSHans Rosenfeld 918ecee5a1fSHans Rosenfeld if (npa->npa_isns && npa->npa_idctl->id_fna.fn_format != 0) 919ecee5a1fSHans Rosenfeld errx(-1, "%s not supported on individual namespace", 920ecee5a1fSHans Rosenfeld npa->npa_cmd->c_name); 921ecee5a1fSHans Rosenfeld 922ecee5a1fSHans Rosenfeld 923ecee5a1fSHans Rosenfeld if (npa->npa_argc > 0) { 924ecee5a1fSHans Rosenfeld errno = 0; 925ecee5a1fSHans Rosenfeld lbaf = strtoul(npa->npa_argv[0], NULL, 10); 926ecee5a1fSHans Rosenfeld 927ecee5a1fSHans Rosenfeld if (errno != 0 || lbaf > NVME_FRMT_MAX_LBAF) 928ecee5a1fSHans Rosenfeld errx(-1, "invalid LBA format %d", lbaf + 1); 929ecee5a1fSHans Rosenfeld 930ecee5a1fSHans Rosenfeld if (npa->npa_idns->id_lbaf[lbaf].lbaf_ms != 0) 931ecee5a1fSHans Rosenfeld errx(-1, "LBA formats with metadata not supported"); 932ecee5a1fSHans Rosenfeld } else { 933ecee5a1fSHans Rosenfeld lbaf = npa->npa_idns->id_flbas.lba_format; 934ecee5a1fSHans Rosenfeld } 935ecee5a1fSHans Rosenfeld 936ecee5a1fSHans Rosenfeld return (do_format_common(fd, npa, lbaf, 0)); 937ecee5a1fSHans Rosenfeld } 938ecee5a1fSHans Rosenfeld 939ecee5a1fSHans Rosenfeld static void 940ecee5a1fSHans Rosenfeld usage_secure_erase(const char *c_name) 941ecee5a1fSHans Rosenfeld { 942ecee5a1fSHans Rosenfeld (void) fprintf(stderr, "%s <ctl>[/<ns>] [-c]\n\n" 943ecee5a1fSHans Rosenfeld " Secure-Erase one or all namespaces of the specified " 944ecee5a1fSHans Rosenfeld "NVMe controller.\n", c_name); 945ecee5a1fSHans Rosenfeld } 946ecee5a1fSHans Rosenfeld 947ecee5a1fSHans Rosenfeld static int 948ecee5a1fSHans Rosenfeld do_secure_erase(int fd, const nvme_process_arg_t *npa) 949ecee5a1fSHans Rosenfeld { 950ecee5a1fSHans Rosenfeld unsigned long lbaf; 951ecee5a1fSHans Rosenfeld uint8_t ses = NVME_FRMT_SES_USER; 952ecee5a1fSHans Rosenfeld 953ecee5a1fSHans Rosenfeld if (npa->npa_idctl->id_oacs.oa_format == 0) 954ecee5a1fSHans Rosenfeld errx(-1, "%s not supported", npa->npa_cmd->c_name); 955ecee5a1fSHans Rosenfeld 956ecee5a1fSHans Rosenfeld if (npa->npa_isns && npa->npa_idctl->id_fna.fn_sec_erase != 0) 957ecee5a1fSHans Rosenfeld errx(-1, "%s not supported on individual namespace", 958ecee5a1fSHans Rosenfeld npa->npa_cmd->c_name); 959ecee5a1fSHans Rosenfeld 960ecee5a1fSHans Rosenfeld if (npa->npa_argc > 0) { 961ecee5a1fSHans Rosenfeld if (strcmp(npa->npa_argv[0], "-c") == 0) 962ecee5a1fSHans Rosenfeld ses = NVME_FRMT_SES_CRYPTO; 963ecee5a1fSHans Rosenfeld else 964ecee5a1fSHans Rosenfeld usage(npa->npa_cmd); 965ecee5a1fSHans Rosenfeld } 966ecee5a1fSHans Rosenfeld 967ecee5a1fSHans Rosenfeld if (ses == NVME_FRMT_SES_CRYPTO && 968ecee5a1fSHans Rosenfeld npa->npa_idctl->id_fna.fn_crypt_erase == 0) 969ecee5a1fSHans Rosenfeld errx(-1, "cryptographic %s not supported", 970ecee5a1fSHans Rosenfeld npa->npa_cmd->c_name); 971ecee5a1fSHans Rosenfeld 972ecee5a1fSHans Rosenfeld lbaf = npa->npa_idns->id_flbas.lba_format; 973ecee5a1fSHans Rosenfeld 974ecee5a1fSHans Rosenfeld return (do_format_common(fd, npa, lbaf, ses)); 975ecee5a1fSHans Rosenfeld } 976ecee5a1fSHans Rosenfeld 977ecee5a1fSHans Rosenfeld static void 978ecee5a1fSHans Rosenfeld usage_attach_detach(const char *c_name) 979ecee5a1fSHans Rosenfeld { 980ecee5a1fSHans Rosenfeld (void) fprintf(stderr, "%s <ctl>[/<ns>]\n\n" 981ecee5a1fSHans Rosenfeld " %c%s blkdev(7d) %s one or all namespaces of the " 982ecee5a1fSHans Rosenfeld "specified NVMe controller.\n", 983ecee5a1fSHans Rosenfeld c_name, toupper(c_name[0]), &c_name[1], 984ecee5a1fSHans Rosenfeld c_name[0] == 'd' ? "from" : "to"); 985ecee5a1fSHans Rosenfeld } 986ecee5a1fSHans Rosenfeld 987ecee5a1fSHans Rosenfeld static int 988ecee5a1fSHans Rosenfeld do_attach_detach(int fd, const nvme_process_arg_t *npa) 989ecee5a1fSHans Rosenfeld { 990ecee5a1fSHans Rosenfeld char *c_name = npa->npa_cmd->c_name; 991ecee5a1fSHans Rosenfeld 992ecee5a1fSHans Rosenfeld if (!npa->npa_isns) { 993ecee5a1fSHans Rosenfeld nvme_process_arg_t ns_npa = { 0 }; 994ecee5a1fSHans Rosenfeld 995ecee5a1fSHans Rosenfeld ns_npa.npa_name = npa->npa_name; 996ecee5a1fSHans Rosenfeld ns_npa.npa_isns = B_TRUE; 997ecee5a1fSHans Rosenfeld ns_npa.npa_cmd = npa->npa_cmd; 998ecee5a1fSHans Rosenfeld 999ecee5a1fSHans Rosenfeld nvme_walk(&ns_npa, npa->npa_node); 1000ecee5a1fSHans Rosenfeld 1001ecee5a1fSHans Rosenfeld return (exitcode); 1002ecee5a1fSHans Rosenfeld } else { 1003ecee5a1fSHans Rosenfeld if ((c_name[0] == 'd' ? nvme_detach : nvme_attach)(fd) 1004ecee5a1fSHans Rosenfeld == B_FALSE) { 1005ecee5a1fSHans Rosenfeld warn("%s failed", c_name); 1006ecee5a1fSHans Rosenfeld return (-1); 1007ecee5a1fSHans Rosenfeld } 1008ecee5a1fSHans Rosenfeld } 1009ecee5a1fSHans Rosenfeld 1010ecee5a1fSHans Rosenfeld return (0); 1011ecee5a1fSHans Rosenfeld } 1012