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