2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
4 * Copyright (c) 2013 EMC Corp.
7 * Copyright (C) 2012-2013 Intel Corporation
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
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.
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
32 #include <sys/cdefs.h>
33 __FBSDID("$FreeBSD$");
35 #include <sys/param.h>
36 #include <sys/ioccom.h>
47 #include <sys/endian.h>
49 #include "nvmecontrol.h"
51 /* Tables for command line parsing */
53 static cmd_fn_t logpage;
55 #define NONE 0xffffffffu
56 static struct options {
70 static const struct opts logpage_opts[] = {
71 #define OPT(l, s, t, opt, addr, desc) { l, s, t, &opt.addr, desc }
72 OPT("binary", 'b', arg_none, opt, binary,
73 "Dump the log page as binary"),
74 OPT("hex", 'x', arg_none, opt, hex,
75 "Dump the log page as hex"),
76 OPT("page", 'p', arg_uint32, opt, page,
78 OPT("vendor", 'v', arg_string, opt, vendor,
79 "Vendor specific formatting"),
80 { NULL, 0, arg_none, NULL, NULL }
84 static const struct args logpage_args[] = {
85 { arg_string, &opt.dev, "<controller id|namespace id>" },
86 { arg_none, NULL, NULL },
89 static struct cmd logpage_cmd = {
92 .descr = "Print logpages in human-readable form",
93 .ctx_size = sizeof(opt),
98 CMD_COMMAND(logpage_cmd);
100 /* End of tables for command line parsing */
102 #define MAX_FW_SLOTS (7)
104 static SLIST_HEAD(,logpage_function) logpages;
107 logpage_register(struct logpage_function *p)
110 SLIST_INSERT_HEAD(&logpages, p, link);
114 kv_lookup(const struct kv_name *kv, size_t kv_count, uint32_t key)
119 for (i = 0; i < kv_count; i++, kv++)
122 snprintf(bad, sizeof(bad), "Attribute %#x", key);
127 print_log_hex(const struct nvme_controller_data *cdata __unused, void *data, uint32_t length)
130 print_hex(data, length);
134 print_bin(const struct nvme_controller_data *cdata __unused, void *data, uint32_t length)
137 write(STDOUT_FILENO, data, length);
141 get_log_buffer(uint32_t size)
145 if ((buf = malloc(size)) == NULL)
146 errx(1, "unable to malloc %u bytes", size);
148 memset(buf, 0, size);
153 read_logpage(int fd, uint8_t log_page, uint32_t nsid, void *payload,
154 uint32_t payload_size)
156 struct nvme_pt_command pt;
157 struct nvme_error_information_entry *err_entry;
160 memset(&pt, 0, sizeof(pt));
161 pt.cmd.opc = NVME_OPC_GET_LOG_PAGE;
162 pt.cmd.nsid = htole32(nsid);
163 pt.cmd.cdw10 = ((payload_size/sizeof(uint32_t)) - 1) << 16;
164 pt.cmd.cdw10 |= log_page;
165 pt.cmd.cdw10 = htole32(pt.cmd.cdw10);
167 pt.len = payload_size;
170 if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0)
171 err(1, "get log page request failed");
173 /* Convert data to host endian */
176 err_entry = (struct nvme_error_information_entry *)payload;
177 err_pages = payload_size / sizeof(struct nvme_error_information_entry);
178 for (i = 0; i < err_pages; i++)
179 nvme_error_information_entry_swapbytes(err_entry++);
181 case NVME_LOG_HEALTH_INFORMATION:
182 nvme_health_information_page_swapbytes(
183 (struct nvme_health_information_page *)payload);
185 case NVME_LOG_FIRMWARE_SLOT:
186 nvme_firmware_page_swapbytes(
187 (struct nvme_firmware_page *)payload);
189 case INTEL_LOG_TEMP_STATS:
190 intel_log_temp_stats_swapbytes(
191 (struct intel_log_temp_stats *)payload);
197 if (nvme_completion_is_error(&pt.cpl))
198 errx(1, "get log page request returned error");
202 print_log_error(const struct nvme_controller_data *cdata __unused, void *buf, uint32_t size)
206 uint8_t p, sc, sct, m, dnr;
207 struct nvme_error_information_entry *entry = buf;
209 printf("Error Information Log\n");
210 printf("=====================\n");
212 if (entry->error_count == 0) {
213 printf("No error entries found\n");
217 nentries = size/sizeof(struct nvme_error_information_entry);
218 for (i = 0; i < nentries; i++, entry++) {
219 if (entry->error_count == 0)
222 status = entry->status;
224 p = NVME_STATUS_GET_P(status);
225 sc = NVME_STATUS_GET_SC(status);
226 sct = NVME_STATUS_GET_SCT(status);
227 m = NVME_STATUS_GET_M(status);
228 dnr = NVME_STATUS_GET_DNR(status);
230 printf("Entry %02d\n", i + 1);
231 printf("=========\n");
232 printf(" Error count: %ju\n", entry->error_count);
233 printf(" Submission queue ID: %u\n", entry->sqid);
234 printf(" Command ID: %u\n", entry->cid);
235 /* TODO: Export nvme_status_string structures from kernel? */
236 printf(" Status:\n");
237 printf(" Phase tag: %d\n", p);
238 printf(" Status code: %d\n", sc);
239 printf(" Status code type: %d\n", sct);
240 printf(" More: %d\n", m);
241 printf(" DNR: %d\n", dnr);
242 printf(" Error location: %u\n", entry->error_location);
243 printf(" LBA: %ju\n", entry->lba);
244 printf(" Namespace ID: %u\n", entry->nsid);
245 printf(" Vendor specific info: %u\n", entry->vendor_specific);
250 print_temp(uint16_t t)
252 printf("%u K, %2.2f C, %3.2f F\n", t, (float)t - 273.15, (float)t * 9 / 5 - 459.67);
257 print_log_health(const struct nvme_controller_data *cdata __unused, void *buf, uint32_t size __unused)
259 struct nvme_health_information_page *health = buf;
260 char cbuf[UINT128_DIG + 1];
264 warning = health->critical_warning;
266 printf("SMART/Health Information Log\n");
267 printf("============================\n");
269 printf("Critical Warning State: 0x%02x\n", warning);
270 printf(" Available spare: %d\n",
271 !!(warning & NVME_CRIT_WARN_ST_AVAILABLE_SPARE));
272 printf(" Temperature: %d\n",
273 !!(warning & NVME_CRIT_WARN_ST_TEMPERATURE));
274 printf(" Device reliability: %d\n",
275 !!(warning & NVME_CRIT_WARN_ST_DEVICE_RELIABILITY));
276 printf(" Read only: %d\n",
277 !!(warning & NVME_CRIT_WARN_ST_READ_ONLY));
278 printf(" Volatile memory backup: %d\n",
279 !!(warning & NVME_CRIT_WARN_ST_VOLATILE_MEMORY_BACKUP));
280 printf("Temperature: ");
281 print_temp(health->temperature);
282 printf("Available spare: %u\n",
283 health->available_spare);
284 printf("Available spare threshold: %u\n",
285 health->available_spare_threshold);
286 printf("Percentage used: %u\n",
287 health->percentage_used);
289 printf("Data units (512,000 byte) read: %s\n",
290 uint128_to_str(to128(health->data_units_read), cbuf, sizeof(cbuf)));
291 printf("Data units written: %s\n",
292 uint128_to_str(to128(health->data_units_written), cbuf, sizeof(cbuf)));
293 printf("Host read commands: %s\n",
294 uint128_to_str(to128(health->host_read_commands), cbuf, sizeof(cbuf)));
295 printf("Host write commands: %s\n",
296 uint128_to_str(to128(health->host_write_commands), cbuf, sizeof(cbuf)));
297 printf("Controller busy time (minutes): %s\n",
298 uint128_to_str(to128(health->controller_busy_time), cbuf, sizeof(cbuf)));
299 printf("Power cycles: %s\n",
300 uint128_to_str(to128(health->power_cycles), cbuf, sizeof(cbuf)));
301 printf("Power on hours: %s\n",
302 uint128_to_str(to128(health->power_on_hours), cbuf, sizeof(cbuf)));
303 printf("Unsafe shutdowns: %s\n",
304 uint128_to_str(to128(health->unsafe_shutdowns), cbuf, sizeof(cbuf)));
305 printf("Media errors: %s\n",
306 uint128_to_str(to128(health->media_errors), cbuf, sizeof(cbuf)));
307 printf("No. error info log entries: %s\n",
308 uint128_to_str(to128(health->num_error_info_log_entries), cbuf, sizeof(cbuf)));
310 printf("Warning Temp Composite Time: %d\n", health->warning_temp_time);
311 printf("Error Temp Composite Time: %d\n", health->error_temp_time);
312 for (i = 0; i < 8; i++) {
313 if (health->temp_sensor[i] == 0)
315 printf("Temperature Sensor %d: ", i + 1);
316 print_temp(health->temp_sensor[i]);
321 print_log_firmware(const struct nvme_controller_data *cdata, void *buf, uint32_t size __unused)
325 struct nvme_firmware_page *fw = buf;
328 uint8_t fw_num_slots;
330 afi_slot = fw->afi >> NVME_FIRMWARE_PAGE_AFI_SLOT_SHIFT;
331 afi_slot &= NVME_FIRMWARE_PAGE_AFI_SLOT_MASK;
333 oacs_fw = (cdata->oacs >> NVME_CTRLR_DATA_OACS_FIRMWARE_SHIFT) &
334 NVME_CTRLR_DATA_OACS_FIRMWARE_MASK;
335 fw_num_slots = (cdata->frmw >> NVME_CTRLR_DATA_FRMW_NUM_SLOTS_SHIFT) &
336 NVME_CTRLR_DATA_FRMW_NUM_SLOTS_MASK;
338 printf("Firmware Slot Log\n");
339 printf("=================\n");
344 slots = MIN(fw_num_slots, MAX_FW_SLOTS);
346 for (i = 0; i < slots; i++) {
347 printf("Slot %d: ", i + 1);
348 if (afi_slot == i + 1)
353 if (fw->revision[i] == 0LLU)
356 if (isprint(*(char *)&fw->revision[i]))
357 printf("[%s] %.8s\n", status,
358 (char *)&fw->revision[i]);
360 printf("[%s] %016jx\n", status,
366 * Table of log page printer / sizing.
368 * Make sure you keep all the pages of one vendor together so -v help
369 * lists all the vendors pages.
372 NVME_LOG_ERROR, NULL, "Drive Error Log",
375 NVME_LOG_HEALTH_INFORMATION, NULL, "Health/SMART Data",
376 print_log_health, sizeof(struct nvme_health_information_page));
378 NVME_LOG_FIRMWARE_SLOT, NULL, "Firmware Information",
379 print_log_firmware, sizeof(struct nvme_firmware_page));
384 const struct logpage_function *f;
387 fprintf(stderr, "\n");
388 fprintf(stderr, "%-8s %-10s %s\n", "Page", "Vendor","Page Name");
389 fprintf(stderr, "-------- ---------- ----------\n");
390 SLIST_FOREACH(f, &logpages, link) {
391 v = f->vendor == NULL ? "-" : f->vendor;
392 fprintf(stderr, "0x%02x %-10s %s\n", f->log_page, v, f->name);
399 logpage(const struct cmd *f, int argc, char *argv[])
405 const struct logpage_function *lpf;
406 struct nvme_controller_data cdata;
410 if (arg_parse(argc, argv, f))
412 if (opt.hex && opt.binary) {
414 "Can't specify both binary and hex\n");
415 arg_help(argc, argv, f);
417 if (opt.vendor != NULL && strcmp(opt.vendor, "help") == 0)
419 if (opt.page == NONE) {
420 fprintf(stderr, "Missing page_id (-p).\n");
421 arg_help(argc, argv, f);
423 open_dev(opt.dev, &fd, 1, 1);
424 get_nsid(fd, &path, &nsid);
426 nsid = NVME_GLOBAL_NAMESPACE_TAG;
429 open_dev(path, &fd, 1, 1);
433 read_controller_data(fd, &cdata);
435 ns_smart = (cdata.lpa >> NVME_CTRLR_DATA_LPA_NS_SMART_SHIFT) &
436 NVME_CTRLR_DATA_LPA_NS_SMART_MASK;
439 * The log page attribtues indicate whether or not the controller
440 * supports the SMART/Health information log page on a per
443 if (nsid != NVME_GLOBAL_NAMESPACE_TAG) {
444 if (opt.page != NVME_LOG_HEALTH_INFORMATION)
445 errx(1, "log page %d valid only at controller level",
449 "controller does not support per namespace "
450 "smart/health information");
453 print_fn = print_log_hex;
456 print_fn = print_bin;
457 if (!opt.binary && !opt.hex) {
459 * See if there is a pretty print function for the specified log
460 * page. If one isn't found, we just revert to the default
461 * (print_hex). If there was a vendor specified by the user, and
462 * the page is vendor specific, don't match the print function
463 * unless the vendors match.
465 SLIST_FOREACH(lpf, &logpages, link) {
466 if (lpf->vendor != NULL && opt.vendor != NULL &&
467 strcmp(lpf->vendor, opt.vendor) != 0)
469 if (opt.page != lpf->log_page)
471 print_fn = lpf->print_fn;
477 if (opt.page == NVME_LOG_ERROR) {
478 size = sizeof(struct nvme_error_information_entry);
479 size *= (cdata.elpe + 1);
482 /* Read the log page */
483 buf = get_log_buffer(size);
484 read_logpage(fd, opt.page, nsid, buf, size);
485 print_fn(&cdata, buf, size);