1*2dfbbe2dSRobert Mustacchi /*
2*2dfbbe2dSRobert Mustacchi * This file and its contents are supplied under the terms of the
3*2dfbbe2dSRobert Mustacchi * Common Development and Distribution License ("CDDL"), version 1.0.
4*2dfbbe2dSRobert Mustacchi * You may only use this file in accordance with the terms of version
5*2dfbbe2dSRobert Mustacchi * 1.0 of the CDDL.
6*2dfbbe2dSRobert Mustacchi *
7*2dfbbe2dSRobert Mustacchi * A full copy of the text of the CDDL should have accompanied this
8*2dfbbe2dSRobert Mustacchi * source. A copy of the CDDL is also available via the Internet at
9*2dfbbe2dSRobert Mustacchi * http://www.illumos.org/license/CDDL.
10*2dfbbe2dSRobert Mustacchi */
11*2dfbbe2dSRobert Mustacchi
12*2dfbbe2dSRobert Mustacchi /*
13*2dfbbe2dSRobert Mustacchi * Copyright 2026 Oxide Computer Company
14*2dfbbe2dSRobert Mustacchi */
15*2dfbbe2dSRobert Mustacchi
16*2dfbbe2dSRobert Mustacchi /*
17*2dfbbe2dSRobert Mustacchi * NVMe Vendor Unique Command related functions.
18*2dfbbe2dSRobert Mustacchi */
19*2dfbbe2dSRobert Mustacchi
20*2dfbbe2dSRobert Mustacchi #include <getopt.h>
21*2dfbbe2dSRobert Mustacchi #include <err.h>
22*2dfbbe2dSRobert Mustacchi #include <stdlib.h>
23*2dfbbe2dSRobert Mustacchi #include <string.h>
24*2dfbbe2dSRobert Mustacchi #include <sys/stat.h>
25*2dfbbe2dSRobert Mustacchi #include <fcntl.h>
26*2dfbbe2dSRobert Mustacchi #include <unistd.h>
27*2dfbbe2dSRobert Mustacchi #include <sys/debug.h>
28*2dfbbe2dSRobert Mustacchi #include <sys/sysmacros.h>
29*2dfbbe2dSRobert Mustacchi
30*2dfbbe2dSRobert Mustacchi #include "nvmeadm.h"
31*2dfbbe2dSRobert Mustacchi
32*2dfbbe2dSRobert Mustacchi /*
33*2dfbbe2dSRobert Mustacchi * We choose 60 seconds as a reasonable enough default VUC timeout. This is a
34*2dfbbe2dSRobert Mustacchi * fairly arbitrary selection but should be good enough for most non-formatting
35*2dfbbe2dSRobert Mustacchi * related commands.
36*2dfbbe2dSRobert Mustacchi */
37*2dfbbe2dSRobert Mustacchi #define NVMEADM_VUC_TO_DEFAULT 60
38*2dfbbe2dSRobert Mustacchi
39*2dfbbe2dSRobert Mustacchi /*
40*2dfbbe2dSRobert Mustacchi * We need some upper bound on how much data we'll read in and zero. The kernel
41*2dfbbe2dSRobert Mustacchi * may change its maximum that it'll allow. It'd probably be smart of us to ask
42*2dfbbe2dSRobert Mustacchi * what the max is to help reduce hardcoding. For now we use 2x its current
43*2dfbbe2dSRobert Mustacchi * value 32 MiB. We make the minimum 4 bytes because we need 4 byte alignment.
44*2dfbbe2dSRobert Mustacchi */
45*2dfbbe2dSRobert Mustacchi #define NVMEADM_VUC_LEN_MAX (32 * 1024 * 1024)
46*2dfbbe2dSRobert Mustacchi #define NVMEADM_VUC_LEN_ALIGN 4
47*2dfbbe2dSRobert Mustacchi
48*2dfbbe2dSRobert Mustacchi typedef struct nvmeadm_vuc {
49*2dfbbe2dSRobert Mustacchi uint8_t vuc_opc;
50*2dfbbe2dSRobert Mustacchi uint32_t vuc_nsid;
51*2dfbbe2dSRobert Mustacchi uint32_t vuc_cdw12;
52*2dfbbe2dSRobert Mustacchi uint32_t vuc_cdw13;
53*2dfbbe2dSRobert Mustacchi uint32_t vuc_cdw14;
54*2dfbbe2dSRobert Mustacchi uint32_t vuc_cdw15;
55*2dfbbe2dSRobert Mustacchi uint32_t vuc_timeout;
56*2dfbbe2dSRobert Mustacchi uint32_t vuc_dlen;
57*2dfbbe2dSRobert Mustacchi const char *vuc_input;
58*2dfbbe2dSRobert Mustacchi const char *vuc_output;
59*2dfbbe2dSRobert Mustacchi nvme_lock_level_t vuc_lock;
60*2dfbbe2dSRobert Mustacchi nvme_vuc_disc_impact_t vuc_impact;
61*2dfbbe2dSRobert Mustacchi } nvmeadm_vuc_t;
62*2dfbbe2dSRobert Mustacchi
63*2dfbbe2dSRobert Mustacchi nvme_vuc_disc_t *
nvmeadm_vuc_init(const nvme_process_arg_t * npa,const char * name)64*2dfbbe2dSRobert Mustacchi nvmeadm_vuc_init(const nvme_process_arg_t *npa, const char *name)
65*2dfbbe2dSRobert Mustacchi {
66*2dfbbe2dSRobert Mustacchi nvme_vuc_disc_t *vuc;
67*2dfbbe2dSRobert Mustacchi nvme_vuc_disc_lock_t lock;
68*2dfbbe2dSRobert Mustacchi
69*2dfbbe2dSRobert Mustacchi if (!nvme_vuc_discover_by_name(npa->npa_ctrl, name, 0, &vuc)) {
70*2dfbbe2dSRobert Mustacchi nvmeadm_fatal(npa, "%s does not support operation %s: device "
71*2dfbbe2dSRobert Mustacchi "does not support vendor unique command %s", npa->npa_name,
72*2dfbbe2dSRobert Mustacchi npa->npa_cmd->c_name, name);
73*2dfbbe2dSRobert Mustacchi }
74*2dfbbe2dSRobert Mustacchi
75*2dfbbe2dSRobert Mustacchi lock = nvme_vuc_disc_lock(vuc);
76*2dfbbe2dSRobert Mustacchi switch (lock) {
77*2dfbbe2dSRobert Mustacchi case NVME_VUC_DISC_LOCK_NONE:
78*2dfbbe2dSRobert Mustacchi break;
79*2dfbbe2dSRobert Mustacchi case NVME_VUC_DISC_LOCK_READ:
80*2dfbbe2dSRobert Mustacchi nvmeadm_excl(npa, NVME_LOCK_L_READ);
81*2dfbbe2dSRobert Mustacchi break;
82*2dfbbe2dSRobert Mustacchi case NVME_VUC_DISC_LOCK_WRITE:
83*2dfbbe2dSRobert Mustacchi nvmeadm_excl(npa, NVME_LOCK_L_WRITE);
84*2dfbbe2dSRobert Mustacchi break;
85*2dfbbe2dSRobert Mustacchi }
86*2dfbbe2dSRobert Mustacchi
87*2dfbbe2dSRobert Mustacchi return (vuc);
88*2dfbbe2dSRobert Mustacchi }
89*2dfbbe2dSRobert Mustacchi
90*2dfbbe2dSRobert Mustacchi void
nvmeadm_vuc_fini(const nvme_process_arg_t * npa,nvme_vuc_disc_t * vuc)91*2dfbbe2dSRobert Mustacchi nvmeadm_vuc_fini(const nvme_process_arg_t *npa, nvme_vuc_disc_t *vuc)
92*2dfbbe2dSRobert Mustacchi {
93*2dfbbe2dSRobert Mustacchi if (nvme_vuc_disc_lock(vuc) != NVME_VUC_DISC_LOCK_NONE) {
94*2dfbbe2dSRobert Mustacchi if (npa->npa_ns != NULL) {
95*2dfbbe2dSRobert Mustacchi nvme_ns_unlock(npa->npa_ns);
96*2dfbbe2dSRobert Mustacchi } else if (npa->npa_ctrl != NULL) {
97*2dfbbe2dSRobert Mustacchi nvme_ctrl_unlock(npa->npa_ctrl);
98*2dfbbe2dSRobert Mustacchi }
99*2dfbbe2dSRobert Mustacchi }
100*2dfbbe2dSRobert Mustacchi
101*2dfbbe2dSRobert Mustacchi nvme_vuc_disc_free(vuc);
102*2dfbbe2dSRobert Mustacchi }
103*2dfbbe2dSRobert Mustacchi
104*2dfbbe2dSRobert Mustacchi void
usage_vendor_cmd(const char * c_name)105*2dfbbe2dSRobert Mustacchi usage_vendor_cmd(const char *c_name)
106*2dfbbe2dSRobert Mustacchi {
107*2dfbbe2dSRobert Mustacchi (void) fprintf(stderr, "%s -O opcode [-n nsid] [--cdw12 cdw12] "
108*2dfbbe2dSRobert Mustacchi "[--cdw13 cdw13]\n\t [--cdw14 cdw14] [--cdw15 cdw15] "
109*2dfbbe2dSRobert Mustacchi "[-l length [-i file | -o file]]\n\t [-L lock] [-I impact] "
110*2dfbbe2dSRobert Mustacchi "[-t timeout] <ctl>[/<ns>]\n\n", c_name);
111*2dfbbe2dSRobert Mustacchi (void) fprintf(stderr, " Run a vendor-specific command against a "
112*2dfbbe2dSRobert Mustacchi "device\n");
113*2dfbbe2dSRobert Mustacchi }
114*2dfbbe2dSRobert Mustacchi
115*2dfbbe2dSRobert Mustacchi /*
116*2dfbbe2dSRobert Mustacchi * Most folks reasonably expect short options for all long options. We do have
117*2dfbbe2dSRobert Mustacchi * these here for the various --cdw arguments, but there are no good short
118*2dfbbe2dSRobert Mustacchi * options here depending on what we want to do. These are different from the
119*2dfbbe2dSRobert Mustacchi * Linux nvme-cli, so we would have preferred not to have them at all, but the
120*2dfbbe2dSRobert Mustacchi * mappings in that tool are not very usable either, e.g. --cdw12 is 6. When
121*2dfbbe2dSRobert Mustacchi * we're doing documentation: usage statements, manuals, overviews, prefer the
122*2dfbbe2dSRobert Mustacchi * --cdw form.
123*2dfbbe2dSRobert Mustacchi */
124*2dfbbe2dSRobert Mustacchi static const struct option vendor_cmd_lopts[] = {
125*2dfbbe2dSRobert Mustacchi { "opcode", required_argument, NULL, 'O' },
126*2dfbbe2dSRobert Mustacchi { "nsid", required_argument, NULL, 'n' },
127*2dfbbe2dSRobert Mustacchi { "cdw12", required_argument, NULL, '2' },
128*2dfbbe2dSRobert Mustacchi { "cdw13", required_argument, NULL, '3' },
129*2dfbbe2dSRobert Mustacchi { "cdw14", required_argument, NULL, '4' },
130*2dfbbe2dSRobert Mustacchi { "cdw15", required_argument, NULL, '5' },
131*2dfbbe2dSRobert Mustacchi { "length", required_argument, NULL, 'l' },
132*2dfbbe2dSRobert Mustacchi { "input", required_argument, NULL, 'i' },
133*2dfbbe2dSRobert Mustacchi { "output", required_argument, NULL, 'o' },
134*2dfbbe2dSRobert Mustacchi { "lock", required_argument, NULL, 'L' },
135*2dfbbe2dSRobert Mustacchi { "impact", required_argument, NULL, 'I' },
136*2dfbbe2dSRobert Mustacchi { "timeout", required_argument, NULL, 't' },
137*2dfbbe2dSRobert Mustacchi { NULL, 0, NULL, 0 }
138*2dfbbe2dSRobert Mustacchi };
139*2dfbbe2dSRobert Mustacchi
140*2dfbbe2dSRobert Mustacchi static long long
optparse_vendor_cmd_ui(const char * raw,const char * field,uint64_t min,uint64_t max)141*2dfbbe2dSRobert Mustacchi optparse_vendor_cmd_ui(const char *raw, const char *field, uint64_t min,
142*2dfbbe2dSRobert Mustacchi uint64_t max)
143*2dfbbe2dSRobert Mustacchi {
144*2dfbbe2dSRobert Mustacchi const char *errstr;
145*2dfbbe2dSRobert Mustacchi long long l;
146*2dfbbe2dSRobert Mustacchi
147*2dfbbe2dSRobert Mustacchi l = strtonumx(raw, min, max, &errstr, 0);
148*2dfbbe2dSRobert Mustacchi if (errstr != NULL) {
149*2dfbbe2dSRobert Mustacchi errx(-1, "failed to parse %s: value %s is %s: valid values "
150*2dfbbe2dSRobert Mustacchi "are in the range [0x%" PRIx64 ", 0x%" PRIx64 "]", field,
151*2dfbbe2dSRobert Mustacchi raw, errstr, min, max);
152*2dfbbe2dSRobert Mustacchi }
153*2dfbbe2dSRobert Mustacchi
154*2dfbbe2dSRobert Mustacchi return (l);
155*2dfbbe2dSRobert Mustacchi }
156*2dfbbe2dSRobert Mustacchi
157*2dfbbe2dSRobert Mustacchi void
optparse_vendor_cmd(nvme_process_arg_t * npa)158*2dfbbe2dSRobert Mustacchi optparse_vendor_cmd(nvme_process_arg_t *npa)
159*2dfbbe2dSRobert Mustacchi {
160*2dfbbe2dSRobert Mustacchi int c;
161*2dfbbe2dSRobert Mustacchi nvmeadm_vuc_t *vuc;
162*2dfbbe2dSRobert Mustacchi
163*2dfbbe2dSRobert Mustacchi if ((vuc = calloc(1, sizeof (nvmeadm_vuc_t))) == NULL) {
164*2dfbbe2dSRobert Mustacchi err(-1, "failed to allocate memory for option tracking");
165*2dfbbe2dSRobert Mustacchi }
166*2dfbbe2dSRobert Mustacchi vuc->vuc_timeout = NVMEADM_VUC_TO_DEFAULT;
167*2dfbbe2dSRobert Mustacchi
168*2dfbbe2dSRobert Mustacchi /*
169*2dfbbe2dSRobert Mustacchi * Normally we can reset optind to 0 to make sure that we can account
170*2dfbbe2dSRobert Mustacchi * for the fact that we've modified our arguments. Unfortunately
171*2dfbbe2dSRobert Mustacchi * getopt_long() tries to detect this as a case where some tools have
172*2dfbbe2dSRobert Mustacchi * used it as a way to ask for option processing to be reset and thus
173*2dfbbe2dSRobert Mustacchi * skip our first argument. As such we cheat a bit with the arguments we
174*2dfbbe2dSRobert Mustacchi * pass.
175*2dfbbe2dSRobert Mustacchi */
176*2dfbbe2dSRobert Mustacchi while ((c = getopt_long(npa->npa_argc + 1, npa->npa_argv - 1,
177*2dfbbe2dSRobert Mustacchi ":O:n:l:i:I:o:L:t:2:3:4:5:", vendor_cmd_lopts, NULL)) != -1) {
178*2dfbbe2dSRobert Mustacchi char *last;
179*2dfbbe2dSRobert Mustacchi
180*2dfbbe2dSRobert Mustacchi switch (c) {
181*2dfbbe2dSRobert Mustacchi case 'O':
182*2dfbbe2dSRobert Mustacchi vuc->vuc_opc = (uint8_t)optparse_vendor_cmd_ui(optarg,
183*2dfbbe2dSRobert Mustacchi "opcode", NVME_PASSTHRU_MIN_ADMIN_OPC,
184*2dfbbe2dSRobert Mustacchi NVME_PASSTHRU_MAX_ADMIN_OPC);
185*2dfbbe2dSRobert Mustacchi break;
186*2dfbbe2dSRobert Mustacchi case 'n':
187*2dfbbe2dSRobert Mustacchi /*
188*2dfbbe2dSRobert Mustacchi * We don't use NVME_NSID_MIN here because we want to
189*2dfbbe2dSRobert Mustacchi * allow the invalid nsid 0 to be specified for this
190*2dfbbe2dSRobert Mustacchi * field.
191*2dfbbe2dSRobert Mustacchi */
192*2dfbbe2dSRobert Mustacchi vuc->vuc_nsid = (uint8_t)optparse_vendor_cmd_ui(optarg,
193*2dfbbe2dSRobert Mustacchi "opcode", 0, NVME_NSID_BCAST);
194*2dfbbe2dSRobert Mustacchi break;
195*2dfbbe2dSRobert Mustacchi case 'l':
196*2dfbbe2dSRobert Mustacchi vuc->vuc_dlen = (uint32_t)optparse_vendor_cmd_ui(optarg,
197*2dfbbe2dSRobert Mustacchi "length", 0, NVMEADM_VUC_LEN_MAX);
198*2dfbbe2dSRobert Mustacchi if (vuc->vuc_dlen % NVMEADM_VUC_LEN_ALIGN != 0) {
199*2dfbbe2dSRobert Mustacchi errx(-1, "invalid data length %u: must be a "
200*2dfbbe2dSRobert Mustacchi "multiple of 4 bytes", vuc->vuc_dlen);
201*2dfbbe2dSRobert Mustacchi }
202*2dfbbe2dSRobert Mustacchi break;
203*2dfbbe2dSRobert Mustacchi case 'i':
204*2dfbbe2dSRobert Mustacchi vuc->vuc_input = optarg;
205*2dfbbe2dSRobert Mustacchi break;
206*2dfbbe2dSRobert Mustacchi case 'o':
207*2dfbbe2dSRobert Mustacchi vuc->vuc_output = optarg;
208*2dfbbe2dSRobert Mustacchi break;
209*2dfbbe2dSRobert Mustacchi case 'L':
210*2dfbbe2dSRobert Mustacchi if (strcmp(optarg, "read") == 0) {
211*2dfbbe2dSRobert Mustacchi vuc->vuc_lock = NVME_LOCK_L_READ;
212*2dfbbe2dSRobert Mustacchi } else if (strcmp(optarg, "write") == 0) {
213*2dfbbe2dSRobert Mustacchi vuc->vuc_lock = NVME_LOCK_L_WRITE;
214*2dfbbe2dSRobert Mustacchi } else {
215*2dfbbe2dSRobert Mustacchi errx(-1, "invalid lock value %s: valid values "
216*2dfbbe2dSRobert Mustacchi "are 'read' or 'write'", optarg);
217*2dfbbe2dSRobert Mustacchi }
218*2dfbbe2dSRobert Mustacchi break;
219*2dfbbe2dSRobert Mustacchi case 'I':
220*2dfbbe2dSRobert Mustacchi for (char *s = strtok_r(optarg, ",", &last); s != NULL;
221*2dfbbe2dSRobert Mustacchi s = strtok_r(NULL, ",", &last)) {
222*2dfbbe2dSRobert Mustacchi if (strcmp(s, "data") == 0) {
223*2dfbbe2dSRobert Mustacchi vuc->vuc_impact |=
224*2dfbbe2dSRobert Mustacchi NVME_VUC_DISC_IMPACT_DATA;
225*2dfbbe2dSRobert Mustacchi } else if (strcmp(s, "namespace") == 0) {
226*2dfbbe2dSRobert Mustacchi vuc->vuc_impact |=
227*2dfbbe2dSRobert Mustacchi NVME_VUC_DISC_IMPACT_NS;
228*2dfbbe2dSRobert Mustacchi } else {
229*2dfbbe2dSRobert Mustacchi errx(-1, "invalid impact string: %s",
230*2dfbbe2dSRobert Mustacchi s);
231*2dfbbe2dSRobert Mustacchi }
232*2dfbbe2dSRobert Mustacchi }
233*2dfbbe2dSRobert Mustacchi break;
234*2dfbbe2dSRobert Mustacchi case 't':
235*2dfbbe2dSRobert Mustacchi /* This will be further constrained by libnvme */
236*2dfbbe2dSRobert Mustacchi vuc->vuc_timeout = (uint32_t)optparse_vendor_cmd_ui(
237*2dfbbe2dSRobert Mustacchi optarg, "timeout", 1, UINT32_MAX);
238*2dfbbe2dSRobert Mustacchi break;
239*2dfbbe2dSRobert Mustacchi case '2':
240*2dfbbe2dSRobert Mustacchi vuc->vuc_cdw12 = (uint32_t)optparse_vendor_cmd_ui(
241*2dfbbe2dSRobert Mustacchi optarg, "cdw12", 0, UINT32_MAX);
242*2dfbbe2dSRobert Mustacchi break;
243*2dfbbe2dSRobert Mustacchi case '3':
244*2dfbbe2dSRobert Mustacchi vuc->vuc_cdw13 = (uint32_t)optparse_vendor_cmd_ui(
245*2dfbbe2dSRobert Mustacchi optarg, "cdw13", 0, UINT32_MAX);
246*2dfbbe2dSRobert Mustacchi break;
247*2dfbbe2dSRobert Mustacchi case '4':
248*2dfbbe2dSRobert Mustacchi vuc->vuc_cdw14 = (uint32_t)optparse_vendor_cmd_ui(
249*2dfbbe2dSRobert Mustacchi optarg, "cdw14", 0, UINT32_MAX);
250*2dfbbe2dSRobert Mustacchi break;
251*2dfbbe2dSRobert Mustacchi case '5':
252*2dfbbe2dSRobert Mustacchi vuc->vuc_cdw15 = (uint32_t)optparse_vendor_cmd_ui(
253*2dfbbe2dSRobert Mustacchi optarg, "cdw15", 0, UINT32_MAX);
254*2dfbbe2dSRobert Mustacchi break;
255*2dfbbe2dSRobert Mustacchi case '?':
256*2dfbbe2dSRobert Mustacchi errx(-1, "unknown option: -%c", optopt);
257*2dfbbe2dSRobert Mustacchi case ':':
258*2dfbbe2dSRobert Mustacchi errx(-1, "option -%c requires an argument", optopt);
259*2dfbbe2dSRobert Mustacchi break;
260*2dfbbe2dSRobert Mustacchi }
261*2dfbbe2dSRobert Mustacchi }
262*2dfbbe2dSRobert Mustacchi
263*2dfbbe2dSRobert Mustacchi /*
264*2dfbbe2dSRobert Mustacchi * Undo our optind lies.
265*2dfbbe2dSRobert Mustacchi */
266*2dfbbe2dSRobert Mustacchi optind--;
267*2dfbbe2dSRobert Mustacchi
268*2dfbbe2dSRobert Mustacchi if (vuc->vuc_opc == 0) {
269*2dfbbe2dSRobert Mustacchi errx(-1, "missing required command opcode");
270*2dfbbe2dSRobert Mustacchi }
271*2dfbbe2dSRobert Mustacchi
272*2dfbbe2dSRobert Mustacchi if (vuc->vuc_input != NULL && vuc->vuc_output != NULL) {
273*2dfbbe2dSRobert Mustacchi errx(-1, "cannot specify both an input file (-i) and an output "
274*2dfbbe2dSRobert Mustacchi "file (-o)");
275*2dfbbe2dSRobert Mustacchi }
276*2dfbbe2dSRobert Mustacchi
277*2dfbbe2dSRobert Mustacchi if ((vuc->vuc_input != NULL || vuc->vuc_output != NULL) &&
278*2dfbbe2dSRobert Mustacchi vuc->vuc_dlen == 0) {
279*2dfbbe2dSRobert Mustacchi errx(-1, "asked to transfer data (-%c) but missing required "
280*2dfbbe2dSRobert Mustacchi "data length (-l)", vuc->vuc_input != NULL ? 'i' : 'o');
281*2dfbbe2dSRobert Mustacchi }
282*2dfbbe2dSRobert Mustacchi
283*2dfbbe2dSRobert Mustacchi if (vuc->vuc_input == NULL && vuc->vuc_output == NULL &&
284*2dfbbe2dSRobert Mustacchi vuc->vuc_dlen != 0) {
285*2dfbbe2dSRobert Mustacchi errx(-1, "%u bytes of data transfer requested (-l), but no "
286*2dfbbe2dSRobert Mustacchi "input (-i) or output (-o) specified", vuc->vuc_dlen);
287*2dfbbe2dSRobert Mustacchi }
288*2dfbbe2dSRobert Mustacchi
289*2dfbbe2dSRobert Mustacchi /*
290*2dfbbe2dSRobert Mustacchi * Only check if the namespace id matches if the user specified a
291*2dfbbe2dSRobert Mustacchi * namespace.
292*2dfbbe2dSRobert Mustacchi */
293*2dfbbe2dSRobert Mustacchi if (npa->npa_ns != NULL) {
294*2dfbbe2dSRobert Mustacchi uint32_t nsid = nvme_ns_info_nsid(npa->npa_ns_info);
295*2dfbbe2dSRobert Mustacchi if (vuc->vuc_nsid != 0 && vuc->vuc_nsid != nsid) {
296*2dfbbe2dSRobert Mustacchi errx(-1, "Requested namespace id (-n) %u does not "
297*2dfbbe2dSRobert Mustacchi "match the nsid of %s (%u): either remove the "
298*2dfbbe2dSRobert Mustacchi "-n argument or specify just a controller",
299*2dfbbe2dSRobert Mustacchi vuc->vuc_nsid, npa->npa_name, nsid);
300*2dfbbe2dSRobert Mustacchi }
301*2dfbbe2dSRobert Mustacchi
302*2dfbbe2dSRobert Mustacchi vuc->vuc_nsid = nsid;
303*2dfbbe2dSRobert Mustacchi }
304*2dfbbe2dSRobert Mustacchi
305*2dfbbe2dSRobert Mustacchi npa->npa_cmd_arg = vuc;
306*2dfbbe2dSRobert Mustacchi }
307*2dfbbe2dSRobert Mustacchi
308*2dfbbe2dSRobert Mustacchi int
do_vendor_cmd(const nvme_process_arg_t * npa)309*2dfbbe2dSRobert Mustacchi do_vendor_cmd(const nvme_process_arg_t *npa)
310*2dfbbe2dSRobert Mustacchi {
311*2dfbbe2dSRobert Mustacchi const nvmeadm_vuc_t *vuc = npa->npa_cmd_arg;
312*2dfbbe2dSRobert Mustacchi uint8_t *buf = NULL;
313*2dfbbe2dSRobert Mustacchi nvme_vuc_req_t *req;
314*2dfbbe2dSRobert Mustacchi int ofd = -1;
315*2dfbbe2dSRobert Mustacchi
316*2dfbbe2dSRobert Mustacchi /*
317*2dfbbe2dSRobert Mustacchi * Verify we can get a request. This is effectively our is this
318*2dfbbe2dSRobert Mustacchi * supported check.
319*2dfbbe2dSRobert Mustacchi */
320*2dfbbe2dSRobert Mustacchi if (!nvme_vuc_req_init(npa->npa_ctrl, &req)) {
321*2dfbbe2dSRobert Mustacchi nvmeadm_fatal(npa, "failed to initialize vendor unique "
322*2dfbbe2dSRobert Mustacchi "request");
323*2dfbbe2dSRobert Mustacchi }
324*2dfbbe2dSRobert Mustacchi
325*2dfbbe2dSRobert Mustacchi if (vuc->vuc_dlen > 0) {
326*2dfbbe2dSRobert Mustacchi if ((buf = calloc(sizeof (uint8_t), vuc->vuc_dlen)) == NULL) {
327*2dfbbe2dSRobert Mustacchi nvmeadm_fatal(npa, "failed to allocate 0x%x byte "
328*2dfbbe2dSRobert Mustacchi "request data buffer", vuc->vuc_dlen);
329*2dfbbe2dSRobert Mustacchi }
330*2dfbbe2dSRobert Mustacchi
331*2dfbbe2dSRobert Mustacchi /*
332*2dfbbe2dSRobert Mustacchi * If we have an input file, then we want to read data from it
333*2dfbbe2dSRobert Mustacchi * until we either hit EOF or we read sufficient bytes from it
334*2dfbbe2dSRobert Mustacchi * to fill our buffer. Anything we don't will be zero filled,
335*2dfbbe2dSRobert Mustacchi * which was already taken care of by using calloc.
336*2dfbbe2dSRobert Mustacchi */
337*2dfbbe2dSRobert Mustacchi if (vuc->vuc_input != NULL) {
338*2dfbbe2dSRobert Mustacchi int ifd = open(vuc->vuc_input, O_RDONLY);
339*2dfbbe2dSRobert Mustacchi if (ifd < 0) {
340*2dfbbe2dSRobert Mustacchi err(EXIT_FAILURE, "failed to open input file "
341*2dfbbe2dSRobert Mustacchi "%s", vuc->vuc_input);
342*2dfbbe2dSRobert Mustacchi }
343*2dfbbe2dSRobert Mustacchi
344*2dfbbe2dSRobert Mustacchi size_t rem = vuc->vuc_dlen, off = 0;
345*2dfbbe2dSRobert Mustacchi while (rem > 0) {
346*2dfbbe2dSRobert Mustacchi size_t toread = MIN(16 * 1024, rem);
347*2dfbbe2dSRobert Mustacchi ssize_t ret = read(ifd, buf + off, toread);
348*2dfbbe2dSRobert Mustacchi if (ret < 0) {
349*2dfbbe2dSRobert Mustacchi nvmeadm_fatal(npa, "failed to read %zu "
350*2dfbbe2dSRobert Mustacchi "bytes at offset %zu from %s",
351*2dfbbe2dSRobert Mustacchi toread, off, vuc->vuc_input);
352*2dfbbe2dSRobert Mustacchi } else if (ret == 0) {
353*2dfbbe2dSRobert Mustacchi break;
354*2dfbbe2dSRobert Mustacchi }
355*2dfbbe2dSRobert Mustacchi
356*2dfbbe2dSRobert Mustacchi rem -= (size_t)ret;
357*2dfbbe2dSRobert Mustacchi off += (size_t)ret;
358*2dfbbe2dSRobert Mustacchi }
359*2dfbbe2dSRobert Mustacchi
360*2dfbbe2dSRobert Mustacchi VERIFY0(close(ifd));
361*2dfbbe2dSRobert Mustacchi } else if (vuc->vuc_output != NULL) {
362*2dfbbe2dSRobert Mustacchi ofd = open(vuc->vuc_output, O_RDWR | O_TRUNC | O_CREAT,
363*2dfbbe2dSRobert Mustacchi 0644);
364*2dfbbe2dSRobert Mustacchi if (ofd < 0) {
365*2dfbbe2dSRobert Mustacchi err(-1, "failed to open output file %s",
366*2dfbbe2dSRobert Mustacchi vuc->vuc_output);
367*2dfbbe2dSRobert Mustacchi }
368*2dfbbe2dSRobert Mustacchi }
369*2dfbbe2dSRobert Mustacchi }
370*2dfbbe2dSRobert Mustacchi
371*2dfbbe2dSRobert Mustacchi if (!nvme_vuc_req_set_opcode(req, vuc->vuc_opc) ||
372*2dfbbe2dSRobert Mustacchi !nvme_vuc_req_set_nsid(req, vuc->vuc_nsid) ||
373*2dfbbe2dSRobert Mustacchi !nvme_vuc_req_set_timeout(req, vuc->vuc_timeout) ||
374*2dfbbe2dSRobert Mustacchi !nvme_vuc_req_set_cdw12(req, vuc->vuc_cdw12) ||
375*2dfbbe2dSRobert Mustacchi !nvme_vuc_req_set_cdw13(req, vuc->vuc_cdw13) ||
376*2dfbbe2dSRobert Mustacchi !nvme_vuc_req_set_cdw14(req, vuc->vuc_cdw14) ||
377*2dfbbe2dSRobert Mustacchi !nvme_vuc_req_set_cdw15(req, vuc->vuc_cdw15) ||
378*2dfbbe2dSRobert Mustacchi !nvme_vuc_req_set_impact(req, vuc->vuc_impact)) {
379*2dfbbe2dSRobert Mustacchi nvmeadm_fatal(npa, "failed to set request fields");
380*2dfbbe2dSRobert Mustacchi }
381*2dfbbe2dSRobert Mustacchi
382*2dfbbe2dSRobert Mustacchi if (vuc->vuc_input != NULL) {
383*2dfbbe2dSRobert Mustacchi if (!nvme_vuc_req_set_input(req, buf, vuc->vuc_dlen)) {
384*2dfbbe2dSRobert Mustacchi nvmeadm_fatal(npa, "failed to set input buffer");
385*2dfbbe2dSRobert Mustacchi }
386*2dfbbe2dSRobert Mustacchi } else if (vuc->vuc_output != NULL) {
387*2dfbbe2dSRobert Mustacchi if (!nvme_vuc_req_set_output(req, buf, vuc->vuc_dlen)) {
388*2dfbbe2dSRobert Mustacchi nvmeadm_fatal(npa, "failed to set output buffer");
389*2dfbbe2dSRobert Mustacchi }
390*2dfbbe2dSRobert Mustacchi }
391*2dfbbe2dSRobert Mustacchi
392*2dfbbe2dSRobert Mustacchi if (vuc->vuc_lock != 0) {
393*2dfbbe2dSRobert Mustacchi nvmeadm_excl(npa, vuc->vuc_lock);
394*2dfbbe2dSRobert Mustacchi }
395*2dfbbe2dSRobert Mustacchi
396*2dfbbe2dSRobert Mustacchi if (!nvme_vuc_req_exec(req)) {
397*2dfbbe2dSRobert Mustacchi nvmeadm_fatal(npa, "failed to execute request");
398*2dfbbe2dSRobert Mustacchi }
399*2dfbbe2dSRobert Mustacchi
400*2dfbbe2dSRobert Mustacchi uint32_t cdw0;
401*2dfbbe2dSRobert Mustacchi if (nvme_vuc_req_get_cdw0(req, &cdw0)) {
402*2dfbbe2dSRobert Mustacchi (void) printf("Request cdw0: 0x%x\n", cdw0);
403*2dfbbe2dSRobert Mustacchi }
404*2dfbbe2dSRobert Mustacchi
405*2dfbbe2dSRobert Mustacchi /*
406*2dfbbe2dSRobert Mustacchi * Remove the lock manually. npa->npa_excl isn't set, so we need to
407*2dfbbe2dSRobert Mustacchi * manually take care of this.
408*2dfbbe2dSRobert Mustacchi */
409*2dfbbe2dSRobert Mustacchi if (vuc->vuc_lock != 0) {
410*2dfbbe2dSRobert Mustacchi if (npa->npa_ns != NULL) {
411*2dfbbe2dSRobert Mustacchi nvme_ns_unlock(npa->npa_ns);
412*2dfbbe2dSRobert Mustacchi } else {
413*2dfbbe2dSRobert Mustacchi nvme_ctrl_unlock(npa->npa_ctrl);
414*2dfbbe2dSRobert Mustacchi }
415*2dfbbe2dSRobert Mustacchi }
416*2dfbbe2dSRobert Mustacchi
417*2dfbbe2dSRobert Mustacchi if (vuc->vuc_output != NULL) {
418*2dfbbe2dSRobert Mustacchi size_t rem = vuc->vuc_dlen, off = 0;
419*2dfbbe2dSRobert Mustacchi while (rem > 0) {
420*2dfbbe2dSRobert Mustacchi size_t towrite = MIN(16 * 1024, rem);
421*2dfbbe2dSRobert Mustacchi ssize_t ret = write(ofd, buf + off, towrite);
422*2dfbbe2dSRobert Mustacchi
423*2dfbbe2dSRobert Mustacchi if (ret < 0) {
424*2dfbbe2dSRobert Mustacchi nvmeadm_fatal(npa, "failed to write %zu bytes "
425*2dfbbe2dSRobert Mustacchi "of output data at offset %zu to %s",
426*2dfbbe2dSRobert Mustacchi towrite, off, vuc->vuc_output);
427*2dfbbe2dSRobert Mustacchi }
428*2dfbbe2dSRobert Mustacchi
429*2dfbbe2dSRobert Mustacchi rem -= towrite;
430*2dfbbe2dSRobert Mustacchi off += towrite;
431*2dfbbe2dSRobert Mustacchi }
432*2dfbbe2dSRobert Mustacchi }
433*2dfbbe2dSRobert Mustacchi
434*2dfbbe2dSRobert Mustacchi if (ofd >= 0) {
435*2dfbbe2dSRobert Mustacchi VERIFY0(close(ofd));
436*2dfbbe2dSRobert Mustacchi }
437*2dfbbe2dSRobert Mustacchi nvme_vuc_req_fini(req);
438*2dfbbe2dSRobert Mustacchi free(buf);
439*2dfbbe2dSRobert Mustacchi return (0);
440*2dfbbe2dSRobert Mustacchi }
441