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 * 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
84 /*
85 * An NVMe 1.0 version of the base controller with per-NS Health.
86 */
87 static const nvme_identify_ctrl_t nvme_ctrl_base_health = {
88 .id_oacs = {
89 .oa_firmware = 1,
90 .oa_format = 1
91 },
92 .id_lpa = {
93 .lp_smart = 1
94 },
95 .id_nn = 1,
96 .id_frmw = {
97 .fw_nslot = 1
98 },
99 .id_elpe = 3
100 };
101
102 const nvme_valid_ctrl_data_t nvme_ctrl_health_1v0 = {
103 .vcd_vers = &nvme_vers_1v0,
104 .vcd_id = &nvme_ctrl_base_health
105 };
106
107 /*
108 * Next, a more complex controller that has all the current optional features.
109 * It has namespace support with 128 namespaces.
110 */
111 static const nvme_identify_ctrl_t nvme_ctrl_fancy = {
112 .id_oacs = {
113 .oa_firmware = 1,
114 .oa_format = 1,
115 .oa_nsmgmt = 1
116 },
117 .id_oncs = {
118 .on_save = 1,
119 },
120 .id_vwc = {
121 .vwc_present = 1
122 },
123 .id_apsta = {
124 .ap_sup = 1
125 },
126 .id_nn = 128,
127 .id_frmw = {
128 .fw_nslot = 1
129 },
130 .id_lpa = {
131 .lp_extsup = 1,
132 .lp_smart = 1,
133 .lp_cmdeff = 1,
134 },
135 .id_oaes = {
136 .oaes_nsan = 1
137 }
138 };
139
140 const nvme_valid_ctrl_data_t nvme_ctrl_ns_1v2 = {
141 .vcd_vers = &nvme_vers_1v2,
142 .vcd_id = &nvme_ctrl_fancy
143 };
144
145 const nvme_valid_ctrl_data_t nvme_ctrl_ns_1v3 = {
146 .vcd_vers = &nvme_vers_1v3,
147 .vcd_id = &nvme_ctrl_fancy
148 };
149
150 const nvme_valid_ctrl_data_t nvme_ctrl_ns_1v4 = {
151 .vcd_vers = &nvme_vers_1v4,
152 .vcd_id = &nvme_ctrl_fancy
153 };
154
155 const nvme_valid_ctrl_data_t nvme_ctrl_ns_2v0 = {
156 .vcd_vers = &nvme_vers_2v0,
157 .vcd_id = &nvme_ctrl_fancy
158 };
159
160 /*
161 * This next controller is designed to help test log size and offset properties.
162 * A log offset is only allowed if the corresponding LPA is set. Similarly, the
163 * length changes from 12 bits to 32 bits of dwords when that is present.
164 */
165 static const nvme_identify_ctrl_t nvme_ctrl_nolpa = {
166 .id_oacs = {
167 .oa_firmware = 1,
168 .oa_format = 1,
169 .oa_nsmgmt = 1
170 },
171 .id_oncs = {
172 .on_save = 1,
173 },
174 .id_vwc = {
175 .vwc_present = 1
176 },
177 .id_apsta = {
178 .ap_sup = 1
179 },
180 .id_nn = 128,
181 .id_frmw = {
182 .fw_nslot = 1
183 },
184 .id_oaes = {
185 .oaes_nsan = 1
186 }
187 };
188
189 const nvme_valid_ctrl_data_t nvme_ctrl_nolpa_1v4 = {
190 .vcd_vers = &nvme_vers_1v4,
191 .vcd_id = &nvme_ctrl_nolpa
192 };
193
194 /*
195 * A variant on the fancy controller without namespace management.
196 */
197 static const nvme_identify_ctrl_t nvme_ctrl_nons = {
198 .id_oacs = {
199 .oa_firmware = 1,
200 .oa_format = 1,
201 },
202 .id_oncs = {
203 .on_save = 1,
204 },
205 .id_vwc = {
206 .vwc_present = 1
207 },
208 .id_apsta = {
209 .ap_sup = 1
210 },
211 .id_nn = 1,
212 .id_frmw = {
213 .fw_nslot = 1
214 },
215 .id_lpa = {
216 .lp_extsup = 1,
217 .lp_smart = 1,
218 .lp_cmdeff = 1
219 },
220 .id_oaes = {
221 .oaes_nsan = 1
222 }
223 };
224
225 const nvme_valid_ctrl_data_t nvme_ctrl_nons_1v3 = {
226 .vcd_vers = &nvme_vers_1v3,
227 .vcd_id = &nvme_ctrl_nons
228 };
229
230 const nvme_valid_ctrl_data_t nvme_ctrl_nons_1v4 = {
231 .vcd_vers = &nvme_vers_1v4,
232 .vcd_id = &nvme_ctrl_nons
233 };
234
235 const nvme_valid_ctrl_data_t nvme_ctrl_nons_2v0 = {
236 .vcd_vers = &nvme_vers_2v0,
237 .vcd_id = &nvme_ctrl_nons
238 };
239
240 /*
241 * This is a controller that supports none of the optional features at all.
242 */
243 static const nvme_identify_ctrl_t nvme_ctrl_nocmds = {
244 .id_nn = 1,
245 .id_frmw = {
246 .fw_nslot = 1
247 },
248 };
249
250 const nvme_valid_ctrl_data_t nvme_ctrl_nocmds_1v0 = {
251 .vcd_vers = &nvme_vers_1v0,
252 .vcd_id = &nvme_ctrl_nocmds
253 };
254
255 /*
256 * Controllers with explicitly no granularity and one with 8k.
257 */
258 static const nvme_identify_ctrl_t nvme_ctrl_nogran = {
259 .id_oacs = {
260 .oa_firmware = 1,
261 .oa_format = 1,
262 },
263 .id_oncs = {
264 .on_save = 1
265 },
266 .id_frmw = {
267 .fw_nslot = 3
268 },
269 .id_nn = 1,
270 .ap_fwug = 0xff
271 };
272
273 static const nvme_identify_ctrl_t nvme_ctrl_8kgran = {
274 .id_oacs = {
275 .oa_firmware = 1,
276 .oa_format = 1,
277 },
278 .id_oncs = {
279 .on_save = 1
280 },
281 .id_frmw = {
282 .fw_nslot = 7
283 },
284 .id_nn = 1,
285 .ap_fwug = 0x2
286 };
287
288 const nvme_valid_ctrl_data_t nvme_ctrl_nogran_1v3 = {
289 .vcd_vers = &nvme_vers_1v3,
290 .vcd_id = &nvme_ctrl_nogran
291 };
292
293 const nvme_valid_ctrl_data_t nvme_ctrl_8kgran_1v3 = {
294 .vcd_vers = &nvme_vers_1v3,
295 .vcd_id = &nvme_ctrl_8kgran
296 };
297
298 static const char *
nvme_field_error_to_str(nvme_field_error_t err)299 nvme_field_error_to_str(nvme_field_error_t err)
300 {
301 switch (err) {
302 case NVME_FIELD_ERR_OK:
303 return ("NVME_FIELD_ERR_OK");
304 case NVME_FIELD_ERR_UNSUP_VERSION:
305 return ("NVME_FIELD_ERR_UNSUP_VERSION");
306 case NVME_FIELD_ERR_UNSUP_FIELD:
307 return ("NVME_FIELD_ERR_UNSUP_FIELD");
308 case NVME_FIELD_ERR_BAD_VALUE:
309 return ("NVME_FIELD_ERR_BAD_VALUE");
310 default:
311 return ("unknown");
312 }
313 }
314
315 static bool
nvme_unit_field_test_one(const nvme_unit_field_test_t * test)316 nvme_unit_field_test_one(const nvme_unit_field_test_t *test)
317 {
318 char buf[128];
319 const nvme_field_info_t *field;
320 nvme_field_error_t err;
321
322 buf[0] = '\0';
323 field = &test->nu_fields[test->nu_index];
324 err = nvme_field_validate(field, test->nu_data, test->nu_value, buf,
325 sizeof (buf));
326
327 if (err != test->nu_ret) {
328 warnx("TEST FAILED: %s: found wrong return value %s (%u), "
329 "expected %s (%u)", test->nu_desc,
330 nvme_field_error_to_str(err), err,
331 nvme_field_error_to_str(test->nu_ret), test->nu_ret);
332 return (false);
333 }
334
335 (void) printf("TEST PASSED: %s: got correct return value\n",
336 test->nu_desc);
337 if (err != NVME_FIELD_ERR_OK && buf[0] == '\0') {
338 warnx("TEST FAILED: %s: error buffer was empty", test->nu_desc);
339 return (false);
340 } else if (err == NVME_FIELD_ERR_OK && buf[0] != '\0') {
341 warnx("TEST FAILED: %s: error buffer was not empty",
342 test->nu_desc);
343 return (false);
344 }
345
346 (void) printf("TEST PASSED: %s: error buffer properly formed\n",
347 test->nu_desc);
348 return (true);
349 }
350
351 bool
nvme_unit_field_test(const nvme_unit_field_test_t * tests,size_t ntests)352 nvme_unit_field_test(const nvme_unit_field_test_t *tests, size_t ntests)
353 {
354 bool ret = true;
355
356 for (size_t i = 0; i < ntests; i++) {
357 if (!nvme_unit_field_test_one(&tests[i])) {
358 ret = false;
359 }
360 }
361
362 return (ret);
363 }
364