1 /* $NetBSD: logpage.c,v 1.4 2017/04/29 00:06:40 nonaka Exp $ */ 2 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 #ifndef lint 34 __RCSID("$NetBSD: logpage.c,v 1.4 2017/04/29 00:06:40 nonaka Exp $"); 35 #if 0 36 __FBSDID("$FreeBSD: head/sbin/nvmecontrol/logpage.c 314230 2017-02-25 00:09:16Z imp $"); 37 #endif 38 #endif 39 40 #include <sys/param.h> 41 #include <sys/ioccom.h> 42 #include <sys/endian.h> 43 44 #include <ctype.h> 45 #include <err.h> 46 #include <fcntl.h> 47 #include <stdbool.h> 48 #include <stddef.h> 49 #include <stdio.h> 50 #include <stdlib.h> 51 #include <string.h> 52 #include <unistd.h> 53 54 #include "nvmectl.h" 55 #include "bn.h" 56 57 #define DEFAULT_SIZE (4096) 58 #define MAX_FW_SLOTS (7) 59 60 typedef void (*print_fn_t)(void *buf, uint32_t size); 61 62 struct kv_name { 63 uint32_t key; 64 const char *name; 65 }; 66 67 static const char * 68 kv_lookup(const struct kv_name *kv, size_t kv_count, uint32_t key) 69 { 70 static char bad[32]; 71 size_t i; 72 73 for (i = 0; i < kv_count; i++, kv++) 74 if (kv->key == key) 75 return kv->name; 76 snprintf(bad, sizeof(bad), "Attribute %#x", key); 77 return bad; 78 } 79 80 static void 81 print_bin(void *data, uint32_t length) 82 { 83 write(STDOUT_FILENO, data, length); 84 } 85 86 /* "Missing" from endian.h */ 87 static __inline uint64_t 88 le48dec(const void *pp) 89 { 90 uint8_t const *p = (uint8_t const *)pp; 91 92 return (((uint64_t)le16dec(p + 4) << 32) | le32dec(p)); 93 } 94 95 static void * 96 get_log_buffer(uint32_t size) 97 { 98 void *buf; 99 100 if ((buf = malloc(size)) == NULL) 101 errx(1, "unable to malloc %u bytes", size); 102 103 memset(buf, 0, size); 104 return (buf); 105 } 106 107 void 108 read_logpage(int fd, uint8_t log_page, int nsid, void *payload, 109 uint32_t payload_size) 110 { 111 struct nvme_pt_command pt; 112 113 memset(&pt, 0, sizeof(pt)); 114 pt.cmd.opcode = NVM_ADMIN_GET_LOG_PG; 115 pt.cmd.nsid = nsid; 116 pt.cmd.cdw10 = ((payload_size/sizeof(uint32_t)) - 1) << 16; 117 pt.cmd.cdw10 |= log_page; 118 pt.buf = payload; 119 pt.len = payload_size; 120 pt.is_read = 1; 121 122 if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0) 123 err(1, "get log page request failed"); 124 125 if (nvme_completion_is_error(&pt.cpl)) 126 errx(1, "get log page request returned error"); 127 } 128 129 static void 130 print_log_error(void *buf, uint32_t size) 131 { 132 int i, nentries; 133 struct nvme_error_information_entry *entry = buf; 134 135 printf("Error Information Log\n"); 136 printf("=====================\n"); 137 138 if (entry->error_count == 0) { 139 printf("No error entries found\n"); 140 return; 141 } 142 143 nentries = size/sizeof(struct nvme_error_information_entry); 144 for (i = 0; i < nentries; i++, entry++) { 145 if (entry->error_count == 0) 146 break; 147 148 printf("Entry %02d\n", i + 1); 149 printf("=========\n"); 150 printf(" Error count: %ju\n", entry->error_count); 151 printf(" Submission queue ID: %u\n", entry->sqid); 152 printf(" Command ID: %u\n", entry->cid); 153 /* TODO: Export nvme_status_string structures from kernel? */ 154 printf(" Status:\n"); 155 printf(" Phase tag: %d\n", 156 (uint16_t)__SHIFTOUT(entry->status, NVME_CQE_PHASE)); 157 printf(" Status code: %d\n", 158 (uint16_t)__SHIFTOUT(entry->status, NVME_CQE_SC_MASK)); 159 printf(" Status code type: %d\n", 160 (uint16_t)__SHIFTOUT(entry->status, NVME_CQE_SCT_MASK)); 161 printf(" More: %d\n", 162 (uint16_t)__SHIFTOUT(entry->status, NVME_CQE_M)); 163 printf(" DNR: %d\n", 164 (uint16_t)__SHIFTOUT(entry->status, NVME_CQE_DNR)); 165 printf(" Error location: %u\n", entry->error_location); 166 printf(" LBA: %ju\n", entry->lba); 167 printf(" Namespace ID: %u\n", entry->nsid); 168 printf(" Vendor specific info: %u\n", entry->vendor_specific); 169 printf(" Command specific info: %ju\n", 170 entry->command_specific); 171 } 172 } 173 174 #define METRIX_PREFIX_BUFSIZ 17 175 #define NO_METRIX_PREFIX_BUFSIZ 42 176 177 static void 178 print_bignum(const char *title, uint64_t v[2], const char *suffix) 179 { 180 char buf[64]; 181 uint8_t tmp[16]; 182 uint64_t l = le64toh(v[0]); 183 uint64_t h = le64toh(v[1]); 184 185 tmp[ 0] = (h >> 56) & 0xff; 186 tmp[ 1] = (h >> 48) & 0xff; 187 tmp[ 2] = (h >> 40) & 0xff; 188 tmp[ 3] = (h >> 32) & 0xff; 189 tmp[ 4] = (h >> 24) & 0xff; 190 tmp[ 5] = (h >> 16) & 0xff; 191 tmp[ 6] = (h >> 8) & 0xff; 192 tmp[ 7] = h & 0xff; 193 tmp[ 8] = (l >> 56) & 0xff; 194 tmp[ 9] = (l >> 48) & 0xff; 195 tmp[10] = (l >> 40) & 0xff; 196 tmp[11] = (l >> 32) & 0xff; 197 tmp[12] = (l >> 24) & 0xff; 198 tmp[13] = (l >> 16) & 0xff; 199 tmp[14] = (l >> 8) & 0xff; 200 tmp[15] = l & 0xff; 201 202 buf[0] = '\0'; 203 BIGNUM *bn = BN_bin2bn(tmp, __arraycount(tmp), NULL); 204 if (bn != NULL) { 205 humanize_bignum(buf, METRIX_PREFIX_BUFSIZ + strlen(suffix), 206 bn, suffix, HN_AUTOSCALE, HN_DECIMAL); 207 BN_free(bn); 208 } 209 if (buf[0] == '\0') 210 snprintf(buf, sizeof(buf), "0x%016jx%016jx", h, l); 211 printf("%-31s %s\n", title, buf); 212 } 213 214 static void 215 print_temp(uint16_t t) 216 { 217 printf("%u K, %2.2f C, %3.2f F\n", t, (float)t - 273.15, 218 (float)t * 9 / 5 - 459.67); 219 } 220 221 static void 222 print_log_health(void *buf, uint32_t size __unused) 223 { 224 struct nvme_health_information_page *health = buf; 225 int i; 226 227 printf("SMART/Health Information Log\n"); 228 printf("============================\n"); 229 230 printf("Critical Warning State: 0x%02x\n", 231 health->critical_warning); 232 printf(" Available spare: %d\n", 233 (uint8_t)__SHIFTOUT(health->critical_warning, 234 NVME_HEALTH_PAGE_CW_AVAIL_SPARE)); 235 printf(" Temperature: %d\n", 236 (uint8_t)__SHIFTOUT(health->critical_warning, 237 NVME_HEALTH_PAGE_CW_TEMPERTURE)); 238 printf(" Device reliability: %d\n", 239 (uint8_t)__SHIFTOUT(health->critical_warning, 240 NVME_HEALTH_PAGE_CW_DEVICE_RELIABLITY)); 241 printf(" Read only: %d\n", 242 (uint8_t)__SHIFTOUT(health->critical_warning, 243 NVME_HEALTH_PAGE_CW_READ_ONLY)); 244 printf(" Volatile memory backup: %d\n", 245 (uint8_t)__SHIFTOUT(health->critical_warning, 246 NVME_HEALTH_PAGE_CW_VOLATILE_MEMORY_BACKUP)); 247 printf("Temperature: "); 248 print_temp(health->composite_temperature); 249 printf("Available spare: %u\n", 250 health->available_spare); 251 printf("Available spare threshold: %u\n", 252 health->available_spare_threshold); 253 printf("Percentage used: %u\n", 254 health->percentage_used); 255 256 print_bignum("Data units (512 byte) read:", health->data_units_read, ""); 257 print_bignum("Data units (512 byte) written:", health->data_units_written, 258 ""); 259 print_bignum("Host read commands:", health->host_read_commands, ""); 260 print_bignum("Host write commands:", health->host_write_commands, ""); 261 print_bignum("Controller busy time (minutes):", health->controller_busy_time, 262 ""); 263 print_bignum("Power cycles:", health->power_cycles, ""); 264 print_bignum("Power on hours:", health->power_on_hours, ""); 265 print_bignum("Unsafe shutdowns:", health->unsafe_shutdowns, ""); 266 print_bignum("Media errors:", health->media_errors, ""); 267 print_bignum("No. error info log entries:", 268 health->num_error_info_log_entries, ""); 269 270 printf("Warning Temp Composite Time: %d\n", health->warning_temp_time); 271 printf("Error Temp Composite Time: %d\n", health->error_temp_time); 272 for (i = 0; i < 7; i++) { 273 if (health->temp_sensor[i] == 0) 274 continue; 275 printf("Temperature Sensor %d: ", i + 1); 276 print_temp(health->temp_sensor[i]); 277 } 278 } 279 280 static void 281 print_log_firmware(void *buf, uint32_t size __unused) 282 { 283 u_int i; 284 const char *status; 285 struct nvme_firmware_page *fw = buf; 286 287 printf("Firmware Slot Log\n"); 288 printf("=================\n"); 289 290 for (i = 0; i < MAX_FW_SLOTS; i++) { 291 printf("Slot %d: ", i + 1); 292 if (__SHIFTOUT(fw->afi, NVME_FW_PAGE_AFI_SLOT) == i + 1) 293 status = " Active"; 294 else 295 status = "Inactive"; 296 297 if (fw->revision[i] == 0LLU) 298 printf("Empty\n"); 299 else 300 if (isprint(*(uint8_t *)&fw->revision[i])) 301 printf("[%s] %.8s\n", status, 302 (char *)&fw->revision[i]); 303 else 304 printf("[%s] %016jx\n", status, 305 fw->revision[i]); 306 } 307 } 308 309 /* 310 * Intel specific log pages from 311 * http://www.intel.com/content/dam/www/public/us/en/documents/product-specifications/ssd-dc-p3700-spec.pdf 312 * 313 * Though the version as of this date has a typo for the size of log page 0xca, 314 * offset 147: it is only 1 byte, not 6. 315 */ 316 static void 317 print_intel_temp_stats(void *buf, uint32_t size __unused) 318 { 319 struct intel_log_temp_stats *temp = buf; 320 321 printf("Intel Temperature Log\n"); 322 printf("=====================\n"); 323 324 printf("Current: "); 325 print_temp(temp->current); 326 printf("Overtemp Last Flags %#jx\n", 327 (uintmax_t)temp->overtemp_flag_last); 328 printf("Overtemp Lifetime Flags %#jx\n", 329 (uintmax_t)temp->overtemp_flag_life); 330 printf("Max Temperature "); 331 print_temp(temp->max_temp); 332 printf("Min Temperature "); 333 print_temp(temp->min_temp); 334 printf("Max Operating Temperature "); 335 print_temp(temp->max_oper_temp); 336 printf("Min Operating Temperature "); 337 print_temp(temp->min_oper_temp); 338 printf("Estimated Temperature Offset: %ju C/K\n", 339 (uintmax_t)temp->est_offset); 340 } 341 342 /* 343 * Format from Table 22, section 5.7 IO Command Latency Statistics. 344 * Read and write stats pages have identical encoding. 345 */ 346 static void 347 print_intel_read_write_lat_log(void *buf, uint32_t size __unused) 348 { 349 const char *walker = buf; 350 int i; 351 352 printf("Major: %d\n", le16dec(walker + 0)); 353 printf("Minor: %d\n", le16dec(walker + 2)); 354 for (i = 0; i < 32; i++) 355 printf("%4dus-%4dus: %ju\n", i * 32, (i + 1) * 32, 356 (uintmax_t)le32dec(walker + 4 + i * 4)); 357 for (i = 1; i < 32; i++) 358 printf("%4dms-%4dms: %ju\n", i, i + 1, 359 (uintmax_t)le32dec(walker + 132 + i * 4)); 360 for (i = 1; i < 32; i++) 361 printf("%4dms-%4dms: %ju\n", i * 32, (i + 1) * 32, 362 (uintmax_t)le32dec(walker + 256 + i * 4)); 363 } 364 365 static void 366 print_intel_read_lat_log(void *buf, uint32_t size) 367 { 368 369 printf("Intel Read Latency Log\n"); 370 printf("======================\n"); 371 print_intel_read_write_lat_log(buf, size); 372 } 373 374 static void 375 print_intel_write_lat_log(void *buf, uint32_t size) 376 { 377 378 printf("Intel Write Latency Log\n"); 379 printf("=======================\n"); 380 print_intel_read_write_lat_log(buf, size); 381 } 382 383 /* 384 * Table 19. 5.4 SMART Attributes. 385 * Samsung also implements this and some extra data not documented. 386 */ 387 static void 388 print_intel_add_smart(void *buf, uint32_t size __unused) 389 { 390 uint8_t *walker = buf; 391 uint8_t *end = walker + 150; 392 const char *name; 393 uint64_t raw; 394 uint8_t normalized; 395 396 static struct kv_name kv[] = { 397 { 0xab, "Program Fail Count" }, 398 { 0xac, "Erase Fail Count" }, 399 { 0xad, "Wear Leveling Count" }, 400 { 0xb8, "End to End Error Count" }, 401 { 0xc7, "CRC Error Count" }, 402 { 0xe2, "Timed: Media Wear" }, 403 { 0xe3, "Timed: Host Read %" }, 404 { 0xe4, "Timed: Elapsed Time" }, 405 { 0xea, "Thermal Throttle Status" }, 406 { 0xf0, "Retry Buffer Overflows" }, 407 { 0xf3, "PLL Lock Loss Count" }, 408 { 0xf4, "NAND Bytes Written" }, 409 { 0xf5, "Host Bytes Written" }, 410 }; 411 412 printf("Additional SMART Data Log\n"); 413 printf("=========================\n"); 414 /* 415 * walker[0] = Key 416 * walker[1,2] = reserved 417 * walker[3] = Normalized Value 418 * walker[4] = reserved 419 * walker[5..10] = Little Endian Raw value 420 * (or other represenations) 421 * walker[11] = reserved 422 */ 423 while (walker < end) { 424 name = kv_lookup(kv, __arraycount(kv), *walker); 425 normalized = walker[3]; 426 raw = le48dec(walker + 5); 427 switch (*walker){ 428 case 0: 429 break; 430 case 0xad: 431 printf("%-32s: %3d min: %u max: %u ave: %u\n", name, 432 normalized, le16dec(walker + 5), le16dec(walker + 7), 433 le16dec(walker + 9)); 434 break; 435 case 0xe2: 436 printf("%-32s: %3d %.3f%%\n", name, normalized, raw / 1024.0); 437 break; 438 case 0xea: 439 printf("%-32s: %3d %d%% %d times\n", name, normalized, 440 walker[5], le32dec(walker+6)); 441 break; 442 default: 443 printf("%-32s: %3d %ju\n", name, normalized, (uintmax_t)raw); 444 break; 445 } 446 walker += 12; 447 } 448 } 449 450 /* 451 * HGST's 0xc1 page. This is a grab bag of additional data. Please see 452 * https://www.hgst.com/sites/default/files/resources/US_SN150_ProdManual.pdf 453 * https://www.hgst.com/sites/default/files/resources/US_SN100_ProdManual.pdf 454 * Appendix A for details 455 */ 456 457 typedef void (*subprint_fn_t)(void *buf, uint16_t subtype, uint8_t res, uint32_t size); 458 459 struct subpage_print { 460 uint16_t key; 461 subprint_fn_t fn; 462 }; 463 464 static void print_hgst_info_write_errors(void *, uint16_t, uint8_t, uint32_t); 465 static void print_hgst_info_read_errors(void *, uint16_t, uint8_t, uint32_t); 466 static void print_hgst_info_verify_errors(void *, uint16_t, uint8_t, uint32_t); 467 static void print_hgst_info_self_test(void *, uint16_t, uint8_t, uint32_t); 468 static void print_hgst_info_background_scan(void *, uint16_t, uint8_t, uint32_t); 469 static void print_hgst_info_erase_errors(void *, uint16_t, uint8_t, uint32_t); 470 static void print_hgst_info_erase_counts(void *, uint16_t, uint8_t, uint32_t); 471 static void print_hgst_info_temp_history(void *, uint16_t, uint8_t, uint32_t); 472 static void print_hgst_info_ssd_perf(void *, uint16_t, uint8_t, uint32_t); 473 static void print_hgst_info_firmware_load(void *, uint16_t, uint8_t, uint32_t); 474 475 static struct subpage_print hgst_subpage[] = { 476 { 0x02, print_hgst_info_write_errors }, 477 { 0x03, print_hgst_info_read_errors }, 478 { 0x05, print_hgst_info_verify_errors }, 479 { 0x10, print_hgst_info_self_test }, 480 { 0x15, print_hgst_info_background_scan }, 481 { 0x30, print_hgst_info_erase_errors }, 482 { 0x31, print_hgst_info_erase_counts }, 483 { 0x32, print_hgst_info_temp_history }, 484 { 0x37, print_hgst_info_ssd_perf }, 485 { 0x38, print_hgst_info_firmware_load }, 486 }; 487 488 /* Print a subpage that is basically just key value pairs */ 489 static void 490 print_hgst_info_subpage_gen(void *buf, uint16_t subtype __unused, uint32_t size, 491 const struct kv_name *kv, size_t kv_count) 492 { 493 uint8_t *wsp, *esp; 494 uint16_t ptype; 495 uint8_t plen; 496 uint64_t param; 497 int i; 498 499 wsp = buf; 500 esp = wsp + size; 501 while (wsp < esp) { 502 ptype = le16dec(wsp); 503 wsp += 2; 504 wsp++; /* Flags, just ignore */ 505 plen = *wsp++; 506 param = 0; 507 for (i = 0; i < plen; i++) 508 param |= (uint64_t)*wsp++ << (i * 8); 509 printf(" %-30s: %jd\n", kv_lookup(kv, kv_count, ptype), 510 (uintmax_t)param); 511 } 512 } 513 514 static void 515 print_hgst_info_write_errors(void *buf, uint16_t subtype, uint8_t res __unused, 516 uint32_t size) 517 { 518 static const struct kv_name kv[] = { 519 { 0x0000, "Corrected Without Delay" }, 520 { 0x0001, "Corrected Maybe Delayed" }, 521 { 0x0002, "Re-Writes" }, 522 { 0x0003, "Errors Corrected" }, 523 { 0x0004, "Correct Algorithm Used" }, 524 { 0x0005, "Bytes Processed" }, 525 { 0x0006, "Uncorrected Errors" }, 526 { 0x8000, "Flash Write Commands" }, 527 { 0x8001, "HGST Special" }, 528 }; 529 530 printf("Write Errors Subpage:\n"); 531 print_hgst_info_subpage_gen(buf, subtype, size, kv, __arraycount(kv)); 532 } 533 534 static void 535 print_hgst_info_read_errors(void *buf, uint16_t subtype, uint8_t res __unused, 536 uint32_t size) 537 { 538 static const struct kv_name kv[] = { 539 { 0x0000, "Corrected Without Delay" }, 540 { 0x0001, "Corrected Maybe Delayed" }, 541 { 0x0002, "Re-Reads" }, 542 { 0x0003, "Errors Corrected" }, 543 { 0x0004, "Correct Algorithm Used" }, 544 { 0x0005, "Bytes Processed" }, 545 { 0x0006, "Uncorrected Errors" }, 546 { 0x8000, "Flash Read Commands" }, 547 { 0x8001, "XOR Recovered" }, 548 { 0x8002, "Total Corrected Bits" }, 549 }; 550 551 printf("Read Errors Subpage:\n"); 552 print_hgst_info_subpage_gen(buf, subtype, size, kv, __arraycount(kv)); 553 } 554 555 static void 556 print_hgst_info_verify_errors(void *buf, uint16_t subtype, uint8_t res __unused, 557 uint32_t size) 558 { 559 static const struct kv_name kv[] = { 560 { 0x0000, "Corrected Without Delay" }, 561 { 0x0001, "Corrected Maybe Delayed" }, 562 { 0x0002, "Re-Reads" }, 563 { 0x0003, "Errors Corrected" }, 564 { 0x0004, "Correct Algorithm Used" }, 565 { 0x0005, "Bytes Processed" }, 566 { 0x0006, "Uncorrected Errors" }, 567 { 0x8000, "Commands Processed" }, 568 }; 569 570 printf("Verify Errors Subpage:\n"); 571 print_hgst_info_subpage_gen(buf, subtype, size, kv, __arraycount(kv)); 572 } 573 574 static void 575 print_hgst_info_self_test(void *buf, uint16_t subtype __unused, uint8_t res __unused, 576 uint32_t size) 577 { 578 size_t i; 579 uint8_t *walker = buf; 580 uint16_t code, hrs; 581 uint32_t lba; 582 583 printf("Self Test Subpage:\n"); 584 for (i = 0; i < size / 20; i++) { /* Each entry is 20 bytes */ 585 code = le16dec(walker); 586 walker += 2; 587 walker++; /* Ignore fixed flags */ 588 if (*walker == 0) /* Last entry is zero length */ 589 break; 590 if (*walker++ != 0x10) { 591 printf("Bad length for self test report\n"); 592 return; 593 } 594 printf(" %-30s: %d\n", "Recent Test", code); 595 printf(" %-28s: %#x\n", "Self-Test Results", *walker & 0xf); 596 printf(" %-28s: %#x\n", "Self-Test Code", (*walker >> 5) & 0x7); 597 walker++; 598 printf(" %-28s: %#x\n", "Self-Test Number", *walker++); 599 hrs = le16dec(walker); 600 walker += 2; 601 lba = le32dec(walker); 602 walker += 4; 603 printf(" %-28s: %u\n", "Total Power On Hrs", hrs); 604 printf(" %-28s: %#jx (%jd)\n", "LBA", (uintmax_t)lba, 605 (uintmax_t)lba); 606 printf(" %-28s: %#x\n", "Sense Key", *walker++ & 0xf); 607 printf(" %-28s: %#x\n", "Additional Sense Code", *walker++); 608 printf(" %-28s: %#x\n", "Additional Sense Qualifier", *walker++); 609 printf(" %-28s: %#x\n", "Vendor Specific Detail", *walker++); 610 } 611 } 612 613 static void 614 print_hgst_info_background_scan(void *buf, uint16_t subtype __unused, 615 uint8_t res __unused, uint32_t size) 616 { 617 uint8_t *walker = buf; 618 uint8_t status; 619 uint16_t code, nscan, progress; 620 uint32_t pom, nand; 621 622 printf("Background Media Scan Subpage:\n"); 623 /* Decode the header */ 624 code = le16dec(walker); 625 walker += 2; 626 walker++; /* Ignore fixed flags */ 627 if (*walker++ != 0x10) { 628 printf("Bad length for background scan header\n"); 629 return; 630 } 631 if (code != 0) { 632 printf("Expceted code 0, found code %#x\n", code); 633 return; 634 } 635 pom = le32dec(walker); 636 walker += 4; 637 walker++; /* Reserved */ 638 status = *walker++; 639 nscan = le16dec(walker); 640 walker += 2; 641 progress = le16dec(walker); 642 walker += 2; 643 walker += 6; /* Reserved */ 644 printf(" %-30s: %d\n", "Power On Minutes", pom); 645 printf(" %-30s: %x (%s)\n", "BMS Status", status, 646 status == 0 ? "idle" : (status == 1 ? "active" : 647 (status == 8 ? "suspended" : "unknown"))); 648 printf(" %-30s: %d\n", "Number of BMS", nscan); 649 printf(" %-30s: %d\n", "Progress Current BMS", progress); 650 /* Report retirements */ 651 if (walker - (uint8_t *)buf != 20) { 652 printf("Coding error, offset not 20\n"); 653 return; 654 } 655 size -= 20; 656 printf(" %-30s: %d\n", "BMS retirements", size / 0x18); 657 while (size > 0) { 658 code = le16dec(walker); 659 walker += 2; 660 walker++; 661 if (*walker++ != 0x14) { 662 printf("Bad length parameter\n"); 663 return; 664 } 665 pom = le32dec(walker); 666 walker += 4; 667 /* 668 * Spec sheet says the following are hard coded, if true, just 669 * print the NAND retirement. 670 */ 671 if (walker[0] == 0x41 && 672 walker[1] == 0x0b && 673 walker[2] == 0x01 && 674 walker[3] == 0x00 && 675 walker[4] == 0x00 && 676 walker[5] == 0x00 && 677 walker[6] == 0x00 && 678 walker[7] == 0x00) { 679 walker += 8; 680 walker += 4; /* Skip reserved */ 681 nand = le32dec(walker); 682 walker += 4; 683 printf(" %-30s: %d\n", "Retirement number", code); 684 printf(" %-28s: %#x\n", "NAND (C/T)BBBPPP", nand); 685 } else { 686 printf("Parameter %#x entry corrupt\n", code); 687 walker += 16; 688 } 689 } 690 } 691 692 static void 693 print_hgst_info_erase_errors(void *buf, uint16_t subtype __unused, 694 uint8_t res __unused, uint32_t size) 695 { 696 static const struct kv_name kv[] = { 697 { 0x0000, "Corrected Without Delay" }, 698 { 0x0001, "Corrected Maybe Delayed" }, 699 { 0x0002, "Re-Erase" }, 700 { 0x0003, "Errors Corrected" }, 701 { 0x0004, "Correct Algorithm Used" }, 702 { 0x0005, "Bytes Processed" }, 703 { 0x0006, "Uncorrected Errors" }, 704 { 0x8000, "Flash Erase Commands" }, 705 { 0x8001, "Mfg Defect Count" }, 706 { 0x8002, "Grown Defect Count" }, 707 { 0x8003, "Erase Count -- User" }, 708 { 0x8004, "Erase Count -- System" }, 709 }; 710 711 printf("Erase Errors Subpage:\n"); 712 print_hgst_info_subpage_gen(buf, subtype, size, kv, __arraycount(kv)); 713 } 714 715 static void 716 print_hgst_info_erase_counts(void *buf, uint16_t subtype, uint8_t res __unused, 717 uint32_t size) 718 { 719 /* My drive doesn't export this -- so not coding up */ 720 printf("XXX: Erase counts subpage: %p, %#x %d\n", buf, subtype, size); 721 } 722 723 static void 724 print_hgst_info_temp_history(void *buf, uint16_t subtype __unused, 725 uint8_t res __unused, uint32_t size __unused) 726 { 727 uint8_t *walker = buf; 728 uint32_t min; 729 730 printf("Temperature History:\n"); 731 printf(" %-30s: %d C\n", "Current Temperature", *walker++); 732 printf(" %-30s: %d C\n", "Reference Temperature", *walker++); 733 printf(" %-30s: %d C\n", "Maximum Temperature", *walker++); 734 printf(" %-30s: %d C\n", "Minimum Temperature", *walker++); 735 min = le32dec(walker); 736 walker += 4; 737 printf(" %-30s: %d:%02d:00\n", "Max Temperature Time", min / 60, min % 60); 738 min = le32dec(walker); 739 walker += 4; 740 printf(" %-30s: %d:%02d:00\n", "Over Temperature Duration", min / 60, 741 min % 60); 742 min = le32dec(walker); 743 walker += 4; 744 printf(" %-30s: %d:%02d:00\n", "Min Temperature Time", min / 60, min % 60); 745 } 746 747 static void 748 print_hgst_info_ssd_perf(void *buf, uint16_t subtype __unused, uint8_t res, 749 uint32_t size __unused) 750 { 751 uint8_t *walker = buf; 752 uint64_t val; 753 754 printf("SSD Performance Subpage Type %d:\n", res); 755 val = le64dec(walker); 756 walker += 8; 757 printf(" %-30s: %ju\n", "Host Read Commands", val); 758 val = le64dec(walker); 759 walker += 8; 760 printf(" %-30s: %ju\n", "Host Read Blocks", val); 761 val = le64dec(walker); 762 walker += 8; 763 printf(" %-30s: %ju\n", "Host Cache Read Hits Commands", val); 764 val = le64dec(walker); 765 walker += 8; 766 printf(" %-30s: %ju\n", "Host Cache Read Hits Blocks", val); 767 val = le64dec(walker); 768 walker += 8; 769 printf(" %-30s: %ju\n", "Host Read Commands Stalled", val); 770 val = le64dec(walker); 771 walker += 8; 772 printf(" %-30s: %ju\n", "Host Write Commands", val); 773 val = le64dec(walker); 774 walker += 8; 775 printf(" %-30s: %ju\n", "Host Write Blocks", val); 776 val = le64dec(walker); 777 walker += 8; 778 printf(" %-30s: %ju\n", "Host Write Odd Start Commands", val); 779 val = le64dec(walker); 780 walker += 8; 781 printf(" %-30s: %ju\n", "Host Write Odd End Commands", val); 782 val = le64dec(walker); 783 walker += 8; 784 printf(" %-30s: %ju\n", "Host Write Commands Stalled", val); 785 val = le64dec(walker); 786 walker += 8; 787 printf(" %-30s: %ju\n", "NAND Read Commands", val); 788 val = le64dec(walker); 789 walker += 8; 790 printf(" %-30s: %ju\n", "NAND Read Blocks", val); 791 val = le64dec(walker); 792 walker += 8; 793 printf(" %-30s: %ju\n", "NAND Write Commands", val); 794 val = le64dec(walker); 795 walker += 8; 796 printf(" %-30s: %ju\n", "NAND Write Blocks", val); 797 val = le64dec(walker); 798 walker += 8; 799 printf(" %-30s: %ju\n", "NAND Read Before Writes", val); 800 } 801 802 static void 803 print_hgst_info_firmware_load(void *buf, uint16_t subtype __unused, 804 uint8_t res __unused, uint32_t size __unused) 805 { 806 uint8_t *walker = buf; 807 808 printf("Firmware Load Subpage:\n"); 809 printf(" %-30s: %d\n", "Firmware Downloads", le32dec(walker)); 810 } 811 812 static void 813 kv_indirect(void *buf, uint32_t subtype, uint8_t res, uint32_t size, 814 struct subpage_print *sp, size_t nsp) 815 { 816 size_t i; 817 818 for (i = 0; i < nsp; i++, sp++) { 819 if (sp->key == subtype) { 820 sp->fn(buf, subtype, res, size); 821 return; 822 } 823 } 824 printf("No handler for page type %x\n", subtype); 825 } 826 827 static void 828 print_hgst_info_log(void *buf, uint32_t size __unused) 829 { 830 uint8_t *walker, *end, *subpage; 831 int pages __unused; 832 uint16_t len; 833 uint8_t subtype, res; 834 835 printf("HGST Extra Info Log\n"); 836 printf("===================\n"); 837 838 walker = buf; 839 pages = *walker++; 840 walker++; 841 len = le16dec(walker); 842 walker += 2; 843 end = walker + len; /* Length is exclusive of this header */ 844 845 while (walker < end) { 846 subpage = walker + 4; 847 subtype = *walker++ & 0x3f; /* subtype */ 848 res = *walker++; /* Reserved */ 849 len = le16dec(walker); 850 walker += len + 2; /* Length, not incl header */ 851 if (walker > end) { 852 printf("Ooops! Off the end of the list\n"); 853 break; 854 } 855 kv_indirect(subpage, subtype, res, len, hgst_subpage, 856 __arraycount(hgst_subpage)); 857 } 858 } 859 860 /* 861 * Table of log page printer / sizing. 862 * 863 * This includes Intel specific pages that are widely implemented. 864 * Make sure you keep all the pages of one vendor together so -v help 865 * lists all the vendors pages. 866 */ 867 static struct logpage_function { 868 uint8_t log_page; 869 const char *vendor; 870 const char *name; 871 print_fn_t print_fn; 872 size_t size; 873 } logfuncs[] = { 874 {NVME_LOG_ERROR, NULL, "Drive Error Log", 875 print_log_error, 0}, 876 {NVME_LOG_HEALTH_INFORMATION, NULL, "Health/SMART Data", 877 print_log_health, sizeof(struct nvme_health_information_page)}, 878 {NVME_LOG_FIRMWARE_SLOT, NULL, "Firmware Information", 879 print_log_firmware, sizeof(struct nvme_firmware_page)}, 880 {HGST_INFO_LOG, "hgst", "Detailed Health/SMART", 881 print_hgst_info_log, DEFAULT_SIZE}, 882 {HGST_INFO_LOG, "wds", "Detailed Health/SMART", 883 print_hgst_info_log, DEFAULT_SIZE}, 884 {INTEL_LOG_TEMP_STATS, "intel", "Temperature Stats", 885 print_intel_temp_stats, sizeof(struct intel_log_temp_stats)}, 886 {INTEL_LOG_READ_LAT_LOG, "intel", "Read Latencies", 887 print_intel_read_lat_log, DEFAULT_SIZE}, 888 {INTEL_LOG_WRITE_LAT_LOG, "intel", "Write Latencies", 889 print_intel_write_lat_log, DEFAULT_SIZE}, 890 {INTEL_LOG_ADD_SMART, "intel", "Extra Health/SMART Data", 891 print_intel_add_smart, DEFAULT_SIZE}, 892 {INTEL_LOG_ADD_SMART, "samsung", "Extra Health/SMART Data", 893 print_intel_add_smart, DEFAULT_SIZE}, 894 895 {0, NULL, NULL, NULL, 0}, 896 }; 897 898 __dead static void 899 logpage_usage(void) 900 { 901 fprintf(stderr, "usage:\n"); 902 fprintf(stderr, LOGPAGE_USAGE); 903 exit(1); 904 } 905 906 __dead static void 907 logpage_help(void) 908 { 909 struct logpage_function *f; 910 const char *v; 911 912 fprintf(stderr, "\n"); 913 fprintf(stderr, "%-8s %-10s %s\n", "Page", "Vendor","Page Name"); 914 fprintf(stderr, "-------- ---------- ----------\n"); 915 for (f = logfuncs; f->log_page > 0; f++) { 916 v = f->vendor == NULL ? "-" : f->vendor; 917 fprintf(stderr, "0x%02x %-10s %s\n", f->log_page, v, f->name); 918 } 919 920 exit(1); 921 } 922 923 void 924 logpage(int argc, char *argv[]) 925 { 926 int fd, nsid; 927 int log_page = 0, pageflag = false; 928 int binflag = false, hexflag = false, ns_specified; 929 int ch; 930 char *p; 931 char cname[64]; 932 uint32_t size; 933 void *buf; 934 const char *vendor = NULL; 935 struct logpage_function *f; 936 struct nvm_identify_controller cdata; 937 print_fn_t print_fn; 938 939 while ((ch = getopt(argc, argv, "bp:xv:")) != -1) { 940 switch (ch) { 941 case 'b': 942 binflag = true; 943 break; 944 case 'p': 945 if (strcmp(optarg, "help") == 0) 946 logpage_help(); 947 948 /* TODO: Add human-readable ASCII page IDs */ 949 log_page = strtol(optarg, &p, 0); 950 if (p != NULL && *p != '\0') { 951 fprintf(stderr, 952 "\"%s\" not valid log page id.\n", 953 optarg); 954 logpage_usage(); 955 } 956 pageflag = true; 957 break; 958 case 'x': 959 hexflag = true; 960 break; 961 case 'v': 962 if (strcmp(optarg, "help") == 0) 963 logpage_help(); 964 vendor = optarg; 965 break; 966 } 967 } 968 969 if (!pageflag) { 970 printf("Missing page_id (-p).\n"); 971 logpage_usage(); 972 } 973 974 /* Check that a controller and/or namespace was specified. */ 975 if (optind >= argc) 976 logpage_usage(); 977 978 if (strstr(argv[optind], NVME_NS_PREFIX) != NULL) { 979 ns_specified = true; 980 parse_ns_str(argv[optind], cname, &nsid); 981 open_dev(cname, &fd, 1, 1); 982 } else { 983 ns_specified = false; 984 nsid = 0xffffffff; 985 open_dev(argv[optind], &fd, 1, 1); 986 } 987 988 read_controller_data(fd, &cdata); 989 990 /* 991 * The log page attribtues indicate whether or not the controller 992 * supports the SMART/Health information log page on a per 993 * namespace basis. 994 */ 995 if (ns_specified) { 996 if (log_page != NVME_LOG_HEALTH_INFORMATION) 997 errx(1, "log page %d valid only at controller level", 998 log_page); 999 if (!(cdata.lpa & NVME_ID_CTRLR_LPA_NS_SMART)) 1000 errx(1, 1001 "controller does not support per namespace " 1002 "smart/health information"); 1003 } 1004 1005 print_fn = print_hex; 1006 size = DEFAULT_SIZE; 1007 if (binflag) 1008 print_fn = print_bin; 1009 if (!binflag && !hexflag) { 1010 /* 1011 * See if there is a pretty print function for the specified log 1012 * page. If one isn't found, we just revert to the default 1013 * (print_hex). If there was a vendor specified bt the user, and 1014 * the page is vendor specific, don't match the print function 1015 * unless the vendors match. 1016 */ 1017 for (f = logfuncs; f->log_page > 0; f++) { 1018 if (f->vendor != NULL && vendor != NULL && 1019 strcmp(f->vendor, vendor) != 0) 1020 continue; 1021 if (log_page != f->log_page) 1022 continue; 1023 print_fn = f->print_fn; 1024 size = f->size; 1025 break; 1026 } 1027 } 1028 1029 if (log_page == NVME_LOG_ERROR) { 1030 size = sizeof(struct nvme_error_information_entry); 1031 size *= (cdata.elpe + 1); 1032 } 1033 1034 /* Read the log page */ 1035 buf = get_log_buffer(size); 1036 read_logpage(fd, log_page, nsid, buf, size); 1037 print_fn(buf, size); 1038 1039 close(fd); 1040 exit(0); 1041 } 1042