1252277Sjimharris/*- 2330449Seadler * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3330449Seadler * 4252277Sjimharris * Copyright (c) 2013 EMC Corp. 5252277Sjimharris * All rights reserved. 6252277Sjimharris * 7252277Sjimharris * Copyright (C) 2012-2013 Intel Corporation 8252277Sjimharris * All rights reserved. 9252277Sjimharris * 10252277Sjimharris * Redistribution and use in source and binary forms, with or without 11252277Sjimharris * modification, are permitted provided that the following conditions 12252277Sjimharris * are met: 13252277Sjimharris * 1. Redistributions of source code must retain the above copyright 14252277Sjimharris * notice, this list of conditions and the following disclaimer. 15252277Sjimharris * 2. Redistributions in binary form must reproduce the above copyright 16252277Sjimharris * notice, this list of conditions and the following disclaimer in the 17252277Sjimharris * documentation and/or other materials provided with the distribution. 18252277Sjimharris * 19252277Sjimharris * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 20252277Sjimharris * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21252277Sjimharris * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22252277Sjimharris * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 23252277Sjimharris * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24252277Sjimharris * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25252277Sjimharris * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26252277Sjimharris * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27252277Sjimharris * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28252277Sjimharris * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29252277Sjimharris * SUCH DAMAGE. 30252277Sjimharris */ 31252277Sjimharris 32252277Sjimharris#include <sys/cdefs.h> 33252277Sjimharris__FBSDID("$FreeBSD: stable/11/sbin/nvmecontrol/logpage.c 330449 2018-03-05 07:26:05Z eadler $"); 34252277Sjimharris 35252277Sjimharris#include <sys/param.h> 36252277Sjimharris#include <sys/ioccom.h> 37252277Sjimharris 38252277Sjimharris#include <ctype.h> 39253109Sjimharris#include <err.h> 40252277Sjimharris#include <fcntl.h> 41252277Sjimharris#include <stdbool.h> 42252277Sjimharris#include <stddef.h> 43252277Sjimharris#include <stdio.h> 44252277Sjimharris#include <stdlib.h> 45252277Sjimharris#include <string.h> 46252277Sjimharris#include <unistd.h> 47328668Smav#include <sys/endian.h> 48252277Sjimharris 49328668Smav#if _BYTE_ORDER != _LITTLE_ENDIAN 50328668Smav#error "Code only works on little endian machines" 51328668Smav#endif 52328668Smav 53252277Sjimharris#include "nvmecontrol.h" 54252277Sjimharris 55252277Sjimharris#define DEFAULT_SIZE (4096) 56252277Sjimharris#define MAX_FW_SLOTS (7) 57252277Sjimharris 58328741Smavtypedef void (*print_fn_t)(const struct nvme_controller_data *cdata, void *buf, uint32_t size); 59252277Sjimharris 60328674Smavstruct kv_name 61328674Smav{ 62328674Smav uint32_t key; 63328674Smav const char *name; 64328674Smav}; 65328674Smav 66328674Smavstatic const char * 67328674Smavkv_lookup(const struct kv_name *kv, size_t kv_count, uint32_t key) 68328674Smav{ 69328674Smav static char bad[32]; 70328674Smav size_t i; 71328674Smav 72328674Smav for (i = 0; i < kv_count; i++, kv++) 73328674Smav if (kv->key == key) 74328674Smav return kv->name; 75328674Smav snprintf(bad, sizeof(bad), "Attribute %#x", key); 76328674Smav return bad; 77328674Smav} 78328674Smav 79328721Smavstatic void 80328741Smavprint_log_hex(const struct nvme_controller_data *cdata __unused, void *data, uint32_t length) 81328721Smav{ 82328741Smav 83328741Smav print_hex(data, length); 84328741Smav} 85328741Smav 86328741Smavstatic void 87328741Smavprint_bin(const struct nvme_controller_data *cdata __unused, void *data, uint32_t length) 88328741Smav{ 89328741Smav 90328721Smav write(STDOUT_FILENO, data, length); 91328721Smav} 92328721Smav 93252277Sjimharrisstatic void * 94253109Sjimharrisget_log_buffer(uint32_t size) 95252277Sjimharris{ 96252277Sjimharris void *buf; 97252277Sjimharris 98253109Sjimharris if ((buf = malloc(size)) == NULL) 99253109Sjimharris errx(1, "unable to malloc %u bytes", size); 100253109Sjimharris 101252277Sjimharris memset(buf, 0, size); 102252277Sjimharris return (buf); 103252277Sjimharris} 104252277Sjimharris 105252277Sjimharrisvoid 106328673Smavread_logpage(int fd, uint8_t log_page, int nsid, void *payload, 107252277Sjimharris uint32_t payload_size) 108252277Sjimharris{ 109252277Sjimharris struct nvme_pt_command pt; 110252277Sjimharris 111252277Sjimharris memset(&pt, 0, sizeof(pt)); 112252277Sjimharris pt.cmd.opc = NVME_OPC_GET_LOG_PAGE; 113252277Sjimharris pt.cmd.nsid = nsid; 114252277Sjimharris pt.cmd.cdw10 = ((payload_size/sizeof(uint32_t)) - 1) << 16; 115252277Sjimharris pt.cmd.cdw10 |= log_page; 116252277Sjimharris pt.buf = payload; 117252277Sjimharris pt.len = payload_size; 118252277Sjimharris pt.is_read = 1; 119252277Sjimharris 120253109Sjimharris if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0) 121253109Sjimharris err(1, "get log page request failed"); 122252277Sjimharris 123253109Sjimharris if (nvme_completion_is_error(&pt.cpl)) 124253109Sjimharris errx(1, "get log page request returned error"); 125252277Sjimharris} 126252277Sjimharris 127252277Sjimharrisstatic void 128328741Smavprint_log_error(const struct nvme_controller_data *cdata __unused, void *buf, uint32_t size) 129252277Sjimharris{ 130252277Sjimharris int i, nentries; 131252277Sjimharris struct nvme_error_information_entry *entry = buf; 132252277Sjimharris struct nvme_status *status; 133252277Sjimharris 134252277Sjimharris printf("Error Information Log\n"); 135252277Sjimharris printf("=====================\n"); 136252277Sjimharris 137252277Sjimharris if (entry->error_count == 0) { 138252277Sjimharris printf("No error entries found\n"); 139252277Sjimharris return; 140252277Sjimharris } 141252277Sjimharris 142252277Sjimharris nentries = size/sizeof(struct nvme_error_information_entry); 143252277Sjimharris for (i = 0; i < nentries; i++, entry++) { 144252277Sjimharris if (entry->error_count == 0) 145252277Sjimharris break; 146252277Sjimharris 147252277Sjimharris status = &entry->status; 148252277Sjimharris printf("Entry %02d\n", i + 1); 149252277Sjimharris printf("=========\n"); 150252277Sjimharris printf(" Error count: %ju\n", entry->error_count); 151252277Sjimharris printf(" Submission queue ID: %u\n", entry->sqid); 152252277Sjimharris printf(" Command ID: %u\n", entry->cid); 153252277Sjimharris /* TODO: Export nvme_status_string structures from kernel? */ 154252277Sjimharris printf(" Status:\n"); 155252277Sjimharris printf(" Phase tag: %d\n", status->p); 156252277Sjimharris printf(" Status code: %d\n", status->sc); 157252277Sjimharris printf(" Status code type: %d\n", status->sct); 158252277Sjimharris printf(" More: %d\n", status->m); 159252277Sjimharris printf(" DNR: %d\n", status->dnr); 160252277Sjimharris printf(" Error location: %u\n", entry->error_location); 161252277Sjimharris printf(" LBA: %ju\n", entry->lba); 162252277Sjimharris printf(" Namespace ID: %u\n", entry->nsid); 163252277Sjimharris printf(" Vendor specific info: %u\n", entry->vendor_specific); 164252277Sjimharris } 165252277Sjimharris} 166252277Sjimharris 167252277Sjimharrisstatic void 168328669Smavprint_temp(uint16_t t) 169328669Smav{ 170328669Smav printf("%u K, %2.2f C, %3.2f F\n", t, (float)t - 273.15, (float)t * 9 / 5 - 459.67); 171328669Smav} 172328669Smav 173328669Smav 174328669Smavstatic void 175328741Smavprint_log_health(const struct nvme_controller_data *cdata __unused, void *buf, uint32_t size __unused) 176252277Sjimharris{ 177252277Sjimharris struct nvme_health_information_page *health = buf; 178328668Smav char cbuf[UINT128_DIG + 1]; 179328669Smav int i; 180252277Sjimharris 181252277Sjimharris printf("SMART/Health Information Log\n"); 182252277Sjimharris printf("============================\n"); 183252277Sjimharris 184252277Sjimharris printf("Critical Warning State: 0x%02x\n", 185252277Sjimharris health->critical_warning.raw); 186252277Sjimharris printf(" Available spare: %d\n", 187252277Sjimharris health->critical_warning.bits.available_spare); 188252277Sjimharris printf(" Temperature: %d\n", 189252277Sjimharris health->critical_warning.bits.temperature); 190252277Sjimharris printf(" Device reliability: %d\n", 191252277Sjimharris health->critical_warning.bits.device_reliability); 192252277Sjimharris printf(" Read only: %d\n", 193252277Sjimharris health->critical_warning.bits.read_only); 194252277Sjimharris printf(" Volatile memory backup: %d\n", 195252277Sjimharris health->critical_warning.bits.volatile_memory_backup); 196328669Smav printf("Temperature: "); 197328669Smav print_temp(health->temperature); 198252277Sjimharris printf("Available spare: %u\n", 199252277Sjimharris health->available_spare); 200252277Sjimharris printf("Available spare threshold: %u\n", 201252277Sjimharris health->available_spare_threshold); 202252277Sjimharris printf("Percentage used: %u\n", 203252277Sjimharris health->percentage_used); 204252277Sjimharris 205328669Smav printf("Data units (512,000 byte) read: %s\n", 206328668Smav uint128_to_str(to128(health->data_units_read), cbuf, sizeof(cbuf))); 207328669Smav printf("Data units written: %s\n", 208328668Smav uint128_to_str(to128(health->data_units_written), cbuf, sizeof(cbuf))); 209328668Smav printf("Host read commands: %s\n", 210328668Smav uint128_to_str(to128(health->host_read_commands), cbuf, sizeof(cbuf))); 211328668Smav printf("Host write commands: %s\n", 212328668Smav uint128_to_str(to128(health->host_write_commands), cbuf, sizeof(cbuf))); 213328668Smav printf("Controller busy time (minutes): %s\n", 214328668Smav uint128_to_str(to128(health->controller_busy_time), cbuf, sizeof(cbuf))); 215328668Smav printf("Power cycles: %s\n", 216328668Smav uint128_to_str(to128(health->power_cycles), cbuf, sizeof(cbuf))); 217328668Smav printf("Power on hours: %s\n", 218328668Smav uint128_to_str(to128(health->power_on_hours), cbuf, sizeof(cbuf))); 219328668Smav printf("Unsafe shutdowns: %s\n", 220328668Smav uint128_to_str(to128(health->unsafe_shutdowns), cbuf, sizeof(cbuf))); 221328668Smav printf("Media errors: %s\n", 222328668Smav uint128_to_str(to128(health->media_errors), cbuf, sizeof(cbuf))); 223328668Smav printf("No. error info log entries: %s\n", 224328668Smav uint128_to_str(to128(health->num_error_info_log_entries), cbuf, sizeof(cbuf))); 225328669Smav 226328669Smav printf("Warning Temp Composite Time: %d\n", health->warning_temp_time); 227328669Smav printf("Error Temp Composite Time: %d\n", health->error_temp_time); 228328669Smav for (i = 0; i < 7; i++) { 229328669Smav if (health->temp_sensor[i] == 0) 230328669Smav continue; 231328669Smav printf("Temperature Sensor %d: ", i + 1); 232328669Smav print_temp(health->temp_sensor[i]); 233328669Smav } 234252277Sjimharris} 235252277Sjimharris 236252277Sjimharrisstatic void 237328741Smavprint_log_firmware(const struct nvme_controller_data *cdata __unused, void *buf, uint32_t size __unused) 238252277Sjimharris{ 239328748Smav int i, slots; 240252277Sjimharris const char *status; 241252277Sjimharris struct nvme_firmware_page *fw = buf; 242252277Sjimharris 243252277Sjimharris printf("Firmware Slot Log\n"); 244252277Sjimharris printf("=================\n"); 245252277Sjimharris 246328748Smav if (cdata->oacs.firmware == 0) 247328748Smav slots = 1; 248328748Smav else 249328748Smav slots = MIN(cdata->frmw.num_slots, MAX_FW_SLOTS); 250328748Smav 251328748Smav for (i = 0; i < slots; i++) { 252252277Sjimharris printf("Slot %d: ", i + 1); 253252277Sjimharris if (fw->afi.slot == i + 1) 254252277Sjimharris status = " Active"; 255252277Sjimharris else 256252277Sjimharris status = "Inactive"; 257252277Sjimharris 258252277Sjimharris if (fw->revision[i] == 0LLU) 259252277Sjimharris printf("Empty\n"); 260252277Sjimharris else 261252277Sjimharris if (isprint(*(char *)&fw->revision[i])) 262252277Sjimharris printf("[%s] %.8s\n", status, 263252277Sjimharris (char *)&fw->revision[i]); 264252277Sjimharris else 265252277Sjimharris printf("[%s] %016jx\n", status, 266252277Sjimharris fw->revision[i]); 267252277Sjimharris } 268252277Sjimharris} 269252277Sjimharris 270328708Smav/* 271328708Smav * Intel specific log pages from 272328708Smav * http://www.intel.com/content/dam/www/public/us/en/documents/product-specifications/ssd-dc-p3700-spec.pdf 273328708Smav * 274328708Smav * Though the version as of this date has a typo for the size of log page 0xca, 275328708Smav * offset 147: it is only 1 byte, not 6. 276328708Smav */ 277328673Smavstatic void 278328741Smavprint_intel_temp_stats(const struct nvme_controller_data *cdata __unused, void *buf, uint32_t size __unused) 279328673Smav{ 280328673Smav struct intel_log_temp_stats *temp = buf; 281328673Smav 282328673Smav printf("Intel Temperature Log\n"); 283328673Smav printf("=====================\n"); 284328673Smav 285328673Smav printf("Current: "); 286328673Smav print_temp(temp->current); 287328673Smav printf("Overtemp Last Flags %#jx\n", (uintmax_t)temp->overtemp_flag_last); 288328673Smav printf("Overtemp Lifetime Flags %#jx\n", (uintmax_t)temp->overtemp_flag_life); 289328673Smav printf("Max Temperature "); 290328673Smav print_temp(temp->max_temp); 291328673Smav printf("Min Temperature "); 292328673Smav print_temp(temp->min_temp); 293328673Smav printf("Max Operating Temperature "); 294328673Smav print_temp(temp->max_oper_temp); 295328673Smav printf("Min Operating Temperature "); 296328673Smav print_temp(temp->min_oper_temp); 297328673Smav printf("Estimated Temperature Offset: %ju C/K\n", (uintmax_t)temp->est_offset); 298328673Smav} 299328673Smav 300328712Smav/* 301328712Smav * Format from Table 22, section 5.7 IO Command Latency Statistics. 302328712Smav * Read and write stats pages have identical encoding. 303328712Smav */ 304328708Smavstatic void 305328741Smavprint_intel_read_write_lat_log(const struct nvme_controller_data *cdata __unused, void *buf, uint32_t size __unused) 306328712Smav{ 307328712Smav const char *walker = buf; 308328712Smav int i; 309328712Smav 310328712Smav printf("Major: %d\n", le16dec(walker + 0)); 311328712Smav printf("Minor: %d\n", le16dec(walker + 2)); 312328712Smav for (i = 0; i < 32; i++) 313328712Smav printf("%4dus-%4dus: %ju\n", i * 32, (i + 1) * 32, (uintmax_t)le32dec(walker + 4 + i * 4)); 314328712Smav for (i = 1; i < 32; i++) 315328712Smav printf("%4dms-%4dms: %ju\n", i, i + 1, (uintmax_t)le32dec(walker + 132 + i * 4)); 316328712Smav for (i = 1; i < 32; i++) 317328712Smav printf("%4dms-%4dms: %ju\n", i * 32, (i + 1) * 32, (uintmax_t)le32dec(walker + 256 + i * 4)); 318328712Smav} 319328712Smav 320328712Smavstatic void 321328741Smavprint_intel_read_lat_log(const struct nvme_controller_data *cdata __unused, void *buf, uint32_t size) 322328712Smav{ 323328712Smav 324328712Smav printf("Intel Read Latency Log\n"); 325328712Smav printf("======================\n"); 326328741Smav print_intel_read_write_lat_log(cdata, buf, size); 327328712Smav} 328328712Smav 329328712Smavstatic void 330328741Smavprint_intel_write_lat_log(const struct nvme_controller_data *cdata __unused, void *buf, uint32_t size) 331328712Smav{ 332328712Smav 333328712Smav printf("Intel Write Latency Log\n"); 334328712Smav printf("=======================\n"); 335328741Smav print_intel_read_write_lat_log(cdata, buf, size); 336328712Smav} 337328712Smav 338328712Smav/* 339328725Smav * Table 19. 5.4 SMART Attributes. Samsung also implements this and some extra data not documented. 340328712Smav */ 341328712Smavstatic void 342328741Smavprint_intel_add_smart(const struct nvme_controller_data *cdata __unused, void *buf, uint32_t size __unused) 343328708Smav{ 344328708Smav uint8_t *walker = buf; 345328708Smav uint8_t *end = walker + 150; 346328708Smav const char *name; 347328708Smav uint64_t raw; 348328708Smav uint8_t normalized; 349328708Smav 350328708Smav static struct kv_name kv[] = 351328708Smav { 352328708Smav { 0xab, "Program Fail Count" }, 353328708Smav { 0xac, "Erase Fail Count" }, 354328708Smav { 0xad, "Wear Leveling Count" }, 355328708Smav { 0xb8, "End to End Error Count" }, 356328708Smav { 0xc7, "CRC Error Count" }, 357328708Smav { 0xe2, "Timed: Media Wear" }, 358328708Smav { 0xe3, "Timed: Host Read %" }, 359328708Smav { 0xe4, "Timed: Elapsed Time" }, 360328708Smav { 0xea, "Thermal Throttle Status" }, 361328708Smav { 0xf0, "Retry Buffer Overflows" }, 362328708Smav { 0xf3, "PLL Lock Loss Count" }, 363328708Smav { 0xf4, "NAND Bytes Written" }, 364328708Smav { 0xf5, "Host Bytes Written" }, 365328708Smav }; 366328708Smav 367328708Smav printf("Additional SMART Data Log\n"); 368328708Smav printf("=========================\n"); 369328708Smav /* 370328708Smav * walker[0] = Key 371328708Smav * walker[1,2] = reserved 372328708Smav * walker[3] = Normalized Value 373328708Smav * walker[4] = reserved 374328708Smav * walker[5..10] = Little Endian Raw value 375328708Smav * (or other represenations) 376328708Smav * walker[11] = reserved 377328708Smav */ 378328708Smav while (walker < end) { 379328708Smav name = kv_lookup(kv, nitems(kv), *walker); 380328708Smav normalized = walker[3]; 381328708Smav raw = le48dec(walker + 5); 382328708Smav switch (*walker){ 383328708Smav case 0: 384328708Smav break; 385328708Smav case 0xad: 386328708Smav printf("%-32s: %3d min: %u max: %u ave: %u\n", name, normalized, 387328708Smav le16dec(walker + 5), le16dec(walker + 7), le16dec(walker + 9)); 388328708Smav break; 389328708Smav case 0xe2: 390328708Smav printf("%-32s: %3d %.3f%%\n", name, normalized, raw / 1024.0); 391328708Smav break; 392328708Smav case 0xea: 393328708Smav printf("%-32s: %3d %d%% %d times\n", name, normalized, walker[5], le32dec(walker+6)); 394328708Smav break; 395328708Smav default: 396328708Smav printf("%-32s: %3d %ju\n", name, normalized, (uintmax_t)raw); 397328708Smav break; 398328708Smav } 399328708Smav walker += 12; 400328708Smav } 401328708Smav} 402328708Smav 403328673Smav/* 404328674Smav * HGST's 0xc1 page. This is a grab bag of additional data. Please see 405328674Smav * https://www.hgst.com/sites/default/files/resources/US_SN150_ProdManual.pdf 406328674Smav * https://www.hgst.com/sites/default/files/resources/US_SN100_ProdManual.pdf 407328674Smav * Appendix A for details 408328674Smav */ 409328674Smav 410328674Smavtypedef void (*subprint_fn_t)(void *buf, uint16_t subtype, uint8_t res, uint32_t size); 411328674Smav 412328674Smavstruct subpage_print 413328674Smav{ 414328674Smav uint16_t key; 415328674Smav subprint_fn_t fn; 416328674Smav}; 417328674Smav 418328674Smavstatic void print_hgst_info_write_errors(void *buf, uint16_t subtype, uint8_t res, uint32_t size); 419328674Smavstatic void print_hgst_info_read_errors(void *buf, uint16_t subtype, uint8_t res, uint32_t size); 420328674Smavstatic void print_hgst_info_verify_errors(void *buf, uint16_t subtype, uint8_t res, uint32_t size); 421328674Smavstatic void print_hgst_info_self_test(void *buf, uint16_t subtype, uint8_t res, uint32_t size); 422328674Smavstatic void print_hgst_info_background_scan(void *buf, uint16_t subtype, uint8_t res, uint32_t size); 423328674Smavstatic void print_hgst_info_erase_errors(void *buf, uint16_t subtype, uint8_t res, uint32_t size); 424328674Smavstatic void print_hgst_info_erase_counts(void *buf, uint16_t subtype, uint8_t res, uint32_t size); 425328674Smavstatic void print_hgst_info_temp_history(void *buf, uint16_t subtype, uint8_t res, uint32_t size); 426328674Smavstatic void print_hgst_info_ssd_perf(void *buf, uint16_t subtype, uint8_t res, uint32_t size); 427328674Smavstatic void print_hgst_info_firmware_load(void *buf, uint16_t subtype, uint8_t res, uint32_t size); 428328674Smav 429328674Smavstatic struct subpage_print hgst_subpage[] = { 430328674Smav { 0x02, print_hgst_info_write_errors }, 431328674Smav { 0x03, print_hgst_info_read_errors }, 432328674Smav { 0x05, print_hgst_info_verify_errors }, 433328674Smav { 0x10, print_hgst_info_self_test }, 434328674Smav { 0x15, print_hgst_info_background_scan }, 435328674Smav { 0x30, print_hgst_info_erase_errors }, 436328674Smav { 0x31, print_hgst_info_erase_counts }, 437328674Smav { 0x32, print_hgst_info_temp_history }, 438328674Smav { 0x37, print_hgst_info_ssd_perf }, 439328674Smav { 0x38, print_hgst_info_firmware_load }, 440328674Smav}; 441328674Smav 442328674Smav/* Print a subpage that is basically just key value pairs */ 443328674Smavstatic void 444328674Smavprint_hgst_info_subpage_gen(void *buf, uint16_t subtype __unused, uint32_t size, 445328674Smav const struct kv_name *kv, size_t kv_count) 446328674Smav{ 447328674Smav uint8_t *wsp, *esp; 448328674Smav uint16_t ptype; 449328674Smav uint8_t plen; 450328674Smav uint64_t param; 451328674Smav int i; 452328674Smav 453328674Smav wsp = buf; 454328674Smav esp = wsp + size; 455328674Smav while (wsp < esp) { 456328674Smav ptype = le16dec(wsp); 457328674Smav wsp += 2; 458328674Smav wsp++; /* Flags, just ignore */ 459328674Smav plen = *wsp++; 460328674Smav param = 0; 461328674Smav for (i = 0; i < plen; i++) 462328674Smav param |= (uint64_t)*wsp++ << (i * 8); 463328674Smav printf(" %-30s: %jd\n", kv_lookup(kv, kv_count, ptype), (uintmax_t)param); 464328674Smav } 465328674Smav} 466328674Smav 467328674Smavstatic void 468328674Smavprint_hgst_info_write_errors(void *buf, uint16_t subtype, uint8_t res __unused, uint32_t size) 469328674Smav{ 470328674Smav static struct kv_name kv[] = 471328674Smav { 472328674Smav { 0x0000, "Corrected Without Delay" }, 473328674Smav { 0x0001, "Corrected Maybe Delayed" }, 474328674Smav { 0x0002, "Re-Writes" }, 475328674Smav { 0x0003, "Errors Corrected" }, 476328674Smav { 0x0004, "Correct Algorithm Used" }, 477328674Smav { 0x0005, "Bytes Processed" }, 478328674Smav { 0x0006, "Uncorrected Errors" }, 479328674Smav { 0x8000, "Flash Write Commands" }, 480328674Smav { 0x8001, "HGST Special" }, 481328674Smav }; 482328674Smav 483328674Smav printf("Write Errors Subpage:\n"); 484328674Smav print_hgst_info_subpage_gen(buf, subtype, size, kv, nitems(kv)); 485328674Smav} 486328674Smav 487328674Smavstatic void 488328674Smavprint_hgst_info_read_errors(void *buf, uint16_t subtype, uint8_t res __unused, uint32_t size) 489328674Smav{ 490328674Smav static struct kv_name kv[] = 491328674Smav { 492328674Smav { 0x0000, "Corrected Without Delay" }, 493328674Smav { 0x0001, "Corrected Maybe Delayed" }, 494328674Smav { 0x0002, "Re-Reads" }, 495328674Smav { 0x0003, "Errors Corrected" }, 496328674Smav { 0x0004, "Correct Algorithm Used" }, 497328674Smav { 0x0005, "Bytes Processed" }, 498328674Smav { 0x0006, "Uncorrected Errors" }, 499328674Smav { 0x8000, "Flash Read Commands" }, 500328674Smav { 0x8001, "XOR Recovered" }, 501328674Smav { 0x8002, "Total Corrected Bits" }, 502328674Smav }; 503328674Smav 504328674Smav printf("Read Errors Subpage:\n"); 505328674Smav print_hgst_info_subpage_gen(buf, subtype, size, kv, nitems(kv)); 506328674Smav} 507328674Smav 508328674Smavstatic void 509328674Smavprint_hgst_info_verify_errors(void *buf, uint16_t subtype, uint8_t res __unused, uint32_t size) 510328674Smav{ 511328674Smav static struct kv_name kv[] = 512328674Smav { 513328674Smav { 0x0000, "Corrected Without Delay" }, 514328674Smav { 0x0001, "Corrected Maybe Delayed" }, 515328674Smav { 0x0002, "Re-Reads" }, 516328674Smav { 0x0003, "Errors Corrected" }, 517328674Smav { 0x0004, "Correct Algorithm Used" }, 518328674Smav { 0x0005, "Bytes Processed" }, 519328674Smav { 0x0006, "Uncorrected Errors" }, 520328674Smav { 0x8000, "Commands Processed" }, 521328674Smav }; 522328674Smav 523328674Smav printf("Verify Errors Subpage:\n"); 524328674Smav print_hgst_info_subpage_gen(buf, subtype, size, kv, nitems(kv)); 525328674Smav} 526328674Smav 527328674Smavstatic void 528328674Smavprint_hgst_info_self_test(void *buf, uint16_t subtype __unused, uint8_t res __unused, uint32_t size) 529328674Smav{ 530328674Smav size_t i; 531328674Smav uint8_t *walker = buf; 532328674Smav uint16_t code, hrs; 533328674Smav uint32_t lba; 534328674Smav 535328674Smav printf("Self Test Subpage:\n"); 536328674Smav for (i = 0; i < size / 20; i++) { /* Each entry is 20 bytes */ 537328674Smav code = le16dec(walker); 538328674Smav walker += 2; 539328674Smav walker++; /* Ignore fixed flags */ 540328674Smav if (*walker == 0) /* Last entry is zero length */ 541328674Smav break; 542328674Smav if (*walker++ != 0x10) { 543328674Smav printf("Bad length for self test report\n"); 544328674Smav return; 545328674Smav } 546328674Smav printf(" %-30s: %d\n", "Recent Test", code); 547328674Smav printf(" %-28s: %#x\n", "Self-Test Results", *walker & 0xf); 548328674Smav printf(" %-28s: %#x\n", "Self-Test Code", (*walker >> 5) & 0x7); 549328674Smav walker++; 550328674Smav printf(" %-28s: %#x\n", "Self-Test Number", *walker++); 551328674Smav hrs = le16dec(walker); 552328674Smav walker += 2; 553328674Smav lba = le32dec(walker); 554328674Smav walker += 4; 555328674Smav printf(" %-28s: %u\n", "Total Power On Hrs", hrs); 556328674Smav printf(" %-28s: %#jx (%jd)\n", "LBA", (uintmax_t)lba, (uintmax_t)lba); 557328674Smav printf(" %-28s: %#x\n", "Sense Key", *walker++ & 0xf); 558328674Smav printf(" %-28s: %#x\n", "Additional Sense Code", *walker++); 559328674Smav printf(" %-28s: %#x\n", "Additional Sense Qualifier", *walker++); 560328674Smav printf(" %-28s: %#x\n", "Vendor Specific Detail", *walker++); 561328674Smav } 562328674Smav} 563328674Smav 564328674Smavstatic void 565328674Smavprint_hgst_info_background_scan(void *buf, uint16_t subtype __unused, uint8_t res __unused, uint32_t size) 566328674Smav{ 567328674Smav uint8_t *walker = buf; 568328674Smav uint8_t status; 569328674Smav uint16_t code, nscan, progress; 570328674Smav uint32_t pom, nand; 571328674Smav 572328674Smav printf("Background Media Scan Subpage:\n"); 573328674Smav /* Decode the header */ 574328674Smav code = le16dec(walker); 575328674Smav walker += 2; 576328674Smav walker++; /* Ignore fixed flags */ 577328674Smav if (*walker++ != 0x10) { 578328674Smav printf("Bad length for background scan header\n"); 579328674Smav return; 580328674Smav } 581328674Smav if (code != 0) { 582328674Smav printf("Expceted code 0, found code %#x\n", code); 583328674Smav return; 584328674Smav } 585328674Smav pom = le32dec(walker); 586328674Smav walker += 4; 587328674Smav walker++; /* Reserved */ 588328674Smav status = *walker++; 589328674Smav nscan = le16dec(walker); 590328674Smav walker += 2; 591328674Smav progress = le16dec(walker); 592328674Smav walker += 2; 593328674Smav walker += 6; /* Reserved */ 594328674Smav printf(" %-30s: %d\n", "Power On Minutes", pom); 595328674Smav printf(" %-30s: %x (%s)\n", "BMS Status", status, 596328674Smav status == 0 ? "idle" : (status == 1 ? "active" : (status == 8 ? "suspended" : "unknown"))); 597328674Smav printf(" %-30s: %d\n", "Number of BMS", nscan); 598328674Smav printf(" %-30s: %d\n", "Progress Current BMS", progress); 599328674Smav /* Report retirements */ 600328674Smav if (walker - (uint8_t *)buf != 20) { 601328674Smav printf("Coding error, offset not 20\n"); 602328674Smav return; 603328674Smav } 604328674Smav size -= 20; 605328674Smav printf(" %-30s: %d\n", "BMS retirements", size / 0x18); 606328674Smav while (size > 0) { 607328674Smav code = le16dec(walker); 608328674Smav walker += 2; 609328674Smav walker++; 610328674Smav if (*walker++ != 0x14) { 611328674Smav printf("Bad length parameter\n"); 612328674Smav return; 613328674Smav } 614328674Smav pom = le32dec(walker); 615328674Smav walker += 4; 616328674Smav /* 617328674Smav * Spec sheet says the following are hard coded, if true, just 618328674Smav * print the NAND retirement. 619328674Smav */ 620328674Smav if (walker[0] == 0x41 && 621328674Smav walker[1] == 0x0b && 622328674Smav walker[2] == 0x01 && 623328674Smav walker[3] == 0x00 && 624328674Smav walker[4] == 0x00 && 625328674Smav walker[5] == 0x00 && 626328674Smav walker[6] == 0x00 && 627328674Smav walker[7] == 0x00) { 628328674Smav walker += 8; 629328674Smav walker += 4; /* Skip reserved */ 630328674Smav nand = le32dec(walker); 631328674Smav walker += 4; 632328674Smav printf(" %-30s: %d\n", "Retirement number", code); 633328674Smav printf(" %-28s: %#x\n", "NAND (C/T)BBBPPP", nand); 634328674Smav } else { 635328674Smav printf("Parameter %#x entry corrupt\n", code); 636328674Smav walker += 16; 637328674Smav } 638328674Smav } 639328674Smav} 640328674Smav 641328674Smavstatic void 642328674Smavprint_hgst_info_erase_errors(void *buf, uint16_t subtype __unused, uint8_t res __unused, uint32_t size) 643328674Smav{ 644328674Smav static struct kv_name kv[] = 645328674Smav { 646328674Smav { 0x0000, "Corrected Without Delay" }, 647328674Smav { 0x0001, "Corrected Maybe Delayed" }, 648328674Smav { 0x0002, "Re-Erase" }, 649328674Smav { 0x0003, "Errors Corrected" }, 650328674Smav { 0x0004, "Correct Algorithm Used" }, 651328674Smav { 0x0005, "Bytes Processed" }, 652328674Smav { 0x0006, "Uncorrected Errors" }, 653328674Smav { 0x8000, "Flash Erase Commands" }, 654328674Smav { 0x8001, "Mfg Defect Count" }, 655328674Smav { 0x8002, "Grown Defect Count" }, 656328674Smav { 0x8003, "Erase Count -- User" }, 657328674Smav { 0x8004, "Erase Count -- System" }, 658328674Smav }; 659328674Smav 660328674Smav printf("Erase Errors Subpage:\n"); 661328674Smav print_hgst_info_subpage_gen(buf, subtype, size, kv, nitems(kv)); 662328674Smav} 663328674Smav 664328674Smavstatic void 665328674Smavprint_hgst_info_erase_counts(void *buf, uint16_t subtype, uint8_t res __unused, uint32_t size) 666328674Smav{ 667328674Smav /* My drive doesn't export this -- so not coding up */ 668328674Smav printf("XXX: Erase counts subpage: %p, %#x %d\n", buf, subtype, size); 669328674Smav} 670328674Smav 671328674Smavstatic void 672328674Smavprint_hgst_info_temp_history(void *buf, uint16_t subtype __unused, uint8_t res __unused, uint32_t size __unused) 673328674Smav{ 674328674Smav uint8_t *walker = buf; 675328674Smav uint32_t min; 676328674Smav 677328674Smav printf("Temperature History:\n"); 678328674Smav printf(" %-30s: %d C\n", "Current Temperature", *walker++); 679328674Smav printf(" %-30s: %d C\n", "Reference Temperature", *walker++); 680328674Smav printf(" %-30s: %d C\n", "Maximum Temperature", *walker++); 681328674Smav printf(" %-30s: %d C\n", "Minimum Temperature", *walker++); 682328674Smav min = le32dec(walker); 683328674Smav walker += 4; 684328723Smav printf(" %-30s: %d:%02d:00\n", "Max Temperature Time", min / 60, min % 60); 685328674Smav min = le32dec(walker); 686328674Smav walker += 4; 687328723Smav printf(" %-30s: %d:%02d:00\n", "Over Temperature Duration", min / 60, min % 60); 688328674Smav min = le32dec(walker); 689328674Smav walker += 4; 690328723Smav printf(" %-30s: %d:%02d:00\n", "Min Temperature Time", min / 60, min % 60); 691328674Smav} 692328674Smav 693328674Smavstatic void 694328674Smavprint_hgst_info_ssd_perf(void *buf, uint16_t subtype __unused, uint8_t res, uint32_t size __unused) 695328674Smav{ 696328674Smav uint8_t *walker = buf; 697328674Smav uint64_t val; 698328674Smav 699328674Smav printf("SSD Performance Subpage Type %d:\n", res); 700328674Smav val = le64dec(walker); 701328674Smav walker += 8; 702328674Smav printf(" %-30s: %ju\n", "Host Read Commands", val); 703328674Smav val = le64dec(walker); 704328674Smav walker += 8; 705328674Smav printf(" %-30s: %ju\n", "Host Read Blocks", val); 706328674Smav val = le64dec(walker); 707328674Smav walker += 8; 708328674Smav printf(" %-30s: %ju\n", "Host Cache Read Hits Commands", val); 709328674Smav val = le64dec(walker); 710328674Smav walker += 8; 711328674Smav printf(" %-30s: %ju\n", "Host Cache Read Hits Blocks", val); 712328674Smav val = le64dec(walker); 713328674Smav walker += 8; 714328674Smav printf(" %-30s: %ju\n", "Host Read Commands Stalled", val); 715328674Smav val = le64dec(walker); 716328674Smav walker += 8; 717328674Smav printf(" %-30s: %ju\n", "Host Write Commands", val); 718328674Smav val = le64dec(walker); 719328674Smav walker += 8; 720328674Smav printf(" %-30s: %ju\n", "Host Write Blocks", val); 721328674Smav val = le64dec(walker); 722328674Smav walker += 8; 723328674Smav printf(" %-30s: %ju\n", "Host Write Odd Start Commands", val); 724328674Smav val = le64dec(walker); 725328674Smav walker += 8; 726328674Smav printf(" %-30s: %ju\n", "Host Write Odd End Commands", val); 727328674Smav val = le64dec(walker); 728328674Smav walker += 8; 729328674Smav printf(" %-30s: %ju\n", "Host Write Commands Stalled", val); 730328674Smav val = le64dec(walker); 731328674Smav walker += 8; 732328674Smav printf(" %-30s: %ju\n", "NAND Read Commands", val); 733328674Smav val = le64dec(walker); 734328674Smav walker += 8; 735328674Smav printf(" %-30s: %ju\n", "NAND Read Blocks", val); 736328674Smav val = le64dec(walker); 737328674Smav walker += 8; 738328674Smav printf(" %-30s: %ju\n", "NAND Write Commands", val); 739328674Smav val = le64dec(walker); 740328674Smav walker += 8; 741328674Smav printf(" %-30s: %ju\n", "NAND Write Blocks", val); 742328674Smav val = le64dec(walker); 743328674Smav walker += 8; 744328674Smav printf(" %-30s: %ju\n", "NAND Read Before Writes", val); 745328674Smav} 746328674Smav 747328674Smavstatic void 748328674Smavprint_hgst_info_firmware_load(void *buf, uint16_t subtype __unused, uint8_t res __unused, uint32_t size __unused) 749328674Smav{ 750328674Smav uint8_t *walker = buf; 751328674Smav 752328674Smav printf("Firmware Load Subpage:\n"); 753328674Smav printf(" %-30s: %d\n", "Firmware Downloads", le32dec(walker)); 754328674Smav} 755328674Smav 756328674Smavstatic void 757328674Smavkv_indirect(void *buf, uint32_t subtype, uint8_t res, uint32_t size, struct subpage_print *sp, size_t nsp) 758328674Smav{ 759328674Smav size_t i; 760328674Smav 761328674Smav for (i = 0; i < nsp; i++, sp++) { 762328674Smav if (sp->key == subtype) { 763328674Smav sp->fn(buf, subtype, res, size); 764328674Smav return; 765328674Smav } 766328674Smav } 767328674Smav printf("No handler for page type %x\n", subtype); 768328674Smav} 769328674Smav 770328674Smavstatic void 771328741Smavprint_hgst_info_log(const struct nvme_controller_data *cdata __unused, void *buf, uint32_t size __unused) 772328674Smav{ 773328674Smav uint8_t *walker, *end, *subpage; 774328674Smav int pages; 775328674Smav uint16_t len; 776328674Smav uint8_t subtype, res; 777328674Smav 778328674Smav printf("HGST Extra Info Log\n"); 779328674Smav printf("===================\n"); 780328674Smav 781328674Smav walker = buf; 782328674Smav pages = *walker++; 783328674Smav walker++; 784328674Smav len = le16dec(walker); 785328674Smav walker += 2; 786328674Smav end = walker + len; /* Length is exclusive of this header */ 787328674Smav 788328674Smav while (walker < end) { 789328674Smav subpage = walker + 4; 790328674Smav subtype = *walker++ & 0x3f; /* subtype */ 791328674Smav res = *walker++; /* Reserved */ 792328674Smav len = le16dec(walker); 793328674Smav walker += len + 2; /* Length, not incl header */ 794328674Smav if (walker > end) { 795328674Smav printf("Ooops! Off the end of the list\n"); 796328674Smav break; 797328674Smav } 798328674Smav kv_indirect(subpage, subtype, res, len, hgst_subpage, nitems(hgst_subpage)); 799328674Smav } 800328674Smav} 801328674Smav 802328674Smav/* 803328673Smav * Table of log page printer / sizing. 804328673Smav * 805328725Smav * This includes Intel specific pages that are widely implemented. 806328725Smav * Make sure you keep all the pages of one vendor together so -v help 807328725Smav * lists all the vendors pages. 808328673Smav */ 809252302Sglebiusstatic struct logpage_function { 810252277Sjimharris uint8_t log_page; 811328710Smav const char *vendor; 812328725Smav const char *name; 813328672Smav print_fn_t print_fn; 814328672Smav size_t size; 815252277Sjimharris} logfuncs[] = { 816328725Smav {NVME_LOG_ERROR, NULL, "Drive Error Log", 817328725Smav print_log_error, 0}, 818328725Smav {NVME_LOG_HEALTH_INFORMATION, NULL, "Health/SMART Data", 819328725Smav print_log_health, sizeof(struct nvme_health_information_page)}, 820328725Smav {NVME_LOG_FIRMWARE_SLOT, NULL, "Firmware Information", 821328725Smav print_log_firmware, sizeof(struct nvme_firmware_page)}, 822328725Smav {HGST_INFO_LOG, "hgst", "Detailed Health/SMART", 823328725Smav print_hgst_info_log, DEFAULT_SIZE}, 824328725Smav {HGST_INFO_LOG, "wds", "Detailed Health/SMART", 825328725Smav print_hgst_info_log, DEFAULT_SIZE}, 826328725Smav {INTEL_LOG_TEMP_STATS, "intel", "Temperature Stats", 827328725Smav print_intel_temp_stats, sizeof(struct intel_log_temp_stats)}, 828328725Smav {INTEL_LOG_READ_LAT_LOG, "intel", "Read Latencies", 829328725Smav print_intel_read_lat_log, DEFAULT_SIZE}, 830328725Smav {INTEL_LOG_WRITE_LAT_LOG, "intel", "Write Latencies", 831328725Smav print_intel_write_lat_log, DEFAULT_SIZE}, 832328725Smav {INTEL_LOG_ADD_SMART, "intel", "Extra Health/SMART Data", 833328725Smav print_intel_add_smart, DEFAULT_SIZE}, 834328725Smav {INTEL_LOG_ADD_SMART, "samsung", "Extra Health/SMART Data", 835328725Smav print_intel_add_smart, DEFAULT_SIZE}, 836328725Smav 837328725Smav {0, NULL, NULL, NULL, 0}, 838252277Sjimharris}; 839252277Sjimharris 840252277Sjimharrisstatic void 841252277Sjimharrislogpage_usage(void) 842252277Sjimharris{ 843252277Sjimharris fprintf(stderr, "usage:\n"); 844252277Sjimharris fprintf(stderr, LOGPAGE_USAGE); 845253109Sjimharris exit(1); 846252277Sjimharris} 847252277Sjimharris 848328725Smavstatic void 849328725Smavlogpage_help(void) 850328725Smav{ 851328725Smav struct logpage_function *f; 852328725Smav const char *v; 853328725Smav 854328725Smav fprintf(stderr, "\n"); 855328725Smav fprintf(stderr, "%-8s %-10s %s\n", "Page", "Vendor","Page Name"); 856328725Smav fprintf(stderr, "-------- ---------- ----------\n"); 857328725Smav for (f = logfuncs; f->log_page > 0; f++) { 858328725Smav v = f->vendor == NULL ? "-" : f->vendor; 859328725Smav fprintf(stderr, "0x%02x %-10s %s\n", f->log_page, v, f->name); 860328725Smav } 861328725Smav 862328725Smav exit(1); 863328725Smav} 864328725Smav 865252277Sjimharrisvoid 866252277Sjimharrislogpage(int argc, char *argv[]) 867252277Sjimharris{ 868253114Sjimharris int fd, nsid; 869252277Sjimharris int log_page = 0, pageflag = false; 870328721Smav int binflag = false, hexflag = false, ns_specified; 871253114Sjimharris char ch, *p; 872253114Sjimharris char cname[64]; 873253109Sjimharris uint32_t size; 874252277Sjimharris void *buf; 875328710Smav const char *vendor = NULL; 876252277Sjimharris struct logpage_function *f; 877252277Sjimharris struct nvme_controller_data cdata; 878252277Sjimharris print_fn_t print_fn; 879252277Sjimharris 880328721Smav while ((ch = getopt(argc, argv, "bp:xv:")) != -1) { 881252277Sjimharris switch (ch) { 882328721Smav case 'b': 883328721Smav binflag = true; 884328721Smav break; 885252277Sjimharris case 'p': 886328725Smav if (strcmp(optarg, "help") == 0) 887328725Smav logpage_help(); 888328725Smav 889252277Sjimharris /* TODO: Add human-readable ASCII page IDs */ 890252277Sjimharris log_page = strtol(optarg, &p, 0); 891252277Sjimharris if (p != NULL && *p != '\0') { 892252277Sjimharris fprintf(stderr, 893252277Sjimharris "\"%s\" not valid log page id.\n", 894252277Sjimharris optarg); 895252277Sjimharris logpage_usage(); 896252277Sjimharris } 897252277Sjimharris pageflag = true; 898252277Sjimharris break; 899252277Sjimharris case 'x': 900252277Sjimharris hexflag = true; 901252277Sjimharris break; 902328710Smav case 'v': 903328725Smav if (strcmp(optarg, "help") == 0) 904328725Smav logpage_help(); 905328710Smav vendor = optarg; 906328710Smav break; 907252277Sjimharris } 908252277Sjimharris } 909252277Sjimharris 910252277Sjimharris if (!pageflag) { 911252277Sjimharris printf("Missing page_id (-p).\n"); 912252277Sjimharris logpage_usage(); 913252277Sjimharris } 914252277Sjimharris 915252277Sjimharris /* Check that a controller and/or namespace was specified. */ 916252277Sjimharris if (optind >= argc) 917252277Sjimharris logpage_usage(); 918252277Sjimharris 919253114Sjimharris if (strstr(argv[optind], NVME_NS_PREFIX) != NULL) { 920253114Sjimharris ns_specified = true; 921253114Sjimharris parse_ns_str(argv[optind], cname, &nsid); 922253114Sjimharris open_dev(cname, &fd, 1, 1); 923253114Sjimharris } else { 924253114Sjimharris ns_specified = false; 925253114Sjimharris nsid = NVME_GLOBAL_NAMESPACE_TAG; 926253114Sjimharris open_dev(argv[optind], &fd, 1, 1); 927253114Sjimharris } 928253114Sjimharris 929285796Sjimharris read_controller_data(fd, &cdata); 930285796Sjimharris 931252277Sjimharris /* 932252277Sjimharris * The log page attribtues indicate whether or not the controller 933252277Sjimharris * supports the SMART/Health information log page on a per 934252277Sjimharris * namespace basis. 935252277Sjimharris */ 936253114Sjimharris if (ns_specified) { 937253114Sjimharris if (log_page != NVME_LOG_HEALTH_INFORMATION) 938253114Sjimharris errx(1, "log page %d valid only at controller level", 939253114Sjimharris log_page); 940253114Sjimharris if (cdata.lpa.ns_smart == 0) 941253114Sjimharris errx(1, 942253114Sjimharris "controller does not support per namespace " 943253114Sjimharris "smart/health information"); 944253114Sjimharris } 945252277Sjimharris 946328741Smav print_fn = print_log_hex; 947328672Smav size = DEFAULT_SIZE; 948328721Smav if (binflag) 949328721Smav print_fn = print_bin; 950328721Smav if (!binflag && !hexflag) { 951252277Sjimharris /* 952328710Smav * See if there is a pretty print function for the specified log 953328710Smav * page. If one isn't found, we just revert to the default 954328710Smav * (print_hex). If there was a vendor specified bt the user, and 955328710Smav * the page is vendor specific, don't match the print function 956328710Smav * unless the vendors match. 957252277Sjimharris */ 958328710Smav for (f = logfuncs; f->log_page > 0; f++) { 959328710Smav if (f->vendor != NULL && vendor != NULL && 960328710Smav strcmp(f->vendor, vendor) != 0) 961328710Smav continue; 962328710Smav if (log_page != f->log_page) 963328710Smav continue; 964328710Smav print_fn = f->print_fn; 965328710Smav size = f->size; 966328710Smav break; 967252277Sjimharris } 968252277Sjimharris } 969252277Sjimharris 970328672Smav if (log_page == NVME_LOG_ERROR) { 971252277Sjimharris size = sizeof(struct nvme_error_information_entry); 972252277Sjimharris size *= (cdata.elpe + 1); 973252277Sjimharris } 974252277Sjimharris 975328672Smav /* Read the log page */ 976252277Sjimharris buf = get_log_buffer(size); 977252277Sjimharris read_logpage(fd, log_page, nsid, buf, size); 978328741Smav print_fn(&cdata, buf, size); 979252277Sjimharris 980252277Sjimharris close(fd); 981253109Sjimharris exit(0); 982252277Sjimharris} 983