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