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