2021-10-20 08:30:31 +00:00
|
|
|
|
/*
|
|
|
|
|
* Copyright (C) 2021 Canonical Ltd.
|
|
|
|
|
*
|
|
|
|
|
* This library is free software; you can redistribute it and/or
|
|
|
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
|
|
|
* License as published by the Free Software Foundation; either
|
|
|
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
|
|
|
*
|
|
|
|
|
* This library is distributed in the hope that it will be useful,
|
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
|
* Lesser General Public License for more details.
|
|
|
|
|
*
|
|
|
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
|
|
|
* License along with this library; If not, see
|
|
|
|
|
* <http://www.gnu.org/licenses/>.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include <config.h>
|
|
|
|
|
|
|
|
|
|
#include "internal.h"
|
|
|
|
|
#include "testutils.h"
|
|
|
|
|
#include "virpcivpd.h"
|
|
|
|
|
|
|
|
|
|
#define LIBVIRT_VIRPCIVPDPRIV_H_ALLOW
|
|
|
|
|
|
|
|
|
|
#include "virpcivpdpriv.h"
|
|
|
|
|
#include "virlog.h"
|
|
|
|
|
|
|
|
|
|
#define VIR_FROM_THIS VIR_FROM_NONE
|
|
|
|
|
|
|
|
|
|
#ifdef __linux__
|
|
|
|
|
|
|
|
|
|
VIR_LOG_INIT("tests.vpdtest");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
typedef struct _TestPCIVPDKeywordValue {
|
|
|
|
|
const char *keyword;
|
|
|
|
|
const char *value;
|
|
|
|
|
char **actual;
|
|
|
|
|
} TestPCIVPDKeywordValue;
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
testPCIVPDResourceBasic(const void *data G_GNUC_UNUSED)
|
|
|
|
|
{
|
|
|
|
|
size_t i = 0;
|
|
|
|
|
g_autoptr(virPCIVPDResourceRO) ro = virPCIVPDResourceRONew();
|
|
|
|
|
g_autoptr(virPCIVPDResourceRW) rw = virPCIVPDResourceRWNew();
|
|
|
|
|
/* Note: when the same keyword is updated multiple times the
|
|
|
|
|
* virPCIVPDResourceUpdateKeyword function is expected to free the
|
|
|
|
|
* previous value whether it is a fixed keyword or a custom one.
|
|
|
|
|
* */
|
|
|
|
|
const TestPCIVPDKeywordValue readOnlyCases[] = {
|
|
|
|
|
{.keyword = "EC", .value = "level1", .actual = &ro->change_level},
|
|
|
|
|
{.keyword = "EC", .value = "level2", .actual = &ro->change_level},
|
|
|
|
|
{.keyword = "change_level", .value = "level3", .actual = &ro->change_level},
|
|
|
|
|
{.keyword = "PN", .value = "number1", .actual = &ro->part_number},
|
|
|
|
|
{.keyword = "PN", .value = "number2", .actual = &ro->part_number},
|
|
|
|
|
{.keyword = "part_number", .value = "number3", .actual = &ro->part_number},
|
|
|
|
|
{.keyword = "MN", .value = "id1", .actual = &ro->manufacture_id},
|
|
|
|
|
{.keyword = "MN", .value = "id2", .actual = &ro->manufacture_id},
|
|
|
|
|
{.keyword = "manufacture_id", .value = "id3", &ro->manufacture_id},
|
|
|
|
|
{.keyword = "SN", .value = "serial1", .actual = &ro->serial_number},
|
|
|
|
|
{.keyword = "SN", .value = "serial2", .actual = &ro->serial_number},
|
|
|
|
|
{.keyword = "serial_number", .value = "serial3", .actual = &ro->serial_number},
|
|
|
|
|
};
|
|
|
|
|
const TestPCIVPDKeywordValue unsupportedFieldCases[] = {
|
|
|
|
|
{.keyword = "FG", .value = "42", .actual = NULL},
|
|
|
|
|
{.keyword = "LC", .value = "42", .actual = NULL},
|
|
|
|
|
{.keyword = "PG", .value = "42", .actual = NULL},
|
|
|
|
|
{.keyword = "CP", .value = "42", .actual = NULL},
|
|
|
|
|
{.keyword = "EX", .value = "42", .actual = NULL},
|
|
|
|
|
};
|
2021-11-02 16:32:43 +00:00
|
|
|
|
size_t numROCases = G_N_ELEMENTS(readOnlyCases);
|
|
|
|
|
size_t numUnsupportedCases = G_N_ELEMENTS(unsupportedFieldCases);
|
2021-10-20 08:30:31 +00:00
|
|
|
|
g_autoptr(virPCIVPDResource) res = g_new0(virPCIVPDResource, 1);
|
|
|
|
|
virPCIVPDResourceCustom *custom = NULL;
|
|
|
|
|
|
|
|
|
|
g_autofree char *val = g_strdup("testval");
|
|
|
|
|
res->name = g_steal_pointer(&val);
|
|
|
|
|
|
|
|
|
|
/* Initialize RO */
|
|
|
|
|
res->ro = g_steal_pointer(&ro);
|
|
|
|
|
|
|
|
|
|
/* Update keywords one by one and compare actual values with the expected ones. */
|
|
|
|
|
for (i = 0; i < numROCases; ++i) {
|
2024-01-24 16:15:10 +00:00
|
|
|
|
virPCIVPDResourceUpdateKeyword(res, true,
|
|
|
|
|
readOnlyCases[i].keyword,
|
|
|
|
|
readOnlyCases[i].value);
|
2021-10-20 08:30:31 +00:00
|
|
|
|
if (STRNEQ(readOnlyCases[i].value, *readOnlyCases[i].actual))
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Do a basic vendor field check. */
|
2024-01-24 16:15:10 +00:00
|
|
|
|
virPCIVPDResourceUpdateKeyword(res, true, "V0", "vendor0");
|
2021-10-20 08:30:31 +00:00
|
|
|
|
|
|
|
|
|
if (res->ro->vendor_specific->len != 1)
|
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
custom = g_ptr_array_index(res->ro->vendor_specific, 0);
|
|
|
|
|
if (custom->idx != '0' || STRNEQ(custom->value, "vendor0"))
|
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
/* Make sure unsupported RO keyword updates are not fatal. */
|
|
|
|
|
for (i = 0; i < numUnsupportedCases; ++i) {
|
2024-01-24 16:15:10 +00:00
|
|
|
|
virPCIVPDResourceUpdateKeyword(res, true,
|
|
|
|
|
unsupportedFieldCases[i].keyword,
|
|
|
|
|
unsupportedFieldCases[i].value);
|
2021-10-20 08:30:31 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Initialize RW */
|
|
|
|
|
res->rw = g_steal_pointer(&rw);
|
2024-01-24 16:15:10 +00:00
|
|
|
|
virPCIVPDResourceUpdateKeyword(res, false, "YA", "tag1");
|
|
|
|
|
if (STRNEQ(res->rw->asset_tag, "tag1"))
|
2021-10-20 08:30:31 +00:00
|
|
|
|
return -1;
|
|
|
|
|
|
2024-01-24 16:15:10 +00:00
|
|
|
|
virPCIVPDResourceUpdateKeyword(res, false, "asset_tag", "tag2");
|
|
|
|
|
if (STRNEQ(res->rw->asset_tag, "tag2"))
|
2021-10-20 08:30:31 +00:00
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
/* Do a basic system field check. */
|
2024-01-24 16:15:10 +00:00
|
|
|
|
virPCIVPDResourceUpdateKeyword(res, false, "Y0", "system0");
|
2021-10-20 08:30:31 +00:00
|
|
|
|
|
|
|
|
|
if (res->rw->system_specific->len != 1)
|
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
custom = g_ptr_array_index(res->rw->system_specific, 0);
|
|
|
|
|
if (custom->idx != '0' || STRNEQ(custom->value, "system0"))
|
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
/* Make sure unsupported RW keyword updates are not fatal. */
|
|
|
|
|
for (i = 0; i < numUnsupportedCases; ++i) {
|
2024-01-24 16:15:10 +00:00
|
|
|
|
/* This test is deliberately left in despite
|
|
|
|
|
* virPCIVPDResourceUpdateKeyword always succeeding to prevent
|
|
|
|
|
* possible regressions if the function is ever rewritten */
|
|
|
|
|
virPCIVPDResourceUpdateKeyword(res, false,
|
|
|
|
|
unsupportedFieldCases[i].keyword,
|
|
|
|
|
unsupportedFieldCases[i].value);
|
2021-10-20 08:30:31 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Just make sure the name has not been changed during keyword updates. */
|
|
|
|
|
if (!STREQ_NULLABLE(res->name, "testval"))
|
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
testPCIVPDResourceCustomCompareIndex(const void *data G_GNUC_UNUSED)
|
|
|
|
|
{
|
2021-11-02 16:26:48 +00:00
|
|
|
|
g_autoptr(virPCIVPDResourceCustom) a = NULL;
|
|
|
|
|
g_autoptr(virPCIVPDResourceCustom) b = NULL;
|
2021-10-20 08:30:31 +00:00
|
|
|
|
|
|
|
|
|
/* Both are NULL */
|
|
|
|
|
if (!virPCIVPDResourceCustomCompareIndex(a, b))
|
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
/* a is not NULL */
|
|
|
|
|
a = g_new0(virPCIVPDResourceCustom, 1);
|
|
|
|
|
if (virPCIVPDResourceCustomCompareIndex(a, b))
|
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
/* Reverse */
|
|
|
|
|
if (virPCIVPDResourceCustomCompareIndex(b, a))
|
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
/* Same index, different strings */
|
|
|
|
|
b = g_new0(virPCIVPDResourceCustom, 1);
|
|
|
|
|
a->idx = 'z';
|
|
|
|
|
a->value = g_strdup("42");
|
|
|
|
|
b->idx = 'z';
|
|
|
|
|
b->value = g_strdup("24");
|
|
|
|
|
if (!virPCIVPDResourceCustomCompareIndex(b, a))
|
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
/* Different index, different strings */
|
|
|
|
|
a->idx = 'a';
|
|
|
|
|
if (virPCIVPDResourceCustomCompareIndex(b, a))
|
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
virPCIVPDResourceCustomFree(a);
|
|
|
|
|
virPCIVPDResourceCustomFree(b);
|
|
|
|
|
a = g_new0(virPCIVPDResourceCustom, 1);
|
|
|
|
|
b = g_new0(virPCIVPDResourceCustom, 1);
|
|
|
|
|
|
|
|
|
|
/* Same index, same strings */
|
|
|
|
|
a->idx = 'z';
|
|
|
|
|
a->value = g_strdup("42");
|
|
|
|
|
b->idx = 'z';
|
|
|
|
|
b->value = g_strdup("42");
|
|
|
|
|
if (!virPCIVPDResourceCustomCompareIndex(b, a))
|
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
/* Different index, same strings */
|
|
|
|
|
a->idx = 'a';
|
|
|
|
|
if (virPCIVPDResourceCustomCompareIndex(b, a))
|
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
/* Different index, same value pointers */
|
2022-01-28 17:42:45 +00:00
|
|
|
|
g_clear_pointer(&b->value, g_free);
|
2021-10-20 08:30:31 +00:00
|
|
|
|
b->value = a->value;
|
2022-01-28 19:37:42 +00:00
|
|
|
|
if (virPCIVPDResourceCustomCompareIndex(b, a)) {
|
|
|
|
|
b->value = NULL;
|
2021-10-20 08:30:31 +00:00
|
|
|
|
return -1;
|
2022-01-28 19:37:42 +00:00
|
|
|
|
}
|
2021-10-20 08:30:31 +00:00
|
|
|
|
|
|
|
|
|
b->value = NULL;
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
testPCIVPDResourceCustomUpsertValue(const void *data G_GNUC_UNUSED)
|
|
|
|
|
{
|
|
|
|
|
g_autoptr(GPtrArray) arr = g_ptr_array_new_full(0, (GDestroyNotify)virPCIVPDResourceCustomFree);
|
|
|
|
|
virPCIVPDResourceCustom *custom = NULL;
|
2024-01-24 15:11:24 +00:00
|
|
|
|
virPCIVPDResourceCustomUpsertValue(arr, 'A', "testval");
|
2021-10-20 08:30:31 +00:00
|
|
|
|
|
|
|
|
|
if (arr->len != 1)
|
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
custom = g_ptr_array_index(arr, 0);
|
|
|
|
|
if (custom == NULL || custom->idx != 'A' || STRNEQ_NULLABLE(custom->value, "testval"))
|
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
/* Idempotency */
|
2024-01-24 15:11:24 +00:00
|
|
|
|
virPCIVPDResourceCustomUpsertValue(arr, 'A', "testval");
|
2021-10-20 08:30:31 +00:00
|
|
|
|
|
|
|
|
|
if (arr->len != 1)
|
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
custom = g_ptr_array_index(arr, 0);
|
|
|
|
|
if (custom == NULL || custom->idx != 'A' || STRNEQ_NULLABLE(custom->value, "testval"))
|
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
/* Existing value updates. */
|
2024-01-24 15:11:24 +00:00
|
|
|
|
virPCIVPDResourceCustomUpsertValue(arr, 'A', "testvalnew");
|
2021-10-20 08:30:31 +00:00
|
|
|
|
|
|
|
|
|
if (arr->len != 1)
|
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
custom = g_ptr_array_index(arr, 0);
|
|
|
|
|
if (custom == NULL || custom->idx != 'A' || STRNEQ_NULLABLE(custom->value, "testvalnew"))
|
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
/* Inserting multiple values */
|
2024-01-24 15:11:24 +00:00
|
|
|
|
virPCIVPDResourceCustomUpsertValue(arr, '1', "42");
|
2021-10-20 08:30:31 +00:00
|
|
|
|
|
|
|
|
|
if (arr->len != 2)
|
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
custom = g_ptr_array_index(arr, 1);
|
|
|
|
|
if (custom == NULL || custom->idx != '1' || STRNEQ_NULLABLE(custom->value, "42"))
|
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
typedef struct _TestPCIVPDExpectedString {
|
|
|
|
|
const char *keyword;
|
|
|
|
|
bool expected;
|
|
|
|
|
} TestPCIVPDExpectedString;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* testPCIVPDIsValidTextValue:
|
|
|
|
|
*
|
|
|
|
|
* Test expected text value validation. Static metadata about possible values is taken
|
|
|
|
|
* from the PCI(e) standards and based on some real-world hardware examples.
|
|
|
|
|
* */
|
|
|
|
|
static int
|
|
|
|
|
testPCIVPDIsValidTextValue(const void *data G_GNUC_UNUSED)
|
|
|
|
|
{
|
|
|
|
|
size_t i = 0;
|
|
|
|
|
|
|
|
|
|
const TestPCIVPDExpectedString textValueCases[] = {
|
|
|
|
|
/* Numbers */
|
|
|
|
|
{"42", true},
|
|
|
|
|
/* Alphanumeric */
|
|
|
|
|
{"DCM1001008FC52101008FC53201008FC54301008FC5", true},
|
|
|
|
|
/* Dots */
|
|
|
|
|
{"DSV1028VPDR.VER1.0", true},
|
|
|
|
|
/* Whitespace presence */
|
|
|
|
|
{"NMVIntel Corp", true},
|
|
|
|
|
/* Comma and spaces */
|
|
|
|
|
{"BlueField-2 DPU 25GbE Dual-Port SFP56, Tall Bracket", true},
|
|
|
|
|
/* Equal signs and colons. */
|
|
|
|
|
{"MLX:MN=MLNX:CSKU=V2:UUID=V3:PCI=V0:MODL=BF2H332A", true},
|
|
|
|
|
/* Dashes */
|
|
|
|
|
{"MBF2H332A-AEEOT", true},
|
|
|
|
|
{"under_score_example", true},
|
|
|
|
|
{"", true},
|
|
|
|
|
{";", true},
|
2021-10-29 18:57:17 +00:00
|
|
|
|
{"\\42", true},
|
|
|
|
|
{"N/A", true},
|
|
|
|
|
/* The first and last code points are outside ASCII (multi-byte in UTF-8). */
|
|
|
|
|
{"гbl🐧", false},
|
2021-10-20 08:30:31 +00:00
|
|
|
|
};
|
2021-11-02 16:32:43 +00:00
|
|
|
|
for (i = 0; i < G_N_ELEMENTS(textValueCases); ++i) {
|
2021-10-20 08:30:31 +00:00
|
|
|
|
if (virPCIVPDResourceIsValidTextValue(textValueCases[i].keyword) !=
|
|
|
|
|
textValueCases[i].expected)
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* testPCIVPDGetFieldValueFormat:
|
|
|
|
|
*
|
|
|
|
|
* A simple test to assess the functionality of the
|
|
|
|
|
* virPCIVPDResourceGetFieldValueFormat function.
|
|
|
|
|
* */
|
|
|
|
|
static int
|
|
|
|
|
testPCIVPDGetFieldValueFormat(const void *data G_GNUC_UNUSED)
|
|
|
|
|
{
|
|
|
|
|
typedef struct _TestPCIVPDExpectedFieldValueFormat {
|
|
|
|
|
const char *keyword;
|
|
|
|
|
virPCIVPDResourceFieldValueFormat expected;
|
|
|
|
|
} TestPCIVPDExpectedFieldValueFormat;
|
|
|
|
|
|
|
|
|
|
size_t i = 0;
|
|
|
|
|
|
|
|
|
|
const TestPCIVPDExpectedFieldValueFormat valueFormatCases[] = {
|
|
|
|
|
{"SN", VIR_PCI_VPD_RESOURCE_FIELD_VALUE_FORMAT_TEXT},
|
|
|
|
|
{"EC", VIR_PCI_VPD_RESOURCE_FIELD_VALUE_FORMAT_TEXT},
|
|
|
|
|
{"MN", VIR_PCI_VPD_RESOURCE_FIELD_VALUE_FORMAT_TEXT},
|
|
|
|
|
{"PN", VIR_PCI_VPD_RESOURCE_FIELD_VALUE_FORMAT_TEXT},
|
|
|
|
|
{"RV", VIR_PCI_VPD_RESOURCE_FIELD_VALUE_FORMAT_RESVD},
|
|
|
|
|
{"RW", VIR_PCI_VPD_RESOURCE_FIELD_VALUE_FORMAT_RDWR},
|
|
|
|
|
{"VA", VIR_PCI_VPD_RESOURCE_FIELD_VALUE_FORMAT_TEXT},
|
|
|
|
|
{"YA", VIR_PCI_VPD_RESOURCE_FIELD_VALUE_FORMAT_TEXT},
|
|
|
|
|
{"YZ", VIR_PCI_VPD_RESOURCE_FIELD_VALUE_FORMAT_TEXT},
|
|
|
|
|
{"CP", VIR_PCI_VPD_RESOURCE_FIELD_VALUE_FORMAT_BINARY},
|
|
|
|
|
/* Invalid keywords. */
|
|
|
|
|
{"", VIR_PCI_VPD_RESOURCE_FIELD_VALUE_FORMAT_LAST},
|
|
|
|
|
{"sn", VIR_PCI_VPD_RESOURCE_FIELD_VALUE_FORMAT_LAST},
|
|
|
|
|
{"ec", VIR_PCI_VPD_RESOURCE_FIELD_VALUE_FORMAT_LAST},
|
|
|
|
|
{"mn", VIR_PCI_VPD_RESOURCE_FIELD_VALUE_FORMAT_LAST},
|
|
|
|
|
{"pn", VIR_PCI_VPD_RESOURCE_FIELD_VALUE_FORMAT_LAST},
|
|
|
|
|
{"4", VIR_PCI_VPD_RESOURCE_FIELD_VALUE_FORMAT_LAST},
|
|
|
|
|
{"42", VIR_PCI_VPD_RESOURCE_FIELD_VALUE_FORMAT_LAST},
|
|
|
|
|
{"Y", VIR_PCI_VPD_RESOURCE_FIELD_VALUE_FORMAT_LAST},
|
|
|
|
|
{"V", VIR_PCI_VPD_RESOURCE_FIELD_VALUE_FORMAT_LAST},
|
|
|
|
|
{"v", VIR_PCI_VPD_RESOURCE_FIELD_VALUE_FORMAT_LAST},
|
|
|
|
|
{"vA", VIR_PCI_VPD_RESOURCE_FIELD_VALUE_FORMAT_LAST},
|
|
|
|
|
{"va", VIR_PCI_VPD_RESOURCE_FIELD_VALUE_FORMAT_LAST},
|
|
|
|
|
{"ya", VIR_PCI_VPD_RESOURCE_FIELD_VALUE_FORMAT_LAST},
|
|
|
|
|
{"Ya", VIR_PCI_VPD_RESOURCE_FIELD_VALUE_FORMAT_LAST},
|
|
|
|
|
/* 2 bytes but not present in the spec. */
|
|
|
|
|
{"EX", VIR_PCI_VPD_RESOURCE_FIELD_VALUE_FORMAT_LAST},
|
|
|
|
|
/* Many numeric bytes. */
|
|
|
|
|
{"4242", VIR_PCI_VPD_RESOURCE_FIELD_VALUE_FORMAT_LAST},
|
|
|
|
|
/* Many letters. */
|
|
|
|
|
{"EXAMPLE", VIR_PCI_VPD_RESOURCE_FIELD_VALUE_FORMAT_LAST},
|
|
|
|
|
};
|
2021-11-02 16:32:43 +00:00
|
|
|
|
for (i = 0; i < G_N_ELEMENTS(valueFormatCases); ++i) {
|
2021-10-20 08:30:31 +00:00
|
|
|
|
if (virPCIVPDResourceGetFieldValueFormat(valueFormatCases[i].keyword) !=
|
|
|
|
|
valueFormatCases[i].expected)
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# define VPD_STRING_RESOURCE_EXAMPLE_HEADER \
|
|
|
|
|
PCI_VPD_LARGE_RESOURCE_FLAG | PCI_VPD_STRING_RESOURCE_FLAG, 0x08, 0x00
|
|
|
|
|
|
|
|
|
|
# define VPD_STRING_RESOURCE_EXAMPLE_DATA \
|
|
|
|
|
't', 'e', 's', 't', 'n', 'a', 'm', 'e'
|
|
|
|
|
|
|
|
|
|
# define VPD_R_FIELDS_EXAMPLE_HEADER \
|
|
|
|
|
PCI_VPD_LARGE_RESOURCE_FLAG | PCI_VPD_READ_ONLY_LARGE_RESOURCE_FLAG, 0x16, 0x00
|
|
|
|
|
|
|
|
|
|
# define VPD_R_EXAMPLE_VALID_RV_FIELD \
|
|
|
|
|
'R', 'V', 0x02, 0x31, 0x00
|
|
|
|
|
|
|
|
|
|
# define VPD_R_EXAMPLE_INVALID_RV_FIELD \
|
|
|
|
|
'R', 'V', 0x02, 0xFF, 0x00
|
|
|
|
|
|
|
|
|
|
# define VPD_R_EXAMPLE_FIELDS \
|
|
|
|
|
'P', 'N', 0x02, '4', '2', \
|
|
|
|
|
'E', 'C', 0x04, '4', '2', '4', '2', \
|
|
|
|
|
'V', 'A', 0x02, 'E', 'X'
|
|
|
|
|
|
|
|
|
|
# define VPD_R_FIELDS_EXAMPLE_DATA \
|
|
|
|
|
VPD_R_EXAMPLE_FIELDS, \
|
|
|
|
|
VPD_R_EXAMPLE_VALID_RV_FIELD
|
|
|
|
|
|
|
|
|
|
# define VPD_W_FIELDS_EXAMPLE_HEADER \
|
|
|
|
|
PCI_VPD_LARGE_RESOURCE_FLAG | PCI_VPD_READ_WRITE_LARGE_RESOURCE_FLAG, 0x19, 0x00
|
|
|
|
|
|
|
|
|
|
# define VPD_W_EXAMPLE_FIELDS \
|
|
|
|
|
'V', 'Z', 0x02, '4', '2', \
|
2024-01-23 15:40:34 +00:00
|
|
|
|
'Y', 'A', 0x04, '!', '<', '>', ':', \
|
2021-10-20 08:30:31 +00:00
|
|
|
|
'Y', 'F', 0x02, 'E', 'X', \
|
|
|
|
|
'Y', 'E', 0x00, \
|
|
|
|
|
'R', 'W', 0x02, 0x00, 0x00
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
testVirPCIVPDValidateExampleReadOnlyFields(virPCIVPDResource *res)
|
|
|
|
|
{
|
|
|
|
|
const char *expectedName = "testname";
|
|
|
|
|
virPCIVPDResourceCustom *custom = NULL;
|
|
|
|
|
if (STRNEQ(res->name, expectedName)) {
|
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
|
"Unexpected string resource value: %s, expected: %s",
|
|
|
|
|
res->name, expectedName);
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!res->ro) {
|
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
|
"Read-only keywords are missing from the VPD resource.");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (STRNEQ_NULLABLE(res->ro->part_number, "42")) {
|
|
|
|
|
return -1;
|
|
|
|
|
} else if (STRNEQ_NULLABLE(res->ro->change_level, "4242")) {
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
if (!res->ro->vendor_specific)
|
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
custom = g_ptr_array_index(res->ro->vendor_specific, 0);
|
|
|
|
|
if (custom->idx != 'A' || STRNEQ_NULLABLE(custom->value, "EX"))
|
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
testVirPCIVPDParseFullVPD(const void *opaque G_GNUC_UNUSED)
|
|
|
|
|
{
|
2022-02-10 10:32:00 +00:00
|
|
|
|
VIR_AUTOCLOSE fd = -1;
|
2021-10-20 08:30:31 +00:00
|
|
|
|
size_t dataLen = 0;
|
|
|
|
|
|
|
|
|
|
g_autoptr(virPCIVPDResource) res = NULL;
|
|
|
|
|
/* Note: Custom fields are supposed to be freed by the resource cleanup code. */
|
|
|
|
|
virPCIVPDResourceCustom *custom = NULL;
|
|
|
|
|
|
|
|
|
|
const uint8_t fullVPDExample[] = {
|
|
|
|
|
VPD_STRING_RESOURCE_EXAMPLE_HEADER, VPD_STRING_RESOURCE_EXAMPLE_DATA,
|
|
|
|
|
VPD_R_FIELDS_EXAMPLE_HEADER, VPD_R_FIELDS_EXAMPLE_DATA,
|
|
|
|
|
VPD_W_FIELDS_EXAMPLE_HEADER, VPD_W_EXAMPLE_FIELDS,
|
|
|
|
|
PCI_VPD_RESOURCE_END_VAL
|
|
|
|
|
};
|
|
|
|
|
|
2021-11-02 16:32:43 +00:00
|
|
|
|
dataLen = G_N_ELEMENTS(fullVPDExample);
|
2021-11-23 14:05:43 +00:00
|
|
|
|
if ((fd = virCreateAnonymousFile(fullVPDExample, dataLen)) < 0)
|
|
|
|
|
return -1;
|
|
|
|
|
|
2021-10-20 08:30:31 +00:00
|
|
|
|
res = virPCIVPDParse(fd);
|
|
|
|
|
|
|
|
|
|
if (!res) {
|
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
|
"The resource pointer is NULL after parsing which is unexpected");
|
2021-10-29 18:57:18 +00:00
|
|
|
|
return -1;
|
2021-10-20 08:30:31 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!res->ro) {
|
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
|
"Read-only keywords are missing from the VPD resource.");
|
|
|
|
|
return -1;
|
|
|
|
|
} else if (!res->rw) {
|
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
|
"Read-write keywords are missing from the VPD resource.");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (testVirPCIVPDValidateExampleReadOnlyFields(res))
|
|
|
|
|
return -1;
|
|
|
|
|
|
2024-01-23 15:40:34 +00:00
|
|
|
|
if (STRNEQ_NULLABLE(res->rw->asset_tag, "!<>:"))
|
2021-10-20 08:30:31 +00:00
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
if (!res->rw->vendor_specific)
|
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
custom = g_ptr_array_index(res->rw->vendor_specific, 0);
|
|
|
|
|
if (custom->idx != 'Z' || STRNEQ_NULLABLE(custom->value, "42"))
|
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
if (!res->rw->system_specific)
|
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
custom = g_ptr_array_index(res->rw->system_specific, 0);
|
|
|
|
|
if (custom->idx != 'F' || STRNEQ_NULLABLE(custom->value, "EX"))
|
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
custom = g_ptr_array_index(res->rw->system_specific, 1);
|
|
|
|
|
if (custom->idx != 'E' || STRNEQ_NULLABLE(custom->value, ""))
|
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
custom = NULL;
|
2021-10-29 18:57:18 +00:00
|
|
|
|
return 0;
|
2021-10-20 08:30:31 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-10-29 18:57:16 +00:00
|
|
|
|
static int
|
|
|
|
|
testVirPCIVPDParseZeroLengthRW(const void *opaque G_GNUC_UNUSED)
|
|
|
|
|
{
|
2022-02-10 10:32:00 +00:00
|
|
|
|
VIR_AUTOCLOSE fd = -1;
|
2021-10-29 18:57:16 +00:00
|
|
|
|
size_t dataLen = 0;
|
|
|
|
|
|
|
|
|
|
g_autoptr(virPCIVPDResource) res = NULL;
|
|
|
|
|
virPCIVPDResourceCustom *custom = NULL;
|
|
|
|
|
|
|
|
|
|
/* The RW field has a zero length which means there is no more RW space left. */
|
|
|
|
|
const uint8_t fullVPDExample[] = {
|
|
|
|
|
VPD_STRING_RESOURCE_EXAMPLE_HEADER, VPD_STRING_RESOURCE_EXAMPLE_DATA,
|
|
|
|
|
VPD_R_FIELDS_EXAMPLE_HEADER, VPD_R_FIELDS_EXAMPLE_DATA,
|
|
|
|
|
PCI_VPD_LARGE_RESOURCE_FLAG | PCI_VPD_READ_WRITE_LARGE_RESOURCE_FLAG, 0x08, 0x00,
|
|
|
|
|
'V', 'Z', 0x02, '4', '2',
|
|
|
|
|
'R', 'W', 0x00,
|
|
|
|
|
PCI_VPD_RESOURCE_END_VAL
|
|
|
|
|
};
|
|
|
|
|
|
2021-11-02 16:32:43 +00:00
|
|
|
|
dataLen = G_N_ELEMENTS(fullVPDExample);
|
2021-11-23 14:05:43 +00:00
|
|
|
|
if ((fd = virCreateAnonymousFile(fullVPDExample, dataLen)) < 0)
|
|
|
|
|
return -1;
|
|
|
|
|
|
2021-10-29 18:57:16 +00:00
|
|
|
|
res = virPCIVPDParse(fd);
|
|
|
|
|
|
|
|
|
|
if (!res) {
|
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
|
"The resource pointer is NULL after parsing which is unexpected");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!res->ro) {
|
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
|
"Read-only keywords are missing from the VPD resource.");
|
|
|
|
|
return -1;
|
|
|
|
|
} else if (!res->rw) {
|
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
|
"Read-write keywords are missing from the VPD resource.");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (testVirPCIVPDValidateExampleReadOnlyFields(res))
|
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
custom = g_ptr_array_index(res->rw->vendor_specific, 0);
|
|
|
|
|
if (custom->idx != 'Z' || STRNEQ_NULLABLE(custom->value, "42"))
|
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
custom = NULL;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
testVirPCIVPDParseNoRW(const void *opaque G_GNUC_UNUSED)
|
|
|
|
|
{
|
2022-02-10 10:32:00 +00:00
|
|
|
|
VIR_AUTOCLOSE fd = -1;
|
2021-10-29 18:57:16 +00:00
|
|
|
|
size_t dataLen = 0;
|
|
|
|
|
|
|
|
|
|
g_autoptr(virPCIVPDResource) res = NULL;
|
|
|
|
|
virPCIVPDResourceCustom *custom = NULL;
|
|
|
|
|
|
|
|
|
|
/* The RW field has a zero length which means there is no more RW space left. */
|
|
|
|
|
const uint8_t fullVPDExample[] = {
|
|
|
|
|
VPD_STRING_RESOURCE_EXAMPLE_HEADER, VPD_STRING_RESOURCE_EXAMPLE_DATA,
|
|
|
|
|
VPD_R_FIELDS_EXAMPLE_HEADER, VPD_R_FIELDS_EXAMPLE_DATA,
|
|
|
|
|
PCI_VPD_LARGE_RESOURCE_FLAG | PCI_VPD_READ_WRITE_LARGE_RESOURCE_FLAG, 0x05, 0x00,
|
|
|
|
|
'V', 'Z', 0x02, '4', '2',
|
|
|
|
|
PCI_VPD_RESOURCE_END_VAL
|
|
|
|
|
};
|
|
|
|
|
|
2021-11-02 16:32:43 +00:00
|
|
|
|
dataLen = G_N_ELEMENTS(fullVPDExample);
|
2021-11-23 14:05:43 +00:00
|
|
|
|
if ((fd = virCreateAnonymousFile(fullVPDExample, dataLen)) < 0)
|
|
|
|
|
return -1;
|
|
|
|
|
|
2021-10-29 18:57:16 +00:00
|
|
|
|
res = virPCIVPDParse(fd);
|
|
|
|
|
|
|
|
|
|
if (!res) {
|
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
|
"The resource pointer is NULL after parsing which is unexpected");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!res->ro) {
|
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
|
"Read-only keywords are missing from the VPD resource.");
|
|
|
|
|
return -1;
|
|
|
|
|
} else if (!res->rw) {
|
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
|
"Read-write keywords are missing from the VPD resource.");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (testVirPCIVPDValidateExampleReadOnlyFields(res))
|
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
custom = g_ptr_array_index(res->rw->vendor_specific, 0);
|
|
|
|
|
if (custom->idx != 'Z' || STRNEQ_NULLABLE(custom->value, "42"))
|
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
custom = NULL;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2021-10-20 08:30:31 +00:00
|
|
|
|
static int
|
|
|
|
|
testVirPCIVPDParseFullVPDSkipInvalidKeywords(const void *opaque G_GNUC_UNUSED)
|
|
|
|
|
{
|
2022-02-10 10:32:00 +00:00
|
|
|
|
VIR_AUTOCLOSE fd = -1;
|
2021-10-20 08:30:31 +00:00
|
|
|
|
size_t dataLen = 0;
|
|
|
|
|
|
|
|
|
|
g_autoptr(virPCIVPDResource) res = NULL;
|
|
|
|
|
|
|
|
|
|
const uint8_t fullVPDExample[] = {
|
|
|
|
|
VPD_STRING_RESOURCE_EXAMPLE_HEADER,
|
|
|
|
|
VPD_STRING_RESOURCE_EXAMPLE_DATA,
|
|
|
|
|
PCI_VPD_LARGE_RESOURCE_FLAG | PCI_VPD_READ_ONLY_LARGE_RESOURCE_FLAG, 0x25, 0x00,
|
|
|
|
|
VPD_R_EXAMPLE_FIELDS,
|
|
|
|
|
/* The keywords below (except for "RV") are invalid but will be skipped by the parser */
|
|
|
|
|
0x07, 'A', 0x02, 0x00, 0x00,
|
|
|
|
|
'V', 0x07, 0x02, 0x00, 0x00,
|
|
|
|
|
'e', 'x', 0x02, 0x00, 0x00,
|
|
|
|
|
'R', 'V', 0x02, 0x9A, 0x00,
|
|
|
|
|
PCI_VPD_RESOURCE_END_VAL
|
|
|
|
|
};
|
|
|
|
|
|
2021-11-02 16:32:43 +00:00
|
|
|
|
dataLen = G_N_ELEMENTS(fullVPDExample);
|
2021-11-23 14:05:43 +00:00
|
|
|
|
if ((fd = virCreateAnonymousFile(fullVPDExample, dataLen)) < 0)
|
|
|
|
|
return -1;
|
|
|
|
|
|
2021-10-20 08:30:31 +00:00
|
|
|
|
res = virPCIVPDParse(fd);
|
|
|
|
|
|
|
|
|
|
if (!res) {
|
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
|
"The resource pointer is NULL after parsing which is unexpected.");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
if (!res->ro) {
|
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
|
"The RO portion of the VPD resource is NULL.");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (testVirPCIVPDValidateExampleReadOnlyFields(res))
|
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2021-10-29 18:57:17 +00:00
|
|
|
|
static int
|
|
|
|
|
testVirPCIVPDParseFullVPDSkipInvalidValues(const void *opaque G_GNUC_UNUSED)
|
|
|
|
|
{
|
2022-02-10 10:32:00 +00:00
|
|
|
|
VIR_AUTOCLOSE fd = -1;
|
2021-10-29 18:57:17 +00:00
|
|
|
|
size_t dataLen = 0;
|
|
|
|
|
size_t i = 0;
|
|
|
|
|
virPCIVPDResourceCustom *custom = NULL;
|
|
|
|
|
|
|
|
|
|
g_autoptr(virPCIVPDResource) res = NULL;
|
|
|
|
|
|
|
|
|
|
/* This example is based on real-world hardware which was programmed by the vendor with
|
|
|
|
|
* invalid field values in both the RO section and RW section. The RO section contains
|
|
|
|
|
* fields that are not valid per the spec but accepted by Libvirt as printable ASCII
|
|
|
|
|
* characters. The RW field has a 0 length which means there is no more space in the
|
|
|
|
|
* RW section. */
|
|
|
|
|
const uint8_t fullVPDExample[] = {
|
|
|
|
|
0x82, 0x23, 0x00, 0x48, 0x50, 0x20, 0x45, 0x74, 0x68, 0x65, 0x72, 0x6e, 0x65, 0x74,
|
|
|
|
|
0x20, 0x31, 0x47, 0x62, 0x20, 0x32, 0x2d, 0x70, 0x6f, 0x72, 0x74, 0x20, 0x33, 0x36,
|
|
|
|
|
0x31, 0x69, 0x20, 0x41, 0x64, 0x61, 0x70, 0x74, 0x65, 0x72, 0x90, 0x42, 0x00, 0x50,
|
|
|
|
|
0x4e, 0x03, 0x4e, 0x2f, 0x41, 0x45, 0x43, 0x03, 0x4e, 0x2f, 0x41, 0x53, 0x4e, 0x03,
|
|
|
|
|
0x4e, 0x2f, 0x41, 0x56, 0x30, 0x29, 0x34, 0x57, 0x2f, 0x31, 0x57, 0x20, 0x50, 0x43,
|
|
|
|
|
0x49, 0x65, 0x47, 0x32, 0x78, 0x34, 0x20, 0x32, 0x70, 0x20, 0x31, 0x47, 0x62, 0x45,
|
|
|
|
|
0x20, 0x52, 0x4a, 0x34, 0x35, 0x20, 0x49, 0x6e, 0x74, 0x65, 0x6c, 0x20, 0x69, 0x33,
|
|
|
|
|
0x35, 0x30, 0x20, 0x20, 0x20, 0x52, 0x56, 0x01, 0x63, 0x91, 0x47, 0x00, 0x56, 0x31,
|
|
|
|
|
0x06, 0x35, 0x2e, 0x37, 0x2e, 0x30, 0x36, 0x56, 0x33, 0x06, 0x32, 0x2e, 0x38, 0x2e,
|
|
|
|
|
0x32, 0x30, 0x56, 0x36, 0x06, 0x31, 0x2e, 0x35, 0x2e, 0x33, 0x35, 0x59, 0x41, 0x03,
|
|
|
|
|
0x4e, 0x2f, 0x41, 0x59, 0x42, 0x10, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
|
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x59, 0x43, 0x0D, 0xff, 0xff, 0xff,
|
|
|
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 'R', 'W', 0x00, 0x78,
|
|
|
|
|
};
|
|
|
|
|
|
2021-11-02 16:32:43 +00:00
|
|
|
|
dataLen = G_N_ELEMENTS(fullVPDExample);
|
2021-11-23 14:05:43 +00:00
|
|
|
|
if ((fd = virCreateAnonymousFile(fullVPDExample, dataLen)) < 0)
|
|
|
|
|
return -1;
|
|
|
|
|
|
2021-10-29 18:57:17 +00:00
|
|
|
|
res = virPCIVPDParse(fd);
|
|
|
|
|
|
|
|
|
|
if (!res) {
|
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
|
"The resource pointer is NULL after parsing which is unexpected.");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
/* Some values in the read-write section are invalid but parsing should succeed
|
|
|
|
|
* considering the parser is implemented to be graceful about invalid keywords and
|
|
|
|
|
* values. */
|
|
|
|
|
if (!res->ro) {
|
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
|
"The RO section consisting of only invalid fields got parsed successfully");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
if (!res->rw) {
|
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
|
"Could not successfully parse an RW section with some invalid fields");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!STREQ_NULLABLE(res->ro->change_level, "N/A")) {
|
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
|
"Could not parse a change level field with acceptable contents");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
if (!STREQ_NULLABLE(res->ro->part_number, "N/A")) {
|
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
|
"Could not parse a part number field with acceptable contents");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
if (!STREQ_NULLABLE(res->ro->serial_number, "N/A")) {
|
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
|
"Could not parse a serial number with acceptable contents");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
if (!STREQ_NULLABLE(res->rw->asset_tag, "N/A")) {
|
|
|
|
|
/* The asset tag has an invalid value in this case so it should be NULL. */
|
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
|
"Could not parse an asset tag with acceptable contents");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
if (res->rw->vendor_specific->len != 3) {
|
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
|
"The number of parsed vendor fields is not equal to the expected number.");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
if (res->rw->system_specific->len > 0) {
|
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
|
"Successfully parsed some systems-specific fields while none are valid");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
for (i = 0; i < res->rw->vendor_specific->len; ++i) {
|
|
|
|
|
custom = ((virPCIVPDResourceCustom*)g_ptr_array_index(res->rw->vendor_specific, i));
|
|
|
|
|
if (custom->idx == '1') {
|
|
|
|
|
if (STRNEQ(custom->value, "5.7.06")) {
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
} else if (custom->idx == '3') {
|
|
|
|
|
if (STRNEQ(custom->value, "2.8.20")) {
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
} else if (custom->idx == '6') {
|
|
|
|
|
if (STRNEQ(custom->value, "1.5.35")) {
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2021-10-20 08:30:31 +00:00
|
|
|
|
static int
|
|
|
|
|
testVirPCIVPDParseFullVPDInvalid(const void *opaque G_GNUC_UNUSED)
|
|
|
|
|
{
|
|
|
|
|
size_t dataLen = 0;
|
|
|
|
|
|
|
|
|
|
# define VPD_INVALID_ZERO_BYTE \
|
|
|
|
|
0x00
|
|
|
|
|
|
|
|
|
|
# define VPD_INVALID_STRING_HEADER_DATA_LONG \
|
|
|
|
|
PCI_VPD_LARGE_RESOURCE_FLAG | PCI_VPD_STRING_RESOURCE_FLAG, 0x04, 0x00, \
|
|
|
|
|
VPD_STRING_RESOURCE_EXAMPLE_DATA, \
|
|
|
|
|
PCI_VPD_LARGE_RESOURCE_FLAG | PCI_VPD_READ_ONLY_LARGE_RESOURCE_FLAG, 0x05, 0x00, \
|
|
|
|
|
'R', 'V', 0x02, 0xDA, 0x00, \
|
|
|
|
|
PCI_VPD_RESOURCE_END_VAL
|
|
|
|
|
|
|
|
|
|
# define VPD_INVALID_STRING_HEADER_DATA_SHORT \
|
|
|
|
|
PCI_VPD_LARGE_RESOURCE_FLAG | PCI_VPD_STRING_RESOURCE_FLAG, 0x0A, 0x00, \
|
|
|
|
|
VPD_STRING_RESOURCE_EXAMPLE_DATA, \
|
|
|
|
|
PCI_VPD_LARGE_RESOURCE_FLAG | PCI_VPD_READ_ONLY_LARGE_RESOURCE_FLAG, 0x05, 0x00, \
|
|
|
|
|
'R', 'V', 0x02, 0xD4, 0x00, \
|
|
|
|
|
PCI_VPD_RESOURCE_END_VAL
|
|
|
|
|
|
|
|
|
|
# define VPD_NO_VPD_R \
|
|
|
|
|
VPD_STRING_RESOURCE_EXAMPLE_HEADER, \
|
|
|
|
|
VPD_STRING_RESOURCE_EXAMPLE_DATA, \
|
|
|
|
|
PCI_VPD_RESOURCE_END_VAL
|
|
|
|
|
|
|
|
|
|
# define VPD_R_NO_RV \
|
|
|
|
|
VPD_STRING_RESOURCE_EXAMPLE_HEADER, \
|
|
|
|
|
VPD_STRING_RESOURCE_EXAMPLE_DATA, \
|
|
|
|
|
VPD_R_FIELDS_EXAMPLE_HEADER, \
|
|
|
|
|
VPD_R_EXAMPLE_FIELDS, \
|
|
|
|
|
PCI_VPD_RESOURCE_END_VAL
|
|
|
|
|
|
|
|
|
|
# define VPD_R_INVALID_RV \
|
|
|
|
|
VPD_STRING_RESOURCE_EXAMPLE_HEADER, \
|
|
|
|
|
VPD_STRING_RESOURCE_EXAMPLE_DATA, \
|
|
|
|
|
VPD_R_FIELDS_EXAMPLE_HEADER, \
|
|
|
|
|
VPD_R_EXAMPLE_FIELDS, \
|
|
|
|
|
VPD_R_EXAMPLE_INVALID_RV_FIELD, \
|
|
|
|
|
PCI_VPD_RESOURCE_END_VAL
|
|
|
|
|
|
|
|
|
|
# define VPD_R_INVALID_RV_ZERO_LENGTH \
|
|
|
|
|
VPD_STRING_RESOURCE_EXAMPLE_HEADER, \
|
|
|
|
|
VPD_STRING_RESOURCE_EXAMPLE_DATA, \
|
|
|
|
|
PCI_VPD_LARGE_RESOURCE_FLAG | PCI_VPD_READ_ONLY_LARGE_RESOURCE_FLAG, 0x14, 0x00, \
|
|
|
|
|
VPD_R_EXAMPLE_FIELDS, \
|
|
|
|
|
'R', 'V', 0x00, \
|
|
|
|
|
PCI_VPD_RESOURCE_END_VAL
|
|
|
|
|
|
|
|
|
|
/* The RW key is not expected in a VPD-R record. */
|
|
|
|
|
# define VPD_R_UNEXPECTED_RW_IN_VPD_R_KEY \
|
|
|
|
|
VPD_STRING_RESOURCE_EXAMPLE_HEADER, \
|
|
|
|
|
VPD_STRING_RESOURCE_EXAMPLE_DATA, \
|
|
|
|
|
PCI_VPD_LARGE_RESOURCE_FLAG | PCI_VPD_READ_ONLY_LARGE_RESOURCE_FLAG, 0x1B, 0x00, \
|
|
|
|
|
VPD_R_EXAMPLE_FIELDS, \
|
|
|
|
|
'R', 'W', 0x02, 0x00, 0x00, \
|
|
|
|
|
'R', 'V', 0x02, 0x81, 0x00, \
|
|
|
|
|
PCI_VPD_RESOURCE_END_VAL
|
|
|
|
|
|
|
|
|
|
# define VPD_INVALID_STRING_RESOURCE_VALUE \
|
|
|
|
|
VPD_STRING_RESOURCE_EXAMPLE_HEADER, \
|
|
|
|
|
't', 0x03, 's', 't', 'n', 'a', 'm', 'e', \
|
|
|
|
|
PCI_VPD_LARGE_RESOURCE_FLAG | PCI_VPD_READ_ONLY_LARGE_RESOURCE_FLAG, 0x0A, 0x00, \
|
|
|
|
|
'S', 'N', 0x02, 0x04, 0x02, \
|
|
|
|
|
'R', 'V', 0x02, 0x8A, 0x00, \
|
|
|
|
|
PCI_VPD_RESOURCE_END_VAL
|
|
|
|
|
|
2021-10-29 18:57:16 +00:00
|
|
|
|
/* The SN field has a length field that goes past the resource boundaries. */
|
|
|
|
|
# define VPD_INVALID_SN_FIELD_LENGTH \
|
|
|
|
|
VPD_STRING_RESOURCE_EXAMPLE_HEADER, \
|
|
|
|
|
't', 'e', 's', 't', 'n', 'a', 'm', 'e', \
|
|
|
|
|
PCI_VPD_LARGE_RESOURCE_FLAG | PCI_VPD_READ_ONLY_LARGE_RESOURCE_FLAG, 0x0A, 0x00, \
|
|
|
|
|
'S', 'N', 0x42, 0x04, 0x02, \
|
|
|
|
|
'R', 'V', 0x02, 0xE8, 0x00, \
|
|
|
|
|
PCI_VPD_RESOURCE_END_VAL
|
|
|
|
|
|
|
|
|
|
/* The RV field is not the last one in VPD-R while the checksum is valid. */
|
|
|
|
|
# define VPD_INVALID_RV_NOT_LAST \
|
|
|
|
|
VPD_STRING_RESOURCE_EXAMPLE_HEADER, \
|
|
|
|
|
't', 'e', 's', 't', 'n', 'a', 'm', 'e', \
|
|
|
|
|
PCI_VPD_LARGE_RESOURCE_FLAG | PCI_VPD_READ_ONLY_LARGE_RESOURCE_FLAG, 0x0A, 0x00, \
|
|
|
|
|
'R', 'V', 0x02, 0xD1, 0x00, \
|
|
|
|
|
'S', 'N', 0x02, 0x04, 0x02, \
|
|
|
|
|
PCI_VPD_RESOURCE_END_VAL
|
|
|
|
|
|
|
|
|
|
# define VPD_INVALID_RW_NOT_LAST \
|
|
|
|
|
VPD_STRING_RESOURCE_EXAMPLE_HEADER, VPD_STRING_RESOURCE_EXAMPLE_DATA, \
|
|
|
|
|
VPD_R_FIELDS_EXAMPLE_HEADER, VPD_R_FIELDS_EXAMPLE_DATA, \
|
|
|
|
|
PCI_VPD_LARGE_RESOURCE_FLAG | PCI_VPD_READ_WRITE_LARGE_RESOURCE_FLAG, 0x08, 0x00, \
|
|
|
|
|
'R', 'W', 0x00, \
|
|
|
|
|
'V', 'Z', 0x02, '4', '2', \
|
|
|
|
|
PCI_VPD_RESOURCE_END_VAL
|
|
|
|
|
|
|
|
|
|
|
2021-10-20 08:30:31 +00:00
|
|
|
|
# define TEST_INVALID_VPD(invalidVPD) \
|
|
|
|
|
do { \
|
2022-02-10 10:32:00 +00:00
|
|
|
|
VIR_AUTOCLOSE fd = -1; \
|
2021-10-20 08:30:31 +00:00
|
|
|
|
g_autoptr(virPCIVPDResource) res = NULL; \
|
|
|
|
|
const uint8_t testCase[] = { invalidVPD }; \
|
2021-11-02 16:32:43 +00:00
|
|
|
|
dataLen = G_N_ELEMENTS(testCase); \
|
2021-11-23 14:05:43 +00:00
|
|
|
|
if ((fd = virCreateAnonymousFile(testCase, dataLen)) < 0) \
|
|
|
|
|
return -1; \
|
2021-10-20 08:30:31 +00:00
|
|
|
|
if ((res = virPCIVPDParse(fd))) { \
|
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s", \
|
|
|
|
|
"Successfully parsed an invalid VPD - this is not expected"); \
|
|
|
|
|
return -1; \
|
|
|
|
|
} \
|
|
|
|
|
} while (0);
|
|
|
|
|
|
|
|
|
|
TEST_INVALID_VPD(VPD_INVALID_ZERO_BYTE);
|
|
|
|
|
TEST_INVALID_VPD(VPD_INVALID_STRING_HEADER_DATA_SHORT);
|
|
|
|
|
TEST_INVALID_VPD(VPD_INVALID_STRING_HEADER_DATA_LONG);
|
|
|
|
|
TEST_INVALID_VPD(VPD_NO_VPD_R);
|
|
|
|
|
TEST_INVALID_VPD(VPD_R_NO_RV);
|
|
|
|
|
TEST_INVALID_VPD(VPD_R_INVALID_RV);
|
|
|
|
|
TEST_INVALID_VPD(VPD_R_INVALID_RV_ZERO_LENGTH);
|
|
|
|
|
TEST_INVALID_VPD(VPD_R_UNEXPECTED_RW_IN_VPD_R_KEY);
|
|
|
|
|
TEST_INVALID_VPD(VPD_INVALID_STRING_RESOURCE_VALUE);
|
2021-10-29 18:57:16 +00:00
|
|
|
|
TEST_INVALID_VPD(VPD_INVALID_SN_FIELD_LENGTH);
|
|
|
|
|
TEST_INVALID_VPD(VPD_INVALID_RV_NOT_LAST);
|
|
|
|
|
TEST_INVALID_VPD(VPD_INVALID_RW_NOT_LAST);
|
2021-10-20 08:30:31 +00:00
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
mymain(void)
|
|
|
|
|
{
|
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
|
|
if (virTestRun("Basic functionality of virPCIVPDResource ", testPCIVPDResourceBasic, NULL) < 0)
|
|
|
|
|
ret = -1;
|
|
|
|
|
if (virTestRun("Custom field index comparison",
|
|
|
|
|
testPCIVPDResourceCustomCompareIndex, NULL) < 0)
|
|
|
|
|
ret = -1;
|
|
|
|
|
if (virTestRun("Custom field value insertion and updates ",
|
|
|
|
|
testPCIVPDResourceCustomUpsertValue, NULL) < 0)
|
|
|
|
|
ret = -1;
|
|
|
|
|
if (virTestRun("Valid text values ", testPCIVPDIsValidTextValue, NULL) < 0)
|
|
|
|
|
ret = -1;
|
|
|
|
|
if (virTestRun("Determining a field value format by a key ",
|
|
|
|
|
testPCIVPDGetFieldValueFormat, NULL) < 0)
|
|
|
|
|
ret = -1;
|
2021-10-29 18:57:16 +00:00
|
|
|
|
if (virTestRun("Parsing a VPD resource with a zero-length RW ",
|
|
|
|
|
testVirPCIVPDParseZeroLengthRW, NULL) < 0)
|
|
|
|
|
ret = -1;
|
|
|
|
|
if (virTestRun("Parsing a VPD resource without an RW ",
|
|
|
|
|
testVirPCIVPDParseNoRW, NULL) < 0)
|
|
|
|
|
ret = -1;
|
2021-10-29 18:57:17 +00:00
|
|
|
|
if (virTestRun("Parsing a VPD resource with an invalid values ",
|
|
|
|
|
testVirPCIVPDParseFullVPDSkipInvalidValues, NULL) < 0)
|
|
|
|
|
ret = -1;
|
2021-10-20 08:30:31 +00:00
|
|
|
|
if (virTestRun("Parsing a VPD resource with an invalid keyword ",
|
|
|
|
|
testVirPCIVPDParseFullVPDSkipInvalidKeywords, NULL) < 0)
|
|
|
|
|
ret = -1;
|
|
|
|
|
if (virTestRun("Parsing VPD resources from a full VPD ", testVirPCIVPDParseFullVPD, NULL) < 0)
|
|
|
|
|
ret = -1;
|
|
|
|
|
if (virTestRun("Parsing invalid VPD records ", testVirPCIVPDParseFullVPDInvalid, NULL) < 0)
|
|
|
|
|
ret = -1;
|
|
|
|
|
|
|
|
|
|
return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VIR_TEST_MAIN(mymain)
|
|
|
|
|
|
|
|
|
|
#else /* ! __linux__ */
|
|
|
|
|
int
|
|
|
|
|
main(void)
|
|
|
|
|
{
|
|
|
|
|
return EXIT_AM_SKIP;
|
|
|
|
|
}
|
|
|
|
|
#endif /* ! __linux__ */
|