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