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 
511 static void
nvme_get_feat_req_clear_need(nvme_get_feat_req_t * req,nvme_get_feat_req_field_t field)512 nvme_get_feat_req_clear_need(nvme_get_feat_req_t *req,
513     nvme_get_feat_req_field_t field)
514 {
515 	req->gfr_need &= ~(1 << field);
516 }
517 
518 static const nvme_field_check_t nvme_get_feat_check_fid = {
519 	nvme_get_feat_fields, NVME_GET_FEAT_REQ_FIELD_FID,
520 	NVME_ERR_FEAT_FID_RANGE, 0, 0
521 };
522 
523 bool
nvme_get_feat_req_set_fid(nvme_get_feat_req_t * req,uint32_t fid)524 nvme_get_feat_req_set_fid(nvme_get_feat_req_t *req, uint32_t fid)
525 {
526 	if (!nvme_field_check_one(req->gfr_ctrl, fid, "get feature",
527 	    &nvme_get_feat_check_fid, req->gfr_allow)) {
528 		return (false);
529 	}
530 
531 	req->gfr_fid = fid;
532 	nvme_get_feat_req_clear_need(req, NVME_GET_FEAT_REQ_FIELD_FID);
533 	return (nvme_ctrl_success(req->gfr_ctrl));
534 }
535 
536 static const nvme_field_check_t nvme_get_feat_check_sel = {
537 	nvme_get_feat_fields, NVME_GET_FEAT_REQ_FIELD_SEL,
538 	NVME_ERR_FEAT_SEL_RANGE, NVME_ERR_FEAT_SEL_UNSUP, 0
539 };
540 
541 bool
nvme_get_feat_req_set_sel(nvme_get_feat_req_t * req,uint32_t sel)542 nvme_get_feat_req_set_sel(nvme_get_feat_req_t *req, uint32_t sel)
543 {
544 	if (!nvme_field_check_one(req->gfr_ctrl, sel, "get feature",
545 	    &nvme_get_feat_check_sel, req->gfr_allow)) {
546 		return (false);
547 	}
548 
549 	if (sel == NVME_FEATURE_SEL_SUPPORTED) {
550 		return (nvme_ctrl_error(req->gfr_ctrl,
551 		    NVME_ERR_FEAT_SEL_RANGE, 0, "the get feature APIs do "
552 		    "not support supported capabilities selector"));
553 	}
554 
555 	req->gfr_sel = sel;
556 	nvme_get_feat_req_clear_need(req, NVME_GET_FEAT_REQ_FIELD_SEL);
557 	return (nvme_ctrl_success(req->gfr_ctrl));
558 }
559 
560 static const nvme_field_check_t nvme_get_feat_check_cdw11 = {
561 	nvme_get_feat_fields, NVME_GET_FEAT_REQ_FIELD_CDW11,
562 	NVME_ERR_FEAT_CDW11_RANGE, 0, NVME_ERR_FEAT_CDW11_UNUSE
563 };
564 
565 bool
nvme_get_feat_req_set_cdw11(nvme_get_feat_req_t * req,uint32_t cdw11)566 nvme_get_feat_req_set_cdw11(nvme_get_feat_req_t *req, uint32_t cdw11)
567 {
568 	if (!nvme_field_check_one(req->gfr_ctrl, cdw11, "get feature",
569 	    &nvme_get_feat_check_cdw11, req->gfr_allow)) {
570 		return (false);
571 	}
572 
573 	req->gfr_cdw11 = cdw11;
574 	nvme_get_feat_req_clear_need(req, NVME_GET_FEAT_REQ_FIELD_CDW11);
575 	return (nvme_ctrl_success(req->gfr_ctrl));
576 }
577 
578 static const nvme_field_check_t nvme_get_feat_check_nsid = {
579 	nvme_get_feat_fields, NVME_GET_FEAT_REQ_FIELD_NSID,
580 	NVME_ERR_NS_RANGE, 0, NVME_ERR_NS_UNUSE
581 };
582 
583 bool
nvme_get_feat_req_set_nsid(nvme_get_feat_req_t * req,uint32_t nsid)584 nvme_get_feat_req_set_nsid(nvme_get_feat_req_t *req, uint32_t nsid)
585 {
586 	nvme_ctrl_t *ctrl = req->gfr_ctrl;
587 
588 	/*
589 	 * Check the NSID first before we go into the generic validation code so
590 	 * we can get a better error message.
591 	 */
592 	if (nsid == NVME_NSID_BCAST &&
593 	    (req->gfr_flags & NVME_FEAT_F_GET_BCAST_NSID) == 0) {
594 		return (nvme_ctrl_error(ctrl, NVME_ERR_NS_RANGE, 0, "the all "
595 		    "namespaces/controller nsid (0x%x) is not allowed for this "
596 		    "feature, valid namespaces are [0x%x, 0x%x]", nsid,
597 		    NVME_NSID_MIN, req->gfr_ctrl->nc_info.id_nn));
598 	}
599 
600 	if (!nvme_field_check_one(req->gfr_ctrl, nsid, "get feature",
601 	    &nvme_get_feat_check_nsid, req->gfr_allow)) {
602 		return (false);
603 	}
604 
605 	req->gfr_nsid = nsid;
606 	nvme_get_feat_req_clear_need(req, NVME_GET_FEAT_REQ_FIELD_NSID);
607 	return (nvme_ctrl_success(ctrl));
608 }
609 
610 bool
nvme_get_feat_req_set_output(nvme_get_feat_req_t * req,void * buf,size_t len)611 nvme_get_feat_req_set_output(nvme_get_feat_req_t *req, void *buf, size_t len)
612 {
613 	if (buf == NULL) {
614 		return (nvme_ctrl_error(req->gfr_ctrl, NVME_ERR_BAD_PTR, 0,
615 		    "get feature output buffer cannot be NULL"));
616 	}
617 
618 	if (len == 0) {
619 		return (nvme_ctrl_error(req->gfr_ctrl,
620 		    NVME_ERR_FEAT_DATA_RANGE, 0, "get feature output length "
621 		    "cannot be zero"));
622 	}
623 
624 	/*
625 	 * Something to consider for the future here is that we know the fixed
626 	 * size data that we're expecting for the feature. It would be nice if
627 	 * we validated that we have that size now versus later.
628 	 */
629 
630 	req->gfr_buf = buf;
631 	req->gfr_len = len;
632 	nvme_get_feat_req_clear_need(req, NVME_GET_FEAT_REQ_FIELD_DPTR);
633 	return (nvme_ctrl_success(req->gfr_ctrl));
634 }
635 
636 bool
nvme_get_feat_req_get_cdw0(nvme_get_feat_req_t * req,uint32_t * cdw0)637 nvme_get_feat_req_get_cdw0(nvme_get_feat_req_t *req, uint32_t *cdw0)
638 {
639 	if (cdw0 == NULL) {
640 		return (nvme_ctrl_error(req->gfr_ctrl, NVME_ERR_BAD_PTR, 0,
641 		    "encountered invalid cdw0 output pointer: %p", cdw0));
642 	}
643 
644 	if (!req->gfr_results_valid) {
645 		return (nvme_ctrl_error(req->gfr_ctrl,
646 		    NVME_ERR_FEAT_NO_RESULTS, 0, "get feature results are not "
647 		    "currently valid and cannot be returned"));
648 	}
649 
650 	*cdw0 = req->gfr_cdw0;
651 	return (nvme_ctrl_success(req->gfr_ctrl));
652 }
653 
654 bool
nvme_get_feat_req_exec(nvme_get_feat_req_t * req)655 nvme_get_feat_req_exec(nvme_get_feat_req_t *req)
656 {
657 	nvme_ctrl_t *ctrl = req->gfr_ctrl;
658 	nvme_ioctl_get_feature_t feat;
659 
660 	/*
661 	 * Because this has been called, we need to immediately invalidate our
662 	 * stored cdw0 results. We do this as a precaution regardless of whether
663 	 * or not it is valid.
664 	 */
665 	req->gfr_results_valid = false;
666 	req->gfr_cdw0 = 0;
667 
668 	if (req->gfr_need != 0) {
669 		return (nvme_field_miss_err(ctrl, nvme_get_feat_fields,
670 		    nvme_get_feat_nfields, NVME_ERR_GET_FEAT_REQ_MISSING_FIELDS,
671 		    "get feature", req->gfr_need));
672 	}
673 
674 	(void) memset(&feat, 0, sizeof (nvme_ioctl_get_feature_t));
675 	feat.nigf_common.nioc_nsid = req->gfr_nsid;
676 	feat.nigf_fid = req->gfr_fid;
677 	feat.nigf_sel = req->gfr_sel;
678 	feat.nigf_cdw11 = req->gfr_cdw11;
679 	if (req->gfr_buf != NULL) {
680 		feat.nigf_data = (uintptr_t)req->gfr_buf;
681 		feat.nigf_len = req->gfr_len;
682 	}
683 
684 	if (ioctl(ctrl->nc_fd, NVME_IOC_GET_FEATURE, &feat) != 0) {
685 		int e = errno;
686 		return (nvme_ioctl_syserror(ctrl, e, "get feature"));
687 	}
688 
689 	if (feat.nigf_common.nioc_drv_err != NVME_IOCTL_E_OK) {
690 		return (nvme_ioctl_error(ctrl, &feat.nigf_common,
691 		    "get feature"));
692 	}
693 
694 	req->gfr_results_valid = true;
695 	req->gfr_cdw0 = feat.nigf_cdw0;
696 
697 	return (nvme_ctrl_success(ctrl));
698 }
699