mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2024-12-23 06:05:27 +00:00
05cd957836
Fixes:59c1bc3a0e
Fixes:43820e4b80
Fixes:600f580d62
Signed-off-by: Ján Tomko <jtomko@redhat.com> Reviewed-by: Kristína Hanicová <khanicov@redhat.com>
1044 lines
35 KiB
C
1044 lines
35 KiB
C
/*
|
||
* 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 readWriteCases[] = {
|
||
{.keyword = "YA", .value = "tag1", .actual = &ro->change_level},
|
||
{.keyword = "YA", .value = "tag2", .actual = &ro->change_level},
|
||
{.keyword = "asset_tag", .value = "tag3", .actual = &ro->change_level},
|
||
};
|
||
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},
|
||
};
|
||
size_t numROCases = G_N_ELEMENTS(readOnlyCases);
|
||
size_t numRWCases = G_N_ELEMENTS(readWriteCases);
|
||
size_t numUnsupportedCases = G_N_ELEMENTS(unsupportedFieldCases);
|
||
g_autoptr(virPCIVPDResource) res = g_new0(virPCIVPDResource, 1);
|
||
virPCIVPDResourceCustom *custom = NULL;
|
||
|
||
g_autofree char *val = g_strdup("testval");
|
||
res->name = g_steal_pointer(&val);
|
||
|
||
/* RO has not been initialized - make sure updates fail. */
|
||
for (i = 0; i < numROCases; ++i) {
|
||
if (virPCIVPDResourceUpdateKeyword(res, true,
|
||
readOnlyCases[i].keyword,
|
||
readOnlyCases[i].value))
|
||
return -1;
|
||
}
|
||
/* RW has not been initialized - make sure updates fail. */
|
||
for (i = 0; i < numRWCases; ++i) {
|
||
if (virPCIVPDResourceUpdateKeyword(res, false,
|
||
readWriteCases[i].keyword,
|
||
readWriteCases[i].value))
|
||
return -1;
|
||
}
|
||
/* 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) {
|
||
if (!virPCIVPDResourceUpdateKeyword(res, true,
|
||
readOnlyCases[i].keyword,
|
||
readOnlyCases[i].value))
|
||
return -1;
|
||
if (STRNEQ(readOnlyCases[i].value, *readOnlyCases[i].actual))
|
||
return -1;
|
||
}
|
||
|
||
/* Do a basic vendor field check. */
|
||
if (!virPCIVPDResourceUpdateKeyword(res, true, "V0", "vendor0"))
|
||
return -1;
|
||
|
||
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) {
|
||
if (!virPCIVPDResourceUpdateKeyword(res, true,
|
||
unsupportedFieldCases[i].keyword,
|
||
unsupportedFieldCases[i].value))
|
||
return -1;
|
||
}
|
||
|
||
/* Check that RW updates fail if RW has not been initialized. */
|
||
if (virPCIVPDResourceUpdateKeyword(res, false, "YA", "tag1"))
|
||
return -1;
|
||
|
||
if (virPCIVPDResourceUpdateKeyword(res, false, "asset_tag", "tag1"))
|
||
return -1;
|
||
|
||
/* Initialize RW */
|
||
res->rw = g_steal_pointer(&rw);
|
||
if (!virPCIVPDResourceUpdateKeyword(res, false, "YA", "tag1")
|
||
|| STRNEQ(res->rw->asset_tag, "tag1"))
|
||
return -1;
|
||
|
||
if (!virPCIVPDResourceUpdateKeyword(res, false, "asset_tag", "tag2")
|
||
|| STRNEQ(res->rw->asset_tag, "tag2"))
|
||
return -1;
|
||
|
||
/* Do a basic system field check. */
|
||
if (!virPCIVPDResourceUpdateKeyword(res, false, "Y0", "system0"))
|
||
return -1;
|
||
|
||
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) {
|
||
if (!virPCIVPDResourceUpdateKeyword(res, false,
|
||
unsupportedFieldCases[i].keyword,
|
||
unsupportedFieldCases[i].value))
|
||
return -1;
|
||
}
|
||
|
||
|
||
/* 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)
|
||
{
|
||
g_autoptr(virPCIVPDResourceCustom) a = NULL;
|
||
g_autoptr(virPCIVPDResourceCustom) b = NULL;
|
||
|
||
/* 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 */
|
||
g_free(b->value);
|
||
b->value = a->value;
|
||
if (virPCIVPDResourceCustomCompareIndex(b, a))
|
||
return -1;
|
||
|
||
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;
|
||
if (!virPCIVPDResourceCustomUpsertValue(arr, 'A', "testval"))
|
||
return -1;
|
||
|
||
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 */
|
||
if (!virPCIVPDResourceCustomUpsertValue(arr, 'A', "testval"))
|
||
return -1;
|
||
|
||
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. */
|
||
if (!virPCIVPDResourceCustomUpsertValue(arr, 'A', "testvalnew"))
|
||
return -1;
|
||
|
||
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 */
|
||
if (!virPCIVPDResourceCustomUpsertValue(arr, '1', "42"))
|
||
return -1;
|
||
|
||
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},
|
||
{"\\42", true},
|
||
{"N/A", true},
|
||
/* The first and last code points are outside ASCII (multi-byte in UTF-8). */
|
||
{"гbl🐧", false},
|
||
};
|
||
for (i = 0; i < G_N_ELEMENTS(textValueCases); ++i) {
|
||
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},
|
||
};
|
||
for (i = 0; i < G_N_ELEMENTS(valueFormatCases); ++i) {
|
||
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', \
|
||
'Y', 'A', 0x04, 'I', 'D', '4', '2', \
|
||
'Y', 'F', 0x02, 'E', 'X', \
|
||
'Y', 'E', 0x00, \
|
||
'R', 'W', 0x02, 0x00, 0x00
|
||
|
||
static int
|
||
testVirPCIVPDReadVPDBytes(const void *opaque G_GNUC_UNUSED)
|
||
{
|
||
int fd = -1;
|
||
g_autofree uint8_t *buf = NULL;
|
||
uint8_t csum = 0;
|
||
size_t readBytes = 0;
|
||
size_t dataLen = 0;
|
||
|
||
/* An example of a valid VPD record with one VPD-R resource and 2 fields. */
|
||
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_RESOURCE_END_VAL
|
||
};
|
||
dataLen = G_N_ELEMENTS(fullVPDExample) - 2;
|
||
buf = g_malloc0(dataLen);
|
||
|
||
if ((fd = virCreateAnonymousFile(fullVPDExample, dataLen)) < 0)
|
||
return -1;
|
||
|
||
readBytes = virPCIVPDReadVPDBytes(fd, buf, dataLen, 0, &csum);
|
||
|
||
if (readBytes != dataLen) {
|
||
virReportError(VIR_ERR_INTERNAL_ERROR,
|
||
"The number of bytes read %zu is lower than expected %zu ",
|
||
readBytes, dataLen);
|
||
return -1;
|
||
}
|
||
|
||
if (csum) {
|
||
virReportError(VIR_ERR_INTERNAL_ERROR,
|
||
"The sum of all VPD bytes up to and including the checksum byte"
|
||
"is equal to zero: 0x%02x", csum);
|
||
return -1;
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
static int
|
||
testVirPCIVPDParseVPDStringResource(const void *opaque G_GNUC_UNUSED)
|
||
{
|
||
int fd = -1;
|
||
uint8_t csum = 0;
|
||
size_t dataLen = 0;
|
||
bool result = false;
|
||
|
||
g_autoptr(virPCIVPDResource) res = g_new0(virPCIVPDResource, 1);
|
||
const char *expectedValue = "testname";
|
||
|
||
const uint8_t stringResExample[] = {
|
||
VPD_STRING_RESOURCE_EXAMPLE_DATA
|
||
};
|
||
|
||
dataLen = G_N_ELEMENTS(stringResExample);
|
||
if ((fd = virCreateAnonymousFile(stringResExample, dataLen)) < 0)
|
||
return -1;
|
||
|
||
result = virPCIVPDParseVPDLargeResourceString(fd, 0, dataLen, &csum, res);
|
||
VIR_FORCE_CLOSE(fd);
|
||
|
||
if (!result) {
|
||
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
||
"Could not parse the example resource.");
|
||
return -1;
|
||
}
|
||
|
||
if (STRNEQ(expectedValue, res->name)) {
|
||
virReportError(VIR_ERR_INTERNAL_ERROR,
|
||
"Unexpected string resource value: %s, expected: %s",
|
||
res->name, expectedValue);
|
||
return -1;
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
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)
|
||
{
|
||
int fd = -1;
|
||
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
|
||
};
|
||
|
||
dataLen = G_N_ELEMENTS(fullVPDExample);
|
||
if ((fd = virCreateAnonymousFile(fullVPDExample, dataLen)) < 0)
|
||
return -1;
|
||
|
||
res = virPCIVPDParse(fd);
|
||
VIR_FORCE_CLOSE(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;
|
||
|
||
if (STRNEQ_NULLABLE(res->rw->asset_tag, "ID42"))
|
||
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;
|
||
return 0;
|
||
}
|
||
|
||
static int
|
||
testVirPCIVPDParseZeroLengthRW(const void *opaque G_GNUC_UNUSED)
|
||
{
|
||
int fd = -1;
|
||
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
|
||
};
|
||
|
||
dataLen = G_N_ELEMENTS(fullVPDExample);
|
||
if ((fd = virCreateAnonymousFile(fullVPDExample, dataLen)) < 0)
|
||
return -1;
|
||
|
||
res = virPCIVPDParse(fd);
|
||
VIR_FORCE_CLOSE(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)
|
||
{
|
||
int fd = -1;
|
||
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
|
||
};
|
||
|
||
dataLen = G_N_ELEMENTS(fullVPDExample);
|
||
if ((fd = virCreateAnonymousFile(fullVPDExample, dataLen)) < 0)
|
||
return -1;
|
||
|
||
res = virPCIVPDParse(fd);
|
||
VIR_FORCE_CLOSE(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
|
||
testVirPCIVPDParseFullVPDSkipInvalidKeywords(const void *opaque G_GNUC_UNUSED)
|
||
{
|
||
int fd = -1;
|
||
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
|
||
};
|
||
|
||
dataLen = G_N_ELEMENTS(fullVPDExample);
|
||
if ((fd = virCreateAnonymousFile(fullVPDExample, dataLen)) < 0)
|
||
return -1;
|
||
|
||
res = virPCIVPDParse(fd);
|
||
VIR_FORCE_CLOSE(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;
|
||
}
|
||
|
||
static int
|
||
testVirPCIVPDParseFullVPDSkipInvalidValues(const void *opaque G_GNUC_UNUSED)
|
||
{
|
||
int fd = -1;
|
||
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,
|
||
};
|
||
|
||
dataLen = G_N_ELEMENTS(fullVPDExample);
|
||
if ((fd = virCreateAnonymousFile(fullVPDExample, dataLen)) < 0)
|
||
return -1;
|
||
|
||
res = virPCIVPDParse(fd);
|
||
VIR_FORCE_CLOSE(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;
|
||
}
|
||
|
||
|
||
static int
|
||
testVirPCIVPDParseFullVPDInvalid(const void *opaque G_GNUC_UNUSED)
|
||
{
|
||
int fd = -1;
|
||
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
|
||
|
||
/* 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
|
||
|
||
|
||
# define TEST_INVALID_VPD(invalidVPD) \
|
||
do { \
|
||
g_autoptr(virPCIVPDResource) res = NULL; \
|
||
const uint8_t testCase[] = { invalidVPD }; \
|
||
dataLen = G_N_ELEMENTS(testCase); \
|
||
if ((fd = virCreateAnonymousFile(testCase, dataLen)) < 0) \
|
||
return -1; \
|
||
if ((res = virPCIVPDParse(fd))) { \
|
||
virReportError(VIR_ERR_INTERNAL_ERROR, "%s", \
|
||
"Successfully parsed an invalid VPD - this is not expected"); \
|
||
return -1; \
|
||
} \
|
||
VIR_FORCE_CLOSE(fd); \
|
||
} 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);
|
||
TEST_INVALID_VPD(VPD_INVALID_SN_FIELD_LENGTH);
|
||
TEST_INVALID_VPD(VPD_INVALID_RV_NOT_LAST);
|
||
TEST_INVALID_VPD(VPD_INVALID_RW_NOT_LAST);
|
||
|
||
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;
|
||
if (virTestRun("Reading VPD bytes ", testVirPCIVPDReadVPDBytes, NULL) < 0)
|
||
ret = -1;
|
||
if (virTestRun("Parsing VPD string resources ", testVirPCIVPDParseVPDStringResource, NULL) < 0)
|
||
ret = -1;
|
||
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;
|
||
if (virTestRun("Parsing a VPD resource with an invalid values ",
|
||
testVirPCIVPDParseFullVPDSkipInvalidValues, NULL) < 0)
|
||
ret = -1;
|
||
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__ */
|