1 /*- 2 * Copyright (c) 2013 EMC Corp. 3 * All rights reserved. 4 * 5 * Copyright (C) 2012-2013 Intel Corporation 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 */ 29 30 #include <sys/cdefs.h> 31 __FBSDID("$FreeBSD$"); 32 33 #include <sys/param.h> 34 #include <sys/ioccom.h> 35 36 #include <ctype.h> 37 #include <err.h> 38 #include <fcntl.h> 39 #include <stdbool.h> 40 #include <stddef.h> 41 #include <stdio.h> 42 #include <stdlib.h> 43 #include <string.h> 44 #include <unistd.h> 45 #include <sys/endian.h> 46 47 #if _BYTE_ORDER != _LITTLE_ENDIAN 48 #error "Code only works on little endian machines" 49 #endif 50 51 #include "nvmecontrol.h" 52 53 #define DEFAULT_SIZE (4096) 54 #define MAX_FW_SLOTS (7) 55 56 typedef void (*print_fn_t)(void *buf, uint32_t size); 57 58 struct kv_name 59 { 60 uint32_t key; 61 const char *name; 62 }; 63 64 static const char * 65 kv_lookup(const struct kv_name *kv, size_t kv_count, uint32_t key) 66 { 67 static char bad[32]; 68 size_t i; 69 70 for (i = 0; i < kv_count; i++, kv++) 71 if (kv->key == key) 72 return kv->name; 73 snprintf(bad, sizeof(bad), "Attribute %#x", key); 74 return bad; 75 } 76 77 /* 78 * 128-bit integer augments to standard values. On i386 this 79 * doesn't exist, so we use 64-bit values. The 128-bit counters 80 * are crazy anyway, since for this purpose, you'd need a 81 * billion IOPs for billions of seconds to overflow them. 82 * So, on 32-bit i386, you'll get truncated values. 83 */ 84 #define UINT128_DIG 39 85 #ifdef __i386__ 86 typedef uint64_t uint128_t; 87 #else 88 typedef __uint128_t uint128_t; 89 #endif 90 91 static inline uint128_t 92 to128(void *p) 93 { 94 return *(uint128_t *)p; 95 } 96 97 static char * 98 uint128_to_str(uint128_t u, char *buf, size_t buflen) 99 { 100 char *end = buf + buflen - 1; 101 102 *end-- = '\0'; 103 if (u == 0) 104 *end-- = '0'; 105 while (u && end >= buf) { 106 *end-- = u % 10 + '0'; 107 u /= 10; 108 } 109 end++; 110 if (u != 0) 111 return NULL; 112 113 return end; 114 } 115 116 /* "fMissing" from endian.h */ 117 static __inline uint64_t 118 le48dec(const void *pp) 119 { 120 uint8_t const *p = (uint8_t const *)pp; 121 122 return (((uint64_t)le16dec(p + 4) << 32) | le32dec(p)); 123 } 124 125 static void * 126 get_log_buffer(uint32_t size) 127 { 128 void *buf; 129 130 if ((buf = malloc(size)) == NULL) 131 errx(1, "unable to malloc %u bytes", size); 132 133 memset(buf, 0, size); 134 return (buf); 135 } 136 137 void 138 read_logpage(int fd, uint8_t log_page, int nsid, void *payload, 139 uint32_t payload_size) 140 { 141 struct nvme_pt_command pt; 142 143 memset(&pt, 0, sizeof(pt)); 144 pt.cmd.opc = NVME_OPC_GET_LOG_PAGE; 145 pt.cmd.nsid = nsid; 146 pt.cmd.cdw10 = ((payload_size/sizeof(uint32_t)) - 1) << 16; 147 pt.cmd.cdw10 |= log_page; 148 pt.buf = payload; 149 pt.len = payload_size; 150 pt.is_read = 1; 151 152 if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0) 153 err(1, "get log page request failed"); 154 155 if (nvme_completion_is_error(&pt.cpl)) 156 errx(1, "get log page request returned error"); 157 } 158 159 static void 160 print_log_error(void *buf, uint32_t size) 161 { 162 int i, nentries; 163 struct nvme_error_information_entry *entry = buf; 164 struct nvme_status *status; 165 166 printf("Error Information Log\n"); 167 printf("=====================\n"); 168 169 if (entry->error_count == 0) { 170 printf("No error entries found\n"); 171 return; 172 } 173 174 nentries = size/sizeof(struct nvme_error_information_entry); 175 for (i = 0; i < nentries; i++, entry++) { 176 if (entry->error_count == 0) 177 break; 178 179 status = &entry->status; 180 printf("Entry %02d\n", i + 1); 181 printf("=========\n"); 182 printf(" Error count: %ju\n", entry->error_count); 183 printf(" Submission queue ID: %u\n", entry->sqid); 184 printf(" Command ID: %u\n", entry->cid); 185 /* TODO: Export nvme_status_string structures from kernel? */ 186 printf(" Status:\n"); 187 printf(" Phase tag: %d\n", status->p); 188 printf(" Status code: %d\n", status->sc); 189 printf(" Status code type: %d\n", status->sct); 190 printf(" More: %d\n", status->m); 191 printf(" DNR: %d\n", status->dnr); 192 printf(" Error location: %u\n", entry->error_location); 193 printf(" LBA: %ju\n", entry->lba); 194 printf(" Namespace ID: %u\n", entry->nsid); 195 printf(" Vendor specific info: %u\n", entry->vendor_specific); 196 } 197 } 198 199 static void 200 print_temp(uint16_t t) 201 { 202 printf("%u K, %2.2f C, %3.2f F\n", t, (float)t - 273.15, (float)t * 9 / 5 - 459.67); 203 } 204 205 206 static void 207 print_log_health(void *buf, uint32_t size __unused) 208 { 209 struct nvme_health_information_page *health = buf; 210 char cbuf[UINT128_DIG + 1]; 211 int i; 212 213 printf("SMART/Health Information Log\n"); 214 printf("============================\n"); 215 216 printf("Critical Warning State: 0x%02x\n", 217 health->critical_warning.raw); 218 printf(" Available spare: %d\n", 219 health->critical_warning.bits.available_spare); 220 printf(" Temperature: %d\n", 221 health->critical_warning.bits.temperature); 222 printf(" Device reliability: %d\n", 223 health->critical_warning.bits.device_reliability); 224 printf(" Read only: %d\n", 225 health->critical_warning.bits.read_only); 226 printf(" Volatile memory backup: %d\n", 227 health->critical_warning.bits.volatile_memory_backup); 228 printf("Temperature: "); 229 print_temp(health->temperature); 230 printf("Available spare: %u\n", 231 health->available_spare); 232 printf("Available spare threshold: %u\n", 233 health->available_spare_threshold); 234 printf("Percentage used: %u\n", 235 health->percentage_used); 236 237 printf("Data units (512,000 byte) read: %s\n", 238 uint128_to_str(to128(health->data_units_read), cbuf, sizeof(cbuf))); 239 printf("Data units written: %s\n", 240 uint128_to_str(to128(health->data_units_written), cbuf, sizeof(cbuf))); 241 printf("Host read commands: %s\n", 242 uint128_to_str(to128(health->host_read_commands), cbuf, sizeof(cbuf))); 243 printf("Host write commands: %s\n", 244 uint128_to_str(to128(health->host_write_commands), cbuf, sizeof(cbuf))); 245 printf("Controller busy time (minutes): %s\n", 246 uint128_to_str(to128(health->controller_busy_time), cbuf, sizeof(cbuf))); 247 printf("Power cycles: %s\n", 248 uint128_to_str(to128(health->power_cycles), cbuf, sizeof(cbuf))); 249 printf("Power on hours: %s\n", 250 uint128_to_str(to128(health->power_on_hours), cbuf, sizeof(cbuf))); 251 printf("Unsafe shutdowns: %s\n", 252 uint128_to_str(to128(health->unsafe_shutdowns), cbuf, sizeof(cbuf))); 253 printf("Media errors: %s\n", 254 uint128_to_str(to128(health->media_errors), cbuf, sizeof(cbuf))); 255 printf("No. error info log entries: %s\n", 256 uint128_to_str(to128(health->num_error_info_log_entries), cbuf, sizeof(cbuf))); 257 258 printf("Warning Temp Composite Time: %d\n", health->warning_temp_time); 259 printf("Error Temp Composite Time: %d\n", health->error_temp_time); 260 for (i = 0; i < 7; i++) { 261 if (health->temp_sensor[i] == 0) 262 continue; 263 printf("Temperature Sensor %d: ", i + 1); 264 print_temp(health->temp_sensor[i]); 265 } 266 } 267 268 static void 269 print_log_firmware(void *buf, uint32_t size __unused) 270 { 271 int i; 272 const char *status; 273 struct nvme_firmware_page *fw = buf; 274 275 printf("Firmware Slot Log\n"); 276 printf("=================\n"); 277 278 for (i = 0; i < MAX_FW_SLOTS; i++) { 279 printf("Slot %d: ", i + 1); 280 if (fw->afi.slot == i + 1) 281 status = " Active"; 282 else 283 status = "Inactive"; 284 285 if (fw->revision[i] == 0LLU) 286 printf("Empty\n"); 287 else 288 if (isprint(*(char *)&fw->revision[i])) 289 printf("[%s] %.8s\n", status, 290 (char *)&fw->revision[i]); 291 else 292 printf("[%s] %016jx\n", status, 293 fw->revision[i]); 294 } 295 } 296 297 /* 298 * Intel specific log pages from 299 * http://www.intel.com/content/dam/www/public/us/en/documents/product-specifications/ssd-dc-p3700-spec.pdf 300 * 301 * Though the version as of this date has a typo for the size of log page 0xca, 302 * offset 147: it is only 1 byte, not 6. 303 */ 304 static void 305 print_intel_temp_stats(void *buf, uint32_t size __unused) 306 { 307 struct intel_log_temp_stats *temp = buf; 308 309 printf("Intel Temperature Log\n"); 310 printf("=====================\n"); 311 312 printf("Current: "); 313 print_temp(temp->current); 314 printf("Overtemp Last Flags %#jx\n", (uintmax_t)temp->overtemp_flag_last); 315 printf("Overtemp Lifetime Flags %#jx\n", (uintmax_t)temp->overtemp_flag_life); 316 printf("Max Temperature "); 317 print_temp(temp->max_temp); 318 printf("Min Temperature "); 319 print_temp(temp->min_temp); 320 printf("Max Operating Temperature "); 321 print_temp(temp->max_oper_temp); 322 printf("Min Operating Temperature "); 323 print_temp(temp->min_oper_temp); 324 printf("Estimated Temperature Offset: %ju C/K\n", (uintmax_t)temp->est_offset); 325 } 326 327 static void 328 print_intel_add_smart(void *buf, uint32_t size __unused) 329 { 330 uint8_t *walker = buf; 331 uint8_t *end = walker + 150; 332 const char *name; 333 uint64_t raw; 334 uint8_t normalized; 335 336 static struct kv_name kv[] = 337 { 338 { 0xab, "Program Fail Count" }, 339 { 0xac, "Erase Fail Count" }, 340 { 0xad, "Wear Leveling Count" }, 341 { 0xb8, "End to End Error Count" }, 342 { 0xc7, "CRC Error Count" }, 343 { 0xe2, "Timed: Media Wear" }, 344 { 0xe3, "Timed: Host Read %" }, 345 { 0xe4, "Timed: Elapsed Time" }, 346 { 0xea, "Thermal Throttle Status" }, 347 { 0xf0, "Retry Buffer Overflows" }, 348 { 0xf3, "PLL Lock Loss Count" }, 349 { 0xf4, "NAND Bytes Written" }, 350 { 0xf5, "Host Bytes Written" }, 351 }; 352 353 printf("Additional SMART Data Log\n"); 354 printf("=========================\n"); 355 /* 356 * walker[0] = Key 357 * walker[1,2] = reserved 358 * walker[3] = Normalized Value 359 * walker[4] = reserved 360 * walker[5..10] = Little Endian Raw value 361 * (or other represenations) 362 * walker[11] = reserved 363 */ 364 while (walker < end) { 365 name = kv_lookup(kv, nitems(kv), *walker); 366 normalized = walker[3]; 367 raw = le48dec(walker + 5); 368 switch (*walker){ 369 case 0: 370 break; 371 case 0xad: 372 printf("%-32s: %3d min: %u max: %u ave: %u\n", name, normalized, 373 le16dec(walker + 5), le16dec(walker + 7), le16dec(walker + 9)); 374 break; 375 case 0xe2: 376 printf("%-32s: %3d %.3f%%\n", name, normalized, raw / 1024.0); 377 break; 378 case 0xea: 379 printf("%-32s: %3d %d%% %d times\n", name, normalized, walker[5], le32dec(walker+6)); 380 break; 381 default: 382 printf("%-32s: %3d %ju\n", name, normalized, (uintmax_t)raw); 383 break; 384 } 385 walker += 12; 386 } 387 } 388 389 /* 390 * HGST's 0xc1 page. This is a grab bag of additional data. Please see 391 * https://www.hgst.com/sites/default/files/resources/US_SN150_ProdManual.pdf 392 * https://www.hgst.com/sites/default/files/resources/US_SN100_ProdManual.pdf 393 * Appendix A for details 394 */ 395 396 typedef void (*subprint_fn_t)(void *buf, uint16_t subtype, uint8_t res, uint32_t size); 397 398 struct subpage_print 399 { 400 uint16_t key; 401 subprint_fn_t fn; 402 }; 403 404 static void print_hgst_info_write_errors(void *buf, uint16_t subtype, uint8_t res, uint32_t size); 405 static void print_hgst_info_read_errors(void *buf, uint16_t subtype, uint8_t res, uint32_t size); 406 static void print_hgst_info_verify_errors(void *buf, uint16_t subtype, uint8_t res, uint32_t size); 407 static void print_hgst_info_self_test(void *buf, uint16_t subtype, uint8_t res, uint32_t size); 408 static void print_hgst_info_background_scan(void *buf, uint16_t subtype, uint8_t res, uint32_t size); 409 static void print_hgst_info_erase_errors(void *buf, uint16_t subtype, uint8_t res, uint32_t size); 410 static void print_hgst_info_erase_counts(void *buf, uint16_t subtype, uint8_t res, uint32_t size); 411 static void print_hgst_info_temp_history(void *buf, uint16_t subtype, uint8_t res, uint32_t size); 412 static void print_hgst_info_ssd_perf(void *buf, uint16_t subtype, uint8_t res, uint32_t size); 413 static void print_hgst_info_firmware_load(void *buf, uint16_t subtype, uint8_t res, uint32_t size); 414 415 static struct subpage_print hgst_subpage[] = { 416 { 0x02, print_hgst_info_write_errors }, 417 { 0x03, print_hgst_info_read_errors }, 418 { 0x05, print_hgst_info_verify_errors }, 419 { 0x10, print_hgst_info_self_test }, 420 { 0x15, print_hgst_info_background_scan }, 421 { 0x30, print_hgst_info_erase_errors }, 422 { 0x31, print_hgst_info_erase_counts }, 423 { 0x32, print_hgst_info_temp_history }, 424 { 0x37, print_hgst_info_ssd_perf }, 425 { 0x38, print_hgst_info_firmware_load }, 426 }; 427 428 /* Print a subpage that is basically just key value pairs */ 429 static void 430 print_hgst_info_subpage_gen(void *buf, uint16_t subtype __unused, uint32_t size, 431 const struct kv_name *kv, size_t kv_count) 432 { 433 uint8_t *wsp, *esp; 434 uint16_t ptype; 435 uint8_t plen; 436 uint64_t param; 437 int i; 438 439 wsp = buf; 440 esp = wsp + size; 441 while (wsp < esp) { 442 ptype = le16dec(wsp); 443 wsp += 2; 444 wsp++; /* Flags, just ignore */ 445 plen = *wsp++; 446 param = 0; 447 for (i = 0; i < plen; i++) 448 param |= (uint64_t)*wsp++ << (i * 8); 449 printf(" %-30s: %jd\n", kv_lookup(kv, kv_count, ptype), (uintmax_t)param); 450 } 451 } 452 453 static void 454 print_hgst_info_write_errors(void *buf, uint16_t subtype, uint8_t res __unused, uint32_t size) 455 { 456 static struct kv_name kv[] = 457 { 458 { 0x0000, "Corrected Without Delay" }, 459 { 0x0001, "Corrected Maybe Delayed" }, 460 { 0x0002, "Re-Writes" }, 461 { 0x0003, "Errors Corrected" }, 462 { 0x0004, "Correct Algorithm Used" }, 463 { 0x0005, "Bytes Processed" }, 464 { 0x0006, "Uncorrected Errors" }, 465 { 0x8000, "Flash Write Commands" }, 466 { 0x8001, "HGST Special" }, 467 }; 468 469 printf("Write Errors Subpage:\n"); 470 print_hgst_info_subpage_gen(buf, subtype, size, kv, nitems(kv)); 471 } 472 473 static void 474 print_hgst_info_read_errors(void *buf, uint16_t subtype, uint8_t res __unused, uint32_t size) 475 { 476 static struct kv_name kv[] = 477 { 478 { 0x0000, "Corrected Without Delay" }, 479 { 0x0001, "Corrected Maybe Delayed" }, 480 { 0x0002, "Re-Reads" }, 481 { 0x0003, "Errors Corrected" }, 482 { 0x0004, "Correct Algorithm Used" }, 483 { 0x0005, "Bytes Processed" }, 484 { 0x0006, "Uncorrected Errors" }, 485 { 0x8000, "Flash Read Commands" }, 486 { 0x8001, "XOR Recovered" }, 487 { 0x8002, "Total Corrected Bits" }, 488 }; 489 490 printf("Read Errors Subpage:\n"); 491 print_hgst_info_subpage_gen(buf, subtype, size, kv, nitems(kv)); 492 } 493 494 static void 495 print_hgst_info_verify_errors(void *buf, uint16_t subtype, uint8_t res __unused, uint32_t size) 496 { 497 static struct kv_name kv[] = 498 { 499 { 0x0000, "Corrected Without Delay" }, 500 { 0x0001, "Corrected Maybe Delayed" }, 501 { 0x0002, "Re-Reads" }, 502 { 0x0003, "Errors Corrected" }, 503 { 0x0004, "Correct Algorithm Used" }, 504 { 0x0005, "Bytes Processed" }, 505 { 0x0006, "Uncorrected Errors" }, 506 { 0x8000, "Commands Processed" }, 507 }; 508 509 printf("Verify Errors Subpage:\n"); 510 print_hgst_info_subpage_gen(buf, subtype, size, kv, nitems(kv)); 511 } 512 513 static void 514 print_hgst_info_self_test(void *buf, uint16_t subtype __unused, uint8_t res __unused, uint32_t size) 515 { 516 size_t i; 517 uint8_t *walker = buf; 518 uint16_t code, hrs; 519 uint32_t lba; 520 521 printf("Self Test Subpage:\n"); 522 for (i = 0; i < size / 20; i++) { /* Each entry is 20 bytes */ 523 code = le16dec(walker); 524 walker += 2; 525 walker++; /* Ignore fixed flags */ 526 if (*walker == 0) /* Last entry is zero length */ 527 break; 528 if (*walker++ != 0x10) { 529 printf("Bad length for self test report\n"); 530 return; 531 } 532 printf(" %-30s: %d\n", "Recent Test", code); 533 printf(" %-28s: %#x\n", "Self-Test Results", *walker & 0xf); 534 printf(" %-28s: %#x\n", "Self-Test Code", (*walker >> 5) & 0x7); 535 walker++; 536 printf(" %-28s: %#x\n", "Self-Test Number", *walker++); 537 hrs = le16dec(walker); 538 walker += 2; 539 lba = le32dec(walker); 540 walker += 4; 541 printf(" %-28s: %u\n", "Total Power On Hrs", hrs); 542 printf(" %-28s: %#jx (%jd)\n", "LBA", (uintmax_t)lba, (uintmax_t)lba); 543 printf(" %-28s: %#x\n", "Sense Key", *walker++ & 0xf); 544 printf(" %-28s: %#x\n", "Additional Sense Code", *walker++); 545 printf(" %-28s: %#x\n", "Additional Sense Qualifier", *walker++); 546 printf(" %-28s: %#x\n", "Vendor Specific Detail", *walker++); 547 } 548 } 549 550 static void 551 print_hgst_info_background_scan(void *buf, uint16_t subtype __unused, uint8_t res __unused, uint32_t size) 552 { 553 uint8_t *walker = buf; 554 uint8_t status; 555 uint16_t code, nscan, progress; 556 uint32_t pom, nand; 557 558 printf("Background Media Scan Subpage:\n"); 559 /* Decode the header */ 560 code = le16dec(walker); 561 walker += 2; 562 walker++; /* Ignore fixed flags */ 563 if (*walker++ != 0x10) { 564 printf("Bad length for background scan header\n"); 565 return; 566 } 567 if (code != 0) { 568 printf("Expceted code 0, found code %#x\n", code); 569 return; 570 } 571 pom = le32dec(walker); 572 walker += 4; 573 walker++; /* Reserved */ 574 status = *walker++; 575 nscan = le16dec(walker); 576 walker += 2; 577 progress = le16dec(walker); 578 walker += 2; 579 walker += 6; /* Reserved */ 580 printf(" %-30s: %d\n", "Power On Minutes", pom); 581 printf(" %-30s: %x (%s)\n", "BMS Status", status, 582 status == 0 ? "idle" : (status == 1 ? "active" : (status == 8 ? "suspended" : "unknown"))); 583 printf(" %-30s: %d\n", "Number of BMS", nscan); 584 printf(" %-30s: %d\n", "Progress Current BMS", progress); 585 /* Report retirements */ 586 if (walker - (uint8_t *)buf != 20) { 587 printf("Coding error, offset not 20\n"); 588 return; 589 } 590 size -= 20; 591 printf(" %-30s: %d\n", "BMS retirements", size / 0x18); 592 while (size > 0) { 593 code = le16dec(walker); 594 walker += 2; 595 walker++; 596 if (*walker++ != 0x14) { 597 printf("Bad length parameter\n"); 598 return; 599 } 600 pom = le32dec(walker); 601 walker += 4; 602 /* 603 * Spec sheet says the following are hard coded, if true, just 604 * print the NAND retirement. 605 */ 606 if (walker[0] == 0x41 && 607 walker[1] == 0x0b && 608 walker[2] == 0x01 && 609 walker[3] == 0x00 && 610 walker[4] == 0x00 && 611 walker[5] == 0x00 && 612 walker[6] == 0x00 && 613 walker[7] == 0x00) { 614 walker += 8; 615 walker += 4; /* Skip reserved */ 616 nand = le32dec(walker); 617 walker += 4; 618 printf(" %-30s: %d\n", "Retirement number", code); 619 printf(" %-28s: %#x\n", "NAND (C/T)BBBPPP", nand); 620 } else { 621 printf("Parameter %#x entry corrupt\n", code); 622 walker += 16; 623 } 624 } 625 } 626 627 static void 628 print_hgst_info_erase_errors(void *buf, uint16_t subtype __unused, uint8_t res __unused, uint32_t size) 629 { 630 static struct kv_name kv[] = 631 { 632 { 0x0000, "Corrected Without Delay" }, 633 { 0x0001, "Corrected Maybe Delayed" }, 634 { 0x0002, "Re-Erase" }, 635 { 0x0003, "Errors Corrected" }, 636 { 0x0004, "Correct Algorithm Used" }, 637 { 0x0005, "Bytes Processed" }, 638 { 0x0006, "Uncorrected Errors" }, 639 { 0x8000, "Flash Erase Commands" }, 640 { 0x8001, "Mfg Defect Count" }, 641 { 0x8002, "Grown Defect Count" }, 642 { 0x8003, "Erase Count -- User" }, 643 { 0x8004, "Erase Count -- System" }, 644 }; 645 646 printf("Erase Errors Subpage:\n"); 647 print_hgst_info_subpage_gen(buf, subtype, size, kv, nitems(kv)); 648 } 649 650 static void 651 print_hgst_info_erase_counts(void *buf, uint16_t subtype, uint8_t res __unused, uint32_t size) 652 { 653 /* My drive doesn't export this -- so not coding up */ 654 printf("XXX: Erase counts subpage: %p, %#x %d\n", buf, subtype, size); 655 } 656 657 static void 658 print_hgst_info_temp_history(void *buf, uint16_t subtype __unused, uint8_t res __unused, uint32_t size __unused) 659 { 660 uint8_t *walker = buf; 661 uint32_t min; 662 663 printf("Temperature History:\n"); 664 printf(" %-30s: %d C\n", "Current Temperature", *walker++); 665 printf(" %-30s: %d C\n", "Reference Temperature", *walker++); 666 printf(" %-30s: %d C\n", "Maximum Temperature", *walker++); 667 printf(" %-30s: %d C\n", "Minimum Temperature", *walker++); 668 min = le32dec(walker); 669 walker += 4; 670 printf(" %-30s: %d:%02d:00\n", "Max Temperture Time", min / 60, min % 60); 671 min = le32dec(walker); 672 walker += 4; 673 printf(" %-30s: %d:%02d:00\n", "Over Temperture Duration", min / 60, min % 60); 674 min = le32dec(walker); 675 walker += 4; 676 printf(" %-30s: %d:%02d:00\n", "Min Temperture Time", min / 60, min % 60); 677 } 678 679 static void 680 print_hgst_info_ssd_perf(void *buf, uint16_t subtype __unused, uint8_t res, uint32_t size __unused) 681 { 682 uint8_t *walker = buf; 683 uint64_t val; 684 685 printf("SSD Performance Subpage Type %d:\n", res); 686 val = le64dec(walker); 687 walker += 8; 688 printf(" %-30s: %ju\n", "Host Read Commands", val); 689 val = le64dec(walker); 690 walker += 8; 691 printf(" %-30s: %ju\n", "Host Read Blocks", val); 692 val = le64dec(walker); 693 walker += 8; 694 printf(" %-30s: %ju\n", "Host Cache Read Hits Commands", val); 695 val = le64dec(walker); 696 walker += 8; 697 printf(" %-30s: %ju\n", "Host Cache Read Hits Blocks", val); 698 val = le64dec(walker); 699 walker += 8; 700 printf(" %-30s: %ju\n", "Host Read Commands Stalled", val); 701 val = le64dec(walker); 702 walker += 8; 703 printf(" %-30s: %ju\n", "Host Write Commands", val); 704 val = le64dec(walker); 705 walker += 8; 706 printf(" %-30s: %ju\n", "Host Write Blocks", val); 707 val = le64dec(walker); 708 walker += 8; 709 printf(" %-30s: %ju\n", "Host Write Odd Start Commands", val); 710 val = le64dec(walker); 711 walker += 8; 712 printf(" %-30s: %ju\n", "Host Write Odd End Commands", val); 713 val = le64dec(walker); 714 walker += 8; 715 printf(" %-30s: %ju\n", "Host Write Commands Stalled", val); 716 val = le64dec(walker); 717 walker += 8; 718 printf(" %-30s: %ju\n", "NAND Read Commands", val); 719 val = le64dec(walker); 720 walker += 8; 721 printf(" %-30s: %ju\n", "NAND Read Blocks", val); 722 val = le64dec(walker); 723 walker += 8; 724 printf(" %-30s: %ju\n", "NAND Write Commands", val); 725 val = le64dec(walker); 726 walker += 8; 727 printf(" %-30s: %ju\n", "NAND Write Blocks", val); 728 val = le64dec(walker); 729 walker += 8; 730 printf(" %-30s: %ju\n", "NAND Read Before Writes", val); 731 } 732 733 static void 734 print_hgst_info_firmware_load(void *buf, uint16_t subtype __unused, uint8_t res __unused, uint32_t size __unused) 735 { 736 uint8_t *walker = buf; 737 738 printf("Firmware Load Subpage:\n"); 739 printf(" %-30s: %d\n", "Firmware Downloads", le32dec(walker)); 740 } 741 742 static void 743 kv_indirect(void *buf, uint32_t subtype, uint8_t res, uint32_t size, struct subpage_print *sp, size_t nsp) 744 { 745 size_t i; 746 747 for (i = 0; i < nsp; i++, sp++) { 748 if (sp->key == subtype) { 749 sp->fn(buf, subtype, res, size); 750 return; 751 } 752 } 753 printf("No handler for page type %x\n", subtype); 754 } 755 756 static void 757 print_hgst_info_log(void *buf, uint32_t size __unused) 758 { 759 uint8_t *walker, *end, *subpage; 760 int pages; 761 uint16_t len; 762 uint8_t subtype, res; 763 764 printf("HGST Extra Info Log\n"); 765 printf("===================\n"); 766 767 walker = buf; 768 pages = *walker++; 769 walker++; 770 len = le16dec(walker); 771 walker += 2; 772 end = walker + len; /* Length is exclusive of this header */ 773 774 while (walker < end) { 775 subpage = walker + 4; 776 subtype = *walker++ & 0x3f; /* subtype */ 777 res = *walker++; /* Reserved */ 778 len = le16dec(walker); 779 walker += len + 2; /* Length, not incl header */ 780 if (walker > end) { 781 printf("Ooops! Off the end of the list\n"); 782 break; 783 } 784 kv_indirect(subpage, subtype, res, len, hgst_subpage, nitems(hgst_subpage)); 785 } 786 } 787 788 /* 789 * Table of log page printer / sizing. 790 * 791 * This includes Intel specific pages that are widely implemented. Not 792 * sure how best to switch between different vendors. 793 */ 794 static struct logpage_function { 795 uint8_t log_page; 796 print_fn_t print_fn; 797 size_t size; 798 } logfuncs[] = { 799 {NVME_LOG_ERROR, print_log_error, 800 0}, 801 {NVME_LOG_HEALTH_INFORMATION, print_log_health, 802 sizeof(struct nvme_health_information_page)}, 803 {NVME_LOG_FIRMWARE_SLOT, print_log_firmware, 804 sizeof(struct nvme_firmware_page)}, 805 {INTEL_LOG_TEMP_STATS, print_intel_temp_stats, 806 sizeof(struct intel_log_temp_stats)}, 807 {INTEL_LOG_ADD_SMART, print_intel_add_smart, 808 DEFAULT_SIZE}, 809 {HGST_INFO_LOG, print_hgst_info_log, 810 DEFAULT_SIZE}, 811 {0, NULL, 812 0}, 813 }; 814 815 static void 816 logpage_usage(void) 817 { 818 fprintf(stderr, "usage:\n"); 819 fprintf(stderr, LOGPAGE_USAGE); 820 exit(1); 821 } 822 823 void 824 logpage(int argc, char *argv[]) 825 { 826 int fd, nsid; 827 int log_page = 0, pageflag = false; 828 int hexflag = false, ns_specified; 829 char ch, *p; 830 char cname[64]; 831 uint32_t size; 832 void *buf; 833 struct logpage_function *f; 834 struct nvme_controller_data cdata; 835 print_fn_t print_fn; 836 837 while ((ch = getopt(argc, argv, "p:x")) != -1) { 838 switch (ch) { 839 case 'p': 840 /* TODO: Add human-readable ASCII page IDs */ 841 log_page = strtol(optarg, &p, 0); 842 if (p != NULL && *p != '\0') { 843 fprintf(stderr, 844 "\"%s\" not valid log page id.\n", 845 optarg); 846 logpage_usage(); 847 } 848 pageflag = true; 849 break; 850 case 'x': 851 hexflag = true; 852 break; 853 } 854 } 855 856 if (!pageflag) { 857 printf("Missing page_id (-p).\n"); 858 logpage_usage(); 859 } 860 861 /* Check that a controller and/or namespace was specified. */ 862 if (optind >= argc) 863 logpage_usage(); 864 865 if (strstr(argv[optind], NVME_NS_PREFIX) != NULL) { 866 ns_specified = true; 867 parse_ns_str(argv[optind], cname, &nsid); 868 open_dev(cname, &fd, 1, 1); 869 } else { 870 ns_specified = false; 871 nsid = NVME_GLOBAL_NAMESPACE_TAG; 872 open_dev(argv[optind], &fd, 1, 1); 873 } 874 875 read_controller_data(fd, &cdata); 876 877 /* 878 * The log page attribtues indicate whether or not the controller 879 * supports the SMART/Health information log page on a per 880 * namespace basis. 881 */ 882 if (ns_specified) { 883 if (log_page != NVME_LOG_HEALTH_INFORMATION) 884 errx(1, "log page %d valid only at controller level", 885 log_page); 886 if (cdata.lpa.ns_smart == 0) 887 errx(1, 888 "controller does not support per namespace " 889 "smart/health information"); 890 } 891 892 print_fn = print_hex; 893 size = DEFAULT_SIZE; 894 if (!hexflag) { 895 /* 896 * See if there is a pretty print function for the 897 * specified log page. If one isn't found, we 898 * just revert to the default (print_hex). 899 */ 900 f = logfuncs; 901 while (f->log_page > 0) { 902 if (log_page == f->log_page) { 903 print_fn = f->print_fn; 904 size = f->size; 905 break; 906 } 907 f++; 908 } 909 } 910 911 if (log_page == NVME_LOG_ERROR) { 912 size = sizeof(struct nvme_error_information_entry); 913 size *= (cdata.elpe + 1); 914 } 915 916 /* Read the log page */ 917 buf = get_log_buffer(size); 918 read_logpage(fd, log_page, nsid, buf, size); 919 print_fn(buf, size); 920 921 close(fd); 922 exit(0); 923 } 924