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 /* Tables for command line parsing */ 52 53 static cmd_fn_t logpage; 54 55 #define NONE 0xffffffffu 56 static struct options { 57 bool binary; 58 bool hex; 59 uint32_t page; 60 const char *vendor; 61 const char *dev; 62 } opt = { 63 .binary = false, 64 .hex = false, 65 .page = NONE, 66 .vendor = NULL, 67 .dev = NULL, 68 }; 69 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, 77 "Page to dump"), 78 OPT("vendor", 'v', arg_string, opt, vendor, 79 "Vendor specific formatting"), 80 { NULL, 0, arg_none, NULL, NULL } 81 }; 82 #undef OPT 83 84 static const struct args logpage_args[] = { 85 { arg_string, &opt.dev, "<controller id|namespace id>" }, 86 { arg_none, NULL, NULL }, 87 }; 88 89 static struct cmd logpage_cmd = { 90 .name = "logpage", 91 .fn = logpage, 92 .descr = "Print logpages in human-readable form", 93 .ctx_size = sizeof(opt), 94 .opts = logpage_opts, 95 .args = logpage_args, 96 }; 97 98 CMD_COMMAND(logpage_cmd); 99 100 /* End of tables for command line parsing */ 101 102 #define MAX_FW_SLOTS (7) 103 104 static SLIST_HEAD(,logpage_function) logpages; 105 106 void 107 logpage_register(struct logpage_function *p) 108 { 109 110 SLIST_INSERT_HEAD(&logpages, p, link); 111 } 112 113 const char * 114 kv_lookup(const struct kv_name *kv, size_t kv_count, uint32_t key) 115 { 116 static char bad[32]; 117 size_t i; 118 119 for (i = 0; i < kv_count; i++, kv++) 120 if (kv->key == key) 121 return kv->name; 122 snprintf(bad, sizeof(bad), "Attribute %#x", key); 123 return bad; 124 } 125 126 static void 127 print_log_hex(const struct nvme_controller_data *cdata __unused, void *data, uint32_t length) 128 { 129 130 print_hex(data, length); 131 } 132 133 static void 134 print_bin(const struct nvme_controller_data *cdata __unused, void *data, uint32_t length) 135 { 136 137 write(STDOUT_FILENO, data, length); 138 } 139 140 static void * 141 get_log_buffer(uint32_t size) 142 { 143 void *buf; 144 145 if ((buf = malloc(size)) == NULL) 146 errx(1, "unable to malloc %u bytes", size); 147 148 memset(buf, 0, size); 149 return (buf); 150 } 151 152 void 153 read_logpage(int fd, uint8_t log_page, uint32_t nsid, void *payload, 154 uint32_t payload_size) 155 { 156 struct nvme_pt_command pt; 157 struct nvme_error_information_entry *err_entry; 158 int i, err_pages; 159 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); 166 pt.buf = payload; 167 pt.len = payload_size; 168 pt.is_read = 1; 169 170 if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0) 171 err(1, "get log page request failed"); 172 173 /* Convert data to host endian */ 174 switch (log_page) { 175 case NVME_LOG_ERROR: 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++); 180 break; 181 case NVME_LOG_HEALTH_INFORMATION: 182 nvme_health_information_page_swapbytes( 183 (struct nvme_health_information_page *)payload); 184 break; 185 case NVME_LOG_FIRMWARE_SLOT: 186 nvme_firmware_page_swapbytes( 187 (struct nvme_firmware_page *)payload); 188 break; 189 case INTEL_LOG_TEMP_STATS: 190 intel_log_temp_stats_swapbytes( 191 (struct intel_log_temp_stats *)payload); 192 break; 193 default: 194 break; 195 } 196 197 if (nvme_completion_is_error(&pt.cpl)) 198 errx(1, "get log page request returned error"); 199 } 200 201 static void 202 print_log_error(const struct nvme_controller_data *cdata __unused, void *buf, uint32_t size) 203 { 204 int i, nentries; 205 uint16_t status; 206 uint8_t p, sc, sct, m, dnr; 207 struct nvme_error_information_entry *entry = buf; 208 209 printf("Error Information Log\n"); 210 printf("=====================\n"); 211 212 if (entry->error_count == 0) { 213 printf("No error entries found\n"); 214 return; 215 } 216 217 nentries = size/sizeof(struct nvme_error_information_entry); 218 for (i = 0; i < nentries; i++, entry++) { 219 if (entry->error_count == 0) 220 break; 221 222 status = entry->status; 223 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); 229 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); 246 } 247 } 248 249 void 250 print_temp(uint16_t t) 251 { 252 printf("%u K, %2.2f C, %3.2f F\n", t, (float)t - 273.15, (float)t * 9 / 5 - 459.67); 253 } 254 255 256 static void 257 print_log_health(const struct nvme_controller_data *cdata __unused, void *buf, uint32_t size __unused) 258 { 259 struct nvme_health_information_page *health = buf; 260 char cbuf[UINT128_DIG + 1]; 261 uint8_t warning; 262 int i; 263 264 warning = health->critical_warning; 265 266 printf("SMART/Health Information Log\n"); 267 printf("============================\n"); 268 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); 288 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))); 309 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) 314 continue; 315 printf("Temperature Sensor %d: ", i + 1); 316 print_temp(health->temp_sensor[i]); 317 } 318 } 319 320 static void 321 print_log_firmware(const struct nvme_controller_data *cdata, void *buf, uint32_t size __unused) 322 { 323 int i, slots; 324 const char *status; 325 struct nvme_firmware_page *fw = buf; 326 uint8_t afi_slot; 327 uint16_t oacs_fw; 328 uint8_t fw_num_slots; 329 330 afi_slot = fw->afi >> NVME_FIRMWARE_PAGE_AFI_SLOT_SHIFT; 331 afi_slot &= NVME_FIRMWARE_PAGE_AFI_SLOT_MASK; 332 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; 337 338 printf("Firmware Slot Log\n"); 339 printf("=================\n"); 340 341 if (oacs_fw == 0) 342 slots = 1; 343 else 344 slots = MIN(fw_num_slots, MAX_FW_SLOTS); 345 346 for (i = 0; i < slots; i++) { 347 printf("Slot %d: ", i + 1); 348 if (afi_slot == i + 1) 349 status = " Active"; 350 else 351 status = "Inactive"; 352 353 if (fw->revision[i] == 0LLU) 354 printf("Empty\n"); 355 else 356 if (isprint(*(char *)&fw->revision[i])) 357 printf("[%s] %.8s\n", status, 358 (char *)&fw->revision[i]); 359 else 360 printf("[%s] %016jx\n", status, 361 fw->revision[i]); 362 } 363 } 364 365 /* 366 * Table of log page printer / sizing. 367 * 368 * Make sure you keep all the pages of one vendor together so -v help 369 * lists all the vendors pages. 370 */ 371 NVME_LOGPAGE(error, 372 NVME_LOG_ERROR, NULL, "Drive Error Log", 373 print_log_error, 0); 374 NVME_LOGPAGE(health, 375 NVME_LOG_HEALTH_INFORMATION, NULL, "Health/SMART Data", 376 print_log_health, sizeof(struct nvme_health_information_page)); 377 NVME_LOGPAGE(fw, 378 NVME_LOG_FIRMWARE_SLOT, NULL, "Firmware Information", 379 print_log_firmware, sizeof(struct nvme_firmware_page)); 380 381 static void 382 logpage_help(void) 383 { 384 const struct logpage_function *f; 385 const char *v; 386 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); 393 } 394 395 exit(1); 396 } 397 398 static void 399 logpage(const struct cmd *f, int argc, char *argv[]) 400 { 401 int fd; 402 bool ns_specified; 403 char cname[64]; 404 uint32_t nsid, size; 405 void *buf; 406 const struct logpage_function *lpf; 407 struct nvme_controller_data cdata; 408 print_fn_t print_fn; 409 uint8_t ns_smart; 410 411 if (arg_parse(argc, argv, f)) 412 return; 413 if (opt.hex && opt.binary) { 414 fprintf(stderr, 415 "Can't specify both binary and hex\n"); 416 arg_help(argc, argv, f); 417 } 418 if (opt.vendor != NULL && strcmp(opt.vendor, "help") == 0) 419 logpage_help(); 420 if (opt.page == NONE) { 421 fprintf(stderr, "Missing page_id (-p).\n"); 422 arg_help(argc, argv, f); 423 } 424 if (strstr(opt.dev, NVME_NS_PREFIX) != NULL) { 425 ns_specified = true; 426 parse_ns_str(opt.dev, cname, &nsid); 427 open_dev(cname, &fd, 1, 1); 428 } else { 429 ns_specified = false; 430 nsid = NVME_GLOBAL_NAMESPACE_TAG; 431 open_dev(opt.dev, &fd, 1, 1); 432 } 433 434 read_controller_data(fd, &cdata); 435 436 ns_smart = (cdata.lpa >> NVME_CTRLR_DATA_LPA_NS_SMART_SHIFT) & 437 NVME_CTRLR_DATA_LPA_NS_SMART_MASK; 438 439 /* 440 * The log page attribtues indicate whether or not the controller 441 * supports the SMART/Health information log page on a per 442 * namespace basis. 443 */ 444 if (ns_specified) { 445 if (opt.page != NVME_LOG_HEALTH_INFORMATION) 446 errx(1, "log page %d valid only at controller level", 447 opt.page); 448 if (ns_smart == 0) 449 errx(1, 450 "controller does not support per namespace " 451 "smart/health information"); 452 } 453 454 print_fn = print_log_hex; 455 size = DEFAULT_SIZE; 456 if (opt.binary) 457 print_fn = print_bin; 458 if (!opt.binary && !opt.hex) { 459 /* 460 * See if there is a pretty print function for the specified log 461 * page. If one isn't found, we just revert to the default 462 * (print_hex). If there was a vendor specified by the user, and 463 * the page is vendor specific, don't match the print function 464 * unless the vendors match. 465 */ 466 SLIST_FOREACH(lpf, &logpages, link) { 467 if (lpf->vendor != NULL && opt.vendor != NULL && 468 strcmp(lpf->vendor, opt.vendor) != 0) 469 continue; 470 if (opt.page != lpf->log_page) 471 continue; 472 print_fn = lpf->print_fn; 473 size = lpf->size; 474 break; 475 } 476 } 477 478 if (opt.page == NVME_LOG_ERROR) { 479 size = sizeof(struct nvme_error_information_entry); 480 size *= (cdata.elpe + 1); 481 } 482 483 /* Read the log page */ 484 buf = get_log_buffer(size); 485 read_logpage(fd, opt.page, nsid, buf, size); 486 print_fn(&cdata, buf, size); 487 488 close(fd); 489 exit(0); 490 } 491