root/usr/src/test/util-tests/tests/smbios/smbios_test_fwinfo.c
/*
 * 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 2025 Oxide Computer Company
 */

/*
 * Tests for SMBIOS type 45 - SMB_TYPE_FWINFO.
 */

#include "smbios_test.h"

static const char *smbios_fwinfo_name = "Sheikah Slate";
static const char *smbios_fwinfo_vers = "1.3.1";
static const char *smbios_fwinfo_id = "ganon";
static const char *smbios_fwinfo_reldate = "2017-03-03";
static const char *smbios_fwinfo_mfg = "The Goddess Hylia";
static const char *smbios_fwinfo_lsv = "zelda";
static const uint64_t smbios_fwinfo_size = 0xb0111b;
static const uint_t smbios_fwinfo_ncomps = 23;
static const uint8_t smbios_fwinfo_versid = 0x66;

static void
smbios_test_fwinfo_mktable_common_fwinfo(smb_fwinfo_t *fw)
{
        fw->smbfwii_hdr.smbh_type = SMB_TYPE_FWINFO;
        fw->smbfwii_hdr.smbh_len = sizeof (*fw);
        fw->smbfwii_name = 1;
        fw->smbfwii_vers = 2;
        fw->smbfwii_vers_fmt = smbios_fwinfo_versid;
        fw->smbfwii_id = 3;
        fw->smbfwii_id_fmt = smbios_fwinfo_versid;
        fw->smbfwii_reldate = 4;
        fw->smbfwii_mfg = 5;
        fw->smbfwii_lsv = 6;
        fw->smbfwii_imgsz = htole64(smbios_fwinfo_size);
        fw->smbfwii_chars = SMB_FWC_WP;
        fw->smbfwii_state = SMB_FWS_UA_OFFLINE;
        fw->smbfwii_ncomps = 0;
}

static void
smbios_test_fwinfo_mktable_common_fini(smbios_test_table_t *table)
{
        smbios_test_table_append_string(table, smbios_fwinfo_name);
        smbios_test_table_append_string(table, smbios_fwinfo_vers);
        smbios_test_table_append_string(table, smbios_fwinfo_id);
        smbios_test_table_append_string(table, smbios_fwinfo_reldate);
        smbios_test_table_append_string(table, smbios_fwinfo_mfg);
        smbios_test_table_append_string(table, smbios_fwinfo_lsv);
        smbios_test_table_str_fini(table);
        smbios_test_table_append_eot(table);
}

/*
 * Generate a table that's too short to get basic info.
 */
boolean_t
smbios_test_fwinfo_mktable_invlen_base(smbios_test_table_t *table)
{
        smb_header_t hdr;

        hdr.smbh_type = SMB_TYPE_FWINFO;
        hdr.smbh_len = sizeof (hdr);

        (void) smbios_test_table_append(table, &hdr, sizeof (hdr));
        smbios_test_table_append_eot(table);

        return (B_TRUE);
}

/*
 * Generate a table where the table is too short for a specified number of
 * components.
 */
boolean_t
smbios_test_fwinfo_mktable_invlen_comps(smbios_test_table_t *table)
{
        smb_fwinfo_t fw;

        smbios_test_fwinfo_mktable_common_fwinfo(&fw);
        fw.smbfwii_ncomps = smbios_fwinfo_ncomps;
        (void) smbios_test_table_append(table, &fw, sizeof (fw));
        smbios_test_fwinfo_mktable_common_fini(table);

        return (B_TRUE);
}

boolean_t
smbios_test_fwinfo_mktable_nocomps(smbios_test_table_t *table)
{
        smb_fwinfo_t fw;

        smbios_test_fwinfo_mktable_common_fwinfo(&fw);
        (void) smbios_test_table_append(table, &fw, sizeof (fw));
        smbios_test_fwinfo_mktable_common_fini(table);

        return (B_TRUE);
}

boolean_t
smbios_test_fwinfo_mktable_comps(smbios_test_table_t *table)
{
        smb_fwinfo_t fw;

        smbios_test_fwinfo_mktable_common_fwinfo(&fw);

        fw.smbfwii_hdr.smbh_len += smbios_fwinfo_ncomps * sizeof (uint16_t);
        fw.smbfwii_ncomps = smbios_fwinfo_ncomps;
        (void) smbios_test_table_append(table, &fw, sizeof (fw));

        for (uint_t i = 0; i < fw.smbfwii_ncomps; i++) {
                uint16_t comp = 0x2300 + i;

                smbios_test_table_append_raw(table, &comp, sizeof (comp));
        }
        smbios_test_fwinfo_mktable_common_fini(table);

        return (B_TRUE);
}

