logpage.c revision 328674
1252277Sjimharris/*- 2252277Sjimharris * Copyright (c) 2013 EMC Corp. 3252277Sjimharris * All rights reserved. 4252277Sjimharris * 5252277Sjimharris * Copyright (C) 2012-2013 Intel Corporation 6252277Sjimharris * All rights reserved. 7252277Sjimharris * 8252277Sjimharris * Redistribution and use in source and binary forms, with or without 9252277Sjimharris * modification, are permitted provided that the following conditions 10252277Sjimharris * are met: 11252277Sjimharris * 1. Redistributions of source code must retain the above copyright 12252277Sjimharris * notice, this list of conditions and the following disclaimer. 13252277Sjimharris * 2. Redistributions in binary form must reproduce the above copyright 14252277Sjimharris * notice, this list of conditions and the following disclaimer in the 15252277Sjimharris * documentation and/or other materials provided with the distribution. 16252277Sjimharris * 17252277Sjimharris * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18252277Sjimharris * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19252277Sjimharris * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20252277Sjimharris * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21252277Sjimharris * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22252277Sjimharris * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23252277Sjimharris * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24252277Sjimharris * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25252277Sjimharris * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26252277Sjimharris * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27252277Sjimharris * SUCH DAMAGE. 28252277Sjimharris */ 29252277Sjimharris 30252277Sjimharris#include <sys/cdefs.h> 31252277Sjimharris__FBSDID("$FreeBSD: stable/11/sbin/nvmecontrol/logpage.c 328674 2018-02-01 16:22:28Z mav $"); 32252277Sjimharris 33252277Sjimharris#include <sys/param.h> 34252277Sjimharris#include <sys/ioccom.h> 35252277Sjimharris 36252277Sjimharris#include <ctype.h> 37253109Sjimharris#include <err.h> 38252277Sjimharris#include <fcntl.h> 39252277Sjimharris#include <stdbool.h> 40252277Sjimharris#include <stddef.h> 41252277Sjimharris#include <stdio.h> 42252277Sjimharris#include <stdlib.h> 43252277Sjimharris#include <string.h> 44252277Sjimharris#include <unistd.h> 45328668Smav#include <sys/endian.h> 46252277Sjimharris 47328668Smav#if _BYTE_ORDER != _LITTLE_ENDIAN 48328668Smav#error "Code only works on little endian machines" 49328668Smav#endif 50328668Smav 51252277Sjimharris#include "nvmecontrol.h" 52252277Sjimharris 53252277Sjimharris#define DEFAULT_SIZE (4096) 54252277Sjimharris#define MAX_FW_SLOTS (7) 55252277Sjimharris 56252277Sjimharristypedef void (*print_fn_t)(void *buf, uint32_t size); 57252277Sjimharris 58328668Smav 59328674Smavstruct kv_name 60328674Smav{ 61328674Smav uint32_t key; 62328674Smav const char *name; 63328674Smav}; 64328674Smav 65328674Smavstatic const char * 66328674Smavkv_lookup(const struct kv_name *kv, size_t kv_count, uint32_t key) 67328674Smav{ 68328674Smav static char bad[32]; 69328674Smav size_t i; 70328674Smav 71328674Smav for (i = 0; i < kv_count; i++, kv++) 72328674Smav if (kv->key == key) 73328674Smav return kv->name; 74328674Smav snprintf(bad, sizeof(bad), "Attribute %#x", key); 75328674Smav return bad; 76328674Smav} 77328674Smav 78328668Smav/* 79328668Smav * 128-bit integer augments to standard values 80328668Smav */ 81328668Smav#define UINT128_DIG 39 82328668Smavtypedef __uint128_t uint128_t; 83328668Smav 84328668Smavstatic inline uint128_t 85328668Smavto128(void *p) 86328668Smav{ 87328668Smav return *(uint128_t *)p; 88328668Smav} 89328668Smav 90328668Smavstatic char * 91328668Smavuint128_to_str(uint128_t u, char *buf, size_t buflen) 92328668Smav{ 93328668Smav char *end = buf + buflen - 1; 94328668Smav 95328668Smav *end-- = '\0'; 96328668Smav if (u == 0) 97328668Smav *end-- = '0'; 98328668Smav while (u && end >= buf) { 99328668Smav *end-- = u % 10 + '0'; 100328668Smav u /= 10; 101328668Smav } 102328668Smav end++; 103328668Smav if (u != 0) 104328668Smav return NULL; 105328668Smav 106328668Smav return end; 107328668Smav} 108328668Smav 109252277Sjimharrisstatic void * 110253109Sjimharrisget_log_buffer(uint32_t size) 111252277Sjimharris{ 112252277Sjimharris void *buf; 113252277Sjimharris 114253109Sjimharris if ((buf = malloc(size)) == NULL) 115253109Sjimharris errx(1, "unable to malloc %u bytes", size); 116253109Sjimharris 117252277Sjimharris memset(buf, 0, size); 118252277Sjimharris return (buf); 119252277Sjimharris} 120252277Sjimharris 121252277Sjimharrisvoid 122328673Smavread_logpage(int fd, uint8_t log_page, int nsid, void *payload, 123252277Sjimharris uint32_t payload_size) 124252277Sjimharris{ 125252277Sjimharris struct nvme_pt_command pt; 126252277Sjimharris 127252277Sjimharris memset(&pt, 0, sizeof(pt)); 128252277Sjimharris pt.cmd.opc = NVME_OPC_GET_LOG_PAGE; 129252277Sjimharris pt.cmd.nsid = nsid; 130252277Sjimharris pt.cmd.cdw10 = ((payload_size/sizeof(uint32_t)) - 1) << 16; 131252277Sjimharris pt.cmd.cdw10 |= log_page; 132252277Sjimharris pt.buf = payload; 133252277Sjimharris pt.len = payload_size; 134252277Sjimharris pt.is_read = 1; 135252277Sjimharris 136253109Sjimharris if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0) 137253109Sjimharris err(1, "get log page request failed"); 138252277Sjimharris 139253109Sjimharris if (nvme_completion_is_error(&pt.cpl)) 140253109Sjimharris errx(1, "get log page request returned error"); 141252277Sjimharris} 142252277Sjimharris 143252277Sjimharrisstatic void 144252277Sjimharrisprint_log_error(void *buf, uint32_t size) 145252277Sjimharris{ 146252277Sjimharris int i, nentries; 147252277Sjimharris struct nvme_error_information_entry *entry = buf; 148252277Sjimharris struct nvme_status *status; 149252277Sjimharris 150252277Sjimharris printf("Error Information Log\n"); 151252277Sjimharris printf("=====================\n"); 152252277Sjimharris 153252277Sjimharris if (entry->error_count == 0) { 154252277Sjimharris printf("No error entries found\n"); 155252277Sjimharris return; 156252277Sjimharris } 157252277Sjimharris 158252277Sjimharris nentries = size/sizeof(struct nvme_error_information_entry); 159252277Sjimharris for (i = 0; i < nentries; i++, entry++) { 160252277Sjimharris if (entry->error_count == 0) 161252277Sjimharris break; 162252277Sjimharris 163252277Sjimharris status = &entry->status; 164252277Sjimharris printf("Entry %02d\n", i + 1); 165252277Sjimharris printf("=========\n"); 166252277Sjimharris printf(" Error count: %ju\n", entry->error_count); 167252277Sjimharris printf(" Submission queue ID: %u\n", entry->sqid); 168252277Sjimharris printf(" Command ID: %u\n", entry->cid); 169252277Sjimharris /* TODO: Export nvme_status_string structures from kernel? */ 170252277Sjimharris printf(" Status:\n"); 171252277Sjimharris printf(" Phase tag: %d\n", status->p); 172252277Sjimharris printf(" Status code: %d\n", status->sc); 173252277Sjimharris printf(" Status code type: %d\n", status->sct); 174252277Sjimharris printf(" More: %d\n", status->m); 175252277Sjimharris printf(" DNR: %d\n", status->dnr); 176252277Sjimharris printf(" Error location: %u\n", entry->error_location); 177252277Sjimharris printf(" LBA: %ju\n", entry->lba); 178252277Sjimharris printf(" Namespace ID: %u\n", entry->nsid); 179252277Sjimharris printf(" Vendor specific info: %u\n", entry->vendor_specific); 180252277Sjimharris } 181252277Sjimharris} 182252277Sjimharris 183252277Sjimharrisstatic void 184328669Smavprint_temp(uint16_t t) 185328669Smav{ 186328669Smav printf("%u K, %2.2f C, %3.2f F\n", t, (float)t - 273.15, (float)t * 9 / 5 - 459.67); 187328669Smav} 188328669Smav 189328669Smav 190328669Smavstatic void 191252277Sjimharrisprint_log_health(void *buf, uint32_t size __unused) 192252277Sjimharris{ 193252277Sjimharris struct nvme_health_information_page *health = buf; 194328668Smav char cbuf[UINT128_DIG + 1]; 195328669Smav int i; 196252277Sjimharris 197252277Sjimharris printf("SMART/Health Information Log\n"); 198252277Sjimharris printf("============================\n"); 199252277Sjimharris 200252277Sjimharris printf("Critical Warning State: 0x%02x\n", 201252277Sjimharris health->critical_warning.raw); 202252277Sjimharris printf(" Available spare: %d\n", 203252277Sjimharris health->critical_warning.bits.available_spare); 204252277Sjimharris printf(" Temperature: %d\n", 205252277Sjimharris health->critical_warning.bits.temperature); 206252277Sjimharris printf(" Device reliability: %d\n", 207252277Sjimharris health->critical_warning.bits.device_reliability); 208252277Sjimharris printf(" Read only: %d\n", 209252277Sjimharris health->critical_warning.bits.read_only); 210252277Sjimharris printf(" Volatile memory backup: %d\n", 211252277Sjimharris health->critical_warning.bits.volatile_memory_backup); 212328669Smav printf("Temperature: "); 213328669Smav print_temp(health->temperature); 214252277Sjimharris printf("Available spare: %u\n", 215252277Sjimharris health->available_spare); 216252277Sjimharris printf("Available spare threshold: %u\n", 217252277Sjimharris health->available_spare_threshold); 218252277Sjimharris printf("Percentage used: %u\n", 219252277Sjimharris health->percentage_used); 220252277Sjimharris 221328669Smav printf("Data units (512,000 byte) read: %s\n", 222328668Smav uint128_to_str(to128(health->data_units_read), cbuf, sizeof(cbuf))); 223328669Smav printf("Data units written: %s\n", 224328668Smav uint128_to_str(to128(health->data_units_written), cbuf, sizeof(cbuf))); 225328668Smav printf("Host read commands: %s\n", 226328668Smav uint128_to_str(to128(health->host_read_commands), cbuf, sizeof(cbuf))); 227328668Smav printf("Host write commands: %s\n", 228328668Smav uint128_to_str(to128(health->host_write_commands), cbuf, sizeof(cbuf))); 229328668Smav printf("Controller busy time (minutes): %s\n", 230328668Smav uint128_to_str(to128(health->controller_busy_time), cbuf, sizeof(cbuf))); 231328668Smav printf("Power cycles: %s\n", 232328668Smav uint128_to_str(to128(health->power_cycles), cbuf, sizeof(cbuf))); 233328668Smav printf("Power on hours: %s\n", 234328668Smav uint128_to_str(to128(health->power_on_hours), cbuf, sizeof(cbuf))); 235328668Smav printf("Unsafe shutdowns: %s\n", 236328668Smav uint128_to_str(to128(health->unsafe_shutdowns), cbuf, sizeof(cbuf))); 237328668Smav printf("Media errors: %s\n", 238328668Smav uint128_to_str(to128(health->media_errors), cbuf, sizeof(cbuf))); 239328668Smav printf("No. error info log entries: %s\n", 240328668Smav uint128_to_str(to128(health->num_error_info_log_entries), cbuf, sizeof(cbuf))); 241328669Smav 242328669Smav printf("Warning Temp Composite Time: %d\n", health->warning_temp_time); 243328669Smav printf("Error Temp Composite Time: %d\n", health->error_temp_time); 244328669Smav for (i = 0; i < 7; i++) { 245328669Smav if (health->temp_sensor[i] == 0) 246328669Smav continue; 247328669Smav printf("Temperature Sensor %d: ", i + 1); 248328669Smav print_temp(health->temp_sensor[i]); 249328669Smav } 250252277Sjimharris} 251252277Sjimharris 252252277Sjimharrisstatic void 253252277Sjimharrisprint_log_firmware(void *buf, uint32_t size __unused) 254252277Sjimharris{ 255252277Sjimharris int i; 256252277Sjimharris const char *status; 257252277Sjimharris struct nvme_firmware_page *fw = buf; 258252277Sjimharris 259252277Sjimharris printf("Firmware Slot Log\n"); 260252277Sjimharris printf("=================\n"); 261252277Sjimharris 262252277Sjimharris for (i = 0; i < MAX_FW_SLOTS; i++) { 263252277Sjimharris printf("Slot %d: ", i + 1); 264252277Sjimharris if (fw->afi.slot == i + 1) 265252277Sjimharris status = " Active"; 266252277Sjimharris else 267252277Sjimharris status = "Inactive"; 268252277Sjimharris 269252277Sjimharris if (fw->revision[i] == 0LLU) 270252277Sjimharris printf("Empty\n"); 271252277Sjimharris else 272252277Sjimharris if (isprint(*(char *)&fw->revision[i])) 273252277Sjimharris printf("[%s] %.8s\n", status, 274252277Sjimharris (char *)&fw->revision[i]); 275252277Sjimharris else 276252277Sjimharris printf("[%s] %016jx\n", status, 277252277Sjimharris fw->revision[i]); 278252277Sjimharris } 279252277Sjimharris} 280252277Sjimharris 281328673Smavstatic void 282328673Smavprint_intel_temp_stats(void *buf, uint32_t size __unused) 283328673Smav{ 284328673Smav struct intel_log_temp_stats *temp = buf; 285328673Smav 286328673Smav printf("Intel Temperature Log\n"); 287328673Smav printf("=====================\n"); 288328673Smav 289328673Smav printf("Current: "); 290328673Smav print_temp(temp->current); 291328673Smav printf("Overtemp Last Flags %#jx\n", (uintmax_t)temp->overtemp_flag_last); 292328673Smav printf("Overtemp Lifetime Flags %#jx\n", (uintmax_t)temp->overtemp_flag_life); 293328673Smav printf("Max Temperature "); 294328673Smav print_temp(temp->max_temp); 295328673Smav printf("Min Temperature "); 296328673Smav print_temp(temp->min_temp); 297328673Smav printf("Max Operating Temperature "); 298328673Smav print_temp(temp->max_oper_temp); 299328673Smav printf("Min Operating Temperature "); 300328673Smav print_temp(temp->min_oper_temp); 301328673Smav printf("Estimated Temperature Offset: %ju C/K\n", (uintmax_t)temp->est_offset); 302328673Smav} 303328673Smav 304328673Smav/* 305328674Smav * HGST's 0xc1 page. This is a grab bag of additional data. Please see 306328674Smav * https://www.hgst.com/sites/default/files/resources/US_SN150_ProdManual.pdf 307328674Smav * https://www.hgst.com/sites/default/files/resources/US_SN100_ProdManual.pdf 308328674Smav * Appendix A for details 309328674Smav */ 310328674Smav 311328674Smavtypedef void (*subprint_fn_t)(void *buf, uint16_t subtype, uint8_t res, uint32_t size); 312328674Smav 313328674Smavstruct subpage_print 314328674Smav{ 315328674Smav uint16_t key; 316328674Smav subprint_fn_t fn; 317328674Smav}; 318328674Smav 319328674Smavstatic void print_hgst_info_write_errors(void *buf, uint16_t subtype, uint8_t res, uint32_t size); 320328674Smavstatic void print_hgst_info_read_errors(void *buf, uint16_t subtype, uint8_t res, uint32_t size); 321328674Smavstatic void print_hgst_info_verify_errors(void *buf, uint16_t subtype, uint8_t res, uint32_t size); 322328674Smavstatic void print_hgst_info_self_test(void *buf, uint16_t subtype, uint8_t res, uint32_t size); 323328674Smavstatic void print_hgst_info_background_scan(void *buf, uint16_t subtype, uint8_t res, uint32_t size); 324328674Smavstatic void print_hgst_info_erase_errors(void *buf, uint16_t subtype, uint8_t res, uint32_t size); 325328674Smavstatic void print_hgst_info_erase_counts(void *buf, uint16_t subtype, uint8_t res, uint32_t size); 326328674Smavstatic void print_hgst_info_temp_history(void *buf, uint16_t subtype, uint8_t res, uint32_t size); 327328674Smavstatic void print_hgst_info_ssd_perf(void *buf, uint16_t subtype, uint8_t res, uint32_t size); 328328674Smavstatic void print_hgst_info_firmware_load(void *buf, uint16_t subtype, uint8_t res, uint32_t size); 329328674Smav 330328674Smavstatic struct subpage_print hgst_subpage[] = { 331328674Smav { 0x02, print_hgst_info_write_errors }, 332328674Smav { 0x03, print_hgst_info_read_errors }, 333328674Smav { 0x05, print_hgst_info_verify_errors }, 334328674Smav { 0x10, print_hgst_info_self_test }, 335328674Smav { 0x15, print_hgst_info_background_scan }, 336328674Smav { 0x30, print_hgst_info_erase_errors }, 337328674Smav { 0x31, print_hgst_info_erase_counts }, 338328674Smav { 0x32, print_hgst_info_temp_history }, 339328674Smav { 0x37, print_hgst_info_ssd_perf }, 340328674Smav { 0x38, print_hgst_info_firmware_load }, 341328674Smav}; 342328674Smav 343328674Smav/* Print a subpage that is basically just key value pairs */ 344328674Smavstatic void 345328674Smavprint_hgst_info_subpage_gen(void *buf, uint16_t subtype __unused, uint32_t size, 346328674Smav const struct kv_name *kv, size_t kv_count) 347328674Smav{ 348328674Smav uint8_t *wsp, *esp; 349328674Smav uint16_t ptype; 350328674Smav uint8_t plen; 351328674Smav uint64_t param; 352328674Smav int i; 353328674Smav 354328674Smav wsp = buf; 355328674Smav esp = wsp + size; 356328674Smav while (wsp < esp) { 357328674Smav ptype = le16dec(wsp); 358328674Smav wsp += 2; 359328674Smav wsp++; /* Flags, just ignore */ 360328674Smav plen = *wsp++; 361328674Smav param = 0; 362328674Smav for (i = 0; i < plen; i++) 363328674Smav param |= (uint64_t)*wsp++ << (i * 8); 364328674Smav printf(" %-30s: %jd\n", kv_lookup(kv, kv_count, ptype), (uintmax_t)param); 365328674Smav } 366328674Smav} 367328674Smav 368328674Smavstatic void 369328674Smavprint_hgst_info_write_errors(void *buf, uint16_t subtype, uint8_t res __unused, uint32_t size) 370328674Smav{ 371328674Smav static struct kv_name kv[] = 372328674Smav { 373328674Smav { 0x0000, "Corrected Without Delay" }, 374328674Smav { 0x0001, "Corrected Maybe Delayed" }, 375328674Smav { 0x0002, "Re-Writes" }, 376328674Smav { 0x0003, "Errors Corrected" }, 377328674Smav { 0x0004, "Correct Algorithm Used" }, 378328674Smav { 0x0005, "Bytes Processed" }, 379328674Smav { 0x0006, "Uncorrected Errors" }, 380328674Smav { 0x8000, "Flash Write Commands" }, 381328674Smav { 0x8001, "HGST Special" }, 382328674Smav }; 383328674Smav 384328674Smav printf("Write Errors Subpage:\n"); 385328674Smav print_hgst_info_subpage_gen(buf, subtype, size, kv, nitems(kv)); 386328674Smav} 387328674Smav 388328674Smavstatic void 389328674Smavprint_hgst_info_read_errors(void *buf, uint16_t subtype, uint8_t res __unused, uint32_t size) 390328674Smav{ 391328674Smav static struct kv_name kv[] = 392328674Smav { 393328674Smav { 0x0000, "Corrected Without Delay" }, 394328674Smav { 0x0001, "Corrected Maybe Delayed" }, 395328674Smav { 0x0002, "Re-Reads" }, 396328674Smav { 0x0003, "Errors Corrected" }, 397328674Smav { 0x0004, "Correct Algorithm Used" }, 398328674Smav { 0x0005, "Bytes Processed" }, 399328674Smav { 0x0006, "Uncorrected Errors" }, 400328674Smav { 0x8000, "Flash Read Commands" }, 401328674Smav { 0x8001, "XOR Recovered" }, 402328674Smav { 0x8002, "Total Corrected Bits" }, 403328674Smav }; 404328674Smav 405328674Smav printf("Read Errors Subpage:\n"); 406328674Smav print_hgst_info_subpage_gen(buf, subtype, size, kv, nitems(kv)); 407328674Smav} 408328674Smav 409328674Smavstatic void 410328674Smavprint_hgst_info_verify_errors(void *buf, uint16_t subtype, uint8_t res __unused, uint32_t size) 411328674Smav{ 412328674Smav static struct kv_name kv[] = 413328674Smav { 414328674Smav { 0x0000, "Corrected Without Delay" }, 415328674Smav { 0x0001, "Corrected Maybe Delayed" }, 416328674Smav { 0x0002, "Re-Reads" }, 417328674Smav { 0x0003, "Errors Corrected" }, 418328674Smav { 0x0004, "Correct Algorithm Used" }, 419328674Smav { 0x0005, "Bytes Processed" }, 420328674Smav { 0x0006, "Uncorrected Errors" }, 421328674Smav { 0x8000, "Commands Processed" }, 422328674Smav }; 423328674Smav 424328674Smav printf("Verify Errors Subpage:\n"); 425328674Smav print_hgst_info_subpage_gen(buf, subtype, size, kv, nitems(kv)); 426328674Smav} 427328674Smav 428328674Smavstatic void 429328674Smavprint_hgst_info_self_test(void *buf, uint16_t subtype __unused, uint8_t res __unused, uint32_t size) 430328674Smav{ 431328674Smav size_t i; 432328674Smav uint8_t *walker = buf; 433328674Smav uint16_t code, hrs; 434328674Smav uint32_t lba; 435328674Smav 436328674Smav printf("Self Test Subpage:\n"); 437328674Smav for (i = 0; i < size / 20; i++) { /* Each entry is 20 bytes */ 438328674Smav code = le16dec(walker); 439328674Smav walker += 2; 440328674Smav walker++; /* Ignore fixed flags */ 441328674Smav if (*walker == 0) /* Last entry is zero length */ 442328674Smav break; 443328674Smav if (*walker++ != 0x10) { 444328674Smav printf("Bad length for self test report\n"); 445328674Smav return; 446328674Smav } 447328674Smav printf(" %-30s: %d\n", "Recent Test", code); 448328674Smav printf(" %-28s: %#x\n", "Self-Test Results", *walker & 0xf); 449328674Smav printf(" %-28s: %#x\n", "Self-Test Code", (*walker >> 5) & 0x7); 450328674Smav walker++; 451328674Smav printf(" %-28s: %#x\n", "Self-Test Number", *walker++); 452328674Smav hrs = le16dec(walker); 453328674Smav walker += 2; 454328674Smav lba = le32dec(walker); 455328674Smav walker += 4; 456328674Smav printf(" %-28s: %u\n", "Total Power On Hrs", hrs); 457328674Smav printf(" %-28s: %#jx (%jd)\n", "LBA", (uintmax_t)lba, (uintmax_t)lba); 458328674Smav printf(" %-28s: %#x\n", "Sense Key", *walker++ & 0xf); 459328674Smav printf(" %-28s: %#x\n", "Additional Sense Code", *walker++); 460328674Smav printf(" %-28s: %#x\n", "Additional Sense Qualifier", *walker++); 461328674Smav printf(" %-28s: %#x\n", "Vendor Specific Detail", *walker++); 462328674Smav } 463328674Smav} 464328674Smav 465328674Smavstatic void 466328674Smavprint_hgst_info_background_scan(void *buf, uint16_t subtype __unused, uint8_t res __unused, uint32_t size) 467328674Smav{ 468328674Smav uint8_t *walker = buf; 469328674Smav uint8_t status; 470328674Smav uint16_t code, nscan, progress; 471328674Smav uint32_t pom, nand; 472328674Smav 473328674Smav printf("Background Media Scan Subpage:\n"); 474328674Smav /* Decode the header */ 475328674Smav code = le16dec(walker); 476328674Smav walker += 2; 477328674Smav walker++; /* Ignore fixed flags */ 478328674Smav if (*walker++ != 0x10) { 479328674Smav printf("Bad length for background scan header\n"); 480328674Smav return; 481328674Smav } 482328674Smav if (code != 0) { 483328674Smav printf("Expceted code 0, found code %#x\n", code); 484328674Smav return; 485328674Smav } 486328674Smav pom = le32dec(walker); 487328674Smav walker += 4; 488328674Smav walker++; /* Reserved */ 489328674Smav status = *walker++; 490328674Smav nscan = le16dec(walker); 491328674Smav walker += 2; 492328674Smav progress = le16dec(walker); 493328674Smav walker += 2; 494328674Smav walker += 6; /* Reserved */ 495328674Smav printf(" %-30s: %d\n", "Power On Minutes", pom); 496328674Smav printf(" %-30s: %x (%s)\n", "BMS Status", status, 497328674Smav status == 0 ? "idle" : (status == 1 ? "active" : (status == 8 ? "suspended" : "unknown"))); 498328674Smav printf(" %-30s: %d\n", "Number of BMS", nscan); 499328674Smav printf(" %-30s: %d\n", "Progress Current BMS", progress); 500328674Smav /* Report retirements */ 501328674Smav if (walker - (uint8_t *)buf != 20) { 502328674Smav printf("Coding error, offset not 20\n"); 503328674Smav return; 504328674Smav } 505328674Smav size -= 20; 506328674Smav printf(" %-30s: %d\n", "BMS retirements", size / 0x18); 507328674Smav while (size > 0) { 508328674Smav code = le16dec(walker); 509328674Smav walker += 2; 510328674Smav walker++; 511328674Smav if (*walker++ != 0x14) { 512328674Smav printf("Bad length parameter\n"); 513328674Smav return; 514328674Smav } 515328674Smav pom = le32dec(walker); 516328674Smav walker += 4; 517328674Smav /* 518328674Smav * Spec sheet says the following are hard coded, if true, just 519328674Smav * print the NAND retirement. 520328674Smav */ 521328674Smav if (walker[0] == 0x41 && 522328674Smav walker[1] == 0x0b && 523328674Smav walker[2] == 0x01 && 524328674Smav walker[3] == 0x00 && 525328674Smav walker[4] == 0x00 && 526328674Smav walker[5] == 0x00 && 527328674Smav walker[6] == 0x00 && 528328674Smav walker[7] == 0x00) { 529328674Smav walker += 8; 530328674Smav walker += 4; /* Skip reserved */ 531328674Smav nand = le32dec(walker); 532328674Smav walker += 4; 533328674Smav printf(" %-30s: %d\n", "Retirement number", code); 534328674Smav printf(" %-28s: %#x\n", "NAND (C/T)BBBPPP", nand); 535328674Smav } else { 536328674Smav printf("Parameter %#x entry corrupt\n", code); 537328674Smav walker += 16; 538328674Smav } 539328674Smav } 540328674Smav} 541328674Smav 542328674Smavstatic void 543328674Smavprint_hgst_info_erase_errors(void *buf, uint16_t subtype __unused, uint8_t res __unused, uint32_t size) 544328674Smav{ 545328674Smav static struct kv_name kv[] = 546328674Smav { 547328674Smav { 0x0000, "Corrected Without Delay" }, 548328674Smav { 0x0001, "Corrected Maybe Delayed" }, 549328674Smav { 0x0002, "Re-Erase" }, 550328674Smav { 0x0003, "Errors Corrected" }, 551328674Smav { 0x0004, "Correct Algorithm Used" }, 552328674Smav { 0x0005, "Bytes Processed" }, 553328674Smav { 0x0006, "Uncorrected Errors" }, 554328674Smav { 0x8000, "Flash Erase Commands" }, 555328674Smav { 0x8001, "Mfg Defect Count" }, 556328674Smav { 0x8002, "Grown Defect Count" }, 557328674Smav { 0x8003, "Erase Count -- User" }, 558328674Smav { 0x8004, "Erase Count -- System" }, 559328674Smav }; 560328674Smav 561328674Smav printf("Erase Errors Subpage:\n"); 562328674Smav print_hgst_info_subpage_gen(buf, subtype, size, kv, nitems(kv)); 563328674Smav} 564328674Smav 565328674Smavstatic void 566328674Smavprint_hgst_info_erase_counts(void *buf, uint16_t subtype, uint8_t res __unused, uint32_t size) 567328674Smav{ 568328674Smav /* My drive doesn't export this -- so not coding up */ 569328674Smav printf("XXX: Erase counts subpage: %p, %#x %d\n", buf, subtype, size); 570328674Smav} 571328674Smav 572328674Smavstatic void 573328674Smavprint_hgst_info_temp_history(void *buf, uint16_t subtype __unused, uint8_t res __unused, uint32_t size __unused) 574328674Smav{ 575328674Smav uint8_t *walker = buf; 576328674Smav uint32_t min; 577328674Smav 578328674Smav printf("Temperature History:\n"); 579328674Smav printf(" %-30s: %d C\n", "Current Temperature", *walker++); 580328674Smav printf(" %-30s: %d C\n", "Reference Temperature", *walker++); 581328674Smav printf(" %-30s: %d C\n", "Maximum Temperature", *walker++); 582328674Smav printf(" %-30s: %d C\n", "Minimum Temperature", *walker++); 583328674Smav min = le32dec(walker); 584328674Smav walker += 4; 585328674Smav printf(" %-30s: %d:%02d:00\n", "Max Temperture Time", min / 60, min % 60); 586328674Smav min = le32dec(walker); 587328674Smav walker += 4; 588328674Smav printf(" %-30s: %d:%02d:00\n", "Over Temperture Duration", min / 60, min % 60); 589328674Smav min = le32dec(walker); 590328674Smav walker += 4; 591328674Smav printf(" %-30s: %d:%02d:00\n", "Min Temperture Time", min / 60, min % 60); 592328674Smav} 593328674Smav 594328674Smavstatic void 595328674Smavprint_hgst_info_ssd_perf(void *buf, uint16_t subtype __unused, uint8_t res, uint32_t size __unused) 596328674Smav{ 597328674Smav uint8_t *walker = buf; 598328674Smav uint64_t val; 599328674Smav 600328674Smav printf("SSD Performance Subpage Type %d:\n", res); 601328674Smav val = le64dec(walker); 602328674Smav walker += 8; 603328674Smav printf(" %-30s: %ju\n", "Host Read Commands", val); 604328674Smav val = le64dec(walker); 605328674Smav walker += 8; 606328674Smav printf(" %-30s: %ju\n", "Host Read Blocks", val); 607328674Smav val = le64dec(walker); 608328674Smav walker += 8; 609328674Smav printf(" %-30s: %ju\n", "Host Cache Read Hits Commands", val); 610328674Smav val = le64dec(walker); 611328674Smav walker += 8; 612328674Smav printf(" %-30s: %ju\n", "Host Cache Read Hits Blocks", val); 613328674Smav val = le64dec(walker); 614328674Smav walker += 8; 615328674Smav printf(" %-30s: %ju\n", "Host Read Commands Stalled", val); 616328674Smav val = le64dec(walker); 617328674Smav walker += 8; 618328674Smav printf(" %-30s: %ju\n", "Host Write Commands", val); 619328674Smav val = le64dec(walker); 620328674Smav walker += 8; 621328674Smav printf(" %-30s: %ju\n", "Host Write Blocks", val); 622328674Smav val = le64dec(walker); 623328674Smav walker += 8; 624328674Smav printf(" %-30s: %ju\n", "Host Write Odd Start Commands", val); 625328674Smav val = le64dec(walker); 626328674Smav walker += 8; 627328674Smav printf(" %-30s: %ju\n", "Host Write Odd End Commands", val); 628328674Smav val = le64dec(walker); 629328674Smav walker += 8; 630328674Smav printf(" %-30s: %ju\n", "Host Write Commands Stalled", val); 631328674Smav val = le64dec(walker); 632328674Smav walker += 8; 633328674Smav printf(" %-30s: %ju\n", "NAND Read Commands", val); 634328674Smav val = le64dec(walker); 635328674Smav walker += 8; 636328674Smav printf(" %-30s: %ju\n", "NAND Read Blocks", val); 637328674Smav val = le64dec(walker); 638328674Smav walker += 8; 639328674Smav printf(" %-30s: %ju\n", "NAND Write Commands", val); 640328674Smav val = le64dec(walker); 641328674Smav walker += 8; 642328674Smav printf(" %-30s: %ju\n", "NAND Write Blocks", val); 643328674Smav val = le64dec(walker); 644328674Smav walker += 8; 645328674Smav printf(" %-30s: %ju\n", "NAND Read Before Writes", val); 646328674Smav} 647328674Smav 648328674Smavstatic void 649328674Smavprint_hgst_info_firmware_load(void *buf, uint16_t subtype __unused, uint8_t res __unused, uint32_t size __unused) 650328674Smav{ 651328674Smav uint8_t *walker = buf; 652328674Smav 653328674Smav printf("Firmware Load Subpage:\n"); 654328674Smav printf(" %-30s: %d\n", "Firmware Downloads", le32dec(walker)); 655328674Smav} 656328674Smav 657328674Smavstatic void 658328674Smavkv_indirect(void *buf, uint32_t subtype, uint8_t res, uint32_t size, struct subpage_print *sp, size_t nsp) 659328674Smav{ 660328674Smav size_t i; 661328674Smav 662328674Smav for (i = 0; i < nsp; i++, sp++) { 663328674Smav if (sp->key == subtype) { 664328674Smav sp->fn(buf, subtype, res, size); 665328674Smav return; 666328674Smav } 667328674Smav } 668328674Smav printf("No handler for page type %x\n", subtype); 669328674Smav} 670328674Smav 671328674Smavstatic void 672328674Smavprint_hgst_info_log(void *buf, uint32_t size __unused) 673328674Smav{ 674328674Smav uint8_t *walker, *end, *subpage; 675328674Smav int pages; 676328674Smav uint16_t len; 677328674Smav uint8_t subtype, res; 678328674Smav 679328674Smav printf("HGST Extra Info Log\n"); 680328674Smav printf("===================\n"); 681328674Smav 682328674Smav walker = buf; 683328674Smav pages = *walker++; 684328674Smav walker++; 685328674Smav len = le16dec(walker); 686328674Smav walker += 2; 687328674Smav end = walker + len; /* Length is exclusive of this header */ 688328674Smav 689328674Smav while (walker < end) { 690328674Smav subpage = walker + 4; 691328674Smav subtype = *walker++ & 0x3f; /* subtype */ 692328674Smav res = *walker++; /* Reserved */ 693328674Smav len = le16dec(walker); 694328674Smav walker += len + 2; /* Length, not incl header */ 695328674Smav if (walker > end) { 696328674Smav printf("Ooops! Off the end of the list\n"); 697328674Smav break; 698328674Smav } 699328674Smav kv_indirect(subpage, subtype, res, len, hgst_subpage, nitems(hgst_subpage)); 700328674Smav } 701328674Smav} 702328674Smav 703328674Smav/* 704328673Smav * Table of log page printer / sizing. 705328673Smav * 706328673Smav * This includes Intel specific pages that are widely implemented. Not 707328673Smav * sure how best to switch between different vendors. 708328673Smav */ 709252302Sglebiusstatic struct logpage_function { 710252277Sjimharris uint8_t log_page; 711328672Smav print_fn_t print_fn; 712328672Smav size_t size; 713252277Sjimharris} logfuncs[] = { 714328672Smav {NVME_LOG_ERROR, print_log_error, 715328672Smav 0}, 716328672Smav {NVME_LOG_HEALTH_INFORMATION, print_log_health, 717328672Smav sizeof(struct nvme_health_information_page)}, 718328672Smav {NVME_LOG_FIRMWARE_SLOT, print_log_firmware, 719328672Smav sizeof(struct nvme_firmware_page)}, 720328673Smav {INTEL_LOG_TEMP_STATS, print_intel_temp_stats, 721328673Smav sizeof(struct intel_log_temp_stats)}, 722328674Smav {HGST_INFO_LOG, print_hgst_info_log, 723328674Smav DEFAULT_SIZE}, 724328672Smav {0, NULL, 725328672Smav 0}, 726252277Sjimharris}; 727252277Sjimharris 728252277Sjimharrisstatic void 729252277Sjimharrislogpage_usage(void) 730252277Sjimharris{ 731252277Sjimharris fprintf(stderr, "usage:\n"); 732252277Sjimharris fprintf(stderr, LOGPAGE_USAGE); 733253109Sjimharris exit(1); 734252277Sjimharris} 735252277Sjimharris 736252277Sjimharrisvoid 737252277Sjimharrislogpage(int argc, char *argv[]) 738252277Sjimharris{ 739253114Sjimharris int fd, nsid; 740252277Sjimharris int log_page = 0, pageflag = false; 741253114Sjimharris int hexflag = false, ns_specified; 742253114Sjimharris char ch, *p; 743253114Sjimharris char cname[64]; 744253109Sjimharris uint32_t size; 745252277Sjimharris void *buf; 746252277Sjimharris struct logpage_function *f; 747252277Sjimharris struct nvme_controller_data cdata; 748252277Sjimharris print_fn_t print_fn; 749252277Sjimharris 750252277Sjimharris while ((ch = getopt(argc, argv, "p:x")) != -1) { 751252277Sjimharris switch (ch) { 752252277Sjimharris case 'p': 753252277Sjimharris /* TODO: Add human-readable ASCII page IDs */ 754252277Sjimharris log_page = strtol(optarg, &p, 0); 755252277Sjimharris if (p != NULL && *p != '\0') { 756252277Sjimharris fprintf(stderr, 757252277Sjimharris "\"%s\" not valid log page id.\n", 758252277Sjimharris optarg); 759252277Sjimharris logpage_usage(); 760252277Sjimharris /* TODO: Define valid log page id ranges in nvme.h? */ 761252277Sjimharris } else if (log_page == 0 || 762252277Sjimharris (log_page >= 0x04 && log_page <= 0x7F) || 763252277Sjimharris (log_page >= 0x80 && log_page <= 0xBF)) { 764252277Sjimharris fprintf(stderr, 765252277Sjimharris "\"%s\" not valid log page id.\n", 766252277Sjimharris optarg); 767252277Sjimharris logpage_usage(); 768252277Sjimharris } 769252277Sjimharris pageflag = true; 770252277Sjimharris break; 771252277Sjimharris case 'x': 772252277Sjimharris hexflag = true; 773252277Sjimharris break; 774252277Sjimharris } 775252277Sjimharris } 776252277Sjimharris 777252277Sjimharris if (!pageflag) { 778252277Sjimharris printf("Missing page_id (-p).\n"); 779252277Sjimharris logpage_usage(); 780252277Sjimharris } 781252277Sjimharris 782252277Sjimharris /* Check that a controller and/or namespace was specified. */ 783252277Sjimharris if (optind >= argc) 784252277Sjimharris logpage_usage(); 785252277Sjimharris 786253114Sjimharris if (strstr(argv[optind], NVME_NS_PREFIX) != NULL) { 787253114Sjimharris ns_specified = true; 788253114Sjimharris parse_ns_str(argv[optind], cname, &nsid); 789253114Sjimharris open_dev(cname, &fd, 1, 1); 790253114Sjimharris } else { 791253114Sjimharris ns_specified = false; 792253114Sjimharris nsid = NVME_GLOBAL_NAMESPACE_TAG; 793253114Sjimharris open_dev(argv[optind], &fd, 1, 1); 794253114Sjimharris } 795253114Sjimharris 796285796Sjimharris read_controller_data(fd, &cdata); 797285796Sjimharris 798252277Sjimharris /* 799252277Sjimharris * The log page attribtues indicate whether or not the controller 800252277Sjimharris * supports the SMART/Health information log page on a per 801252277Sjimharris * namespace basis. 802252277Sjimharris */ 803253114Sjimharris if (ns_specified) { 804253114Sjimharris if (log_page != NVME_LOG_HEALTH_INFORMATION) 805253114Sjimharris errx(1, "log page %d valid only at controller level", 806253114Sjimharris log_page); 807253114Sjimharris if (cdata.lpa.ns_smart == 0) 808253114Sjimharris errx(1, 809253114Sjimharris "controller does not support per namespace " 810253114Sjimharris "smart/health information"); 811253114Sjimharris } 812252277Sjimharris 813252277Sjimharris print_fn = print_hex; 814328672Smav size = DEFAULT_SIZE; 815252277Sjimharris if (!hexflag) { 816252277Sjimharris /* 817252277Sjimharris * See if there is a pretty print function for the 818252277Sjimharris * specified log page. If one isn't found, we 819252277Sjimharris * just revert to the default (print_hex). 820252277Sjimharris */ 821252277Sjimharris f = logfuncs; 822252277Sjimharris while (f->log_page > 0) { 823252277Sjimharris if (log_page == f->log_page) { 824328672Smav print_fn = f->print_fn; 825328672Smav size = f->size; 826252277Sjimharris break; 827252277Sjimharris } 828252277Sjimharris f++; 829252277Sjimharris } 830252277Sjimharris } 831252277Sjimharris 832328672Smav if (log_page == NVME_LOG_ERROR) { 833252277Sjimharris size = sizeof(struct nvme_error_information_entry); 834252277Sjimharris size *= (cdata.elpe + 1); 835252277Sjimharris } 836252277Sjimharris 837328672Smav /* Read the log page */ 838252277Sjimharris buf = get_log_buffer(size); 839252277Sjimharris read_logpage(fd, log_page, nsid, buf, size); 840252277Sjimharris print_fn(buf, size); 841252277Sjimharris 842252277Sjimharris close(fd); 843253109Sjimharris exit(0); 844252277Sjimharris} 845