1 /*
2 * This file and its contents are supplied under the terms of the
3 * Common Development and Distribution License ("CDDL"), version 1.0.
4 * You may only use this file in accordance with the terms of version
5 * 1.0 of the CDDL.
6 *
7 * A full copy of the text of the CDDL should have accompanied this
8 * source. A copy of the CDDL is also available via the Internet at
9 * http://www.illumos.org/license/CDDL.
10 */
11
12 /*
13 * Copyright 2026 Oxide Computer Company
14 */
15
16 /*
17 * Sandisk vendor-specific commands. These generally start with the x6x
18 * generation of controllers. For prior generations (e.g. x4x and x5x), see
19 * nvmeadm_wdc.c.
20 */
21
22 #include <stdio.h>
23 #include <err.h>
24 #include <stdlib.h>
25 #include <sys/stat.h>
26 #include <fcntl.h>
27 #include <unistd.h>
28 #include <sys/debug.h>
29 #include <sys/sysmacros.h>
30 #include <sys/nvme/wdc.h>
31
32 #include "nvmeadm.h"
33
34 typedef struct {
35 uint8_t se_lane;
36 const char *se_output;
37 } sandisk_eye_t;
38
39 void
usage_sandisk_hwrev(const char * c_name)40 usage_sandisk_hwrev(const char *c_name)
41 {
42 (void) fprintf(stderr, "%s <ctl>\n\n"
43 " Print device hardware revision\n", c_name);
44 }
45
46 int
do_sandisk_hwrev(const nvme_process_arg_t * npa)47 do_sandisk_hwrev(const nvme_process_arg_t *npa)
48 {
49 uint8_t major, minor;
50 nvme_vuc_disc_t *vuc;
51
52 if (npa->npa_argc > 0) {
53 errx(-1, "%s passed extraneous arguments starting with %s",
54 npa->npa_cmd->c_name, npa->npa_argv[0]);
55 }
56
57 vuc = nvmeadm_vuc_init(npa, npa->npa_cmd->c_name);
58 if (!nvme_sndk_hw_rev(npa->npa_ctrl, &major, &minor)) {
59 nvmeadm_fatal(npa, "failed to retrieve hardware revision");
60 }
61
62 (void) printf("%u.%u\n", major, minor);
63 nvmeadm_vuc_fini(npa, vuc);
64 return (0);
65 }
66
67 void
usage_sandisk_pcieye(const char * c_name)68 usage_sandisk_pcieye(const char *c_name)
69 {
70 (void) fprintf(stderr, "%s -l lane -o output <ctl>\n\n"
71 " Write PCIe eye data from the specified lane to output file.\n",
72 c_name);
73 }
74
75 void
optparse_sandisk_pcieye(nvme_process_arg_t * npa)76 optparse_sandisk_pcieye(nvme_process_arg_t *npa)
77 {
78 int c;
79 sandisk_eye_t *eye;
80
81 if ((eye = calloc(1, sizeof (sandisk_eye_t))) == NULL) {
82 err(-1, "failed to allocate memory for pci-eye options "
83 "structure");
84 }
85
86 eye->se_lane = UINT8_MAX;
87 while ((c = getopt(npa->npa_argc, npa->npa_argv, ":l:o:")) != -1) {
88 const char *errstr;
89
90 switch (c) {
91 case 'l':
92 eye->se_lane = (uint8_t)strtonumx(optarg, 0, 3, &errstr,
93 0);
94 if (errstr != NULL) {
95 errx(-1, "invalid lane specified, valid values "
96 "are 0-3: %s is %s", optarg, errstr);
97 }
98 break;
99 case 'o':
100 eye->se_output = optarg;
101 break;
102 case '?':
103 errx(-1, "unknown option: -%c", optopt);
104 case ':':
105 errx(-1, "option -%c requires an argument", optopt);
106 }
107 }
108
109 if (eye->se_lane == UINT8_MAX) {
110 errx(-1, "missing required PCIe lane (0-3), specify with -l");
111 }
112
113 if (eye->se_output == NULL) {
114 errx(-1, "missing required output file, specify with -o");
115 }
116
117 npa->npa_cmd_arg = eye;
118 }
119
120 int
do_sandisk_pcieye(const nvme_process_arg_t * npa)121 do_sandisk_pcieye(const nvme_process_arg_t *npa)
122 {
123 const sandisk_eye_t *eye = npa->npa_cmd_arg;
124 nvme_vuc_disc_t *vuc;
125 uint8_t *buf;
126 int ofd;
127
128 if ((buf = calloc(WDC_SN861_VUC_EYE_LEN, sizeof (uint8_t))) == NULL) {
129 err(-1, "failed to allocate eye diagram buffer");
130 }
131
132 vuc = nvmeadm_vuc_init(npa, npa->npa_cmd->c_name);
133
134 ofd = open(eye->se_output, O_WRONLY | O_CREAT | O_TRUNC, 0644);
135 if (ofd < 0) {
136 err(-1, "failed to open output file %s", eye->se_output);
137 }
138
139 if (!nvme_sndk_pci_eye(npa->npa_ctrl, eye->se_lane, buf,
140 WDC_SN861_VUC_EYE_LEN)) {
141 nvmeadm_fatal(npa, "failed to retrieve PCIe eye information");
142 }
143
144 size_t off = 0, len = WDC_SN861_VUC_EYE_LEN;
145 while (len > 0) {
146 size_t towrite = MIN(len, 32 * 1024);
147 ssize_t ret = write(ofd, buf + off, towrite);
148 if (ret < 0) {
149 err(-1, "failed to write eye data to output file");
150 }
151
152 off += (size_t)ret;
153 len -= (size_t)ret;
154 }
155
156 nvmeadm_vuc_fini(npa, vuc);
157 VERIFY0(close(ofd));
158 free(buf);
159 return (0);
160 }
161