Home | History | Annotate | Line # | Download | only in nvmectl
      1 /*	$NetBSD: nvmectl.c,v 1.7 2018/04/18 10:11:44 nonaka Exp $	*/
      2 
      3 /*-
      4  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
      5  *
      6  * Copyright (C) 2012-2013 Intel Corporation
      7  * All rights reserved.
      8  *
      9  * Redistribution and use in source and binary forms, with or without
     10  * modification, are permitted provided that the following conditions
     11  * are met:
     12  * 1. Redistributions of source code must retain the above copyright
     13  *    notice, this list of conditions and the following disclaimer.
     14  * 2. Redistributions in binary form must reproduce the above copyright
     15  *    notice, this list of conditions and the following disclaimer in the
     16  *    documentation and/or other materials provided with the distribution.
     17  *
     18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
     19  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     21  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
     22  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     24  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     27  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     28  * SUCH DAMAGE.
     29  */
     30 
     31 #include <sys/cdefs.h>
     32 #ifndef lint
     33 __RCSID("$NetBSD: nvmectl.c,v 1.7 2018/04/18 10:11:44 nonaka Exp $");
     34 #if 0
     35 __FBSDID("$FreeBSD: head/sbin/nvmecontrol/nvmecontrol.c 329824 2018-02-22 13:32:31Z wma $");
     36 #endif
     37 #endif
     38 
     39 #include <sys/param.h>
     40 #include <sys/ioccom.h>
     41 #include <sys/stat.h>
     42 
     43 #include <ctype.h>
     44 #include <err.h>
     45 #include <errno.h>
     46 #include <fcntl.h>
     47 #include <paths.h>
     48 #include <stdbool.h>
     49 #include <stddef.h>
     50 #include <stdio.h>
     51 #include <stdlib.h>
     52 #include <string.h>
     53 #include <unistd.h>
     54 
     55 #include "nvmectl.h"
     56 
     57 static const struct nvme_function funcs[] = {
     58 	{"devlist",	devlist,	DEVLIST_USAGE},
     59 	{"identify",	identify,	IDENTIFY_USAGE},
     60 #ifdef PERFTEST_USAGE
     61 	{"perftest",	perftest,	PERFTEST_USAGE},
     62 #endif
     63 #ifdef RESET_USAGE
     64 	{"reset",	reset,		RESET_USAGE},
     65 #endif
     66 	{"logpage",	logpage,	LOGPAGE_USAGE},
     67 #ifdef FIRMWARE_USAGE
     68 	{"firmware",	firmware,	FIRMWARE_USAGE},
     69 #endif
     70 	{"power",	power,		POWER_USAGE},
     71 	{"wdc",		wdc,		WDC_USAGE},
     72 	{NULL,		NULL,		NULL},
     73 };
     74 
     75 static __dead void
     76 gen_usage(const struct nvme_function *f)
     77 {
     78 
     79 	fprintf(stderr, "usage:\n");
     80 	while (f->name != NULL) {
     81 		fprintf(stderr, "\t%s %s", getprogname(), f->usage);
     82 		f++;
     83 	}
     84 	exit(1);
     85 }
     86 
     87 __dead void
     88 dispatch(int argc, char *argv[], const struct nvme_function *tbl)
     89 {
     90 	const struct nvme_function *f = tbl;
     91 
     92 	if (argv[1] == NULL)
     93 		gen_usage(tbl);
     94 
     95 	while (f->name != NULL) {
     96 		if (strcmp(argv[1], f->name) == 0)
     97 			f->fn(argc-1, &argv[1]);
     98 		f++;
     99 	}
    100 
    101 	fprintf(stderr, "Unknown command: %s\n", argv[1]);
    102 	gen_usage(tbl);
    103 }
    104 
    105 static void
    106 print_bytes(void *data, uint32_t length)
    107 {
    108 	uint32_t	i, j;
    109 	uint8_t		*p, *end;
    110 
    111 	end = (uint8_t *)data + length;
    112 
    113 	for (i = 0; i < length; i++) {
    114 		p = (uint8_t *)data + (i*16);
    115 		printf("%03x: ", i*16);
    116 		for (j = 0; j < 16 && p < end; j++)
    117 			printf("%02x ", *p++);
    118 		if (p >= end)
    119 			break;
    120 		printf("\n");
    121 	}
    122 	printf("\n");
    123 }
    124 
    125 static void
    126 print_dwords(void *data, uint32_t length)
    127 {
    128 	uint32_t	*p;
    129 	uint32_t	i, j;
    130 
    131 	p = (uint32_t *)data;
    132 	length /= sizeof(uint32_t);
    133 
    134 	for (i = 0; i < length; i+=8) {
    135 		printf("%03x: ", i*4);
    136 		for (j = 0; j < 8; j++)
    137 			printf("%08x ", p[i+j]);
    138 		printf("\n");
    139 	}
    140 
    141 	printf("\n");
    142 }
    143 
    144 void
    145 print_hex(void *data, uint32_t length)
    146 {
    147 	if (length >= sizeof(uint32_t) || length % sizeof(uint32_t) == 0)
    148 		print_dwords(data, length);
    149 	else
    150 		print_bytes(data, length);
    151 }
    152 
    153 void
    154 read_controller_data(int fd, struct nvm_identify_controller *cdata)
    155 {
    156 	struct nvme_pt_command	pt;
    157 
    158 	memset(&pt, 0, sizeof(pt));
    159 	pt.cmd.opcode = NVM_ADMIN_IDENTIFY;
    160 	pt.cmd.cdw10 = 1;
    161 	pt.buf = cdata;
    162 	pt.len = sizeof(*cdata);
    163 	pt.is_read = 1;
    164 
    165 	if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0)
    166 		err(1, "identify request failed");
    167 
    168 	if (nvme_completion_is_error(&pt.cpl))
    169 		errx(1, "identify request returned error");
    170 
    171 	/* Convert data to host endian */
    172 	nvme_identify_controller_swapbytes(cdata);
    173 }
    174 
    175 void
    176 read_namespace_data(int fd, int nsid, struct nvm_identify_namespace *nsdata)
    177 {
    178 	struct nvme_pt_command	pt;
    179 
    180 	memset(&pt, 0, sizeof(pt));
    181 	pt.cmd.opcode = NVM_ADMIN_IDENTIFY;
    182 	pt.cmd.nsid = nsid;
    183 	pt.buf = nsdata;
    184 	pt.len = sizeof(*nsdata);
    185 	pt.is_read = 1;
    186 
    187 	if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0)
    188 		err(1, "identify request failed");
    189 
    190 	if (nvme_completion_is_error(&pt.cpl))
    191 		errx(1, "identify request returned error");
    192 
    193 	/* Convert data to host endian */
    194 	nvme_identify_namespace_swapbytes(nsdata);
    195 }
    196 
    197 int
    198 open_dev(const char *str, int *fd, int show_error, int exit_on_error)
    199 {
    200 	char		full_path[64];
    201 
    202 	if (!strnstr(str, NVME_CTRLR_PREFIX, strlen(NVME_CTRLR_PREFIX))) {
    203 		if (show_error)
    204 			warnx("controller/namespace ids must begin with '%s'",
    205 			    NVME_CTRLR_PREFIX);
    206 		if (exit_on_error)
    207 			exit(1);
    208 		else
    209 			return (EINVAL);
    210 	}
    211 
    212 	snprintf(full_path, sizeof(full_path), _PATH_DEV"%s", str);
    213 	*fd = open(full_path, O_RDWR);
    214 	if (*fd < 0) {
    215 		if (show_error)
    216 			warn("could not open %s", full_path);
    217 		if (exit_on_error)
    218 			exit(1);
    219 		else
    220 			return (errno);
    221 	}
    222 
    223 	return (0);
    224 }
    225 
    226 void
    227 parse_ns_str(const char *ns_str, char *ctrlr_str, int *nsid)
    228 {
    229 	char	*nsloc;
    230 
    231 	/*
    232 	 * Pull the namespace id from the string. +2 skips past the "ns" part
    233 	 *  of the string.  Don't search past 10 characters into the string,
    234 	 *  otherwise we know it is malformed.
    235 	 */
    236 	nsloc = strnstr(ns_str, NVME_NS_PREFIX, 10);
    237 	if (nsloc != NULL)
    238 		*nsid = strtol(nsloc + 2, NULL, 10);
    239 	if (nsloc == NULL || (*nsid == 0 && errno != 0))
    240 		errx(1, "invalid namespace ID '%s'", ns_str);
    241 
    242 	/*
    243 	 * The controller string will include only the nvmX part of the
    244 	 *  nvmeXnsY string.
    245 	 */
    246 	snprintf(ctrlr_str, nsloc - ns_str + 1, "%s", ns_str);
    247 }
    248 
    249 int
    250 main(int argc, char *argv[])
    251 {
    252 	setprogname(argv[0]);
    253 
    254 	if (argc < 2)
    255 		gen_usage(funcs);
    256 
    257 	dispatch(argc, argv, funcs);
    258 
    259 	return (0);
    260 }
    261