xref: /illumos-gate/usr/src/test/nvme-tests/tests/libnvme/set-feature.c (revision 564eeb7d65973f1d64a5390ff477b689707e9daf)
1*564eeb7dSRobert Mustacchi /*
2*564eeb7dSRobert Mustacchi  * This file and its contents are supplied under the terms of the
3*564eeb7dSRobert Mustacchi  * Common Development and Distribution License ("CDDL"), version 1.0.
4*564eeb7dSRobert Mustacchi  * You may only use this file in accordance with the terms of version
5*564eeb7dSRobert Mustacchi  * 1.0 of the CDDL.
6*564eeb7dSRobert Mustacchi  *
7*564eeb7dSRobert Mustacchi  * A full copy of the text of the CDDL should have accompanied this
8*564eeb7dSRobert Mustacchi  * source.  A copy of the CDDL is also available via the Internet at
9*564eeb7dSRobert Mustacchi  * http://www.illumos.org/license/CDDL.
10*564eeb7dSRobert Mustacchi  */
11*564eeb7dSRobert Mustacchi 
12*564eeb7dSRobert Mustacchi /*
13*564eeb7dSRobert Mustacchi  * Copyright 2026 Oxide Computer Company
14*564eeb7dSRobert Mustacchi  */
15*564eeb7dSRobert Mustacchi 
16*564eeb7dSRobert Mustacchi /*
17*564eeb7dSRobert Mustacchi  * Go through and test the basics of our set feature functionality. This is
18*564eeb7dSRobert Mustacchi  * designed to be run against a device that supports the requisite OCP features:
19*564eeb7dSRobert Mustacchi  *
20*564eeb7dSRobert Mustacchi  * - Error Injection (0xc0)
21*564eeb7dSRobert Mustacchi  * - PLP Health Check Interval (c6h)
22*564eeb7dSRobert Mustacchi  *
23*564eeb7dSRobert Mustacchi  * In this test we make sure that we can do:
24*564eeb7dSRobert Mustacchi  *
25*564eeb7dSRobert Mustacchi  * - Verify that we can't set a feature without the controller lock
26*564eeb7dSRobert Mustacchi  * - Verify we aren't allowed to set a standard feature
27*564eeb7dSRobert Mustacchi  * - A basic get feature / set feature loop without data
28*564eeb7dSRobert Mustacchi  * - Test the missing fields and basics of the libnvme ocp error injection
29*564eeb7dSRobert Mustacchi  *   features which wraps set feature. This also gives us a basic set feature
30*564eeb7dSRobert Mustacchi  *   with data tests.
31*564eeb7dSRobert Mustacchi  */
32*564eeb7dSRobert Mustacchi 
33*564eeb7dSRobert Mustacchi #include <err.h>
34*564eeb7dSRobert Mustacchi #include <sys/sysmacros.h>
35*564eeb7dSRobert Mustacchi #include <sys/nvme/ocp.h>
36*564eeb7dSRobert Mustacchi 
37*564eeb7dSRobert Mustacchi #include "libnvme_test_common.h"
38*564eeb7dSRobert Mustacchi 
39*564eeb7dSRobert Mustacchi static void
set_feats_supported(nvme_ctrl_t * ctrl)40*564eeb7dSRobert Mustacchi set_feats_supported(nvme_ctrl_t *ctrl)
41*564eeb7dSRobert Mustacchi {
42*564eeb7dSRobert Mustacchi 	nvme_set_feat_req_t *req;
43*564eeb7dSRobert Mustacchi 	const char *feats[] = { "ocp/plphealth", "ocp/errinj" };
44*564eeb7dSRobert Mustacchi 
45*564eeb7dSRobert Mustacchi 	for (size_t i = 0; i < ARRAY_SIZE(feats); i++) {
46*564eeb7dSRobert Mustacchi 		if (!nvme_set_feat_req_init_by_name(ctrl, feats[i], 0, NULL,
47*564eeb7dSRobert Mustacchi 		    &req)) {
48*564eeb7dSRobert Mustacchi 			libnvme_test_ctrl_fatal(ctrl, "missing required "
49*564eeb7dSRobert Mustacchi 			    "feature %s: test cannot proceed", feats[i]);
50*564eeb7dSRobert Mustacchi 		}
51*564eeb7dSRobert Mustacchi 
52*564eeb7dSRobert Mustacchi 		nvme_set_feat_req_fini(req);
53*564eeb7dSRobert Mustacchi 	}
54*564eeb7dSRobert Mustacchi }
55*564eeb7dSRobert Mustacchi 
56*564eeb7dSRobert Mustacchi static bool
set_feat_fail(nvme_ctrl_t * ctrl,const char * feat,const char * desc,uint32_t cdw11,nvme_err_t exp)57*564eeb7dSRobert Mustacchi set_feat_fail(nvme_ctrl_t *ctrl, const char *feat, const char *desc,
58*564eeb7dSRobert Mustacchi     uint32_t cdw11, nvme_err_t exp)
59*564eeb7dSRobert Mustacchi {
60*564eeb7dSRobert Mustacchi 	nvme_err_t err;
61*564eeb7dSRobert Mustacchi 	bool ret = false;
62*564eeb7dSRobert Mustacchi 	nvme_set_feat_req_t *set = NULL;
63*564eeb7dSRobert Mustacchi 
64*564eeb7dSRobert Mustacchi 	if (!nvme_set_feat_req_init_by_name(ctrl, feat, 0, NULL, &set)) {
65*564eeb7dSRobert Mustacchi 		libnvme_test_ctrl_warn(ctrl, "set feature %s: failed to "
66*564eeb7dSRobert Mustacchi 		    "create %s request", desc, feat);
67*564eeb7dSRobert Mustacchi 		goto done;
68*564eeb7dSRobert Mustacchi 	}
69*564eeb7dSRobert Mustacchi 
70*564eeb7dSRobert Mustacchi 	if (!nvme_set_feat_req_set_cdw11(set, cdw11)) {
71*564eeb7dSRobert Mustacchi 		libnvme_test_ctrl_warn(ctrl, "set feature %s: failed to set "
72*564eeb7dSRobert Mustacchi 		    "%s cdw11 value", desc, feat);
73*564eeb7dSRobert Mustacchi 		goto done;
74*564eeb7dSRobert Mustacchi 	}
75*564eeb7dSRobert Mustacchi 
76*564eeb7dSRobert Mustacchi 	if (nvme_set_feat_req_exec(set)) {
77*564eeb7dSRobert Mustacchi 		warnx("TEST FAILED: set feature %s unexpectedly passed: "
78*564eeb7dSRobert Mustacchi 		    "expected failure", desc);
79*564eeb7dSRobert Mustacchi 		goto done;
80*564eeb7dSRobert Mustacchi 	}
81*564eeb7dSRobert Mustacchi 
82*564eeb7dSRobert Mustacchi 	err = nvme_ctrl_err(ctrl);
83*564eeb7dSRobert Mustacchi 	if (err != exp) {
84*564eeb7dSRobert Mustacchi 		warnx("TEST FAILED: expected set feature %s to fail with %s "
85*564eeb7dSRobert Mustacchi 		    "(0x%x), but found %s (0x%x)", desc,
86*564eeb7dSRobert Mustacchi 		    nvme_ctrl_errtostr(ctrl, exp), exp,
87*564eeb7dSRobert Mustacchi 		    nvme_ctrl_errtostr(ctrl, err), err);
88*564eeb7dSRobert Mustacchi 		goto done;
89*564eeb7dSRobert Mustacchi 	}
90*564eeb7dSRobert Mustacchi 
91*564eeb7dSRobert Mustacchi 	ret = true;
92*564eeb7dSRobert Mustacchi 	(void) printf("TEST PASSED: set feature %s correctly failed with "
93*564eeb7dSRobert Mustacchi 	    "%s\n", desc, nvme_ctrl_errtostr(ctrl, exp));
94*564eeb7dSRobert Mustacchi done:
95*564eeb7dSRobert Mustacchi 	nvme_set_feat_req_fini(set);
96*564eeb7dSRobert Mustacchi 	return (ret);
97*564eeb7dSRobert Mustacchi }
98*564eeb7dSRobert Mustacchi 
99*564eeb7dSRobert Mustacchi static bool
get_plp(nvme_ctrl_t * ctrl,nvme_get_feat_req_t * get,uint32_t * valp)100*564eeb7dSRobert Mustacchi get_plp(nvme_ctrl_t *ctrl, nvme_get_feat_req_t *get, uint32_t *valp)
101*564eeb7dSRobert Mustacchi {
102*564eeb7dSRobert Mustacchi 	if (!nvme_get_feat_req_exec(get)) {
103*564eeb7dSRobert Mustacchi 		libnvme_test_ctrl_warn(ctrl, "failed to execute PLP get "
104*564eeb7dSRobert Mustacchi 		    "feature request");
105*564eeb7dSRobert Mustacchi 		return (false);
106*564eeb7dSRobert Mustacchi 	}
107*564eeb7dSRobert Mustacchi 
108*564eeb7dSRobert Mustacchi 	if (!nvme_get_feat_req_get_cdw0(get, valp)) {
109*564eeb7dSRobert Mustacchi 		libnvme_test_ctrl_warn(ctrl, "failed to get PLP cdw0 value");
110*564eeb7dSRobert Mustacchi 		return (false);
111*564eeb7dSRobert Mustacchi 	}
112*564eeb7dSRobert Mustacchi 
113*564eeb7dSRobert Mustacchi 	*valp &= UINT16_MAX;
114*564eeb7dSRobert Mustacchi 	return (true);
115*564eeb7dSRobert Mustacchi }
116*564eeb7dSRobert Mustacchi 
117*564eeb7dSRobert Mustacchi static bool
set_plp(nvme_ctrl_t * ctrl,nvme_set_feat_req_t * set,uint16_t val)118*564eeb7dSRobert Mustacchi set_plp(nvme_ctrl_t *ctrl, nvme_set_feat_req_t *set, uint16_t val)
119*564eeb7dSRobert Mustacchi {
120*564eeb7dSRobert Mustacchi 	/*
121*564eeb7dSRobert Mustacchi 	 * The PLP value is actually in the upper 16 bits when one performs a
122*564eeb7dSRobert Mustacchi 	 * GET FEATURE.
123*564eeb7dSRobert Mustacchi 	 */
124*564eeb7dSRobert Mustacchi 	if (!nvme_set_feat_req_set_cdw11(set, val << 16)) {
125*564eeb7dSRobert Mustacchi 		libnvme_test_ctrl_warn(ctrl, "failed to set PLP cdw11 value");
126*564eeb7dSRobert Mustacchi 		return (false);
127*564eeb7dSRobert Mustacchi 	}
128*564eeb7dSRobert Mustacchi 
129*564eeb7dSRobert Mustacchi 	if (!nvme_set_feat_req_exec(set)) {
130*564eeb7dSRobert Mustacchi 		libnvme_test_ctrl_warn(ctrl, "failed to execute PLP set "
131*564eeb7dSRobert Mustacchi 		    "feature request");
132*564eeb7dSRobert Mustacchi 		return (false);
133*564eeb7dSRobert Mustacchi 	}
134*564eeb7dSRobert Mustacchi 
135*564eeb7dSRobert Mustacchi 	return (true);
136*564eeb7dSRobert Mustacchi }
137*564eeb7dSRobert Mustacchi 
138*564eeb7dSRobert Mustacchi /*
139*564eeb7dSRobert Mustacchi  * Using the PLP Health Interval Checking, perform round-trip getting and
140*564eeb7dSRobert Mustacchi  * setting of the feature and verify our values hold.
141*564eeb7dSRobert Mustacchi  */
142*564eeb7dSRobert Mustacchi static bool
roundtrip_plp(nvme_ctrl_t * ctrl)143*564eeb7dSRobert Mustacchi roundtrip_plp(nvme_ctrl_t *ctrl)
144*564eeb7dSRobert Mustacchi {
145*564eeb7dSRobert Mustacchi 	nvme_get_feat_req_t *get = NULL;
146*564eeb7dSRobert Mustacchi 	nvme_set_feat_req_t *set = NULL;
147*564eeb7dSRobert Mustacchi 	bool ret = true;
148*564eeb7dSRobert Mustacchi 	uint32_t cdw0, new;
149*564eeb7dSRobert Mustacchi 
150*564eeb7dSRobert Mustacchi 	if (!nvme_get_feat_req_init_by_name(ctrl, "ocp/plphealth", 0, NULL,
151*564eeb7dSRobert Mustacchi 	    &get)) {
152*564eeb7dSRobert Mustacchi 		libnvme_test_ctrl_warn(ctrl, "failed to create PLP get feature "
153*564eeb7dSRobert Mustacchi 		    "request");
154*564eeb7dSRobert Mustacchi 		ret = false;
155*564eeb7dSRobert Mustacchi 		goto done;
156*564eeb7dSRobert Mustacchi 	}
157*564eeb7dSRobert Mustacchi 
158*564eeb7dSRobert Mustacchi 	if (!nvme_set_feat_req_init_by_name(ctrl, "ocp/plphealth", 0, NULL,
159*564eeb7dSRobert Mustacchi 	    &set)) {
160*564eeb7dSRobert Mustacchi 		libnvme_test_ctrl_warn(ctrl, "failed to create PLP set feature "
161*564eeb7dSRobert Mustacchi 		    "request");
162*564eeb7dSRobert Mustacchi 		ret = false;
163*564eeb7dSRobert Mustacchi 		goto done;
164*564eeb7dSRobert Mustacchi 	}
165*564eeb7dSRobert Mustacchi 
166*564eeb7dSRobert Mustacchi 	if (!get_plp(ctrl, get, &cdw0)) {
167*564eeb7dSRobert Mustacchi 		ret = false;
168*564eeb7dSRobert Mustacchi 		goto done;
169*564eeb7dSRobert Mustacchi 	}
170*564eeb7dSRobert Mustacchi 
171*564eeb7dSRobert Mustacchi 	if (!set_plp(ctrl, set, cdw0 + 1)) {
172*564eeb7dSRobert Mustacchi 		ret = false;
173*564eeb7dSRobert Mustacchi 		goto done;
174*564eeb7dSRobert Mustacchi 	}
175*564eeb7dSRobert Mustacchi 
176*564eeb7dSRobert Mustacchi 	if (!get_plp(ctrl, get, &new)) {
177*564eeb7dSRobert Mustacchi 		ret = false;
178*564eeb7dSRobert Mustacchi 		goto done;
179*564eeb7dSRobert Mustacchi 	}
180*564eeb7dSRobert Mustacchi 
181*564eeb7dSRobert Mustacchi 	if (cdw0 + 1 != new) {
182*564eeb7dSRobert Mustacchi 		warnx("setting ocp/plphealth didn't work, originally had 0x%x, "
183*564eeb7dSRobert Mustacchi 		    "expected 0x%x, found 0x%x", cdw0, cdw0 + 1, new);
184*564eeb7dSRobert Mustacchi 		ret = false;
185*564eeb7dSRobert Mustacchi 	}
186*564eeb7dSRobert Mustacchi 
187*564eeb7dSRobert Mustacchi 	if (!set_plp(ctrl, set, cdw0)) {
188*564eeb7dSRobert Mustacchi 		ret = false;
189*564eeb7dSRobert Mustacchi 		goto done;
190*564eeb7dSRobert Mustacchi 	}
191*564eeb7dSRobert Mustacchi 
192*564eeb7dSRobert Mustacchi 	if (!get_plp(ctrl, get, &new)) {
193*564eeb7dSRobert Mustacchi 		ret = false;
194*564eeb7dSRobert Mustacchi 		goto done;
195*564eeb7dSRobert Mustacchi 	}
196*564eeb7dSRobert Mustacchi 
197*564eeb7dSRobert Mustacchi 	if (cdw0 != new) {
198*564eeb7dSRobert Mustacchi 		warnx("TEST FAILED: failed to restore ocp/plphealth back to "
199*564eeb7dSRobert Mustacchi 		    "0x%x: found 0x%x", cdw0, new);
200*564eeb7dSRobert Mustacchi 		ret = false;
201*564eeb7dSRobert Mustacchi 	}
202*564eeb7dSRobert Mustacchi 
203*564eeb7dSRobert Mustacchi 	if (ret) {
204*564eeb7dSRobert Mustacchi 		(void) printf("TEST PASSED: ocp/plphealth set feature round "
205*564eeb7dSRobert Mustacchi 		    "trip\n");
206*564eeb7dSRobert Mustacchi 	}
207*564eeb7dSRobert Mustacchi 
208*564eeb7dSRobert Mustacchi done:
209*564eeb7dSRobert Mustacchi 	nvme_get_feat_req_fini(get);
210*564eeb7dSRobert Mustacchi 	nvme_set_feat_req_fini(set);
211*564eeb7dSRobert Mustacchi 	return (ret);
212*564eeb7dSRobert Mustacchi }
213*564eeb7dSRobert Mustacchi 
214*564eeb7dSRobert Mustacchi static bool
errinj_req_fail(nvme_ctrl_t * ctrl,nvme_ocp_errinj_req_t * req,const char * desc,nvme_err_t exp)215*564eeb7dSRobert Mustacchi errinj_req_fail(nvme_ctrl_t *ctrl, nvme_ocp_errinj_req_t *req, const char *desc,
216*564eeb7dSRobert Mustacchi     nvme_err_t exp)
217*564eeb7dSRobert Mustacchi {
218*564eeb7dSRobert Mustacchi 	const char *exps = nvme_ctrl_errtostr(ctrl, exp);
219*564eeb7dSRobert Mustacchi 	nvme_err_t err;
220*564eeb7dSRobert Mustacchi 
221*564eeb7dSRobert Mustacchi 	if (nvme_ocp_errinj_req_exec(req)) {
222*564eeb7dSRobert Mustacchi 		warnx("TEST FAILED: %s unexpectedly succeeded, but expected %s",
223*564eeb7dSRobert Mustacchi 		    desc, exps);
224*564eeb7dSRobert Mustacchi 		return (false);
225*564eeb7dSRobert Mustacchi 	}
226*564eeb7dSRobert Mustacchi 
227*564eeb7dSRobert Mustacchi 	err = nvme_ctrl_err(ctrl);
228*564eeb7dSRobert Mustacchi 	if (err != exp) {
229*564eeb7dSRobert Mustacchi 		warnx("TEST FAILED: expected %s to fail with %s (0x%x), but "
230*564eeb7dSRobert Mustacchi 		    "found %s (0x%x)", desc, nvme_ctrl_errtostr(ctrl, exp), exp,
231*564eeb7dSRobert Mustacchi 		    nvme_ctrl_errtostr(ctrl, err), err);
232*564eeb7dSRobert Mustacchi 		return (false);
233*564eeb7dSRobert Mustacchi 	}
234*564eeb7dSRobert Mustacchi 
235*564eeb7dSRobert Mustacchi 
236*564eeb7dSRobert Mustacchi 	(void) printf("TEST PASSED: %s correctly failed with %s\n", desc, exps);
237*564eeb7dSRobert Mustacchi 	return (true);
238*564eeb7dSRobert Mustacchi }
239*564eeb7dSRobert Mustacchi 
240*564eeb7dSRobert Mustacchi static bool
errinj_set_fail(nvme_ctrl_t * ctrl,const char * desc,nvme_err_t exp,bool res)241*564eeb7dSRobert Mustacchi errinj_set_fail(nvme_ctrl_t *ctrl, const char *desc, nvme_err_t exp, bool res)
242*564eeb7dSRobert Mustacchi {
243*564eeb7dSRobert Mustacchi 	if (res) {
244*564eeb7dSRobert Mustacchi 		warnx("TEST FAILED: setting error injection field %s to bad "
245*564eeb7dSRobert Mustacchi 		    "value incorrectly worked: expected %s (0x%x)", desc,
246*564eeb7dSRobert Mustacchi 		    nvme_ctrl_errtostr(ctrl, exp), exp);
247*564eeb7dSRobert Mustacchi 		return (false);
248*564eeb7dSRobert Mustacchi 	}
249*564eeb7dSRobert Mustacchi 
250*564eeb7dSRobert Mustacchi 	nvme_err_t err = nvme_ctrl_err(ctrl);
251*564eeb7dSRobert Mustacchi 	if (err != exp) {
252*564eeb7dSRobert Mustacchi 		warnx("TEST FAILED: expected bad error injection field %s "
253*564eeb7dSRobert Mustacchi 		    "value to fail with %s (0x%x): found %s (0x%x)", desc,
254*564eeb7dSRobert Mustacchi 		    nvme_ctrl_errtostr(ctrl, exp), exp,
255*564eeb7dSRobert Mustacchi 		    nvme_ctrl_errtostr(ctrl, err), err);
256*564eeb7dSRobert Mustacchi 		return (false);
257*564eeb7dSRobert Mustacchi 	}
258*564eeb7dSRobert Mustacchi 
259*564eeb7dSRobert Mustacchi 	(void) printf("TEST PASSED: bad error injection field %s value "
260*564eeb7dSRobert Mustacchi 	    "correctly failed with %s\n", desc, nvme_ctrl_errtostr(ctrl, exp));
261*564eeb7dSRobert Mustacchi 	return (true);
262*564eeb7dSRobert Mustacchi }
263*564eeb7dSRobert Mustacchi 
264*564eeb7dSRobert Mustacchi /*
265*564eeb7dSRobert Mustacchi  * Go through and verify that various different errors are caught:
266*564eeb7dSRobert Mustacchi  *
267*564eeb7dSRobert Mustacchi  *  - Missing fields
268*564eeb7dSRobert Mustacchi  *  - Invalid values for type, number of reads, delay
269*564eeb7dSRobert Mustacchi  *  - Delay value unsupported
270*564eeb7dSRobert Mustacchi  */
271*564eeb7dSRobert Mustacchi static bool
errinj_errors(nvme_ctrl_t * ctrl)272*564eeb7dSRobert Mustacchi errinj_errors(nvme_ctrl_t *ctrl)
273*564eeb7dSRobert Mustacchi {
274*564eeb7dSRobert Mustacchi 	bool ret = true;
275*564eeb7dSRobert Mustacchi 	nvme_ocp_errinj_req_t *req = NULL;
276*564eeb7dSRobert Mustacchi 
277*564eeb7dSRobert Mustacchi 	if (!nvme_ocp_errinj_req_init(ctrl, &req)) {
278*564eeb7dSRobert Mustacchi 		libnvme_test_ctrl_warn(ctrl, "failed to create error injection "
279*564eeb7dSRobert Mustacchi 		    "request");
280*564eeb7dSRobert Mustacchi 		ret = false;
281*564eeb7dSRobert Mustacchi 		goto done;
282*564eeb7dSRobert Mustacchi 	}
283*564eeb7dSRobert Mustacchi 
284*564eeb7dSRobert Mustacchi 	if (!errinj_req_fail(ctrl, req, "error injection with missing fields",
285*564eeb7dSRobert Mustacchi 	    NVME_ERR_OCP_ERRINJ_REQ_MISSING_FIELDS)) {
286*564eeb7dSRobert Mustacchi 		ret = false;
287*564eeb7dSRobert Mustacchi 	}
288*564eeb7dSRobert Mustacchi 
289*564eeb7dSRobert Mustacchi 	if (!errinj_set_fail(ctrl, "type", NVME_ERR_OCP_ERRINJ_TYPE_RANGE,
290*564eeb7dSRobert Mustacchi 	    nvme_ocp_errinj_req_set_type(req, 0xb22b7777)))
291*564eeb7dSRobert Mustacchi 		ret = false;
292*564eeb7dSRobert Mustacchi 
293*564eeb7dSRobert Mustacchi 	if (!errinj_set_fail(ctrl, "nrtde", NVME_ERR_OCP_ERRINJ_NRTDE_RANGE,
294*564eeb7dSRobert Mustacchi 	    nvme_ocp_errinj_req_set_nrtde(req, 0x12345)))
295*564eeb7dSRobert Mustacchi 		ret = false;
296*564eeb7dSRobert Mustacchi 
297*564eeb7dSRobert Mustacchi 	/*
298*564eeb7dSRobert Mustacchi 	 * Set us up for an unusable error on exec when we set an ld value with
299*564eeb7dSRobert Mustacchi 	 * a different type.
300*564eeb7dSRobert Mustacchi 	 */
301*564eeb7dSRobert Mustacchi 	if (!nvme_ocp_errinj_req_set_type(req, OCP_ERRINJ_T_CPU_HANG) ||
302*564eeb7dSRobert Mustacchi 	    !nvme_ocp_errinj_req_set_ld(req, 0x23)) {
303*564eeb7dSRobert Mustacchi 		libnvme_test_ctrl_warn(ctrl, "failed to set fields for error "
304*564eeb7dSRobert Mustacchi 		    "injection duration not settable");
305*564eeb7dSRobert Mustacchi 		ret = false;
306*564eeb7dSRobert Mustacchi 	} else if (!errinj_req_fail(ctrl, req, "error injection duration not "
307*564eeb7dSRobert Mustacchi 	    "settable", NVME_ERR_OCP_ERRINJ_LD_UNUSE)) {
308*564eeb7dSRobert Mustacchi 		ret = false;
309*564eeb7dSRobert Mustacchi 	}
310*564eeb7dSRobert Mustacchi 
311*564eeb7dSRobert Mustacchi done:
312*564eeb7dSRobert Mustacchi 	nvme_ocp_errinj_req_fini(req);
313*564eeb7dSRobert Mustacchi 	return (ret);
314*564eeb7dSRobert Mustacchi }
315*564eeb7dSRobert Mustacchi 
316*564eeb7dSRobert Mustacchi /*
317*564eeb7dSRobert Mustacchi  * Test error injection by creating something with a high rate of reads required
318*564eeb7dSRobert Mustacchi  * that we shouldn't trigger. Verify we can see it with a GET FEATURE. Then
319*564eeb7dSRobert Mustacchi  * clear it. Verify it's not there in a GET.
320*564eeb7dSRobert Mustacchi  */
321*564eeb7dSRobert Mustacchi static bool
roundtrip_errinj(nvme_ctrl_t * ctrl)322*564eeb7dSRobert Mustacchi roundtrip_errinj(nvme_ctrl_t *ctrl)
323*564eeb7dSRobert Mustacchi {
324*564eeb7dSRobert Mustacchi 	bool ret = true;
325*564eeb7dSRobert Mustacchi 	nvme_ocp_errinj_req_t *req = NULL;
326*564eeb7dSRobert Mustacchi 	nvme_get_feat_req_t *get = NULL;
327*564eeb7dSRobert Mustacchi 	nvme_feat_disc_t *disc = NULL;
328*564eeb7dSRobert Mustacchi 	void *buf = NULL;
329*564eeb7dSRobert Mustacchi 	uint64_t len;
330*564eeb7dSRobert Mustacchi 	uint32_t cdw0;
331*564eeb7dSRobert Mustacchi 
332*564eeb7dSRobert Mustacchi 	if (!nvme_get_feat_req_init_by_name(ctrl, "ocp/errinj", 0, &disc,
333*564eeb7dSRobert Mustacchi 	    &get)) {
334*564eeb7dSRobert Mustacchi 		libnvme_test_ctrl_warn(ctrl, "failed to create error injection "
335*564eeb7dSRobert Mustacchi 		    "get feature request");
336*564eeb7dSRobert Mustacchi 		ret = false;
337*564eeb7dSRobert Mustacchi 		goto done;
338*564eeb7dSRobert Mustacchi 	}
339*564eeb7dSRobert Mustacchi 
340*564eeb7dSRobert Mustacchi 	len = nvme_feat_disc_data_size(disc);
341*564eeb7dSRobert Mustacchi 	buf = calloc(len, sizeof (uint8_t));
342*564eeb7dSRobert Mustacchi 	if (buf == NULL) {
343*564eeb7dSRobert Mustacchi 		err(EXIT_FAILURE, "INTERNAL TEST FAILED: failed to alloc %"
344*564eeb7dSRobert Mustacchi 		    PRIu64 "bytes for error injection buffer", len);
345*564eeb7dSRobert Mustacchi 	}
346*564eeb7dSRobert Mustacchi 	if (!nvme_get_feat_req_set_output(get, buf, len)) {
347*564eeb7dSRobert Mustacchi 		libnvme_test_ctrl_warn(ctrl, "failed to set get feature output "
348*564eeb7dSRobert Mustacchi 		    "buffer");
349*564eeb7dSRobert Mustacchi 		ret = false;
350*564eeb7dSRobert Mustacchi 		goto done;
351*564eeb7dSRobert Mustacchi 	}
352*564eeb7dSRobert Mustacchi 
353*564eeb7dSRobert Mustacchi 	if (!nvme_ocp_errinj_req_init(ctrl, &req)) {
354*564eeb7dSRobert Mustacchi 		libnvme_test_ctrl_warn(ctrl, "failed to create error injection "
355*564eeb7dSRobert Mustacchi 		    "request");
356*564eeb7dSRobert Mustacchi 		ret = false;
357*564eeb7dSRobert Mustacchi 		goto done;
358*564eeb7dSRobert Mustacchi 	}
359*564eeb7dSRobert Mustacchi 
360*564eeb7dSRobert Mustacchi 	if (!nvme_ocp_errinj_req_set_type(req, OCP_ERRINJ_T_CPU_HANG) ||
361*564eeb7dSRobert Mustacchi 	    !nvme_ocp_errinj_req_set_nrtde(req, 0x7777)) {
362*564eeb7dSRobert Mustacchi 		libnvme_test_ctrl_warn(ctrl, "failed to set basic error "
363*564eeb7dSRobert Mustacchi 		    "injection fields");
364*564eeb7dSRobert Mustacchi 		ret = false;
365*564eeb7dSRobert Mustacchi 		goto done;
366*564eeb7dSRobert Mustacchi 	}
367*564eeb7dSRobert Mustacchi 
368*564eeb7dSRobert Mustacchi 	if (!nvme_ocp_errinj_req_exec(req)) {
369*564eeb7dSRobert Mustacchi 		libnvme_test_ctrl_warn(ctrl, "failed to execute error "
370*564eeb7dSRobert Mustacchi 		    "injection request");
371*564eeb7dSRobert Mustacchi 		ret = false;
372*564eeb7dSRobert Mustacchi 		goto done;
373*564eeb7dSRobert Mustacchi 	}
374*564eeb7dSRobert Mustacchi 
375*564eeb7dSRobert Mustacchi 	if (!nvme_get_feat_req_exec(get) || !nvme_get_feat_req_get_cdw0(get,
376*564eeb7dSRobert Mustacchi 	    &cdw0)) {
377*564eeb7dSRobert Mustacchi 		libnvme_test_ctrl_warn(ctrl, "failed to get error injection "
378*564eeb7dSRobert Mustacchi 		    "request and data");
379*564eeb7dSRobert Mustacchi 		ret = false;
380*564eeb7dSRobert Mustacchi 		goto done;
381*564eeb7dSRobert Mustacchi 	}
382*564eeb7dSRobert Mustacchi 
383*564eeb7dSRobert Mustacchi 	if (cdw0 != 1) {
384*564eeb7dSRobert Mustacchi 		warnx("TEST FAILED: error injection pending count is 0x%x, but "
385*564eeb7dSRobert Mustacchi 		    "expected it to be 1", cdw0);
386*564eeb7dSRobert Mustacchi 		ret = false;
387*564eeb7dSRobert Mustacchi 	}
388*564eeb7dSRobert Mustacchi 
389*564eeb7dSRobert Mustacchi 	const volatile ocp_vuf_errinj_t *err = buf;
390*564eeb7dSRobert Mustacchi 	if (err[0].oei_flags != (OCP_ERRINJ_F_ENABLE | OCP_ERRINJ_F_SINGLE)) {
391*564eeb7dSRobert Mustacchi 		warnx("TEST FAILED: found unexpected error flags 0x%x, "
392*564eeb7dSRobert Mustacchi 		    "expected 0x%x", err[0].oei_flags, OCP_ERRINJ_F_ENABLE |
393*564eeb7dSRobert Mustacchi 		    OCP_ERRINJ_F_SINGLE);
394*564eeb7dSRobert Mustacchi 		ret = false;
395*564eeb7dSRobert Mustacchi 	}
396*564eeb7dSRobert Mustacchi 
397*564eeb7dSRobert Mustacchi 	if (err[0].oei_type != OCP_ERRINJ_T_CPU_HANG) {
398*564eeb7dSRobert Mustacchi 		warnx("TEST FAILED: found unexpected error type 0x%x, expected "
399*564eeb7dSRobert Mustacchi 		    "0x%x", err[0].oei_type, OCP_ERRINJ_T_CPU_HANG);
400*564eeb7dSRobert Mustacchi 		ret = false;
401*564eeb7dSRobert Mustacchi 	}
402*564eeb7dSRobert Mustacchi 
403*564eeb7dSRobert Mustacchi 	/*
404*564eeb7dSRobert Mustacchi 	 * Assume that there is some number of reads potentially going on in the
405*564eeb7dSRobert Mustacchi 	 * background, but hopefully not too much.
406*564eeb7dSRobert Mustacchi 	 */
407*564eeb7dSRobert Mustacchi 	if (err[0].oei_nrtde < 0x6666) {
408*564eeb7dSRobert Mustacchi 		warnx("TEST FAILED: found unexpected nrtde value: 0x%x",
409*564eeb7dSRobert Mustacchi 		    err[0].oei_nrtde);
410*564eeb7dSRobert Mustacchi 		ret = false;
411*564eeb7dSRobert Mustacchi 	}
412*564eeb7dSRobert Mustacchi 
413*564eeb7dSRobert Mustacchi 	if (!nvme_ocp_errinj_clear(ctrl)) {
414*564eeb7dSRobert Mustacchi 		libnvme_test_ctrl_warn(ctrl, "failed to clear error injection "
415*564eeb7dSRobert Mustacchi 		    "request");
416*564eeb7dSRobert Mustacchi 		ret = false;
417*564eeb7dSRobert Mustacchi 		goto done;
418*564eeb7dSRobert Mustacchi 	}
419*564eeb7dSRobert Mustacchi 
420*564eeb7dSRobert Mustacchi 	if (!nvme_get_feat_req_exec(get) || !nvme_get_feat_req_get_cdw0(get,
421*564eeb7dSRobert Mustacchi 	    &cdw0)) {
422*564eeb7dSRobert Mustacchi 		libnvme_test_ctrl_warn(ctrl, "failed to get error injection "
423*564eeb7dSRobert Mustacchi 		    "request and data");
424*564eeb7dSRobert Mustacchi 		ret = false;
425*564eeb7dSRobert Mustacchi 		goto done;
426*564eeb7dSRobert Mustacchi 	}
427*564eeb7dSRobert Mustacchi 
428*564eeb7dSRobert Mustacchi 	if (cdw0 != 0) {
429*564eeb7dSRobert Mustacchi 		warnx("TEST FAILED: error injection pending count is 0x%x "
430*564eeb7dSRobert Mustacchi 		    "after clear, but expected 0", cdw0);
431*564eeb7dSRobert Mustacchi 		ret = false;
432*564eeb7dSRobert Mustacchi 	}
433*564eeb7dSRobert Mustacchi 
434*564eeb7dSRobert Mustacchi 	if (err[0].oei_flags != 0) {
435*564eeb7dSRobert Mustacchi 		warnx("TEST FAILED: found unexpected error flags 0x%x after "
436*564eeb7dSRobert Mustacchi 		    "clear, but expected 0", err[0].oei_flags);
437*564eeb7dSRobert Mustacchi 		ret = false;
438*564eeb7dSRobert Mustacchi 	}
439*564eeb7dSRobert Mustacchi 
440*564eeb7dSRobert Mustacchi 	if (err[0].oei_type != 0) {
441*564eeb7dSRobert Mustacchi 		warnx("TEST FAILED: found unexpected error type 0x%x after "
442*564eeb7dSRobert Mustacchi 		    "clear, but expected 0", err[0].oei_type);
443*564eeb7dSRobert Mustacchi 		ret = false;
444*564eeb7dSRobert Mustacchi 	}
445*564eeb7dSRobert Mustacchi 
446*564eeb7dSRobert Mustacchi 	if (err[0].oei_nrtde != 0) {
447*564eeb7dSRobert Mustacchi 		warnx("TEST FAILED: found unexpected nrtde 0x%x after "
448*564eeb7dSRobert Mustacchi 		    "clear, but expected 0", err[0].oei_nrtde);
449*564eeb7dSRobert Mustacchi 		ret = false;
450*564eeb7dSRobert Mustacchi 	}
451*564eeb7dSRobert Mustacchi 
452*564eeb7dSRobert Mustacchi 	if (ret) {
453*564eeb7dSRobert Mustacchi 		(void) printf("TEST PASSED: error injection roundtrip\n");
454*564eeb7dSRobert Mustacchi 	}
455*564eeb7dSRobert Mustacchi 
456*564eeb7dSRobert Mustacchi done:
457*564eeb7dSRobert Mustacchi 	nvme_ocp_errinj_req_fini(req);
458*564eeb7dSRobert Mustacchi 	free(buf);
459*564eeb7dSRobert Mustacchi 	nvme_feat_disc_free(disc);
460*564eeb7dSRobert Mustacchi 	nvme_get_feat_req_fini(get);
461*564eeb7dSRobert Mustacchi 	return (ret);
462*564eeb7dSRobert Mustacchi }
463*564eeb7dSRobert Mustacchi 
464*564eeb7dSRobert Mustacchi int
main(void)465*564eeb7dSRobert Mustacchi main(void)
466*564eeb7dSRobert Mustacchi {
467*564eeb7dSRobert Mustacchi 	int ret = EXIT_SUCCESS;
468*564eeb7dSRobert Mustacchi 
469*564eeb7dSRobert Mustacchi 	nvme_t *nvme;
470*564eeb7dSRobert Mustacchi 	nvme_ctrl_t *ctrl;
471*564eeb7dSRobert Mustacchi 
472*564eeb7dSRobert Mustacchi 	libnvme_test_init(&nvme, &ctrl);
473*564eeb7dSRobert Mustacchi 	set_feats_supported(ctrl);
474*564eeb7dSRobert Mustacchi 
475*564eeb7dSRobert Mustacchi 	/*
476*564eeb7dSRobert Mustacchi 	 * Verify that if we try to set a feature without a lock this fails.
477*564eeb7dSRobert Mustacchi 	 */
478*564eeb7dSRobert Mustacchi 	if (!set_feat_fail(ctrl, "ocp/plphealth", "without control lock", 0x23,
479*564eeb7dSRobert Mustacchi 	    NVME_ERR_NEED_CTRL_WRLOCK)) {
480*564eeb7dSRobert Mustacchi 		ret = EXIT_FAILURE;
481*564eeb7dSRobert Mustacchi 	}
482*564eeb7dSRobert Mustacchi 
483*564eeb7dSRobert Mustacchi 	/*
484*564eeb7dSRobert Mustacchi 	 * Setting a standard feature should fail.
485*564eeb7dSRobert Mustacchi 	 */
486*564eeb7dSRobert Mustacchi 	if (!set_feat_fail(ctrl, "queues", "standard feature", 0x7777,
487*564eeb7dSRobert Mustacchi 	    NVME_ERR_NEED_CTRL_WRLOCK)) {
488*564eeb7dSRobert Mustacchi 		ret = EXIT_FAILURE;
489*564eeb7dSRobert Mustacchi 	}
490*564eeb7dSRobert Mustacchi 
491*564eeb7dSRobert Mustacchi 	/*
492*564eeb7dSRobert Mustacchi 	 * Take a controller lock for all the remaining tests.
493*564eeb7dSRobert Mustacchi 	 */
494*564eeb7dSRobert Mustacchi 	if (!nvme_ctrl_lock(ctrl, NVME_LOCK_L_WRITE, 0)) {
495*564eeb7dSRobert Mustacchi 		libnvme_test_ctrl_fatal(ctrl, "failed to take a write lock: "
496*564eeb7dSRobert Mustacchi 		    "cannot continue");
497*564eeb7dSRobert Mustacchi 	}
498*564eeb7dSRobert Mustacchi 
499*564eeb7dSRobert Mustacchi 	if (!roundtrip_plp(ctrl))
500*564eeb7dSRobert Mustacchi 		ret = EXIT_FAILURE;
501*564eeb7dSRobert Mustacchi 
502*564eeb7dSRobert Mustacchi 	if (!errinj_errors(ctrl))
503*564eeb7dSRobert Mustacchi 		ret = EXIT_FAILURE;
504*564eeb7dSRobert Mustacchi 
505*564eeb7dSRobert Mustacchi 	if (!roundtrip_errinj(ctrl))
506*564eeb7dSRobert Mustacchi 		ret = EXIT_FAILURE;
507*564eeb7dSRobert Mustacchi 
508*564eeb7dSRobert Mustacchi 	nvme_ctrl_unlock(ctrl);
509*564eeb7dSRobert Mustacchi 	nvme_ctrl_fini(ctrl);
510*564eeb7dSRobert Mustacchi 	nvme_fini(nvme);
511*564eeb7dSRobert Mustacchi 
512*564eeb7dSRobert Mustacchi 	if (ret == EXIT_SUCCESS) {
513*564eeb7dSRobert Mustacchi 		(void) printf("All tests exited successfully\n");
514*564eeb7dSRobert Mustacchi 	}
515*564eeb7dSRobert Mustacchi 
516*564eeb7dSRobert Mustacchi 	return (ret);
517*564eeb7dSRobert Mustacchi }
518