/* * This file and its contents are supplied under the terms of the * Common Development and Distribution License ("CDDL"), version 1.0. * You may only use this file in accordance with the terms of version * 1.0 of the CDDL. * * A full copy of the text of the CDDL should have accompanied this * source. A copy of the CDDL is also available via the Internet at * http://www.illumos.org/license/CDDL. */ /* * Copyright 2024 Oxide Computer Company */ /* * This file contains several synthetic controllers that we plan to use * throughout the rest of our various unit tests. We define rather minimal bits * of the identify controller data structures here. The relevant bits for these * tests are generally the following: * * - Firmware Commit and Download -- id_oacs.oa_firmware (1.0) * - Firmware Update Granularity -- ap_fwug (1.3) * - Format NVM Support -- id_oacs.oa_format (1.0) * - Volatile Write Cache Present -- id_vwc.vwc_present (1.0) * - Autonomous Power State Suport -- id_apsta.ap_sup (1.1) * - Namespace Count -- id_nn (1.0) * - Namespace Management -- id_oacs.oa_nsmgmt (1.2) * - Save/Select in Get/Set Feat -- id_oncs.on_save (1.1) * - Extended Get Log Page -- id_lpa.lp_extsup (1.2) * - Smart/Health Info per NS -- id_lpa.lp_smart (1.0) * - Error Log Page Entries -- id_elpe (1.0) (Z) * - Namespace Change Notices -- id_oaes.oaes_nsan (1.2) * * Note, we skip adding the controller version mostly because our common code * doesn't use it and that way we can reuse entries more often. Items that the * spec defines as zeros based are indicated with a trailing Z. That means that * software will treat the value as what's there + 1. */ #include #include #include "nvme_unit.h" /* * We start with a basic controller. This has a single namespace and supports * the optional format and firmware commands. It doesn't have a volatile write * cache. */ static const nvme_identify_ctrl_t nvme_ctrl_base = { .id_oacs = { .oa_firmware = 1, .oa_format = 1 }, .id_nn = 1, .id_frmw = { .fw_nslot = 1 }, .id_elpe = 3 }; const nvme_valid_ctrl_data_t nvme_ctrl_base_1v0 = { .vcd_vers = &nvme_vers_1v0, .vcd_id = &nvme_ctrl_base }; const nvme_valid_ctrl_data_t nvme_ctrl_base_1v1 = { .vcd_vers = &nvme_vers_1v1, .vcd_id = &nvme_ctrl_base }; const nvme_valid_ctrl_data_t nvme_ctrl_base_1v2 = { .vcd_vers = &nvme_vers_1v2, .vcd_id = &nvme_ctrl_base }; const nvme_valid_ctrl_data_t nvme_ctrl_base_2v0 = { .vcd_vers = &nvme_vers_2v0, .vcd_id = &nvme_ctrl_base }; /* * An NVMe 1.0 version of the base controller with per-NS Health. */ static const nvme_identify_ctrl_t nvme_ctrl_base_health = { .id_oacs = { .oa_firmware = 1, .oa_format = 1 }, .id_lpa = { .lp_smart = 1 }, .id_nn = 1, .id_frmw = { .fw_nslot = 1 }, .id_elpe = 3 }; const nvme_valid_ctrl_data_t nvme_ctrl_health_1v0 = { .vcd_vers = &nvme_vers_1v0, .vcd_id = &nvme_ctrl_base_health }; /* * Next, a more complex controller that has all the current optional features. * It has namespace support with 128 namespaces. */ static const nvme_identify_ctrl_t nvme_ctrl_fancy = { .id_oacs = { .oa_firmware = 1, .oa_format = 1, .oa_nsmgmt = 1 }, .id_oncs = { .on_save = 1, }, .id_vwc = { .vwc_present = 1 }, .id_apsta = { .ap_sup = 1 }, .id_nn = 128, .id_frmw = { .fw_nslot = 1 }, .id_lpa = { .lp_extsup = 1, .lp_smart = 1, .lp_cmdeff = 1, }, .id_oaes = { .oaes_nsan = 1 } }; const nvme_valid_ctrl_data_t nvme_ctrl_ns_1v2 = { .vcd_vers = &nvme_vers_1v2, .vcd_id = &nvme_ctrl_fancy }; const nvme_valid_ctrl_data_t nvme_ctrl_ns_1v3 = { .vcd_vers = &nvme_vers_1v3, .vcd_id = &nvme_ctrl_fancy }; const nvme_valid_ctrl_data_t nvme_ctrl_ns_1v4 = { .vcd_vers = &nvme_vers_1v4, .vcd_id = &nvme_ctrl_fancy }; const nvme_valid_ctrl_data_t nvme_ctrl_ns_2v0 = { .vcd_vers = &nvme_vers_2v0, .vcd_id = &nvme_ctrl_fancy }; /* * This next controller is designed to help test log size and offset properties. * A log offset is only allowed if the corresponding LPA is set. Similarly, the * length changes from 12 bits to 32 bits of dwords when that is present. */ static const nvme_identify_ctrl_t nvme_ctrl_nolpa = { .id_oacs = { .oa_firmware = 1, .oa_format = 1, .oa_nsmgmt = 1 }, .id_oncs = { .on_save = 1, }, .id_vwc = { .vwc_present = 1 }, .id_apsta = { .ap_sup = 1 }, .id_nn = 128, .id_frmw = { .fw_nslot = 1 }, .id_oaes = { .oaes_nsan = 1 } }; const nvme_valid_ctrl_data_t nvme_ctrl_nolpa_1v4 = { .vcd_vers = &nvme_vers_1v4, .vcd_id = &nvme_ctrl_nolpa }; /* * A variant on the fancy controller without namespace management. */ static const nvme_identify_ctrl_t nvme_ctrl_nons = { .id_oacs = { .oa_firmware = 1, .oa_format = 1, }, .id_oncs = { .on_save = 1, }, .id_vwc = { .vwc_present = 1 }, .id_apsta = { .ap_sup = 1 }, .id_nn = 1, .id_frmw = { .fw_nslot = 1 }, .id_lpa = { .lp_extsup = 1, .lp_smart = 1, .lp_cmdeff = 1 }, .id_oaes = { .oaes_nsan = 1 } }; const nvme_valid_ctrl_data_t nvme_ctrl_nons_1v3 = { .vcd_vers = &nvme_vers_1v3, .vcd_id = &nvme_ctrl_nons }; const nvme_valid_ctrl_data_t nvme_ctrl_nons_1v4 = { .vcd_vers = &nvme_vers_1v4, .vcd_id = &nvme_ctrl_nons }; const nvme_valid_ctrl_data_t nvme_ctrl_nons_2v0 = { .vcd_vers = &nvme_vers_2v0, .vcd_id = &nvme_ctrl_nons }; /* * This is a controller that supports none of the optional features at all. */ static const nvme_identify_ctrl_t nvme_ctrl_nocmds = { .id_nn = 1, .id_frmw = { .fw_nslot = 1 }, }; const nvme_valid_ctrl_data_t nvme_ctrl_nocmds_1v0 = { .vcd_vers = &nvme_vers_1v0, .vcd_id = &nvme_ctrl_nocmds }; /* * Controllers with explicitly no granularity and one with 8k. */ static const nvme_identify_ctrl_t nvme_ctrl_nogran = { .id_oacs = { .oa_firmware = 1, .oa_format = 1, }, .id_oncs = { .on_save = 1 }, .id_frmw = { .fw_nslot = 3 }, .id_nn = 1, .ap_fwug = 0xff }; static const nvme_identify_ctrl_t nvme_ctrl_8kgran = { .id_oacs = { .oa_firmware = 1, .oa_format = 1, }, .id_oncs = { .on_save = 1 }, .id_frmw = { .fw_nslot = 7 }, .id_nn = 1, .ap_fwug = 0x2 }; const nvme_valid_ctrl_data_t nvme_ctrl_nogran_1v3 = { .vcd_vers = &nvme_vers_1v3, .vcd_id = &nvme_ctrl_nogran }; const nvme_valid_ctrl_data_t nvme_ctrl_8kgran_1v3 = { .vcd_vers = &nvme_vers_1v3, .vcd_id = &nvme_ctrl_8kgran }; static const char * nvme_field_error_to_str(nvme_field_error_t err) { switch (err) { case NVME_FIELD_ERR_OK: return ("NVME_FIELD_ERR_OK"); case NVME_FIELD_ERR_UNSUP_VERSION: return ("NVME_FIELD_ERR_UNSUP_VERSION"); case NVME_FIELD_ERR_UNSUP_FIELD: return ("NVME_FIELD_ERR_UNSUP_FIELD"); case NVME_FIELD_ERR_BAD_VALUE: return ("NVME_FIELD_ERR_BAD_VALUE"); default: return ("unknown"); } } static bool nvme_unit_field_test_one(const nvme_unit_field_test_t *test) { char buf[128]; const nvme_field_info_t *field; nvme_field_error_t err; buf[0] = '\0'; field = &test->nu_fields[test->nu_index]; err = nvme_field_validate(field, test->nu_data, test->nu_value, buf, sizeof (buf)); if (err != test->nu_ret) { warnx("TEST FAILED: %s: found wrong return value %s (%u), " "expected %s (%u)", test->nu_desc, nvme_field_error_to_str(err), err, nvme_field_error_to_str(test->nu_ret), test->nu_ret); return (false); } (void) printf("TEST PASSED: %s: got correct return value\n", test->nu_desc); if (err != NVME_FIELD_ERR_OK && buf[0] == '\0') { warnx("TEST FAILED: %s: error buffer was empty", test->nu_desc); return (false); } else if (err == NVME_FIELD_ERR_OK && buf[0] != '\0') { warnx("TEST FAILED: %s: error buffer was not empty", test->nu_desc); return (false); } (void) printf("TEST PASSED: %s: error buffer properly formed\n", test->nu_desc); return (true); } bool nvme_unit_field_test(const nvme_unit_field_test_t *tests, size_t ntests) { bool ret = true; for (size_t i = 0; i < ntests; i++) { if (!nvme_unit_field_test_one(&tests[i])) { ret = false; } } return (ret); }