2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
4 * Copyright (c) 2013 EMC Corp.
7 * Copyright (C) 2012-2013 Intel Corporation
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
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.
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
32 #include <sys/cdefs.h>
33 __FBSDID("$FreeBSD$");
35 #include <sys/param.h>
36 #include <sys/ioccom.h>
47 #include <sys/endian.h>
49 #include "nvmecontrol.h"
51 #define LOGPAGE_USAGE \
52 "logpage <-p page_id> [-b] [-v vendor] [-x] <controller id|namespace id>\n" \
54 #define MAX_FW_SLOTS (7)
56 static SLIST_HEAD(,logpage_function) logpages;
59 logpage_register(struct logpage_function *p)
62 SLIST_INSERT_HEAD(&logpages, p, link);
66 kv_lookup(const struct kv_name *kv, size_t kv_count, uint32_t key)
71 for (i = 0; i < kv_count; i++, kv++)
74 snprintf(bad, sizeof(bad), "Attribute %#x", key);
79 print_log_hex(const struct nvme_controller_data *cdata __unused, void *data, uint32_t length)
82 print_hex(data, length);
86 print_bin(const struct nvme_controller_data *cdata __unused, void *data, uint32_t length)
89 write(STDOUT_FILENO, data, length);
93 get_log_buffer(uint32_t size)
97 if ((buf = malloc(size)) == NULL)
98 errx(1, "unable to malloc %u bytes", size);
100 memset(buf, 0, size);
105 read_logpage(int fd, uint8_t log_page, uint32_t nsid, void *payload,
106 uint32_t payload_size)
108 struct nvme_pt_command pt;
109 struct nvme_error_information_entry *err_entry;
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);
119 pt.len = payload_size;
122 if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0)
123 err(1, "get log page request failed");
125 /* Convert data to host endian */
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++);
133 case NVME_LOG_HEALTH_INFORMATION:
134 nvme_health_information_page_swapbytes(
135 (struct nvme_health_information_page *)payload);
137 case NVME_LOG_FIRMWARE_SLOT:
138 nvme_firmware_page_swapbytes(
139 (struct nvme_firmware_page *)payload);
141 case INTEL_LOG_TEMP_STATS:
142 intel_log_temp_stats_swapbytes(
143 (struct intel_log_temp_stats *)payload);
149 if (nvme_completion_is_error(&pt.cpl))
150 errx(1, "get log page request returned error");
154 print_log_error(const struct nvme_controller_data *cdata __unused, void *buf, uint32_t size)
158 uint8_t p, sc, sct, m, dnr;
159 struct nvme_error_information_entry *entry = buf;
161 printf("Error Information Log\n");
162 printf("=====================\n");
164 if (entry->error_count == 0) {
165 printf("No error entries found\n");
169 nentries = size/sizeof(struct nvme_error_information_entry);
170 for (i = 0; i < nentries; i++, entry++) {
171 if (entry->error_count == 0)
174 status = entry->status;
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);
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);
202 print_temp(uint16_t t)
204 printf("%u K, %2.2f C, %3.2f F\n", t, (float)t - 273.15, (float)t * 9 / 5 - 459.67);
209 print_log_health(const struct nvme_controller_data *cdata __unused, void *buf, uint32_t size __unused)
211 struct nvme_health_information_page *health = buf;
212 char cbuf[UINT128_DIG + 1];
216 warning = health->critical_warning;
218 printf("SMART/Health Information Log\n");
219 printf("============================\n");
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);
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)));
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)
267 printf("Temperature Sensor %d: ", i + 1);
268 print_temp(health->temp_sensor[i]);
273 print_log_firmware(const struct nvme_controller_data *cdata, void *buf, uint32_t size __unused)
277 struct nvme_firmware_page *fw = buf;
280 uint8_t fw_num_slots;
282 afi_slot = fw->afi >> NVME_FIRMWARE_PAGE_AFI_SLOT_SHIFT;
283 afi_slot &= NVME_FIRMWARE_PAGE_AFI_SLOT_MASK;
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;
290 printf("Firmware Slot Log\n");
291 printf("=================\n");
296 slots = MIN(fw_num_slots, MAX_FW_SLOTS);
298 for (i = 0; i < slots; i++) {
299 printf("Slot %d: ", i + 1);
300 if (afi_slot == i + 1)
305 if (fw->revision[i] == 0LLU)
308 if (isprint(*(char *)&fw->revision[i]))
309 printf("[%s] %.8s\n", status,
310 (char *)&fw->revision[i]);
312 printf("[%s] %016jx\n", status,
318 * Table of log page printer / sizing.
320 * Make sure you keep all the pages of one vendor together so -v help
321 * lists all the vendors pages.
324 NVME_LOG_ERROR, NULL, "Drive Error Log",
327 NVME_LOG_HEALTH_INFORMATION, NULL, "Health/SMART Data",
328 print_log_health, sizeof(struct nvme_health_information_page));
330 NVME_LOG_FIRMWARE_SLOT, NULL, "Firmware Information",
331 print_log_firmware, sizeof(struct nvme_firmware_page));
336 const struct logpage_function *f;
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);
351 logpage(const struct nvme_function *nf, int argc, char *argv[])
354 int log_page = 0, pageflag = false;
355 int binflag = false, hexflag = false, ns_specified;
361 const char *vendor = NULL;
362 const struct logpage_function *f;
363 struct nvme_controller_data cdata;
367 while ((opt = getopt(argc, argv, "bp:xv:")) != -1) {
373 if (strcmp(optarg, "help") == 0)
376 /* TODO: Add human-readable ASCII page IDs */
377 log_page = strtol(optarg, &p, 0);
378 if (p != NULL && *p != '\0') {
380 "\"%s\" not valid log page id.\n",
390 if (strcmp(optarg, "help") == 0)
398 printf("Missing page_id (-p).\n");
402 /* Check that a controller and/or namespace was specified. */
406 if (strstr(argv[optind], NVME_NS_PREFIX) != NULL) {
408 parse_ns_str(argv[optind], cname, &nsid);
409 open_dev(cname, &fd, 1, 1);
411 ns_specified = false;
412 nsid = NVME_GLOBAL_NAMESPACE_TAG;
413 open_dev(argv[optind], &fd, 1, 1);
416 read_controller_data(fd, &cdata);
418 ns_smart = (cdata.lpa >> NVME_CTRLR_DATA_LPA_NS_SMART_SHIFT) &
419 NVME_CTRLR_DATA_LPA_NS_SMART_MASK;
422 * The log page attribtues indicate whether or not the controller
423 * supports the SMART/Health information log page on a per
427 if (log_page != NVME_LOG_HEALTH_INFORMATION)
428 errx(1, "log page %d valid only at controller level",
432 "controller does not support per namespace "
433 "smart/health information");
436 print_fn = print_log_hex;
439 print_fn = print_bin;
440 if (!binflag && !hexflag) {
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.
448 SLIST_FOREACH(f, &logpages, link) {
449 if (f->vendor != NULL && vendor != NULL &&
450 strcmp(f->vendor, vendor) != 0)
452 if (log_page != f->log_page)
454 print_fn = f->print_fn;
460 if (log_page == NVME_LOG_ERROR) {
461 size = sizeof(struct nvme_error_information_entry);
462 size *= (cdata.elpe + 1);
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);
474 NVME_COMMAND(top, logpage, logpage, LOGPAGE_USAGE);