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