]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sbin/nvmecontrol/logpage.c
MFC r341409 (by imp): Move common logpage routines into nvmecontrol.h
[FreeBSD/FreeBSD.git] / sbin / nvmecontrol / logpage.c
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 SET_DECLARE(logpage, struct logpage_function);
52
53 #define LOGPAGE_USAGE                                                          \
54 "       nvmecontrol logpage <-p page_id> [-b] [-v vendor] [-x] <controller id|namespace id>\n"  \
55
56 #define MAX_FW_SLOTS    (7)
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 static 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  * Intel specific log pages from
312  * http://www.intel.com/content/dam/www/public/us/en/documents/product-specifications/ssd-dc-p3700-spec.pdf
313  *
314  * Though the version as of this date has a typo for the size of log page 0xca,
315  * offset 147: it is only 1 byte, not 6.
316  */
317 static void
318 print_intel_temp_stats(const struct nvme_controller_data *cdata __unused, void *buf, uint32_t size __unused)
319 {
320         struct intel_log_temp_stats     *temp = buf;
321
322         printf("Intel Temperature Log\n");
323         printf("=====================\n");
324
325         printf("Current:                        ");
326         print_temp(temp->current);
327         printf("Overtemp Last Flags             %#jx\n", (uintmax_t)temp->overtemp_flag_last);
328         printf("Overtemp Lifetime Flags         %#jx\n", (uintmax_t)temp->overtemp_flag_life);
329         printf("Max Temperature                 ");
330         print_temp(temp->max_temp);
331         printf("Min Temperature                 ");
332         print_temp(temp->min_temp);
333         printf("Max Operating Temperature       ");
334         print_temp(temp->max_oper_temp);
335         printf("Min Operating Temperature       ");
336         print_temp(temp->min_oper_temp);
337         printf("Estimated Temperature Offset:   %ju C/K\n", (uintmax_t)temp->est_offset);
338 }
339
340 /*
341  * Format from Table 22, section 5.7 IO Command Latency Statistics.
342  * Read and write stats pages have identical encoding.
343  */
344 static void
345 print_intel_read_write_lat_log(const struct nvme_controller_data *cdata __unused, void *buf, uint32_t size __unused)
346 {
347         const char *walker = buf;
348         int i;
349
350         printf("Major:                         %d\n", le16dec(walker + 0));
351         printf("Minor:                         %d\n", le16dec(walker + 2));
352         for (i = 0; i < 32; i++)
353                 printf("%4dus-%4dus:                 %ju\n", i * 32, (i + 1) * 32, (uintmax_t)le32dec(walker + 4 + i * 4));
354         for (i = 1; i < 32; i++)
355                 printf("%4dms-%4dms:                 %ju\n", i, i + 1, (uintmax_t)le32dec(walker + 132 + i * 4));
356         for (i = 1; i < 32; i++)
357                 printf("%4dms-%4dms:                 %ju\n", i * 32, (i + 1) * 32, (uintmax_t)le32dec(walker + 256 + i * 4));
358 }
359
360 static void
361 print_intel_read_lat_log(const struct nvme_controller_data *cdata __unused, void *buf, uint32_t size)
362 {
363
364         printf("Intel Read Latency Log\n");
365         printf("======================\n");
366         print_intel_read_write_lat_log(cdata, buf, size);
367 }
368
369 static void
370 print_intel_write_lat_log(const struct nvme_controller_data *cdata __unused, void *buf, uint32_t size)
371 {
372
373         printf("Intel Write Latency Log\n");
374         printf("=======================\n");
375         print_intel_read_write_lat_log(cdata, buf, size);
376 }
377
378 /*
379  * Table 19. 5.4 SMART Attributes. Samsung also implements this and some extra data not documented.
380  */
381 static void
382 print_intel_add_smart(const struct nvme_controller_data *cdata __unused, void *buf, uint32_t size __unused)
383 {
384         uint8_t *walker = buf;
385         uint8_t *end = walker + 150;
386         const char *name;
387         uint64_t raw;
388         uint8_t normalized;
389
390         static struct kv_name kv[] =
391         {
392                 { 0xab, "Program Fail Count" },
393                 { 0xac, "Erase Fail Count" },
394                 { 0xad, "Wear Leveling Count" },
395                 { 0xb8, "End to End Error Count" },
396                 { 0xc7, "CRC Error Count" },
397                 { 0xe2, "Timed: Media Wear" },
398                 { 0xe3, "Timed: Host Read %" },
399                 { 0xe4, "Timed: Elapsed Time" },
400                 { 0xea, "Thermal Throttle Status" },
401                 { 0xf0, "Retry Buffer Overflows" },
402                 { 0xf3, "PLL Lock Loss Count" },
403                 { 0xf4, "NAND Bytes Written" },
404                 { 0xf5, "Host Bytes Written" },
405         };
406
407         printf("Additional SMART Data Log\n");
408         printf("=========================\n");
409         /*
410          * walker[0] = Key
411          * walker[1,2] = reserved
412          * walker[3] = Normalized Value
413          * walker[4] = reserved
414          * walker[5..10] = Little Endian Raw value
415          *      (or other represenations)
416          * walker[11] = reserved
417          */
418         while (walker < end) {
419                 name = kv_lookup(kv, nitems(kv), *walker);
420                 normalized = walker[3];
421                 raw = le48dec(walker + 5);
422                 switch (*walker){
423                 case 0:
424                         break;
425                 case 0xad:
426                         printf("%-32s: %3d min: %u max: %u ave: %u\n", name, normalized,
427                             le16dec(walker + 5), le16dec(walker + 7), le16dec(walker + 9));
428                         break;
429                 case 0xe2:
430                         printf("%-32s: %3d %.3f%%\n", name, normalized, raw / 1024.0);
431                         break;
432                 case 0xea:
433                         printf("%-32s: %3d %d%% %d times\n", name, normalized, walker[5], le32dec(walker+6));
434                         break;
435                 default:
436                         printf("%-32s: %3d %ju\n", name, normalized, (uintmax_t)raw);
437                         break;
438                 }
439                 walker += 12;
440         }
441 }
442
443 /*
444  * HGST's 0xc1 page. This is a grab bag of additional data. Please see
445  * https://www.hgst.com/sites/default/files/resources/US_SN150_ProdManual.pdf
446  * https://www.hgst.com/sites/default/files/resources/US_SN100_ProdManual.pdf
447  * Appendix A for details
448  */
449
450 typedef void (*subprint_fn_t)(void *buf, uint16_t subtype, uint8_t res, uint32_t size);
451
452 struct subpage_print
453 {
454         uint16_t key;
455         subprint_fn_t fn;
456 };
457
458 static void print_hgst_info_write_errors(void *buf, uint16_t subtype, uint8_t res, uint32_t size);
459 static void print_hgst_info_read_errors(void *buf, uint16_t subtype, uint8_t res, uint32_t size);
460 static void print_hgst_info_verify_errors(void *buf, uint16_t subtype, uint8_t res, uint32_t size);
461 static void print_hgst_info_self_test(void *buf, uint16_t subtype, uint8_t res, uint32_t size);
462 static void print_hgst_info_background_scan(void *buf, uint16_t subtype, uint8_t res, uint32_t size);
463 static void print_hgst_info_erase_errors(void *buf, uint16_t subtype, uint8_t res, uint32_t size);
464 static void print_hgst_info_erase_counts(void *buf, uint16_t subtype, uint8_t res, uint32_t size);
465 static void print_hgst_info_temp_history(void *buf, uint16_t subtype, uint8_t res, uint32_t size);
466 static void print_hgst_info_ssd_perf(void *buf, uint16_t subtype, uint8_t res, uint32_t size);
467 static void print_hgst_info_firmware_load(void *buf, uint16_t subtype, uint8_t res, uint32_t size);
468
469 static struct subpage_print hgst_subpage[] = {
470         { 0x02, print_hgst_info_write_errors },
471         { 0x03, print_hgst_info_read_errors },
472         { 0x05, print_hgst_info_verify_errors },
473         { 0x10, print_hgst_info_self_test },
474         { 0x15, print_hgst_info_background_scan },
475         { 0x30, print_hgst_info_erase_errors },
476         { 0x31, print_hgst_info_erase_counts },
477         { 0x32, print_hgst_info_temp_history },
478         { 0x37, print_hgst_info_ssd_perf },
479         { 0x38, print_hgst_info_firmware_load },
480 };
481
482 /* Print a subpage that is basically just key value pairs */
483 static void
484 print_hgst_info_subpage_gen(void *buf, uint16_t subtype __unused, uint32_t size,
485     const struct kv_name *kv, size_t kv_count)
486 {
487         uint8_t *wsp, *esp;
488         uint16_t ptype;
489         uint8_t plen;
490         uint64_t param;
491         int i;
492
493         wsp = buf;
494         esp = wsp + size;
495         while (wsp < esp) {
496                 ptype = le16dec(wsp);
497                 wsp += 2;
498                 wsp++;                  /* Flags, just ignore */
499                 plen = *wsp++;
500                 param = 0;
501                 for (i = 0; i < plen; i++)
502                         param |= (uint64_t)*wsp++ << (i * 8);
503                 printf("  %-30s: %jd\n", kv_lookup(kv, kv_count, ptype), (uintmax_t)param);
504         }
505 }
506
507 static void
508 print_hgst_info_write_errors(void *buf, uint16_t subtype, uint8_t res __unused, uint32_t size)
509 {
510         static struct kv_name kv[] =
511         {
512                 { 0x0000, "Corrected Without Delay" },
513                 { 0x0001, "Corrected Maybe Delayed" },
514                 { 0x0002, "Re-Writes" },
515                 { 0x0003, "Errors Corrected" },
516                 { 0x0004, "Correct Algorithm Used" },
517                 { 0x0005, "Bytes Processed" },
518                 { 0x0006, "Uncorrected Errors" },
519                 { 0x8000, "Flash Write Commands" },
520                 { 0x8001, "HGST Special" },
521         };
522
523         printf("Write Errors Subpage:\n");
524         print_hgst_info_subpage_gen(buf, subtype, size, kv, nitems(kv));
525 }
526
527 static void
528 print_hgst_info_read_errors(void *buf, uint16_t subtype, uint8_t res __unused, uint32_t size)
529 {
530         static struct kv_name kv[] =
531         {
532                 { 0x0000, "Corrected Without Delay" },
533                 { 0x0001, "Corrected Maybe Delayed" },
534                 { 0x0002, "Re-Reads" },
535                 { 0x0003, "Errors Corrected" },
536                 { 0x0004, "Correct Algorithm Used" },
537                 { 0x0005, "Bytes Processed" },
538                 { 0x0006, "Uncorrected Errors" },
539                 { 0x8000, "Flash Read Commands" },
540                 { 0x8001, "XOR Recovered" },
541                 { 0x8002, "Total Corrected Bits" },
542         };
543
544         printf("Read Errors Subpage:\n");
545         print_hgst_info_subpage_gen(buf, subtype, size, kv, nitems(kv));
546 }
547
548 static void
549 print_hgst_info_verify_errors(void *buf, uint16_t subtype, uint8_t res __unused, uint32_t size)
550 {
551         static struct kv_name kv[] =
552         {
553                 { 0x0000, "Corrected Without Delay" },
554                 { 0x0001, "Corrected Maybe Delayed" },
555                 { 0x0002, "Re-Reads" },
556                 { 0x0003, "Errors Corrected" },
557                 { 0x0004, "Correct Algorithm Used" },
558                 { 0x0005, "Bytes Processed" },
559                 { 0x0006, "Uncorrected Errors" },
560                 { 0x8000, "Commands Processed" },
561         };
562
563         printf("Verify Errors Subpage:\n");
564         print_hgst_info_subpage_gen(buf, subtype, size, kv, nitems(kv));
565 }
566
567 static void
568 print_hgst_info_self_test(void *buf, uint16_t subtype __unused, uint8_t res __unused, uint32_t size)
569 {
570         size_t i;
571         uint8_t *walker = buf;
572         uint16_t code, hrs;
573         uint32_t lba;
574
575         printf("Self Test Subpage:\n");
576         for (i = 0; i < size / 20; i++) {       /* Each entry is 20 bytes */
577                 code = le16dec(walker);
578                 walker += 2;
579                 walker++;                       /* Ignore fixed flags */
580                 if (*walker == 0)               /* Last entry is zero length */
581                         break;
582                 if (*walker++ != 0x10) {
583                         printf("Bad length for self test report\n");
584                         return;
585                 }
586                 printf("  %-30s: %d\n", "Recent Test", code);
587                 printf("    %-28s: %#x\n", "Self-Test Results", *walker & 0xf);
588                 printf("    %-28s: %#x\n", "Self-Test Code", (*walker >> 5) & 0x7);
589                 walker++;
590                 printf("    %-28s: %#x\n", "Self-Test Number", *walker++);
591                 hrs = le16dec(walker);
592                 walker += 2;
593                 lba = le32dec(walker);
594                 walker += 4;
595                 printf("    %-28s: %u\n", "Total Power On Hrs", hrs);
596                 printf("    %-28s: %#jx (%jd)\n", "LBA", (uintmax_t)lba, (uintmax_t)lba);
597                 printf("    %-28s: %#x\n", "Sense Key", *walker++ & 0xf);
598                 printf("    %-28s: %#x\n", "Additional Sense Code", *walker++);
599                 printf("    %-28s: %#x\n", "Additional Sense Qualifier", *walker++);
600                 printf("    %-28s: %#x\n", "Vendor Specific Detail", *walker++);
601         }
602 }
603
604 static void
605 print_hgst_info_background_scan(void *buf, uint16_t subtype __unused, uint8_t res __unused, uint32_t size)
606 {
607         uint8_t *walker = buf;
608         uint8_t status;
609         uint16_t code, nscan, progress;
610         uint32_t pom, nand;
611
612         printf("Background Media Scan Subpage:\n");
613         /* Decode the header */
614         code = le16dec(walker);
615         walker += 2;
616         walker++;                       /* Ignore fixed flags */
617         if (*walker++ != 0x10) {
618                 printf("Bad length for background scan header\n");
619                 return;
620         }
621         if (code != 0) {
622                 printf("Expceted code 0, found code %#x\n", code);
623                 return;
624         }
625         pom = le32dec(walker);
626         walker += 4;
627         walker++;                       /* Reserved */
628         status = *walker++;
629         nscan = le16dec(walker);
630         walker += 2;
631         progress = le16dec(walker);
632         walker += 2;
633         walker += 6;                    /* Reserved */
634         printf("  %-30s: %d\n", "Power On Minutes", pom);
635         printf("  %-30s: %x (%s)\n", "BMS Status", status,
636             status == 0 ? "idle" : (status == 1 ? "active" : (status == 8 ? "suspended" : "unknown")));
637         printf("  %-30s: %d\n", "Number of BMS", nscan);
638         printf("  %-30s: %d\n", "Progress Current BMS", progress);
639         /* Report retirements */
640         if (walker - (uint8_t *)buf != 20) {
641                 printf("Coding error, offset not 20\n");
642                 return;
643         }
644         size -= 20;
645         printf("  %-30s: %d\n", "BMS retirements", size / 0x18);
646         while (size > 0) {
647                 code = le16dec(walker);
648                 walker += 2;
649                 walker++;
650                 if (*walker++ != 0x14) {
651                         printf("Bad length parameter\n");
652                         return;
653                 }
654                 pom = le32dec(walker);
655                 walker += 4;
656                 /*
657                  * Spec sheet says the following are hard coded, if true, just
658                  * print the NAND retirement.
659                  */
660                 if (walker[0] == 0x41 &&
661                     walker[1] == 0x0b &&
662                     walker[2] == 0x01 &&
663                     walker[3] == 0x00 &&
664                     walker[4] == 0x00 &&
665                     walker[5] == 0x00 &&
666                     walker[6] == 0x00 &&
667                     walker[7] == 0x00) {
668                         walker += 8;
669                         walker += 4;    /* Skip reserved */
670                         nand = le32dec(walker);
671                         walker += 4;
672                         printf("  %-30s: %d\n", "Retirement number", code);
673                         printf("    %-28s: %#x\n", "NAND (C/T)BBBPPP", nand);
674                 } else {
675                         printf("Parameter %#x entry corrupt\n", code);
676                         walker += 16;
677                 }
678         }
679 }
680
681 static void
682 print_hgst_info_erase_errors(void *buf, uint16_t subtype __unused, uint8_t res __unused, uint32_t size)
683 {
684         static struct kv_name kv[] =
685         {
686                 { 0x0000, "Corrected Without Delay" },
687                 { 0x0001, "Corrected Maybe Delayed" },
688                 { 0x0002, "Re-Erase" },
689                 { 0x0003, "Errors Corrected" },
690                 { 0x0004, "Correct Algorithm Used" },
691                 { 0x0005, "Bytes Processed" },
692                 { 0x0006, "Uncorrected Errors" },
693                 { 0x8000, "Flash Erase Commands" },
694                 { 0x8001, "Mfg Defect Count" },
695                 { 0x8002, "Grown Defect Count" },
696                 { 0x8003, "Erase Count -- User" },
697                 { 0x8004, "Erase Count -- System" },
698         };
699
700         printf("Erase Errors Subpage:\n");
701         print_hgst_info_subpage_gen(buf, subtype, size, kv, nitems(kv));
702 }
703
704 static void
705 print_hgst_info_erase_counts(void *buf, uint16_t subtype, uint8_t res __unused, uint32_t size)
706 {
707         /* My drive doesn't export this -- so not coding up */
708         printf("XXX: Erase counts subpage: %p, %#x %d\n", buf, subtype, size);
709 }
710
711 static void
712 print_hgst_info_temp_history(void *buf, uint16_t subtype __unused, uint8_t res __unused, uint32_t size __unused)
713 {
714         uint8_t *walker = buf;
715         uint32_t min;
716
717         printf("Temperature History:\n");
718         printf("  %-30s: %d C\n", "Current Temperature", *walker++);
719         printf("  %-30s: %d C\n", "Reference Temperature", *walker++);
720         printf("  %-30s: %d C\n", "Maximum Temperature", *walker++);
721         printf("  %-30s: %d C\n", "Minimum Temperature", *walker++);
722         min = le32dec(walker);
723         walker += 4;
724         printf("  %-30s: %d:%02d:00\n", "Max Temperature Time", min / 60, min % 60);
725         min = le32dec(walker);
726         walker += 4;
727         printf("  %-30s: %d:%02d:00\n", "Over Temperature Duration", min / 60, min % 60);
728         min = le32dec(walker);
729         walker += 4;
730         printf("  %-30s: %d:%02d:00\n", "Min Temperature Time", min / 60, min % 60);
731 }
732
733 static void
734 print_hgst_info_ssd_perf(void *buf, uint16_t subtype __unused, uint8_t res, uint32_t size __unused)
735 {
736         uint8_t *walker = buf;
737         uint64_t val;
738
739         printf("SSD Performance Subpage Type %d:\n", res);
740         val = le64dec(walker);
741         walker += 8;
742         printf("  %-30s: %ju\n", "Host Read Commands", val);
743         val = le64dec(walker);
744         walker += 8;
745         printf("  %-30s: %ju\n", "Host Read Blocks", val);
746         val = le64dec(walker);
747         walker += 8;
748         printf("  %-30s: %ju\n", "Host Cache Read Hits Commands", val);
749         val = le64dec(walker);
750         walker += 8;
751         printf("  %-30s: %ju\n", "Host Cache Read Hits Blocks", val);
752         val = le64dec(walker);
753         walker += 8;
754         printf("  %-30s: %ju\n", "Host Read Commands Stalled", val);
755         val = le64dec(walker);
756         walker += 8;
757         printf("  %-30s: %ju\n", "Host Write Commands", val);
758         val = le64dec(walker);
759         walker += 8;
760         printf("  %-30s: %ju\n", "Host Write Blocks", val);
761         val = le64dec(walker);
762         walker += 8;
763         printf("  %-30s: %ju\n", "Host Write Odd Start Commands", val);
764         val = le64dec(walker);
765         walker += 8;
766         printf("  %-30s: %ju\n", "Host Write Odd End Commands", val);
767         val = le64dec(walker);
768         walker += 8;
769         printf("  %-30s: %ju\n", "Host Write Commands Stalled", val);
770         val = le64dec(walker);
771         walker += 8;
772         printf("  %-30s: %ju\n", "NAND Read Commands", val);
773         val = le64dec(walker);
774         walker += 8;
775         printf("  %-30s: %ju\n", "NAND Read Blocks", val);
776         val = le64dec(walker);
777         walker += 8;
778         printf("  %-30s: %ju\n", "NAND Write Commands", val);
779         val = le64dec(walker);
780         walker += 8;
781         printf("  %-30s: %ju\n", "NAND Write Blocks", val);
782         val = le64dec(walker);
783         walker += 8;
784         printf("  %-30s: %ju\n", "NAND Read Before Writes", val);
785 }
786
787 static void
788 print_hgst_info_firmware_load(void *buf, uint16_t subtype __unused, uint8_t res __unused, uint32_t size __unused)
789 {
790         uint8_t *walker = buf;
791
792         printf("Firmware Load Subpage:\n");
793         printf("  %-30s: %d\n", "Firmware Downloads", le32dec(walker));
794 }
795
796 static void
797 kv_indirect(void *buf, uint32_t subtype, uint8_t res, uint32_t size, struct subpage_print *sp, size_t nsp)
798 {
799         size_t i;
800
801         for (i = 0; i < nsp; i++, sp++) {
802                 if (sp->key == subtype) {
803                         sp->fn(buf, subtype, res, size);
804                         return;
805                 }
806         }
807         printf("No handler for page type %x\n", subtype);
808 }
809
810 static void
811 print_hgst_info_log(const struct nvme_controller_data *cdata __unused, void *buf, uint32_t size __unused)
812 {
813         uint8_t *walker, *end, *subpage;
814         int pages;
815         uint16_t len;
816         uint8_t subtype, res;
817
818         printf("HGST Extra Info Log\n");
819         printf("===================\n");
820
821         walker = buf;
822         pages = *walker++;
823         walker++;
824         len = le16dec(walker);
825         walker += 2;
826         end = walker + len;             /* Length is exclusive of this header */
827         
828         while (walker < end) {
829                 subpage = walker + 4;
830                 subtype = *walker++ & 0x3f;     /* subtype */
831                 res = *walker++;                /* Reserved */
832                 len = le16dec(walker);
833                 walker += len + 2;              /* Length, not incl header */
834                 if (walker > end) {
835                         printf("Ooops! Off the end of the list\n");
836                         break;
837                 }
838                 kv_indirect(subpage, subtype, res, len, hgst_subpage, nitems(hgst_subpage));
839         }
840 }
841
842 /*
843  * Table of log page printer / sizing.
844  *
845  * This includes Intel specific pages that are widely implemented.
846  * Make sure you keep all the pages of one vendor together so -v help
847  * lists all the vendors pages.
848  */
849 NVME_LOGPAGE(error,
850     NVME_LOG_ERROR,                     NULL,   "Drive Error Log",
851     print_log_error,                    0);
852 NVME_LOGPAGE(health,
853     NVME_LOG_HEALTH_INFORMATION,        NULL,   "Health/SMART Data",
854     print_log_health,                   sizeof(struct nvme_health_information_page));
855 NVME_LOGPAGE(fw,
856     NVME_LOG_FIRMWARE_SLOT,             NULL,   "Firmware Information",
857     print_log_firmware,                 sizeof(struct nvme_firmware_page));
858 NVME_LOGPAGE(hgst_info,
859     HGST_INFO_LOG,                      "hgst", "Detailed Health/SMART",
860     print_hgst_info_log,                DEFAULT_SIZE);
861 NVME_LOGPAGE(wdc_info,
862     HGST_INFO_LOG,                      "wdc",  "Detailed Health/SMART",
863     print_hgst_info_log,                DEFAULT_SIZE);
864 NVME_LOGPAGE(wds_info,
865     HGST_INFO_LOG,                      "wds",  "Detailed Health/SMART",
866     print_hgst_info_log,                DEFAULT_SIZE);
867 NVME_LOGPAGE(intel_temp,
868     INTEL_LOG_TEMP_STATS,               "intel", "Temperature Stats",
869     print_intel_temp_stats,             sizeof(struct intel_log_temp_stats));
870 NVME_LOGPAGE(intel_rlat,
871     INTEL_LOG_READ_LAT_LOG,             "intel", "Read Latencies",
872     print_intel_read_lat_log,           DEFAULT_SIZE);
873 NVME_LOGPAGE(intel_wlat,
874     INTEL_LOG_WRITE_LAT_LOG,            "intel", "Write Latencies",
875     print_intel_write_lat_log,          DEFAULT_SIZE);
876 NVME_LOGPAGE(intel_smart,
877     INTEL_LOG_ADD_SMART,                "intel", "Extra Health/SMART Data",
878     print_intel_add_smart,              DEFAULT_SIZE);
879 NVME_LOGPAGE(samsung_smart,
880     INTEL_LOG_ADD_SMART,                "samsung", "Extra Health/SMART Data",
881     print_intel_add_smart,              DEFAULT_SIZE);
882
883 static void
884 logpage_usage(void)
885 {
886         fprintf(stderr, "usage:\n");
887         fprintf(stderr, LOGPAGE_USAGE);
888         exit(1);
889 }
890
891 static void
892 logpage_help(void)
893 {
894         struct logpage_function         **f;
895         const char                      *v;
896
897         fprintf(stderr, "\n");
898         fprintf(stderr, "%-8s %-10s %s\n", "Page", "Vendor","Page Name");
899         fprintf(stderr, "-------- ---------- ----------\n");
900         for (f = SET_BEGIN(logpage); f < SET_LIMIT(logpage); f++) {
901                 v = (*f)->vendor == NULL ? "-" : (*f)->vendor;
902                 fprintf(stderr, "0x%02x     %-10s %s\n", (*f)->log_page, v, (*f)->name);
903         }
904
905         exit(1);
906 }
907
908 static void
909 logpage(int argc, char *argv[])
910 {
911         int                             fd;
912         int                             log_page = 0, pageflag = false;
913         int                             binflag = false, hexflag = false, ns_specified;
914         int                             opt;
915         char                            *p;
916         char                            cname[64];
917         uint32_t                        nsid, size;
918         void                            *buf;
919         const char                      *vendor = NULL;
920         struct logpage_function         **f;
921         struct nvme_controller_data     cdata;
922         print_fn_t                      print_fn;
923         uint8_t                         ns_smart;
924
925         while ((opt = getopt(argc, argv, "bp:xv:")) != -1) {
926                 switch (opt) {
927                 case 'b':
928                         binflag = true;
929                         break;
930                 case 'p':
931                         if (strcmp(optarg, "help") == 0)
932                                 logpage_help();
933
934                         /* TODO: Add human-readable ASCII page IDs */
935                         log_page = strtol(optarg, &p, 0);
936                         if (p != NULL && *p != '\0') {
937                                 fprintf(stderr,
938                                     "\"%s\" not valid log page id.\n",
939                                     optarg);
940                                 logpage_usage();
941                         }
942                         pageflag = true;
943                         break;
944                 case 'x':
945                         hexflag = true;
946                         break;
947                 case 'v':
948                         if (strcmp(optarg, "help") == 0)
949                                 logpage_help();
950                         vendor = optarg;
951                         break;
952                 }
953         }
954
955         if (!pageflag) {
956                 printf("Missing page_id (-p).\n");
957                 logpage_usage();
958         }
959
960         /* Check that a controller and/or namespace was specified. */
961         if (optind >= argc)
962                 logpage_usage();
963
964         if (strstr(argv[optind], NVME_NS_PREFIX) != NULL) {
965                 ns_specified = true;
966                 parse_ns_str(argv[optind], cname, &nsid);
967                 open_dev(cname, &fd, 1, 1);
968         } else {
969                 ns_specified = false;
970                 nsid = NVME_GLOBAL_NAMESPACE_TAG;
971                 open_dev(argv[optind], &fd, 1, 1);
972         }
973
974         read_controller_data(fd, &cdata);
975
976         ns_smart = (cdata.lpa >> NVME_CTRLR_DATA_LPA_NS_SMART_SHIFT) &
977                 NVME_CTRLR_DATA_LPA_NS_SMART_MASK;
978
979         /*
980          * The log page attribtues indicate whether or not the controller
981          * supports the SMART/Health information log page on a per
982          * namespace basis.
983          */
984         if (ns_specified) {
985                 if (log_page != NVME_LOG_HEALTH_INFORMATION)
986                         errx(1, "log page %d valid only at controller level",
987                             log_page);
988                 if (ns_smart == 0)
989                         errx(1,
990                             "controller does not support per namespace "
991                             "smart/health information");
992         }
993
994         print_fn = print_log_hex;
995         size = DEFAULT_SIZE;
996         if (binflag)
997                 print_fn = print_bin;
998         if (!binflag && !hexflag) {
999                 /*
1000                  * See if there is a pretty print function for the specified log
1001                  * page.  If one isn't found, we just revert to the default
1002                  * (print_hex). If there was a vendor specified bt the user, and
1003                  * the page is vendor specific, don't match the print function
1004                  * unless the vendors match.
1005                  */
1006                 for (f = SET_BEGIN(logpage); f < SET_LIMIT(logpage); f++) {
1007                         if ((*f)->vendor != NULL && vendor != NULL &&
1008                             strcmp((*f)->vendor, vendor) != 0)
1009                                 continue;
1010                         if (log_page != (*f)->log_page)
1011                                 continue;
1012                         print_fn = (*f)->print_fn;
1013                         size = (*f)->size;
1014                         break;
1015                 }
1016         }
1017
1018         if (log_page == NVME_LOG_ERROR) {
1019                 size = sizeof(struct nvme_error_information_entry);
1020                 size *= (cdata.elpe + 1);
1021         }
1022
1023         /* Read the log page */
1024         buf = get_log_buffer(size);
1025         read_logpage(fd, log_page, nsid, buf, size);
1026         print_fn(&cdata, buf, size);
1027
1028         close(fd);
1029         exit(0);
1030 }
1031
1032 NVME_COMMAND(top, logpage, logpage, LOGPAGE_USAGE);