1252277Sjimharris/*-
2252277Sjimharris * Copyright (c) 2013 EMC Corp.
3252277Sjimharris * All rights reserved.
4252277Sjimharris *
5252277Sjimharris * Copyright (C) 2012-2013 Intel Corporation
6252277Sjimharris * All rights reserved.
7252277Sjimharris *
8252277Sjimharris * Redistribution and use in source and binary forms, with or without
9252277Sjimharris * modification, are permitted provided that the following conditions
10252277Sjimharris * are met:
11252277Sjimharris * 1. Redistributions of source code must retain the above copyright
12252277Sjimharris *    notice, this list of conditions and the following disclaimer.
13252277Sjimharris * 2. Redistributions in binary form must reproduce the above copyright
14252277Sjimharris *    notice, this list of conditions and the following disclaimer in the
15252277Sjimharris *    documentation and/or other materials provided with the distribution.
16252277Sjimharris *
17252277Sjimharris * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18252277Sjimharris * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19252277Sjimharris * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20252277Sjimharris * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21252277Sjimharris * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22252277Sjimharris * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23252277Sjimharris * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24252277Sjimharris * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25252277Sjimharris * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26252277Sjimharris * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27252277Sjimharris * SUCH DAMAGE.
28252277Sjimharris */
29252277Sjimharris
30252277Sjimharris#include <sys/cdefs.h>
31252277Sjimharris__FBSDID("$FreeBSD$");
32252277Sjimharris
33252277Sjimharris#include <sys/param.h>
34252277Sjimharris#include <sys/ioccom.h>
35252277Sjimharris
36252277Sjimharris#include <ctype.h>
37253109Sjimharris#include <err.h>
38252277Sjimharris#include <fcntl.h>
39252277Sjimharris#include <stdbool.h>
40252277Sjimharris#include <stddef.h>
41252277Sjimharris#include <stdio.h>
42252277Sjimharris#include <stdlib.h>
43252277Sjimharris#include <string.h>
44252277Sjimharris#include <unistd.h>
45252277Sjimharris
46252277Sjimharris#include "nvmecontrol.h"
47252277Sjimharris
48252277Sjimharris#define DEFAULT_SIZE	(4096)
49252277Sjimharris#define MAX_FW_SLOTS	(7)
50252277Sjimharris
51252277Sjimharristypedef void (*print_fn_t)(void *buf, uint32_t size);
52252277Sjimharris
53252277Sjimharrisstatic void *
54253109Sjimharrisget_log_buffer(uint32_t size)
55252277Sjimharris{
56252277Sjimharris	void	*buf;
57252277Sjimharris
58253109Sjimharris	if ((buf = malloc(size)) == NULL)
59253109Sjimharris		errx(1, "unable to malloc %u bytes", size);
60253109Sjimharris
61252277Sjimharris	memset(buf, 0, size);
62252277Sjimharris	return (buf);
63252277Sjimharris}
64252277Sjimharris
65252277Sjimharrisvoid
66252277Sjimharrisread_logpage(int fd, uint8_t log_page, int nsid, void *payload,
67252277Sjimharris    uint32_t payload_size)
68252277Sjimharris{
69252277Sjimharris	struct nvme_pt_command	pt;
70252277Sjimharris
71252277Sjimharris	memset(&pt, 0, sizeof(pt));
72252277Sjimharris	pt.cmd.opc = NVME_OPC_GET_LOG_PAGE;
73252277Sjimharris	pt.cmd.nsid = nsid;
74252277Sjimharris	pt.cmd.cdw10 = ((payload_size/sizeof(uint32_t)) - 1) << 16;
75252277Sjimharris	pt.cmd.cdw10 |= log_page;
76252277Sjimharris	pt.buf = payload;
77252277Sjimharris	pt.len = payload_size;
78252277Sjimharris	pt.is_read = 1;
79252277Sjimharris
80253109Sjimharris	if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0)
81253109Sjimharris		err(1, "get log page request failed");
82252277Sjimharris
83253109Sjimharris	if (nvme_completion_is_error(&pt.cpl))
84253109Sjimharris		errx(1, "get log page request returned error");
85252277Sjimharris}
86252277Sjimharris
87252277Sjimharrisstatic void
88252277Sjimharrisprint_log_error(void *buf, uint32_t size)
89252277Sjimharris{
90252277Sjimharris	int					i, nentries;
91252277Sjimharris	struct nvme_error_information_entry	*entry = buf;
92252277Sjimharris	struct nvme_status			*status;
93252277Sjimharris
94252277Sjimharris	printf("Error Information Log\n");
95252277Sjimharris	printf("=====================\n");
96252277Sjimharris
97252277Sjimharris	if (entry->error_count == 0) {
98252277Sjimharris		printf("No error entries found\n");
99252277Sjimharris		return;
100252277Sjimharris	}
101252277Sjimharris
102252277Sjimharris	nentries = size/sizeof(struct nvme_error_information_entry);
103252277Sjimharris	for (i = 0; i < nentries; i++, entry++) {
104252277Sjimharris		if (entry->error_count == 0)
105252277Sjimharris			break;
106252277Sjimharris
107252277Sjimharris		status = &entry->status;
108252277Sjimharris		printf("Entry %02d\n", i + 1);
109252277Sjimharris		printf("=========\n");
110252277Sjimharris		printf(" Error count:          %ju\n", entry->error_count);
111252277Sjimharris		printf(" Submission queue ID:  %u\n", entry->sqid);
112252277Sjimharris		printf(" Command ID:           %u\n", entry->cid);
113252277Sjimharris		/* TODO: Export nvme_status_string structures from kernel? */
114252277Sjimharris		printf(" Status:\n");
115252277Sjimharris		printf("  Phase tag:           %d\n", status->p);
116252277Sjimharris		printf("  Status code:         %d\n", status->sc);
117252277Sjimharris		printf("  Status code type:    %d\n", status->sct);
118252277Sjimharris		printf("  More:                %d\n", status->m);
119252277Sjimharris		printf("  DNR:                 %d\n", status->dnr);
120252277Sjimharris		printf(" Error location:       %u\n", entry->error_location);
121252277Sjimharris		printf(" LBA:                  %ju\n", entry->lba);
122252277Sjimharris		printf(" Namespace ID:         %u\n", entry->nsid);
123252277Sjimharris		printf(" Vendor specific info: %u\n", entry->vendor_specific);
124252277Sjimharris	}
125252277Sjimharris}
126252277Sjimharris
127252277Sjimharrisstatic void
128252277Sjimharrisprint_log_health(void *buf, uint32_t size __unused)
129252277Sjimharris{
130252277Sjimharris	struct nvme_health_information_page *health = buf;
131252277Sjimharris
132252277Sjimharris	printf("SMART/Health Information Log\n");
133252277Sjimharris	printf("============================\n");
134252277Sjimharris
135252277Sjimharris	printf("Critical Warning State:         0x%02x\n",
136252277Sjimharris	    health->critical_warning.raw);
137252277Sjimharris	printf(" Available spare:               %d\n",
138252277Sjimharris	    health->critical_warning.bits.available_spare);
139252277Sjimharris	printf(" Temperature:                   %d\n",
140252277Sjimharris	    health->critical_warning.bits.temperature);
141252277Sjimharris	printf(" Device reliability:            %d\n",
142252277Sjimharris	    health->critical_warning.bits.device_reliability);
143252277Sjimharris	printf(" Read only:                     %d\n",
144252277Sjimharris	    health->critical_warning.bits.read_only);
145252277Sjimharris	printf(" Volatile memory backup:        %d\n",
146252277Sjimharris	    health->critical_warning.bits.volatile_memory_backup);
147252277Sjimharris	printf("Temperature:                    %u K, %2.2f C, %3.2f F\n",
148252277Sjimharris	    health->temperature,
149252277Sjimharris	    (float)health->temperature - (float)273.15,
150252277Sjimharris	    ((float)health->temperature * (float)9/5) - (float)459.67);
151252277Sjimharris	printf("Available spare:                %u\n",
152252277Sjimharris	    health->available_spare);
153252277Sjimharris	printf("Available spare threshold:      %u\n",
154252277Sjimharris	    health->available_spare_threshold);
155252277Sjimharris	printf("Percentage used:                %u\n",
156252277Sjimharris	    health->percentage_used);
157252277Sjimharris
158252277Sjimharris	/*
159252277Sjimharris	 * TODO: These are pretty ugly in hex. Is there a library that
160252277Sjimharris	 *	 will convert 128-bit unsigned values to decimal?
161252277Sjimharris	 */
162252277Sjimharris	printf("Data units (512 byte) read:     0x%016jx%016jx\n",
163252277Sjimharris	    health->data_units_read[1],
164252277Sjimharris	    health->data_units_read[0]);
165252277Sjimharris	printf("Data units (512 byte) written:  0x%016jx%016jx\n",
166252277Sjimharris	    health->data_units_written[1],
167252277Sjimharris	    health->data_units_written[0]);
168252277Sjimharris	printf("Host read commands:             0x%016jx%016jx\n",
169252277Sjimharris	    health->host_read_commands[1],
170252277Sjimharris	    health->host_read_commands[0]);
171252277Sjimharris	printf("Host write commands:            0x%016jx%016jx\n",
172252277Sjimharris	    health->host_write_commands[1],
173252277Sjimharris	    health->host_write_commands[0]);
174252277Sjimharris	printf("Controller busy time (minutes): 0x%016jx%016jx\n",
175252277Sjimharris	    health->controller_busy_time[1],
176252277Sjimharris	    health->controller_busy_time[0]);
177252277Sjimharris	printf("Power cycles:                   0x%016jx%016jx\n",
178252277Sjimharris	    health->power_cycles[1],
179252277Sjimharris	    health->power_cycles[0]);
180252277Sjimharris	printf("Power on hours:                 0x%016jx%016jx\n",
181252277Sjimharris	    health->power_on_hours[1],
182252277Sjimharris	    health->power_on_hours[0]);
183252277Sjimharris	printf("Unsafe shutdowns:               0x%016jx%016jx\n",
184252277Sjimharris	    health->unsafe_shutdowns[1],
185252277Sjimharris	    health->unsafe_shutdowns[0]);
186252277Sjimharris	printf("Media errors:                   0x%016jx%016jx\n",
187252277Sjimharris	    health->media_errors[1],
188252277Sjimharris	    health->media_errors[0]);
189252277Sjimharris	printf("No. error info log entries:     0x%016jx%016jx\n",
190252277Sjimharris	    health->num_error_info_log_entries[1],
191252277Sjimharris	    health->num_error_info_log_entries[0]);
192252277Sjimharris}
193252277Sjimharris
194252277Sjimharrisstatic void
195252277Sjimharrisprint_log_firmware(void *buf, uint32_t size __unused)
196252277Sjimharris{
197252277Sjimharris	int				i;
198252277Sjimharris	const char			*status;
199252277Sjimharris	struct nvme_firmware_page	*fw = buf;
200252277Sjimharris
201252277Sjimharris	printf("Firmware Slot Log\n");
202252277Sjimharris	printf("=================\n");
203252277Sjimharris
204252277Sjimharris	for (i = 0; i < MAX_FW_SLOTS; i++) {
205252277Sjimharris		printf("Slot %d: ", i + 1);
206252277Sjimharris		if (fw->afi.slot == i + 1)
207252277Sjimharris			status = "  Active";
208252277Sjimharris		else
209252277Sjimharris			status = "Inactive";
210252277Sjimharris
211252277Sjimharris		if (fw->revision[i] == 0LLU)
212252277Sjimharris			printf("Empty\n");
213252277Sjimharris		else
214252277Sjimharris			if (isprint(*(char *)&fw->revision[i]))
215252277Sjimharris				printf("[%s] %.8s\n", status,
216252277Sjimharris				    (char *)&fw->revision[i]);
217252277Sjimharris			else
218252277Sjimharris				printf("[%s] %016jx\n", status,
219252277Sjimharris				    fw->revision[i]);
220252277Sjimharris	}
221252277Sjimharris}
222252277Sjimharris
223252302Sglebiusstatic struct logpage_function {
224252277Sjimharris	uint8_t		log_page;
225252277Sjimharris	print_fn_t	fn;
226252277Sjimharris} logfuncs[] = {
227252277Sjimharris	{NVME_LOG_ERROR,		print_log_error		},
228252277Sjimharris	{NVME_LOG_HEALTH_INFORMATION,	print_log_health	},
229252277Sjimharris	{NVME_LOG_FIRMWARE_SLOT,	print_log_firmware	},
230252277Sjimharris	{0,				NULL			},
231252277Sjimharris};
232252277Sjimharris
233252277Sjimharrisstatic void
234252277Sjimharrislogpage_usage(void)
235252277Sjimharris{
236252277Sjimharris	fprintf(stderr, "usage:\n");
237252277Sjimharris	fprintf(stderr, LOGPAGE_USAGE);
238253109Sjimharris	exit(1);
239252277Sjimharris}
240252277Sjimharris
241252277Sjimharrisvoid
242252277Sjimharrislogpage(int argc, char *argv[])
243252277Sjimharris{
244253114Sjimharris	int				fd, nsid;
245252277Sjimharris	int				log_page = 0, pageflag = false;
246253114Sjimharris	int				hexflag = false, ns_specified;
247253114Sjimharris	char				ch, *p;
248253114Sjimharris	char				cname[64];
249253109Sjimharris	uint32_t			size;
250252277Sjimharris	void				*buf;
251252277Sjimharris	struct logpage_function		*f;
252252277Sjimharris	struct nvme_controller_data	cdata;
253252277Sjimharris	print_fn_t			print_fn;
254252277Sjimharris
255252277Sjimharris	while ((ch = getopt(argc, argv, "p:x")) != -1) {
256252277Sjimharris		switch (ch) {
257252277Sjimharris		case 'p':
258252277Sjimharris			/* TODO: Add human-readable ASCII page IDs */
259252277Sjimharris			log_page = strtol(optarg, &p, 0);
260252277Sjimharris			if (p != NULL && *p != '\0') {
261252277Sjimharris				fprintf(stderr,
262252277Sjimharris				    "\"%s\" not valid log page id.\n",
263252277Sjimharris				    optarg);
264252277Sjimharris				logpage_usage();
265252277Sjimharris			/* TODO: Define valid log page id ranges in nvme.h? */
266252277Sjimharris			} else if (log_page == 0 ||
267252277Sjimharris				   (log_page >= 0x04 && log_page <= 0x7F) ||
268252277Sjimharris				   (log_page >= 0x80 && log_page <= 0xBF)) {
269252277Sjimharris				fprintf(stderr,
270252277Sjimharris				    "\"%s\" not valid log page id.\n",
271252277Sjimharris				    optarg);
272252277Sjimharris				logpage_usage();
273252277Sjimharris			}
274252277Sjimharris			pageflag = true;
275252277Sjimharris			break;
276252277Sjimharris		case 'x':
277252277Sjimharris			hexflag = true;
278252277Sjimharris			break;
279252277Sjimharris		}
280252277Sjimharris	}
281252277Sjimharris
282252277Sjimharris	if (!pageflag) {
283252277Sjimharris		printf("Missing page_id (-p).\n");
284252277Sjimharris		logpage_usage();
285252277Sjimharris	}
286252277Sjimharris
287252277Sjimharris	/* Check that a controller and/or namespace was specified. */
288252277Sjimharris	if (optind >= argc)
289252277Sjimharris		logpage_usage();
290252277Sjimharris
291253114Sjimharris	if (strstr(argv[optind], NVME_NS_PREFIX) != NULL) {
292253114Sjimharris		ns_specified = true;
293253114Sjimharris		parse_ns_str(argv[optind], cname, &nsid);
294253114Sjimharris		open_dev(cname, &fd, 1, 1);
295253114Sjimharris	} else {
296253114Sjimharris		ns_specified = false;
297253114Sjimharris		nsid = NVME_GLOBAL_NAMESPACE_TAG;
298253114Sjimharris		open_dev(argv[optind], &fd, 1, 1);
299253114Sjimharris	}
300253114Sjimharris
301252277Sjimharris	/*
302252277Sjimharris	 * The log page attribtues indicate whether or not the controller
303252277Sjimharris	 * supports the SMART/Health information log page on a per
304252277Sjimharris	 * namespace basis.
305252277Sjimharris	 */
306253114Sjimharris	if (ns_specified) {
307253114Sjimharris		if (log_page != NVME_LOG_HEALTH_INFORMATION)
308253114Sjimharris			errx(1, "log page %d valid only at controller level",
309253114Sjimharris			    log_page);
310253114Sjimharris		read_controller_data(fd, &cdata);
311253114Sjimharris		if (cdata.lpa.ns_smart == 0)
312253114Sjimharris			errx(1,
313253114Sjimharris			    "controller does not support per namespace "
314253114Sjimharris			    "smart/health information");
315253114Sjimharris	}
316252277Sjimharris
317252277Sjimharris	print_fn = print_hex;
318252277Sjimharris	if (!hexflag) {
319252277Sjimharris		/*
320252277Sjimharris		 * See if there is a pretty print function for the
321252277Sjimharris		 *  specified log page.  If one isn't found, we
322252277Sjimharris		 *  just revert to the default (print_hex).
323252277Sjimharris		 */
324252277Sjimharris		f = logfuncs;
325252277Sjimharris		while (f->log_page > 0) {
326252277Sjimharris			if (log_page == f->log_page) {
327252277Sjimharris				print_fn = f->fn;
328252277Sjimharris				break;
329252277Sjimharris			}
330252277Sjimharris			f++;
331252277Sjimharris		}
332252277Sjimharris	}
333252277Sjimharris
334252277Sjimharris	/* Read the log page */
335252277Sjimharris	switch (log_page) {
336252277Sjimharris	case NVME_LOG_ERROR:
337252277Sjimharris		size = sizeof(struct nvme_error_information_entry);
338252277Sjimharris		size *= (cdata.elpe + 1);
339252277Sjimharris		break;
340252277Sjimharris	case NVME_LOG_HEALTH_INFORMATION:
341252277Sjimharris		size = sizeof(struct nvme_health_information_page);
342252277Sjimharris		break;
343252277Sjimharris	case NVME_LOG_FIRMWARE_SLOT:
344252277Sjimharris		size = sizeof(struct nvme_firmware_page);
345252277Sjimharris		break;
346252277Sjimharris	default:
347252277Sjimharris		size = DEFAULT_SIZE;
348252277Sjimharris		break;
349252277Sjimharris	}
350252277Sjimharris
351252277Sjimharris	buf = get_log_buffer(size);
352252277Sjimharris	read_logpage(fd, log_page, nsid, buf, size);
353252277Sjimharris	print_fn(buf, size);
354252277Sjimharris
355252277Sjimharris	close(fd);
356253109Sjimharris	exit(0);
357252277Sjimharris}
358