xref: /netbsd-src/sbin/nvmectl/identify.c (revision 5a1f79d8975dbebf135131a3bc49a2687296da8b)
1*5a1f79d8Sandvar /*	$NetBSD: identify.c,v 1.9 2022/02/17 14:33:25 andvar Exp $	*/
2cae3c2f4Snonaka 
3cae3c2f4Snonaka /*-
41f5086ecSnonaka  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
51f5086ecSnonaka  *
6cae3c2f4Snonaka  * Copyright (C) 2012-2013 Intel Corporation
7cae3c2f4Snonaka  * All rights reserved.
8cae3c2f4Snonaka  *
9cae3c2f4Snonaka  * Redistribution and use in source and binary forms, with or without
10cae3c2f4Snonaka  * modification, are permitted provided that the following conditions
11cae3c2f4Snonaka  * are met:
12cae3c2f4Snonaka  * 1. Redistributions of source code must retain the above copyright
13cae3c2f4Snonaka  *    notice, this list of conditions and the following disclaimer.
14cae3c2f4Snonaka  * 2. Redistributions in binary form must reproduce the above copyright
15cae3c2f4Snonaka  *    notice, this list of conditions and the following disclaimer in the
16cae3c2f4Snonaka  *    documentation and/or other materials provided with the distribution.
17cae3c2f4Snonaka  *
18cae3c2f4Snonaka  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19cae3c2f4Snonaka  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20cae3c2f4Snonaka  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21cae3c2f4Snonaka  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22cae3c2f4Snonaka  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23cae3c2f4Snonaka  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24cae3c2f4Snonaka  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25cae3c2f4Snonaka  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26cae3c2f4Snonaka  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27cae3c2f4Snonaka  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28cae3c2f4Snonaka  * SUCH DAMAGE.
29cae3c2f4Snonaka  */
30cae3c2f4Snonaka 
31cae3c2f4Snonaka #include <sys/cdefs.h>
32cae3c2f4Snonaka #ifndef lint
33*5a1f79d8Sandvar __RCSID("$NetBSD: identify.c,v 1.9 2022/02/17 14:33:25 andvar Exp $");
34cae3c2f4Snonaka #if 0
356ac76f5bSnonaka __FBSDID("$FreeBSD: head/sbin/nvmecontrol/identify.c 329824 2018-02-22 13:32:31Z wma $");
36cae3c2f4Snonaka #endif
37cae3c2f4Snonaka #endif
38cae3c2f4Snonaka 
39cae3c2f4Snonaka #include <sys/param.h>
40cae3c2f4Snonaka 
41cae3c2f4Snonaka #include <ctype.h>
42cae3c2f4Snonaka #include <err.h>
43cae3c2f4Snonaka #include <fcntl.h>
44cae3c2f4Snonaka #include <stddef.h>
45cae3c2f4Snonaka #include <stdio.h>
46cae3c2f4Snonaka #include <stdlib.h>
47cae3c2f4Snonaka #include <string.h>
48cae3c2f4Snonaka #include <unistd.h>
49cae3c2f4Snonaka 
50cae3c2f4Snonaka #include "nvmectl.h"
51cae3c2f4Snonaka 
52cae3c2f4Snonaka static void
print_controller(struct nvm_identify_controller * cdata)53cae3c2f4Snonaka print_controller(struct nvm_identify_controller *cdata)
54cae3c2f4Snonaka {
55cae3c2f4Snonaka 	uint8_t str[128];
56cae3c2f4Snonaka 
57cae3c2f4Snonaka 	printf("Controller Capabilities/Features\n");
58cae3c2f4Snonaka 	printf("================================\n");
59cae3c2f4Snonaka 	printf("Vendor ID:                  %04x\n", cdata->vid);
60cae3c2f4Snonaka 	printf("Subsystem Vendor ID:        %04x\n", cdata->ssvid);
61cae3c2f4Snonaka 	nvme_strvis(str, sizeof(str), cdata->sn, sizeof(cdata->sn));
62cae3c2f4Snonaka 	printf("Serial Number:              %s\n", str);
63cae3c2f4Snonaka 	nvme_strvis(str, sizeof(str), cdata->mn, sizeof(cdata->mn));
64cae3c2f4Snonaka 	printf("Model Number:               %s\n", str);
65cae3c2f4Snonaka 	nvme_strvis(str, sizeof(str), cdata->fr, sizeof(cdata->fr));
66cae3c2f4Snonaka 	printf("Firmware Version:           %s\n", str);
67cae3c2f4Snonaka 	printf("Recommended Arb Burst:      %d\n", cdata->rab);
68cae3c2f4Snonaka 	printf("IEEE OUI Identifier:        %02x %02x %02x\n",
69cae3c2f4Snonaka 		cdata->ieee[0], cdata->ieee[1], cdata->ieee[2]);
70cae3c2f4Snonaka 	printf("Multi-Interface Cap:        %02x\n", cdata->cmic);
71cae3c2f4Snonaka 	/* TODO: Use CAP.MPSMIN to determine true memory page size. */
72cae3c2f4Snonaka 	printf("Max Data Transfer Size:     ");
73cae3c2f4Snonaka 	if (cdata->mdts == 0)
74cae3c2f4Snonaka 		printf("Unlimited\n");
75cae3c2f4Snonaka 	else
76cae3c2f4Snonaka 		printf("%ld\n", sysconf(_SC_PAGESIZE) * (1 << cdata->mdts));
771f5086ecSnonaka 	printf("Controller ID:              0x%02x\n", cdata->cntlid);
78cae3c2f4Snonaka 	printf("\n");
79cae3c2f4Snonaka 
80cae3c2f4Snonaka 	printf("Admin Command Set Attributes\n");
81cae3c2f4Snonaka 	printf("============================\n");
82cae3c2f4Snonaka 	printf("Security Send/Receive:       %s\n",
83cae3c2f4Snonaka 		(cdata->oacs & NVME_ID_CTRLR_OACS_SECURITY) ?
84cae3c2f4Snonaka 		"Supported" : "Not Supported");
85cae3c2f4Snonaka 	printf("Format NVM:                  %s\n",
86cae3c2f4Snonaka 		(cdata->oacs & NVME_ID_CTRLR_OACS_FORMAT) ?
87cae3c2f4Snonaka 		"Supported" : "Not Supported");
88cae3c2f4Snonaka 	printf("Firmware Activate/Download:  %s\n",
89cae3c2f4Snonaka 		(cdata->oacs & NVME_ID_CTRLR_OACS_FW) ?
90cae3c2f4Snonaka 		"Supported" : "Not Supported");
917f459241Sandvar 	printf("Namespace Management:        %s\n",
921f5086ecSnonaka 		(cdata->oacs & NVME_ID_CTRLR_OACS_NS) ?
931f5086ecSnonaka 		"Supported" : "Not Supported");
94cae3c2f4Snonaka 	printf("Abort Command Limit:         %d\n", cdata->acl+1);
95cae3c2f4Snonaka 	printf("Async Event Request Limit:   %d\n", cdata->aerl+1);
96cae3c2f4Snonaka 	printf("Number of Firmware Slots:    ");
97cae3c2f4Snonaka 	if (cdata->oacs & NVME_ID_CTRLR_OACS_FW)
98cae3c2f4Snonaka 		printf("%d\n",
99cae3c2f4Snonaka 		    (uint8_t)__SHIFTOUT(cdata->frmw, NVME_ID_CTRLR_FRMW_NSLOT));
100cae3c2f4Snonaka 	else
101cae3c2f4Snonaka 		printf("N/A\n");
102cae3c2f4Snonaka 	printf("Firmware Slot 1 Read-Only:   ");
103cae3c2f4Snonaka 	if (cdata->oacs & NVME_ID_CTRLR_OACS_FW)
104cae3c2f4Snonaka 		printf("%s\n", (cdata->frmw & NVME_ID_CTRLR_FRMW_SLOT1_RO) ?
105cae3c2f4Snonaka 		    "Yes" : "No");
106cae3c2f4Snonaka 	else
107cae3c2f4Snonaka 		printf("N/A\n");
108cae3c2f4Snonaka 	printf("Per-Namespace SMART Log:     %s\n",
109cae3c2f4Snonaka 		(cdata->lpa & NVME_ID_CTRLR_LPA_NS_SMART) ? "Yes" : "No");
110cae3c2f4Snonaka 	printf("Error Log Page Entries:      %d\n", cdata->elpe+1);
111cae3c2f4Snonaka 	printf("Number of Power States:      %d\n", cdata->npss+1);
112cae3c2f4Snonaka 	printf("\n");
113cae3c2f4Snonaka 
114cae3c2f4Snonaka 	printf("NVM Command Set Attributes\n");
115cae3c2f4Snonaka 	printf("==========================\n");
116cae3c2f4Snonaka 	printf("Submission Queue Entry Size\n");
117cae3c2f4Snonaka 	printf("  Max:                       %d\n",
118cae3c2f4Snonaka 	    1 << __SHIFTOUT(cdata->sqes, NVME_ID_CTRLR_SQES_MAX));
119cae3c2f4Snonaka 	printf("  Min:                       %d\n",
120cae3c2f4Snonaka 	    1 << __SHIFTOUT(cdata->sqes, NVME_ID_CTRLR_SQES_MIN));
121cae3c2f4Snonaka 	printf("Completion Queue Entry Size\n");
122cae3c2f4Snonaka 	printf("  Max:                       %d\n",
123cae3c2f4Snonaka 	    1 << __SHIFTOUT(cdata->cqes, NVME_ID_CTRLR_CQES_MAX));
124cae3c2f4Snonaka 	printf("  Min:                       %d\n",
125cae3c2f4Snonaka 	    1 << __SHIFTOUT(cdata->cqes, NVME_ID_CTRLR_CQES_MIN));
126cae3c2f4Snonaka 	printf("Number of Namespaces:        %d\n", cdata->nn);
127cae3c2f4Snonaka 	printf("Compare Command:             %s\n",
128cae3c2f4Snonaka 		(cdata->oncs & NVME_ID_CTRLR_ONCS_COMPARE) ?
129cae3c2f4Snonaka 		"Supported" : "Not Supported");
130cae3c2f4Snonaka 	printf("Write Uncorrectable Command: %s\n",
131cae3c2f4Snonaka 		(cdata->oncs & NVME_ID_CTRLR_ONCS_WRITE_UNC) ?
132cae3c2f4Snonaka 		"Supported" : "Not Supported");
133cae3c2f4Snonaka 	printf("Dataset Management Command:  %s\n",
134cae3c2f4Snonaka 		(cdata->oncs & NVME_ID_CTRLR_ONCS_DSM) ?
135cae3c2f4Snonaka 		"Supported" : "Not Supported");
136cae3c2f4Snonaka 	printf("Write Zeroes Command:        %s\n",
137cae3c2f4Snonaka 		(cdata->oncs & NVME_ID_CTRLR_ONCS_WRITE_ZERO) ?
138cae3c2f4Snonaka 		"Supported" : "Not Supported");
139b066f5eeSjdolecek 	printf("Features Save/Select Field:  %s\n",
140cae3c2f4Snonaka 		(cdata->oncs & NVME_ID_CTRLR_ONCS_SET_FEATURES) ?
141cae3c2f4Snonaka 		"Supported" : "Not Supported");
142cae3c2f4Snonaka 	printf("Reservation:                 %s\n",
143cae3c2f4Snonaka 		(cdata->oncs & NVME_ID_CTRLR_ONCS_RESERVATION) ?
144cae3c2f4Snonaka 		"Supported" : "Not Supported");
145cae3c2f4Snonaka 	printf("Volatile Write Cache:        %s\n",
146cae3c2f4Snonaka 		(cdata->vwc & NVME_ID_CTRLR_VWC_PRESENT) ?
147cae3c2f4Snonaka 		"Present" : "Not Present");
14809685c00Sjdolecek 	printf("Autonomous Power State Transitions:    %s\n",
14909685c00Sjdolecek 		(cdata->apsta & NVME_ID_CTRLR_APSTA_PRESENT) ?
15009685c00Sjdolecek 		"Supported" : "Not Supported");
1511f5086ecSnonaka 
1521f5086ecSnonaka 	if (cdata->oacs & NVME_ID_CTRLR_OACS_NS) {
1531f5086ecSnonaka 		printf("\n");
1541f5086ecSnonaka 		printf("Namespace Drive Attributes\n");
1551f5086ecSnonaka 		printf("==========================\n");
1561f5086ecSnonaka 		print_bignum("NVM total cap:               ",
1571f5086ecSnonaka 		    cdata->untncap.tnvmcap, "");
1581f5086ecSnonaka 		print_bignum("NVM unallocated cap:         ",
1591f5086ecSnonaka 		    cdata->untncap.unvmcap, "");
1601f5086ecSnonaka 	}
161cae3c2f4Snonaka }
162cae3c2f4Snonaka 
163cae3c2f4Snonaka static void
print_namespace(struct nvm_identify_namespace * nsdata)164cae3c2f4Snonaka print_namespace(struct nvm_identify_namespace *nsdata)
165cae3c2f4Snonaka {
166cae3c2f4Snonaka 	uint32_t	i;
167cae3c2f4Snonaka 
168cae3c2f4Snonaka 	printf("Size (in LBAs):              %lld (%lldM)\n",
169cae3c2f4Snonaka 		(long long)nsdata->nsze,
170cae3c2f4Snonaka 		(long long)nsdata->nsze / 1024 / 1024);
171cae3c2f4Snonaka 	printf("Capacity (in LBAs):          %lld (%lldM)\n",
172cae3c2f4Snonaka 		(long long)nsdata->ncap,
173cae3c2f4Snonaka 		(long long)nsdata->ncap / 1024 / 1024);
174cae3c2f4Snonaka 	printf("Utilization (in LBAs):       %lld (%lldM)\n",
175cae3c2f4Snonaka 		(long long)nsdata->nuse,
176cae3c2f4Snonaka 		(long long)nsdata->nuse / 1024 / 1024);
177cae3c2f4Snonaka 	printf("Thin Provisioning:           %s\n",
178cae3c2f4Snonaka 		(nsdata->nsfeat & NVME_ID_NS_NSFEAT_THIN_PROV) ?
179cae3c2f4Snonaka 		"Supported" : "Not Supported");
180cae3c2f4Snonaka 	printf("Number of LBA Formats:       %d\n", nsdata->nlbaf+1);
181cae3c2f4Snonaka 	printf("Current LBA Format:          LBA Format #%02d\n",
182cae3c2f4Snonaka 		NVME_ID_NS_FLBAS(nsdata->flbas));
183cae3c2f4Snonaka 	for (i = 0; i <= nsdata->nlbaf; i++)
184cae3c2f4Snonaka 		printf("LBA Format #%02d: Data Size: %5d  Metadata Size: %5d\n",
185cae3c2f4Snonaka 		    i, 1 << nsdata->lbaf[i].lbads, nsdata->lbaf[i].ms);
186cae3c2f4Snonaka }
187cae3c2f4Snonaka 
1880eed2a94Sjoerg __dead static void
identify_usage(void)189cae3c2f4Snonaka identify_usage(void)
190cae3c2f4Snonaka {
191cae3c2f4Snonaka 	fprintf(stderr, "usage:\n");
192f24079baSjdolecek 	fprintf(stderr, "\t%s " IDENTIFY_USAGE, getprogname());
193cae3c2f4Snonaka 	exit(1);
194cae3c2f4Snonaka }
195cae3c2f4Snonaka 
1960eed2a94Sjoerg __dead static void
identify_ctrlr(int argc,char * argv[])197cae3c2f4Snonaka identify_ctrlr(int argc, char *argv[])
198cae3c2f4Snonaka {
199cae3c2f4Snonaka 	struct nvm_identify_controller	cdata;
200cae3c2f4Snonaka 	int				ch, fd, hexflag = 0, hexlength;
201cae3c2f4Snonaka 	int				verboseflag = 0;
202cae3c2f4Snonaka 
203cae3c2f4Snonaka 	while ((ch = getopt(argc, argv, "vx")) != -1) {
204cae3c2f4Snonaka 		switch (ch) {
205cae3c2f4Snonaka 		case 'v':
206cae3c2f4Snonaka 			verboseflag = 1;
207cae3c2f4Snonaka 			break;
208cae3c2f4Snonaka 		case 'x':
209cae3c2f4Snonaka 			hexflag = 1;
210cae3c2f4Snonaka 			break;
211cae3c2f4Snonaka 		default:
212cae3c2f4Snonaka 			identify_usage();
213cae3c2f4Snonaka 		}
214cae3c2f4Snonaka 	}
215cae3c2f4Snonaka 
216cae3c2f4Snonaka 	/* Check that a controller was specified. */
217cae3c2f4Snonaka 	if (optind >= argc)
218cae3c2f4Snonaka 		identify_usage();
219cae3c2f4Snonaka 
220cae3c2f4Snonaka 	open_dev(argv[optind], &fd, 1, 1);
221cae3c2f4Snonaka 	read_controller_data(fd, &cdata);
222cae3c2f4Snonaka 	close(fd);
223cae3c2f4Snonaka 
224cae3c2f4Snonaka 	if (hexflag == 1) {
225cae3c2f4Snonaka 		if (verboseflag == 1)
226cae3c2f4Snonaka 			hexlength = sizeof(struct nvm_identify_controller);
227cae3c2f4Snonaka 		else
228cae3c2f4Snonaka 			hexlength = offsetof(struct nvm_identify_controller,
229cae3c2f4Snonaka 			    _reserved7);
230cae3c2f4Snonaka 		print_hex(&cdata, hexlength);
231cae3c2f4Snonaka 		exit(0);
232cae3c2f4Snonaka 	}
233cae3c2f4Snonaka 
234cae3c2f4Snonaka 	if (verboseflag == 1) {
235cae3c2f4Snonaka 		fprintf(stderr, "-v not currently supported without -x\n");
236cae3c2f4Snonaka 		identify_usage();
237cae3c2f4Snonaka 	}
238cae3c2f4Snonaka 
239cae3c2f4Snonaka 	print_controller(&cdata);
240cae3c2f4Snonaka 	exit(0);
241cae3c2f4Snonaka }
242cae3c2f4Snonaka 
2430eed2a94Sjoerg __dead static void
identify_ns(int argc,char * argv[])244cae3c2f4Snonaka identify_ns(int argc, char *argv[])
245cae3c2f4Snonaka {
246cae3c2f4Snonaka 	struct nvm_identify_namespace	nsdata;
247cae3c2f4Snonaka 	char				path[64];
248cae3c2f4Snonaka 	int				ch, fd, hexflag = 0, hexlength, nsid;
249cae3c2f4Snonaka 	int				verboseflag = 0;
250cae3c2f4Snonaka 
251cae3c2f4Snonaka 	while ((ch = getopt(argc, argv, "vx")) != -1) {
252cae3c2f4Snonaka 		switch (ch) {
253cae3c2f4Snonaka 		case 'v':
254cae3c2f4Snonaka 			verboseflag = 1;
255cae3c2f4Snonaka 			break;
256cae3c2f4Snonaka 		case 'x':
257cae3c2f4Snonaka 			hexflag = 1;
258cae3c2f4Snonaka 			break;
259cae3c2f4Snonaka 		default:
260cae3c2f4Snonaka 			identify_usage();
261cae3c2f4Snonaka 		}
262cae3c2f4Snonaka 	}
263cae3c2f4Snonaka 
264cae3c2f4Snonaka 	/* Check that a namespace was specified. */
265cae3c2f4Snonaka 	if (optind >= argc)
266cae3c2f4Snonaka 		identify_usage();
267cae3c2f4Snonaka 
268cae3c2f4Snonaka 	/*
269cae3c2f4Snonaka 	 * Check if the specified device node exists before continuing.
270cae3c2f4Snonaka 	 *  This is a cleaner check for cases where the correct controller
271cae3c2f4Snonaka 	 *  is specified, but an invalid namespace on that controller.
272cae3c2f4Snonaka 	 */
273cae3c2f4Snonaka 	open_dev(argv[optind], &fd, 1, 1);
274cae3c2f4Snonaka 	close(fd);
275cae3c2f4Snonaka 
276cae3c2f4Snonaka 	/*
277cae3c2f4Snonaka 	 * We send IDENTIFY commands to the controller, not the namespace,
278cae3c2f4Snonaka 	 *  since it is an admin cmd.  The namespace ID will be specified in
279cae3c2f4Snonaka 	 *  the IDENTIFY command itself.  So parse the namespace's device node
280cae3c2f4Snonaka 	 *  string to get the controller substring and namespace ID.
281cae3c2f4Snonaka 	 */
282cae3c2f4Snonaka 	parse_ns_str(argv[optind], path, &nsid);
283cae3c2f4Snonaka 	open_dev(path, &fd, 1, 1);
284cae3c2f4Snonaka 	read_namespace_data(fd, nsid, &nsdata);
285cae3c2f4Snonaka 	close(fd);
286cae3c2f4Snonaka 
287cae3c2f4Snonaka 	if (hexflag == 1) {
288cae3c2f4Snonaka 		if (verboseflag == 1)
289cae3c2f4Snonaka 			hexlength = sizeof(struct nvm_identify_namespace);
290cae3c2f4Snonaka 		else
291cae3c2f4Snonaka 			hexlength = offsetof(struct nvm_identify_namespace,
292cae3c2f4Snonaka 			    _reserved2);
293cae3c2f4Snonaka 		print_hex(&nsdata, hexlength);
294cae3c2f4Snonaka 		exit(0);
295cae3c2f4Snonaka 	}
296cae3c2f4Snonaka 
297cae3c2f4Snonaka 	if (verboseflag == 1) {
298cae3c2f4Snonaka 		fprintf(stderr, "-v not currently supported without -x\n");
299cae3c2f4Snonaka 		identify_usage();
300cae3c2f4Snonaka 	}
301cae3c2f4Snonaka 
302cae3c2f4Snonaka 	print_namespace(&nsdata);
303cae3c2f4Snonaka 	exit(0);
304cae3c2f4Snonaka }
305cae3c2f4Snonaka 
306cae3c2f4Snonaka void
identify(int argc,char * argv[])307cae3c2f4Snonaka identify(int argc, char *argv[])
308cae3c2f4Snonaka {
309cae3c2f4Snonaka 	char	*target;
310cae3c2f4Snonaka 
311cae3c2f4Snonaka 	if (argc < 2)
312cae3c2f4Snonaka 		identify_usage();
313cae3c2f4Snonaka 
314cae3c2f4Snonaka 	while (getopt(argc, argv, "vx") != -1) ;
315cae3c2f4Snonaka 
316cae3c2f4Snonaka 	/* Check that a controller or namespace was specified. */
317cae3c2f4Snonaka 	if (optind >= argc)
318cae3c2f4Snonaka 		identify_usage();
319cae3c2f4Snonaka 
320cae3c2f4Snonaka 	target = argv[optind];
321cae3c2f4Snonaka 
322cae3c2f4Snonaka 	optreset = 1;
323cae3c2f4Snonaka 	optind = 1;
324cae3c2f4Snonaka 
325cae3c2f4Snonaka 	/*
326cae3c2f4Snonaka 	 * If device node contains "ns", we consider it a namespace,
327cae3c2f4Snonaka 	 *  otherwise, consider it a controller.
328cae3c2f4Snonaka 	 */
329cae3c2f4Snonaka 	if (strstr(target, NVME_NS_PREFIX) == NULL)
330cae3c2f4Snonaka 		identify_ctrlr(argc, argv);
331cae3c2f4Snonaka 	else
332cae3c2f4Snonaka 		identify_ns(argc, argv);
333cae3c2f4Snonaka }
334