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  * Unit tests for the common feature code. Covering fields and whether or not
18  * specific features are supported.
19  */
20 
21 #include <stdlib.h>
22 #include <sys/sysmacros.h>
23 #include <err.h>
24 
25 #include "nvme_unit.h"
26 
27 static const nvme_unit_field_test_t feature_field_tests[] = { {
28 	.nu_desc = "invalid FID (1)",
29 	.nu_fields = nvme_get_feat_fields,
30 	.nu_index = NVME_GET_FEAT_REQ_FIELD_FID,
31 	.nu_data = &nvme_ctrl_base_1v0,
32 	.nu_value = 0x100,
33 	.nu_ret = NVME_FIELD_ERR_BAD_VALUE
34 }, {
35 	.nu_desc = "invalid FID (2)",
36 	.nu_fields = nvme_get_feat_fields,
37 	.nu_index = NVME_GET_FEAT_REQ_FIELD_FID,
38 	.nu_data = &nvme_ctrl_base_1v0,
39 	.nu_value = 0x54321,
40 	.nu_ret = NVME_FIELD_ERR_BAD_VALUE
41 }, {
42 	.nu_desc = "valid FID (1)",
43 	.nu_fields = nvme_get_feat_fields,
44 	.nu_index = NVME_GET_FEAT_REQ_FIELD_FID,
45 	.nu_data = &nvme_ctrl_base_1v0,
46 	.nu_value = 0x0,
47 	.nu_ret = NVME_FIELD_ERR_OK
48 }, {
49 	.nu_desc = "valid FID (1)",
50 	.nu_fields = nvme_get_feat_fields,
51 	.nu_index = NVME_GET_FEAT_REQ_FIELD_FID,
52 	.nu_data = &nvme_ctrl_base_1v0,
53 	.nu_value = 0x0,
54 	.nu_ret = NVME_FIELD_ERR_OK
55 }, {
56 	.nu_desc = "valid FID (2)",
57 	.nu_fields = nvme_get_feat_fields,
58 	.nu_index = NVME_GET_FEAT_REQ_FIELD_FID,
59 	.nu_data = &nvme_ctrl_base_1v0,
60 	.nu_value = 0x78,
61 	.nu_ret = NVME_FIELD_ERR_OK
62 }, {
63 	.nu_desc = "valid FID (3)",
64 	.nu_fields = nvme_get_feat_fields,
65 	.nu_index = NVME_GET_FEAT_REQ_FIELD_FID,
66 	.nu_data = &nvme_ctrl_base_1v0,
67 	.nu_value = 0xaa,
68 	.nu_ret = NVME_FIELD_ERR_OK
69 }, {
70 	.nu_desc = "valid FID (4)",
71 	.nu_fields = nvme_get_feat_fields,
72 	.nu_index = NVME_GET_FEAT_REQ_FIELD_FID,
73 	.nu_data = &nvme_ctrl_base_1v0,
74 	.nu_value = 0xc0,
75 	.nu_ret = NVME_FIELD_ERR_OK
76 }, {
77 	.nu_desc = "valid FID (5)",
78 	.nu_fields = nvme_get_feat_fields,
79 	.nu_index = NVME_GET_FEAT_REQ_FIELD_FID,
80 	.nu_data = &nvme_ctrl_base_1v0,
81 	.nu_value = 0xff,
82 	.nu_ret = NVME_FIELD_ERR_OK
83 }, {
84 	.nu_desc = "unsupported sel (1.0)",
85 	.nu_fields = nvme_get_feat_fields,
86 	.nu_index = NVME_GET_FEAT_REQ_FIELD_SEL,
87 	.nu_data = &nvme_ctrl_base_1v0,
88 	.nu_value = 0x0,
89 	.nu_ret = NVME_FIELD_ERR_UNSUP_VERSION
90 }, {
91 	.nu_desc = "unsupported sel (1.1 No ONCS)",
92 	.nu_fields = nvme_get_feat_fields,
93 	.nu_index = NVME_GET_FEAT_REQ_FIELD_SEL,
94 	.nu_data = &nvme_ctrl_base_1v1,
95 	.nu_value = 0x0,
96 	.nu_ret = NVME_FIELD_ERR_UNSUP_FIELD
97 }, {
98 	.nu_desc = "unsupported sel (2.0 No ONCS)",
99 	.nu_fields = nvme_get_feat_fields,
100 	.nu_index = NVME_GET_FEAT_REQ_FIELD_SEL,
101 	.nu_data = &nvme_ctrl_base_2v0,
102 	.nu_value = 0x0,
103 	.nu_ret = NVME_FIELD_ERR_UNSUP_FIELD
104 }, {
105 	.nu_desc = "invalid sel (1)",
106 	.nu_fields = nvme_get_feat_fields,
107 	.nu_index = NVME_GET_FEAT_REQ_FIELD_SEL,
108 	.nu_data = &nvme_ctrl_ns_1v2,
109 	.nu_value = 0x4,
110 	.nu_ret = NVME_FIELD_ERR_BAD_VALUE
111 }, {
112 	.nu_desc = "invalid sel (2)",
113 	.nu_fields = nvme_get_feat_fields,
114 	.nu_index = NVME_GET_FEAT_REQ_FIELD_SEL,
115 	.nu_data = &nvme_ctrl_ns_1v2,
116 	.nu_value = 0x11,
117 	.nu_ret = NVME_FIELD_ERR_BAD_VALUE
118 }, {
119 	.nu_desc = "valid sel (1)",
120 	.nu_fields = nvme_get_feat_fields,
121 	.nu_index = NVME_GET_FEAT_REQ_FIELD_SEL,
122 	.nu_data = &nvme_ctrl_ns_1v2,
123 	.nu_value = 0x0,
124 	.nu_ret = NVME_FIELD_ERR_OK
125 }, {
126 	.nu_desc = "valid sel (2)",
127 	.nu_fields = nvme_get_feat_fields,
128 	.nu_index = NVME_GET_FEAT_REQ_FIELD_SEL,
129 	.nu_data = &nvme_ctrl_ns_1v2,
130 	.nu_value = 0x3,
131 	.nu_ret = NVME_FIELD_ERR_OK
132 }, {
133 	.nu_desc = "valid sel (2)",
134 	.nu_fields = nvme_get_feat_fields,
135 	.nu_index = NVME_GET_FEAT_REQ_FIELD_SEL,
136 	.nu_data = &nvme_ctrl_ns_1v2,
137 	.nu_value = 0x2,
138 	.nu_ret = NVME_FIELD_ERR_OK
139 }, {
140 	.nu_desc = "invalid cdw11 (1)",
141 	.nu_fields = nvme_get_feat_fields,
142 	.nu_index = NVME_GET_FEAT_REQ_FIELD_CDW11,
143 	.nu_data = &nvme_ctrl_base_1v0,
144 	.nu_value = 0x100000000,
145 	.nu_ret = NVME_FIELD_ERR_BAD_VALUE
146 }, {
147 	.nu_desc = "invalid cdw11 (2)",
148 	.nu_fields = nvme_get_feat_fields,
149 	.nu_index = NVME_GET_FEAT_REQ_FIELD_CDW11,
150 	.nu_data = &nvme_ctrl_base_1v0,
151 	.nu_value = 0x8765445678,
152 	.nu_ret = NVME_FIELD_ERR_BAD_VALUE
153 }, {
154 	.nu_desc = "valid cdw11 (1)",
155 	.nu_fields = nvme_get_feat_fields,
156 	.nu_index = NVME_GET_FEAT_REQ_FIELD_CDW11,
157 	.nu_data = &nvme_ctrl_base_1v0,
158 	.nu_value = 0x0,
159 	.nu_ret = NVME_FIELD_ERR_OK
160 }, {
161 	.nu_desc = "valid cdw11 (2)",
162 	.nu_fields = nvme_get_feat_fields,
163 	.nu_index = NVME_GET_FEAT_REQ_FIELD_CDW11,
164 	.nu_data = &nvme_ctrl_base_1v0,
165 	.nu_value = 0xffffffff,
166 	.nu_ret = NVME_FIELD_ERR_OK
167 }, {
168 	.nu_desc = "valid cdw11 (3)",
169 	.nu_fields = nvme_get_feat_fields,
170 	.nu_index = NVME_GET_FEAT_REQ_FIELD_CDW11,
171 	.nu_data = &nvme_ctrl_base_1v0,
172 	.nu_value = 0x6543210,
173 	.nu_ret = NVME_FIELD_ERR_OK
174 }, {
175 	.nu_desc = "invalid nsid (1)",
176 	.nu_fields = nvme_get_feat_fields,
177 	.nu_index = NVME_GET_FEAT_REQ_FIELD_NSID,
178 	.nu_data = &nvme_ctrl_base_1v0,
179 	.nu_value = 0x0,
180 	.nu_ret = NVME_FIELD_ERR_BAD_VALUE
181 }, {
182 	.nu_desc = "invalid nsid (2)",
183 	.nu_fields = nvme_get_feat_fields,
184 	.nu_index = NVME_GET_FEAT_REQ_FIELD_NSID,
185 	.nu_data = &nvme_ctrl_base_1v0,
186 	.nu_value = 0xfffffffe,
187 	.nu_ret = NVME_FIELD_ERR_BAD_VALUE
188 }, {
189 	.nu_desc = "invalid nsid (3)",
190 	.nu_fields = nvme_get_feat_fields,
191 	.nu_index = NVME_GET_FEAT_REQ_FIELD_NSID,
192 	.nu_data = &nvme_ctrl_base_1v0,
193 	.nu_value = 0x2,
194 	.nu_ret = NVME_FIELD_ERR_BAD_VALUE
195 }, {
196 	.nu_desc = "valid nsid (1)",
197 	.nu_fields = nvme_get_feat_fields,
198 	.nu_index = NVME_GET_FEAT_REQ_FIELD_NSID,
199 	.nu_data = &nvme_ctrl_base_1v0,
200 	.nu_value = 0x1,
201 	.nu_ret = NVME_FIELD_ERR_OK
202 }, {
203 	.nu_desc = "valid nsid (2)",
204 	.nu_fields = nvme_get_feat_fields,
205 	.nu_index = NVME_GET_FEAT_REQ_FIELD_NSID,
206 	.nu_data = &nvme_ctrl_base_1v0,
207 	.nu_value = NVME_NSID_BCAST,
208 	.nu_ret = NVME_FIELD_ERR_OK
209 }, {
210 	.nu_desc = "valid nsid (3)",
211 	.nu_fields = nvme_get_feat_fields,
212 	.nu_index = NVME_GET_FEAT_REQ_FIELD_NSID,
213 	.nu_data = &nvme_ctrl_ns_1v4,
214 	.nu_value = 0x80,
215 	.nu_ret = NVME_FIELD_ERR_OK
216 } };
217 
218 typedef struct feature_impl_test {
219 	const char *fit_desc;
220 	uint32_t fit_fid;
221 	const nvme_valid_ctrl_data_t *fit_data;
222 	nvme_feat_impl_t fit_impl;
223 } feature_impl_test_t;
224 
225 static const feature_impl_test_t feature_impl_tests[] = { {
226 	.fit_desc = "arbitration supported (1.0)",
227 	.fit_fid = NVME_FEAT_ARBITRATION,
228 	.fit_data =  &nvme_ctrl_base_1v0,
229 	.fit_impl = NVME_FEAT_IMPL_SUPPORTED
230 }, {
231 	.fit_desc = "arbitration supported (2.0)",
232 	.fit_fid = NVME_FEAT_ARBITRATION,
233 	.fit_data =  &nvme_ctrl_ns_2v0,
234 	.fit_impl = NVME_FEAT_IMPL_SUPPORTED
235 }, {
236 	.fit_desc = "power management supported (1.0)",
237 	.fit_fid = NVME_FEAT_POWER_MGMT,
238 	.fit_data =  &nvme_ctrl_base_1v0,
239 	.fit_impl = NVME_FEAT_IMPL_SUPPORTED
240 }, {
241 	.fit_desc = "power management supported (2.0)",
242 	.fit_fid = NVME_FEAT_POWER_MGMT,
243 	.fit_data =  &nvme_ctrl_ns_2v0,
244 	.fit_impl = NVME_FEAT_IMPL_SUPPORTED
245 }, {
246 	.fit_desc = "LBA range type unknown (1.0)",
247 	.fit_fid = NVME_FEAT_LBA_RANGE,
248 	.fit_data =  &nvme_ctrl_base_1v0,
249 	.fit_impl = NVME_FEAT_IMPL_UNKNOWN
250 }, {
251 	.fit_desc = "LBA range type unknown (1.4)",
252 	.fit_fid = NVME_FEAT_LBA_RANGE,
253 	.fit_data =  &nvme_ctrl_ns_1v4,
254 	.fit_impl = NVME_FEAT_IMPL_UNKNOWN
255 }, {
256 	.fit_desc = "temp supported (1.0)",
257 	.fit_fid = NVME_FEAT_TEMPERATURE,
258 	.fit_data =  &nvme_ctrl_base_1v0,
259 	.fit_impl = NVME_FEAT_IMPL_SUPPORTED
260 }, {
261 	.fit_desc = "temp supported (1.2)",
262 	.fit_fid = NVME_FEAT_TEMPERATURE,
263 	.fit_data =  &nvme_ctrl_base_1v2,
264 	.fit_impl = NVME_FEAT_IMPL_SUPPORTED
265 }, {
266 	.fit_desc = "temp supported (1.3)",
267 	.fit_fid = NVME_FEAT_TEMPERATURE,
268 	.fit_data =  &nvme_ctrl_nogran_1v3,
269 	.fit_impl = NVME_FEAT_IMPL_SUPPORTED
270 }, {
271 	.fit_desc = "VWC unsupported (1)",
272 	.fit_fid = NVME_FEAT_WRITE_CACHE,
273 	.fit_data =  &nvme_ctrl_base_1v0,
274 	.fit_impl = NVME_FEAT_IMPL_UNSUPPORTED
275 }, {
276 	.fit_desc = "VWC unsupported (2)",
277 	.fit_fid = NVME_FEAT_WRITE_CACHE,
278 	.fit_data =  &nvme_ctrl_base_2v0,
279 	.fit_impl = NVME_FEAT_IMPL_UNSUPPORTED
280 }, {
281 	.fit_desc = "VWC supported (1)",
282 	.fit_fid = NVME_FEAT_WRITE_CACHE,
283 	.fit_data =  &nvme_ctrl_ns_1v2,
284 	.fit_impl = NVME_FEAT_IMPL_SUPPORTED
285 }, {
286 	.fit_desc = "VWC supported (2)",
287 	.fit_fid = NVME_FEAT_WRITE_CACHE,
288 	.fit_data =  &nvme_ctrl_ns_1v4,
289 	.fit_impl = NVME_FEAT_IMPL_SUPPORTED
290 }, {
291 	.fit_desc = "VWC supported (3)",
292 	.fit_fid = NVME_FEAT_WRITE_CACHE,
293 	.fit_data =  &nvme_ctrl_ns_2v0,
294 	.fit_impl = NVME_FEAT_IMPL_SUPPORTED
295 }, {
296 	.fit_desc = "queues supported (1)",
297 	.fit_fid = NVME_FEAT_NQUEUES,
298 	.fit_data =  &nvme_ctrl_ns_2v0,
299 	.fit_impl = NVME_FEAT_IMPL_SUPPORTED
300 }, {
301 	.fit_desc = "queues supported (2)",
302 	.fit_fid = NVME_FEAT_NQUEUES,
303 	.fit_data =  &nvme_ctrl_base_1v0,
304 	.fit_impl = NVME_FEAT_IMPL_SUPPORTED
305 }, {
306 	.fit_desc = "interrupt coalescing supported (1)",
307 	.fit_fid = NVME_FEAT_INTR_COAL,
308 	.fit_data =  &nvme_ctrl_base_1v0,
309 	.fit_impl = NVME_FEAT_IMPL_SUPPORTED
310 }, {
311 	.fit_desc = "interrupt coalescing supported (2)",
312 	.fit_fid = NVME_FEAT_INTR_COAL,
313 	.fit_data =  &nvme_ctrl_base_1v2,
314 	.fit_impl = NVME_FEAT_IMPL_SUPPORTED
315 }, {
316 	.fit_desc = "interrupt coalescing supported (3)",
317 	.fit_fid = NVME_FEAT_INTR_COAL,
318 	.fit_data =  &nvme_ctrl_ns_1v4,
319 	.fit_impl = NVME_FEAT_IMPL_SUPPORTED
320 }, {
321 	.fit_desc = "interrupt vector config supported (1)",
322 	.fit_fid = NVME_FEAT_INTR_VECT,
323 	.fit_data =  &nvme_ctrl_base_1v1,
324 	.fit_impl = NVME_FEAT_IMPL_SUPPORTED
325 }, {
326 	.fit_desc = "interrupt vector config supported (2)",
327 	.fit_fid = NVME_FEAT_INTR_VECT,
328 	.fit_data =  &nvme_ctrl_ns_1v3,
329 	.fit_impl = NVME_FEAT_IMPL_SUPPORTED
330 }, {
331 	.fit_desc = "interrupt vector config supported (3)",
332 	.fit_fid = NVME_FEAT_INTR_VECT,
333 	.fit_data =  &nvme_ctrl_ns_2v0,
334 	.fit_impl = NVME_FEAT_IMPL_SUPPORTED
335 }, {
336 	.fit_desc = "write atomicity supported (1)",
337 	.fit_fid = NVME_FEAT_WRITE_ATOM,
338 	.fit_data =  &nvme_ctrl_base_1v1,
339 	.fit_impl = NVME_FEAT_IMPL_SUPPORTED
340 }, {
341 	.fit_desc = "write atomicity supported (2)",
342 	.fit_fid = NVME_FEAT_WRITE_ATOM,
343 	.fit_data =  &nvme_ctrl_ns_1v3,
344 	.fit_impl = NVME_FEAT_IMPL_SUPPORTED
345 }, {
346 	.fit_desc = "write atomicity supported (3)",
347 	.fit_fid = NVME_FEAT_WRITE_ATOM,
348 	.fit_data =  &nvme_ctrl_ns_2v0,
349 	.fit_impl = NVME_FEAT_IMPL_SUPPORTED
350 }, {
351 	.fit_desc = "async event config supported (1)",
352 	.fit_fid = NVME_FEAT_ASYNC_EVENT,
353 	.fit_data =  &nvme_ctrl_base_1v0,
354 	.fit_impl = NVME_FEAT_IMPL_SUPPORTED
355 }, {
356 	.fit_desc = "async event config supported (2)",
357 	.fit_fid = NVME_FEAT_ASYNC_EVENT,
358 	.fit_data =  &nvme_ctrl_base_1v2,
359 	.fit_impl = NVME_FEAT_IMPL_SUPPORTED
360 }, {
361 	.fit_desc = "async event config supported (3)",
362 	.fit_fid = NVME_FEAT_ASYNC_EVENT,
363 	.fit_data =  &nvme_ctrl_ns_1v4,
364 	.fit_impl = NVME_FEAT_IMPL_SUPPORTED
365 }, {
366 	.fit_desc = "apst unsupported",
367 	.fit_fid = NVME_FEAT_AUTO_PST,
368 	.fit_data =  &nvme_ctrl_base_1v0,
369 	.fit_impl = NVME_FEAT_IMPL_UNSUPPORTED
370 }, {
371 	.fit_desc = "apst supported",
372 	.fit_fid = NVME_FEAT_AUTO_PST,
373 	.fit_data =  &nvme_ctrl_ns_1v2,
374 	.fit_impl = NVME_FEAT_IMPL_SUPPORTED
375 }, {
376 	.fit_desc = "software progress marker unknown (1)",
377 	.fit_fid = NVME_FEAT_PROGRESS,
378 	.fit_data =  &nvme_ctrl_base_1v0,
379 	.fit_impl = NVME_FEAT_IMPL_UNKNOWN
380 }, {
381 	.fit_desc = "software progress marker unknown (2)",
382 	.fit_fid = NVME_FEAT_PROGRESS,
383 	.fit_data =  &nvme_ctrl_base_2v0,
384 	.fit_impl = NVME_FEAT_IMPL_UNKNOWN
385 } };
386 
387 static bool
feature_impl_test_one(const feature_impl_test_t * test)388 feature_impl_test_one(const feature_impl_test_t *test)
389 {
390 	const nvme_feat_info_t *info = NULL;
391 	nvme_feat_impl_t impl;
392 
393 	for (size_t i = 0; i < nvme_std_nfeats; i++) {
394 		if (nvme_std_feats[i].nfeat_fid == test->fit_fid) {
395 			info = &nvme_std_feats[i];
396 			break;
397 		}
398 	}
399 
400 	if (info == NULL) {
401 		errx(EXIT_FAILURE, "malformed test %s: cannot find FID %u",
402 		    test->fit_desc, test->fit_fid);
403 	}
404 
405 	impl = nvme_feat_supported(info, test->fit_data);
406 	if (impl != test->fit_impl) {
407 		warnx("TEST FAILED: %s: expected impl %u, found %u",
408 		    test->fit_desc, test->fit_impl, impl);
409 		return (false);
410 	}
411 
412 	(void) printf("TEST PASSED: %s: got correct impl\n", test->fit_desc);
413 	return (true);
414 }
415 
416 int
main(void)417 main(void)
418 {
419 	int ret = EXIT_SUCCESS;
420 
421 	if (!nvme_unit_field_test(feature_field_tests,
422 	    ARRAY_SIZE(feature_field_tests))) {
423 		ret = EXIT_FAILURE;
424 	}
425 
426 	for (size_t i = 0; i < ARRAY_SIZE(feature_impl_tests); i++) {
427 		if (!feature_impl_test_one(&feature_impl_tests[i])) {
428 			ret = EXIT_FAILURE;
429 		}
430 	}
431 
432 	if (ret == EXIT_SUCCESS) {
433 		(void) printf("All tests passed successfully!\n");
434 	}
435 
436 	return (ret);
437 }
438