xref: /freebsd/sbin/nvmecontrol/identify.c (revision 5bf5ca772c6de2d53344a78cf461447cc322ccea)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (C) 2012-2013 Intel Corporation
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28 
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
31 
32 #include <sys/param.h>
33 
34 #include <ctype.h>
35 #include <err.h>
36 #include <fcntl.h>
37 #include <stddef.h>
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include <unistd.h>
42 
43 #include "nvmecontrol.h"
44 
45 static void
46 print_controller(struct nvme_controller_data *cdata)
47 {
48 	uint8_t str[128];
49 	char cbuf[UINT128_DIG + 1];
50 	uint16_t oncs, oacs;
51 	uint8_t compare, write_unc, dsm, vwc_present;
52 	uint8_t security, fmt, fw, nsmgmt;
53 	uint8_t	fw_slot1_ro, fw_num_slots;
54 	uint8_t ns_smart;
55 	uint8_t sqes_max, sqes_min;
56 	uint8_t cqes_max, cqes_min;
57 
58 	oncs = cdata->oncs;
59 	compare = (oncs >> NVME_CTRLR_DATA_ONCS_COMPARE_SHIFT) &
60 		NVME_CTRLR_DATA_ONCS_COMPARE_MASK;
61 	write_unc = (oncs >> NVME_CTRLR_DATA_ONCS_WRITE_UNC_SHIFT) &
62 		NVME_CTRLR_DATA_ONCS_WRITE_UNC_MASK;
63 	dsm = (oncs >> NVME_CTRLR_DATA_ONCS_DSM_SHIFT) &
64 		NVME_CTRLR_DATA_ONCS_DSM_MASK;
65 	vwc_present = (cdata->vwc >> NVME_CTRLR_DATA_VWC_PRESENT_SHIFT) &
66 		NVME_CTRLR_DATA_VWC_PRESENT_MASK;
67 
68 	oacs = cdata->oacs;
69 	security = (oacs >> NVME_CTRLR_DATA_OACS_SECURITY_SHIFT) &
70 		NVME_CTRLR_DATA_OACS_SECURITY_MASK;
71 	fmt = (oacs >> NVME_CTRLR_DATA_OACS_FORMAT_SHIFT) &
72 		NVME_CTRLR_DATA_OACS_FORMAT_MASK;
73 	fw = (oacs >> NVME_CTRLR_DATA_OACS_FIRMWARE_SHIFT) &
74 		NVME_CTRLR_DATA_OACS_FIRMWARE_MASK;
75 	nsmgmt = (oacs >> NVME_CTRLR_DATA_OACS_NSMGMT_SHIFT) &
76 		NVME_CTRLR_DATA_OACS_NSMGMT_MASK;
77 
78 	fw_num_slots = (cdata->frmw >> NVME_CTRLR_DATA_FRMW_NUM_SLOTS_SHIFT) &
79 		NVME_CTRLR_DATA_FRMW_NUM_SLOTS_MASK;
80 	fw_slot1_ro = (cdata->frmw >> NVME_CTRLR_DATA_FRMW_SLOT1_RO_SHIFT) &
81 		NVME_CTRLR_DATA_FRMW_SLOT1_RO_MASK;
82 
83 	ns_smart = (cdata->lpa >> NVME_CTRLR_DATA_LPA_NS_SMART_SHIFT) &
84 		NVME_CTRLR_DATA_LPA_NS_SMART_MASK;
85 
86 	sqes_min = (cdata->sqes >> NVME_CTRLR_DATA_SQES_MIN_SHIFT) &
87 		NVME_CTRLR_DATA_SQES_MIN_MASK;
88 	sqes_max = (cdata->sqes >> NVME_CTRLR_DATA_SQES_MAX_SHIFT) &
89 		NVME_CTRLR_DATA_SQES_MAX_MASK;
90 
91 	cqes_min = (cdata->cqes >> NVME_CTRLR_DATA_CQES_MIN_SHIFT) &
92 		NVME_CTRLR_DATA_CQES_MIN_MASK;
93 	cqes_max = (cdata->cqes >> NVME_CTRLR_DATA_CQES_MAX_SHIFT) &
94 		NVME_CTRLR_DATA_CQES_MAX_MASK;
95 
96 	printf("Controller Capabilities/Features\n");
97 	printf("================================\n");
98 	printf("Vendor ID:                  %04x\n", cdata->vid);
99 	printf("Subsystem Vendor ID:        %04x\n", cdata->ssvid);
100 	nvme_strvis(str, cdata->sn, sizeof(str), NVME_SERIAL_NUMBER_LENGTH);
101 	printf("Serial Number:              %s\n", str);
102 	nvme_strvis(str, cdata->mn, sizeof(str), NVME_MODEL_NUMBER_LENGTH);
103 	printf("Model Number:               %s\n", str);
104 	nvme_strvis(str, cdata->fr, sizeof(str), NVME_FIRMWARE_REVISION_LENGTH);
105 	printf("Firmware Version:           %s\n", str);
106 	printf("Recommended Arb Burst:      %d\n", cdata->rab);
107 	printf("IEEE OUI Identifier:        %02x %02x %02x\n",
108 		cdata->ieee[0], cdata->ieee[1], cdata->ieee[2]);
109 	printf("Multi-Interface Cap:        %02x\n", cdata->mic);
110 	/* TODO: Use CAP.MPSMIN to determine true memory page size. */
111 	printf("Max Data Transfer Size:     ");
112 	if (cdata->mdts == 0)
113 		printf("Unlimited\n");
114 	else
115 		printf("%d\n", PAGE_SIZE * (1 << cdata->mdts));
116 	printf("Controller ID:              0x%02x\n", cdata->ctrlr_id);
117 	printf("\n");
118 
119 	printf("Admin Command Set Attributes\n");
120 	printf("============================\n");
121 	printf("Security Send/Receive:       %s\n",
122 		security ? "Supported" : "Not Supported");
123 	printf("Format NVM:                  %s\n",
124 		fmt ? "Supported" : "Not Supported");
125 	printf("Firmware Activate/Download:  %s\n",
126 		fw ? "Supported" : "Not Supported");
127 	printf("Namespace Managment:         %s\n",
128 		nsmgmt ? "Supported" : "Not Supported");
129 	printf("Abort Command Limit:         %d\n", cdata->acl+1);
130 	printf("Async Event Request Limit:   %d\n", cdata->aerl+1);
131 	printf("Number of Firmware Slots:    ");
132 	if (fw != 0)
133 		printf("%d\n", fw_num_slots);
134 	else
135 		printf("N/A\n");
136 	printf("Firmware Slot 1 Read-Only:   ");
137 	if (fw != 0)
138 		printf("%s\n", fw_slot1_ro ? "Yes" : "No");
139 	else
140 		printf("N/A\n");
141 	printf("Per-Namespace SMART Log:     %s\n",
142 		ns_smart ? "Yes" : "No");
143 	printf("Error Log Page Entries:      %d\n", cdata->elpe+1);
144 	printf("Number of Power States:      %d\n", cdata->npss+1);
145 
146 	printf("\n");
147 	printf("NVM Command Set Attributes\n");
148 	printf("==========================\n");
149 	printf("Submission Queue Entry Size\n");
150 	printf("  Max:                       %d\n", 1 << sqes_max);
151 	printf("  Min:                       %d\n", 1 << sqes_min);
152 	printf("Completion Queue Entry Size\n");
153 	printf("  Max:                       %d\n", 1 << cqes_max);
154 	printf("  Min:                       %d\n", 1 << cqes_min);
155 	printf("Number of Namespaces:        %d\n", cdata->nn);
156 	printf("Compare Command:             %s\n",
157 		compare ? "Supported" : "Not Supported");
158 	printf("Write Uncorrectable Command: %s\n",
159 		write_unc ? "Supported" : "Not Supported");
160 	printf("Dataset Management Command:  %s\n",
161 		dsm ? "Supported" : "Not Supported");
162 	printf("Volatile Write Cache:        %s\n",
163 		vwc_present ? "Present" : "Not Present");
164 
165 	if (nsmgmt) {
166 		printf("\n");
167 		printf("Namespace Drive Attributes\n");
168 		printf("==========================\n");
169 		printf("NVM total cap:               %s\n",
170 			   uint128_to_str(to128(cdata->untncap.tnvmcap), cbuf, sizeof(cbuf)));
171 		printf("NVM unallocated cap:         %s\n",
172 			   uint128_to_str(to128(cdata->untncap.unvmcap), cbuf, sizeof(cbuf)));
173 	}
174 }
175 
176 static void
177 print_namespace(struct nvme_namespace_data *nsdata)
178 {
179 	uint32_t	i;
180 	uint32_t	lbaf, lbads, ms;
181 	uint8_t		thin_prov;
182 	uint8_t		flbas_fmt;
183 
184 	thin_prov = (nsdata->nsfeat >> NVME_NS_DATA_NSFEAT_THIN_PROV_SHIFT) &
185 		NVME_NS_DATA_NSFEAT_THIN_PROV_MASK;
186 
187 	flbas_fmt = (nsdata->flbas >> NVME_NS_DATA_FLBAS_FORMAT_SHIFT) &
188 		NVME_NS_DATA_FLBAS_FORMAT_MASK;
189 
190 	printf("Size (in LBAs):              %lld (%lldM)\n",
191 		(long long)nsdata->nsze,
192 		(long long)nsdata->nsze / 1024 / 1024);
193 	printf("Capacity (in LBAs):          %lld (%lldM)\n",
194 		(long long)nsdata->ncap,
195 		(long long)nsdata->ncap / 1024 / 1024);
196 	printf("Utilization (in LBAs):       %lld (%lldM)\n",
197 		(long long)nsdata->nuse,
198 		(long long)nsdata->nuse / 1024 / 1024);
199 	printf("Thin Provisioning:           %s\n",
200 		thin_prov ? "Supported" : "Not Supported");
201 	printf("Number of LBA Formats:       %d\n", nsdata->nlbaf+1);
202 	printf("Current LBA Format:          LBA Format #%02d\n", flbas_fmt);
203 	for (i = 0; i <= nsdata->nlbaf; i++) {
204 		lbaf = nsdata->lbaf[i];
205 		lbads = (lbaf >> NVME_NS_DATA_LBAF_LBADS_SHIFT) &
206 			NVME_NS_DATA_LBAF_LBADS_MASK;
207 		ms = (lbaf >> NVME_NS_DATA_LBAF_MS_SHIFT) &
208 			NVME_NS_DATA_LBAF_MS_MASK;
209 		printf("LBA Format #%02d: Data Size: %5d  Metadata Size: %5d\n",
210 		    i, 1 << lbads, ms);
211 	}
212 }
213 
214 static void
215 identify_usage(void)
216 {
217 	fprintf(stderr, "usage:\n");
218 	fprintf(stderr, IDENTIFY_USAGE);
219 	exit(1);
220 }
221 
222 static void
223 identify_ctrlr(int argc, char *argv[])
224 {
225 	struct nvme_controller_data	cdata;
226 	int				ch, fd, hexflag = 0, hexlength;
227 	int				verboseflag = 0;
228 
229 	while ((ch = getopt(argc, argv, "vx")) != -1) {
230 		switch ((char)ch) {
231 		case 'v':
232 			verboseflag = 1;
233 			break;
234 		case 'x':
235 			hexflag = 1;
236 			break;
237 		default:
238 			identify_usage();
239 		}
240 	}
241 
242 	/* Check that a controller was specified. */
243 	if (optind >= argc)
244 		identify_usage();
245 
246 	open_dev(argv[optind], &fd, 1, 1);
247 	read_controller_data(fd, &cdata);
248 	close(fd);
249 
250 	if (hexflag == 1) {
251 		if (verboseflag == 1)
252 			hexlength = sizeof(struct nvme_controller_data);
253 		else
254 			hexlength = offsetof(struct nvme_controller_data,
255 			    reserved5);
256 		print_hex(&cdata, hexlength);
257 		exit(0);
258 	}
259 
260 	if (verboseflag == 1) {
261 		fprintf(stderr, "-v not currently supported without -x\n");
262 		identify_usage();
263 	}
264 
265 	print_controller(&cdata);
266 	exit(0);
267 }
268 
269 static void
270 identify_ns(int argc, char *argv[])
271 {
272 	struct nvme_namespace_data	nsdata;
273 	char				path[64];
274 	int				ch, fd, hexflag = 0, hexlength, nsid;
275 	int				verboseflag = 0;
276 
277 	while ((ch = getopt(argc, argv, "vx")) != -1) {
278 		switch ((char)ch) {
279 		case 'v':
280 			verboseflag = 1;
281 			break;
282 		case 'x':
283 			hexflag = 1;
284 			break;
285 		default:
286 			identify_usage();
287 		}
288 	}
289 
290 	/* Check that a namespace was specified. */
291 	if (optind >= argc)
292 		identify_usage();
293 
294 	/*
295 	 * Check if the specified device node exists before continuing.
296 	 *  This is a cleaner check for cases where the correct controller
297 	 *  is specified, but an invalid namespace on that controller.
298 	 */
299 	open_dev(argv[optind], &fd, 1, 1);
300 	close(fd);
301 
302 	/*
303 	 * We send IDENTIFY commands to the controller, not the namespace,
304 	 *  since it is an admin cmd.  The namespace ID will be specified in
305 	 *  the IDENTIFY command itself.  So parse the namespace's device node
306 	 *  string to get the controller substring and namespace ID.
307 	 */
308 	parse_ns_str(argv[optind], path, &nsid);
309 	open_dev(path, &fd, 1, 1);
310 	read_namespace_data(fd, nsid, &nsdata);
311 	close(fd);
312 
313 	if (hexflag == 1) {
314 		if (verboseflag == 1)
315 			hexlength = sizeof(struct nvme_namespace_data);
316 		else
317 			hexlength = offsetof(struct nvme_namespace_data,
318 			    reserved6);
319 		print_hex(&nsdata, hexlength);
320 		exit(0);
321 	}
322 
323 	if (verboseflag == 1) {
324 		fprintf(stderr, "-v not currently supported without -x\n");
325 		identify_usage();
326 	}
327 
328 	print_namespace(&nsdata);
329 	exit(0);
330 }
331 
332 void
333 identify(int argc, char *argv[])
334 {
335 	char	*target;
336 
337 	if (argc < 2)
338 		identify_usage();
339 
340 	while (getopt(argc, argv, "vx") != -1) ;
341 
342 	/* Check that a controller or namespace was specified. */
343 	if (optind >= argc)
344 		identify_usage();
345 
346 	target = argv[optind];
347 
348 	optreset = 1;
349 	optind = 1;
350 
351 	/*
352 	 * If device node contains "ns", we consider it a namespace,
353 	 *  otherwise, consider it a controller.
354 	 */
355 	if (strstr(target, NVME_NS_PREFIX) == NULL)
356 		identify_ctrlr(argc, argv);
357 	else
358 		identify_ns(argc, argv);
359 }
360