xref: /illumos-gate/usr/src/test/nvme-tests/tests/unit/controllers.c (revision 6a6b02a5b5fa41079d2d47a96b026fd2f8040ac6)
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 2026 Oxide Computer Company
14  */
15 
16 /*
17  * This file contains several synthetic controllers that we plan to use
18  * throughout the rest of our various unit tests. We define rather minimal bits
19  * of the identify controller data structures here. The relevant bits for these
20  * tests are generally the following:
21  *
22  *  - Firmware Commit and Download -- id_oacs.oa_firmware (1.0)
23  *  - Firmware Update Granularity -- ap_fwug (1.3)
24  *  - Format NVM Support -- id_oacs.oa_format (1.0)
25  *  - Volatile Write Cache Present -- id_vwc.vwc_present (1.0)
26  *  - Autonomous Power State Suport -- id_apsta.ap_sup (1.1)
27  *  - Namespace Count -- id_nn (1.0)
28  *  - Namespace Management -- id_oacs.oa_nsmgmt (1.2)
29  *  - Save/Select in Get/Set Feat -- id_oncs.on_save (1.1)
30  *  - Extended Get Log Page -- id_lpa.lp_extsup (1.2)
31  *  - Smart/Health Info per NS -- id_lpa.lp_smart (1.0)
32  *  - Error Log Page Entries -- id_elpe (1.0) (Z)
33  *  - Namespace Change Notices -- id_oaes.oaes_nsan (1.2)
34  *
35  * Note, we skip adding the controller version mostly because our common code
36  * doesn't use it and that way we can reuse entries more often. Items that the
37  * spec defines as zeros based are indicated with a trailing Z. That means that
38  * software will treat the value as what's there + 1.
39  */
40 
41 #include <err.h>
42 #include <stdio.h>
43 
44 #include "nvme_unit.h"
45 
46 /*
47  * We start with a basic controller. This has a single namespace and supports
48  * the optional format and firmware commands. It doesn't have a volatile write
49  * cache.
50  */
51 static const nvme_identify_ctrl_t nvme_ctrl_base = {
52 	.id_oacs = {
53 		.oa_firmware = 1,
54 		.oa_format = 1
55 	},
56 	.id_nn = 1,
57 	.id_frmw = {
58 		.fw_nslot = 1
59 	},
60 	.id_elpe = 3
61 };
62 
63 const nvme_valid_ctrl_data_t nvme_ctrl_base_1v0 = {
64 	.vcd_vers = &nvme_vers_1v0,
65 	.vcd_id = &nvme_ctrl_base
66 };
67 
68 const nvme_valid_ctrl_data_t nvme_ctrl_base_1v1 = {
69 	.vcd_vers = &nvme_vers_1v1,
70 	.vcd_id = &nvme_ctrl_base
71 };
72 
73 const nvme_valid_ctrl_data_t nvme_ctrl_base_1v2 = {
74 	.vcd_vers = &nvme_vers_1v2,
75 	.vcd_id = &nvme_ctrl_base
76 };
77 
78 const nvme_valid_ctrl_data_t nvme_ctrl_base_2v0 = {
79 	.vcd_vers = &nvme_vers_2v0,
80 	.vcd_id = &nvme_ctrl_base
81 };
82 
83 const nvme_valid_ctrl_data_t nvme_ctrl_base_2v2 = {
84 	.vcd_vers = &nvme_vers_2v2,
85 	.vcd_id = &nvme_ctrl_base
86 };
87 
88 
89 /*
90  * An NVMe 1.0 version of the base controller with per-NS Health.
91  */
92 static const nvme_identify_ctrl_t nvme_ctrl_base_health = {
93 	.id_oacs = {
94 		.oa_firmware = 1,
95 		.oa_format = 1
96 	},
97 	.id_lpa = {
98 		.lp_smart = 1
99 	},
100 	.id_nn = 1,
101 	.id_frmw = {
102 		.fw_nslot = 1
103 	},
104 	.id_elpe = 3
105 };
106 
107 const nvme_valid_ctrl_data_t nvme_ctrl_health_1v0 = {
108 	.vcd_vers = &nvme_vers_1v0,
109 	.vcd_id = &nvme_ctrl_base_health
110 };
111 
112 /*
113  * Next, a more complex controller that has all the current optional features.
114  * It has namespace support with 128 namespaces.
115  */
116 static const nvme_identify_ctrl_t nvme_ctrl_fancy = {
117 	.id_oacs = {
118 		.oa_firmware = 1,
119 		.oa_format = 1,
120 		.oa_nsmgmt = 1
121 	},
122 	.id_oncs = {
123 		.on_save = 1,
124 	},
125 	.id_vwc = {
126 		.vwc_present = 1
127 	},
128 	.id_apsta = {
129 		.ap_sup = 1
130 	},
131 	.id_nn = 128,
132 	.id_frmw = {
133 		.fw_nslot = 1
134 	},
135 	.id_lpa = {
136 		.lp_extsup = 1,
137 		.lp_smart = 1,
138 		.lp_cmdeff = 1,
139 		.lp_persist = 1
140 	},
141 	.id_oaes = {
142 		.oaes_nsan = 1
143 	}
144 };
145 
146 const nvme_valid_ctrl_data_t nvme_ctrl_ns_1v2 = {
147 	.vcd_vers = &nvme_vers_1v2,
148 	.vcd_id = &nvme_ctrl_fancy
149 };
150 
151 const nvme_valid_ctrl_data_t nvme_ctrl_ns_1v3 = {
152 	.vcd_vers = &nvme_vers_1v3,
153 	.vcd_id = &nvme_ctrl_fancy
154 };
155 
156 const nvme_valid_ctrl_data_t nvme_ctrl_ns_1v4 = {
157 	.vcd_vers = &nvme_vers_1v4,
158 	.vcd_id = &nvme_ctrl_fancy
159 };
160 
161 const nvme_valid_ctrl_data_t nvme_ctrl_ns_2v0 = {
162 	.vcd_vers = &nvme_vers_2v0,
163 	.vcd_id = &nvme_ctrl_fancy
164 };
165 
166 /*
167  * This next controller is designed to help test log size and offset properties.
168  * A log offset is only allowed if the corresponding LPA is set. Similarly, the
169  * length changes from 12 bits to 32 bits of dwords when that is present.
170  */
171 static const nvme_identify_ctrl_t nvme_ctrl_nolpa = {
172 	.id_oacs = {
173 		.oa_firmware = 1,
174 		.oa_format = 1,
175 		.oa_nsmgmt = 1
176 	},
177 	.id_oncs = {
178 		.on_save = 1,
179 	},
180 	.id_vwc = {
181 		.vwc_present = 1
182 	},
183 	.id_apsta = {
184 		.ap_sup = 1
185 	},
186 	.id_nn = 128,
187 	.id_frmw = {
188 		.fw_nslot = 1
189 	},
190 	.id_oaes = {
191 		.oaes_nsan = 1
192 	}
193 };
194 
195 const nvme_valid_ctrl_data_t nvme_ctrl_nolpa_1v4 = {
196 	.vcd_vers = &nvme_vers_1v4,
197 	.vcd_id = &nvme_ctrl_nolpa
198 };
199 
200 /*
201  * A variant on the fancy controller without namespace management.
202  */
203 static const nvme_identify_ctrl_t nvme_ctrl_nons = {
204 	.id_oacs = {
205 		.oa_firmware = 1,
206 		.oa_format = 1,
207 	},
208 	.id_oncs = {
209 		.on_save = 1,
210 	},
211 	.id_vwc = {
212 		.vwc_present = 1
213 	},
214 	.id_apsta = {
215 		.ap_sup = 1
216 	},
217 	.id_nn = 1,
218 	.id_frmw = {
219 		.fw_nslot = 1
220 	},
221 	.id_lpa = {
222 		.lp_extsup = 1,
223 		.lp_smart = 1,
224 		.lp_cmdeff = 1,
225 		.lp_persist = 1
226 	},
227 	.id_oaes = {
228 		.oaes_nsan = 1
229 	}
230 };
231 
232 const nvme_valid_ctrl_data_t nvme_ctrl_nons_1v3 = {
233 	.vcd_vers = &nvme_vers_1v3,
234 	.vcd_id = &nvme_ctrl_nons
235 };
236 
237 const nvme_valid_ctrl_data_t nvme_ctrl_nons_1v4 = {
238 	.vcd_vers = &nvme_vers_1v4,
239 	.vcd_id = &nvme_ctrl_nons
240 };
241 
242 const nvme_valid_ctrl_data_t nvme_ctrl_nons_2v0 = {
243 	.vcd_vers = &nvme_vers_2v0,
244 	.vcd_id = &nvme_ctrl_nons
245 };
246 
247 /*
248  * This is a controller that supports none of the optional features at all.
249  */
250 static const nvme_identify_ctrl_t nvme_ctrl_nocmds = {
251 	.id_nn = 1,
252 	.id_frmw = {
253 		.fw_nslot = 1
254 	},
255 };
256 
257 const nvme_valid_ctrl_data_t nvme_ctrl_nocmds_1v0 = {
258 	.vcd_vers = &nvme_vers_1v0,
259 	.vcd_id = &nvme_ctrl_nocmds
260 };
261 
262 /*
263  * Controllers with explicitly no granularity and one with 8k.
264  */
265 static const nvme_identify_ctrl_t nvme_ctrl_nogran = {
266 	.id_oacs = {
267 		.oa_firmware = 1,
268 		.oa_format = 1,
269 	},
270 	.id_oncs = {
271 		.on_save = 1
272 	},
273 	.id_frmw = {
274 		.fw_nslot = 3
275 	},
276 	.id_nn = 1,
277 	.ap_fwug = 0xff
278 };
279 
280 static const nvme_identify_ctrl_t nvme_ctrl_8kgran = {
281 	.id_oacs = {
282 		.oa_firmware = 1,
283 		.oa_format = 1,
284 	},
285 	.id_oncs = {
286 		.on_save = 1
287 	},
288 	.id_frmw = {
289 		.fw_nslot = 7
290 	},
291 	.id_nn = 1,
292 	.ap_fwug = 0x2
293 };
294 
295 const nvme_valid_ctrl_data_t nvme_ctrl_nogran_1v3 = {
296 	.vcd_vers = &nvme_vers_1v3,
297 	.vcd_id = &nvme_ctrl_nogran
298 };
299 
300 const nvme_valid_ctrl_data_t nvme_ctrl_8kgran_1v3 = {
301 	.vcd_vers = &nvme_vers_1v3,
302 	.vcd_id = &nvme_ctrl_8kgran
303 };
304 
305 static const char *
nvme_field_error_to_str(nvme_field_error_t err)306 nvme_field_error_to_str(nvme_field_error_t err)
307 {
308 	switch (err) {
309 	case NVME_FIELD_ERR_OK:
310 		return ("NVME_FIELD_ERR_OK");
311 	case NVME_FIELD_ERR_UNSUP_VERSION:
312 		return ("NVME_FIELD_ERR_UNSUP_VERSION");
313 	case NVME_FIELD_ERR_UNSUP_FIELD:
314 		return ("NVME_FIELD_ERR_UNSUP_FIELD");
315 	case NVME_FIELD_ERR_BAD_VALUE:
316 		return ("NVME_FIELD_ERR_BAD_VALUE");
317 	default:
318 		return ("unknown");
319 	}
320 }
321 
322 static bool
nvme_unit_field_test_one(const nvme_unit_field_test_t * test)323 nvme_unit_field_test_one(const nvme_unit_field_test_t *test)
324 {
325 	char buf[128];
326 	const nvme_field_info_t *field;
327 	nvme_field_error_t err;
328 
329 	buf[0] = '\0';
330 	field = &test->nu_fields[test->nu_index];
331 	err = nvme_field_validate(field, test->nu_data, test->nu_value, buf,
332 	    sizeof (buf));
333 
334 	if (err != test->nu_ret) {
335 		warnx("TEST FAILED: %s: found wrong return value %s (%u), "
336 		    "expected %s (%u)", test->nu_desc,
337 		    nvme_field_error_to_str(err), err,
338 		    nvme_field_error_to_str(test->nu_ret), test->nu_ret);
339 		return (false);
340 	}
341 
342 	(void) printf("TEST PASSED: %s: got correct return value\n",
343 	    test->nu_desc);
344 	if (err != NVME_FIELD_ERR_OK && buf[0] == '\0') {
345 		warnx("TEST FAILED: %s: error buffer was empty", test->nu_desc);
346 		return (false);
347 	} else if (err == NVME_FIELD_ERR_OK && buf[0] != '\0') {
348 		warnx("TEST FAILED: %s: error buffer was not empty",
349 		    test->nu_desc);
350 		return (false);
351 	}
352 
353 	(void) printf("TEST PASSED: %s: error buffer properly formed\n",
354 	    test->nu_desc);
355 	return (true);
356 }
357 
358 bool
nvme_unit_field_test(const nvme_unit_field_test_t * tests,size_t ntests)359 nvme_unit_field_test(const nvme_unit_field_test_t *tests, size_t ntests)
360 {
361 	bool ret = true;
362 
363 	for (size_t i = 0; i < ntests; i++) {
364 		if (!nvme_unit_field_test_one(&tests[i])) {
365 			ret = false;
366 		}
367 	}
368 
369 	return (ret);
370 }
371