xref: /illumos-gate/usr/src/cmd/nvmeadm/nvmeadm_sandisk.c (revision fe5224a3e61e4d791ea239810e79697fc96f7b16)
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