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