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 * 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 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 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