On Mon, Oct 11, 2021 at 05:04:42PM +0300, Dmitrii Shcherbakov wrote:
Add support for deserializing the binary PCI/PCIe VPD format and
storing
results in memory.
The VPD format is specified in "I.3. VPD Definitions" in PCI specs
(2.2+) and "6.28.1 VPD Format" PCIe 4.0. As section 6.28 in PCIe 4.0
notes, the PCI Local Bus and PCIe VPD formats are binary compatible
and PCIe 4.0 merely started incorporating what was already present in
PCI specs.
Linux kernel exposes a binary blob in the VPD format via sysfs since
v2.6.26 (commit 94e6108803469a37ee1e3c92dafdd1d59298602f) which requires
a parser to interpret.
Signed-off-by: Dmitrii Shcherbakov <dmitrii.shcherbakov(a)canonical.com>
---
build-aux/syntax-check.mk | 4 +-
po/POTFILES.in | 1 +
src/libvirt_private.syms | 18 +
src/util/meson.build | 1 +
src/util/virpcivpd.c | 743 ++++++++++++++++++++++++++++++++++++
src/util/virpcivpd.h | 111 ++++++
src/util/virpcivpdpriv.h | 48 +++
tests/meson.build | 1 +
tests/testutils.c | 35 ++
tests/testutils.h | 4 +
tests/virpcivpdtest.c | 768 ++++++++++++++++++++++++++++++++++++++
11 files changed, 1732 insertions(+), 2 deletions(-)
create mode 100644 src/util/virpcivpd.c
create mode 100644 src/util/virpcivpd.h
create mode 100644 src/util/virpcivpdpriv.h
create mode 100644 tests/virpcivpdtest.c
diff --git a/src/util/virpcivpd.c b/src/util/virpcivpd.c
new file mode 100644
index 0000000000..5caede815f
--- /dev/null
+++ b/src/util/virpcivpd.c
@@ -0,0 +1,743 @@
+/*
+ * virpcivpd.c: helper APIs for working with the PCI/PCIe VPD capability
+ *
+ * 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/>.
+ */
+void
+virPCIVPDResourceFree(virPCIVPDResource *res)
+{
+ if (!res) {
+ return;
+ }
+ virPCIVPDResourceROFree(res->ro);
+ virPCIVPDResourceRWFree(res->rw);
g_free(res);
+}
+
+virPCIVPDResourceRO *
+virPCIVPDResourceRONew(void)
+{
+ g_autoptr(virPCIVPDResourceRO) ro = g_new0(virPCIVPDResourceRO, 1);
+ ro->vendor_specific = g_ptr_array_new_full(0,
(GDestroyNotify)virPCIVPDResourceCustomFree);
+ return g_steal_pointer(&ro);
+}
+
+void
+virPCIVPDResourceROFree(virPCIVPDResourceRO *ro)
+{
+ if (!ro) {
+ return;
+ }
+ g_free(ro->change_level);
+ g_free(ro->manufacture_id);
+ g_free(ro->part_number);
+ g_free(ro->serial_number);
+ g_ptr_array_unref(ro->vendor_specific);
g_free(ro);
+}
+
+virPCIVPDResourceRW *
+virPCIVPDResourceRWNew(void)
+{
+ g_autoptr(virPCIVPDResourceRW) rw = g_new0(virPCIVPDResourceRW, 1);
+ rw->vendor_specific = g_ptr_array_new_full(0,
(GDestroyNotify)virPCIVPDResourceCustomFree);
+ rw->system_specific = g_ptr_array_new_full(0,
(GDestroyNotify)virPCIVPDResourceCustomFree);
+ return g_steal_pointer(&rw);
+}
+
+void
+virPCIVPDResourceRWFree(virPCIVPDResourceRW *rw)
+{
+ if (!rw) {
+ return;
+ }
+ g_free(rw->asset_tag);
+ g_ptr_array_unref(rw->vendor_specific);
+ g_ptr_array_unref(rw->system_specific);
+}
g_free(rw);
+
+void
+virPCIVPDResourceCustomFree(virPCIVPDResourceCustom *custom)
+{
+ g_free(custom->value);
+ g_free(custom);
+}
+/**
+ * virPCIVPDResourceUpdateKeyword:
+ * @res: A non-NULL pointer to a virPCIVPDResource where a keyword will be updated.
+ * @readOnly: A bool specifying which section to update (in-memory): read-only or
read-write.
+ * @keyword: A non-NULL pointer to a name of the keyword that will be updated.
+ * @value: A pointer to the keyword value or NULL. The value is copied on successful
update.
+ *
+ * The caller is responsible for initializing the relevant RO or RW sections of the
resource,
+ * otherwise, false will be returned.
+ *
+ * Keyword names are either 2-byte keywords from the spec or their human-readable
alternatives
+ * used in XML elements. For vendor-specific and system-specific keywords only V%s and
Y%s
+ * (except "YA" which is an asset tag) formatted values are accepted.
+ *
+ * Returns: true if a keyword has been updated successfully, false otherwise.
+ */
+bool
+virPCIVPDResourceUpdateKeyword(virPCIVPDResource *res, const bool readOnly,
+ const char *const keyword, const char *const value)
+{
+ if (!res) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("Cannot update the resource: a NULL resource pointer has
been provided."));
+ return false;
+ } else if (!keyword) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("Cannot update the resource: a NULL keyword pointer has
been provided."));
+ return false;
+ }
+
+ if (readOnly) {
+ if (!res->ro) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("Cannot update the read-only keyword: RO section not
initialized."));
+ return false;
+ }
+
+ if (STREQ("EC", keyword) || STREQ("change_level", keyword))
{
+ res->ro->change_level = g_strdup(value);
+ return true;
+ } else if (STREQ("MN", keyword) || STREQ("manufacture_id",
keyword)) {
+ res->ro->manufacture_id = g_strdup(value);
+ return true;
+ } else if (STREQ("PN", keyword) || STREQ("part_number",
keyword)) {
+ res->ro->part_number = g_strdup(value);
+ return true;
+ } else if (STREQ("SN", keyword) || STREQ("serial_number",
keyword)) {
+ res->ro->serial_number = g_strdup(value);
+ return true;
+ } else if (virPCIVPDResourceIsVendorKeyword(keyword)) {
+ if (!virPCIVPDResourceCustomUpsertValue(res->ro->vendor_specific,
keyword[1], value)) {
+ return false;
+ }
+ return true;
+ } else if (STREQ("FG", keyword) || STREQ("LC", keyword) ||
STREQ("PG", keyword)) {
+ /* Legacy PICMIG keywords are skipped on purpose. */
+ return true;
+ } else if (STREQ("CP", keyword)) {
+ /* The CP keyword is currently not supported and is skipped. */
+ return true;
+ }
+
+ } else {
+ if (!res->rw) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _
+ ("Cannot update the read-write keyword: read-write
section not initialized."));
+ return false;
+ }
+
+ if (STREQ("YA", keyword) || STREQ("asset_tag", keyword)) {
+ res->rw->asset_tag = g_strdup(value);
+ return true;
+ } else if (virPCIVPDResourceIsVendorKeyword(keyword)) {
+ if (!virPCIVPDResourceCustomUpsertValue(res->rw->vendor_specific,
keyword[1], value)) {
+ return false;
+ }
+ return true;
+ } else if (virPCIVPDResourceIsSystemKeyword(keyword)) {
+ if (!virPCIVPDResourceCustomUpsertValue(res->rw->system_specific,
keyword[1], value)) {
+ return false;
+ }
+ return true;
+ }
+ }
+ /* Unsupported keyword. */
Earlier we have some genuine fatal errors we return false for.
I'd say we should VIR_WARN("Unexpected keyword ....") here and then
'return true' to indicate success, since it is harmless.....
+ return false;
+}
+ /* The field format, keyword and value are determined.
Attempt to update the resource. */
+ if (!virPCIVPDResourceUpdateKeyword(res, readOnly, fieldKeyword, fieldValue)) {
+ /* Write an error but attempt to handle it gracefully by continuing.
+ * Unexpected keywords may be a result of a spec extension in the future.
*/
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("Could not update the VPD resource keyword: %s"),
fieldKeyword);
...so we can honour the failure status here, and not repeat virReportError
which has already been done by the method we called.
diff --git a/src/util/virpcivpd.h b/src/util/virpcivpd.h
new file mode 100644
index 0000000000..25aee5dffe
--- /dev/null
+++ b/src/util/virpcivpd.h
@@ -0,0 +1,111 @@
+/*
+ * virpcivpd.h: helper APIs for working with the PCI/PCIe VPD capability
+ *
+ * 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/>.
+ */
+
+#pragma once
+
+#include "internal.h"
+
+/*
+ * PCI Local bus (2.2+, Appendix I) and PCIe 4.0+ (7.9.19 VPD Capability) define
+ * the VPD capability structure (8 bytes in total) and VPD registers that can be used to
access
+ * VPD data including:
+ * bit 31 of the first 32-bit DWORD: data transfer completion flag (between the VPD data
register
+ * and the VPD data storage hardware);
+ * bits 30:16 of the first 32-bit DWORD: VPD address of the first VPD data byte to be
accessed;
+ * bits 31:0 of the second 32-bit DWORD: VPD data bytes with LSB being pointed to by the
VPD address.
+ *
+ * Given that only 15 bits (30:16) are allocated for VPD address its mask is 0x7fff.
+*/
+#define PCI_VPD_ADDR_MASK 0x7FFF
+
+/*
+ * VPD data consists of small and large resource data types. Information within a
resource type
+ * consists of a 2-byte keyword, 1-byte length and data bytes (up to 255).
+*/
+#define PCI_VPD_MAX_FIELD_SIZE 255
+#define PCI_VPD_LARGE_RESOURCE_FLAG 0x80
+#define PCI_VPD_STRING_RESOURCE_FLAG 0x02
+#define PCI_VPD_READ_ONLY_LARGE_RESOURCE_FLAG 0x10
+#define PCI_VPD_READ_WRITE_LARGE_RESOURCE_FLAG 0x11
+#define PCI_VPD_RESOURCE_END_TAG 0x0F
+#define PCI_VPD_RESOURCE_END_VAL PCI_VPD_RESOURCE_END_TAG << 3
+
+typedef enum {
+ VIR_PCI_VPD_RESOURCE_FIELD_VALUE_FORMAT_TEXT = 1,
+ VIR_PCI_VPD_RESOURCE_FIELD_VALUE_FORMAT_BINARY,
+ VIR_PCI_VPD_RESOURCE_FIELD_VALUE_FORMAT_RESVD,
+ VIR_PCI_VPD_RESOURCE_FIELD_VALUE_FORMAT_RDWR,
+ VIR_PCI_VPD_RESOURCE_FIELD_VALUE_FORMAT_LAST
+} virPCIVPDResourceFieldValueFormat;
+
+virPCIVPDResourceFieldValueFormat virPCIVPDResourceGetFieldValueFormat(const char
*value);
THe pieces above this point are only needed by the parser, so can
all go in the .c file, so the .h is only stuff needed by the
external callers.
+
+typedef struct virPCIVPDResourceCustom virPCIVPDResourceCustom;
+struct virPCIVPDResourceCustom {
+ char idx;
+ char *value;
+};
+
+typedef struct virPCIVPDResourceRO virPCIVPDResourceRO;
+struct virPCIVPDResourceRO {
+ char *part_number;
+ char *change_level;
+ char *manufacture_id;
+ char *serial_number;
+ GPtrArray *vendor_specific;
+};
+
+typedef struct virPCIVPDResourceRW virPCIVPDResourceRW;
+struct virPCIVPDResourceRW {
+ char *asset_tag;
+ GPtrArray *vendor_specific;
+ GPtrArray *system_specific;
+};
+
+typedef struct virPCIVPDResource virPCIVPDResource;
+struct virPCIVPDResource {
+ char *name;
+ virPCIVPDResourceRO *ro;
+ virPCIVPDResourceRW *rw;
+};
+
+
+virPCIVPDResource *virPCIVPDParse(int vpdFileFd);
+void virPCIVPDResourceFree(virPCIVPDResource *res);
+
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(virPCIVPDResource, virPCIVPDResourceFree);
+
+virPCIVPDResourceRO *virPCIVPDResourceRONew(void);
+void virPCIVPDResourceROFree(virPCIVPDResourceRO *ro);
+
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(virPCIVPDResourceRO, virPCIVPDResourceROFree);
+
+virPCIVPDResourceRW *virPCIVPDResourceRWNew(void);
+void virPCIVPDResourceRWFree(virPCIVPDResourceRW *rw);
+
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(virPCIVPDResourceRW, virPCIVPDResourceRWFree);
+
+bool
+virPCIVPDResourceUpdateKeyword(virPCIVPDResource *res, const bool readOnly,
+ const char *const keyword, const char *const value);
+
+void virPCIVPDResourceCustomFree(virPCIVPDResourceCustom *custom);
+
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(virPCIVPDResourceCustom, virPCIVPDResourceCustomFree);
+static int
+testPCIVPDResourceBasic(const void *data G_GNUC_UNUSED)
+{
+ size_t i = 0;
+ g_autoptr(virPCIVPDResourceRO) ro = virPCIVPDResourceRONew();
+ g_autoptr(virPCIVPDResourceRW) rw = virPCIVPDResourceRWNew();
+ 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},
+ };
These repeated values of the same keyword are causing memory leaks
as the struct only stores a single value. We should free the existong
value if we see repeats to avoid te leak
+ 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},
+ };
+ size_t numROCases = sizeof(readOnlyCases) / sizeof(TestPCIVPDKeywordValue);
+ size_t numRWCases = sizeof(readWriteCases) / sizeof(TestPCIVPDKeywordValue);
+ 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;
+ }
+
+ /* 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;
+ }
+
+ /* Just make sure the name has not been changed during keyword updates. */
+ if (!STREQ_NULLABLE(res->name, "testval")) {
+ return -1;
+ }
+ return 0;
+}
+static int
+testVirPCIVPDParseFullVPD(const void *opaque G_GNUC_UNUSED)
+{
+ int fd = -1;
+ size_t dataLen = 0;
+ int ret = 0;
+
+ virPCIVPDResource *res = NULL;
g_autoptr
+ 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 = sizeof(fullVPDExample) / sizeof(uint8_t);
+ fd = virCreateAnonymousFile(fullVPDExample, dataLen);
+ 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 ret;
+ }
+
+ 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;
+ }
+ return ret;
+}
+
+static int
+testVirPCIVPDParseFullVPDSkipInvalidKeywords(const void *opaque G_GNUC_UNUSED)
+{
+ int fd = -1;
+ size_t dataLen = 0;
+
+ virPCIVPDResource *res = NULL;
g_autoptr
+
+ 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 = sizeof(fullVPDExample) / sizeof(uint8_t);
+ fd = virCreateAnonymousFile(fullVPDExample, dataLen);
+ 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
+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 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)
+#endif
running valgrind --leak-check=full virpcivpdtest reports some
errors. I pointed out most of them above I think, but might be some
more to catch.
Regards,
Daniel
--
|:
https://berrange.com -o-
https://www.flickr.com/photos/dberrange :|
|:
https://libvirt.org -o-
https://fstop138.berrange.com :|
|:
https://entangle-photo.org -o-
https://www.instagram.com/dberrange :|