boolean_t
smbios_test_fwinfo_verify_badtype(smbios_hdl_t *hdl)
{
        smbios_struct_t sp;
        smbios_fwinfo_t fw;

        if (smbios_lookup_type(hdl, SMB_TYPE_MEMDEVICE, &sp) == -1) {
                warnx("failed to lookup SMBIOS memory device: %s",
                    smbios_errmsg(smbios_errno(hdl)));
                return (B_FALSE);
        }

        if (smbios_info_fwinfo(hdl, sp.smbstr_id, &fw) != -1) {
                warnx("accidentally parsed invalid fwinfo information as "
                    "valid");
                return (B_FALSE);
        }

        if (smbios_errno(hdl) != ESMB_TYPE) {
                warnx("encountered wrong error for fwinfo, expected: "
                    "0x%x, found: 0x%x", ESMB_TYPE, smbios_errno(hdl));
                return (B_FALSE);
        }

        return (B_TRUE);
}

boolean_t
smbios_test_fwinfo_verify_invlen_base(smbios_hdl_t *hdl)
{
        smbios_struct_t sp;
        smbios_fwinfo_t fw;

        if (smbios_lookup_type(hdl, SMB_TYPE_FWINFO, &sp) == -1) {
                warnx("failed to lookup SMBIOS fwinfo: %s",
                    smbios_errmsg(smbios_errno(hdl)));
                return (B_FALSE);
        }

        if (smbios_info_fwinfo(hdl, sp.smbstr_id, &fw) != -1) {
                warnx("accidentally parsed invalid fwinfo information as "
                    "valid");
                return (B_FALSE);
        }

        if (smbios_errno(hdl) != ESMB_SHORT) {
                warnx("encountered wrong error for fwinfo, expected: "
                    "0x%x, found: 0x%x", ESMB_SHORT, smbios_errno(hdl));
                return (B_FALSE);
        }

        return (B_TRUE);
}

static boolean_t
smbios_test_fwinfo_verify_common(smbios_hdl_t *hdl, smbios_struct_t *sp,
    const smbios_fwinfo_t *fw)
{
        boolean_t ret = B_TRUE;
        smbios_info_t info;

        if (strcmp(fw->smbfw_name, smbios_fwinfo_name) != 0) {
                warnx("firmware inventory name mismatch, found %s",
                    fw->smbfw_name);
                ret = B_FALSE;
        }

        if (strcmp(fw->smbfw_id, smbios_fwinfo_id) != 0) {
                warnx("firmware inventory id mismatch, found %s",
                    fw->smbfw_id);
                ret = B_FALSE;
        }

        if (strcmp(fw->smbfw_reldate, smbios_fwinfo_reldate) != 0) {
                warnx("firmware inventory release date mismatch, found %s",
                    fw->smbfw_reldate);
                ret = B_FALSE;
        }

        if (strcmp(fw->smbfw_lsv, smbios_fwinfo_lsv) != 0) {
                warnx("firmware inventory lsv mismatch, found %s",
                    fw->smbfw_lsv);
                ret = B_FALSE;
        }

        if (fw->smbfw_imgsz != smbios_fwinfo_size) {
                warnx("firmware inventory size mismatch, found 0x%" PRIx64,
                    fw->smbfw_imgsz);
                ret = B_FALSE;
        }

        if (fw->smbfw_chars != SMB_FWC_WP) {
                warnx("firmware inventory chars mismatch, found 0x%x",
                    fw->smbfw_chars);
                ret = B_FALSE;
        }

        if (fw->smbfw_state != SMB_FWS_UA_OFFLINE) {
                warnx("firmware inventory state mismatch, found 0x%x",
                    fw->smbfw_state);
                ret = B_FALSE;
        }

        if (fw->smbfw_vers_fmt != smbios_fwinfo_versid) {
                warnx("firmware inventory version format mismatch, found 0x%x",
                    fw->smbfw_vers_fmt);
                ret = B_FALSE;
        }

        if (fw->smbfw_id_fmt != smbios_fwinfo_versid) {
                warnx("firmware inventory ID format mismatch, found 0x%x",
                    fw->smbfw_id_fmt);
                ret = B_FALSE;
        }

        if (smbios_info_common(hdl, sp->smbstr_id, &info) == -1) {
                warnx("failed to get firmware inventory common items: %s",
                    smbios_errmsg(smbios_errno(hdl)));
                return (B_FALSE);
        }

        if (strcmp(info.smbi_manufacturer, smbios_fwinfo_mfg) != 0) {
                warnx("firmware inventory manufacturer mismatch, found %s",
                    info.smbi_manufacturer);
                ret = B_FALSE;
        }

        if (strcmp(info.smbi_version, smbios_fwinfo_vers) != 0) {
                warnx("firmware inventory version mismatch, found %s",
                    info.smbi_version);
                ret = B_FALSE;
        }

        return (ret);
}

