source: rtems-libbsd/freebsd/sbin/nvmecontrol/modules/wdc/wdc.c @ e6acc15

5
Last change on this file since e6acc15 was e6acc15, checked in by Sebastian Huber <sebastian.huber@…>, on 09/20/19 at 05:57:01

NVMECONTROL(8): Port to RTEMS

Update #3821.

  • Property mode set to 100644
File size: 18.1 KB
Line 
1#include <machine/rtems-bsd-user-space.h>
2
3/*-
4 * Copyright (c) 2017 Netflix, Inc.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 */
27
28#ifdef __rtems__
29#include <machine/rtems-bsd-program.h>
30#endif /* __rtems__ */
31#include <sys/cdefs.h>
32__FBSDID("$FreeBSD$");
33
34#include <sys/param.h>
35#include <sys/ioccom.h>
36#include <sys/endian.h>
37
38#include <ctype.h>
39#include <err.h>
40#include <fcntl.h>
41#include <stddef.h>
42#include <stdio.h>
43#include <stdlib.h>
44#include <string.h>
45#include <unistd.h>
46
47#include "../../nvmecontrol.h"
48
49/* Tables for command line parsing */
50
51static cmd_fn_t wdc;
52static cmd_fn_t wdc_cap_diag;
53
54#define NONE 0xffffffffu
55#define NONE64 0xffffffffffffffffull
56#define OPT(l, s, t, opt, addr, desc) { l, s, t, &opt.addr, desc }
57#define OPT_END { NULL, 0, arg_none, NULL, NULL }
58
59static struct cmd wdc_cmd = {
60        .name = "wdc", .fn = wdc, .descr = "wdc vendor specific commands", .ctx_size = 0, .opts = NULL, .args = NULL,
61};
62
63CMD_COMMAND(wdc_cmd);
64
65static struct options
66{
67        const char *template;
68        const char *dev;
69} opt = {
70        .template = NULL,
71        .dev = NULL,
72};
73
74static const struct opts opts[] = {
75        OPT("template", 'o', arg_string, opt, template,
76            "Template for paths to use for different logs"),
77        OPT_END
78};
79
80static const struct args args[] = {
81        { arg_string, &opt.dev, "controller-id" },
82        { arg_none, NULL, NULL },
83};
84
85static struct cmd cap_diag_cmd = {
86        .name = "cap-diag",
87        .fn = wdc_cap_diag,
88        .descr = "Retrieve the cap-diag logs from the drive",
89        .ctx_size = sizeof(struct options),
90        .opts = opts,
91        .args = args,
92};
93
94CMD_SUBCOMMAND(wdc_cmd, cap_diag_cmd);
95
96#define WDC_NVME_TOC_SIZE       8
97
98#define WDC_NVME_CAP_DIAG_OPCODE        0xe6
99#define WDC_NVME_CAP_DIAG_CMD           0x0000
100
101static void
102wdc_append_serial_name(int fd, char *buf, size_t len, const char *suffix)
103{
104        struct nvme_controller_data     cdata;
105        char sn[NVME_SERIAL_NUMBER_LENGTH + 1];
106        char *walker;
107
108        len -= strlen(buf);
109        buf += strlen(buf);
110        read_controller_data(fd, &cdata);
111        memcpy(sn, cdata.sn, NVME_SERIAL_NUMBER_LENGTH);
112        walker = sn + NVME_SERIAL_NUMBER_LENGTH - 1;
113        while (walker > sn && *walker == ' ')
114                walker--;
115        *++walker = '\0';
116        snprintf(buf, len, "%s%s.bin", sn, suffix);
117}
118
119static void
120wdc_get_data(int fd, uint32_t opcode, uint32_t len, uint32_t off, uint32_t cmd,
121    uint8_t *buffer, size_t buflen)
122{
123        struct nvme_pt_command  pt;
124
125        memset(&pt, 0, sizeof(pt));
126        pt.cmd.opc = opcode;
127        pt.cmd.cdw10 = htole32(len / sizeof(uint32_t)); /* - 1 like all the others ??? */
128        pt.cmd.cdw11 = htole32(off / sizeof(uint32_t));
129        pt.cmd.cdw12 = htole32(cmd);
130        pt.buf = buffer;
131        pt.len = buflen;
132        pt.is_read = 1;
133//      printf("opcode %#x cdw10(len) %#x cdw11(offset?) %#x cdw12(cmd/sub) %#x buflen %zd\n",
134//          (int)opcode, (int)cdw10, (int)cdw11, (int)cdw12, buflen);
135
136        if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0)
137                err(1, "wdc_get_data request failed");
138        if (nvme_completion_is_error(&pt.cpl))
139                errx(1, "wdc_get_data request returned error");
140}
141
142static void
143wdc_do_dump(int fd, char *tmpl, const char *suffix, uint32_t opcode,
144    uint32_t cmd, int len_off)
145{
146        int first;
147        int fd2;
148        uint8_t *buf;
149        uint32_t len, offset;
150        size_t resid;
151
152        wdc_append_serial_name(fd, tmpl, MAXPATHLEN, suffix);
153
154        /* XXX overwrite protection? */
155        fd2 = open(tmpl, O_WRONLY | O_CREAT | O_TRUNC, 0644);
156        if (fd2 < 0)
157                err(1, "open %s", tmpl);
158        buf = aligned_alloc(PAGE_SIZE, NVME_MAX_XFER_SIZE);
159        if (buf == NULL)
160                errx(1, "Can't get buffer to read dump");
161        offset = 0;
162        len = NVME_MAX_XFER_SIZE;
163        first = 1;
164
165        do {
166                resid = len > NVME_MAX_XFER_SIZE ? NVME_MAX_XFER_SIZE : len;
167                wdc_get_data(fd, opcode, resid, offset, cmd, buf, resid);
168
169                if (first) {
170                        len = be32dec(buf + len_off);
171                        if (len == 0)
172                                errx(1, "No data for %s", suffix);
173                        if (memcmp("E6LG", buf, 4) != 0)
174                                printf("Expected header of E6LG, found '%4.4s' instead\n",
175                                    buf);
176                        printf("Dumping %d bytes of version %d.%d log to %s\n", len,
177                            buf[8], buf[9], tmpl);
178                        /*
179                         * Adjust amount to dump if total dump < 1MB,
180                         * though it likely doesn't matter to the WDC
181                         * analysis tools.
182                         */
183                        if (resid > len)
184                                resid = len;
185                        first = 0;
186                }
187                if (write(fd2, buf, resid) != (ssize_t)resid)
188                        err(1, "write");
189                offset += resid;
190                len -= resid;
191        } while (len > 0);
192        free(buf);
193        close(fd2);
194}
195
196static void
197wdc_cap_diag(const struct cmd *f, int argc, char *argv[])
198{
199        char tmpl[MAXPATHLEN];
200        int fd;
201
202        if (arg_parse(argc, argv, f))
203                return;
204        if (opt.template == NULL) {
205                fprintf(stderr, "Missing template arg.\n");
206                arg_help(argc, argv, f);
207        }
208        strlcpy(tmpl, opt.template, sizeof(tmpl));
209        open_dev(opt.dev, &fd, 1, 1);
210        wdc_do_dump(fd, tmpl, "cap_diag", WDC_NVME_CAP_DIAG_OPCODE,
211            WDC_NVME_CAP_DIAG_CMD, 4);
212
213        close(fd);
214
215        exit(1);       
216}
217
218static void
219wdc(const struct cmd *nf __unused, int argc, char *argv[])
220{
221
222        cmd_dispatch(argc, argv, &wdc_cmd);
223}
224
225/*
226 * HGST's 0xc1 page. This is a grab bag of additional data. Please see
227 * https://www.hgst.com/sites/default/files/resources/US_SN150_ProdManual.pdf
228 * https://www.hgst.com/sites/default/files/resources/US_SN100_ProdManual.pdf
229 * Appendix A for details
230 */
231
232typedef void (*subprint_fn_t)(void *buf, uint16_t subtype, uint8_t res, uint32_t size);
233
234struct subpage_print
235{
236        uint16_t key;
237        subprint_fn_t fn;
238};
239
240static void print_hgst_info_write_errors(void *buf, uint16_t subtype, uint8_t res, uint32_t size);
241static void print_hgst_info_read_errors(void *buf, uint16_t subtype, uint8_t res, uint32_t size);
242static void print_hgst_info_verify_errors(void *buf, uint16_t subtype, uint8_t res, uint32_t size);
243static void print_hgst_info_self_test(void *buf, uint16_t subtype, uint8_t res, uint32_t size);
244static void print_hgst_info_background_scan(void *buf, uint16_t subtype, uint8_t res, uint32_t size);
245static void print_hgst_info_erase_errors(void *buf, uint16_t subtype, uint8_t res, uint32_t size);
246static void print_hgst_info_erase_counts(void *buf, uint16_t subtype, uint8_t res, uint32_t size);
247static void print_hgst_info_temp_history(void *buf, uint16_t subtype, uint8_t res, uint32_t size);
248static void print_hgst_info_ssd_perf(void *buf, uint16_t subtype, uint8_t res, uint32_t size);
249static void print_hgst_info_firmware_load(void *buf, uint16_t subtype, uint8_t res, uint32_t size);
250
251static struct subpage_print hgst_subpage[] = {
252        { 0x02, print_hgst_info_write_errors },
253        { 0x03, print_hgst_info_read_errors },
254        { 0x05, print_hgst_info_verify_errors },
255        { 0x10, print_hgst_info_self_test },
256        { 0x15, print_hgst_info_background_scan },
257        { 0x30, print_hgst_info_erase_errors },
258        { 0x31, print_hgst_info_erase_counts },
259        { 0x32, print_hgst_info_temp_history },
260        { 0x37, print_hgst_info_ssd_perf },
261        { 0x38, print_hgst_info_firmware_load },
262};
263
264/* Print a subpage that is basically just key value pairs */
265static void
266print_hgst_info_subpage_gen(void *buf, uint16_t subtype __unused, uint32_t size,
267    const struct kv_name *kv, size_t kv_count)
268{
269        uint8_t *wsp, *esp;
270        uint16_t ptype;
271        uint8_t plen;
272        uint64_t param;
273        int i;
274
275        wsp = buf;
276        esp = wsp + size;
277        while (wsp < esp) {
278                ptype = le16dec(wsp);
279                wsp += 2;
280                wsp++;                  /* Flags, just ignore */
281                plen = *wsp++;
282                param = 0;
283                for (i = 0; i < plen; i++)
284                        param |= (uint64_t)*wsp++ << (i * 8);
285                printf("  %-30s: %jd\n", kv_lookup(kv, kv_count, ptype), (uintmax_t)param);
286        }
287}
288
289static void
290print_hgst_info_write_errors(void *buf, uint16_t subtype, uint8_t res __unused, uint32_t size)
291{
292        static struct kv_name kv[] =
293        {
294                { 0x0000, "Corrected Without Delay" },
295                { 0x0001, "Corrected Maybe Delayed" },
296                { 0x0002, "Re-Writes" },
297                { 0x0003, "Errors Corrected" },
298                { 0x0004, "Correct Algorithm Used" },
299                { 0x0005, "Bytes Processed" },
300                { 0x0006, "Uncorrected Errors" },
301                { 0x8000, "Flash Write Commands" },
302                { 0x8001, "HGST Special" },
303        };
304
305        printf("Write Errors Subpage:\n");
306        print_hgst_info_subpage_gen(buf, subtype, size, kv, nitems(kv));
307}
308
309static void
310print_hgst_info_read_errors(void *buf, uint16_t subtype, uint8_t res __unused, uint32_t size)
311{
312        static struct kv_name kv[] =
313        {
314                { 0x0000, "Corrected Without Delay" },
315                { 0x0001, "Corrected Maybe Delayed" },
316                { 0x0002, "Re-Reads" },
317                { 0x0003, "Errors Corrected" },
318                { 0x0004, "Correct Algorithm Used" },
319                { 0x0005, "Bytes Processed" },
320                { 0x0006, "Uncorrected Errors" },
321                { 0x8000, "Flash Read Commands" },
322                { 0x8001, "XOR Recovered" },
323                { 0x8002, "Total Corrected Bits" },
324        };
325
326        printf("Read Errors Subpage:\n");
327        print_hgst_info_subpage_gen(buf, subtype, size, kv, nitems(kv));
328}
329
330static void
331print_hgst_info_verify_errors(void *buf, uint16_t subtype, uint8_t res __unused, uint32_t size)
332{
333        static struct kv_name kv[] =
334        {
335                { 0x0000, "Corrected Without Delay" },
336                { 0x0001, "Corrected Maybe Delayed" },
337                { 0x0002, "Re-Reads" },
338                { 0x0003, "Errors Corrected" },
339                { 0x0004, "Correct Algorithm Used" },
340                { 0x0005, "Bytes Processed" },
341                { 0x0006, "Uncorrected Errors" },
342                { 0x8000, "Commands Processed" },
343        };
344
345        printf("Verify Errors Subpage:\n");
346        print_hgst_info_subpage_gen(buf, subtype, size, kv, nitems(kv));
347}
348
349static void
350print_hgst_info_self_test(void *buf, uint16_t subtype __unused, uint8_t res __unused, uint32_t size)
351{
352        size_t i;
353        uint8_t *walker = buf;
354        uint16_t code, hrs;
355        uint32_t lba;
356
357        printf("Self Test Subpage:\n");
358        for (i = 0; i < size / 20; i++) {       /* Each entry is 20 bytes */
359                code = le16dec(walker);
360                walker += 2;
361                walker++;                       /* Ignore fixed flags */
362                if (*walker == 0)               /* Last entry is zero length */
363                        break;
364                if (*walker++ != 0x10) {
365                        printf("Bad length for self test report\n");
366                        return;
367                }
368                printf("  %-30s: %d\n", "Recent Test", code);
369                printf("    %-28s: %#x\n", "Self-Test Results", *walker & 0xf);
370                printf("    %-28s: %#x\n", "Self-Test Code", (*walker >> 5) & 0x7);
371                walker++;
372                printf("    %-28s: %#x\n", "Self-Test Number", *walker++);
373                hrs = le16dec(walker);
374                walker += 2;
375                lba = le32dec(walker);
376                walker += 4;
377                printf("    %-28s: %u\n", "Total Power On Hrs", hrs);
378                printf("    %-28s: %#jx (%jd)\n", "LBA", (uintmax_t)lba, (uintmax_t)lba);
379                printf("    %-28s: %#x\n", "Sense Key", *walker++ & 0xf);
380                printf("    %-28s: %#x\n", "Additional Sense Code", *walker++);
381                printf("    %-28s: %#x\n", "Additional Sense Qualifier", *walker++);
382                printf("    %-28s: %#x\n", "Vendor Specific Detail", *walker++);
383        }
384}
385
386static void
387print_hgst_info_background_scan(void *buf, uint16_t subtype __unused, uint8_t res __unused, uint32_t size)
388{
389        uint8_t *walker = buf;
390        uint8_t status;
391        uint16_t code, nscan, progress;
392        uint32_t pom, nand;
393
394        printf("Background Media Scan Subpage:\n");
395        /* Decode the header */
396        code = le16dec(walker);
397        walker += 2;
398        walker++;                       /* Ignore fixed flags */
399        if (*walker++ != 0x10) {
400                printf("Bad length for background scan header\n");
401                return;
402        }
403        if (code != 0) {
404                printf("Expceted code 0, found code %#x\n", code);
405                return;
406        }
407        pom = le32dec(walker);
408        walker += 4;
409        walker++;                       /* Reserved */
410        status = *walker++;
411        nscan = le16dec(walker);
412        walker += 2;
413        progress = le16dec(walker);
414        walker += 2;
415        walker += 6;                    /* Reserved */
416        printf("  %-30s: %d\n", "Power On Minutes", pom);
417        printf("  %-30s: %x (%s)\n", "BMS Status", status,
418            status == 0 ? "idle" : (status == 1 ? "active" : (status == 8 ? "suspended" : "unknown")));
419        printf("  %-30s: %d\n", "Number of BMS", nscan);
420        printf("  %-30s: %d\n", "Progress Current BMS", progress);
421        /* Report retirements */
422        if (walker - (uint8_t *)buf != 20) {
423                printf("Coding error, offset not 20\n");
424                return;
425        }
426        size -= 20;
427        printf("  %-30s: %d\n", "BMS retirements", size / 0x18);
428        while (size > 0) {
429                code = le16dec(walker);
430                walker += 2;
431                walker++;
432                if (*walker++ != 0x14) {
433                        printf("Bad length parameter\n");
434                        return;
435                }
436                pom = le32dec(walker);
437                walker += 4;
438                /*
439                 * Spec sheet says the following are hard coded, if true, just
440                 * print the NAND retirement.
441                 */
442                if (walker[0] == 0x41 &&
443                    walker[1] == 0x0b &&
444                    walker[2] == 0x01 &&
445                    walker[3] == 0x00 &&
446                    walker[4] == 0x00 &&
447                    walker[5] == 0x00 &&
448                    walker[6] == 0x00 &&
449                    walker[7] == 0x00) {
450                        walker += 8;
451                        walker += 4;    /* Skip reserved */
452                        nand = le32dec(walker);
453                        walker += 4;
454                        printf("  %-30s: %d\n", "Retirement number", code);
455                        printf("    %-28s: %#x\n", "NAND (C/T)BBBPPP", nand);
456                } else {
457                        printf("Parameter %#x entry corrupt\n", code);
458                        walker += 16;
459                }
460        }
461}
462
463static void
464print_hgst_info_erase_errors(void *buf, uint16_t subtype __unused, uint8_t res __unused, uint32_t size)
465{
466        static struct kv_name kv[] =
467        {
468                { 0x0000, "Corrected Without Delay" },
469                { 0x0001, "Corrected Maybe Delayed" },
470                { 0x0002, "Re-Erase" },
471                { 0x0003, "Errors Corrected" },
472                { 0x0004, "Correct Algorithm Used" },
473                { 0x0005, "Bytes Processed" },
474                { 0x0006, "Uncorrected Errors" },
475                { 0x8000, "Flash Erase Commands" },
476                { 0x8001, "Mfg Defect Count" },
477                { 0x8002, "Grown Defect Count" },
478                { 0x8003, "Erase Count -- User" },
479                { 0x8004, "Erase Count -- System" },
480        };
481
482        printf("Erase Errors Subpage:\n");
483        print_hgst_info_subpage_gen(buf, subtype, size, kv, nitems(kv));
484}
485
486static void
487print_hgst_info_erase_counts(void *buf, uint16_t subtype, uint8_t res __unused, uint32_t size)
488{
489        /* My drive doesn't export this -- so not coding up */
490        printf("XXX: Erase counts subpage: %p, %#x %d\n", buf, subtype, size);
491}
492
493static void
494print_hgst_info_temp_history(void *buf, uint16_t subtype __unused, uint8_t res __unused, uint32_t size __unused)
495{
496        uint8_t *walker = buf;
497        uint32_t min;
498
499        printf("Temperature History:\n");
500        printf("  %-30s: %d C\n", "Current Temperature", *walker++);
501        printf("  %-30s: %d C\n", "Reference Temperature", *walker++);
502        printf("  %-30s: %d C\n", "Maximum Temperature", *walker++);
503        printf("  %-30s: %d C\n", "Minimum Temperature", *walker++);
504        min = le32dec(walker);
505        walker += 4;
506        printf("  %-30s: %d:%02d:00\n", "Max Temperature Time", min / 60, min % 60);
507        min = le32dec(walker);
508        walker += 4;
509        printf("  %-30s: %d:%02d:00\n", "Over Temperature Duration", min / 60, min % 60);
510        min = le32dec(walker);
511        walker += 4;
512        printf("  %-30s: %d:%02d:00\n", "Min Temperature Time", min / 60, min % 60);
513}
514
515static void
516print_hgst_info_ssd_perf(void *buf, uint16_t subtype __unused, uint8_t res, uint32_t size __unused)
517{
518        uint8_t *walker = buf;
519        uint64_t val;
520
521        printf("SSD Performance Subpage Type %d:\n", res);
522        val = le64dec(walker);
523        walker += 8;
524        printf("  %-30s: %ju\n", "Host Read Commands", val);
525        val = le64dec(walker);
526        walker += 8;
527        printf("  %-30s: %ju\n", "Host Read Blocks", val);
528        val = le64dec(walker);
529        walker += 8;
530        printf("  %-30s: %ju\n", "Host Cache Read Hits Commands", val);
531        val = le64dec(walker);
532        walker += 8;
533        printf("  %-30s: %ju\n", "Host Cache Read Hits Blocks", val);
534        val = le64dec(walker);
535        walker += 8;
536        printf("  %-30s: %ju\n", "Host Read Commands Stalled", val);
537        val = le64dec(walker);
538        walker += 8;
539        printf("  %-30s: %ju\n", "Host Write Commands", val);
540        val = le64dec(walker);
541        walker += 8;
542        printf("  %-30s: %ju\n", "Host Write Blocks", val);
543        val = le64dec(walker);
544        walker += 8;
545        printf("  %-30s: %ju\n", "Host Write Odd Start Commands", val);
546        val = le64dec(walker);
547        walker += 8;
548        printf("  %-30s: %ju\n", "Host Write Odd End Commands", val);
549        val = le64dec(walker);
550        walker += 8;
551        printf("  %-30s: %ju\n", "Host Write Commands Stalled", val);
552        val = le64dec(walker);
553        walker += 8;
554        printf("  %-30s: %ju\n", "NAND Read Commands", val);
555        val = le64dec(walker);
556        walker += 8;
557        printf("  %-30s: %ju\n", "NAND Read Blocks", val);
558        val = le64dec(walker);
559        walker += 8;
560        printf("  %-30s: %ju\n", "NAND Write Commands", val);
561        val = le64dec(walker);
562        walker += 8;
563        printf("  %-30s: %ju\n", "NAND Write Blocks", val);
564        val = le64dec(walker);
565        walker += 8;
566        printf("  %-30s: %ju\n", "NAND Read Before Writes", val);
567}
568
569static void
570print_hgst_info_firmware_load(void *buf, uint16_t subtype __unused, uint8_t res __unused, uint32_t size __unused)
571{
572        uint8_t *walker = buf;
573
574        printf("Firmware Load Subpage:\n");
575        printf("  %-30s: %d\n", "Firmware Downloads", le32dec(walker));
576}
577
578static void
579kv_indirect(void *buf, uint32_t subtype, uint8_t res, uint32_t size, struct subpage_print *sp, size_t nsp)
580{
581        size_t i;
582
583        for (i = 0; i < nsp; i++, sp++) {
584                if (sp->key == subtype) {
585                        sp->fn(buf, subtype, res, size);
586                        return;
587                }
588        }
589        printf("No handler for page type %x\n", subtype);
590}
591
592static void
593print_hgst_info_log(const struct nvme_controller_data *cdata __unused, void *buf, uint32_t size __unused)
594{
595        uint8_t *walker, *end, *subpage;
596        int pages;
597        uint16_t len;
598        uint8_t subtype, res;
599
600        printf("HGST Extra Info Log\n");
601        printf("===================\n");
602
603        walker = buf;
604        pages = *walker++;
605        walker++;
606        len = le16dec(walker);
607        walker += 2;
608        end = walker + len;             /* Length is exclusive of this header */
609       
610        while (walker < end) {
611                subpage = walker + 4;
612                subtype = *walker++ & 0x3f;     /* subtype */
613                res = *walker++;                /* Reserved */
614                len = le16dec(walker);
615                walker += len + 2;              /* Length, not incl header */
616                if (walker > end) {
617                        printf("Ooops! Off the end of the list\n");
618                        break;
619                }
620                kv_indirect(subpage, subtype, res, len, hgst_subpage, nitems(hgst_subpage));
621        }
622}
623
624NVME_LOGPAGE(hgst_info,
625    HGST_INFO_LOG,                      "hgst", "Detailed Health/SMART",
626    print_hgst_info_log,                DEFAULT_SIZE);
627NVME_LOGPAGE(wdc_info,
628    HGST_INFO_LOG,                      "wdc",  "Detailed Health/SMART",
629    print_hgst_info_log,                DEFAULT_SIZE);
Note: See TracBrowser for help on using the repository browser.