xref: /freebsd/sbin/nvmecontrol/logpage.c (revision fa50a3552d1e759e1bb65e54cb0b7e863bcf54d5)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2013 EMC Corp.
5  * All rights reserved.
6  *
7  * Copyright (C) 2012-2013 Intel Corporation
8  * All rights reserved.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31 
32 #include <sys/cdefs.h>
33 __FBSDID("$FreeBSD$");
34 
35 #include <sys/param.h>
36 #include <sys/ioccom.h>
37 
38 #include <ctype.h>
39 #include <err.h>
40 #include <fcntl.h>
41 #include <stdbool.h>
42 #include <stddef.h>
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <string.h>
46 #include <unistd.h>
47 #include <sys/endian.h>
48 
49 #include "nvmecontrol.h"
50 
51 #define LOGPAGE_USAGE							       \
52 	"logpage <-p page_id> [-b] [-v vendor] [-x] <controller id|namespace id>\n"  \
53 
54 #define MAX_FW_SLOTS	(7)
55 
56 SET_CONCAT_DEF(logpage, struct logpage_function);
57 
58 const char *
59 kv_lookup(const struct kv_name *kv, size_t kv_count, uint32_t key)
60 {
61 	static char bad[32];
62 	size_t i;
63 
64 	for (i = 0; i < kv_count; i++, kv++)
65 		if (kv->key == key)
66 			return kv->name;
67 	snprintf(bad, sizeof(bad), "Attribute %#x", key);
68 	return bad;
69 }
70 
71 static void
72 print_log_hex(const struct nvme_controller_data *cdata __unused, void *data, uint32_t length)
73 {
74 
75 	print_hex(data, length);
76 }
77 
78 static void
79 print_bin(const struct nvme_controller_data *cdata __unused, void *data, uint32_t length)
80 {
81 
82 	write(STDOUT_FILENO, data, length);
83 }
84 
85 static void *
86 get_log_buffer(uint32_t size)
87 {
88 	void	*buf;
89 
90 	if ((buf = malloc(size)) == NULL)
91 		errx(1, "unable to malloc %u bytes", size);
92 
93 	memset(buf, 0, size);
94 	return (buf);
95 }
96 
97 void
98 read_logpage(int fd, uint8_t log_page, uint32_t nsid, void *payload,
99     uint32_t payload_size)
100 {
101 	struct nvme_pt_command	pt;
102 	struct nvme_error_information_entry	*err_entry;
103 	int i, err_pages;
104 
105 	memset(&pt, 0, sizeof(pt));
106 	pt.cmd.opc = NVME_OPC_GET_LOG_PAGE;
107 	pt.cmd.nsid = htole32(nsid);
108 	pt.cmd.cdw10 = ((payload_size/sizeof(uint32_t)) - 1) << 16;
109 	pt.cmd.cdw10 |= log_page;
110 	pt.cmd.cdw10 = htole32(pt.cmd.cdw10);
111 	pt.buf = payload;
112 	pt.len = payload_size;
113 	pt.is_read = 1;
114 
115 	if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0)
116 		err(1, "get log page request failed");
117 
118 	/* Convert data to host endian */
119 	switch (log_page) {
120 	case NVME_LOG_ERROR:
121 		err_entry = (struct nvme_error_information_entry *)payload;
122 		err_pages = payload_size / sizeof(struct nvme_error_information_entry);
123 		for (i = 0; i < err_pages; i++)
124 			nvme_error_information_entry_swapbytes(err_entry++);
125 		break;
126 	case NVME_LOG_HEALTH_INFORMATION:
127 		nvme_health_information_page_swapbytes(
128 		    (struct nvme_health_information_page *)payload);
129 		break;
130 	case NVME_LOG_FIRMWARE_SLOT:
131 		nvme_firmware_page_swapbytes(
132 		    (struct nvme_firmware_page *)payload);
133 		break;
134 	case INTEL_LOG_TEMP_STATS:
135 		intel_log_temp_stats_swapbytes(
136 		    (struct intel_log_temp_stats *)payload);
137 		break;
138 	default:
139 		break;
140 	}
141 
142 	if (nvme_completion_is_error(&pt.cpl))
143 		errx(1, "get log page request returned error");
144 }
145 
146 static void
147 print_log_error(const struct nvme_controller_data *cdata __unused, void *buf, uint32_t size)
148 {
149 	int					i, nentries;
150 	uint16_t				status;
151 	uint8_t					p, sc, sct, m, dnr;
152 	struct nvme_error_information_entry	*entry = buf;
153 
154 	printf("Error Information Log\n");
155 	printf("=====================\n");
156 
157 	if (entry->error_count == 0) {
158 		printf("No error entries found\n");
159 		return;
160 	}
161 
162 	nentries = size/sizeof(struct nvme_error_information_entry);
163 	for (i = 0; i < nentries; i++, entry++) {
164 		if (entry->error_count == 0)
165 			break;
166 
167 		status = entry->status;
168 
169 		p = NVME_STATUS_GET_P(status);
170 		sc = NVME_STATUS_GET_SC(status);
171 		sct = NVME_STATUS_GET_SCT(status);
172 		m = NVME_STATUS_GET_M(status);
173 		dnr = NVME_STATUS_GET_DNR(status);
174 
175 		printf("Entry %02d\n", i + 1);
176 		printf("=========\n");
177 		printf(" Error count:          %ju\n", entry->error_count);
178 		printf(" Submission queue ID:  %u\n", entry->sqid);
179 		printf(" Command ID:           %u\n", entry->cid);
180 		/* TODO: Export nvme_status_string structures from kernel? */
181 		printf(" Status:\n");
182 		printf("  Phase tag:           %d\n", p);
183 		printf("  Status code:         %d\n", sc);
184 		printf("  Status code type:    %d\n", sct);
185 		printf("  More:                %d\n", m);
186 		printf("  DNR:                 %d\n", dnr);
187 		printf(" Error location:       %u\n", entry->error_location);
188 		printf(" LBA:                  %ju\n", entry->lba);
189 		printf(" Namespace ID:         %u\n", entry->nsid);
190 		printf(" Vendor specific info: %u\n", entry->vendor_specific);
191 	}
192 }
193 
194 void
195 print_temp(uint16_t t)
196 {
197 	printf("%u K, %2.2f C, %3.2f F\n", t, (float)t - 273.15, (float)t * 9 / 5 - 459.67);
198 }
199 
200 
201 static void
202 print_log_health(const struct nvme_controller_data *cdata __unused, void *buf, uint32_t size __unused)
203 {
204 	struct nvme_health_information_page *health = buf;
205 	char cbuf[UINT128_DIG + 1];
206 	uint8_t	warning;
207 	int i;
208 
209 	warning = health->critical_warning;
210 
211 	printf("SMART/Health Information Log\n");
212 	printf("============================\n");
213 
214 	printf("Critical Warning State:         0x%02x\n", warning);
215 	printf(" Available spare:               %d\n",
216 	    !!(warning & NVME_CRIT_WARN_ST_AVAILABLE_SPARE));
217 	printf(" Temperature:                   %d\n",
218 	    !!(warning & NVME_CRIT_WARN_ST_TEMPERATURE));
219 	printf(" Device reliability:            %d\n",
220 	    !!(warning & NVME_CRIT_WARN_ST_DEVICE_RELIABILITY));
221 	printf(" Read only:                     %d\n",
222 	    !!(warning & NVME_CRIT_WARN_ST_READ_ONLY));
223 	printf(" Volatile memory backup:        %d\n",
224 	    !!(warning & NVME_CRIT_WARN_ST_VOLATILE_MEMORY_BACKUP));
225 	printf("Temperature:                    ");
226 	print_temp(health->temperature);
227 	printf("Available spare:                %u\n",
228 	    health->available_spare);
229 	printf("Available spare threshold:      %u\n",
230 	    health->available_spare_threshold);
231 	printf("Percentage used:                %u\n",
232 	    health->percentage_used);
233 
234 	printf("Data units (512,000 byte) read: %s\n",
235 	    uint128_to_str(to128(health->data_units_read), cbuf, sizeof(cbuf)));
236 	printf("Data units written:             %s\n",
237 	    uint128_to_str(to128(health->data_units_written), cbuf, sizeof(cbuf)));
238 	printf("Host read commands:             %s\n",
239 	    uint128_to_str(to128(health->host_read_commands), cbuf, sizeof(cbuf)));
240 	printf("Host write commands:            %s\n",
241 	    uint128_to_str(to128(health->host_write_commands), cbuf, sizeof(cbuf)));
242 	printf("Controller busy time (minutes): %s\n",
243 	    uint128_to_str(to128(health->controller_busy_time), cbuf, sizeof(cbuf)));
244 	printf("Power cycles:                   %s\n",
245 	    uint128_to_str(to128(health->power_cycles), cbuf, sizeof(cbuf)));
246 	printf("Power on hours:                 %s\n",
247 	    uint128_to_str(to128(health->power_on_hours), cbuf, sizeof(cbuf)));
248 	printf("Unsafe shutdowns:               %s\n",
249 	    uint128_to_str(to128(health->unsafe_shutdowns), cbuf, sizeof(cbuf)));
250 	printf("Media errors:                   %s\n",
251 	    uint128_to_str(to128(health->media_errors), cbuf, sizeof(cbuf)));
252 	printf("No. error info log entries:     %s\n",
253 	    uint128_to_str(to128(health->num_error_info_log_entries), cbuf, sizeof(cbuf)));
254 
255 	printf("Warning Temp Composite Time:    %d\n", health->warning_temp_time);
256 	printf("Error Temp Composite Time:      %d\n", health->error_temp_time);
257 	for (i = 0; i < 8; i++) {
258 		if (health->temp_sensor[i] == 0)
259 			continue;
260 		printf("Temperature Sensor %d:           ", i + 1);
261 		print_temp(health->temp_sensor[i]);
262 	}
263 }
264 
265 static void
266 print_log_firmware(const struct nvme_controller_data *cdata, void *buf, uint32_t size __unused)
267 {
268 	int				i, slots;
269 	const char			*status;
270 	struct nvme_firmware_page	*fw = buf;
271 	uint8_t				afi_slot;
272 	uint16_t			oacs_fw;
273 	uint8_t				fw_num_slots;
274 
275 	afi_slot = fw->afi >> NVME_FIRMWARE_PAGE_AFI_SLOT_SHIFT;
276 	afi_slot &= NVME_FIRMWARE_PAGE_AFI_SLOT_MASK;
277 
278 	oacs_fw = (cdata->oacs >> NVME_CTRLR_DATA_OACS_FIRMWARE_SHIFT) &
279 		NVME_CTRLR_DATA_OACS_FIRMWARE_MASK;
280 	fw_num_slots = (cdata->frmw >> NVME_CTRLR_DATA_FRMW_NUM_SLOTS_SHIFT) &
281 		NVME_CTRLR_DATA_FRMW_NUM_SLOTS_MASK;
282 
283 	printf("Firmware Slot Log\n");
284 	printf("=================\n");
285 
286 	if (oacs_fw == 0)
287 		slots = 1;
288 	else
289 		slots = MIN(fw_num_slots, MAX_FW_SLOTS);
290 
291 	for (i = 0; i < slots; i++) {
292 		printf("Slot %d: ", i + 1);
293 		if (afi_slot == i + 1)
294 			status = "  Active";
295 		else
296 			status = "Inactive";
297 
298 		if (fw->revision[i] == 0LLU)
299 			printf("Empty\n");
300 		else
301 			if (isprint(*(char *)&fw->revision[i]))
302 				printf("[%s] %.8s\n", status,
303 				    (char *)&fw->revision[i]);
304 			else
305 				printf("[%s] %016jx\n", status,
306 				    fw->revision[i]);
307 	}
308 }
309 
310 /*
311  * Table of log page printer / sizing.
312  *
313  * Make sure you keep all the pages of one vendor together so -v help
314  * lists all the vendors pages.
315  */
316 NVME_LOGPAGE(error,
317     NVME_LOG_ERROR,			NULL,	"Drive Error Log",
318     print_log_error, 			0);
319 NVME_LOGPAGE(health,
320     NVME_LOG_HEALTH_INFORMATION,	NULL,	"Health/SMART Data",
321     print_log_health, 			sizeof(struct nvme_health_information_page));
322 NVME_LOGPAGE(fw,
323     NVME_LOG_FIRMWARE_SLOT,		NULL,	"Firmware Information",
324     print_log_firmware,			sizeof(struct nvme_firmware_page));
325 
326 static void
327 logpage_help(void)
328 {
329 	const struct logpage_function	* const *f;
330 	const char 			*v;
331 
332 	fprintf(stderr, "\n");
333 	fprintf(stderr, "%-8s %-10s %s\n", "Page", "Vendor","Page Name");
334 	fprintf(stderr, "-------- ---------- ----------\n");
335 	for (f = logpage_begin(); f < logpage_limit(); f++) {
336 		v = (*f)->vendor == NULL ? "-" : (*f)->vendor;
337 		fprintf(stderr, "0x%02x     %-10s %s\n", (*f)->log_page, v, (*f)->name);
338 	}
339 
340 	exit(1);
341 }
342 
343 static void
344 logpage(const struct nvme_function *nf, int argc, char *argv[])
345 {
346 	int				fd;
347 	int				log_page = 0, pageflag = false;
348 	int				binflag = false, hexflag = false, ns_specified;
349 	int				opt;
350 	char				*p;
351 	char				cname[64];
352 	uint32_t			nsid, size;
353 	void				*buf;
354 	const char			*vendor = NULL;
355 	const struct logpage_function	* const *f;
356 	struct nvme_controller_data	cdata;
357 	print_fn_t			print_fn;
358 	uint8_t				ns_smart;
359 
360 	while ((opt = getopt(argc, argv, "bp:xv:")) != -1) {
361 		switch (opt) {
362 		case 'b':
363 			binflag = true;
364 			break;
365 		case 'p':
366 			if (strcmp(optarg, "help") == 0)
367 				logpage_help();
368 
369 			/* TODO: Add human-readable ASCII page IDs */
370 			log_page = strtol(optarg, &p, 0);
371 			if (p != NULL && *p != '\0') {
372 				fprintf(stderr,
373 				    "\"%s\" not valid log page id.\n",
374 				    optarg);
375 				usage(nf);
376 			}
377 			pageflag = true;
378 			break;
379 		case 'x':
380 			hexflag = true;
381 			break;
382 		case 'v':
383 			if (strcmp(optarg, "help") == 0)
384 				logpage_help();
385 			vendor = optarg;
386 			break;
387 		}
388 	}
389 
390 	if (!pageflag) {
391 		printf("Missing page_id (-p).\n");
392 		usage(nf);
393 	}
394 
395 	/* Check that a controller and/or namespace was specified. */
396 	if (optind >= argc)
397 		usage(nf);
398 
399 	if (strstr(argv[optind], NVME_NS_PREFIX) != NULL) {
400 		ns_specified = true;
401 		parse_ns_str(argv[optind], cname, &nsid);
402 		open_dev(cname, &fd, 1, 1);
403 	} else {
404 		ns_specified = false;
405 		nsid = NVME_GLOBAL_NAMESPACE_TAG;
406 		open_dev(argv[optind], &fd, 1, 1);
407 	}
408 
409 	read_controller_data(fd, &cdata);
410 
411 	ns_smart = (cdata.lpa >> NVME_CTRLR_DATA_LPA_NS_SMART_SHIFT) &
412 		NVME_CTRLR_DATA_LPA_NS_SMART_MASK;
413 
414 	/*
415 	 * The log page attribtues indicate whether or not the controller
416 	 * supports the SMART/Health information log page on a per
417 	 * namespace basis.
418 	 */
419 	if (ns_specified) {
420 		if (log_page != NVME_LOG_HEALTH_INFORMATION)
421 			errx(1, "log page %d valid only at controller level",
422 			    log_page);
423 		if (ns_smart == 0)
424 			errx(1,
425 			    "controller does not support per namespace "
426 			    "smart/health information");
427 	}
428 
429 	print_fn = print_log_hex;
430 	size = DEFAULT_SIZE;
431 	if (binflag)
432 		print_fn = print_bin;
433 	if (!binflag && !hexflag) {
434 		/*
435 		 * See if there is a pretty print function for the specified log
436 		 * page.  If one isn't found, we just revert to the default
437 		 * (print_hex). If there was a vendor specified by the user, and
438 		 * the page is vendor specific, don't match the print function
439 		 * unless the vendors match.
440 		 */
441 		for (f = logpage_begin(); f < logpage_limit(); f++) {
442 			if ((*f)->vendor != NULL && vendor != NULL &&
443 			    strcmp((*f)->vendor, vendor) != 0)
444 				continue;
445 			if (log_page != (*f)->log_page)
446 				continue;
447 			print_fn = (*f)->print_fn;
448 			size = (*f)->size;
449 			break;
450 		}
451 	}
452 
453 	if (log_page == NVME_LOG_ERROR) {
454 		size = sizeof(struct nvme_error_information_entry);
455 		size *= (cdata.elpe + 1);
456 	}
457 
458 	/* Read the log page */
459 	buf = get_log_buffer(size);
460 	read_logpage(fd, log_page, nsid, buf, size);
461 	print_fn(&cdata, buf, size);
462 
463 	close(fd);
464 	exit(0);
465 }
466 
467 NVME_COMMAND(top, logpage, logpage, LOGPAGE_USAGE);
468