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