1/*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (C) 2012-2013 Intel Corporation
5 * All rights reserved.
6 * Copyright (C) 2018-2019 Alexander Motin <mav@FreeBSD.org>
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30#include <sys/param.h>
31
32#include <ctype.h>
33#include <err.h>
34#include <fcntl.h>
35#include <stdbool.h>
36#include <stddef.h>
37#include <stdio.h>
38#include <stdlib.h>
39#include <string.h>
40#include <sysexits.h>
41#include <unistd.h>
42
43#include "nvmecontrol.h"
44#include "nvmecontrol_ext.h"
45
46#define NONE 0xfffffffeu
47
48static struct options {
49	bool		hex;
50	bool		verbose;
51	const char	*dev;
52	uint32_t	nsid;
53} opt = {
54	.hex = false,
55	.verbose = false,
56	.dev = NULL,
57	.nsid = NONE,
58};
59
60void
61print_namespace(struct nvme_namespace_data *nsdata)
62{
63	char cbuf[UINT128_DIG + 1];
64	uint32_t	i;
65	uint32_t	lbaf, lbads, ms, rp;
66	uint8_t		thin_prov, ptype;
67	uint8_t		flbas_fmt, t;
68
69	thin_prov = NVMEV(NVME_NS_DATA_NSFEAT_THIN_PROV, nsdata->nsfeat);
70
71	flbas_fmt = NVMEV(NVME_NS_DATA_FLBAS_FORMAT, nsdata->flbas);
72
73	printf("Size:                        %lld blocks\n",
74	    (long long)nsdata->nsze);
75	printf("Capacity:                    %lld blocks\n",
76	    (long long)nsdata->ncap);
77	printf("Utilization:                 %lld blocks\n",
78	    (long long)nsdata->nuse);
79	printf("Thin Provisioning:           %s\n",
80		thin_prov ? "Supported" : "Not Supported");
81	printf("Number of LBA Formats:       %d\n", nsdata->nlbaf+1);
82	printf("Current LBA Format:          LBA Format #%02d", flbas_fmt);
83	if (NVMEV(NVME_NS_DATA_LBAF_MS, nsdata->lbaf[flbas_fmt]) != 0)
84		printf(" %s metadata\n",
85		    NVMEV(NVME_NS_DATA_FLBAS_EXTENDED, nsdata->flbas) != 0 ?
86		    "Extended" : "Separate");
87	else
88		printf("\n");
89	printf("Metadata Capabilities\n");
90	printf("  Extended:                  %s\n",
91	    NVMEV(NVME_NS_DATA_MC_EXTENDED, nsdata->mc) != 0 ? "Supported" :
92	    "Not Supported");
93	printf("  Separate:                  %s\n",
94	    NVMEV(NVME_NS_DATA_MC_POINTER, nsdata->mc) != 0 ? "Supported" :
95	    "Not Supported");
96	printf("Data Protection Caps:        %s%s%s%s%s%s\n",
97	    (nsdata->dpc == 0) ? "Not Supported" : "",
98	    NVMEV(NVME_NS_DATA_DPC_MD_END, nsdata->dpc) != 0 ? "Last Bytes, " :
99	    "",
100	    NVMEV(NVME_NS_DATA_DPC_MD_START, nsdata->dpc) != 0 ?
101	    "First Bytes, " : "",
102	    NVMEV(NVME_NS_DATA_DPC_PIT3, nsdata->dpc) != 0 ? "Type 3, " : "",
103	    NVMEV(NVME_NS_DATA_DPC_PIT2, nsdata->dpc) != 0 ? "Type 2, " : "",
104	    NVMEV(NVME_NS_DATA_DPC_PIT1, nsdata->dpc) != 0 ? "Type 1" : "");
105	printf("Data Protection Settings:    ");
106	ptype = NVMEV(NVME_NS_DATA_DPS_PIT, nsdata->dps);
107	if (ptype != 0) {
108		printf("Type %d, %s Bytes\n", ptype,
109		    NVMEV(NVME_NS_DATA_DPS_MD_START, nsdata->dps) != 0 ?
110		    "First" : "Last");
111	} else {
112		printf("Not Enabled\n");
113	}
114	printf("Multi-Path I/O Capabilities: %s%s\n",
115	    (nsdata->nmic == 0) ? "Not Supported" : "",
116	    NVMEV(NVME_NS_DATA_NMIC_MAY_BE_SHARED, nsdata->nmic) != 0 ?
117	    "May be shared" : "");
118	printf("Reservation Capabilities:    %s%s%s%s%s%s%s%s%s\n",
119	    (nsdata->rescap == 0) ? "Not Supported" : "",
120	    NVMEV(NVME_NS_DATA_RESCAP_IEKEY13, nsdata->rescap) != 0 ?
121	    "IEKEY13, " : "",
122	    NVMEV(NVME_NS_DATA_RESCAP_EX_AC_AR, nsdata->rescap) != 0 ?
123	    "EX_AC_AR, " : "",
124	    NVMEV(NVME_NS_DATA_RESCAP_WR_EX_AR, nsdata->rescap) != 0 ?
125	    "WR_EX_AR, " : "",
126	    NVMEV(NVME_NS_DATA_RESCAP_EX_AC_RO, nsdata->rescap) != 0 ?
127	    "EX_AC_RO, " : "",
128	    NVMEV(NVME_NS_DATA_RESCAP_WR_EX_RO, nsdata->rescap) != 0 ?
129	    "WR_EX_RO, " : "",
130	    NVMEV(NVME_NS_DATA_RESCAP_EX_AC, nsdata->rescap) != 0 ?
131	    "EX_AC, " : "",
132	    NVMEV(NVME_NS_DATA_RESCAP_WR_EX, nsdata->rescap) != 0 ?
133	    "WR_EX, " : "",
134	    NVMEV(NVME_NS_DATA_RESCAP_PTPL, nsdata->rescap) != 0 ? "PTPL" : "");
135	printf("Format Progress Indicator:   ");
136	if (NVMEV(NVME_NS_DATA_FPI_SUPP, nsdata->fpi) != 0) {
137		printf("%u%% remains\n",
138		    NVMEV(NVME_NS_DATA_FPI_PERC, nsdata->fpi));
139	} else
140		printf("Not Supported\n");
141	t = NVMEV(NVME_NS_DATA_DLFEAT_READ, nsdata->dlfeat);
142	printf("Deallocate Logical Block:    Read %s%s%s\n",
143	    (t == NVME_NS_DATA_DLFEAT_READ_NR) ? "Not Reported" :
144	    (t == NVME_NS_DATA_DLFEAT_READ_00) ? "00h" :
145	    (t == NVME_NS_DATA_DLFEAT_READ_FF) ? "FFh" : "Unknown",
146	    NVMEV(NVME_NS_DATA_DLFEAT_DWZ, nsdata->dlfeat) != 0 ?
147	    ", Write Zero" : "",
148	    NVMEV(NVME_NS_DATA_DLFEAT_GCRC, nsdata->dlfeat) != 0 ?
149	    ", Guard CRC" : "");
150	printf("Optimal I/O Boundary:        %u blocks\n", nsdata->noiob);
151	printf("NVM Capacity:                %s bytes\n",
152	   uint128_to_str(to128(nsdata->nvmcap), cbuf, sizeof(cbuf)));
153	if (NVMEV(NVME_NS_DATA_NSFEAT_NPVALID, nsdata->nsfeat) != 0) {
154		printf("Preferred Write Granularity: %u blocks\n",
155		    nsdata->npwg + 1);
156		printf("Preferred Write Alignment:   %u blocks\n",
157		    nsdata->npwa + 1);
158		printf("Preferred Deallocate Granul: %u blocks\n",
159		    nsdata->npdg + 1);
160		printf("Preferred Deallocate Align:  %u blocks\n",
161		    nsdata->npda + 1);
162		printf("Optimal Write Size:          %u blocks\n",
163		    nsdata->nows + 1);
164	}
165	printf("Globally Unique Identifier:  ");
166	for (i = 0; i < sizeof(nsdata->nguid); i++)
167		printf("%02x", nsdata->nguid[i]);
168	printf("\n");
169	printf("IEEE EUI64:                  ");
170	for (i = 0; i < sizeof(nsdata->eui64); i++)
171		printf("%02x", nsdata->eui64[i]);
172	printf("\n");
173	for (i = 0; i <= nsdata->nlbaf; i++) {
174		lbaf = nsdata->lbaf[i];
175		lbads = NVMEV(NVME_NS_DATA_LBAF_LBADS, lbaf);
176		if (lbads == 0)
177			continue;
178		ms = NVMEV(NVME_NS_DATA_LBAF_MS, lbaf);
179		rp = NVMEV(NVME_NS_DATA_LBAF_RP, lbaf);
180		printf("LBA Format #%02d: Data Size: %5d  Metadata Size: %5d"
181		    "  Performance: %s\n",
182		    i, 1 << lbads, ms, (rp == 0) ? "Best" :
183		    (rp == 1) ? "Better" : (rp == 2) ? "Good" : "Degraded");
184	}
185}
186
187static void
188identify_ctrlr(int fd)
189{
190	struct nvme_controller_data	cdata;
191	int				hexlength;
192
193	if (read_controller_data(fd, &cdata))
194		errx(EX_IOERR, "Identify request failed");
195	close(fd);
196
197	if (opt.hex) {
198		if (opt.verbose)
199			hexlength = sizeof(struct nvme_controller_data);
200		else
201			hexlength = offsetof(struct nvme_controller_data,
202			    reserved8);
203		print_hex(&cdata, hexlength);
204		exit(0);
205	}
206
207	nvme_print_controller(&cdata);
208	exit(0);
209}
210
211static void
212identify_ns(int fd, uint32_t nsid)
213{
214	struct nvme_namespace_data	nsdata;
215	int				hexlength;
216
217	if (read_namespace_data(fd, nsid, &nsdata))
218		errx(EX_IOERR, "Identify request failed");
219	close(fd);
220
221	if (opt.hex) {
222		if (opt.verbose)
223			hexlength = sizeof(struct nvme_namespace_data);
224		else
225			hexlength = offsetof(struct nvme_namespace_data,
226			    reserved6);
227		print_hex(&nsdata, hexlength);
228		exit(0);
229	}
230
231	print_namespace(&nsdata);
232	exit(0);
233}
234
235static void
236identify(const struct cmd *f, int argc, char *argv[])
237{
238	char		*path;
239	int		fd;
240	uint32_t	nsid;
241
242	if (arg_parse(argc, argv, f))
243		return;
244
245	open_dev(opt.dev, &fd, 0, 1);
246	get_nsid(fd, &path, &nsid);
247	if (nsid != 0) {
248		/*
249		 * We got namespace device, but we need to send IDENTIFY
250		 * commands to the controller, not the namespace, since it
251		 * is an admin cmd.  The namespace ID will be specified in
252		 * the IDENTIFY command itself.
253		 */
254		close(fd);
255		open_dev(path, &fd, 0, 1);
256	}
257	free(path);
258	if (opt.nsid != NONE)
259		nsid = opt.nsid;
260
261	if (nsid == 0)
262		identify_ctrlr(fd);
263	else
264		identify_ns(fd, nsid);
265}
266
267static const struct opts identify_opts[] = {
268#define OPT(l, s, t, opt, addr, desc) { l, s, t, &opt.addr, desc }
269	OPT("hex", 'x', arg_none, opt, hex,
270	    "Print identiy information in hex"),
271	OPT("verbose", 'v', arg_none, opt, verbose,
272	    "More verbosity: print entire identify table"),
273	OPT("nsid", 'n', arg_uint32, opt, nsid,
274	    "Namespace ID to use if not in device name"),
275	{ NULL, 0, arg_none, NULL, NULL }
276};
277#undef OPT
278
279static const struct args identify_args[] = {
280	{ arg_string, &opt.dev, "controller-id|namespace-id" },
281	{ arg_none, NULL, NULL },
282};
283
284static struct cmd identify_cmd = {
285	.name = "identify",
286	.fn = identify,
287	.descr = "Print summary of the IDENTIFY information",
288	.ctx_size = sizeof(opt),
289	.opts = identify_opts,
290	.args = identify_args,
291};
292
293CMD_COMMAND(identify_cmd);
294