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$"); 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> 45252277Sjimharris 46252277Sjimharris#include "nvmecontrol.h" 47252277Sjimharris 48252277Sjimharris#define DEFAULT_SIZE (4096) 49252277Sjimharris#define MAX_FW_SLOTS (7) 50252277Sjimharris 51252277Sjimharristypedef void (*print_fn_t)(void *buf, uint32_t size); 52252277Sjimharris 53252277Sjimharrisstatic void * 54253109Sjimharrisget_log_buffer(uint32_t size) 55252277Sjimharris{ 56252277Sjimharris void *buf; 57252277Sjimharris 58253109Sjimharris if ((buf = malloc(size)) == NULL) 59253109Sjimharris errx(1, "unable to malloc %u bytes", size); 60253109Sjimharris 61252277Sjimharris memset(buf, 0, size); 62252277Sjimharris return (buf); 63252277Sjimharris} 64252277Sjimharris 65252277Sjimharrisvoid 66252277Sjimharrisread_logpage(int fd, uint8_t log_page, int nsid, void *payload, 67252277Sjimharris uint32_t payload_size) 68252277Sjimharris{ 69252277Sjimharris struct nvme_pt_command pt; 70252277Sjimharris 71252277Sjimharris memset(&pt, 0, sizeof(pt)); 72252277Sjimharris pt.cmd.opc = NVME_OPC_GET_LOG_PAGE; 73252277Sjimharris pt.cmd.nsid = nsid; 74252277Sjimharris pt.cmd.cdw10 = ((payload_size/sizeof(uint32_t)) - 1) << 16; 75252277Sjimharris pt.cmd.cdw10 |= log_page; 76252277Sjimharris pt.buf = payload; 77252277Sjimharris pt.len = payload_size; 78252277Sjimharris pt.is_read = 1; 79252277Sjimharris 80253109Sjimharris if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0) 81253109Sjimharris err(1, "get log page request failed"); 82252277Sjimharris 83253109Sjimharris if (nvme_completion_is_error(&pt.cpl)) 84253109Sjimharris errx(1, "get log page request returned error"); 85252277Sjimharris} 86252277Sjimharris 87252277Sjimharrisstatic void 88252277Sjimharrisprint_log_error(void *buf, uint32_t size) 89252277Sjimharris{ 90252277Sjimharris int i, nentries; 91252277Sjimharris struct nvme_error_information_entry *entry = buf; 92252277Sjimharris struct nvme_status *status; 93252277Sjimharris 94252277Sjimharris printf("Error Information Log\n"); 95252277Sjimharris printf("=====================\n"); 96252277Sjimharris 97252277Sjimharris if (entry->error_count == 0) { 98252277Sjimharris printf("No error entries found\n"); 99252277Sjimharris return; 100252277Sjimharris } 101252277Sjimharris 102252277Sjimharris nentries = size/sizeof(struct nvme_error_information_entry); 103252277Sjimharris for (i = 0; i < nentries; i++, entry++) { 104252277Sjimharris if (entry->error_count == 0) 105252277Sjimharris break; 106252277Sjimharris 107252277Sjimharris status = &entry->status; 108252277Sjimharris printf("Entry %02d\n", i + 1); 109252277Sjimharris printf("=========\n"); 110252277Sjimharris printf(" Error count: %ju\n", entry->error_count); 111252277Sjimharris printf(" Submission queue ID: %u\n", entry->sqid); 112252277Sjimharris printf(" Command ID: %u\n", entry->cid); 113252277Sjimharris /* TODO: Export nvme_status_string structures from kernel? */ 114252277Sjimharris printf(" Status:\n"); 115252277Sjimharris printf(" Phase tag: %d\n", status->p); 116252277Sjimharris printf(" Status code: %d\n", status->sc); 117252277Sjimharris printf(" Status code type: %d\n", status->sct); 118252277Sjimharris printf(" More: %d\n", status->m); 119252277Sjimharris printf(" DNR: %d\n", status->dnr); 120252277Sjimharris printf(" Error location: %u\n", entry->error_location); 121252277Sjimharris printf(" LBA: %ju\n", entry->lba); 122252277Sjimharris printf(" Namespace ID: %u\n", entry->nsid); 123252277Sjimharris printf(" Vendor specific info: %u\n", entry->vendor_specific); 124252277Sjimharris } 125252277Sjimharris} 126252277Sjimharris 127252277Sjimharrisstatic void 128252277Sjimharrisprint_log_health(void *buf, uint32_t size __unused) 129252277Sjimharris{ 130252277Sjimharris struct nvme_health_information_page *health = buf; 131252277Sjimharris 132252277Sjimharris printf("SMART/Health Information Log\n"); 133252277Sjimharris printf("============================\n"); 134252277Sjimharris 135252277Sjimharris printf("Critical Warning State: 0x%02x\n", 136252277Sjimharris health->critical_warning.raw); 137252277Sjimharris printf(" Available spare: %d\n", 138252277Sjimharris health->critical_warning.bits.available_spare); 139252277Sjimharris printf(" Temperature: %d\n", 140252277Sjimharris health->critical_warning.bits.temperature); 141252277Sjimharris printf(" Device reliability: %d\n", 142252277Sjimharris health->critical_warning.bits.device_reliability); 143252277Sjimharris printf(" Read only: %d\n", 144252277Sjimharris health->critical_warning.bits.read_only); 145252277Sjimharris printf(" Volatile memory backup: %d\n", 146252277Sjimharris health->critical_warning.bits.volatile_memory_backup); 147252277Sjimharris printf("Temperature: %u K, %2.2f C, %3.2f F\n", 148252277Sjimharris health->temperature, 149252277Sjimharris (float)health->temperature - (float)273.15, 150252277Sjimharris ((float)health->temperature * (float)9/5) - (float)459.67); 151252277Sjimharris printf("Available spare: %u\n", 152252277Sjimharris health->available_spare); 153252277Sjimharris printf("Available spare threshold: %u\n", 154252277Sjimharris health->available_spare_threshold); 155252277Sjimharris printf("Percentage used: %u\n", 156252277Sjimharris health->percentage_used); 157252277Sjimharris 158252277Sjimharris /* 159252277Sjimharris * TODO: These are pretty ugly in hex. Is there a library that 160252277Sjimharris * will convert 128-bit unsigned values to decimal? 161252277Sjimharris */ 162252277Sjimharris printf("Data units (512 byte) read: 0x%016jx%016jx\n", 163252277Sjimharris health->data_units_read[1], 164252277Sjimharris health->data_units_read[0]); 165252277Sjimharris printf("Data units (512 byte) written: 0x%016jx%016jx\n", 166252277Sjimharris health->data_units_written[1], 167252277Sjimharris health->data_units_written[0]); 168252277Sjimharris printf("Host read commands: 0x%016jx%016jx\n", 169252277Sjimharris health->host_read_commands[1], 170252277Sjimharris health->host_read_commands[0]); 171252277Sjimharris printf("Host write commands: 0x%016jx%016jx\n", 172252277Sjimharris health->host_write_commands[1], 173252277Sjimharris health->host_write_commands[0]); 174252277Sjimharris printf("Controller busy time (minutes): 0x%016jx%016jx\n", 175252277Sjimharris health->controller_busy_time[1], 176252277Sjimharris health->controller_busy_time[0]); 177252277Sjimharris printf("Power cycles: 0x%016jx%016jx\n", 178252277Sjimharris health->power_cycles[1], 179252277Sjimharris health->power_cycles[0]); 180252277Sjimharris printf("Power on hours: 0x%016jx%016jx\n", 181252277Sjimharris health->power_on_hours[1], 182252277Sjimharris health->power_on_hours[0]); 183252277Sjimharris printf("Unsafe shutdowns: 0x%016jx%016jx\n", 184252277Sjimharris health->unsafe_shutdowns[1], 185252277Sjimharris health->unsafe_shutdowns[0]); 186252277Sjimharris printf("Media errors: 0x%016jx%016jx\n", 187252277Sjimharris health->media_errors[1], 188252277Sjimharris health->media_errors[0]); 189252277Sjimharris printf("No. error info log entries: 0x%016jx%016jx\n", 190252277Sjimharris health->num_error_info_log_entries[1], 191252277Sjimharris health->num_error_info_log_entries[0]); 192252277Sjimharris} 193252277Sjimharris 194252277Sjimharrisstatic void 195252277Sjimharrisprint_log_firmware(void *buf, uint32_t size __unused) 196252277Sjimharris{ 197252277Sjimharris int i; 198252277Sjimharris const char *status; 199252277Sjimharris struct nvme_firmware_page *fw = buf; 200252277Sjimharris 201252277Sjimharris printf("Firmware Slot Log\n"); 202252277Sjimharris printf("=================\n"); 203252277Sjimharris 204252277Sjimharris for (i = 0; i < MAX_FW_SLOTS; i++) { 205252277Sjimharris printf("Slot %d: ", i + 1); 206252277Sjimharris if (fw->afi.slot == i + 1) 207252277Sjimharris status = " Active"; 208252277Sjimharris else 209252277Sjimharris status = "Inactive"; 210252277Sjimharris 211252277Sjimharris if (fw->revision[i] == 0LLU) 212252277Sjimharris printf("Empty\n"); 213252277Sjimharris else 214252277Sjimharris if (isprint(*(char *)&fw->revision[i])) 215252277Sjimharris printf("[%s] %.8s\n", status, 216252277Sjimharris (char *)&fw->revision[i]); 217252277Sjimharris else 218252277Sjimharris printf("[%s] %016jx\n", status, 219252277Sjimharris fw->revision[i]); 220252277Sjimharris } 221252277Sjimharris} 222252277Sjimharris 223252302Sglebiusstatic struct logpage_function { 224252277Sjimharris uint8_t log_page; 225252277Sjimharris print_fn_t fn; 226252277Sjimharris} logfuncs[] = { 227252277Sjimharris {NVME_LOG_ERROR, print_log_error }, 228252277Sjimharris {NVME_LOG_HEALTH_INFORMATION, print_log_health }, 229252277Sjimharris {NVME_LOG_FIRMWARE_SLOT, print_log_firmware }, 230252277Sjimharris {0, NULL }, 231252277Sjimharris}; 232252277Sjimharris 233252277Sjimharrisstatic void 234252277Sjimharrislogpage_usage(void) 235252277Sjimharris{ 236252277Sjimharris fprintf(stderr, "usage:\n"); 237252277Sjimharris fprintf(stderr, LOGPAGE_USAGE); 238253109Sjimharris exit(1); 239252277Sjimharris} 240252277Sjimharris 241252277Sjimharrisvoid 242252277Sjimharrislogpage(int argc, char *argv[]) 243252277Sjimharris{ 244253114Sjimharris int fd, nsid; 245252277Sjimharris int log_page = 0, pageflag = false; 246253114Sjimharris int hexflag = false, ns_specified; 247253114Sjimharris char ch, *p; 248253114Sjimharris char cname[64]; 249253109Sjimharris uint32_t size; 250252277Sjimharris void *buf; 251252277Sjimharris struct logpage_function *f; 252252277Sjimharris struct nvme_controller_data cdata; 253252277Sjimharris print_fn_t print_fn; 254252277Sjimharris 255252277Sjimharris while ((ch = getopt(argc, argv, "p:x")) != -1) { 256252277Sjimharris switch (ch) { 257252277Sjimharris case 'p': 258252277Sjimharris /* TODO: Add human-readable ASCII page IDs */ 259252277Sjimharris log_page = strtol(optarg, &p, 0); 260252277Sjimharris if (p != NULL && *p != '\0') { 261252277Sjimharris fprintf(stderr, 262252277Sjimharris "\"%s\" not valid log page id.\n", 263252277Sjimharris optarg); 264252277Sjimharris logpage_usage(); 265252277Sjimharris /* TODO: Define valid log page id ranges in nvme.h? */ 266252277Sjimharris } else if (log_page == 0 || 267252277Sjimharris (log_page >= 0x04 && log_page <= 0x7F) || 268252277Sjimharris (log_page >= 0x80 && log_page <= 0xBF)) { 269252277Sjimharris fprintf(stderr, 270252277Sjimharris "\"%s\" not valid log page id.\n", 271252277Sjimharris optarg); 272252277Sjimharris logpage_usage(); 273252277Sjimharris } 274252277Sjimharris pageflag = true; 275252277Sjimharris break; 276252277Sjimharris case 'x': 277252277Sjimharris hexflag = true; 278252277Sjimharris break; 279252277Sjimharris } 280252277Sjimharris } 281252277Sjimharris 282252277Sjimharris if (!pageflag) { 283252277Sjimharris printf("Missing page_id (-p).\n"); 284252277Sjimharris logpage_usage(); 285252277Sjimharris } 286252277Sjimharris 287252277Sjimharris /* Check that a controller and/or namespace was specified. */ 288252277Sjimharris if (optind >= argc) 289252277Sjimharris logpage_usage(); 290252277Sjimharris 291253114Sjimharris if (strstr(argv[optind], NVME_NS_PREFIX) != NULL) { 292253114Sjimharris ns_specified = true; 293253114Sjimharris parse_ns_str(argv[optind], cname, &nsid); 294253114Sjimharris open_dev(cname, &fd, 1, 1); 295253114Sjimharris } else { 296253114Sjimharris ns_specified = false; 297253114Sjimharris nsid = NVME_GLOBAL_NAMESPACE_TAG; 298253114Sjimharris open_dev(argv[optind], &fd, 1, 1); 299253114Sjimharris } 300253114Sjimharris 301252277Sjimharris /* 302252277Sjimharris * The log page attribtues indicate whether or not the controller 303252277Sjimharris * supports the SMART/Health information log page on a per 304252277Sjimharris * namespace basis. 305252277Sjimharris */ 306253114Sjimharris if (ns_specified) { 307253114Sjimharris if (log_page != NVME_LOG_HEALTH_INFORMATION) 308253114Sjimharris errx(1, "log page %d valid only at controller level", 309253114Sjimharris log_page); 310253114Sjimharris read_controller_data(fd, &cdata); 311253114Sjimharris if (cdata.lpa.ns_smart == 0) 312253114Sjimharris errx(1, 313253114Sjimharris "controller does not support per namespace " 314253114Sjimharris "smart/health information"); 315253114Sjimharris } 316252277Sjimharris 317252277Sjimharris print_fn = print_hex; 318252277Sjimharris if (!hexflag) { 319252277Sjimharris /* 320252277Sjimharris * See if there is a pretty print function for the 321252277Sjimharris * specified log page. If one isn't found, we 322252277Sjimharris * just revert to the default (print_hex). 323252277Sjimharris */ 324252277Sjimharris f = logfuncs; 325252277Sjimharris while (f->log_page > 0) { 326252277Sjimharris if (log_page == f->log_page) { 327252277Sjimharris print_fn = f->fn; 328252277Sjimharris break; 329252277Sjimharris } 330252277Sjimharris f++; 331252277Sjimharris } 332252277Sjimharris } 333252277Sjimharris 334252277Sjimharris /* Read the log page */ 335252277Sjimharris switch (log_page) { 336252277Sjimharris case NVME_LOG_ERROR: 337252277Sjimharris size = sizeof(struct nvme_error_information_entry); 338252277Sjimharris size *= (cdata.elpe + 1); 339252277Sjimharris break; 340252277Sjimharris case NVME_LOG_HEALTH_INFORMATION: 341252277Sjimharris size = sizeof(struct nvme_health_information_page); 342252277Sjimharris break; 343252277Sjimharris case NVME_LOG_FIRMWARE_SLOT: 344252277Sjimharris size = sizeof(struct nvme_firmware_page); 345252277Sjimharris break; 346252277Sjimharris default: 347252277Sjimharris size = DEFAULT_SIZE; 348252277Sjimharris break; 349252277Sjimharris } 350252277Sjimharris 351252277Sjimharris buf = get_log_buffer(size); 352252277Sjimharris read_logpage(fd, log_page, nsid, buf, size); 353252277Sjimharris print_fn(buf, size); 354252277Sjimharris 355252277Sjimharris close(fd); 356253109Sjimharris exit(0); 357252277Sjimharris} 358