]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sbin/nvmecontrol/logpage.c
Merge clang trunk r338150, and resolve conflicts.
[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 #define DEFAULT_SIZE    (4096)
52 #define MAX_FW_SLOTS    (7)
53
54 typedef void (*print_fn_t)(const struct nvme_controller_data *cdata, void *buf, uint32_t size);
55
56 struct kv_name
57 {
58         uint32_t key;
59         const char *name;
60 };
61
62 static const char *
63 kv_lookup(const struct kv_name *kv, size_t kv_count, uint32_t key)
64 {
65         static char bad[32];
66         size_t i;
67
68         for (i = 0; i < kv_count; i++, kv++)
69                 if (kv->key == key)
70                         return kv->name;
71         snprintf(bad, sizeof(bad), "Attribute %#x", key);
72         return bad;
73 }
74
75 static void
76 print_log_hex(const struct nvme_controller_data *cdata __unused, void *data, uint32_t length)
77 {
78
79         print_hex(data, length);
80 }
81
82 static void
83 print_bin(const struct nvme_controller_data *cdata __unused, void *data, uint32_t length)
84 {
85
86         write(STDOUT_FILENO, data, length);
87 }
88
89 static void *
90 get_log_buffer(uint32_t size)
91 {
92         void    *buf;
93
94         if ((buf = malloc(size)) == NULL)
95                 errx(1, "unable to malloc %u bytes", size);
96
97         memset(buf, 0, size);
98         return (buf);
99 }
100
101 void
102 read_logpage(int fd, uint8_t log_page, uint32_t nsid, void *payload,
103     uint32_t payload_size)
104 {
105         struct nvme_pt_command  pt;
106         struct nvme_error_information_entry     *err_entry;
107         int i, err_pages;
108
109         memset(&pt, 0, sizeof(pt));
110         pt.cmd.opc_fuse = NVME_CMD_SET_OPC(NVME_OPC_GET_LOG_PAGE);
111         pt.cmd.nsid = htole32(nsid);
112         pt.cmd.cdw10 = ((payload_size/sizeof(uint32_t)) - 1) << 16;
113         pt.cmd.cdw10 |= log_page;
114         pt.cmd.cdw10 = htole32(pt.cmd.cdw10);
115         pt.buf = payload;
116         pt.len = payload_size;
117         pt.is_read = 1;
118
119         if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0)
120                 err(1, "get log page request failed");
121
122         /* Convert data to host endian */
123         switch (log_page) {
124         case NVME_LOG_ERROR:
125                 err_entry = (struct nvme_error_information_entry *)payload;
126                 err_pages = payload_size / sizeof(struct nvme_error_information_entry);
127                 for (i = 0; i < err_pages; i++)
128                         nvme_error_information_entry_swapbytes(err_entry++);
129                 break;
130         case NVME_LOG_HEALTH_INFORMATION:
131                 nvme_health_information_page_swapbytes(
132                     (struct nvme_health_information_page *)payload);
133                 break;
134         case NVME_LOG_FIRMWARE_SLOT:
135                 nvme_firmware_page_swapbytes(
136                     (struct nvme_firmware_page *)payload);
137                 break;
138         case INTEL_LOG_TEMP_STATS:
139                 intel_log_temp_stats_swapbytes(
140                     (struct intel_log_temp_stats *)payload);
141                 break;
142         default:
143                 break;
144         }
145
146         if (nvme_completion_is_error(&pt.cpl))
147                 errx(1, "get log page request returned error");
148 }
149
150 static void
151 print_log_error(const struct nvme_controller_data *cdata __unused, void *buf, uint32_t size)
152 {
153         int                                     i, nentries;
154         uint16_t                                status;
155         uint8_t                                 p, sc, sct, m, dnr;
156         struct nvme_error_information_entry     *entry = buf;
157
158         printf("Error Information Log\n");
159         printf("=====================\n");
160
161         if (entry->error_count == 0) {
162                 printf("No error entries found\n");
163                 return;
164         }
165
166         nentries = size/sizeof(struct nvme_error_information_entry);
167         for (i = 0; i < nentries; i++, entry++) {
168                 if (entry->error_count == 0)
169                         break;
170
171                 status = entry->status;
172
173                 p = NVME_STATUS_GET_P(status);
174                 sc = NVME_STATUS_GET_SC(status);
175                 sct = NVME_STATUS_GET_SCT(status);
176                 m = NVME_STATUS_GET_M(status);
177                 dnr = NVME_STATUS_GET_DNR(status);
178
179                 printf("Entry %02d\n", i + 1);
180                 printf("=========\n");
181                 printf(" Error count:          %ju\n", entry->error_count);
182                 printf(" Submission queue ID:  %u\n", entry->sqid);
183                 printf(" Command ID:           %u\n", entry->cid);
184                 /* TODO: Export nvme_status_string structures from kernel? */
185                 printf(" Status:\n");
186                 printf("  Phase tag:           %d\n", p);
187                 printf("  Status code:         %d\n", sc);
188                 printf("  Status code type:    %d\n", sct);
189                 printf("  More:                %d\n", m);
190                 printf("  DNR:                 %d\n", dnr);
191                 printf(" Error location:       %u\n", entry->error_location);
192                 printf(" LBA:                  %ju\n", entry->lba);
193                 printf(" Namespace ID:         %u\n", entry->nsid);
194                 printf(" Vendor specific info: %u\n", entry->vendor_specific);
195         }
196 }
197
198 static void
199 print_temp(uint16_t t)
200 {
201         printf("%u K, %2.2f C, %3.2f F\n", t, (float)t - 273.15, (float)t * 9 / 5 - 459.67);
202 }
203
204
205 static void
206 print_log_health(const struct nvme_controller_data *cdata __unused, void *buf, uint32_t size __unused)
207 {
208         struct nvme_health_information_page *health = buf;
209         char cbuf[UINT128_DIG + 1];
210         uint8_t warning;
211         int i;
212
213         warning = health->critical_warning;
214
215         printf("SMART/Health Information Log\n");
216         printf("============================\n");
217
218         printf("Critical Warning State:         0x%02x\n", warning);
219         printf(" Available spare:               %d\n",
220             !!(warning & NVME_CRIT_WARN_ST_AVAILABLE_SPARE));
221         printf(" Temperature:                   %d\n",
222             !!(warning & NVME_CRIT_WARN_ST_TEMPERATURE));
223         printf(" Device reliability:            %d\n",
224             !!(warning & NVME_CRIT_WARN_ST_DEVICE_RELIABILITY));
225         printf(" Read only:                     %d\n",
226             !!(warning & NVME_CRIT_WARN_ST_READ_ONLY));
227         printf(" Volatile memory backup:        %d\n",
228             !!(warning & NVME_CRIT_WARN_ST_VOLATILE_MEMORY_BACKUP));
229         printf("Temperature:                    ");
230         print_temp(health->temperature);
231         printf("Available spare:                %u\n",
232             health->available_spare);
233         printf("Available spare threshold:      %u\n",
234             health->available_spare_threshold);
235         printf("Percentage used:                %u\n",
236             health->percentage_used);
237
238         printf("Data units (512,000 byte) read: %s\n",
239             uint128_to_str(to128(health->data_units_read), cbuf, sizeof(cbuf)));
240         printf("Data units written:             %s\n",
241             uint128_to_str(to128(health->data_units_written), cbuf, sizeof(cbuf)));
242         printf("Host read commands:             %s\n",
243             uint128_to_str(to128(health->host_read_commands), cbuf, sizeof(cbuf)));
244         printf("Host write commands:            %s\n",
245             uint128_to_str(to128(health->host_write_commands), cbuf, sizeof(cbuf)));
246         printf("Controller busy time (minutes): %s\n",
247             uint128_to_str(to128(health->controller_busy_time), cbuf, sizeof(cbuf)));
248         printf("Power cycles:                   %s\n",
249             uint128_to_str(to128(health->power_cycles), cbuf, sizeof(cbuf)));
250         printf("Power on hours:                 %s\n",
251             uint128_to_str(to128(health->power_on_hours), cbuf, sizeof(cbuf)));
252         printf("Unsafe shutdowns:               %s\n",
253             uint128_to_str(to128(health->unsafe_shutdowns), cbuf, sizeof(cbuf)));
254         printf("Media errors:                   %s\n",
255             uint128_to_str(to128(health->media_errors), cbuf, sizeof(cbuf)));
256         printf("No. error info log entries:     %s\n",
257             uint128_to_str(to128(health->num_error_info_log_entries), cbuf, sizeof(cbuf)));
258
259         printf("Warning Temp Composite Time:    %d\n", health->warning_temp_time);
260         printf("Error Temp Composite Time:      %d\n", health->error_temp_time);
261         for (i = 0; i < 8; i++) {
262                 if (health->temp_sensor[i] == 0)
263                         continue;
264                 printf("Temperature Sensor %d:           ", i + 1);
265                 print_temp(health->temp_sensor[i]);
266         }
267 }
268
269 static void
270 print_log_firmware(const struct nvme_controller_data *cdata, void *buf, uint32_t size __unused)
271 {
272         int                             i, slots;
273         const char                      *status;
274         struct nvme_firmware_page       *fw = buf;
275         uint8_t                         afi_slot;
276         uint16_t                        oacs_fw;
277         uint8_t                         fw_num_slots;
278
279         afi_slot = fw->afi >> NVME_FIRMWARE_PAGE_AFI_SLOT_SHIFT;
280         afi_slot &= NVME_FIRMWARE_PAGE_AFI_SLOT_MASK;
281
282         oacs_fw = (cdata->oacs >> NVME_CTRLR_DATA_OACS_FIRMWARE_SHIFT) &
283                 NVME_CTRLR_DATA_OACS_FIRMWARE_MASK;
284         fw_num_slots = (cdata->frmw >> NVME_CTRLR_DATA_FRMW_NUM_SLOTS_SHIFT) &
285                 NVME_CTRLR_DATA_FRMW_NUM_SLOTS_MASK;
286
287         printf("Firmware Slot Log\n");
288         printf("=================\n");
289
290         if (oacs_fw == 0)
291                 slots = 1;
292         else
293                 slots = MIN(fw_num_slots, MAX_FW_SLOTS);
294
295         for (i = 0; i < slots; i++) {
296                 printf("Slot %d: ", i + 1);
297                 if (afi_slot == i + 1)
298                         status = "  Active";
299                 else
300                         status = "Inactive";
301
302                 if (fw->revision[i] == 0LLU)
303                         printf("Empty\n");
304                 else
305                         if (isprint(*(char *)&fw->revision[i]))
306                                 printf("[%s] %.8s\n", status,
307                                     (char *)&fw->revision[i]);
308                         else
309                                 printf("[%s] %016jx\n", status,
310                                     fw->revision[i]);
311         }
312 }
313
314 /*
315  * Intel specific log pages from
316  * http://www.intel.com/content/dam/www/public/us/en/documents/product-specifications/ssd-dc-p3700-spec.pdf
317  *
318  * Though the version as of this date has a typo for the size of log page 0xca,
319  * offset 147: it is only 1 byte, not 6.
320  */
321 static void
322 print_intel_temp_stats(const struct nvme_controller_data *cdata __unused, void *buf, uint32_t size __unused)
323 {
324         struct intel_log_temp_stats     *temp = buf;
325
326         printf("Intel Temperature Log\n");
327         printf("=====================\n");
328
329         printf("Current:                        ");
330         print_temp(temp->current);
331         printf("Overtemp Last Flags             %#jx\n", (uintmax_t)temp->overtemp_flag_last);
332         printf("Overtemp Lifetime Flags         %#jx\n", (uintmax_t)temp->overtemp_flag_life);
333         printf("Max Temperature                 ");
334         print_temp(temp->max_temp);
335         printf("Min Temperature                 ");
336         print_temp(temp->min_temp);
337         printf("Max Operating Temperature       ");
338         print_temp(temp->max_oper_temp);
339         printf("Min Operating Temperature       ");
340         print_temp(temp->min_oper_temp);
341         printf("Estimated Temperature Offset:   %ju C/K\n", (uintmax_t)temp->est_offset);
342 }
343
344 /*
345  * Format from Table 22, section 5.7 IO Command Latency Statistics.
346  * Read and write stats pages have identical encoding.
347  */
348 static void
349 print_intel_read_write_lat_log(const struct nvme_controller_data *cdata __unused, void *buf, uint32_t size __unused)
350 {
351         const char *walker = buf;
352         int i;
353
354         printf("Major:                         %d\n", le16dec(walker + 0));
355         printf("Minor:                         %d\n", le16dec(walker + 2));
356         for (i = 0; i < 32; i++)
357                 printf("%4dus-%4dus:                 %ju\n", i * 32, (i + 1) * 32, (uintmax_t)le32dec(walker + 4 + i * 4));
358         for (i = 1; i < 32; i++)
359                 printf("%4dms-%4dms:                 %ju\n", i, i + 1, (uintmax_t)le32dec(walker + 132 + i * 4));
360         for (i = 1; i < 32; i++)
361                 printf("%4dms-%4dms:                 %ju\n", i * 32, (i + 1) * 32, (uintmax_t)le32dec(walker + 256 + i * 4));
362 }
363
364 static void
365 print_intel_read_lat_log(const struct nvme_controller_data *cdata __unused, void *buf, uint32_t size)
366 {
367
368         printf("Intel Read Latency Log\n");
369         printf("======================\n");
370         print_intel_read_write_lat_log(cdata, buf, size);
371 }
372
373 static void
374 print_intel_write_lat_log(const struct nvme_controller_data *cdata __unused, void *buf, uint32_t size)
375 {
376
377         printf("Intel Write Latency Log\n");
378         printf("=======================\n");
379         print_intel_read_write_lat_log(cdata, buf, size);
380 }
381
382 /*
383  * Table 19. 5.4 SMART Attributes. Samsung also implements this and some extra data not documented.
384  */
385 static void
386 print_intel_add_smart(const struct nvme_controller_data *cdata __unused, void *buf, uint32_t size __unused)
387 {
388         uint8_t *walker = buf;
389         uint8_t *end = walker + 150;
390         const char *name;
391         uint64_t raw;
392         uint8_t normalized;
393
394         static struct kv_name kv[] =
395         {
396                 { 0xab, "Program Fail Count" },
397                 { 0xac, "Erase Fail Count" },
398                 { 0xad, "Wear Leveling Count" },
399                 { 0xb8, "End to End Error Count" },
400                 { 0xc7, "CRC Error Count" },
401                 { 0xe2, "Timed: Media Wear" },
402                 { 0xe3, "Timed: Host Read %" },
403                 { 0xe4, "Timed: Elapsed Time" },
404                 { 0xea, "Thermal Throttle Status" },
405                 { 0xf0, "Retry Buffer Overflows" },
406                 { 0xf3, "PLL Lock Loss Count" },
407                 { 0xf4, "NAND Bytes Written" },
408                 { 0xf5, "Host Bytes Written" },
409         };
410
411         printf("Additional SMART Data Log\n");
412         printf("=========================\n");
413         /*
414          * walker[0] = Key
415          * walker[1,2] = reserved
416          * walker[3] = Normalized Value
417          * walker[4] = reserved
418          * walker[5..10] = Little Endian Raw value
419          *      (or other represenations)
420          * walker[11] = reserved
421          */
422         while (walker < end) {
423                 name = kv_lookup(kv, nitems(kv), *walker);
424                 normalized = walker[3];
425                 raw = le48dec(walker + 5);
426                 switch (*walker){
427                 case 0:
428                         break;
429                 case 0xad:
430                         printf("%-32s: %3d min: %u max: %u ave: %u\n", name, normalized,
431                             le16dec(walker + 5), le16dec(walker + 7), le16dec(walker + 9));
432                         break;
433                 case 0xe2:
434                         printf("%-32s: %3d %.3f%%\n", name, normalized, raw / 1024.0);
435                         break;
436                 case 0xea:
437                         printf("%-32s: %3d %d%% %d times\n", name, normalized, walker[5], le32dec(walker+6));
438                         break;
439                 default:
440                         printf("%-32s: %3d %ju\n", name, normalized, (uintmax_t)raw);
441                         break;
442                 }
443                 walker += 12;
444         }
445 }
446
447 /*
448  * HGST's 0xc1 page. This is a grab bag of additional data. Please see
449  * https://www.hgst.com/sites/default/files/resources/US_SN150_ProdManual.pdf
450  * https://www.hgst.com/sites/default/files/resources/US_SN100_ProdManual.pdf
451  * Appendix A for details
452  */
453
454 typedef void (*subprint_fn_t)(void *buf, uint16_t subtype, uint8_t res, uint32_t size);
455
456 struct subpage_print
457 {
458         uint16_t key;
459         subprint_fn_t fn;
460 };
461
462 static void print_hgst_info_write_errors(void *buf, uint16_t subtype, uint8_t res, uint32_t size);
463 static void print_hgst_info_read_errors(void *buf, uint16_t subtype, uint8_t res, uint32_t size);
464 static void print_hgst_info_verify_errors(void *buf, uint16_t subtype, uint8_t res, uint32_t size);
465 static void print_hgst_info_self_test(void *buf, uint16_t subtype, uint8_t res, uint32_t size);
466 static void print_hgst_info_background_scan(void *buf, uint16_t subtype, uint8_t res, uint32_t size);
467 static void print_hgst_info_erase_errors(void *buf, uint16_t subtype, uint8_t res, uint32_t size);
468 static void print_hgst_info_erase_counts(void *buf, uint16_t subtype, uint8_t res, uint32_t size);
469 static void print_hgst_info_temp_history(void *buf, uint16_t subtype, uint8_t res, uint32_t size);
470 static void print_hgst_info_ssd_perf(void *buf, uint16_t subtype, uint8_t res, uint32_t size);
471 static void print_hgst_info_firmware_load(void *buf, uint16_t subtype, uint8_t res, uint32_t size);
472
473 static struct subpage_print hgst_subpage[] = {
474         { 0x02, print_hgst_info_write_errors },
475         { 0x03, print_hgst_info_read_errors },
476         { 0x05, print_hgst_info_verify_errors },
477         { 0x10, print_hgst_info_self_test },
478         { 0x15, print_hgst_info_background_scan },
479         { 0x30, print_hgst_info_erase_errors },
480         { 0x31, print_hgst_info_erase_counts },
481         { 0x32, print_hgst_info_temp_history },
482         { 0x37, print_hgst_info_ssd_perf },
483         { 0x38, print_hgst_info_firmware_load },
484 };
485
486 /* Print a subpage that is basically just key value pairs */
487 static void
488 print_hgst_info_subpage_gen(void *buf, uint16_t subtype __unused, uint32_t size,
489     const struct kv_name *kv, size_t kv_count)
490 {
491         uint8_t *wsp, *esp;
492         uint16_t ptype;
493         uint8_t plen;
494         uint64_t param;
495         int i;
496
497         wsp = buf;
498         esp = wsp + size;
499         while (wsp < esp) {
500                 ptype = le16dec(wsp);
501                 wsp += 2;
502                 wsp++;                  /* Flags, just ignore */
503                 plen = *wsp++;
504                 param = 0;
505                 for (i = 0; i < plen; i++)
506                         param |= (uint64_t)*wsp++ << (i * 8);
507                 printf("  %-30s: %jd\n", kv_lookup(kv, kv_count, ptype), (uintmax_t)param);
508         }
509 }
510
511 static void
512 print_hgst_info_write_errors(void *buf, uint16_t subtype, uint8_t res __unused, uint32_t size)
513 {
514         static struct kv_name kv[] =
515         {
516                 { 0x0000, "Corrected Without Delay" },
517                 { 0x0001, "Corrected Maybe Delayed" },
518                 { 0x0002, "Re-Writes" },
519                 { 0x0003, "Errors Corrected" },
520                 { 0x0004, "Correct Algorithm Used" },
521                 { 0x0005, "Bytes Processed" },
522                 { 0x0006, "Uncorrected Errors" },
523                 { 0x8000, "Flash Write Commands" },
524                 { 0x8001, "HGST Special" },
525         };
526
527         printf("Write Errors Subpage:\n");
528         print_hgst_info_subpage_gen(buf, subtype, size, kv, nitems(kv));
529 }
530
531 static void
532 print_hgst_info_read_errors(void *buf, uint16_t subtype, uint8_t res __unused, uint32_t size)
533 {
534         static struct kv_name kv[] =
535         {
536                 { 0x0000, "Corrected Without Delay" },
537                 { 0x0001, "Corrected Maybe Delayed" },
538                 { 0x0002, "Re-Reads" },
539                 { 0x0003, "Errors Corrected" },
540                 { 0x0004, "Correct Algorithm Used" },
541                 { 0x0005, "Bytes Processed" },
542                 { 0x0006, "Uncorrected Errors" },
543                 { 0x8000, "Flash Read Commands" },
544                 { 0x8001, "XOR Recovered" },
545                 { 0x8002, "Total Corrected Bits" },
546         };
547
548         printf("Read Errors Subpage:\n");
549         print_hgst_info_subpage_gen(buf, subtype, size, kv, nitems(kv));
550 }
551
552 static void
553 print_hgst_info_verify_errors(void *buf, uint16_t subtype, uint8_t res __unused, uint32_t size)
554 {
555         static struct kv_name kv[] =
556         {
557                 { 0x0000, "Corrected Without Delay" },
558                 { 0x0001, "Corrected Maybe Delayed" },
559                 { 0x0002, "Re-Reads" },
560                 { 0x0003, "Errors Corrected" },
561                 { 0x0004, "Correct Algorithm Used" },
562                 { 0x0005, "Bytes Processed" },
563                 { 0x0006, "Uncorrected Errors" },
564                 { 0x8000, "Commands Processed" },
565         };
566
567         printf("Verify Errors Subpage:\n");
568         print_hgst_info_subpage_gen(buf, subtype, size, kv, nitems(kv));
569 }
570
571 static void
572 print_hgst_info_self_test(void *buf, uint16_t subtype __unused, uint8_t res __unused, uint32_t size)
573 {
574         size_t i;
575         uint8_t *walker = buf;
576         uint16_t code, hrs;
577         uint32_t lba;
578
579         printf("Self Test Subpage:\n");
580         for (i = 0; i < size / 20; i++) {       /* Each entry is 20 bytes */
581                 code = le16dec(walker);
582                 walker += 2;
583                 walker++;                       /* Ignore fixed flags */
584                 if (*walker == 0)               /* Last entry is zero length */
585                         break;
586                 if (*walker++ != 0x10) {
587                         printf("Bad length for self test report\n");
588                         return;
589                 }
590                 printf("  %-30s: %d\n", "Recent Test", code);
591                 printf("    %-28s: %#x\n", "Self-Test Results", *walker & 0xf);
592                 printf("    %-28s: %#x\n", "Self-Test Code", (*walker >> 5) & 0x7);
593                 walker++;
594                 printf("    %-28s: %#x\n", "Self-Test Number", *walker++);
595                 hrs = le16dec(walker);
596                 walker += 2;
597                 lba = le32dec(walker);
598                 walker += 4;
599                 printf("    %-28s: %u\n", "Total Power On Hrs", hrs);
600                 printf("    %-28s: %#jx (%jd)\n", "LBA", (uintmax_t)lba, (uintmax_t)lba);
601                 printf("    %-28s: %#x\n", "Sense Key", *walker++ & 0xf);
602                 printf("    %-28s: %#x\n", "Additional Sense Code", *walker++);
603                 printf("    %-28s: %#x\n", "Additional Sense Qualifier", *walker++);
604                 printf("    %-28s: %#x\n", "Vendor Specific Detail", *walker++);
605         }
606 }
607
608 static void
609 print_hgst_info_background_scan(void *buf, uint16_t subtype __unused, uint8_t res __unused, uint32_t size)
610 {
611         uint8_t *walker = buf;
612         uint8_t status;
613         uint16_t code, nscan, progress;
614         uint32_t pom, nand;
615
616         printf("Background Media Scan Subpage:\n");
617         /* Decode the header */
618         code = le16dec(walker);
619         walker += 2;
620         walker++;                       /* Ignore fixed flags */
621         if (*walker++ != 0x10) {
622                 printf("Bad length for background scan header\n");
623                 return;
624         }
625         if (code != 0) {
626                 printf("Expceted code 0, found code %#x\n", code);
627                 return;
628         }
629         pom = le32dec(walker);
630         walker += 4;
631         walker++;                       /* Reserved */
632         status = *walker++;
633         nscan = le16dec(walker);
634         walker += 2;
635         progress = le16dec(walker);
636         walker += 2;
637         walker += 6;                    /* Reserved */
638         printf("  %-30s: %d\n", "Power On Minutes", pom);
639         printf("  %-30s: %x (%s)\n", "BMS Status", status,
640             status == 0 ? "idle" : (status == 1 ? "active" : (status == 8 ? "suspended" : "unknown")));
641         printf("  %-30s: %d\n", "Number of BMS", nscan);
642         printf("  %-30s: %d\n", "Progress Current BMS", progress);
643         /* Report retirements */
644         if (walker - (uint8_t *)buf != 20) {
645                 printf("Coding error, offset not 20\n");
646                 return;
647         }
648         size -= 20;
649         printf("  %-30s: %d\n", "BMS retirements", size / 0x18);
650         while (size > 0) {
651                 code = le16dec(walker);
652                 walker += 2;
653                 walker++;
654                 if (*walker++ != 0x14) {
655                         printf("Bad length parameter\n");
656                         return;
657                 }
658                 pom = le32dec(walker);
659                 walker += 4;
660                 /*
661                  * Spec sheet says the following are hard coded, if true, just
662                  * print the NAND retirement.
663                  */
664                 if (walker[0] == 0x41 &&
665                     walker[1] == 0x0b &&
666                     walker[2] == 0x01 &&
667                     walker[3] == 0x00 &&
668                     walker[4] == 0x00 &&
669                     walker[5] == 0x00 &&
670                     walker[6] == 0x00 &&
671                     walker[7] == 0x00) {
672                         walker += 8;
673                         walker += 4;    /* Skip reserved */
674                         nand = le32dec(walker);
675                         walker += 4;
676                         printf("  %-30s: %d\n", "Retirement number", code);
677                         printf("    %-28s: %#x\n", "NAND (C/T)BBBPPP", nand);
678                 } else {
679                         printf("Parameter %#x entry corrupt\n", code);
680                         walker += 16;
681                 }
682         }
683 }
684
685 static void
686 print_hgst_info_erase_errors(void *buf, uint16_t subtype __unused, uint8_t res __unused, uint32_t size)
687 {
688         static struct kv_name kv[] =
689         {
690                 { 0x0000, "Corrected Without Delay" },
691                 { 0x0001, "Corrected Maybe Delayed" },
692                 { 0x0002, "Re-Erase" },
693                 { 0x0003, "Errors Corrected" },
694                 { 0x0004, "Correct Algorithm Used" },
695                 { 0x0005, "Bytes Processed" },
696                 { 0x0006, "Uncorrected Errors" },
697                 { 0x8000, "Flash Erase Commands" },
698                 { 0x8001, "Mfg Defect Count" },
699                 { 0x8002, "Grown Defect Count" },
700                 { 0x8003, "Erase Count -- User" },
701                 { 0x8004, "Erase Count -- System" },
702         };
703
704         printf("Erase Errors Subpage:\n");
705         print_hgst_info_subpage_gen(buf, subtype, size, kv, nitems(kv));
706 }
707
708 static void
709 print_hgst_info_erase_counts(void *buf, uint16_t subtype, uint8_t res __unused, uint32_t size)
710 {
711         /* My drive doesn't export this -- so not coding up */
712         printf("XXX: Erase counts subpage: %p, %#x %d\n", buf, subtype, size);
713 }
714
715 static void
716 print_hgst_info_temp_history(void *buf, uint16_t subtype __unused, uint8_t res __unused, uint32_t size __unused)
717 {
718         uint8_t *walker = buf;
719         uint32_t min;
720
721         printf("Temperature History:\n");
722         printf("  %-30s: %d C\n", "Current Temperature", *walker++);
723         printf("  %-30s: %d C\n", "Reference Temperature", *walker++);
724         printf("  %-30s: %d C\n", "Maximum Temperature", *walker++);
725         printf("  %-30s: %d C\n", "Minimum Temperature", *walker++);
726         min = le32dec(walker);
727         walker += 4;
728         printf("  %-30s: %d:%02d:00\n", "Max Temperature Time", min / 60, min % 60);
729         min = le32dec(walker);
730         walker += 4;
731         printf("  %-30s: %d:%02d:00\n", "Over Temperature Duration", min / 60, min % 60);
732         min = le32dec(walker);
733         walker += 4;
734         printf("  %-30s: %d:%02d:00\n", "Min Temperature Time", min / 60, min % 60);
735 }
736
737 static void
738 print_hgst_info_ssd_perf(void *buf, uint16_t subtype __unused, uint8_t res, uint32_t size __unused)
739 {
740         uint8_t *walker = buf;
741         uint64_t val;
742
743         printf("SSD Performance Subpage Type %d:\n", res);
744         val = le64dec(walker);
745         walker += 8;
746         printf("  %-30s: %ju\n", "Host Read Commands", val);
747         val = le64dec(walker);
748         walker += 8;
749         printf("  %-30s: %ju\n", "Host Read Blocks", val);
750         val = le64dec(walker);
751         walker += 8;
752         printf("  %-30s: %ju\n", "Host Cache Read Hits Commands", val);
753         val = le64dec(walker);
754         walker += 8;
755         printf("  %-30s: %ju\n", "Host Cache Read Hits Blocks", val);
756         val = le64dec(walker);
757         walker += 8;
758         printf("  %-30s: %ju\n", "Host Read Commands Stalled", val);
759         val = le64dec(walker);
760         walker += 8;
761         printf("  %-30s: %ju\n", "Host Write Commands", val);
762         val = le64dec(walker);
763         walker += 8;
764         printf("  %-30s: %ju\n", "Host Write Blocks", val);
765         val = le64dec(walker);
766         walker += 8;
767         printf("  %-30s: %ju\n", "Host Write Odd Start Commands", val);
768         val = le64dec(walker);
769         walker += 8;
770         printf("  %-30s: %ju\n", "Host Write Odd End Commands", val);
771         val = le64dec(walker);
772         walker += 8;
773         printf("  %-30s: %ju\n", "Host Write Commands Stalled", val);
774         val = le64dec(walker);
775         walker += 8;
776         printf("  %-30s: %ju\n", "NAND Read Commands", val);
777         val = le64dec(walker);
778         walker += 8;
779         printf("  %-30s: %ju\n", "NAND Read Blocks", val);
780         val = le64dec(walker);
781         walker += 8;
782         printf("  %-30s: %ju\n", "NAND Write Commands", val);
783         val = le64dec(walker);
784         walker += 8;
785         printf("  %-30s: %ju\n", "NAND Write Blocks", val);
786         val = le64dec(walker);
787         walker += 8;
788         printf("  %-30s: %ju\n", "NAND Read Before Writes", val);
789 }
790
791 static void
792 print_hgst_info_firmware_load(void *buf, uint16_t subtype __unused, uint8_t res __unused, uint32_t size __unused)
793 {
794         uint8_t *walker = buf;
795
796         printf("Firmware Load Subpage:\n");
797         printf("  %-30s: %d\n", "Firmware Downloads", le32dec(walker));
798 }
799
800 static void
801 kv_indirect(void *buf, uint32_t subtype, uint8_t res, uint32_t size, struct subpage_print *sp, size_t nsp)
802 {
803         size_t i;
804
805         for (i = 0; i < nsp; i++, sp++) {
806                 if (sp->key == subtype) {
807                         sp->fn(buf, subtype, res, size);
808                         return;
809                 }
810         }
811         printf("No handler for page type %x\n", subtype);
812 }
813
814 static void
815 print_hgst_info_log(const struct nvme_controller_data *cdata __unused, void *buf, uint32_t size __unused)
816 {
817         uint8_t *walker, *end, *subpage;
818         int pages;
819         uint16_t len;
820         uint8_t subtype, res;
821
822         printf("HGST Extra Info Log\n");
823         printf("===================\n");
824
825         walker = buf;
826         pages = *walker++;
827         walker++;
828         len = le16dec(walker);
829         walker += 2;
830         end = walker + len;             /* Length is exclusive of this header */
831         
832         while (walker < end) {
833                 subpage = walker + 4;
834                 subtype = *walker++ & 0x3f;     /* subtype */
835                 res = *walker++;                /* Reserved */
836                 len = le16dec(walker);
837                 walker += len + 2;              /* Length, not incl header */
838                 if (walker > end) {
839                         printf("Ooops! Off the end of the list\n");
840                         break;
841                 }
842                 kv_indirect(subpage, subtype, res, len, hgst_subpage, nitems(hgst_subpage));
843         }
844 }
845
846 /*
847  * Table of log page printer / sizing.
848  *
849  * This includes Intel specific pages that are widely implemented.
850  * Make sure you keep all the pages of one vendor together so -v help
851  * lists all the vendors pages.
852  */
853 static struct logpage_function {
854         uint8_t         log_page;
855         const char     *vendor;
856         const char     *name;
857         print_fn_t      print_fn;
858         size_t          size;
859 } logfuncs[] = {
860         {NVME_LOG_ERROR,                NULL,   "Drive Error Log",
861          print_log_error,               0},
862         {NVME_LOG_HEALTH_INFORMATION,   NULL,   "Health/SMART Data",
863          print_log_health,              sizeof(struct nvme_health_information_page)},
864         {NVME_LOG_FIRMWARE_SLOT,        NULL,   "Firmware Information",
865          print_log_firmware,            sizeof(struct nvme_firmware_page)},
866         {HGST_INFO_LOG,                 "hgst", "Detailed Health/SMART",
867          print_hgst_info_log,           DEFAULT_SIZE},
868         {HGST_INFO_LOG,                 "wds",  "Detailed Health/SMART",
869          print_hgst_info_log,           DEFAULT_SIZE},
870         {INTEL_LOG_TEMP_STATS,          "intel", "Temperature Stats",
871          print_intel_temp_stats,        sizeof(struct intel_log_temp_stats)},
872         {INTEL_LOG_READ_LAT_LOG,        "intel", "Read Latencies",
873          print_intel_read_lat_log,      DEFAULT_SIZE},
874         {INTEL_LOG_WRITE_LAT_LOG,       "intel", "Write Latencies",
875          print_intel_write_lat_log,     DEFAULT_SIZE},
876         {INTEL_LOG_ADD_SMART,           "intel", "Extra Health/SMART Data",
877          print_intel_add_smart,         DEFAULT_SIZE},
878         {INTEL_LOG_ADD_SMART,           "samsung", "Extra Health/SMART Data",
879          print_intel_add_smart,         DEFAULT_SIZE},
880
881         {0, NULL, NULL, NULL, 0},
882 };
883
884 static void
885 logpage_usage(void)
886 {
887         fprintf(stderr, "usage:\n");
888         fprintf(stderr, LOGPAGE_USAGE);
889         exit(1);
890 }
891
892 static void
893 logpage_help(void)
894 {
895         struct logpage_function         *f;
896         const char                      *v;
897
898         fprintf(stderr, "\n");
899         fprintf(stderr, "%-8s %-10s %s\n", "Page", "Vendor","Page Name");
900         fprintf(stderr, "-------- ---------- ----------\n");
901         for (f = logfuncs; f->log_page > 0; f++) {
902                 v = f->vendor == NULL ? "-" : f->vendor;
903                 fprintf(stderr, "0x%02x     %-10s %s\n", f->log_page, v, f->name);
904         }
905
906         exit(1);
907 }
908
909 void
910 logpage(int argc, char *argv[])
911 {
912         int                             fd;
913         int                             log_page = 0, pageflag = false;
914         int                             binflag = false, hexflag = false, ns_specified;
915         int                             opt;
916         char                            *p;
917         char                            cname[64];
918         uint32_t                        nsid, size;
919         void                            *buf;
920         const char                      *vendor = NULL;
921         struct logpage_function         *f;
922         struct nvme_controller_data     cdata;
923         print_fn_t                      print_fn;
924         uint8_t                         ns_smart;
925
926         while ((opt = getopt(argc, argv, "bp:xv:")) != -1) {
927                 switch (opt) {
928                 case 'b':
929                         binflag = true;
930                         break;
931                 case 'p':
932                         if (strcmp(optarg, "help") == 0)
933                                 logpage_help();
934
935                         /* TODO: Add human-readable ASCII page IDs */
936                         log_page = strtol(optarg, &p, 0);
937                         if (p != NULL && *p != '\0') {
938                                 fprintf(stderr,
939                                     "\"%s\" not valid log page id.\n",
940                                     optarg);
941                                 logpage_usage();
942                         }
943                         pageflag = true;
944                         break;
945                 case 'x':
946                         hexflag = true;
947                         break;
948                 case 'v':
949                         if (strcmp(optarg, "help") == 0)
950                                 logpage_help();
951                         vendor = optarg;
952                         break;
953                 }
954         }
955
956         if (!pageflag) {
957                 printf("Missing page_id (-p).\n");
958                 logpage_usage();
959         }
960
961         /* Check that a controller and/or namespace was specified. */
962         if (optind >= argc)
963                 logpage_usage();
964
965         if (strstr(argv[optind], NVME_NS_PREFIX) != NULL) {
966                 ns_specified = true;
967                 parse_ns_str(argv[optind], cname, &nsid);
968                 open_dev(cname, &fd, 1, 1);
969         } else {
970                 ns_specified = false;
971                 nsid = NVME_GLOBAL_NAMESPACE_TAG;
972                 open_dev(argv[optind], &fd, 1, 1);
973         }
974
975         read_controller_data(fd, &cdata);
976
977         ns_smart = (cdata.lpa >> NVME_CTRLR_DATA_LPA_NS_SMART_SHIFT) &
978                 NVME_CTRLR_DATA_LPA_NS_SMART_MASK;
979
980         /*
981          * The log page attribtues indicate whether or not the controller
982          * supports the SMART/Health information log page on a per
983          * namespace basis.
984          */
985         if (ns_specified) {
986                 if (log_page != NVME_LOG_HEALTH_INFORMATION)
987                         errx(1, "log page %d valid only at controller level",
988                             log_page);
989                 if (ns_smart == 0)
990                         errx(1,
991                             "controller does not support per namespace "
992                             "smart/health information");
993         }
994
995         print_fn = print_log_hex;
996         size = DEFAULT_SIZE;
997         if (binflag)
998                 print_fn = print_bin;
999         if (!binflag && !hexflag) {
1000                 /*
1001                  * See if there is a pretty print function for the specified log
1002                  * page.  If one isn't found, we just revert to the default
1003                  * (print_hex). If there was a vendor specified bt the user, and
1004                  * the page is vendor specific, don't match the print function
1005                  * unless the vendors match.
1006                  */
1007                 for (f = logfuncs; f->log_page > 0; f++) {
1008                         if (f->vendor != NULL && vendor != NULL &&
1009                             strcmp(f->vendor, vendor) != 0)
1010                                 continue;
1011                         if (log_page != f->log_page)
1012                                 continue;
1013                         print_fn = f->print_fn;
1014                         size = f->size;
1015                         break;
1016                 }
1017         }
1018
1019         if (log_page == NVME_LOG_ERROR) {
1020                 size = sizeof(struct nvme_error_information_entry);
1021                 size *= (cdata.elpe + 1);
1022         }
1023
1024         /* Read the log page */
1025         buf = get_log_buffer(size);
1026         read_logpage(fd, log_page, nsid, buf, size);
1027         print_fn(&cdata, buf, size);
1028
1029         close(fd);
1030         exit(0);
1031 }