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