1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 * 4 * Copyright (c) 2013 EMC Corp. 5 * All rights reserved. 6 * 7 * Copyright (C) 2012-2013 Intel Corporation 8 * All rights reserved. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 */ 31 32 #include <sys/cdefs.h> 33 __FBSDID("$FreeBSD$"); 34 35 #include <sys/param.h> 36 #include <sys/ioccom.h> 37 38 #include <ctype.h> 39 #include <err.h> 40 #include <fcntl.h> 41 #include <stdbool.h> 42 #include <stddef.h> 43 #include <stdio.h> 44 #include <stdlib.h> 45 #include <string.h> 46 #include <unistd.h> 47 #include <sys/endian.h> 48 49 #include "nvmecontrol.h" 50 51 #define LOGPAGE_USAGE \ 52 "logpage <-p page_id> [-b] [-v vendor] [-x] <controller id|namespace id>\n" \ 53 54 #define MAX_FW_SLOTS (7) 55 56 static SLIST_HEAD(,logpage_function) logpages; 57 58 void 59 logpage_register(struct logpage_function *p) 60 { 61 62 SLIST_INSERT_HEAD(&logpages, p, link); 63 } 64 65 const char * 66 kv_lookup(const struct kv_name *kv, size_t kv_count, uint32_t key) 67 { 68 static char bad[32]; 69 size_t i; 70 71 for (i = 0; i < kv_count; i++, kv++) 72 if (kv->key == key) 73 return kv->name; 74 snprintf(bad, sizeof(bad), "Attribute %#x", key); 75 return bad; 76 } 77 78 static void 79 print_log_hex(const struct nvme_controller_data *cdata __unused, void *data, uint32_t length) 80 { 81 82 print_hex(data, length); 83 } 84 85 static void 86 print_bin(const struct nvme_controller_data *cdata __unused, void *data, uint32_t length) 87 { 88 89 write(STDOUT_FILENO, data, length); 90 } 91 92 static void * 93 get_log_buffer(uint32_t size) 94 { 95 void *buf; 96 97 if ((buf = malloc(size)) == NULL) 98 errx(1, "unable to malloc %u bytes", size); 99 100 memset(buf, 0, size); 101 return (buf); 102 } 103 104 void 105 read_logpage(int fd, uint8_t log_page, uint32_t nsid, void *payload, 106 uint32_t payload_size) 107 { 108 struct nvme_pt_command pt; 109 struct nvme_error_information_entry *err_entry; 110 int i, err_pages; 111 112 memset(&pt, 0, sizeof(pt)); 113 pt.cmd.opc = NVME_OPC_GET_LOG_PAGE; 114 pt.cmd.nsid = htole32(nsid); 115 pt.cmd.cdw10 = ((payload_size/sizeof(uint32_t)) - 1) << 16; 116 pt.cmd.cdw10 |= log_page; 117 pt.cmd.cdw10 = htole32(pt.cmd.cdw10); 118 pt.buf = payload; 119 pt.len = payload_size; 120 pt.is_read = 1; 121 122 if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0) 123 err(1, "get log page request failed"); 124 125 /* Convert data to host endian */ 126 switch (log_page) { 127 case NVME_LOG_ERROR: 128 err_entry = (struct nvme_error_information_entry *)payload; 129 err_pages = payload_size / sizeof(struct nvme_error_information_entry); 130 for (i = 0; i < err_pages; i++) 131 nvme_error_information_entry_swapbytes(err_entry++); 132 break; 133 case NVME_LOG_HEALTH_INFORMATION: 134 nvme_health_information_page_swapbytes( 135 (struct nvme_health_information_page *)payload); 136 break; 137 case NVME_LOG_FIRMWARE_SLOT: 138 nvme_firmware_page_swapbytes( 139 (struct nvme_firmware_page *)payload); 140 break; 141 case INTEL_LOG_TEMP_STATS: 142 intel_log_temp_stats_swapbytes( 143 (struct intel_log_temp_stats *)payload); 144 break; 145 default: 146 break; 147 } 148 149 if (nvme_completion_is_error(&pt.cpl)) 150 errx(1, "get log page request returned error"); 151 } 152 153 static void 154 print_log_error(const struct nvme_controller_data *cdata __unused, void *buf, uint32_t size) 155 { 156 int i, nentries; 157 uint16_t status; 158 uint8_t p, sc, sct, m, dnr; 159 struct nvme_error_information_entry *entry = buf; 160 161 printf("Error Information Log\n"); 162 printf("=====================\n"); 163 164 if (entry->error_count == 0) { 165 printf("No error entries found\n"); 166 return; 167 } 168 169 nentries = size/sizeof(struct nvme_error_information_entry); 170 for (i = 0; i < nentries; i++, entry++) { 171 if (entry->error_count == 0) 172 break; 173 174 status = entry->status; 175 176 p = NVME_STATUS_GET_P(status); 177 sc = NVME_STATUS_GET_SC(status); 178 sct = NVME_STATUS_GET_SCT(status); 179 m = NVME_STATUS_GET_M(status); 180 dnr = NVME_STATUS_GET_DNR(status); 181 182 printf("Entry %02d\n", i + 1); 183 printf("=========\n"); 184 printf(" Error count: %ju\n", entry->error_count); 185 printf(" Submission queue ID: %u\n", entry->sqid); 186 printf(" Command ID: %u\n", entry->cid); 187 /* TODO: Export nvme_status_string structures from kernel? */ 188 printf(" Status:\n"); 189 printf(" Phase tag: %d\n", p); 190 printf(" Status code: %d\n", sc); 191 printf(" Status code type: %d\n", sct); 192 printf(" More: %d\n", m); 193 printf(" DNR: %d\n", dnr); 194 printf(" Error location: %u\n", entry->error_location); 195 printf(" LBA: %ju\n", entry->lba); 196 printf(" Namespace ID: %u\n", entry->nsid); 197 printf(" Vendor specific info: %u\n", entry->vendor_specific); 198 } 199 } 200 201 void 202 print_temp(uint16_t t) 203 { 204 printf("%u K, %2.2f C, %3.2f F\n", t, (float)t - 273.15, (float)t * 9 / 5 - 459.67); 205 } 206 207 208 static void 209 print_log_health(const struct nvme_controller_data *cdata __unused, void *buf, uint32_t size __unused) 210 { 211 struct nvme_health_information_page *health = buf; 212 char cbuf[UINT128_DIG + 1]; 213 uint8_t warning; 214 int i; 215 216 warning = health->critical_warning; 217 218 printf("SMART/Health Information Log\n"); 219 printf("============================\n"); 220 221 printf("Critical Warning State: 0x%02x\n", warning); 222 printf(" Available spare: %d\n", 223 !!(warning & NVME_CRIT_WARN_ST_AVAILABLE_SPARE)); 224 printf(" Temperature: %d\n", 225 !!(warning & NVME_CRIT_WARN_ST_TEMPERATURE)); 226 printf(" Device reliability: %d\n", 227 !!(warning & NVME_CRIT_WARN_ST_DEVICE_RELIABILITY)); 228 printf(" Read only: %d\n", 229 !!(warning & NVME_CRIT_WARN_ST_READ_ONLY)); 230 printf(" Volatile memory backup: %d\n", 231 !!(warning & NVME_CRIT_WARN_ST_VOLATILE_MEMORY_BACKUP)); 232 printf("Temperature: "); 233 print_temp(health->temperature); 234 printf("Available spare: %u\n", 235 health->available_spare); 236 printf("Available spare threshold: %u\n", 237 health->available_spare_threshold); 238 printf("Percentage used: %u\n", 239 health->percentage_used); 240 241 printf("Data units (512,000 byte) read: %s\n", 242 uint128_to_str(to128(health->data_units_read), cbuf, sizeof(cbuf))); 243 printf("Data units written: %s\n", 244 uint128_to_str(to128(health->data_units_written), cbuf, sizeof(cbuf))); 245 printf("Host read commands: %s\n", 246 uint128_to_str(to128(health->host_read_commands), cbuf, sizeof(cbuf))); 247 printf("Host write commands: %s\n", 248 uint128_to_str(to128(health->host_write_commands), cbuf, sizeof(cbuf))); 249 printf("Controller busy time (minutes): %s\n", 250 uint128_to_str(to128(health->controller_busy_time), cbuf, sizeof(cbuf))); 251 printf("Power cycles: %s\n", 252 uint128_to_str(to128(health->power_cycles), cbuf, sizeof(cbuf))); 253 printf("Power on hours: %s\n", 254 uint128_to_str(to128(health->power_on_hours), cbuf, sizeof(cbuf))); 255 printf("Unsafe shutdowns: %s\n", 256 uint128_to_str(to128(health->unsafe_shutdowns), cbuf, sizeof(cbuf))); 257 printf("Media errors: %s\n", 258 uint128_to_str(to128(health->media_errors), cbuf, sizeof(cbuf))); 259 printf("No. error info log entries: %s\n", 260 uint128_to_str(to128(health->num_error_info_log_entries), cbuf, sizeof(cbuf))); 261 262 printf("Warning Temp Composite Time: %d\n", health->warning_temp_time); 263 printf("Error Temp Composite Time: %d\n", health->error_temp_time); 264 for (i = 0; i < 8; i++) { 265 if (health->temp_sensor[i] == 0) 266 continue; 267 printf("Temperature Sensor %d: ", i + 1); 268 print_temp(health->temp_sensor[i]); 269 } 270 } 271 272 static void 273 print_log_firmware(const struct nvme_controller_data *cdata, void *buf, uint32_t size __unused) 274 { 275 int i, slots; 276 const char *status; 277 struct nvme_firmware_page *fw = buf; 278 uint8_t afi_slot; 279 uint16_t oacs_fw; 280 uint8_t fw_num_slots; 281 282 afi_slot = fw->afi >> NVME_FIRMWARE_PAGE_AFI_SLOT_SHIFT; 283 afi_slot &= NVME_FIRMWARE_PAGE_AFI_SLOT_MASK; 284 285 oacs_fw = (cdata->oacs >> NVME_CTRLR_DATA_OACS_FIRMWARE_SHIFT) & 286 NVME_CTRLR_DATA_OACS_FIRMWARE_MASK; 287 fw_num_slots = (cdata->frmw >> NVME_CTRLR_DATA_FRMW_NUM_SLOTS_SHIFT) & 288 NVME_CTRLR_DATA_FRMW_NUM_SLOTS_MASK; 289 290 printf("Firmware Slot Log\n"); 291 printf("=================\n"); 292 293 if (oacs_fw == 0) 294 slots = 1; 295 else 296 slots = MIN(fw_num_slots, MAX_FW_SLOTS); 297 298 for (i = 0; i < slots; i++) { 299 printf("Slot %d: ", i + 1); 300 if (afi_slot == i + 1) 301 status = " Active"; 302 else 303 status = "Inactive"; 304 305 if (fw->revision[i] == 0LLU) 306 printf("Empty\n"); 307 else 308 if (isprint(*(char *)&fw->revision[i])) 309 printf("[%s] %.8s\n", status, 310 (char *)&fw->revision[i]); 311 else 312 printf("[%s] %016jx\n", status, 313 fw->revision[i]); 314 } 315 } 316 317 /* 318 * Table of log page printer / sizing. 319 * 320 * Make sure you keep all the pages of one vendor together so -v help 321 * lists all the vendors pages. 322 */ 323 NVME_LOGPAGE(error, 324 NVME_LOG_ERROR, NULL, "Drive Error Log", 325 print_log_error, 0); 326 NVME_LOGPAGE(health, 327 NVME_LOG_HEALTH_INFORMATION, NULL, "Health/SMART Data", 328 print_log_health, sizeof(struct nvme_health_information_page)); 329 NVME_LOGPAGE(fw, 330 NVME_LOG_FIRMWARE_SLOT, NULL, "Firmware Information", 331 print_log_firmware, sizeof(struct nvme_firmware_page)); 332 333 static void 334 logpage_help(void) 335 { 336 const struct logpage_function *f; 337 const char *v; 338 339 fprintf(stderr, "\n"); 340 fprintf(stderr, "%-8s %-10s %s\n", "Page", "Vendor","Page Name"); 341 fprintf(stderr, "-------- ---------- ----------\n"); 342 SLIST_FOREACH(f, &logpages, link) { 343 v = f->vendor == NULL ? "-" : f->vendor; 344 fprintf(stderr, "0x%02x %-10s %s\n", f->log_page, v, f->name); 345 } 346 347 exit(1); 348 } 349 350 static void 351 logpage(const struct nvme_function *nf, int argc, char *argv[]) 352 { 353 int fd; 354 int log_page = 0, pageflag = false; 355 int binflag = false, hexflag = false, ns_specified; 356 int opt; 357 char *p; 358 char cname[64]; 359 uint32_t nsid, size; 360 void *buf; 361 const char *vendor = NULL; 362 const struct logpage_function *f; 363 struct nvme_controller_data cdata; 364 print_fn_t print_fn; 365 uint8_t ns_smart; 366 367 while ((opt = getopt(argc, argv, "bp:xv:")) != -1) { 368 switch (opt) { 369 case 'b': 370 binflag = true; 371 break; 372 case 'p': 373 if (strcmp(optarg, "help") == 0) 374 logpage_help(); 375 376 /* TODO: Add human-readable ASCII page IDs */ 377 log_page = strtol(optarg, &p, 0); 378 if (p != NULL && *p != '\0') { 379 fprintf(stderr, 380 "\"%s\" not valid log page id.\n", 381 optarg); 382 usage(nf); 383 } 384 pageflag = true; 385 break; 386 case 'x': 387 hexflag = true; 388 break; 389 case 'v': 390 if (strcmp(optarg, "help") == 0) 391 logpage_help(); 392 vendor = optarg; 393 break; 394 } 395 } 396 397 if (!pageflag) { 398 printf("Missing page_id (-p).\n"); 399 usage(nf); 400 } 401 402 /* Check that a controller and/or namespace was specified. */ 403 if (optind >= argc) 404 usage(nf); 405 406 if (strstr(argv[optind], NVME_NS_PREFIX) != NULL) { 407 ns_specified = true; 408 parse_ns_str(argv[optind], cname, &nsid); 409 open_dev(cname, &fd, 1, 1); 410 } else { 411 ns_specified = false; 412 nsid = NVME_GLOBAL_NAMESPACE_TAG; 413 open_dev(argv[optind], &fd, 1, 1); 414 } 415 416 read_controller_data(fd, &cdata); 417 418 ns_smart = (cdata.lpa >> NVME_CTRLR_DATA_LPA_NS_SMART_SHIFT) & 419 NVME_CTRLR_DATA_LPA_NS_SMART_MASK; 420 421 /* 422 * The log page attribtues indicate whether or not the controller 423 * supports the SMART/Health information log page on a per 424 * namespace basis. 425 */ 426 if (ns_specified) { 427 if (log_page != NVME_LOG_HEALTH_INFORMATION) 428 errx(1, "log page %d valid only at controller level", 429 log_page); 430 if (ns_smart == 0) 431 errx(1, 432 "controller does not support per namespace " 433 "smart/health information"); 434 } 435 436 print_fn = print_log_hex; 437 size = DEFAULT_SIZE; 438 if (binflag) 439 print_fn = print_bin; 440 if (!binflag && !hexflag) { 441 /* 442 * See if there is a pretty print function for the specified log 443 * page. If one isn't found, we just revert to the default 444 * (print_hex). If there was a vendor specified by the user, and 445 * the page is vendor specific, don't match the print function 446 * unless the vendors match. 447 */ 448 SLIST_FOREACH(f, &logpages, link) { 449 if (f->vendor != NULL && vendor != NULL && 450 strcmp(f->vendor, vendor) != 0) 451 continue; 452 if (log_page != f->log_page) 453 continue; 454 print_fn = f->print_fn; 455 size = f->size; 456 break; 457 } 458 } 459 460 if (log_page == NVME_LOG_ERROR) { 461 size = sizeof(struct nvme_error_information_entry); 462 size *= (cdata.elpe + 1); 463 } 464 465 /* Read the log page */ 466 buf = get_log_buffer(size); 467 read_logpage(fd, log_page, nsid, buf, size); 468 print_fn(&cdata, buf, size); 469 470 close(fd); 471 exit(0); 472 } 473 474 NVME_COMMAND(top, logpage, logpage, LOGPAGE_USAGE); 475