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