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 2024 Oxide Computer Company
14 */
15
16 /*
17 * Manage NVMe feature detection and feature queries.
18 */
19
20 #include <stdlib.h>
21 #include <string.h>
22 #include <unistd.h>
23
24 #include "libnvme_impl.h"
25
26 const char *
nvme_feat_disc_short(const nvme_feat_disc_t * disc)27 nvme_feat_disc_short(const nvme_feat_disc_t *disc)
28 {
29 return (disc->nfd_short);
30 }
31
32 const char *
nvme_feat_disc_spec(const nvme_feat_disc_t * disc)33 nvme_feat_disc_spec(const nvme_feat_disc_t *disc)
34 {
35 return (disc->nfd_spec);
36 }
37
38 uint32_t
nvme_feat_disc_fid(const nvme_feat_disc_t * disc)39 nvme_feat_disc_fid(const nvme_feat_disc_t *disc)
40 {
41 return (disc->nfd_fid);
42 }
43
44 nvme_feat_scope_t
nvme_feat_disc_scope(const nvme_feat_disc_t * disc)45 nvme_feat_disc_scope(const nvme_feat_disc_t *disc)
46 {
47 return (disc->nfd_scope);
48 }
49
50 nvme_feat_kind_t
nvme_feat_disc_kind(const nvme_feat_disc_t * disc)51 nvme_feat_disc_kind(const nvme_feat_disc_t *disc)
52 {
53 return (disc->nfd_kind);
54 }
55
56 nvme_feat_csi_t
nvme_feat_disc_csi(const nvme_feat_disc_t * disc)57 nvme_feat_disc_csi(const nvme_feat_disc_t *disc)
58 {
59 return (disc->nfd_csi);
60 }
61
62 nvme_feat_flags_t
nvme_feat_disc_flags(const nvme_feat_disc_t * disc)63 nvme_feat_disc_flags(const nvme_feat_disc_t *disc)
64 {
65 return (disc->nfd_flags);
66 }
67
68 nvme_get_feat_fields_t
nvme_feat_disc_fields_get(const nvme_feat_disc_t * disc)69 nvme_feat_disc_fields_get(const nvme_feat_disc_t *disc)
70 {
71 return (disc->nfd_in_get);
72 }
73
74 nvme_set_feat_fields_t
nvme_feat_disc_fields_set(const nvme_feat_disc_t * disc)75 nvme_feat_disc_fields_set(const nvme_feat_disc_t *disc)
76 {
77 return (disc->nfd_in_set);
78 }
79
80 nvme_feat_output_t
nvme_feat_disc_output_get(const nvme_feat_disc_t * disc)81 nvme_feat_disc_output_get(const nvme_feat_disc_t *disc)
82 {
83 return (disc->nfd_out_get);
84 }
85
86 nvme_feat_output_t
nvme_feat_disc_output_set(const nvme_feat_disc_t * disc)87 nvme_feat_disc_output_set(const nvme_feat_disc_t *disc)
88 {
89 return (disc->nfd_out_set);
90 }
91
92 uint64_t
nvme_feat_disc_data_size(const nvme_feat_disc_t * disc)93 nvme_feat_disc_data_size(const nvme_feat_disc_t *disc)
94 {
95 return (disc->nfd_len);
96 }
97
98 static bool
nvme_feat_discover_validate(nvme_ctrl_t * ctrl,nvme_feat_scope_t scopes,uint32_t flags)99 nvme_feat_discover_validate(nvme_ctrl_t *ctrl, nvme_feat_scope_t scopes,
100 uint32_t flags)
101 {
102 const nvme_feat_scope_t valid_scopes = NVME_FEAT_SCOPE_CTRL |
103 NVME_FEAT_SCOPE_NS;
104
105 /*
106 * See the note in nvme_log_discover_validate() on why we don't support
107 * a zeroed scope.
108 */
109 if (scopes == 0) {
110 return (nvme_ctrl_error(ctrl, NVME_ERR_BAD_FLAG, 0, "no "
111 "feature scope specified (given 0), a scope must be "
112 "requested"));
113 }
114
115 if ((scopes & ~valid_scopes) != 0) {
116 return (nvme_ctrl_error(ctrl, NVME_ERR_BAD_FLAG, 0,
117 "encountered invalid scope for the nvme_feat_disc_scope_t: "
118 "0x%x", scopes & ~valid_scopes));
119 }
120
121 /*
122 * The flags are meant for future expansion here, hence the all zero
123 * requirement.
124 */
125 if (flags != 0) {
126 return (nvme_ctrl_error(ctrl, NVME_ERR_BAD_FLAG, 0,
127 "encountered invalid feature discovery flags: 0x%x",
128 flags));
129 }
130
131 return (true);
132 }
133
134 nvme_feat_impl_t
nvme_feat_disc_impl(const nvme_feat_disc_t * disc)135 nvme_feat_disc_impl(const nvme_feat_disc_t *disc)
136 {
137 return (disc->nfd_impl);
138 }
139
140 void
nvme_feat_disc_free(nvme_feat_disc_t * disc)141 nvme_feat_disc_free(nvme_feat_disc_t *disc)
142 {
143 free(disc);
144 }
145
146 bool
nvme_feat_disc_dup(nvme_ctrl_t * ctrl,const nvme_feat_disc_t * src,nvme_feat_disc_t ** discp)147 nvme_feat_disc_dup(nvme_ctrl_t *ctrl, const nvme_feat_disc_t *src,
148 nvme_feat_disc_t **discp)
149 {
150 nvme_feat_disc_t *disc;
151
152 if (src == NULL) {
153 return (nvme_ctrl_error(ctrl, NVME_ERR_BAD_PTR, 0,
154 "encountered invalid nvme_feat_disc_t pointer to "
155 "duplicate: %p", src));
156 }
157
158 if (discp == NULL) {
159 return (nvme_ctrl_error(ctrl, NVME_ERR_BAD_PTR, 0,
160 "encountered invalid nvme_feat_disc_t output pointer: %p",
161 discp));
162 }
163
164 disc = calloc(1, sizeof (nvme_feat_disc_t));
165 if (disc == NULL) {
166 int e = errno;
167 return (nvme_ctrl_error(ctrl, NVME_ERR_NO_MEM, e, "failed to "
168 "allocate memory for a new nvme_feat_disc_t: %s",
169 strerror(e)));
170 }
171
172 (void) memcpy(disc, src, sizeof (nvme_feat_disc_t));
173 *discp = disc;
174 return (nvme_ctrl_success(ctrl));
175 }
176
177 void
nvme_feat_discover_fini(nvme_feat_iter_t * iter)178 nvme_feat_discover_fini(nvme_feat_iter_t *iter)
179 {
180 free(iter);
181 }
182
183 static bool
nvme_feat_discover_one(nvme_feat_iter_t * iter,const nvme_feat_info_t * info)184 nvme_feat_discover_one(nvme_feat_iter_t *iter, const nvme_feat_info_t *info)
185 {
186 nvme_feat_disc_t *disc = &iter->nfi_disc;
187 nvme_valid_ctrl_data_t data;
188
189 data.vcd_vers = &iter->nfi_ctrl->nc_vers;
190 data.vcd_id = &iter->nfi_ctrl->nc_info;
191
192 /*
193 * The user is not interested in this feature. Do not include it.
194 */
195 if ((iter->nfi_scope & info->nfeat_scope) == 0) {
196 return (false);
197 }
198
199 (void) memset(disc, 0, sizeof (nvme_feat_disc_t));
200
201 disc->nfd_short = info->nfeat_short;
202 disc->nfd_spec = info->nfeat_spec;
203 disc->nfd_fid = info->nfeat_fid;
204 disc->nfd_kind = info->nfeat_kind;
205 disc->nfd_scope = info->nfeat_scope;
206 disc->nfd_flags = info->nfeat_flags;
207 disc->nfd_csi = info->nfeat_csi;
208 disc->nfd_in_get = info->nfeat_in_get;
209 disc->nfd_in_set = info->nfeat_in_set;
210 disc->nfd_out_get = info->nfeat_out_get;
211 disc->nfd_out_set = info->nfeat_out_set;
212 disc->nfd_len = info->nfeat_len;
213 disc->nfd_impl = nvme_feat_supported(info, &data);
214
215 return (true);
216 }
217
218 nvme_iter_t
nvme_feat_discover_step(nvme_feat_iter_t * iter,const nvme_feat_disc_t ** outp)219 nvme_feat_discover_step(nvme_feat_iter_t *iter, const nvme_feat_disc_t **outp)
220 {
221 *outp = NULL;
222
223 if (iter->nfi_cur_idx == nvme_std_nfeats) {
224 return (NVME_ITER_DONE);
225 }
226
227 while (iter->nfi_cur_idx < nvme_std_nfeats) {
228 const nvme_feat_info_t *feat =
229 &nvme_std_feats[iter->nfi_cur_idx];
230 iter->nfi_cur_idx++;
231
232 if (nvme_feat_discover_one(iter, feat)) {
233 *outp = &iter->nfi_disc;
234 return (NVME_ITER_VALID);
235 }
236 }
237
238 /*
239 * When we add support for vendor-specific features, then we will want
240 * to check NVME_LOG_DISC_F_NO_DB here and if it is not set, proceed to
241 * move past the standard features onto the vendor-specific features
242 * like we do in the log page work right now.
243 */
244 ASSERT3U(iter->nfi_cur_idx, ==, nvme_std_nfeats);
245 return (NVME_ITER_DONE);
246 }
247
248 bool
nvme_feat_discover_init(nvme_ctrl_t * ctrl,nvme_feat_scope_t scope,uint32_t flags,nvme_feat_iter_t ** iterp)249 nvme_feat_discover_init(nvme_ctrl_t *ctrl, nvme_feat_scope_t scope,
250 uint32_t flags, nvme_feat_iter_t **iterp)
251 {
252 nvme_feat_iter_t *iter;
253
254 if (!nvme_feat_discover_validate(ctrl, scope, flags)) {
255 return (false);
256 }
257
258 if (iterp == NULL) {
259 return (nvme_ctrl_error(ctrl, NVME_ERR_BAD_PTR, 0,
260 "encountered invalid nvme_feat_iter_t output pointer: %p",
261 iterp));
262 }
263
264 iter = calloc(1, sizeof (nvme_feat_iter_t));
265 if (iter == NULL) {
266 int e = errno;
267 return (nvme_ctrl_error(ctrl, NVME_ERR_NO_MEM, e, "failed to "
268 "allocate memory for a new nvme_feat_iter_t: %s",
269 strerror(e)));
270 }
271
272 iter->nfi_ctrl = ctrl;
273 iter->nfi_scope = scope;
274
275 *iterp = iter;
276 return (nvme_ctrl_success(ctrl));
277 }
278
279 bool
nvme_feat_discover(nvme_ctrl_t * ctrl,nvme_feat_scope_t scope,uint32_t flags,nvme_feat_disc_f func,void * arg)280 nvme_feat_discover(nvme_ctrl_t *ctrl, nvme_feat_scope_t scope, uint32_t flags,
281 nvme_feat_disc_f func, void *arg)
282 {
283 nvme_feat_iter_t *iter;
284 nvme_iter_t ret;
285 const nvme_feat_disc_t *disc;
286
287
288 if (func == NULL) {
289 return (nvme_ctrl_error(ctrl, NVME_ERR_BAD_PTR, 0,
290 "encountered invalid nvme_feat_disc_f function pointer: %p",
291 func));
292 }
293
294 if (!nvme_feat_discover_init(ctrl, scope, flags, &iter)) {
295 return (false);
296 }
297
298 while ((ret = nvme_feat_discover_step(iter, &disc)) ==
299 NVME_ITER_VALID) {
300 if (!func(ctrl, disc, arg))
301 break;
302 }
303
304 nvme_feat_discover_fini(iter);
305 if (ret == NVME_ITER_ERROR) {
306 return (false);
307 }
308
309 return (nvme_ctrl_success(ctrl));
310 }
311
312 void
nvme_get_feat_req_fini(nvme_get_feat_req_t * req)313 nvme_get_feat_req_fini(nvme_get_feat_req_t *req)
314 {
315 free(req);
316 }
317
318 /*
319 * This instantiates a simple get features request that allows most fields to be
320 * specified.
321 */
322 bool
nvme_get_feat_req_init(nvme_ctrl_t * ctrl,nvme_get_feat_req_t ** reqp)323 nvme_get_feat_req_init(nvme_ctrl_t *ctrl, nvme_get_feat_req_t **reqp)
324 {
325 nvme_get_feat_req_t *req;
326
327 if (reqp == NULL) {
328 return (nvme_ctrl_error(ctrl, NVME_ERR_BAD_PTR, 0,
329 "encountered invalid nvme_get_feat_req_t output pointer: "
330 "%p", reqp));
331 }
332
333 req = calloc(1, sizeof (nvme_get_feat_req_t));
334 if (req == NULL) {
335 int e = errno;
336 return (nvme_ctrl_error(ctrl, NVME_ERR_NO_MEM, e, "failed to "
337 "allocate memory for a new nvme_get_feat_req_t: %s",
338 strerror(e)));
339 }
340
341 req->gfr_ctrl = ctrl;
342 for (size_t i = 0; i < nvme_get_feat_nfields; i++) {
343 if (nvme_get_feat_fields[i].nlfi_def_req) {
344 req->gfr_need |= 1 << i;
345 }
346
347 if (nvme_get_feat_fields[i].nlfi_def_allow) {
348 req->gfr_allow |= 1 << i;
349 }
350 }
351
352 /*
353 * For a generic get feature request, we don't know if this is true or
354 * not. Even though none of the features the kernel supports at this
355 * moment support this, we still want to enable this for the future for
356 * when they do. The kernel will enforce this.
357 */
358 req->gfr_flags = NVME_FEAT_F_GET_BCAST_NSID;
359
360 *reqp = req;
361 return (nvme_ctrl_success(ctrl));
362 }
363
364 bool
nvme_get_feat_req_init_by_disc(nvme_ctrl_t * ctrl,const nvme_feat_disc_t * disc,nvme_get_feat_req_t ** reqp)365 nvme_get_feat_req_init_by_disc(nvme_ctrl_t *ctrl, const nvme_feat_disc_t *disc,
366 nvme_get_feat_req_t **reqp)
367 {
368 nvme_get_feat_req_t *req;
369
370 if (disc == NULL) {
371 return (nvme_ctrl_error(ctrl, NVME_ERR_BAD_PTR, 0,
372 "encountered invalid nvme_feat_disc_t pointer: %p", disc));
373 }
374
375 if (reqp == NULL) {
376 return (nvme_ctrl_error(ctrl, NVME_ERR_BAD_PTR, 0,
377 "encountered invalid nvme_get_feat_req_t output pointer: "
378 "%p", reqp));
379 }
380
381 if (disc->nfd_impl == NVME_FEAT_IMPL_UNSUPPORTED) {
382 return (nvme_ctrl_error(ctrl, NVME_ERR_FEAT_UNSUP_BY_DEV, 0,
383 "cannot create get feature request for feature %s "
384 "(FID 0x%x) because it is not supported by the device",
385 disc->nfd_short, disc->nfd_fid));
386
387 }
388
389 req = calloc(1, sizeof (nvme_get_feat_req_t));
390 if (req == NULL) {
391 int e = errno;
392 return (nvme_ctrl_error(ctrl, NVME_ERR_NO_MEM, e, "failed to "
393 "allocate memory for a new nvme_get_feat_req_t: %s",
394 strerror(e)));
395 }
396
397 req->gfr_ctrl = ctrl;
398
399 /*
400 * Unlike the more generic get feature initialization, we only allow
401 * items to be set if they're actually required. We do not require a
402 * selector to be set and will default to just getting the current
403 * value. Otherwise every other field that is settable is required. We
404 * don't let someone change the fid on us, because that would be quite
405 * confusing.
406 */
407 req->gfr_fid = disc->nfd_fid;
408 req->gfr_flags = disc->nfd_flags;
409 req->gfr_targ_len = disc->nfd_len;
410 req->gfr_allow |= 1 << NVME_GET_FEAT_REQ_FIELD_SEL;
411
412 if ((disc->nfd_in_get & NVME_GET_FEAT_F_CDW11) != 0) {
413 req->gfr_need |= 1 << NVME_GET_FEAT_REQ_FIELD_CDW11;
414 req->gfr_allow |= 1 << NVME_GET_FEAT_REQ_FIELD_CDW11;
415 }
416
417 if ((disc->nfd_in_get & NVME_GET_FEAT_F_DATA) != 0) {
418 req->gfr_need |= 1 << NVME_GET_FEAT_REQ_FIELD_DPTR;
419 req->gfr_allow |= 1 << NVME_GET_FEAT_REQ_FIELD_DPTR;
420 }
421
422 if ((disc->nfd_in_get & NVME_GET_FEAT_F_NSID) != 0) {
423 req->gfr_need |= 1 << NVME_GET_FEAT_REQ_FIELD_NSID;
424 req->gfr_allow |= 1 << NVME_GET_FEAT_REQ_FIELD_NSID;
425 }
426
427 *reqp = req;
428 return (nvme_ctrl_success(ctrl));
429 }
430
431 typedef struct {
432 bool ngfi_found;
433 const char *ngfi_name;
434 nvme_get_feat_req_t *ngfi_req;
435 nvme_feat_disc_t **ngfi_discp;
436 nvme_err_data_t ngfi_err;
437 } nvme_get_feat_init_arg_t;
438
439 static bool
nvme_get_feat_req_init_by_name_cb(nvme_ctrl_t * ctrl,const nvme_feat_disc_t * disc,void * arg)440 nvme_get_feat_req_init_by_name_cb(nvme_ctrl_t *ctrl,
441 const nvme_feat_disc_t *disc, void *arg)
442 {
443 nvme_get_feat_init_arg_t *init = arg;
444
445 if (strcmp(init->ngfi_name, disc->nfd_short) != 0 &&
446 strcmp(init->ngfi_name, disc->nfd_spec) != 0) {
447 return (true);
448 }
449
450 init->ngfi_found = true;
451 if (!nvme_get_feat_req_init_by_disc(ctrl, disc, &init->ngfi_req)) {
452 nvme_ctrl_err_save(ctrl, &init->ngfi_err);
453 init->ngfi_req = NULL;
454 } else if (init->ngfi_discp != NULL) {
455 if (!nvme_feat_disc_dup(ctrl, disc, init->ngfi_discp)) {
456 nvme_ctrl_err_save(ctrl, &init->ngfi_err);
457 nvme_get_feat_req_fini(init->ngfi_req);
458 init->ngfi_req = NULL;
459 }
460 }
461
462 return (false);
463 }
464
465 bool
nvme_get_feat_req_init_by_name(nvme_ctrl_t * ctrl,const char * name,uint32_t df,nvme_feat_disc_t ** discp,nvme_get_feat_req_t ** reqp)466 nvme_get_feat_req_init_by_name(nvme_ctrl_t *ctrl, const char *name,
467 uint32_t df, nvme_feat_disc_t **discp, nvme_get_feat_req_t **reqp)
468 {
469 nvme_get_feat_init_arg_t init;
470
471 /*
472 * Note, we consider discp optional unlikely the name and reqp. The
473 * discover flags, df, will be validated by the discover functions we
474 * call.
475 */
476 if (name == NULL) {
477 return (nvme_ctrl_error(ctrl, NVME_ERR_BAD_PTR, 0,
478 "encountered invalid pointer for log page name: %p", name));
479 }
480
481 if (reqp == NULL) {
482 return (nvme_ctrl_error(ctrl, NVME_ERR_BAD_PTR, 0,
483 "encountered invalid nvme_get_feat_req_t output pointer: "
484 "%p", reqp));
485 }
486
487 (void) memset(&init, 0, sizeof (init));
488 init.ngfi_name = name;
489 init.ngfi_discp = discp;
490
491 if (!nvme_feat_discover(ctrl, NVME_FEAT_SCOPE_CTRL | NVME_FEAT_SCOPE_NS,
492 df, nvme_get_feat_req_init_by_name_cb, &init)) {
493 return (false);
494 }
495
496 if (!init.ngfi_found) {
497 return (nvme_ctrl_error(ctrl, NVME_ERR_FEAT_NAME_UNKNOWN, 0,
498 "failed to find feature with name %s", name));
499 }
500
501 if (init.ngfi_req == NULL) {
502 nvme_ctrl_err_set(ctrl, &init.ngfi_err);
503 return (false);
504 }
505
506 *reqp = init.ngfi_req;
507 return (nvme_ctrl_success(ctrl));
508 }
509
510 static void
nvme_get_feat_req_set_need(nvme_get_feat_req_t * req,nvme_get_feat_req_field_t field)511 nvme_get_feat_req_set_need(nvme_get_feat_req_t *req,
512 nvme_get_feat_req_field_t field)
513 {
514 req->gfr_need |= 1 << field;
515 }
516
517 static void
nvme_get_feat_req_clear_need(nvme_get_feat_req_t * req,nvme_get_feat_req_field_t field)518 nvme_get_feat_req_clear_need(nvme_get_feat_req_t *req,
519 nvme_get_feat_req_field_t field)
520 {
521 req->gfr_need &= ~(1 << field);
522 }
523
524 static const nvme_field_check_t nvme_get_feat_check_fid = {
525 nvme_get_feat_fields, NVME_GET_FEAT_REQ_FIELD_FID,
526 NVME_ERR_FEAT_FID_RANGE, 0, 0
527 };
528
529 bool
nvme_get_feat_req_set_fid(nvme_get_feat_req_t * req,uint32_t fid)530 nvme_get_feat_req_set_fid(nvme_get_feat_req_t *req, uint32_t fid)
531 {
532 if (!nvme_field_check_one(req->gfr_ctrl, fid, "get feature",
533 &nvme_get_feat_check_fid, req->gfr_allow)) {
534 return (false);
535 }
536
537 req->gfr_fid = fid;
538 nvme_get_feat_req_clear_need(req, NVME_GET_FEAT_REQ_FIELD_FID);
539 return (nvme_ctrl_success(req->gfr_ctrl));
540 }
541
542 static const nvme_field_check_t nvme_get_feat_check_sel = {
543 nvme_get_feat_fields, NVME_GET_FEAT_REQ_FIELD_SEL,
544 NVME_ERR_FEAT_SEL_RANGE, NVME_ERR_FEAT_SEL_UNSUP, 0
545 };
546
547 bool
nvme_get_feat_req_set_sel(nvme_get_feat_req_t * req,uint32_t sel)548 nvme_get_feat_req_set_sel(nvme_get_feat_req_t *req, uint32_t sel)
549 {
550 if (!nvme_field_check_one(req->gfr_ctrl, sel, "get feature",
551 &nvme_get_feat_check_sel, req->gfr_allow)) {
552 return (false);
553 }
554
555 if (sel == NVME_FEATURE_SEL_SUPPORTED) {
556 return (nvme_ctrl_error(req->gfr_ctrl,
557 NVME_ERR_FEAT_SEL_RANGE, 0, "the get feature APIs do "
558 "not support supported capabilities selector"));
559 }
560
561 req->gfr_sel = sel;
562 nvme_get_feat_req_clear_need(req, NVME_GET_FEAT_REQ_FIELD_SEL);
563 return (nvme_ctrl_success(req->gfr_ctrl));
564 }
565
566 static const nvme_field_check_t nvme_get_feat_check_cdw11 = {
567 nvme_get_feat_fields, NVME_GET_FEAT_REQ_FIELD_CDW11,
568 NVME_ERR_FEAT_CDW11_RANGE, 0, NVME_ERR_FEAT_CDW11_UNUSE
569 };
570
571 bool
nvme_get_feat_req_set_cdw11(nvme_get_feat_req_t * req,uint32_t cdw11)572 nvme_get_feat_req_set_cdw11(nvme_get_feat_req_t *req, uint32_t cdw11)
573 {
574 if (!nvme_field_check_one(req->gfr_ctrl, cdw11, "get feature",
575 &nvme_get_feat_check_cdw11, req->gfr_allow)) {
576 return (false);
577 }
578
579 req->gfr_cdw11 = cdw11;
580 nvme_get_feat_req_clear_need(req, NVME_GET_FEAT_REQ_FIELD_CDW11);
581 return (nvme_ctrl_success(req->gfr_ctrl));
582 }
583
584 static const nvme_field_check_t nvme_get_feat_check_nsid = {
585 nvme_get_feat_fields, NVME_GET_FEAT_REQ_FIELD_NSID,
586 NVME_ERR_NS_RANGE, 0, NVME_ERR_NS_UNUSE
587 };
588
589 bool
nvme_get_feat_req_set_nsid(nvme_get_feat_req_t * req,uint32_t nsid)590 nvme_get_feat_req_set_nsid(nvme_get_feat_req_t *req, uint32_t nsid)
591 {
592 nvme_ctrl_t *ctrl = req->gfr_ctrl;
593
594 /*
595 * Check the NSID first before we go into the generic validation code so
596 * we can get a better error message.
597 */
598 if (nsid == NVME_NSID_BCAST &&
599 (req->gfr_flags & NVME_FEAT_F_GET_BCAST_NSID) == 0) {
600 return (nvme_ctrl_error(ctrl, NVME_ERR_NS_RANGE, 0, "the all "
601 "namespaces/controller nsid (0x%x) is not allowed for this "
602 "feature, valid namespaces are [0x%x, 0x%x]", nsid,
603 NVME_NSID_MIN, req->gfr_ctrl->nc_info.id_nn));
604 }
605
606 if (!nvme_field_check_one(req->gfr_ctrl, nsid, "get feature",
607 &nvme_get_feat_check_nsid, req->gfr_allow)) {
608 return (false);
609 }
610
611 req->gfr_nsid = nsid;
612 nvme_get_feat_req_clear_need(req, NVME_GET_FEAT_REQ_FIELD_NSID);
613 return (nvme_ctrl_success(ctrl));
614 }
615
616 bool
nvme_get_feat_req_set_output(nvme_get_feat_req_t * req,void * buf,size_t len)617 nvme_get_feat_req_set_output(nvme_get_feat_req_t *req, void *buf, size_t len)
618 {
619 if (buf == NULL) {
620 return (nvme_ctrl_error(req->gfr_ctrl, NVME_ERR_BAD_PTR, 0,
621 "get feature output buffer cannot be NULL"));
622 }
623
624 if (len == 0) {
625 return (nvme_ctrl_error(req->gfr_ctrl,
626 NVME_ERR_FEAT_DATA_RANGE, 0, "get feature output length "
627 "cannot be zero"));
628 }
629
630 /*
631 * Something to consider for the future here is that we know the fixed
632 * size data that we're expecting for the feature. It would be nice if
633 * we validated that we have that size now versus later. Related,
634 * because there is no field check logic for this, we must manually
635 * check that it is allowed and cannot use nvme_field_check_one().
636 */
637 if ((req->gfr_allow & (1 << NVME_GET_FEAT_REQ_FIELD_DPTR)) == 0) {
638 return (nvme_ctrl_error(req->gfr_ctrl, NVME_ERR_FEAT_DATA_UNUSE,
639 0, "field output (dptr) cannot be set in this get feature "
640 "request"));
641 }
642
643 req->gfr_buf = buf;
644 req->gfr_len = len;
645 nvme_get_feat_req_clear_need(req, NVME_GET_FEAT_REQ_FIELD_DPTR);
646 return (nvme_ctrl_success(req->gfr_ctrl));
647 }
648
649 bool
nvme_get_feat_req_clear_output(nvme_get_feat_req_t * req)650 nvme_get_feat_req_clear_output(nvme_get_feat_req_t *req)
651 {
652 if ((req->gfr_allow & (1 << NVME_GET_FEAT_REQ_FIELD_DPTR)) == 0) {
653 return (nvme_ctrl_error(req->gfr_ctrl, NVME_ERR_FEAT_DATA_UNUSE,
654 0, "field output (dptr) cannot be cleared in this get "
655 "feature request"));
656 }
657
658 req->gfr_buf = NULL;
659 req->gfr_len = 0;
660 nvme_get_feat_req_set_need(req, NVME_GET_FEAT_REQ_FIELD_DPTR);
661 return (nvme_ctrl_success(req->gfr_ctrl));
662 }
663
664 bool
nvme_get_feat_req_get_cdw0(nvme_get_feat_req_t * req,uint32_t * cdw0)665 nvme_get_feat_req_get_cdw0(nvme_get_feat_req_t *req, uint32_t *cdw0)
666 {
667 if (cdw0 == NULL) {
668 return (nvme_ctrl_error(req->gfr_ctrl, NVME_ERR_BAD_PTR, 0,
669 "encountered invalid cdw0 output pointer: %p", cdw0));
670 }
671
672 if (!req->gfr_results_valid) {
673 return (nvme_ctrl_error(req->gfr_ctrl,
674 NVME_ERR_FEAT_NO_RESULTS, 0, "get feature results are not "
675 "currently valid and cannot be returned"));
676 }
677
678 *cdw0 = req->gfr_cdw0;
679 return (nvme_ctrl_success(req->gfr_ctrl));
680 }
681
682 bool
nvme_get_feat_req_exec(nvme_get_feat_req_t * req)683 nvme_get_feat_req_exec(nvme_get_feat_req_t *req)
684 {
685 nvme_ctrl_t *ctrl = req->gfr_ctrl;
686 nvme_ioctl_get_feature_t feat;
687
688 /*
689 * Because this has been called, we need to immediately invalidate our
690 * stored cdw0 results. We do this as a precaution regardless of whether
691 * or not it is valid.
692 */
693 req->gfr_results_valid = false;
694 req->gfr_cdw0 = 0;
695
696 if (req->gfr_need != 0) {
697 return (nvme_field_miss_err(ctrl, nvme_get_feat_fields,
698 nvme_get_feat_nfields, NVME_ERR_GET_FEAT_REQ_MISSING_FIELDS,
699 "get feature", req->gfr_need));
700 }
701
702 (void) memset(&feat, 0, sizeof (nvme_ioctl_get_feature_t));
703 feat.nigf_common.nioc_nsid = req->gfr_nsid;
704 feat.nigf_fid = req->gfr_fid;
705 feat.nigf_sel = req->gfr_sel;
706 feat.nigf_cdw11 = req->gfr_cdw11;
707 if (req->gfr_buf != NULL) {
708 feat.nigf_data = (uintptr_t)req->gfr_buf;
709 feat.nigf_len = req->gfr_len;
710 }
711
712 if (ioctl(ctrl->nc_fd, NVME_IOC_GET_FEATURE, &feat) != 0) {
713 int e = errno;
714 return (nvme_ioctl_syserror(ctrl, e, "get feature"));
715 }
716
717 if (feat.nigf_common.nioc_drv_err != NVME_IOCTL_E_OK) {
718 return (nvme_ioctl_error(ctrl, &feat.nigf_common,
719 "get feature"));
720 }
721
722 req->gfr_results_valid = true;
723 req->gfr_cdw0 = feat.nigf_cdw0;
724
725 return (nvme_ctrl_success(ctrl));
726 }
727