boolean_t
smbios_test_fwinfo_verify_nocomps(smbios_hdl_t *hdl)
{
        smbios_struct_t sp;
        smbios_fwinfo_t fw;
        uint_t ncomps;
        smbios_fwinfo_comp_t *comps;
        boolean_t ret = B_TRUE;

        if (smbios_lookup_type(hdl, SMB_TYPE_FWINFO, &sp) == -1) {
                warnx("failed to lookup SMBIOS fwinfo: %s",
                    smbios_errmsg(smbios_errno(hdl)));
                return (B_FALSE);
        }

        if (smbios_info_fwinfo(hdl, sp.smbstr_id, &fw) == -1) {
                warnx("failed to get firmware inventory: %s",
                    smbios_errmsg(smbios_errno(hdl)));
                return (B_FALSE);
        }

        if (!smbios_test_fwinfo_verify_common(hdl, &sp, &fw)) {
                ret = B_FALSE;
        }

        if (fw.smbfw_ncomps != 0) {
                warnx("firmware inventory ncomps mismatch, found 0x%x",
                    fw.smbfw_ncomps);
                ret = B_FALSE;
        }

        if (smbios_info_fwinfo_comps(hdl, sp.smbstr_id, &ncomps, &comps) != 0) {
                warnx("failed to get components: %s",
                    smbios_errmsg(smbios_errno(hdl)));
                ret = B_FALSE;
        }

        if (ncomps != 0) {
                warnx("smbios_info_fwinfo_comps() returned wrong number of "
                    "comps: 0x%x", ncomps);
                ret = B_FALSE;
        }

        if (comps != NULL) {
                warnx("smbios_info_fwinfo_comps() gave a non-NULL comps "
                    "pointer: %p", comps);
                ret = B_FALSE;
        }

        smbios_info_fwinfo_comps_free(hdl, ncomps, comps);

        return (ret);
}

boolean_t
smbios_test_fwinfo_verify_invlen_comps(smbios_hdl_t *hdl)
{
        smbios_struct_t sp;
        smbios_fwinfo_t fw;
        uint_t ncomps;
        smbios_fwinfo_comp_t *comps;
        boolean_t ret = B_TRUE;

        if (smbios_lookup_type(hdl, SMB_TYPE_FWINFO, &sp) == -1) {
                warnx("failed to lookup SMBIOS fwinfo: %s",
                    smbios_errmsg(smbios_errno(hdl)));
                return (B_FALSE);
        }

        if (smbios_info_fwinfo(hdl, sp.smbstr_id, &fw) == -1) {
                warnx("failed to get firmware inventory: %s",
                    smbios_errmsg(smbios_errno(hdl)));
                return (B_FALSE);
        }

        if (!smbios_test_fwinfo_verify_common(hdl, &sp, &fw)) {
                ret = B_FALSE;
        }

        if (fw.smbfw_ncomps != smbios_fwinfo_ncomps) {
                warnx("firmware inventory ncomps mismatch, found 0x%x",
                    fw.smbfw_ncomps);
                ret = B_FALSE;
        }

        if (smbios_info_fwinfo_comps(hdl, sp.smbstr_id, &ncomps, &comps) !=
            -1) {
                warnx("accidentally parsed invalid fwinfo components as "
                    "valid");
                return (B_FALSE);
        }

        if (smbios_errno(hdl) != ESMB_SHORT) {
                warnx("encountered wrong error for fwinfo comps, expected: "
                    "0x%x, found: 0x%x", ESMB_SHORT, smbios_errno(hdl));
                return (B_FALSE);
        }

        return (ret);
}



boolean_t
smbios_test_fwinfo_verify_comps(smbios_hdl_t *hdl)
{
        smbios_struct_t sp;
        smbios_fwinfo_t fw;
        uint_t ncomps;
        smbios_fwinfo_comp_t *comps;
        boolean_t ret = B_TRUE;

        if (smbios_lookup_type(hdl, SMB_TYPE_FWINFO, &sp) == -1) {
                warnx("failed to lookup SMBIOS fwinfo: %s",
                    smbios_errmsg(smbios_errno(hdl)));
                return (B_FALSE);
        }

        if (smbios_info_fwinfo(hdl, sp.smbstr_id, &fw) == -1) {
                warnx("failed to get firmware inventory: %s",
                    smbios_errmsg(smbios_errno(hdl)));
                return (B_FALSE);
        }

        if (!smbios_test_fwinfo_verify_common(hdl, &sp, &fw)) {
                ret = B_FALSE;
        }

        if (fw.smbfw_ncomps != smbios_fwinfo_ncomps) {
                warnx("firmware inventory ncomps mismatch, found 0x%x",
                    fw.smbfw_ncomps);
                ret = B_FALSE;
        }

        if (smbios_info_fwinfo_comps(hdl, sp.smbstr_id, &ncomps, &comps) != 0) {
                warnx("failed to get components: %s",
                    smbios_errmsg(smbios_errno(hdl)));
                ret = B_FALSE;
        }

        if (ncomps != smbios_fwinfo_ncomps) {
                warnx("smbios_info_fwinfo_comps() returned wrong number of "
                    "comps: 0x%x", ncomps);
                ret = B_FALSE;
        }

        if (comps == NULL) {
                warnx("smbios_info_fwinfo_comps() gave a NULL comps pointer");
                ret = B_FALSE;
        } else {
                for (uint_t i = 0; i < smbios_fwinfo_ncomps; i++) {
                        if (comps[i].smbfwe_id != 0x2300 + i) {
                                warnx("component id %u is wrong: 0x%" _PRIxID,
                                    i, comps[i].smbfwe_id);
                                ret = B_FALSE;
                        }
                }
        }

        smbios_info_fwinfo_comps_free(hdl, ncomps, comps);

        return (ret);
}