From nobody Sun May 19 07:16:18 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zohomail.com: domain of redhat.com designates 216.205.24.124 as permitted sender) client-ip=216.205.24.124; envelope-from=libvir-list-bounces@redhat.com; helo=us-smtp-delivery-124.mimecast.com; Authentication-Results: mx.zohomail.com; spf=pass (zohomail.com: domain of redhat.com designates 216.205.24.124 as permitted sender) smtp.mailfrom=libvir-list-bounces@redhat.com; dmarc=fail(p=none dis=none) header.from=canonical.com Return-Path: Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [216.205.24.124]) by mx.zohomail.com with SMTPS id 1632771102366406.84098044487894; Mon, 27 Sep 2021 12:31:42 -0700 (PDT) Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-169-w0IYIzbhPQmHF0z9zPWZIg-1; Mon, 27 Sep 2021 15:31:39 -0400 Received: from smtp.corp.redhat.com (int-mx01.intmail.prod.int.phx2.redhat.com [10.5.11.11]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id D24FC1084689; Mon, 27 Sep 2021 19:31:32 +0000 (UTC) Received: from colo-mx.corp.redhat.com (colo-mx01.intmail.prod.int.phx2.redhat.com [10.5.11.20]) by smtp.corp.redhat.com (Postfix) with ESMTPS id B48C7421F; Mon, 27 Sep 2021 19:31:32 +0000 (UTC) Received: from lists01.pubmisc.prod.ext.phx2.redhat.com (lists01.pubmisc.prod.ext.phx2.redhat.com [10.5.19.33]) by colo-mx.corp.redhat.com (Postfix) with ESMTP id 84E401806D00; Mon, 27 Sep 2021 19:31:32 +0000 (UTC) Received: from smtp.corp.redhat.com (int-mx06.intmail.prod.int.rdu2.redhat.com [10.11.54.6]) by lists01.pubmisc.prod.ext.phx2.redhat.com (8.13.8/8.13.8) with ESMTP id 18RJV78U014428 for ; Mon, 27 Sep 2021 15:31:07 -0400 Received: by smtp.corp.redhat.com (Postfix) id 1DCBD2012E31; Mon, 27 Sep 2021 19:31:07 +0000 (UTC) Received: from mimecast-mx02.redhat.com (mimecast01.extmail.prod.ext.rdu2.redhat.com [10.11.55.17]) by smtp.corp.redhat.com (Postfix) with ESMTPS id 154502144B2D for ; Mon, 27 Sep 2021 19:31:01 +0000 (UTC) Received: from us-smtp-1.mimecast.com (us-smtp-delivery-1.mimecast.com [205.139.110.120]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id 06488858F1E for ; Mon, 27 Sep 2021 19:31:01 +0000 (UTC) Received: from smtp-relay-internal-0.canonical.com (smtp-relay-internal-0.canonical.com [185.125.188.122]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-570-DMIZ8_TTN8yiXMQiJrxqlg-1; Mon, 27 Sep 2021 15:30:58 -0400 Received: from mail-lf1-f70.google.com (mail-lf1-f70.google.com [209.85.167.70]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by smtp-relay-internal-0.canonical.com (Postfix) with ESMTPS id 3ED5F4079E for ; Mon, 27 Sep 2021 19:30:57 +0000 (UTC) Received: by mail-lf1-f70.google.com with SMTP id v197-20020a1948ce000000b003fc99be7fecso16781775lfa.2 for ; Mon, 27 Sep 2021 12:30:57 -0700 (PDT) Received: from ws.lan.d-node.is ([95.165.29.203]) by smtp.gmail.com with ESMTPSA id a8sm1688090lfr.256.2021.09.27.12.30.54 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 27 Sep 2021 12:30:54 -0700 (PDT) X-MC-Unique: w0IYIzbhPQmHF0z9zPWZIg-1 X-MC-Unique: DMIZ8_TTN8yiXMQiJrxqlg-1 X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=buRwcOxV6KslLICGeP5bO/IW6YyuA5Ze11/w9x7w56A=; b=jF7vZYYbjmMCWzg9fGHjZVDjOVgEbtKRL2VlTpMqhBklimUpGj2yq0ONY1A8Lsy/sy zHFk0QpvpkFS1WYBusVbHlnx7znjtF+Md1JWfcY1dX34wihWaJeULUucYIAon8dftFNo hl7/Ubxj3uxEi9J1o9kRV7uBs4eg5RRecZFXdS8P6CY6JASlZdf2Jvwj7pcwsdeMF+Yb /DGhYtxe6NlGVceJptSM0DC+Ex0AvmVsDWU79zz6qnIt1P9F4J5b5/TfX81ndIsqPy39 HDmRFfmInr+odAqCjp2P9YKVyGEfWGLBb1Su3zY7+u0awJGt4PNenqv6NItjFxzrxvqh cNBQ== X-Gm-Message-State: AOAM532N7dFctAaUL1mak9/TgdQx3eUm9bmfLgkF6W95HFgpcGnwHqJo 4yb326hA1SeElxIsynR/0B/TbbeknXFShUt4sHzQiboac5qhYUT40AdDmszttrRZPvo4JjWfB6i 1rUZTZHa0N/Abd4n1FahcJZlNcUJTQ6bZmg== X-Received: by 2002:a05:651c:a0f:: with SMTP id k15mr1495564ljq.231.1632771055953; Mon, 27 Sep 2021 12:30:55 -0700 (PDT) X-Google-Smtp-Source: ABdhPJyorS/E+mzUj9JaGEh0HR0Y3BY9WxCnMnPEx87rTWkhQddAEAOE7h5wEGDpaky/E33rofd6+g== X-Received: by 2002:a05:651c:a0f:: with SMTP id k15mr1495504ljq.231.1632771055184; Mon, 27 Sep 2021 12:30:55 -0700 (PDT) From: Dmitrii Shcherbakov To: libvir-list@redhat.com, dmitrii.shcherbakov@canonical.com Subject: [libvirt PATCH v5 1/7] Add a PCI/PCIe device VPD Parser Date: Mon, 27 Sep 2021 22:30:47 +0300 Message-Id: <20210927193053.109029-2-dmitrii.shcherbakov@canonical.com> In-Reply-To: <20210927193053.109029-1-dmitrii.shcherbakov@canonical.com> References: <20210927193053.109029-1-dmitrii.shcherbakov@canonical.com> MIME-Version: 1.0 X-Mimecast-Impersonation-Protect: Policy=CLT - Impersonation Protection Definition; Similar Internal Domain=false; Similar Monitored External Domain=false; Custom External Domain=false; Mimecast External Domain=false; Newly Observed Domain=false; Internal User Name=false; Custom Display Name List=false; Reply-to Address Mismatch=false; Targeted Threat Dictionary=false; Mimecast Threat Dictionary=false; Custom Threat Dictionary=false X-Scanned-By: MIMEDefang 2.78 on 10.11.54.6 X-MIME-Autoconverted: from quoted-printable to 8bit by lists01.pubmisc.prod.ext.phx2.redhat.com id 18RJV78U014428 X-loop: libvir-list@redhat.com X-BeenThere: libvir-list@redhat.com X-Mailman-Version: 2.1.12 Precedence: junk List-Id: Development discussions about the libvirt library & tools List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: libvir-list-bounces@redhat.com Errors-To: libvir-list-bounces@redhat.com X-Scanned-By: MIMEDefang 2.79 on 10.5.11.11 Authentication-Results: relay.mimecast.com; auth=pass smtp.auth=CUSA124A263 smtp.mailfrom=libvir-list-bounces@redhat.com X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Content-Transfer-Encoding: quoted-printable X-ZM-MESSAGEID: 1632771104122100001 Content-Type: text/plain; charset="utf-8" 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. A GTree is used as a data structure in order to maintain key ordering which will be important in XML to XML tests later. Signed-off-by: Dmitrii Shcherbakov --- build-aux/syntax-check.mk | 4 +- po/POTFILES.in | 1 + src/libvirt_private.syms | 15 + src/util/meson.build | 1 + src/util/virpcivpd.c | 755 ++++++++++++++++++++++++++++++++++++++ src/util/virpcivpd.h | 117 ++++++ src/util/virpcivpdpriv.h | 45 +++ tests/meson.build | 1 + tests/testutils.c | 40 ++ tests/testutils.h | 4 + tests/virpcivpdtest.c | 705 +++++++++++++++++++++++++++++++++++ 11 files changed, 1686 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/build-aux/syntax-check.mk b/build-aux/syntax-check.mk index cb54c8ba36..a428831380 100644 --- a/build-aux/syntax-check.mk +++ b/build-aux/syntax-check.mk @@ -775,9 +775,9 @@ sc_prohibit_windows_special_chars_in_filename: { echo '$(ME): Windows special chars in filename not allowed' 1>&2; echo = exit 1; } || : =20 sc_prohibit_mixed_case_abbreviations: - @prohibit=3D'Pci|Usb|Scsi' \ + @prohibit=3D'Pci|Usb|Scsi|Vpd' \ in_vc_files=3D'\.[ch]$$' \ - halt=3D'Use PCI, USB, SCSI, not Pci, Usb, Scsi' \ + halt=3D'Use PCI, USB, SCSI, VPD, not Pci, Usb, Scsi, Vpd' \ $(_sc_search_regexp) =20 # Require #include in all files that call setlocale() diff --git a/po/POTFILES.in b/po/POTFILES.in index c200d7452a..4be4139529 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -302,6 +302,7 @@ @SRCDIR@src/util/virnvme.c @SRCDIR@src/util/virobject.c @SRCDIR@src/util/virpci.c +@SRCDIR@src/util/virpcivpd.c @SRCDIR@src/util/virperf.c @SRCDIR@src/util/virpidfile.c @SRCDIR@src/util/virpolkit.c diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 6de9d9aef1..bb9b380599 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -3571,6 +3571,21 @@ virVHBAManageVport; virVHBAPathExists; =20 =20 +# util/virpcivpd.h + +virPCIVPDKeywordResourceForEach; +virPCIVPDKeywordResourceNew; +virPCIVPDParse; +virPCIVPDParseVPDLargeResourceFields; +virPCIVPDParseVPDLargeResourceString; +virPCIVPDReadVPDBytes; +virPCIVPDResourceGetFieldValueFormat; +virPCIVPDResourceGetResourceType; +virPCIVPDResourceIsExpectedKeyword; +virPCIVPDResourceIsValidTextValue; +virPCIVPDStringResourceGetValue; +virPCIVPDStringResourceNew; + # util/virvsock.h virVsockAcquireGuestCid; virVsockSetGuestCid; diff --git a/src/util/meson.build b/src/util/meson.build index 05934f6841..24350a3e67 100644 --- a/src/util/meson.build +++ b/src/util/meson.build @@ -105,6 +105,7 @@ util_sources =3D [ 'virutil.c', 'viruuid.c', 'virvhba.c', + 'virpcivpd.c', 'virvsock.c', 'virxml.c', ] diff --git a/src/util/virpcivpd.c b/src/util/virpcivpd.c new file mode 100644 index 0000000000..849ea0570c --- /dev/null +++ b/src/util/virpcivpd.c @@ -0,0 +1,755 @@ +/* + * 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 + * . + */ + +#include + +#ifdef __linux__ +# include +#endif + +#define LIBVIRT_VIRPCIVPDPRIV_H_ALLOW + +#include "virpcivpdpriv.h" +#include "virlog.h" +#include "virerror.h" +#include "virfile.h" + +#define VIR_FROM_THIS VIR_FROM_NONE + +VIR_LOG_INIT("util.pcivpd"); + +GType +vir_pci_vpd_resource_type_get_type(void) +{ + static GType resourceType; + + static const GEnumValue resourceTypes[] =3D { + {VIR_PCI_VPD_RESOURCE_TYPE_STRING, "String resource", "string"}, + {VIR_PCI_VPD_RESOURCE_TYPE_VPD_R, "Read-only resource", "vpd-r"}, + {VIR_PCI_VPD_RESOURCE_TYPE_VPD_W, "Read-write resource", "vpd-w"}, + {VIR_PCI_VPD_RESOURCE_TYPE_LAST, "last", "last"}, + {0, NULL, NULL}, + }; + + if (!resourceType) { + resourceType =3D g_enum_register_static("virPCIVPDResourceType", r= esourceTypes); + } + return resourceType; +} + +static gboolean +virPCIVPDResourceIsVendorKeyword(const gchar *keyword) +{ + return g_str_has_prefix(keyword, "V"); +} + +static gboolean +virPCIVPDResourceIsSystemKeyword(const gchar *keyword) +{ + /* Special-case the system-specific keywords since they share the "Y" = prefix with "YA". */ + return g_str_has_prefix(keyword, "Y") && STRNEQ(keyword, "YA"); +} + +static gchar * +virPCIVPDResourceGetKeywordPrefix(const gchar *keyword) +{ + g_autofree gchar *key =3D NULL; + + /* Keywords must have a length of 2 bytes. */ + if (strlen(keyword) !=3D 2) { + virReportError(VIR_ERR_INTERNAL_ERROR, _("The keyword length is no= t 2 bytes: %s"), keyword); + return NULL; + } else if (!(g_ascii_isalnum(keyword[0]) && g_ascii_isalnum(keyword[1]= ))) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("The keyword contains non-alphanumeric ASCII char= acters")); + return NULL; + } + /* Special-case the system-specific keywords since they share the "Y" = prefix with "YA". */ + if (virPCIVPDResourceIsSystemKeyword(keyword) || virPCIVPDResourceIsVe= ndorKeyword(keyword)) { + key =3D g_strndup(keyword, 1); + } else { + key =3D g_strndup(keyword, 2); + } + return g_steal_pointer(&key); +} + +/** + * virPCIVPDResourceGetFieldValueFormat: + * @keyword: A keyword for which to get a value type + * + * Returns: a virPCIVPDResourceFieldValueFormat value which specifies the = field value type for + * a provided keyword based on the static information from PCI(e) specs. + */ +virPCIVPDResourceFieldValueFormat +virPCIVPDResourceGetFieldValueFormat(const gchar *keyword) +{ + static GHashTable *fieldValueFormats; + g_autofree gchar *key =3D NULL; + gpointer keyVal =3D NULL; + virPCIVPDResourceFieldValueFormat format =3D VIR_PCI_VPD_RESOURCE_FIEL= D_VALUE_FORMAT_LAST; + + /* Keywords are expected to be 2 bytes in length which is defined in t= he specs. */ + if (strlen(keyword) !=3D 2) { + return VIR_PCI_VPD_RESOURCE_FIELD_VALUE_FORMAT_LAST; + } + + if (!fieldValueFormats) { + /* Initialize a hash table once with static format metadata coming= from the PCI(e) specs. + * The VPD format does not embed format metadata into the resource= records so it is not + * possible to do format discovery without static information. Leg= acy PICMIG keywords + * are not included. */ + fieldValueFormats =3D g_hash_table_new(g_str_hash, g_str_equal); + /* Extended capability. Contains binary data per PCI(e) specs. */ + g_hash_table_insert(fieldValueFormats, g_strdup("CP"), + GINT_TO_POINTER(VIR_PCI_VPD_RESOURCE_FIELD_VAL= UE_FORMAT_BINARY)); + /* Engineering Change Level of an Add-in Card. */ + g_hash_table_insert(fieldValueFormats, g_strdup("EC"), + GINT_TO_POINTER(VIR_PCI_VPD_RESOURCE_FIELD_VAL= UE_FORMAT_TEXT)); + /* Manufacture ID */ + g_hash_table_insert(fieldValueFormats, g_strdup("MN"), + GINT_TO_POINTER(VIR_PCI_VPD_RESOURCE_FIELD_VAL= UE_FORMAT_TEXT)); + /* Add-in Card Part Number */ + g_hash_table_insert(fieldValueFormats, g_strdup("PN"), + GINT_TO_POINTER(VIR_PCI_VPD_RESOURCE_FIELD_VAL= UE_FORMAT_TEXT)); + /* Checksum and Reserved */ + g_hash_table_insert(fieldValueFormats, g_strdup("RV"), + GINT_TO_POINTER(VIR_PCI_VPD_RESOURCE_FIELD_VAL= UE_FORMAT_RESVD)); + /* Remaining Read/Write Area */ + g_hash_table_insert(fieldValueFormats, g_strdup("RW"), + GINT_TO_POINTER(VIR_PCI_VPD_RESOURCE_FIELD_VAL= UE_FORMAT_RDWR)); + /* Serial Number */ + g_hash_table_insert(fieldValueFormats, g_strdup("SN"), + GINT_TO_POINTER(VIR_PCI_VPD_RESOURCE_FIELD_VAL= UE_FORMAT_TEXT)); + /* Asset Tag Identifier */ + g_hash_table_insert(fieldValueFormats, g_strdup("YA"), + GINT_TO_POINTER(VIR_PCI_VPD_RESOURCE_FIELD_VAL= UE_FORMAT_TEXT)); + /* This is a vendor specific item and the characters are alphanume= ric. The second + * character (x) of the keyword can be 0 through Z so only the fir= st one is stored. */ + g_hash_table_insert(fieldValueFormats, g_strdup("V"), + GINT_TO_POINTER(VIR_PCI_VPD_RESOURCE_FIELD_VAL= UE_FORMAT_TEXT)); + /* This is a system specific item and the characters are alphanume= ric. + * The second character (x) of the keyword can be 0 through 9 and = B through Z. */ + g_hash_table_insert(fieldValueFormats, g_strdup("Y"), + GINT_TO_POINTER(VIR_PCI_VPD_RESOURCE_FIELD_VAL= UE_FORMAT_TEXT)); + } + + /* The system and vendor-specific keywords have a variable part - look= up + * the prefix significant for determining the value format. */ + key =3D virPCIVPDResourceGetKeywordPrefix(keyword); + if (key) { + keyVal =3D g_hash_table_lookup(fieldValueFormats, key); + if (keyVal) { + format =3D GPOINTER_TO_INT(keyVal); + } + } + return format; +} + +/** + * virPCIVPDResourceIsExpectedKeyword: + * @keyword: A keyword to assess. + * @readOnly: A parameter indicating whether the resource is read-only or = not. + * + * Returns: a boolean indicating whether this keyword is expected to be in= a + * read-only or read-write keyword resource or not. The expectations are b= ased + * on the keywords specified in relevant sections of PCI(e) specifications + * ("I.3. VPD Definitions" in PCI specs, "6.28.1 VPD Format" PCIe 4.0). + */ +gboolean +virPCIVPDResourceIsExpectedKeyword(const gchar *keyword, gboolean readOnly) +{ + g_autofree gchar *key =3D NULL; + + static const char *expectedReadOnlyKeys[] =3D { + "CP", "EC", "FG", "LC", "MN", + "PG", "PN", "RV", "SN", "V", NULL + }; + static const char *expectedReadWriteKeys[] =3D { "V", "Y", "YA", "RW",= NULL }; + + key =3D virPCIVPDResourceGetKeywordPrefix(keyword); + if (!key) { + return false; + } + if (readOnly) { + return g_strv_contains(expectedReadOnlyKeys, key); + } else { + return g_strv_contains(expectedReadWriteKeys, key); + } +} + +/** + * virPCIVPDResourceIsValidTextValue: + * @value: A NULL-terminated string to assess. + * + * Returns: a boolean indicating whether this value is a valid string reso= urce + * value or text field value. The expectations are based on the keywords s= pecified + * in relevant sections of PCI(e) specifications + * ("I.3. VPD Definitions" in PCI specs, "6.28.1 VPD Format" PCIe 4.0). + */ +gboolean +virPCIVPDResourceIsValidTextValue(const gchar *value) +{ + /* + * The PCI(e) specs mention alphanumeric characters when talking about= text fields + * and the string resource but also include spaces and dashes in the p= rovided example. + * Dots, commas, equal signs have also been observed in values used by= major device vendors. + * The specs do not specify a full set of allowed code points and for = Libvirt it is important + * to keep values in the ranges allowed within XML elements (mainly ex= cluding less-than, + * greater-than and ampersand). + */ + if (!g_regex_match_simple("^[a-zA-Z0-9\\-_\\s,.:=3D]*$", value, 0, 0))= { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("The provided value contains invalid characters: = %s"), value); + return false; + } + return true; +} + +/* virPCIVPDResource */ + +typedef enum { + PROP_RESOURCE_TYPE =3D 1, + N_PROPERTIES +} virPCIVPDResourceProperty; + +typedef struct _virPCIVPDResourcePrivate { + GObject parent; + virPCIVPDResourceType resource_type; +} virPCIVPDResourcePrivate; + + +static void vir_pci_vpd_resource_class_init(virPCIVPDResourceClass *klass); +static void vir_pci_vpd_resource_init(virPCIVPDResource *res); +static void virPCIVPDResourceFinalize(GObject *object); + +G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE(virPCIVPDResource, vir_pci_vpd_resourc= e, G_TYPE_OBJECT); +static void +vir_pci_vpd_resource_init(virPCIVPDResource *self) +{ + virPCIVPDResourcePrivate *priv; + + priv =3D vir_pci_vpd_resource_get_instance_private(self); + priv->resource_type =3D VIR_PCI_VPD_RESOURCE_TYPE_LAST; +} + +static void +virPCIVPDResourceSetProperty(GObject *object, guint propertyId, + const GValue *value, GParamSpec *pspec) +{ + virPCIVPDResource *self =3D VIR_PCI_VPD_RESOURCE(object); + virPCIVPDResourcePrivate *priv; + + priv =3D vir_pci_vpd_resource_get_instance_private(self); + + switch ((virPCIVPDResourceProperty) propertyId) { + case PROP_RESOURCE_TYPE: + priv->resource_type =3D g_value_get_enum(value); + break; + case N_PROPERTIES: + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propertyId, pspec); + break; + } +} + +static void +virPCIVPDResourceGetProperty(GObject *object, guint propertyId, + GValue *value, GParamSpec *pspec) +{ + virPCIVPDResource *self =3D VIR_PCI_VPD_RESOURCE(object); + virPCIVPDResourcePrivate *priv; + + priv =3D vir_pci_vpd_resource_get_instance_private(self); + + switch ((virPCIVPDResourceProperty) propertyId) { + case PROP_RESOURCE_TYPE: + g_value_set_enum(value, priv->resource_type); + break; + case N_PROPERTIES: + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propertyId, pspec); + break; + } +} + +static void +vir_pci_vpd_resource_class_init(virPCIVPDResourceClass *klass) +{ + GObjectClass *obj_class =3D G_OBJECT_CLASS(klass); + + obj_class->set_property =3D virPCIVPDResourceSetProperty; + obj_class->get_property =3D virPCIVPDResourceGetProperty; + + g_object_class_install_property(obj_class, PROP_RESOURCE_TYPE, + g_param_spec_enum("resource_type", "Re= source type", + "A VPD resource type= per PCI(e) specifications.", + VIR_TYPE_PCI_VPD_RES= OURCE_TYPE, + VIR_PCI_VPD_RESOURCE= _TYPE_LAST, + G_PARAM_CONSTRUCT_ON= LY | G_PARAM_READWRITE)); + obj_class->finalize =3D virPCIVPDResourceFinalize; +} + +static void +virPCIVPDResourceFinalize(GObject *object) +{ + G_OBJECT_CLASS(vir_pci_vpd_resource_parent_class)->finalize(object); +} + +GEnumValue * +virPCIVPDResourceGetResourceType(virPCIVPDResource *res) +{ + GValue gval =3D G_VALUE_INIT; + GEnumValue *enumVal; + GEnumClass *class; + + g_value_init(&gval, VIR_TYPE_PCI_VPD_RESOURCE_TYPE); + g_object_get_property(G_OBJECT(res), "resource_type", &gval); + + class =3D g_type_class_ref(VIR_TYPE_PCI_VPD_RESOURCE_TYPE); + + enumVal =3D g_enum_get_value(class, g_value_get_enum(&gval)); + g_type_class_unref(class); + return enumVal; +} + + +/* virPCIVPDStringResource */ + +struct _virPCIVPDStringResource { + virPCIVPDResource parent; + gchar *value; +}; + +G_DEFINE_TYPE(virPCIVPDStringResource, vir_pci_vpd_string_resource, VIR_TY= PE_PCI_VPD_RESOURCE); + +static void vir_pci_vpd_string_resource_class_init(virPCIVPDStringResource= Class *klass); +static void vir_pci_vpd_string_resource_init(virPCIVPDStringResource *res); +static void virPCIVPDStringResourceFinalize(GObject *object); + +static void +vir_pci_vpd_string_resource_init(virPCIVPDStringResource *res) +{ + res->value =3D NULL; +} + +static void +vir_pci_vpd_string_resource_class_init(virPCIVPDStringResourceClass *klass) +{ + GObjectClass *obj =3D G_OBJECT_CLASS(klass); + + obj->finalize =3D virPCIVPDStringResourceFinalize; +} + +static void +virPCIVPDStringResourceFinalize(GObject *object) +{ + virPCIVPDStringResource *res =3D VIR_PCI_VPD_STRING_RESOURCE(object); + + g_free(res->value); + G_OBJECT_CLASS(vir_pci_vpd_resource_parent_class)->finalize(object); +} + +virPCIVPDStringResource * +virPCIVPDStringResourceNew(gchar *value) +{ + g_autoptr(virPCIVPDStringResource) res =3D NULL; + + res =3D VIR_PCI_VPD_STRING_RESOURCE(g_object_new(VIR_TYPE_PCI_VPD_STRI= NG_RESOURCE, + "resource_type", + VIR_PCI_VPD_RESOURCE_TY= PE_STRING, NULL)); + res->value =3D value; + return g_steal_pointer(&res); +} + +const gchar * +virPCIVPDStringResourceGetValue(const virPCIVPDStringResource *res) +{ + return res->value; +} + +/* virPCIVPDKeywordResource */ + +struct _virPCIVPDKeywordResource { + virPCIVPDResource parent; + GTree *resource_fields; +}; + +G_DEFINE_TYPE(virPCIVPDKeywordResource, vir_pci_vpd_keyword_resource, VIR_= TYPE_PCI_VPD_RESOURCE); + +static void vir_pci_vpd_keyword_resource_class_init(virPCIVPDKeywordResour= ceClass *klass); +static void vir_pci_vpd_keyword_resource_init(virPCIVPDKeywordResource *re= s); +static void virPCIVPDKeywordResourceFinalize(GObject *object); + +static void +vir_pci_vpd_keyword_resource_class_init(virPCIVPDKeywordResourceClass *kla= ss) +{ + GObjectClass *obj =3D G_OBJECT_CLASS(klass); + + obj->finalize =3D virPCIVPDKeywordResourceFinalize; +} + +static void +vir_pci_vpd_keyword_resource_init(virPCIVPDKeywordResource *res) +{ + res->resource_fields =3D NULL; +} + +static void +virPCIVPDKeywordResourceFinalize(GObject *object) +{ + virPCIVPDKeywordResource *res =3D VIR_PCI_VPD_KEYWORD_RESOURCE(object); + + g_tree_destroy(g_steal_pointer(&res->resource_fields)); + G_OBJECT_CLASS(vir_pci_vpd_resource_parent_class)->finalize(object); +} + +virPCIVPDKeywordResource * +virPCIVPDKeywordResourceNew(GTree *resourceFields, bool readOnly) +{ + g_autoptr(virPCIVPDKeywordResource) res =3D NULL; + virPCIVPDResourceType t; + + t =3D readOnly ? VIR_PCI_VPD_RESOURCE_TYPE_VPD_R : VIR_PCI_VPD_RESOURC= E_TYPE_VPD_W; + /* + * Create an instance and set a property specifying its VPD resource t= ype taking the + * readOnly parameter into account. + */ + res =3D VIR_PCI_VPD_KEYWORD_RESOURCE(g_object_new(VIR_TYPE_PCI_VPD_KEY= WORD_RESOURCE, + "resource_type", t, NU= LL)); + res->resource_fields =3D g_tree_ref(resourceFields); + return g_steal_pointer(&res); +} + +void +virPCIVPDKeywordResourceForEach(virPCIVPDKeywordResource *res, GTraverseFu= nc func, + gpointer userData) +{ + g_tree_foreach(res->resource_fields, func, userData); +} + +#ifdef __linux__ + +/** + * virPCIVPDReadVPDBytes: + * @vpdFileFd: A file descriptor associated with a file containing PCI dev= ice VPD. + * @buf: An allocated buffer to use for storing VPD bytes read. + * @count: The number of bytes to read from the VPD file descriptor. + * @offset: The offset at which bytes need to be read. + * @csum: A pointer to a byte containing the current checksum value. Mutat= ed by this function. + * + * Returns: the number of VPD bytes read from the specified file descripto= r. The csum value is + * also modified as bytes are read. If an error occurs while reading data = from the VPD file + * descriptor, it is reported and -1 is returned to the caller. If EOF is = occurred, 0 is returned + * to the caller. + */ +size_t +virPCIVPDReadVPDBytes(int vpdFileFd, uint8_t *buf, size_t count, off_t off= set, uint8_t *csum) +{ + ssize_t numRead =3D pread(vpdFileFd, buf, count, offset); + + if (numRead =3D=3D -1) { + VIR_DEBUG("Unable to read %zu bytes at offset %ld from fd: %d", co= unt, offset, vpdFileFd); + } else if (numRead) { + /* + * Update the checksum for every byte read. Per the PCI(e) specs + * the checksum is correct if the sum of all bytes in VPD from + * VPD address 0 up to and including the VPD-R RV field's first + * data byte is zero. + */ + while (count--) { + *csum +=3D *buf; + buf++; + } + } + return numRead; +} + +/** + * virPCIVPDParseVPDLargeResourceFields: + * @vpdFileFd: A file descriptor associated with a file containing PCI dev= ice VPD. + * @resPos: A position where the resource data bytes begin in a file descr= iptor. + * @resDataLen: A length of the data portion of a resource. + * @readOnly: A boolean showing whether the resource type is VPD-R or VPD-= W. + * @csum: A pointer to a 1-byte checksum. + * + * Returns: a pointer to a VPDResource which needs to be freed by the call= er or + * NULL if getting it failed for some reason. + */ +virPCIVPDKeywordResource * +virPCIVPDParseVPDLargeResourceFields(int vpdFileFd, uint16_t resPos, uint1= 6_t resDataLen, + bool readOnly, uint8_t *csum) +{ + g_autofree char *fieldKeyword =3D NULL; + g_autofree char *fieldValue =3D NULL; + virPCIVPDResourceFieldValueFormat fieldFormat =3D VIR_PCI_VPD_RESOURCE= _FIELD_VALUE_FORMAT_LAST; + + /* A buffer of up to one resource record field size (plus a zero byte)= is needed. */ + g_autofree uint8_t *buf =3D g_malloc0(PCI_VPD_MAX_FIELD_SIZE + 1); + uint16_t fieldDataLen =3D 0, bytesToRead =3D 0; + uint16_t fieldPos =3D resPos; + + g_autoptr(GTree) resFieldTree =3D g_tree_new_full((GCompareDataFunc)g_= strcmp0, NULL, + g_free, g_free); + bool hasChecksum =3D false; + + while (fieldPos + 3 < resPos + resDataLen) { + /* Keyword resources consist of keywords (2 ASCII bytes per the sp= ec) and 1-byte length. */ + if (virPCIVPDReadVPDBytes(vpdFileFd, buf, 3, fieldPos, csum) !=3D = 3) { + /* Invalid field encountered which means the resource itself i= s invalid too. Report + * That VPD has invalid format and bail. */ + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Could not read a resource field header - VPD= has invalid format")); + return NULL; + } + fieldDataLen =3D buf[2]; + /* Change the position to the field's data portion skipping the ke= yword and length bytes. */ + fieldPos +=3D 3; + fieldKeyword =3D g_strndup((char *)buf, 2); + + if (!virPCIVPDResourceIsExpectedKeyword(fieldKeyword, readOnly)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Unexpected keyword encountered - VPD has inv= alid format")); + return NULL; + } + fieldFormat =3D virPCIVPDResourceGetFieldValueFormat(fieldKeyword); + + if (fieldFormat =3D=3D VIR_PCI_VPD_RESOURCE_FIELD_VALUE_FORMAT_RES= VD) { + if (!fieldDataLen) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("The RV field has a 0 length - VPD has in= valid format.")); + return NULL; + } + /* Only need one byte to be read and accounted towards the che= cksum calculation. */ + bytesToRead =3D 1; + } else { + bytesToRead =3D fieldDataLen; + } + if (virPCIVPDReadVPDBytes(vpdFileFd, buf, bytesToRead, fieldPos, c= sum) !=3D bytesToRead) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Could not parse a resource field data - VPD = has invalid format")); + return NULL; + } + /* Advance the position to the first byte of the next field. */ + fieldPos +=3D fieldDataLen; + + if (fieldFormat =3D=3D VIR_PCI_VPD_RESOURCE_FIELD_VALUE_FORMAT_TEX= T) { + /* Trim whitespace around a retrieved value and set it to be a= field's value. Cases + * where unnecessary whitespace was present around a field val= ue have been encountered + * in the wild. + */ + fieldValue =3D g_strstrip(g_strndup((char *)buf, fieldDataLen)= ); + if (!virPCIVPDResourceIsValidTextValue(fieldValue)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Field value contains invalid characters"= )); + return NULL; + } + } else if (fieldFormat =3D=3D VIR_PCI_VPD_RESOURCE_FIELD_VALUE_FOR= MAT_RESVD) { + if (*csum) { + /* All bytes up to and including the checksum byte should = add up to 0. */ + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Checksum v= alidation has failed")); + return NULL; + } + hasChecksum =3D true; + g_free(g_steal_pointer(&fieldKeyword)); + g_free(g_steal_pointer(&fieldValue)); + continue; + } else if (fieldFormat =3D=3D VIR_PCI_VPD_RESOURCE_FIELD_VALUE_FOR= MAT_RDWR || + fieldFormat =3D=3D VIR_PCI_VPD_RESOURCE_FIELD_VALUE_FOR= MAT_LAST) { + /* Skip fields which are not known in advance or the read-writ= e space field since + * it is not being used. */ + g_free(g_steal_pointer(&fieldKeyword)); + g_free(g_steal_pointer(&fieldValue)); + continue; + } else { + fieldValue =3D g_malloc(fieldDataLen); + memcpy(fieldValue, buf, fieldDataLen); + } + /* At this point it is determined that the keyword is expected and= field format + * is known and acceptable. */ + g_tree_insert(resFieldTree, + g_steal_pointer(&fieldKeyword), g_steal_pointer(&fie= ldValue)); + } + if (readOnly && !hasChecksum) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _ + ("A VPD-R resource does not contain the mandatory R= V field with a checksum")); + return NULL; + } + return virPCIVPDKeywordResourceNew(g_steal_pointer(&resFieldTree), rea= dOnly); +} + +/** + * virPCIVPDParseVPDLargeResourceString: + * @vpdFileFd: A file descriptor associated with a file containing PCI dev= ice VPD. + * @resPos: A position where the resource data bytes begin in a file descr= iptor. + * @resDataLen: A length of the data portion of a resource. + * @csum: A pointer to a 1-byte checksum. + * + * Returns: a pointer to a VPDResource which needs to be freed by the call= er or + * NULL if getting it failed for some reason. + */ +virPCIVPDStringResource * +virPCIVPDParseVPDLargeResourceString(int vpdFileFd, uint16_t resPos, + uint16_t resDataLen, uint8_t *csum) +{ + g_autofree char *resValue =3D NULL; + + /* The resource value is not NULL-terminated so add one more byte. */ + g_autofree char *buf =3D g_malloc0(resDataLen + 1); + + if (virPCIVPDReadVPDBytes(vpdFileFd, (uint8_t *) buf, resDataLen, + resPos, csum) !=3D resDataLen) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Could not read a part of a resource - VPD has in= valid format")); + return NULL; + } + resValue =3D g_strdup(g_strstrip(buf)); + if (!virPCIVPDResourceIsValidTextValue(resValue)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("The string resource has invalid characters in it= s value")); + return NULL; + } + return virPCIVPDStringResourceNew(g_steal_pointer(&resValue)); +} + +/** + * virPCIVPDParse: + * @vpdFileFd: a file descriptor associated with a file containing PCI dev= ice VPD. + * + * Parse a PCI device's Vital Product Data (VPD) contained in a file descr= iptor. + * + * Returns: a pointer to a GList of VPDResource types which needs to be fr= eed by the caller or + * NULL if getting it failed for some reason. + */ +GList * +virPCIVPDParse(int vpdFileFd) +{ + /* A checksum which is calculated as a sum of all bytes from VPD byte = 0 up to + * the checksum byte in the RV field's value. The RV field is only pre= sent in the + * VPD-R resource and the checksum byte there is the first byte of the= field's value. + * The checksum byte in RV field is actually a two's complement of the= sum of all bytes + * of VPD that come before it so adding the two together must produce = 0 if data + * was not corrupted and VPD storage is intact. + */ + uint8_t csum =3D 0; + uint8_t headerBuf[2]; + + g_autolist(virPCIVPDResource) resList =3D NULL; + uint16_t resPos =3D 0, resDataLen; + uint8_t tag =3D 0; + bool endResReached =3D false, hasReadOnlyRes =3D false; + + g_autoptr(virPCIVPDResource) res =3D NULL; + + while (resPos <=3D PCI_VPD_ADDR_MASK) { + /* Read the resource data type tag. */ + if (virPCIVPDReadVPDBytes(vpdFileFd, &tag, 1, resPos, &csum) !=3D = 1) { + break; + } + /* 0x80 =3D=3D 0b10000000 - the large resource data type flag. */ + if (tag & PCI_VPD_LARGE_RESOURCE_FLAG) { + if (resPos > PCI_VPD_ADDR_MASK + 1 - 3) { + /* Bail if the large resource starts at the position where= the end tag should be. */ + break; + } + /* Read the two length bytes of the large resource record. */ + if (virPCIVPDReadVPDBytes(vpdFileFd, headerBuf, 2, resPos + 1,= &csum) !=3D 2) { + break; + } + resDataLen =3D headerBuf[0] + (headerBuf[1] << 8); + /* Change the position to the byte following the tag and lengt= h bytes. */ + resPos +=3D 3; + } else { + /* Handle a small resource record. + * 0xxxxyyy & 00000111, where xxxx - resource data type bits, = yyy - length bits. */ + resDataLen =3D tag & 7; + /* 0xxxxyyy >> 3 =3D=3D 0000xxxx */ + tag >>=3D 3; + /* Change the position to the byte past the byte containing ta= g and length bits. */ + resPos +=3D 1; + } + if (tag =3D=3D PCI_VPD_RESOURCE_END_TAG) { + /* Stop VPD traversal since the end tag was encountered. */ + endResReached =3D true; + break; + } + if (resDataLen > PCI_VPD_ADDR_MASK + 1 - resPos) { + /* Bail if the resource is too long to fit into the VPD addres= s space. */ + break; + } + + switch (tag) { + /* Large resource type which is also a string: 0x80 | 0x02= =3D 0x82 */ + case PCI_VPD_LARGE_RESOURCE_FLAG | PCI_VPD_STRING_RESOURCE_FLA= G: + res =3D + (virPCIVPDResource *) virPCIVPDParseVPDLargeResourceSt= ring(vpdFileFd, resPos, + = resDataLen, &csum); + break; + /* Large resource type which is also a VPD-R: 0x80 | 0x10 = =3D=3D 0x90 */ + case PCI_VPD_LARGE_RESOURCE_FLAG | PCI_VPD_READ_ONLY_LARGE_RES= OURCE_FLAG: + res =3D + (virPCIVPDResource *) virPCIVPDParseVPDLargeResourceFi= elds(vpdFileFd, resPos, + = resDataLen, true, + = &csum); + /* Encountered the VPD-R tag. The resource record parsing = also validates + * the presence of the required checksum in the RV field. = */ + hasReadOnlyRes =3D true; + break; + /* Large resource type which is also a VPD-W: 0x80 | 0x11 = =3D=3D 0x91 */ + case PCI_VPD_LARGE_RESOURCE_FLAG | PCI_VPD_READ_WRITE_LARGE_RE= SOURCE_FLAG: + res =3D + (virPCIVPDResource *) virPCIVPDParseVPDLargeResourceFi= elds(vpdFileFd, resPos, + = resDataLen, false, + = &csum); + break; + default: + /* While we cannot parse unknown resource types, they can = still be skipped + * based on the header and data length. */ + VIR_DEBUG("Encountered an unexpected VPD resource tag: %#x= ", tag); + resPos +=3D resDataLen; + continue; + } + + if (!res) { + VIR_DEBUG("Encountered an invalid VPD"); + return NULL; + } + + resList =3D g_list_append(resList, g_steal_pointer(&res)); + /* Continue processing other resource records. */ + resPos +=3D resDataLen; + } + + if (VIR_CLOSE(vpdFileFd) < 0) { + virReportSystemError(errno, _("Unable to close the VPD file, fd: %= d"), vpdFileFd); + return NULL; + } + if (!hasReadOnlyRes) { + VIR_DEBUG("Encountered an invalid VPD: does not have a VPD-R recor= d"); + return NULL; + } else if (!(endResReached && g_list_length(resList) > 0)) { + /* Does not have an end tag or there are not any other resources. = */ + VIR_DEBUG("Encountered an invalid VPD"); + return NULL; + } + return g_steal_pointer(&resList); +} + +#endif /* __linux__ */ diff --git a/src/util/virpcivpd.h b/src/util/virpcivpd.h new file mode 100644 index 0000000000..7327806df3 --- /dev/null +++ b/src/util/virpcivpd.h @@ -0,0 +1,117 @@ +/* + * 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 + * . + */ + +#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 (betwee= n 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 poi= nted 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 w= ithin 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 +#define VIR_TYPE_PCI_VPD_RESOURCE_TYPE vir_pci_vpd_resource_type_get_type() + +G_BEGIN_DECLS; + +typedef enum { + VIR_PCI_VPD_RESOURCE_FIELD_VALUE_FORMAT_TEXT =3D 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; + +typedef enum { + VIR_PCI_VPD_RESOURCE_TYPE_STRING =3D 1, + VIR_PCI_VPD_RESOURCE_TYPE_VPD_R, + VIR_PCI_VPD_RESOURCE_TYPE_VPD_W, + VIR_PCI_VPD_RESOURCE_TYPE_LAST +} virPCIVPDResourceType; + +GType vir_pci_vpd_resource_type_get_type(void); + +virPCIVPDResourceFieldValueFormat virPCIVPDResourceGetFieldValueFormat(con= st gchar *value); + +#define VIR_TYPE_PCI_VPD_RESOURCE vir_pci_vpd_resource_get_type() +G_DECLARE_DERIVABLE_TYPE(virPCIVPDResource, vir_pci_vpd_resource, VIR, PCI= _VPD_RESOURCE, GObject); +struct _virPCIVPDResourceClass { + GObjectClass parent_class; +}; + +/* Glib 2.59 adds proper support for g_autolist for derivable types + * see 86c073dba9d82ef3f1bc3d3116b058b9b5c3b1eb. At the time of writing + * 2.56 is the minimum version used by Libvirt. Without the below code + * compilation errors will occur. + */ +#if !GLIB_CHECK_VERSION(2, 59, 0) +typedef GList *virPCIVPDResource_listautoptr; +static inline void +glib_listautoptr_cleanup_virPCIVPDResource(GList **_l) +{ + g_list_free_full(*_l, (GDestroyNotify)g_object_unref); +} +#endif + +GEnumValue *virPCIVPDResourceGetResourceType(virPCIVPDResource *res); + +#define VIR_TYPE_PCI_VPD_STRING_RESOURCE vir_pci_vpd_string_resource_get_t= ype() +G_DECLARE_FINAL_TYPE(virPCIVPDStringResource, vir_pci_vpd_string_resource,= VIR, + PCI_VPD_STRING_RESOURCE, virPCIVPDResource); + +struct _virPCIVPDStringResourceClass { + virPCIVPDResourceClass parent_class; +}; + +virPCIVPDStringResource *virPCIVPDStringResourceNew(gchar *value); + +const gchar *virPCIVPDStringResourceGetValue(const virPCIVPDStringResource= *res); + +#define VIR_TYPE_PCI_VPD_KEYWORD_RESOURCE vir_pci_vpd_keyword_resource_get= _type() +G_DECLARE_FINAL_TYPE(virPCIVPDKeywordResource, vir_pci_vpd_keyword_resourc= e, VIR, + PCI_VPD_KEYWORD_RESOURCE, virPCIVPDResource); +virPCIVPDKeywordResource *virPCIVPDKeywordResourceNew(GTree *resourceField= s, bool readOnly); + +void virPCIVPDKeywordResourceForEach(virPCIVPDKeywordResource *res, GTrave= rseFunc func, + gpointer userData); + + +GList *virPCIVPDParse(int vpdFileFd); + +G_END_DECLS diff --git a/src/util/virpcivpdpriv.h b/src/util/virpcivpdpriv.h new file mode 100644 index 0000000000..9a3cc075c1 --- /dev/null +++ b/src/util/virpcivpdpriv.h @@ -0,0 +1,45 @@ +/* + * virpcivpdpriv.h: helper APIs for working with the PCI/PCIe VPD capabili= ty + * + * 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 + * . + */ + +#ifndef LIBVIRT_VIRPCIVPDPRIV_H_ALLOW +# error "virpcivpdpriv.h may only be included by virpcivpd.c or test suite= s" +#endif /* LIBVIRT_VIRPCIVPDPRIV_H_ALLOW */ + +#pragma once + +#include "virpcivpd.h" + +#ifdef __linux__ + +size_t +virPCIVPDReadVPDBytes(int vpdFileFd, uint8_t *buf, size_t count, off_t off= set, uint8_t *csum); + +virPCIVPDKeywordResource *virPCIVPDParseVPDLargeResourceFields(int vpdFile= Fd, uint16_t resPos, + uint16_t re= sDataLen, + bool readOn= ly, uint8_t *csum); + +virPCIVPDStringResource *virPCIVPDParseVPDLargeResourceString(int vpdFileF= d, uint16_t resPos, + uint16_t res= DataLen, + uint8_t *csu= m); + +#endif /* __linux__ */ + +gboolean virPCIVPDResourceIsExpectedKeyword(const gchar *keyword, gboolean= readOnly); +gboolean virPCIVPDResourceIsValidTextValue(const gchar *value); diff --git a/tests/meson.build b/tests/meson.build index dfbc2c01e2..1948c07ae3 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -336,6 +336,7 @@ tests +=3D [ { 'name': 'virtimetest' }, { 'name': 'virtypedparamtest' }, { 'name': 'viruritest' }, + { 'name': 'virpcivpdtest' }, { 'name': 'vshtabletest', 'link_with': [ libvirt_shell_lib ] }, { 'name': 'virmigtest' }, ] diff --git a/tests/testutils.c b/tests/testutils.c index d071abd6d7..35d9d0645d 100644 --- a/tests/testutils.c +++ b/tests/testutils.c @@ -1143,3 +1143,43 @@ virTestStablePath(const char *path) =20 return g_strdup(path); } + +#ifdef __linux__ +/** + * virCreateAnonymousFile: + * @data: a pointer to data to be written into a new file. + * @len: the length of data to be written (in bytes). + * + * Create a fake fd, write initial data to it. + * + */ +int +virCreateAnonymousFile(const uint8_t *data, size_t len) +{ + int fd =3D -1; + char path[] =3D abs_builddir "testutils-memfd-XXXXXX"; + /* A temp file is used since not all supported distributions support m= emfd. */ + if ((fd =3D g_mkstemp_full(path, O_RDWR | O_CLOEXEC, S_IRUSR | S_IWUSR= )) < 0) { + return fd; + } + g_unlink(path); + + if (ftruncate(fd, len) !=3D 0) { + VIR_TEST_DEBUG("%s: %s", "failed to ftruncate an anonymous file", + g_strerror(errno)); + goto cleanup; + } + if (safewrite(fd, data, len) !=3D len) { + VIR_TEST_DEBUG("%s: %s", "failed to write to an anonymous file", + g_strerror(errno)); + goto cleanup; + } + return fd; + cleanup: + if (VIR_CLOSE(fd) < 0) { + VIR_TEST_DEBUG("%s: %s", "failed to close an anonymous file", + g_strerror(errno)); + } + return -1; +} +#endif diff --git a/tests/testutils.h b/tests/testutils.h index 27d135fc02..13a154a5af 100644 --- a/tests/testutils.h +++ b/tests/testutils.h @@ -173,3 +173,7 @@ int testCompareDomXML2XMLFiles(virCaps *caps, =20 char * virTestStablePath(const char *path); + +#ifdef __linux__ +int virCreateAnonymousFile(const uint8_t *data, size_t len); +#endif diff --git a/tests/virpcivpdtest.c b/tests/virpcivpdtest.c new file mode 100644 index 0000000000..09cba4b1a3 --- /dev/null +++ b/tests/virpcivpdtest.c @@ -0,0 +1,705 @@ +/* + * 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 + * . + */ + +#include + +#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"); + + +static int +testPCIVPDStringResourceBasic(const void *data G_GNUC_UNUSED) +{ + g_autoptr(virPCIVPDStringResource) strRes =3D NULL; + g_autofree char *val =3D g_strdup("testval"); + GEnumValue *resType =3D NULL; + + strRes =3D virPCIVPDStringResourceNew(g_steal_pointer(&val)); + + resType =3D virPCIVPDResourceGetResourceType((virPCIVPDResource *)strR= es); + + if (resType->value !=3D VIR_PCI_VPD_RESOURCE_TYPE_STRING) { + VIR_DEBUG("Expected '%d' got '%d'", VIR_PCI_VPD_RESOURCE_TYPE_STRI= NG, resType->value); + return -1; + } + if (!STREQ_NULLABLE(virPCIVPDStringResourceGetValue(strRes), "testval"= )) { + return -1; + } + + return 0; +} + +typedef struct _TestPCIVPDKeyValue { + const char *keyword; + const char *value; +} TestPCIVPDKeyValue; + +static gboolean +testPCIVPDGetOneField(gpointer *key, gpointer *val, gpointer userData) +{ + TestPCIVPDKeyValue *res =3D userData; + + res->keyword =3D (const char *)key; + res->value =3D (const char *)val; + + return true; +} + +static int +testPCIVPDKeywordResourceBasic(const void *data G_GNUC_UNUSED) +{ + g_autoptr(virPCIVPDKeywordResource) kwRes =3D NULL; + g_autoptr(GTree) resFieldTree =3D g_tree_new_full((GCompareDataFunc)g_= strcmp0, NULL, + g_free, g_free); + GEnumValue *resType =3D NULL; + TestPCIVPDKeyValue testKeyVal; + + g_tree_insert(resFieldTree, g_strdup("SN"), g_strdup("DEADBEEFCAFE")); + + kwRes =3D virPCIVPDKeywordResourceNew(g_steal_pointer(&resFieldTree), = true); + resType =3D virPCIVPDResourceGetResourceType((virPCIVPDResource *)kwRe= s); + + if (resType->value !=3D VIR_PCI_VPD_RESOURCE_TYPE_VPD_R) { + return -1; + } + + virPCIVPDKeywordResourceForEach(kwRes, (GTraverseFunc) testPCIVPDGetOn= eField, &testKeyVal); + if (STRNEQ_NULLABLE(testKeyVal.keyword, "SN") || + STRNEQ_NULLABLE(testKeyVal.value, "DEADBEEFCAFE")) { + VIR_DEBUG("Unexpected keyword resource field encountered: %s: %s; = expected: %s: %s", + testKeyVal.keyword, testKeyVal.value, "SN", "DEADBEEFCAF= E"); + return -1; + } + + g_object_unref(g_steal_pointer(&kwRes)); + + resFieldTree =3D g_tree_new_full((GCompareDataFunc)g_strcmp0, NULL, g_= free, g_free); + g_tree_insert(resFieldTree, g_strdup("SN"), g_strdup("CAFEDEADBEEF")); + + kwRes =3D virPCIVPDKeywordResourceNew(g_steal_pointer(&resFieldTree), = false); + resType =3D virPCIVPDResourceGetResourceType((virPCIVPDResource *)kwRe= s); + + if (resType->value !=3D VIR_PCI_VPD_RESOURCE_TYPE_VPD_W) { + return -1; + } + + virPCIVPDKeywordResourceForEach(kwRes, (GTraverseFunc) testPCIVPDGetOn= eField, &testKeyVal); + if (STRNEQ_NULLABLE(testKeyVal.keyword, "SN") || + STRNEQ_NULLABLE(testKeyVal.value, "CAFEDEADBEEF")) { + VIR_DEBUG("Unexpected keyword resource field encountered: %s: %s; = expected: %s: %s", + testKeyVal.keyword, testKeyVal.value, "SN", "DEADBEEFCAF= E"); + return -1; + } + + return 0; +} + +typedef struct _TestPCIVPDExpectedString { + const char *keyword; + gboolean expected; +} TestPCIVPDExpectedString; + +/* + * testPCIVPDIsExpectedKeywordReadOnly: + * + * Test expected keyword validation. Static metadata about expected + * keywords is taken from the PCI(e) standards should be respected + * and only keywords known to have a given type need to be returned. + * */ +static int +testPCIVPDIsExpectedKeywordReadOnly(const void *data G_GNUC_UNUSED) +{ + size_t i =3D 0; + + const TestPCIVPDExpectedString readOnlyCases[] =3D { + {"CP", true}, + {"EC", true}, + {"FG", true}, + {"LC", true}, + {"MN", true}, + {"PG", true}, + {"PN", true}, + {"RV", true}, + {"SN", true}, + {"V0", true}, + {"VG", true}, + {"IV", false}, + {"YA", false}, + {"YB", false}, + {"RW", false}, + /* Empty: */ + {"", false}, + /* 1-byte: */ + {"Y", false}, + /* 3-byte: */ + {"FOO", false}, + /* Not present in the spec: */ + {"42", false}, + {"4A", false}, + }; + for (i =3D 0; i < sizeof(readOnlyCases) / sizeof(readOnlyCases[0]); ++= i) { + if (virPCIVPDResourceIsExpectedKeyword(readOnlyCases[i].keyword, t= rue) !=3D + readOnlyCases[i].expected) { + return -1; + } + } + return 0; +} + +/* + * testPCIVPDIsExpectedKeywordReadWrite: + * + * Test expected keyword validation. Static metadata about expected + * keywords is taken from the PCI(e) standards should be respected + * and only keywords known to have a given type need to be returned. + * */ +static int +testPCIVPDIsExpectedKeywordReadWrite(const void *data G_GNUC_UNUSED) +{ + size_t i =3D 0; + + const TestPCIVPDExpectedString readWriteCases[] =3D { + {"CP", false}, + {"EC", false}, + {"FG", false}, + {"LC", false}, + {"MN", false}, + {"PG", false}, + {"PN", false}, + {"RV", false}, + {"SN", false}, + {"V0", true}, + {"VG", true}, + {"IV", false}, + {"YA", true}, + {"YB", true}, + {"RW", true}, + /* Empty: */ + {"", false}, + /* 1-byte: */ + {"Y", false}, + /* 3-byte: */ + {"FOO", false}, + /* Not present in the spec: */ + {"42", false}, + {"4A", false}, + }; + for (i =3D 0; i < sizeof(readWriteCases) / sizeof(readWriteCases[0]); = ++i) { + if (virPCIVPDResourceIsExpectedKeyword(readWriteCases[i].keyword, = false) !=3D + readWriteCases[i].expected) { + return -1; + } + } + return 0; +} + + + +/* + * testPCIVPDIsValidTextValue: + * + * Test expected text value validation. Static metadata about possible val= ues is taken + * from the PCI(e) standards and based on some real-world hardware example= s. + * */ +static int +testPCIVPDIsValidTextValue(const void *data G_GNUC_UNUSED) +{ + size_t i =3D 0; + + const TestPCIVPDExpectedString textValueCases[] =3D { + /* 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=3DMLNX:CSKU=3DV2:UUID=3DV3:PCI=3DV0:MODL=3DBF2H332A", tru= e}, + /* Dashes */ + {"MBF2H332A-AEEOT", true}, + {"under_score_example", true}, + {"", true}, + {";", false}, + {"\\42", false}, + {"/42", false}, + }; + for (i =3D 0; i < sizeof(textValueCases) / sizeof(textValueCases[0]); = ++i) { + if (virPCIVPDResourceIsValidTextValue(textValueCases[i].keyword) != =3D + 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 =3D 0; + + const TestPCIVPDExpectedFieldValueFormat valueFormatCases[] =3D { + {"SN", 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}, + {"4", 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}, + /* 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 =3D 0; i < sizeof(valueFormatCases) / sizeof(valueFormatCases[0= ]); ++i) { + if (virPCIVPDResourceGetFieldValueFormat(valueFormatCases[i].keywo= rd) !=3D + 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, 0= x16, 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 =3D -1; + g_autofree uint8_t *buf =3D NULL; + uint8_t csum =3D 0; + size_t readBytes =3D 0; + size_t dataLen =3D 0; + + /* An example of a valid VPD record with one VPD-R resource and 2 fiel= ds. */ + uint8_t fullVPDExample[] =3D { + VPD_STRING_RESOURCE_EXAMPLE_HEADER, VPD_STRING_RESOURCE_EXAMPLE_DA= TA, + VPD_R_FIELDS_EXAMPLE_HEADER, VPD_R_FIELDS_EXAMPLE_DATA, + PCI_VPD_RESOURCE_END_VAL + }; + dataLen =3D sizeof(fullVPDExample) / sizeof(uint8_t) - 2; + buf =3D g_malloc0(dataLen); + + fd =3D virCreateAnonymousFile(fullVPDExample, dataLen); + + readBytes =3D virPCIVPDReadVPDBytes(fd, buf, dataLen, 0, &csum); + + if (readBytes !=3D dataLen) { + virReportError(VIR_ERR_INTERNAL_ERROR, + "The number of bytes read %zu is lower than expecte= d %zu ", + readBytes, dataLen); + return -1; + } + + if (csum) { + virReportError(VIR_ERR_INTERNAL_ERROR, + "The sum of all VPD bytes up to and including the c= hecksum byte" + "is equal to zero: 0x%02x", csum); + return -1; + } + return 0; +} + +static int +testVirPCIVPDParseVPDStringResource(const void *opaque G_GNUC_UNUSED) +{ + int fd =3D -1; + uint8_t csum =3D 0; + size_t dataLen =3D 0; + + g_autoptr(virPCIVPDStringResource) strRes =3D NULL; + const gchar *expectedValue =3D "testname", *actualValue =3D NULL; + + const uint8_t stringResExample[] =3D { + VPD_STRING_RESOURCE_EXAMPLE_DATA + }; + + dataLen =3D sizeof(stringResExample) / sizeof(uint8_t); + fd =3D virCreateAnonymousFile(stringResExample, dataLen); + strRes =3D virPCIVPDParseVPDLargeResourceString(fd, 0, dataLen, &csum); + VIR_FORCE_CLOSE(fd); + + actualValue =3D virPCIVPDStringResourceGetValue(strRes); + if (STRNEQ(expectedValue, actualValue)) { + virReportError(VIR_ERR_INTERNAL_ERROR, + "Unexpected string resource value: %s, expected: %s= ", + actualValue, expectedValue); + return -1; + } + return 0; +} + + +typedef struct _TestPCIVPDExpectedFieldsData { + GHashTable *expectedFields; + size_t field_count; +} TestPCIVPDExpectedFieldsData; + +static gboolean +testPCIVPDValidateField(gpointer *key, gpointer *val, gpointer userData) +{ + TestPCIVPDExpectedFieldsData *expectedData =3D userData; + const char *actualKey =3D (const char *)key; + const char *expectedValue =3D NULL, *actualValue =3D (const char *)val; + + expectedValue =3D g_hash_table_lookup(expectedData->expectedFields, (c= har *)key); + if (!expectedValue) { + virReportError(VIR_ERR_INTERNAL_ERROR, "Unexpected key encountered= : %s", actualKey); + return true; + } else if (STRNEQ(expectedValue, actualValue)) { + virReportError(VIR_ERR_INTERNAL_ERROR, + "Unexpected value for key %s encountered: %s expect= ed %s", actualKey, + actualValue, expectedValue); + return true; + } + expectedData->field_count++; + return false; +} + +static gboolean +testVirPCIVPDValidateKeywordResource(virPCIVPDKeywordResource *kwRes, + GHashTable *expectedFields) +{ + size_t expectedCount =3D g_hash_table_size(expectedFields); + TestPCIVPDExpectedFieldsData expectedFieldsData; + + expectedFieldsData.expectedFields =3D expectedFields; + expectedFieldsData.field_count =3D 0; + + virPCIVPDKeywordResourceForEach(kwRes, (GTraverseFunc)testPCIVPDValida= teField, + &expectedFieldsData); + /* + * Check that the number of actual fields observed equals the expected= number + * which also validates that the "RV" field is not present after parsi= ng. + */ + if (expectedFieldsData.field_count !=3D expectedCount) { + virReportError(VIR_ERR_INTERNAL_ERROR, "Unexpected number of field= s observed: %zu vs %zu", + expectedFieldsData.field_count, expectedCount); + return false; + } + return true; +} + + +static int +testVirPCIVPDParseFullVPD(const void *opaque G_GNUC_UNUSED) +{ + int fd =3D -1; + const int EXPECTED_RES_LIST_LENGTH =3D 3; + size_t dataLen =3D 0; + + g_autolist(virPCIVPDResource) resList =3D NULL; + GList *listIter =3D NULL; + virPCIVPDResource *res =3D NULL; + + const uint8_t fullVPDExample[] =3D { + VPD_STRING_RESOURCE_EXAMPLE_HEADER, VPD_STRING_RESOURCE_EXAMPLE_DA= TA, + VPD_R_FIELDS_EXAMPLE_HEADER, VPD_R_FIELDS_EXAMPLE_DATA, + VPD_W_FIELDS_EXAMPLE_HEADER, VPD_W_EXAMPLE_FIELDS, + PCI_VPD_RESOURCE_END_VAL + }; + const gchar *expectedValue =3D "testname", *actualValue =3D NULL; + + g_autoptr(GHashTable) expectedReadOnlyKw =3D NULL, expectedReadWriteKw= =3D NULL; + + expectedReadOnlyKw =3D g_hash_table_new_full(g_str_hash, g_str_equal, = g_free, g_free); + g_hash_table_insert(expectedReadOnlyKw, g_strdup("PN"), g_strdup("42")= ); + g_hash_table_insert(expectedReadOnlyKw, g_strdup("EC"), g_strdup("4242= ")); + g_hash_table_insert(expectedReadOnlyKw, g_strdup("VA"), g_strdup("EX")= ); + + expectedReadWriteKw =3D g_hash_table_new_full(g_str_hash, g_str_equal,= g_free, g_free); + g_hash_table_insert(expectedReadWriteKw, g_strdup("VZ"), g_strdup("42"= )); + g_hash_table_insert(expectedReadWriteKw, g_strdup("YA"), g_strdup("ID4= 2")); + g_hash_table_insert(expectedReadWriteKw, g_strdup("YF"), g_strdup("EX"= )); + g_hash_table_insert(expectedReadWriteKw, g_strdup("YE"), g_strdup("")); + + dataLen =3D sizeof(fullVPDExample) / sizeof(uint8_t); + fd =3D virCreateAnonymousFile(fullVPDExample, dataLen); + resList =3D virPCIVPDParse(fd); + VIR_FORCE_CLOSE(fd); + + if (!resList) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + "The resource list is empty after parsing which is = unexpected"); + return -1; + } else if (g_list_length(resList) !=3D EXPECTED_RES_LIST_LENGTH) { + virReportError(VIR_ERR_INTERNAL_ERROR, + "The resource list length is not equal to: %d", EXP= ECTED_RES_LIST_LENGTH); + return -1; + } + + for (listIter =3D resList; listIter; listIter =3D g_list_next(listIter= )) { + res =3D listIter->data; + switch (virPCIVPDResourceGetResourceType(res)->value) { + case VIR_PCI_VPD_RESOURCE_TYPE_STRING: + actualValue =3D virPCIVPDStringResourceGetValue((virPCIVPD= StringResource *)res); + if (STRNEQ(expectedValue, actualValue)) { + virReportError(VIR_ERR_INTERNAL_ERROR, + "Unexpected string resource value: %s, = expected: %s", + actualValue, expectedValue); + return -1; + } + break; + case VIR_PCI_VPD_RESOURCE_TYPE_VPD_R: + if (!testVirPCIVPDValidateKeywordResource((virPCIVPDKeywor= dResource *)res, + expectedReadOnly= Kw)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + "Keyword resource fields do not match t= he expected ones"); + return -1; + } + break; + case VIR_PCI_VPD_RESOURCE_TYPE_VPD_W: + if (!testVirPCIVPDValidateKeywordResource((virPCIVPDKeywor= dResource *)res, + expectedReadWrit= eKw)) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + "Keyword resource fields do not match t= he expected ones"); + return -1; + } + break; + default: + virReportError(VIR_ERR_INTERNAL_ERROR, + "%s", "unexpected resource type encountered= "); + return -1; + } + } + return 0; +} + +static int +testVirPCIVPDParseFullVPDInvalid(const void *opaque G_GNUC_UNUSED) +{ + int fd =3D -1; + size_t dataLen =3D 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, 0= x05, 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, 0= x05, 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, 0= x14, 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_KEY \ + VPD_STRING_RESOURCE_EXAMPLE_HEADER, \ + VPD_STRING_RESOURCE_EXAMPLE_DATA, \ + PCI_VPD_LARGE_RESOURCE_FLAG | PCI_VPD_READ_ONLY_LARGE_RESOURCE_FLAG, 0= x1B, 0x00, \ + VPD_R_EXAMPLE_FIELDS, \ + 'R', 'W', 0x02, 0x00, 0x00, \ + 'R', 'V', 0x02, 0x8F, 0x00, \ + PCI_VPD_RESOURCE_END_VAL + +# define VPD_R_INVALID_KEY_FIRST_BYTE \ + VPD_STRING_RESOURCE_EXAMPLE_HEADER, \ + VPD_STRING_RESOURCE_EXAMPLE_DATA, \ + PCI_VPD_LARGE_RESOURCE_FLAG | PCI_VPD_READ_ONLY_LARGE_RESOURCE_FLAG, 0= x1B, 0x00, \ + VPD_R_EXAMPLE_FIELDS, \ + 0x07, 'A', 0x02, 0x00, 0x00, \ + 'R', 'V', 0x02, 0xE2, 0x00, \ + PCI_VPD_RESOURCE_END_VAL + +# define VPD_R_INVALID_KEY_SECOND_BYTE \ + VPD_STRING_RESOURCE_EXAMPLE_HEADER, \ + VPD_STRING_RESOURCE_EXAMPLE_DATA, \ + PCI_VPD_LARGE_RESOURCE_FLAG | PCI_VPD_READ_ONLY_LARGE_RESOURCE_FLAG, 0= x1B, 0x00, \ + VPD_R_EXAMPLE_FIELDS, \ + 'V', 0x07, 0x02, 0x00, 0x00, \ + 'R', 'V', 0x02, 0xCD, 0x00, \ + PCI_VPD_RESOURCE_END_VAL + +# define VPD_R_INVALID_FIELD_VALUE \ + VPD_STRING_RESOURCE_EXAMPLE_HEADER, \ + VPD_STRING_RESOURCE_EXAMPLE_DATA, \ + PCI_VPD_LARGE_RESOURCE_FLAG | PCI_VPD_READ_ONLY_LARGE_RESOURCE_FLAG, 0= x0A, 0x00, \ + 'S', 'N', 0x02, 0x04, 0x02, \ + 'R', 'V', 0x02, 0x28, 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, 0= x0A, 0x00, \ + 'S', 'N', 0x02, 0x04, 0x02, \ + 'R', 'V', 0x02, 0x8A, 0x00, \ + PCI_VPD_RESOURCE_END_VAL + +# define TEST_INVALID_VPD(invalidVPD) \ + do { \ + const uint8_t testCase[] =3D { invalidVPD }; \ + dataLen =3D sizeof(testCase) / sizeof(uint8_t); \ + fd =3D virCreateAnonymousFile(testCase, dataLen); \ + if (virPCIVPDParse(fd)) { \ + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", \ + "Successfully parsed an invalid VPD - this is not expe= cted"); \ + 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_KEY); + TEST_INVALID_VPD(VPD_R_INVALID_KEY_FIRST_BYTE); + TEST_INVALID_VPD(VPD_R_INVALID_KEY_SECOND_BYTE); + TEST_INVALID_VPD(VPD_R_INVALID_FIELD_VALUE); + TEST_INVALID_VPD(VPD_INVALID_STRING_RESOURCE_VALUE); + + return 0; +} + +static int +mymain(void) +{ + int ret =3D 0; + + if (virTestRun("String resource (basic test) ", testPCIVPDStringResour= ceBasic, NULL) < 0) + ret =3D -1; + if (virTestRun("Keyword resource (basic test) ", testPCIVPDKeywordReso= urceBasic, NULL) < 0) + ret =3D -1; + if (virTestRun("Expected read-only resource keywords ", testPCIVPDIsEx= pectedKeywordReadOnly, + NULL) < 0) + ret =3D -1; + if (virTestRun("Expected read-write resource keywords ", testPCIVPDIsE= xpectedKeywordReadWrite, + NULL) < 0) + ret =3D -1; + if (virTestRun("Valid text values ", testPCIVPDIsValidTextValue, NULL)= < 0) + ret =3D -1; + if (virTestRun("Determining a field value format by a key ", + testPCIVPDGetFieldValueFormat, NULL) < 0) + ret =3D -1; + if (virTestRun("Reading VPD bytes ", testVirPCIVPDReadVPDBytes, NULL) = < 0) + ret =3D -1; + if (virTestRun("Parsing VPD string resources ", testVirPCIVPDParseVPDS= tringResource, NULL) < 0) + ret =3D -1; + if (virTestRun("Parsing VPD resources from a full VPD ", testVirPCIVPD= ParseFullVPD, NULL) < 0) + ret =3D -1; + if (virTestRun("Parsing invalid VPD records ", testVirPCIVPDParseFullV= PDInvalid, NULL) < 0) + ret =3D -1; + + return ret =3D=3D 0 ? EXIT_SUCCESS : EXIT_FAILURE; +} + +VIR_TEST_MAIN(mymain) +#endif --=20 2.30.2 From nobody Sun May 19 07:16:18 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zohomail.com: domain of redhat.com designates 170.10.133.124 as permitted sender) client-ip=170.10.133.124; envelope-from=libvir-list-bounces@redhat.com; helo=us-smtp-delivery-124.mimecast.com; Authentication-Results: mx.zohomail.com; spf=pass (zohomail.com: domain of redhat.com designates 170.10.133.124 as permitted sender) smtp.mailfrom=libvir-list-bounces@redhat.com; dmarc=fail(p=none dis=none) header.from=canonical.com Return-Path: Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by mx.zohomail.com with SMTPS id 1632771078726235.55566307539164; Mon, 27 Sep 2021 12:31:18 -0700 (PDT) Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-538-XnDrUBbjNUC4VwddWe365Q-1; Mon, 27 Sep 2021 15:31:15 -0400 Received: from smtp.corp.redhat.com (int-mx04.intmail.prod.int.phx2.redhat.com [10.5.11.14]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id B33FE1922040; Mon, 27 Sep 2021 19:31:10 +0000 (UTC) Received: from colo-mx.corp.redhat.com (colo-mx02.intmail.prod.int.phx2.redhat.com [10.5.11.21]) by smtp.corp.redhat.com (Postfix) with ESMTPS id D42DA5DF2F; Mon, 27 Sep 2021 19:31:06 +0000 (UTC) Received: from lists01.pubmisc.prod.ext.phx2.redhat.com (lists01.pubmisc.prod.ext.phx2.redhat.com [10.5.19.33]) by colo-mx.corp.redhat.com (Postfix) with ESMTP id 6D1194E58F; Mon, 27 Sep 2021 19:31:04 +0000 (UTC) Received: from smtp.corp.redhat.com (int-mx05.intmail.prod.int.rdu2.redhat.com [10.11.54.5]) by lists01.pubmisc.prod.ext.phx2.redhat.com (8.13.8/8.13.8) with ESMTP id 18RJV2CX014407 for ; Mon, 27 Sep 2021 15:31:03 -0400 Received: by smtp.corp.redhat.com (Postfix) id DA13AAEC8C; Mon, 27 Sep 2021 19:31:02 +0000 (UTC) Received: from mimecast-mx02.redhat.com (mimecast03.extmail.prod.ext.rdu2.redhat.com [10.11.55.19]) by smtp.corp.redhat.com (Postfix) with ESMTPS id C8395AEC9F for ; Mon, 27 Sep 2021 19:31:00 +0000 (UTC) Received: from us-smtp-1.mimecast.com (us-smtp-2.mimecast.com [205.139.110.61]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id 7A3D7811E80 for ; Mon, 27 Sep 2021 19:31:00 +0000 (UTC) Received: from smtp-relay-internal-0.canonical.com (smtp-relay-internal-0.canonical.com [185.125.188.122]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-58-UW6_DwjTN3W87LhSx9cK5A-1; Mon, 27 Sep 2021 15:30:57 -0400 Received: from mail-lf1-f72.google.com (mail-lf1-f72.google.com [209.85.167.72]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by smtp-relay-internal-0.canonical.com (Postfix) with ESMTPS id AC9C2406E1 for ; Mon, 27 Sep 2021 19:30:56 +0000 (UTC) Received: by mail-lf1-f72.google.com with SMTP id g9-20020a0565123b8900b003f33a027130so16763284lfv.18 for ; Mon, 27 Sep 2021 12:30:56 -0700 (PDT) Received: from ws.lan.d-node.is ([95.165.29.203]) by smtp.gmail.com with ESMTPSA id a8sm1688090lfr.256.2021.09.27.12.30.55 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 27 Sep 2021 12:30:55 -0700 (PDT) X-MC-Unique: XnDrUBbjNUC4VwddWe365Q-1 X-MC-Unique: UW6_DwjTN3W87LhSx9cK5A-1 X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=RczWYusCWa+tZK08q3h5GJHeBWUWfDPHw6S4I06D+5w=; b=ochD0vbDF42pLUC7K1uZRMXta2bd/Uf0EUQUf7jk6EOqEEOSMpA/lUNdTlOXGCkc7u OIgTXcYtR7zc4p0TfO4ULOfgYgVyFWW3cCvLKMFOQoBUKcLunAatbIFdosJJwHFKGJ25 cD+EfOH64Fg9lEqVnC2gXUlALgKmhJ+Lu4xGM26XYY1VdKOHRrSwVFhd/BtqNawlCnYO ckBE8h9+qwPqt0HfH0Wx6c0raI7YzWKM22zGRE11o/ZyhUwJ3O9iior1NlI7OrxzObRV X05kIdqUpXDexyLbBaTnhTe5brtfyRhmp80sRkI5xXepGWj+2SGTmfNJRun4h0EqhUZY ktjQ== X-Gm-Message-State: AOAM530ouJcSsc3zl7gyNdBWKGSxkMBQwU4qWSY5FnfSorB91mqTW8DD NsatP4h62aOKuXBa+udpXND0i2KHY4GAtnUDPwkSx9CZW58uTB5HIZw3IiQPDqhY7gxq8LEyndS B9pln6vvv0zKWNSHjAuQl6yNXu0ZZrKpbWg== X-Received: by 2002:a19:750e:: with SMTP id y14mr1387532lfe.63.1632771055757; Mon, 27 Sep 2021 12:30:55 -0700 (PDT) X-Google-Smtp-Source: ABdhPJzl61CF1QOzHUSk0d+bGApAAXUDeMKs/AWT4Wew71T9BYhlNYMv+7TYnoh9ZhblKf3DSIWDdA== X-Received: by 2002:a19:750e:: with SMTP id y14mr1387513lfe.63.1632771055575; Mon, 27 Sep 2021 12:30:55 -0700 (PDT) From: Dmitrii Shcherbakov To: libvir-list@redhat.com, dmitrii.shcherbakov@canonical.com Subject: [libvirt PATCH v5 2/7] News: Add a PCI VPD parser Date: Mon, 27 Sep 2021 22:30:48 +0300 Message-Id: <20210927193053.109029-3-dmitrii.shcherbakov@canonical.com> In-Reply-To: <20210927193053.109029-1-dmitrii.shcherbakov@canonical.com> References: <20210927193053.109029-1-dmitrii.shcherbakov@canonical.com> MIME-Version: 1.0 X-Mimecast-Impersonation-Protect: Policy=CLT - Impersonation Protection Definition; Similar Internal Domain=false; Similar Monitored External Domain=false; Custom External Domain=false; Mimecast External Domain=false; Newly Observed Domain=false; Internal User Name=false; Custom Display Name List=false; Reply-to Address Mismatch=false; Targeted Threat Dictionary=false; Mimecast Threat Dictionary=false; Custom Threat Dictionary=false X-Scanned-By: MIMEDefang 2.79 on 10.11.54.5 X-MIME-Autoconverted: from quoted-printable to 8bit by lists01.pubmisc.prod.ext.phx2.redhat.com id 18RJV2CX014407 X-loop: libvir-list@redhat.com X-BeenThere: libvir-list@redhat.com X-Mailman-Version: 2.1.12 Precedence: junk List-Id: Development discussions about the libvirt library & tools List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: libvir-list-bounces@redhat.com Errors-To: libvir-list-bounces@redhat.com X-Scanned-By: MIMEDefang 2.79 on 10.5.11.14 Authentication-Results: relay.mimecast.com; auth=pass smtp.auth=CUSA124A263 smtp.mailfrom=libvir-list-bounces@redhat.com X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Content-Transfer-Encoding: quoted-printable X-ZM-MESSAGEID: 1632771079764100001 Content-Type: text/plain; charset="utf-8" Signed-off-by: Dmitrii Shcherbakov --- NEWS.rst | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/NEWS.rst b/NEWS.rst index fd20e50d18..a4178e505c 100644 --- a/NEWS.rst +++ b/NEWS.rst @@ -29,6 +29,14 @@ v7.8.0 (unreleased) active. This information can also be retrieved with the new virsh comm= and ``nodedev-info``. =20 + * virpcivpd: Add a PCI VPD parser + + A parser for the standard PCI/PCIe VPD ("I.3. VPD Definitions" in PCI = 2.2+ + and an equivalent definition in "6.28.1 VPD Format" PCIe 4.0) was added + along with relevant types to represent PCI VPD in memory. This + functionality got added for Linux only at this point (kernels above + v2.6.26 have support for exposing VPD via sysfs). + * **Improvements** =20 * **Bug fixes** --=20 2.30.2 From nobody Sun May 19 07:16:18 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zohomail.com: domain of redhat.com designates 216.205.24.124 as permitted sender) client-ip=216.205.24.124; envelope-from=libvir-list-bounces@redhat.com; helo=us-smtp-delivery-124.mimecast.com; Authentication-Results: mx.zohomail.com; spf=pass (zohomail.com: domain of redhat.com designates 216.205.24.124 as permitted sender) smtp.mailfrom=libvir-list-bounces@redhat.com; dmarc=fail(p=none dis=none) header.from=canonical.com Return-Path: Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [216.205.24.124]) by mx.zohomail.com with SMTPS id 1632771079363334.50575576227686; Mon, 27 Sep 2021 12:31:19 -0700 (PDT) Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-541-bBRSLpFGMamPoUjpYQYHfA-1; Mon, 27 Sep 2021 15:31:16 -0400 Received: from smtp.corp.redhat.com (int-mx08.intmail.prod.int.phx2.redhat.com [10.5.11.23]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id A3783192203E; Mon, 27 Sep 2021 19:31:10 +0000 (UTC) Received: from colo-mx.corp.redhat.com (colo-mx02.intmail.prod.int.phx2.redhat.com [10.5.11.21]) by smtp.corp.redhat.com (Postfix) with ESMTPS id 4DB9E19C79; Mon, 27 Sep 2021 19:31:10 +0000 (UTC) Received: from lists01.pubmisc.prod.ext.phx2.redhat.com (lists01.pubmisc.prod.ext.phx2.redhat.com [10.5.19.33]) by colo-mx.corp.redhat.com (Postfix) with ESMTP id EDF524EA29; Mon, 27 Sep 2021 19:31:08 +0000 (UTC) Received: from smtp.corp.redhat.com (int-mx06.intmail.prod.int.rdu2.redhat.com [10.11.54.6]) by lists01.pubmisc.prod.ext.phx2.redhat.com (8.13.8/8.13.8) with ESMTP id 18RJV7tJ014430 for ; Mon, 27 Sep 2021 15:31:07 -0400 Received: by smtp.corp.redhat.com (Postfix) id 1DDE02144B33; Mon, 27 Sep 2021 19:31:07 +0000 (UTC) Received: from mimecast-mx02.redhat.com (mimecast06.extmail.prod.ext.rdu2.redhat.com [10.11.55.22]) by smtp.corp.redhat.com (Postfix) with ESMTPS id 1586A2144B31 for ; Mon, 27 Sep 2021 19:31:00 +0000 (UTC) Received: from us-smtp-1.mimecast.com (us-smtp-2.mimecast.com [207.211.31.81]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id E7C2218A01A9 for ; Mon, 27 Sep 2021 19:30:59 +0000 (UTC) Received: from smtp-relay-internal-0.canonical.com (smtp-relay-internal-0.canonical.com [185.125.188.122]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-431-zeukNQN7OXKB8gmOwvyHGw-1; Mon, 27 Sep 2021 15:30:57 -0400 Received: from mail-lf1-f70.google.com (mail-lf1-f70.google.com [209.85.167.70]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by smtp-relay-internal-0.canonical.com (Postfix) with ESMTPS id D26894079D for ; Mon, 27 Sep 2021 19:30:56 +0000 (UTC) Received: by mail-lf1-f70.google.com with SMTP id a28-20020a056512021c00b003f5883dcd4bso16755472lfo.1 for ; Mon, 27 Sep 2021 12:30:56 -0700 (PDT) Received: from ws.lan.d-node.is ([95.165.29.203]) by smtp.gmail.com with ESMTPSA id a8sm1688090lfr.256.2021.09.27.12.30.55 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 27 Sep 2021 12:30:55 -0700 (PDT) X-MC-Unique: bBRSLpFGMamPoUjpYQYHfA-1 X-MC-Unique: zeukNQN7OXKB8gmOwvyHGw-1 X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=/LjAbWlB7il8GtfXDCbpvMPXWOYLelbzYpB6WmDq+mw=; b=Z7YQTVhlggSgrnF8MDGv1IH43Ob4OQTJMuIuT6IBlx661J2bChc9OsV9jy6TQa2eh8 GQRRvlBNyHVmyLVDflfh2ydyVmdvLW12aR0JDoaGNSHLJi1jnZNJlI3PLoB3MDNNkjnR dlsGijNqLQiqpzRox/48sAwv5MKYXS12tGPmzXx4PmNOJmbfVoUQKuEP34FuKWKJF6uA u6YZeAla6GsnVtEVO9wiMisviYUfbat+FAgvrlYuL93PHC3HAqEcWV65FMvhCfconzNb KwSVBF9ZKD00vJhwk8/al/J2gK2VxvOwqeT9VkSwN6VqC1HGOc6BGXS7aRAPJ3vBWmNv ekiQ== X-Gm-Message-State: AOAM531419pzGJu8oqyA3Cw1g4RiBc7eY3RGB+d0FfDoW6nvtXdQm1MJ z011MfQaEcUMjBqQK1yfKwi/QT0qxKPp7zNzzRjObaDehYb+fSwvuJt0tCZZGoqy0kZYTO59gpl 1ZYNXWTe1RhfkzYsVux441tqD0pORCLVL3A== X-Received: by 2002:a05:6512:4008:: with SMTP id br8mr1434952lfb.23.1632771056256; Mon, 27 Sep 2021 12:30:56 -0700 (PDT) X-Google-Smtp-Source: ABdhPJzqwAx6D2idJAPAX2iSRHbEyR/z+geuP+wgowc5U8ytl113X/yu2ookCmpdCOv9omN60pZemA== X-Received: by 2002:a05:6512:4008:: with SMTP id br8mr1434934lfb.23.1632771056014; Mon, 27 Sep 2021 12:30:56 -0700 (PDT) From: Dmitrii Shcherbakov To: libvir-list@redhat.com, dmitrii.shcherbakov@canonical.com Subject: [libvirt PATCH v5 3/7] Add PCI VPD-related helper functions to virpci Date: Mon, 27 Sep 2021 22:30:49 +0300 Message-Id: <20210927193053.109029-4-dmitrii.shcherbakov@canonical.com> In-Reply-To: <20210927193053.109029-1-dmitrii.shcherbakov@canonical.com> References: <20210927193053.109029-1-dmitrii.shcherbakov@canonical.com> MIME-Version: 1.0 X-Mimecast-Impersonation-Protect: Policy=CLT - Impersonation Protection Definition; Similar Internal Domain=false; Similar Monitored External Domain=false; Custom External Domain=false; Mimecast External Domain=false; Newly Observed Domain=false; Internal User Name=false; Custom Display Name List=false; Reply-to Address Mismatch=false; Targeted Threat Dictionary=false; Mimecast Threat Dictionary=false; Custom Threat Dictionary=false X-Scanned-By: MIMEDefang 2.78 on 10.11.54.6 X-MIME-Autoconverted: from quoted-printable to 8bit by lists01.pubmisc.prod.ext.phx2.redhat.com id 18RJV7tJ014430 X-loop: libvir-list@redhat.com X-BeenThere: libvir-list@redhat.com X-Mailman-Version: 2.1.12 Precedence: junk List-Id: Development discussions about the libvirt library & tools List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: libvir-list-bounces@redhat.com Errors-To: libvir-list-bounces@redhat.com X-Scanned-By: MIMEDefang 2.84 on 10.5.11.23 Authentication-Results: relay.mimecast.com; auth=pass smtp.auth=CUSA124A263 smtp.mailfrom=libvir-list-bounces@redhat.com X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Content-Transfer-Encoding: quoted-printable X-ZM-MESSAGEID: 1632771081701100003 Content-Type: text/plain; charset="utf-8" Add helper functions to virpci to provide means of checking for a VPD file presence and for VPD resource retrieval using the PCI VPD parser. The added test assesses the basic functionality of VPD retrieval while the full parser is tested by virpcivpdtest. Signed-off-by: Dmitrii Shcherbakov --- src/libvirt_private.syms | 2 ++ src/util/virpci.c | 62 ++++++++++++++++++++++++++++++++++++++ src/util/virpci.h | 3 ++ tests/virpcimock.c | 30 +++++++++++++++++++ tests/virpcitest.c | 64 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 161 insertions(+) diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index bb9b380599..ebee46ce9b 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -2988,7 +2988,9 @@ virPCIDeviceGetReprobe; virPCIDeviceGetStubDriver; virPCIDeviceGetUnbindFromStub; virPCIDeviceGetUsedBy; +virPCIDeviceGetVPDResources; virPCIDeviceHasPCIExpressLink; +virPCIDeviceHasVPD; virPCIDeviceIsAssignable; virPCIDeviceIsPCIExpress; virPCIDeviceListAdd; diff --git a/src/util/virpci.c b/src/util/virpci.c index f307580a53..480d91c17f 100644 --- a/src/util/virpci.c +++ b/src/util/virpci.c @@ -37,6 +37,7 @@ #include "virkmod.h" #include "virstring.h" #include "viralloc.h" +#include "virpcivpd.h" =20 VIR_LOG_INIT("util.pci"); =20 @@ -2640,6 +2641,53 @@ virPCIGetVirtualFunctionInfo(const char *vf_sysfs_de= vice_path, return 0; } =20 + +gboolean +virPCIDeviceHasVPD(virPCIDevice *dev) +{ + g_autofree char *vpdPath =3D NULL; + vpdPath =3D virPCIFile(dev->name, "vpd"); + if (!virFileExists(vpdPath)) { + VIR_INFO("Device VPD file does not exist %s", vpdPath); + return false; + } else if (!virFileIsRegular(vpdPath)) { + VIR_WARN("VPD path does not point to a regular file %s", vpdPath); + return false; + } + return true; +} + +/** + * virPCIDeviceGetVPDResources: + * @dev: a PCI device to get a list of VPD resources for. + * + * Obtain resources stored in the PCI device's Vital Product Data (VPD). + * VPD is optional in both PCI Local Bus and PCIe specifications so there = is + * no guarantee it will be there for a particular device. + * + * Returns: a pointer to a list of VPDResource types which needs to be fre= ed by the caller or + * NULL if getting it failed for some reason. + */ +GList * +virPCIDeviceGetVPDResources(virPCIDevice *dev) +{ + g_autofree char *vpdPath =3D NULL; + int fd; + + vpdPath =3D virPCIFile(dev->name, "vpd"); + if (!virPCIDeviceHasVPD(dev)) { + virReportError(VIR_ERR_INTERNAL_ERROR, _("Device %s does not have = a VPD"), + virPCIDeviceGetName(dev)); + return NULL; + } + if ((fd =3D open(vpdPath, O_RDONLY)) < 0) { + virReportSystemError(-fd, + _("Failed to open a VPD file '%s'"), vpdPath); + return NULL; + } + return virPCIVPDParse(fd); +} + #else static const char *unsupported =3D N_("not supported on non-linux platform= s"); =20 @@ -2713,6 +2761,20 @@ virPCIGetVirtualFunctionInfo(const char *vf_sysfs_de= vice_path G_GNUC_UNUSED, virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _(unsupported)); return -1; } + +gboolean +virPCIDeviceHasVPD(virPCIDevice *dev) +{ + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _(unsupported)); + return NULL; +} + +GList * +virPCIDeviceGetVPDResources(virPCIDevice *dev) +{ + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _(unsupported)); + return NULL; +} #endif /* __linux__ */ =20 int diff --git a/src/util/virpci.h b/src/util/virpci.h index 9a3db6c6d8..a89561496b 100644 --- a/src/util/virpci.h +++ b/src/util/virpci.h @@ -269,6 +269,9 @@ int virPCIGetVirtualFunctionInfo(const char *vf_sysfs_d= evice_path, char **pfname, int *vf_index); =20 +gboolean virPCIDeviceHasVPD(virPCIDevice *dev); +GList * virPCIDeviceGetVPDResources(virPCIDevice *dev); + int virPCIDeviceUnbind(virPCIDevice *dev); int virPCIDeviceRebind(virPCIDevice *dev); int virPCIDeviceGetDriverPathAndName(virPCIDevice *dev, diff --git a/tests/virpcimock.c b/tests/virpcimock.c index d4d43aac51..e10ebce76f 100644 --- a/tests/virpcimock.c +++ b/tests/virpcimock.c @@ -18,6 +18,8 @@ =20 #include =20 +#include "virpcivpd.h" + #if defined(__linux__) || defined(__FreeBSD__) || defined(__APPLE__) # define VIR_MOCK_LOOKUP_MAIN # include "virmock.h" @@ -123,6 +125,13 @@ struct pciDeviceAddress { }; # define ADDR_STR_FMT "%04x:%02x:%02x.%u" =20 +struct pciVPD { + /* PCI VPD contents (binary, may contain NULLs), NULL if not present. = */ + const char *data; + /* VPD length in bytes. */ + size_t vpd_len; +}; + struct pciDevice { struct pciDeviceAddress addr; int vendor; @@ -131,6 +140,7 @@ struct pciDevice { int iommuGroup; const char *physfn; struct pciDriver *driver; /* Driver attached. NULL if attached to no= driver */ + struct pciVPD vpd; }; =20 struct fdCallback { @@ -537,6 +547,10 @@ pci_device_new_from_stub(const struct pciDevice *data) make_symlink(devpath, "physfn", tmp); } =20 + if (dev->vpd.data && dev->vpd.vpd_len) { + make_file(devpath, "vpd", dev->vpd.data, dev->vpd.vpd_len); + } + if (pci_device_autobind(dev) < 0) ABORT("Unable to bind: %s", devid); =20 @@ -942,6 +956,20 @@ static void init_env(void) { g_autofree char *tmp =3D NULL; + const char fullVPDExampleData[] =3D { + PCI_VPD_LARGE_RESOURCE_FLAG | PCI_VPD_STRING_RESOURCE_FLAG, 0x08, = 0x00, + 't', 'e', 's', 't', 'n', 'a', 'm', 'e', + PCI_VPD_LARGE_RESOURCE_FLAG | PCI_VPD_READ_ONLY_LARGE_RESOURCE_FLA= G, 0x16, 0x00, + 'P', 'N', 0x02, '4', '2', + 'E', 'C', 0x04, '4', '2', '4', '2', + 'V', 'A', 0x02, 'E', 'X', + 'R', 'V', 0x02, 0x31, 0x00, + PCI_VPD_RESOURCE_END_VAL + }; + struct pciVPD exampleVPD =3D { + .data =3D fullVPDExampleData, + .vpd_len =3D sizeof(fullVPDExampleData) / sizeof(fullVPDExampleDat= a[0]), + }; =20 if (!(fakerootdir =3D getenv("LIBVIRT_FAKE_ROOT_DIR"))) ABORT("Missing LIBVIRT_FAKE_ROOT_DIR env variable\n"); @@ -1008,6 +1036,8 @@ init_env(void) =20 MAKE_PCI_DEVICE("0000:01:00.0", 0x1cc1, 0x8201, 14, .klass =3D 0x01080= 2); MAKE_PCI_DEVICE("0000:02:00.0", 0x1cc1, 0x8201, 15, .klass =3D 0x01080= 2); + + MAKE_PCI_DEVICE("0000:03:00.0", 0x15b3, 0xa2d6, 16, .vpd =3D exampleVP= D); } =20 =20 diff --git a/tests/virpcitest.c b/tests/virpcitest.c index 6fe9b7d13a..c6bf12ba0b 100644 --- a/tests/virpcitest.c +++ b/tests/virpcitest.c @@ -17,6 +17,7 @@ */ =20 #include +#include "internal.h" =20 #include "testutils.h" =20 @@ -26,6 +27,7 @@ # include # include # include +# include =20 # define VIR_FROM_THIS VIR_FROM_NONE =20 @@ -328,6 +330,66 @@ testVirPCIDeviceUnbind(const void *opaque) return ret; } =20 + +static gboolean +testVirPCIGetOneResourceField(gpointer *key, gpointer *val, gpointer userD= ata) +{ + const char **fieldValue =3D (const char **)userData; + if (STREQ((const char*)key, "PN")) { + *fieldValue =3D (const char*)val; + return true; + } + return false; +} + +static int +testVirPCIDeviceGetVPDResources(const void *opaque) +{ + const struct testPCIDevData *data =3D opaque; + int ret =3D -1; + g_autofree virPCIDevice *dev =3D NULL; + virPCIDeviceAddress devAddr =3D {.domain =3D data->domain, .bus =3D da= ta->bus, + .slot =3D data->slot, .function =3D dat= a->function}; + g_autofree GList *resources =3D NULL; + const char* resValue =3D NULL; + const char* fieldValue =3D NULL; + virPCIVPDStringResource *strRes; + virPCIVPDKeywordResource *kwRes; + + dev =3D virPCIDeviceNew(&devAddr); + if (!dev) { + return ret; + } + + resources =3D virPCIDeviceGetVPDResources(dev); + + /* Here we simply check that 2 resources are present and their basic u= se. + * Full parser validation is done elsewhere. + */ + if (g_list_length(resources) !=3D 2) { + return ret; + } + + strRes =3D g_list_nth_data(resources, 0); + resValue =3D virPCIVPDStringResourceGetValue(strRes); + if (STRNEQ(resValue, "testname")) { + VIR_TEST_DEBUG("Unexpected string resource value: %s", resValue); + return ret; + } + kwRes =3D g_list_nth_data(resources, 1); + virPCIVPDKeywordResourceForEach(kwRes, + (GTraverseFunc)testVirPCIGetOneResourceField, &fieldValue); + + if (!fieldValue || STRNEQ(fieldValue, "42")) { + VIR_TEST_DEBUG("Could not retrieve an expected value from the Keyw= ord resource: %s", + resValue); + return ret; + } + + ret =3D 0; + return ret; +} + # define FAKEROOTDIRTEMPLATE abs_builddir "/fakerootdir-XXXXXX" =20 static int @@ -409,6 +471,8 @@ mymain(void) DO_TEST_PCI(testVirPCIDeviceReattachSingle, 0, 0x0a, 3, 0); DO_TEST_PCI_DRIVER(0, 0x0a, 3, 0, NULL); =20 + DO_TEST_PCI(testVirPCIDeviceGetVPDResources, 0, 0x03, 0, 0); + if (getenv("LIBVIRT_SKIP_CLEANUP") =3D=3D NULL) virFileDeleteTree(fakerootdir); =20 --=20 2.30.2 From nobody Sun May 19 07:16:18 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zohomail.com: domain of redhat.com designates 170.10.133.124 as permitted sender) client-ip=170.10.133.124; envelope-from=libvir-list-bounces@redhat.com; helo=us-smtp-delivery-124.mimecast.com; Authentication-Results: mx.zohomail.com; spf=pass (zohomail.com: domain of redhat.com designates 170.10.133.124 as permitted sender) smtp.mailfrom=libvir-list-bounces@redhat.com; dmarc=fail(p=none dis=none) header.from=canonical.com Return-Path: Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by mx.zohomail.com with SMTPS id 1632771098913570.8787377717631; Mon, 27 Sep 2021 12:31:38 -0700 (PDT) Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-366-wRmCjWeCO1WcDMlg_LRJ7Q-1; Mon, 27 Sep 2021 15:31:31 -0400 Received: from smtp.corp.redhat.com (int-mx05.intmail.prod.int.phx2.redhat.com [10.5.11.15]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id A8BF0100C612; Mon, 27 Sep 2021 19:31:24 +0000 (UTC) Received: from colo-mx.corp.redhat.com (colo-mx01.intmail.prod.int.phx2.redhat.com [10.5.11.20]) by smtp.corp.redhat.com (Postfix) with ESMTPS id 8A2A76A900; Mon, 27 Sep 2021 19:31:24 +0000 (UTC) Received: from lists01.pubmisc.prod.ext.phx2.redhat.com (lists01.pubmisc.prod.ext.phx2.redhat.com [10.5.19.33]) by colo-mx.corp.redhat.com (Postfix) with ESMTP id 5878F1809C84; Mon, 27 Sep 2021 19:31:24 +0000 (UTC) Received: from smtp.corp.redhat.com (int-mx04.intmail.prod.int.rdu2.redhat.com [10.11.54.4]) by lists01.pubmisc.prod.ext.phx2.redhat.com (8.13.8/8.13.8) with ESMTP id 18RJVAxZ014452 for ; Mon, 27 Sep 2021 15:31:10 -0400 Received: by smtp.corp.redhat.com (Postfix) id DF533202F310; Mon, 27 Sep 2021 19:31:09 +0000 (UTC) Received: from mimecast-mx02.redhat.com (mimecast06.extmail.prod.ext.rdu2.redhat.com [10.11.55.22]) by smtp.corp.redhat.com (Postfix) with ESMTPS id D4BB1202F326 for ; Mon, 27 Sep 2021 19:31:01 +0000 (UTC) Received: from us-smtp-1.mimecast.com (us-smtp-2.mimecast.com [207.211.31.81]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id 8B747185A794 for ; Mon, 27 Sep 2021 19:31:01 +0000 (UTC) Received: from smtp-relay-internal-1.canonical.com (smtp-relay-internal-1.canonical.com [185.125.188.123]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-603-qba6RMElOoaLsQl4S9IomQ-1; Mon, 27 Sep 2021 15:30:59 -0400 Received: from mail-lf1-f72.google.com (mail-lf1-f72.google.com [209.85.167.72]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by smtp-relay-internal-1.canonical.com (Postfix) with ESMTPS id 7B2D640291 for ; Mon, 27 Sep 2021 19:30:57 +0000 (UTC) Received: by mail-lf1-f72.google.com with SMTP id s28-20020a056512203c00b003f42015e912so16780057lfs.4 for ; Mon, 27 Sep 2021 12:30:57 -0700 (PDT) Received: from ws.lan.d-node.is ([95.165.29.203]) by smtp.gmail.com with ESMTPSA id a8sm1688090lfr.256.2021.09.27.12.30.56 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 27 Sep 2021 12:30:56 -0700 (PDT) X-MC-Unique: wRmCjWeCO1WcDMlg_LRJ7Q-1 X-MC-Unique: qba6RMElOoaLsQl4S9IomQ-1 X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=fqs7PSf1ZUMBvqD1HfgTgP3NJ/l2TiJ7Gldlr0fEuC0=; b=diLdcjuNSc8s8/ZQ8+H8eS/cnUTEz7iFnFSOpadHxCZrzMnS02AfuIpWo7hiRsn+KG RO760cZGQFXjeYQuDIwwl8ZWuEI4RHUAGK3PGszgsYr1fJalsJ5qa/xnEvZhbsQeBldp UzAtBsmfRSS/k4i/U4x3yVnsaFMLwN5QVoN+58oba/L8EZbm8FagLoC8qzgFhlkoOZxY SvM4bXpiGn5/YHD4Q2vYYkvE/ksFcCXXQ1WWeqYYaTvnkE43QHDIu9083ufHl5/IkcIx Yaev7cQWRQsWarUVEPPNTPGclnt7qmih1EYfFbCZ9p3wX9FBXlOhS/qiyn7AST07fOCj haFw== X-Gm-Message-State: AOAM533EcuJse16BQDwG4jkncv/HV+unpGV0A/gMZgg9Hkw8Tib7uggc skZONyoNOrGb5pK4eQlxR0rv16q7OAFWQH+UpqcQ1Jt1t4eFc/CWgBk4gHavOl1oiy3I9JlffWc zyhF7FAsaqoqBd9/21kGtPPXCIQe4TnieJQ== X-Received: by 2002:a2e:8953:: with SMTP id b19mr1539954ljk.335.1632771056629; Mon, 27 Sep 2021 12:30:56 -0700 (PDT) X-Google-Smtp-Source: ABdhPJyQxN4GuB6ZjemMcThs0R6KucoihFoLmbpazDVr/Ygc4A45u4Lof98/ZcOR5/yR1SQold2LLA== X-Received: by 2002:a2e:8953:: with SMTP id b19mr1539933ljk.335.1632771056421; Mon, 27 Sep 2021 12:30:56 -0700 (PDT) From: Dmitrii Shcherbakov To: libvir-list@redhat.com, dmitrii.shcherbakov@canonical.com Subject: [libvirt PATCH v5 4/7] News: Add PCI VPD-related helper functions to virpci Date: Mon, 27 Sep 2021 22:30:50 +0300 Message-Id: <20210927193053.109029-5-dmitrii.shcherbakov@canonical.com> In-Reply-To: <20210927193053.109029-1-dmitrii.shcherbakov@canonical.com> References: <20210927193053.109029-1-dmitrii.shcherbakov@canonical.com> MIME-Version: 1.0 X-Mimecast-Impersonation-Protect: Policy=CLT - Impersonation Protection Definition; Similar Internal Domain=false; Similar Monitored External Domain=false; Custom External Domain=false; Mimecast External Domain=false; Newly Observed Domain=false; Internal User Name=false; Custom Display Name List=false; Reply-to Address Mismatch=false; Targeted Threat Dictionary=false; Mimecast Threat Dictionary=false; Custom Threat Dictionary=false X-Scanned-By: MIMEDefang 2.78 on 10.11.54.4 X-MIME-Autoconverted: from quoted-printable to 8bit by lists01.pubmisc.prod.ext.phx2.redhat.com id 18RJVAxZ014452 X-loop: libvir-list@redhat.com X-BeenThere: libvir-list@redhat.com X-Mailman-Version: 2.1.12 Precedence: junk List-Id: Development discussions about the libvirt library & tools List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: libvir-list-bounces@redhat.com Errors-To: libvir-list-bounces@redhat.com X-Scanned-By: MIMEDefang 2.79 on 10.5.11.15 Authentication-Results: relay.mimecast.com; auth=pass smtp.auth=CUSA124A263 smtp.mailfrom=libvir-list-bounces@redhat.com X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Content-Transfer-Encoding: quoted-printable X-ZM-MESSAGEID: 1632771099482100003 Content-Type: text/plain; charset="utf-8" Signed-off-by: Dmitrii Shcherbakov --- NEWS.rst | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/NEWS.rst b/NEWS.rst index a4178e505c..4617198f82 100644 --- a/NEWS.rst +++ b/NEWS.rst @@ -37,6 +37,13 @@ v7.8.0 (unreleased) functionality got added for Linux only at this point (kernels above v2.6.26 have support for exposing VPD via sysfs). =20 + * virpci: Add PCI VPD-related helper functions to virpci + + In order to utilize the PCI VPD parser, a couple of helper functions g= ot + introduced to check for the presence of a VPD file in the sysfs tree a= nd + to invoke the PCI VPD parser to get a list of resources representing P= CI + VPD contents in memory. + * **Improvements** =20 * **Bug fixes** --=20 2.30.2 From nobody Sun May 19 07:16:18 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zohomail.com: domain of redhat.com designates 216.205.24.124 as permitted sender) client-ip=216.205.24.124; envelope-from=libvir-list-bounces@redhat.com; helo=us-smtp-delivery-124.mimecast.com; Authentication-Results: mx.zohomail.com; spf=pass (zohomail.com: domain of redhat.com designates 216.205.24.124 as permitted sender) smtp.mailfrom=libvir-list-bounces@redhat.com; dmarc=fail(p=none dis=none) header.from=canonical.com Return-Path: Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [216.205.24.124]) by mx.zohomail.com with SMTPS id 1632771114595909.7035336680375; Mon, 27 Sep 2021 12:31:54 -0700 (PDT) Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-113-DRALJZUsNhiMJoY5JE4CXg-1; Mon, 27 Sep 2021 15:31:42 -0400 Received: from smtp.corp.redhat.com (int-mx04.intmail.prod.int.phx2.redhat.com [10.5.11.14]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 9CF748145E6; Mon, 27 Sep 2021 19:31:31 +0000 (UTC) Received: from colo-mx.corp.redhat.com (colo-mx02.intmail.prod.int.phx2.redhat.com [10.5.11.21]) by smtp.corp.redhat.com (Postfix) with ESMTPS id 7774C5F91D; Mon, 27 Sep 2021 19:31:31 +0000 (UTC) Received: from lists01.pubmisc.prod.ext.phx2.redhat.com (lists01.pubmisc.prod.ext.phx2.redhat.com [10.5.19.33]) by colo-mx.corp.redhat.com (Postfix) with ESMTP id 3DAE44EA38; Mon, 27 Sep 2021 19:31:31 +0000 (UTC) Received: from smtp.corp.redhat.com (int-mx04.intmail.prod.int.rdu2.redhat.com [10.11.54.4]) by lists01.pubmisc.prod.ext.phx2.redhat.com (8.13.8/8.13.8) with ESMTP id 18RJVAaD014453 for ; Mon, 27 Sep 2021 15:31:10 -0400 Received: by smtp.corp.redhat.com (Postfix) id DF6F0202F31E; Mon, 27 Sep 2021 19:31:09 +0000 (UTC) Received: from mimecast-mx02.redhat.com (mimecast04.extmail.prod.ext.rdu2.redhat.com [10.11.55.20]) by smtp.corp.redhat.com (Postfix) with ESMTPS id D4825202F31F for ; Mon, 27 Sep 2021 19:31:02 +0000 (UTC) Received: from us-smtp-1.mimecast.com (us-smtp-1.mimecast.com [205.139.110.61]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id DF8C3106655D for ; Mon, 27 Sep 2021 19:31:01 +0000 (UTC) Received: from smtp-relay-internal-1.canonical.com (smtp-relay-internal-1.canonical.com [185.125.188.123]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-470-zMBsNK7IMri0S1t8nTEgbA-1; Mon, 27 Sep 2021 15:30:59 -0400 Received: from mail-lf1-f70.google.com (mail-lf1-f70.google.com [209.85.167.70]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by smtp-relay-internal-1.canonical.com (Postfix) with ESMTPS id 45CA240784 for ; Mon, 27 Sep 2021 19:30:58 +0000 (UTC) Received: by mail-lf1-f70.google.com with SMTP id q4-20020ac25284000000b003fcebb305a6so3267387lfm.15 for ; Mon, 27 Sep 2021 12:30:58 -0700 (PDT) Received: from ws.lan.d-node.is ([95.165.29.203]) by smtp.gmail.com with ESMTPSA id a8sm1688090lfr.256.2021.09.27.12.30.56 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 27 Sep 2021 12:30:56 -0700 (PDT) X-MC-Unique: DRALJZUsNhiMJoY5JE4CXg-1 X-MC-Unique: zMBsNK7IMri0S1t8nTEgbA-1 X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=xuU21URt/qzTR6CZ+ZChvvcRKvSX7S8/2AVK4Ek2rZw=; b=wfkoNRwjx054OwhIaTZq0adj8gIwgjib4u4Z4HxJsd0KOOslg8wnMlaOcxxGK7t5Un ULIzV4D5F+scOsNzkBQyj1SyWf7wZRV/w92JFrIyEEVxnrozWO1+H8aCrP4e4TMhQYMS iqg/vY5X+CAXPQdjmk5daYgMX1qsdWIMSCPslmGVWzdI+7G9fjLvntjQZ6zog2re1kwT rO01guHlXEFZMxSvPShn7wpyC/brLnOIKG0WaVw11CYEdx1JtmR441xe49u+LMZc0zP6 yIsOMOjwAoqbsum95+38bE1JUXZF6xKz0oMFDDSjLBSAnfyo0VORoPc7s/u71+X1+Q5X Npeg== X-Gm-Message-State: AOAM533FXoj2HXddrVPa3jKO+Znpej6C7MXR8TCUkMRphYkWaRm24AQ8 UCM9uNZBJFm7VFB46BAOHYTjubTHg2QgD13r4hlnVsXxZoHy8BXn3ZTvVQD0CCk2Gh8lzEYcRqi u/y9079KINYpl+YDjt4q9SFadMwlwCLUQ2w== X-Received: by 2002:a05:6512:1087:: with SMTP id j7mr1535582lfg.414.1632771057205; Mon, 27 Sep 2021 12:30:57 -0700 (PDT) X-Google-Smtp-Source: ABdhPJz1lYxzhjz9ulKOX8C+I3Wz89s11c1c+cc3kbyDG0rmGMJxIlb+5eSCkalvj7KkH4EQRUle/A== X-Received: by 2002:a05:6512:1087:: with SMTP id j7mr1535561lfg.414.1632771056865; Mon, 27 Sep 2021 12:30:56 -0700 (PDT) From: Dmitrii Shcherbakov To: libvir-list@redhat.com, dmitrii.shcherbakov@canonical.com Subject: [libvirt PATCH v5 5/7] Add PCI VPD Capability Support Date: Mon, 27 Sep 2021 22:30:51 +0300 Message-Id: <20210927193053.109029-6-dmitrii.shcherbakov@canonical.com> In-Reply-To: <20210927193053.109029-1-dmitrii.shcherbakov@canonical.com> References: <20210927193053.109029-1-dmitrii.shcherbakov@canonical.com> MIME-Version: 1.0 X-Mimecast-Impersonation-Protect: Policy=CLT - Impersonation Protection Definition; Similar Internal Domain=false; Similar Monitored External Domain=false; Custom External Domain=false; Mimecast External Domain=false; Newly Observed Domain=false; Internal User Name=false; Custom Display Name List=false; Reply-to Address Mismatch=false; Targeted Threat Dictionary=false; Mimecast Threat Dictionary=false; Custom Threat Dictionary=false X-Scanned-By: MIMEDefang 2.78 on 10.11.54.4 X-MIME-Autoconverted: from quoted-printable to 8bit by lists01.pubmisc.prod.ext.phx2.redhat.com id 18RJVAaD014453 X-loop: libvir-list@redhat.com X-BeenThere: libvir-list@redhat.com X-Mailman-Version: 2.1.12 Precedence: junk List-Id: Development discussions about the libvirt library & tools List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: libvir-list-bounces@redhat.com Errors-To: libvir-list-bounces@redhat.com X-Scanned-By: MIMEDefang 2.79 on 10.5.11.14 Authentication-Results: relay.mimecast.com; auth=pass smtp.auth=CUSA124A263 smtp.mailfrom=libvir-list-bounces@redhat.com X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Content-Transfer-Encoding: quoted-printable X-ZM-MESSAGEID: 1632771115613100001 Content-Type: text/plain; charset="utf-8" * XML serialization and deserialization of PCI VPD resources; * PCI VPD capability flags added and used in relevant places; * XML to XML tests for the added capability. Signed-off-by: Dmitrii Shcherbakov --- docs/schemas/nodedev.rng | 40 +++ include/libvirt/libvirt-nodedev.h | 1 + src/conf/node_device_conf.c | 271 ++++++++++++++++++ src/conf/node_device_conf.h | 6 +- src/conf/virnodedeviceobj.c | 7 +- src/node_device/node_device_driver.c | 2 + src/node_device/node_device_udev.c | 2 + .../pci_0000_42_00_0_vpd.xml | 33 +++ .../pci_0000_42_00_0_vpd.xml | 1 + tests/nodedevxml2xmltest.c | 1 + tools/virsh-nodedev.c | 3 + 11 files changed, 365 insertions(+), 2 deletions(-) create mode 100644 tests/nodedevschemadata/pci_0000_42_00_0_vpd.xml create mode 120000 tests/nodedevxml2xmlout/pci_0000_42_00_0_vpd.xml diff --git a/docs/schemas/nodedev.rng b/docs/schemas/nodedev.rng index e089e66858..fbbee4d0c6 100644 --- a/docs/schemas/nodedev.rng +++ b/docs/schemas/nodedev.rng @@ -223,6 +223,10 @@ =20 + + + + @@ -770,6 +774,42 @@ =20 + + + + vpd + + + + + + + string + + + + + + + + vpd-r + vpd-w + + + + + + + + + + + + + + + + diff --git a/include/libvirt/libvirt-nodedev.h b/include/libvirt/libvirt-no= dedev.h index e492634217..245365b07f 100644 --- a/include/libvirt/libvirt-nodedev.h +++ b/include/libvirt/libvirt-nodedev.h @@ -84,6 +84,7 @@ typedef enum { VIR_CONNECT_LIST_NODE_DEVICES_CAP_AP_CARD =3D 1 << 18, /* s390 A= P Card device */ VIR_CONNECT_LIST_NODE_DEVICES_CAP_AP_QUEUE =3D 1 << 19, /* s390 A= P Queue */ VIR_CONNECT_LIST_NODE_DEVICES_CAP_AP_MATRIX =3D 1 << 20, /* s390 A= P Matrix */ + VIR_CONNECT_LIST_NODE_DEVICES_CAP_VPD =3D 1 << 21, /* Device= with VPD */ =20 /* filter the devices by active state */ VIR_CONNECT_LIST_NODE_DEVICES_INACTIVE =3D 1 << 30, /* Inacti= ve devices */ diff --git a/src/conf/node_device_conf.c b/src/conf/node_device_conf.c index 9bbff97ffd..7d9f74f45b 100644 --- a/src/conf/node_device_conf.c +++ b/src/conf/node_device_conf.c @@ -36,6 +36,7 @@ #include "virrandom.h" #include "virlog.h" #include "virfcp.h" +#include "virpcivpd.h" =20 #define VIR_FROM_THIS VIR_FROM_NODEDEV =20 @@ -70,6 +71,7 @@ VIR_ENUM_IMPL(virNodeDevCap, "ap_card", "ap_queue", "ap_matrix", + "vpd", ); =20 VIR_ENUM_IMPL(virNodeDevNetCap, @@ -240,6 +242,90 @@ virNodeDeviceCapMdevTypesFormat(virBuffer *buf, } } =20 +static void +virNodeDeviceCapVPDStringResourceFormat(virBuffer *buf, virPCIVPDResource = *res) +{ + virBufferAdjustIndent(buf, 2); + virBufferEscapeString(buf, "%s", virPCIVPDStringResourceGetValue((virP= CIVPDStringResource *)res)); + virBufferAdjustIndent(buf, -2); +} + +static gboolean +virNodeDeviceCapVPDWriteTextField(gpointer key, gpointer val, gpointer use= rData) +{ + const gchar *k =3D (const gchar*)key, *v =3D (const gchar*)val; + virBuffer *buf =3D userData; + virPCIVPDResourceFieldValueFormat format =3D VIR_PCI_VPD_RESOURCE_FIEL= D_VALUE_FORMAT_LAST; + + format =3D virPCIVPDResourceGetFieldValueFormat(k); + + if (format =3D=3D VIR_PCI_VPD_RESOURCE_FIELD_VALUE_FORMAT_TEXT) { + virBufferEscapeString(buf, "", k); + virBufferEscapeString(buf, "%s", v); + virBufferEscapeString(buf, "\n", k); + } + return false; +} + +static void +virNodeDeviceCapVPDKeywordResourceFormat(virBuffer *buf, virPCIVPDResource= *res) +{ + virPCIVPDKeywordResource *keywordRes =3D NULL; + g_autofree GHashTableIter *iter =3D NULL; + + virBufferAddLit(buf, "\n"); + virBufferAdjustIndent(buf, 2); + keywordRes =3D (virPCIVPDKeywordResource*)res; + + virPCIVPDKeywordResourceForEach(keywordRes, virNodeDeviceCapVPDWriteTe= xtField, buf); + virBufferAdjustIndent(buf, -2); +} + +static void +virNodeDeviceCapVPDResourceFormat(virBuffer *buf, virPCIVPDResource *res) +{ + GEnumValue *resRype =3D NULL; + void (*resFormatFunc)(virBuffer *buf, virPCIVPDResource *res); + + if (G_TYPE_CHECK_INSTANCE_TYPE(res, VIR_TYPE_PCI_VPD_STRING_RESOURCE))= { + resFormatFunc =3D virNodeDeviceCapVPDStringResourceFormat; + } else if (G_TYPE_CHECK_INSTANCE_TYPE(res, VIR_TYPE_PCI_VPD_KEYWORD_RE= SOURCE)) { + resFormatFunc =3D virNodeDeviceCapVPDKeywordResourceFormat; + } else { + /* Unexpected resource type. This should not happen unless the PCI= (e) specs + * change and new resource types are introduced into util.virpcivp= d. Either way, + * we can only return the control back to the caller here. + */ + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Unexpected VPD resource type encountered during = formatting")); + return; + } + virBufferAdjustIndent(buf, 2); + + resRype =3D virPCIVPDResourceGetResourceType(res); + virBufferEscapeString(buf, "", resRype->value_ni= ck); + /* Format the resource in a type-specific way. */ + resFormatFunc(buf, res); + virBufferAddLit(buf, "\n"); + + virBufferAdjustIndent(buf, -2); +} + +static void +virNodeDeviceCapVPDFormat(virBuffer *buf, GList *vpdResources) +{ + if (!g_list_length(vpdResources)) { + return; + } + + virBufferAddLit(buf, "\n"); + while (vpdResources) { + GList *next =3D vpdResources->next; + virNodeDeviceCapVPDResourceFormat(buf, vpdResources->data); + vpdResources =3D next; + } + virBufferAddLit(buf, "\n"); +} =20 static void virNodeDeviceCapPCIDefFormat(virBuffer *buf, @@ -315,6 +401,9 @@ virNodeDeviceCapPCIDefFormat(virBuffer *buf, data->pci_dev.mdev_types, data->pci_dev.nmdev_types); } + if (data->pci_dev.flags & VIR_NODE_DEV_CAP_FLAG_PCI_VPD) { + virNodeDeviceCapVPDFormat(buf, data->pci_dev.vpd_resources); + } if (data->pci_dev.nIommuGroupDevices) { virBufferAsprintf(buf, "\n", data->pci_dev.iommuGroupNumber); @@ -673,6 +762,7 @@ virNodeDeviceDefFormat(const virNodeDeviceDef *def) case VIR_NODE_DEV_CAP_MDEV_TYPES: case VIR_NODE_DEV_CAP_FC_HOST: case VIR_NODE_DEV_CAP_VPORTS: + case VIR_NODE_DEV_CAP_VPD: case VIR_NODE_DEV_CAP_LAST: break; } @@ -859,6 +949,132 @@ virNodeDevCapMdevTypesParseXML(xmlXPathContextPtr ctx= t, return ret; } =20 +static GTree * +virNodeDeviceCapVPDParseXMLKeywordResource(xmlXPathContextPtr ctxt) +{ + int nfields =3D -1; + g_autofree gchar* fieldValue =3D NULL; + g_autofree gchar* fieldKeyword =3D NULL; + g_autoptr(GTree) resourceFields =3D NULL; + g_autofree xmlNodePtr *nodes =3D NULL; + xmlNodePtr orignode =3D NULL; + size_t i =3D 0; + + resourceFields =3D g_tree_new_full( + (GCompareDataFunc)g_strcmp0, NULL, g_free, g_free); + + if ((nfields =3D virXPathNodeSet("./field[@keyword]", ctxt, &nodes)) <= 0) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("no VPD elements with a keyword specified found"= )); + ctxt->node =3D orignode; + return NULL; + } + + orignode =3D ctxt->node; + for (i =3D 0; i < nfields; i++) { + ctxt->node =3D nodes[i]; + if (!(fieldKeyword =3D virXPathString("string(./@keyword[1])", ctx= t))) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("VPD resource field keyword parsing has failed")); + continue; + } + if (!(fieldValue =3D virXPathString("string(./text())", ctxt))) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("VPD resource field keyword parsing has failed")); + continue; + } + g_tree_insert(resourceFields, g_steal_pointer(&fieldKeyword), + g_steal_pointer(&fieldValue)); + } + ctxt->node =3D orignode; + return g_steal_pointer(&resourceFields); +} + +static int +virNodeDeviceCapVPDParseXML(xmlXPathContextPtr ctxt, GList **vpdResources) +{ + xmlNodePtr orignode =3D NULL; + g_autofree gchar* resText =3D NULL; + g_autofree GTree *resourceFields =3D NULL; + g_autofree xmlNodePtr *nodes =3D NULL; + int nresources =3D -1; + g_autofree gchar* resTypeStr =3D NULL; + virPCIVPDResourceType type =3D VIR_PCI_VPD_RESOURCE_TYPE_LAST; + GEnumClass *class; + size_t i =3D 0; + + if ((nresources =3D virXPathNodeSet("./resource[@type]", ctxt, &nodes)= ) < 0) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("no VPD elements with a type specified found"= )); + return -1; + } + + orignode =3D ctxt->node; + for (i =3D 0; i < nresources; i++) { + ctxt->node =3D nodes[i]; + if (!(resTypeStr =3D virXPathString("string(./@type[1])", ctxt))) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("VPD resource type parsing has failed")); + ctxt->node =3D orignode; + return -1; + } + + class =3D g_type_class_ref(VIR_TYPE_PCI_VPD_RESOURCE_TYPE); + type =3D g_enum_get_value_by_nick(class, resTypeStr)->value; + g_type_class_unref(class); + if (!type) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("Unexpected VPD resource type encountered")); + /* Skip resources with unknown types. */ + continue; + } + + /* Create in-memory representations of resources based on their ty= pe. If a resource is not + * valid, report an error and skip to parsing another resource. + */ + switch (type) { + case VIR_PCI_VPD_RESOURCE_TYPE_STRING: + if (!(resText =3D virXPathString("string(./text())", ctxt)= )) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("Could not read a string resource text")); + continue; + } + *vpdResources =3D g_list_append(*vpdResources, virPCIVPDSt= ringResourceNew( + g_steal_pointer(&resText))); + break; + case VIR_PCI_VPD_RESOURCE_TYPE_VPD_R: + if (!(resourceFields =3D virNodeDeviceCapVPDParseXMLKeywor= dResource(ctxt))) { + virReportError(VIR_ERR_XML_ERROR, + _("Could not parse %s VPD resource fields"), r= esTypeStr); + continue; + } + *vpdResources =3D g_list_append(*vpdResources, + virPCIVPDKeywordResourceNew(g_steal_pointer(&resou= rceFields), true)); + break; + case VIR_PCI_VPD_RESOURCE_TYPE_VPD_W: + if (!(resourceFields =3D virNodeDeviceCapVPDParseXMLKeywor= dResource(ctxt))) { + virReportError(VIR_ERR_XML_ERROR, + _("Could not parse %s VPD resource fields"), r= esTypeStr); + continue; + } + *vpdResources =3D g_list_append(*vpdResources, + virPCIVPDKeywordResourceNew(g_steal_pointer(&resou= rceFields), false)); + break; + case VIR_PCI_VPD_RESOURCE_TYPE_LAST: + default: + /* If the VPD module is aware of a resource type and it ha= s been serialized into + * XML while the parser does not then this is a bug. + */ + virReportError(VIR_ERR_XML_ERROR, + _("The XML parser has encountered an unsupported r= esource type %s"), + resTypeStr); + ctxt->node =3D orignode; + return -1; + } + } + ctxt->node =3D orignode; + return 0; +} =20 static int virNodeDevAPMatrixCapabilityParseXML(xmlXPathContextPtr ctxt, @@ -1718,6 +1934,11 @@ virNodeDevPCICapabilityParseXML(xmlXPathContextPtr c= txt, &pci_dev->nmdev_types) < 0) return -1; pci_dev->flags |=3D VIR_NODE_DEV_CAP_FLAG_PCI_MDEV; + } else if (STREQ(type, "vpd")) { + if (virNodeDeviceCapVPDParseXML(ctxt, &pci_dev->vpd_resources) < 0= ) { + return -1; + } + pci_dev->flags |=3D VIR_NODE_DEV_CAP_FLAG_PCI_VPD; } else { int hdrType =3D virPCIHeaderTypeFromString(type); =20 @@ -2024,6 +2245,7 @@ virNodeDevCapsDefParseXML(xmlXPathContextPtr ctxt, case VIR_NODE_DEV_CAP_VPORTS: case VIR_NODE_DEV_CAP_SCSI_GENERIC: case VIR_NODE_DEV_CAP_VDPA: + case VIR_NODE_DEV_CAP_VPD: case VIR_NODE_DEV_CAP_LAST: virReportError(VIR_ERR_INTERNAL_ERROR, _("unknown capability type '%d' for '%s'"), @@ -2287,6 +2509,8 @@ virNodeDevCapsDefFree(virNodeDevCapsDef *caps) for (i =3D 0; i < data->pci_dev.nmdev_types; i++) virMediatedDeviceTypeFree(data->pci_dev.mdev_types[i]); g_free(data->pci_dev.mdev_types); + g_list_free_full(g_steal_pointer(&data->pci_dev.vpd_resources), + g_object_unref); break; case VIR_NODE_DEV_CAP_USB_DEV: g_free(data->usb_dev.product_name); @@ -2352,6 +2576,7 @@ virNodeDevCapsDefFree(virNodeDevCapsDef *caps) case VIR_NODE_DEV_CAP_VDPA: case VIR_NODE_DEV_CAP_AP_CARD: case VIR_NODE_DEV_CAP_AP_QUEUE: + case VIR_NODE_DEV_CAP_VPD: case VIR_NODE_DEV_CAP_LAST: /* This case is here to shutup the compiler */ break; @@ -2418,6 +2643,7 @@ virNodeDeviceUpdateCaps(virNodeDeviceDef *def) case VIR_NODE_DEV_CAP_VDPA: case VIR_NODE_DEV_CAP_AP_CARD: case VIR_NODE_DEV_CAP_AP_QUEUE: + case VIR_NODE_DEV_CAP_VPD: case VIR_NODE_DEV_CAP_LAST: break; } @@ -2489,6 +2715,10 @@ virNodeDeviceCapsListExport(virNodeDeviceDef *def, MAYBE_ADD_CAP(VIR_NODE_DEV_CAP_MDEV_TYPES); ncaps++; } + if (flags & VIR_NODE_DEV_CAP_FLAG_PCI_VPD) { + MAYBE_ADD_CAP(VIR_NODE_DEV_CAP_VPD); + ncaps++; + } } =20 if (caps->data.type =3D=3D VIR_NODE_DEV_CAP_CSS_DEV) { @@ -2749,6 +2979,44 @@ virNodeDeviceGetMdevTypesCaps(const char *sysfspath, } =20 =20 +/** + * virNodeDeviceGetPCIVPDDynamicCap: + * @devCapPCIDev: a virNodeDevCapPCIDev for which to add VPD resources. + * + * While VPD has a read-only portion, there may be a read-write portion per + * the specs which may change dynamically. + * + * Returns: 0 if the operation was successful (even if VPD is not present = for + * that device since it is optional in the specs, -1 otherwise. + */ +static int +virNodeDeviceGetPCIVPDDynamicCap(virNodeDevCapPCIDev *devCapPCIDev) +{ + g_autoptr(virPCIDevice) pciDev =3D NULL; + virPCIDeviceAddress devAddr; + g_autolist(virPCIVPDResource) resourceList =3D NULL; + + devAddr.domain =3D devCapPCIDev->domain; + devAddr.bus =3D devCapPCIDev->bus; + devAddr.slot =3D devCapPCIDev->slot; + devAddr.function =3D devCapPCIDev->function; + + if (!(pciDev =3D virPCIDeviceNew(&devAddr))) + return -1; + + if (virPCIDeviceHasVPD(pciDev)) { + /* VPD is optional in PCI(e) specs. If it is there, attempt to add= it. */ + if ((resourceList =3D virPCIDeviceGetVPDResources(pciDev))) { + devCapPCIDev->flags |=3D VIR_NODE_DEV_CAP_FLAG_PCI_VPD; + devCapPCIDev->vpd_resources =3D g_steal_pointer(&resourceList); + } else { + g_list_free_full(devCapPCIDev->vpd_resources, g_object_unref); + } + } + return 0; +} + + /* virNodeDeviceGetPCIDynamicCaps() get info that is stored in sysfs * about devices related to this device, i.e. things that can change * without this device itself changing. These must be refreshed @@ -2771,6 +3039,9 @@ virNodeDeviceGetPCIDynamicCaps(const char *sysfsPath, if (pci_dev->nmdev_types > 0) pci_dev->flags |=3D VIR_NODE_DEV_CAP_FLAG_PCI_MDEV; =20 + if (virNodeDeviceGetPCIVPDDynamicCap(pci_dev) < 0) + return -1; + return 0; } =20 diff --git a/src/conf/node_device_conf.h b/src/conf/node_device_conf.h index 5a4d9c7a55..32e59fa52a 100644 --- a/src/conf/node_device_conf.h +++ b/src/conf/node_device_conf.h @@ -69,6 +69,7 @@ typedef enum { VIR_NODE_DEV_CAP_AP_CARD, /* s390 AP Card device */ VIR_NODE_DEV_CAP_AP_QUEUE, /* s390 AP Queue */ VIR_NODE_DEV_CAP_AP_MATRIX, /* s390 AP Matrix device */ + VIR_NODE_DEV_CAP_VPD, /* Device provides VPD */ =20 VIR_NODE_DEV_CAP_LAST } virNodeDevCapType; @@ -103,6 +104,7 @@ typedef enum { VIR_NODE_DEV_CAP_FLAG_PCI_VIRTUAL_FUNCTION =3D (1 << 1), VIR_NODE_DEV_CAP_FLAG_PCIE =3D (1 << 2), VIR_NODE_DEV_CAP_FLAG_PCI_MDEV =3D (1 << 3), + VIR_NODE_DEV_CAP_FLAG_PCI_VPD =3D (1 << 4), } virNodeDevPCICapFlags; =20 typedef enum { @@ -181,6 +183,7 @@ struct _virNodeDevCapPCIDev { int hdrType; /* enum virPCIHeaderType or -1 */ virMediatedDeviceType **mdev_types; size_t nmdev_types; + GList *vpd_resources; }; =20 typedef struct _virNodeDevCapUSBDev virNodeDevCapUSBDev; @@ -418,7 +421,8 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC(virNodeDevCapsDef, virNod= eDevCapsDefFree); VIR_CONNECT_LIST_NODE_DEVICES_CAP_VDPA | \ VIR_CONNECT_LIST_NODE_DEVICES_CAP_AP_CARD | \ VIR_CONNECT_LIST_NODE_DEVICES_CAP_AP_QUEUE | \ - VIR_CONNECT_LIST_NODE_DEVICES_CAP_AP_MATRIX) + VIR_CONNECT_LIST_NODE_DEVICES_CAP_AP_MATRIX | \ + VIR_CONNECT_LIST_NODE_DEVICES_CAP_VPD) =20 #define VIR_CONNECT_LIST_NODE_DEVICES_FILTERS_ACTIVE \ VIR_CONNECT_LIST_NODE_DEVICES_ACTIVE | \ diff --git a/src/conf/virnodedeviceobj.c b/src/conf/virnodedeviceobj.c index 9a9841576a..165ec1f1dd 100644 --- a/src/conf/virnodedeviceobj.c +++ b/src/conf/virnodedeviceobj.c @@ -701,6 +701,9 @@ virNodeDeviceObjHasCap(const virNodeDeviceObj *obj, if (type =3D=3D VIR_NODE_DEV_CAP_MDEV_TYPES && (cap->data.pci_dev.flags & VIR_NODE_DEV_CAP_FLAG_PCI_MDEV)) return true; + if (type =3D=3D VIR_NODE_DEV_CAP_VPD && + (cap->data.pci_dev.flags & VIR_NODE_DEV_CAP_FLAG_PCI_VPD)) + return true; break; =20 case VIR_NODE_DEV_CAP_SCSI_HOST: @@ -742,6 +745,7 @@ virNodeDeviceObjHasCap(const virNodeDeviceObj *obj, case VIR_NODE_DEV_CAP_VDPA: case VIR_NODE_DEV_CAP_AP_CARD: case VIR_NODE_DEV_CAP_AP_QUEUE: + case VIR_NODE_DEV_CAP_VPD: case VIR_NODE_DEV_CAP_LAST: break; } @@ -899,7 +903,8 @@ virNodeDeviceObjMatch(virNodeDeviceObj *obj, MATCH_CAP(VDPA) || MATCH_CAP(AP_CARD) || MATCH_CAP(AP_QUEUE) || - MATCH_CAP(AP_MATRIX))) + MATCH_CAP(AP_MATRIX) || + MATCH_CAP(VPD))) return false; } =20 diff --git a/src/node_device/node_device_driver.c b/src/node_device/node_de= vice_driver.c index 3bc6eb1c11..d19ed7d948 100644 --- a/src/node_device/node_device_driver.c +++ b/src/node_device/node_device_driver.c @@ -708,6 +708,7 @@ nodeDeviceObjFormatAddress(virNodeDeviceObj *obj) case VIR_NODE_DEV_CAP_VDPA: case VIR_NODE_DEV_CAP_AP_CARD: case VIR_NODE_DEV_CAP_AP_QUEUE: + case VIR_NODE_DEV_CAP_VPD: case VIR_NODE_DEV_CAP_LAST: break; } @@ -1983,6 +1984,7 @@ int nodeDeviceDefValidate(virNodeDeviceDef *def, case VIR_NODE_DEV_CAP_AP_CARD: case VIR_NODE_DEV_CAP_AP_QUEUE: case VIR_NODE_DEV_CAP_AP_MATRIX: + case VIR_NODE_DEV_CAP_VPD: case VIR_NODE_DEV_CAP_LAST: break; } diff --git a/src/node_device/node_device_udev.c b/src/node_device/node_devi= ce_udev.c index 71f0bef827..7c3bb762b3 100644 --- a/src/node_device/node_device_udev.c +++ b/src/node_device/node_device_udev.c @@ -42,6 +42,7 @@ #include "virnetdev.h" #include "virmdev.h" #include "virutil.h" +#include "virpcivpd.h" =20 #include "configmake.h" =20 @@ -1397,6 +1398,7 @@ udevGetDeviceDetails(struct udev_device *device, case VIR_NODE_DEV_CAP_AP_MATRIX: return udevProcessAPMatrix(device, def); case VIR_NODE_DEV_CAP_MDEV_TYPES: + case VIR_NODE_DEV_CAP_VPD: case VIR_NODE_DEV_CAP_SYSTEM: case VIR_NODE_DEV_CAP_FC_HOST: case VIR_NODE_DEV_CAP_VPORTS: diff --git a/tests/nodedevschemadata/pci_0000_42_00_0_vpd.xml b/tests/noded= evschemadata/pci_0000_42_00_0_vpd.xml new file mode 100644 index 0000000000..831b6feb24 --- /dev/null +++ b/tests/nodedevschemadata/pci_0000_42_00_0_vpd.xml @@ -0,0 +1,33 @@ + + pci_0000_42_00_0 + + 0x020000 + 0 + 66 + 0 + 0 + MT42822 BlueField-2 integrated ConnectX-6 Dx ne= twork controller + Mellanox Technologies + + + BlueField-2 DPU 25GbE Dual-Port SFP56, Cry= pto Enabled, 16GB on-board DDR, 1GbE OOB management, Tall Bracket + + B1 + MBF2H332A-AEEOT + MT2113X00000 + PCIeGen4 x8 + MBF2H332A-AEEOT + 3c53d07eec484d8aab34dabd24fe575aa + MLX:MN=3DMLNX:CSKU=3DV2:UUID=3DV3:PCI=3DV0:M= ODL=3DBF2H332A + + + +
+ + + + + + + + diff --git a/tests/nodedevxml2xmlout/pci_0000_42_00_0_vpd.xml b/tests/noded= evxml2xmlout/pci_0000_42_00_0_vpd.xml new file mode 120000 index 0000000000..a0b5372ca0 --- /dev/null +++ b/tests/nodedevxml2xmlout/pci_0000_42_00_0_vpd.xml @@ -0,0 +1 @@ +../nodedevschemadata/pci_0000_42_00_0_vpd.xml \ No newline at end of file diff --git a/tests/nodedevxml2xmltest.c b/tests/nodedevxml2xmltest.c index 9e32e7d553..557347fb07 100644 --- a/tests/nodedevxml2xmltest.c +++ b/tests/nodedevxml2xmltest.c @@ -121,6 +121,7 @@ mymain(void) DO_TEST("pci_0000_02_10_7_sriov_pf_vfs_all_header_type"); DO_TEST("drm_renderD129"); DO_TEST("pci_0000_02_10_7_mdev_types"); + DO_TEST("pci_0000_42_00_0_vpd"); DO_TEST("mdev_3627463d_b7f0_4fea_b468_f1da537d301b"); DO_TEST("ccw_0_0_ffff"); DO_TEST("css_0_0_ffff"); diff --git a/tools/virsh-nodedev.c b/tools/virsh-nodedev.c index f72359121f..63725587fc 100644 --- a/tools/virsh-nodedev.c +++ b/tools/virsh-nodedev.c @@ -476,6 +476,9 @@ cmdNodeListDevices(vshControl *ctl, const vshCmd *cmd G= _GNUC_UNUSED) case VIR_NODE_DEV_CAP_MDEV: flags |=3D VIR_CONNECT_LIST_NODE_DEVICES_CAP_MDEV; break; + case VIR_NODE_DEV_CAP_VPD: + flags |=3D VIR_CONNECT_LIST_NODE_DEVICES_CAP_VPD; + break; case VIR_NODE_DEV_CAP_CCW_DEV: flags |=3D VIR_CONNECT_LIST_NODE_DEVICES_CAP_CCW_DEV; break; --=20 2.30.2 From nobody Sun May 19 07:16:18 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zohomail.com: domain of redhat.com designates 170.10.129.124 as permitted sender) client-ip=170.10.129.124; envelope-from=libvir-list-bounces@redhat.com; helo=us-smtp-delivery-124.mimecast.com; Authentication-Results: mx.zohomail.com; spf=pass (zohomail.com: domain of redhat.com designates 170.10.129.124 as permitted sender) smtp.mailfrom=libvir-list-bounces@redhat.com; dmarc=fail(p=none dis=none) header.from=canonical.com Return-Path: Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by mx.zohomail.com with SMTPS id 1632771099930423.9626149631508; Mon, 27 Sep 2021 12:31:39 -0700 (PDT) Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-41-MsNCbvAGN7yN__XXL9FaOQ-1; Mon, 27 Sep 2021 15:31:35 -0400 Received: from smtp.corp.redhat.com (int-mx04.intmail.prod.int.phx2.redhat.com [10.5.11.14]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id B598D1084687; Mon, 27 Sep 2021 19:31:29 +0000 (UTC) Received: from colo-mx.corp.redhat.com (colo-mx02.intmail.prod.int.phx2.redhat.com [10.5.11.21]) by smtp.corp.redhat.com (Postfix) with ESMTPS id 940651816A; Mon, 27 Sep 2021 19:31:29 +0000 (UTC) Received: from lists01.pubmisc.prod.ext.phx2.redhat.com (lists01.pubmisc.prod.ext.phx2.redhat.com [10.5.19.33]) by colo-mx.corp.redhat.com (Postfix) with ESMTP id 342334EA30; Mon, 27 Sep 2021 19:31:29 +0000 (UTC) Received: from smtp.corp.redhat.com (int-mx06.intmail.prod.int.rdu2.redhat.com [10.11.54.6]) by lists01.pubmisc.prod.ext.phx2.redhat.com (8.13.8/8.13.8) with ESMTP id 18RJV7R7014429 for ; Mon, 27 Sep 2021 15:31:07 -0400 Received: by smtp.corp.redhat.com (Postfix) id 20CDC2144B2D; Mon, 27 Sep 2021 19:31:07 +0000 (UTC) Received: from mimecast-mx02.redhat.com (mimecast01.extmail.prod.ext.rdu2.redhat.com [10.11.55.17]) by smtp.corp.redhat.com (Postfix) with ESMTPS id 15B222144B32 for ; Mon, 27 Sep 2021 19:31:02 +0000 (UTC) Received: from us-smtp-1.mimecast.com (us-smtp-delivery-1.mimecast.com [207.211.31.120]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id ED53685828B for ; Mon, 27 Sep 2021 19:31:01 +0000 (UTC) Received: from smtp-relay-internal-0.canonical.com (smtp-relay-internal-0.canonical.com [185.125.188.122]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-421-MBhzRVs1MOWGYj9GWKxv2g-1; Mon, 27 Sep 2021 15:30:59 -0400 Received: from mail-lf1-f69.google.com (mail-lf1-f69.google.com [209.85.167.69]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by smtp-relay-internal-0.canonical.com (Postfix) with ESMTPS id 1068A4079F for ; Mon, 27 Sep 2021 19:30:58 +0000 (UTC) Received: by mail-lf1-f69.google.com with SMTP id 12-20020ac2484c000000b003fcb3298d00so13662020lfy.13 for ; Mon, 27 Sep 2021 12:30:58 -0700 (PDT) Received: from ws.lan.d-node.is ([95.165.29.203]) by smtp.gmail.com with ESMTPSA id a8sm1688090lfr.256.2021.09.27.12.30.56 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 27 Sep 2021 12:30:57 -0700 (PDT) X-MC-Unique: MsNCbvAGN7yN__XXL9FaOQ-1 X-MC-Unique: MBhzRVs1MOWGYj9GWKxv2g-1 X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=zId5nzLnBvYf2vu41SVx7K337VOKoxCthMgy789X6cA=; b=684OzHHfiERXPrc/VFBimTqnZC9FpcaJIR/X4kaODiopMwkHGSxD+Hq4b/SUE2bnGD iunqpd3f2EkUz23D3G4kDasCKggUBTjQc4VQ87v6ITYpAJsi9D0BPdOkg7R3EWiEkpDO 3lEU+/0KyS80Egk5PeG6Fo76jO4YFPqeZ1Dapcx5cBc5Qw2WLXO/zCOQHx6r+BZ2p8H0 H/BzSe28F5gsciuE8nbKbWFapi8Fsl1PDdrRkhHr8BEKIK/x/iR1JkQcQrUrK4M4m3gk J5M0WehXbo2xsTpq3PZttdl+K0wJWZTWUgYPbb6YXwXLY+WS+M4RgieIUOTLQ0Ouoarn cdGQ== X-Gm-Message-State: AOAM532Hqgw54KGhFHtLPu2gpksm0HiwF5SXw1GFrYFqZBzmakWjJSxa C6Tw2cegLoUuzy2OtK63szzR1zigWvQILg0X7OTOsGhIucgHak9WzxG731ePH4VofRDyrmKvOuf GnvX7xwCfiyJr/LVTT2tnFiY9KbGimKooFQ== X-Received: by 2002:ac2:53a5:: with SMTP id j5mr1485876lfh.652.1632771057513; Mon, 27 Sep 2021 12:30:57 -0700 (PDT) X-Google-Smtp-Source: ABdhPJx6MubWF4xh8JFcNj3gyG3bX6yOXXvtwbTyadP05/fmN0blYOY6L29gMGEqafb8paPtiMAlGw== X-Received: by 2002:ac2:53a5:: with SMTP id j5mr1485860lfh.652.1632771057280; Mon, 27 Sep 2021 12:30:57 -0700 (PDT) From: Dmitrii Shcherbakov To: libvir-list@redhat.com, dmitrii.shcherbakov@canonical.com Subject: [libvirt PATCH v5 6/7] News: Add PCI VPD capability support Date: Mon, 27 Sep 2021 22:30:52 +0300 Message-Id: <20210927193053.109029-7-dmitrii.shcherbakov@canonical.com> In-Reply-To: <20210927193053.109029-1-dmitrii.shcherbakov@canonical.com> References: <20210927193053.109029-1-dmitrii.shcherbakov@canonical.com> MIME-Version: 1.0 X-Mimecast-Impersonation-Protect: Policy=CLT - Impersonation Protection Definition; Similar Internal Domain=false; Similar Monitored External Domain=false; Custom External Domain=false; Mimecast External Domain=false; Newly Observed Domain=false; Internal User Name=false; Custom Display Name List=false; Reply-to Address Mismatch=false; Targeted Threat Dictionary=false; Mimecast Threat Dictionary=false; Custom Threat Dictionary=false X-Scanned-By: MIMEDefang 2.78 on 10.11.54.6 X-MIME-Autoconverted: from quoted-printable to 8bit by lists01.pubmisc.prod.ext.phx2.redhat.com id 18RJV7R7014429 X-loop: libvir-list@redhat.com X-BeenThere: libvir-list@redhat.com X-Mailman-Version: 2.1.12 Precedence: junk List-Id: Development discussions about the libvirt library & tools List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: libvir-list-bounces@redhat.com Errors-To: libvir-list-bounces@redhat.com X-Scanned-By: MIMEDefang 2.79 on 10.5.11.14 Authentication-Results: relay.mimecast.com; auth=pass smtp.auth=CUSA124A263 smtp.mailfrom=libvir-list-bounces@redhat.com X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Content-Transfer-Encoding: quoted-printable X-ZM-MESSAGEID: 1632771101619100001 Content-Type: text/plain; charset="utf-8" Signed-off-by: Dmitrii Shcherbakov --- NEWS.rst | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/NEWS.rst b/NEWS.rst index 4617198f82..4db4e0e3e3 100644 --- a/NEWS.rst +++ b/NEWS.rst @@ -44,6 +44,13 @@ v7.8.0 (unreleased) to invoke the PCI VPD parser to get a list of resources representing P= CI VPD contents in memory. =20 + * nodedev: Add PCI VPD capability support + + Support for serializing and deserializing PCI VPD data structures is a= dded + following the addition of the PCI VPD parser. A new PCI device capabil= ity + called "vpd" is introduced holding string resources and keyword resour= ces + found in PCI VPD. + * **Improvements** =20 * **Bug fixes** --=20 2.30.2 From nobody Sun May 19 07:16:18 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zohomail.com: domain of redhat.com designates 216.205.24.124 as permitted sender) client-ip=216.205.24.124; envelope-from=libvir-list-bounces@redhat.com; helo=us-smtp-delivery-124.mimecast.com; Authentication-Results: mx.zohomail.com; spf=pass (zohomail.com: domain of redhat.com designates 216.205.24.124 as permitted sender) smtp.mailfrom=libvir-list-bounces@redhat.com; dmarc=fail(p=none dis=none) header.from=canonical.com Return-Path: Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [216.205.24.124]) by mx.zohomail.com with SMTPS id 1632771096370760.7646025547318; Mon, 27 Sep 2021 12:31:36 -0700 (PDT) Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-299-HTpmku5HOlqMggukKJtIGQ-1; Mon, 27 Sep 2021 15:31:33 -0400 Received: from smtp.corp.redhat.com (int-mx02.intmail.prod.int.phx2.redhat.com [10.5.11.12]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 55CC3802ECF; Mon, 27 Sep 2021 19:31:28 +0000 (UTC) Received: from colo-mx.corp.redhat.com (colo-mx02.intmail.prod.int.phx2.redhat.com [10.5.11.21]) by smtp.corp.redhat.com (Postfix) with ESMTPS id 2AED35FCB5; Mon, 27 Sep 2021 19:31:28 +0000 (UTC) Received: from lists01.pubmisc.prod.ext.phx2.redhat.com (lists01.pubmisc.prod.ext.phx2.redhat.com [10.5.19.33]) by colo-mx.corp.redhat.com (Postfix) with ESMTP id E49154EA37; Mon, 27 Sep 2021 19:31:27 +0000 (UTC) Received: from smtp.corp.redhat.com (int-mx04.intmail.prod.int.rdu2.redhat.com [10.11.54.4]) by lists01.pubmisc.prod.ext.phx2.redhat.com (8.13.8/8.13.8) with ESMTP id 18RJVAsK014454 for ; Mon, 27 Sep 2021 15:31:10 -0400 Received: by smtp.corp.redhat.com (Postfix) id E2BBF202F317; Mon, 27 Sep 2021 19:31:09 +0000 (UTC) Received: from mimecast-mx02.redhat.com (mimecast05.extmail.prod.ext.rdu2.redhat.com [10.11.55.21]) by smtp.corp.redhat.com (Postfix) with ESMTPS id D5BBB202F32E for ; Mon, 27 Sep 2021 19:31:02 +0000 (UTC) Received: from us-smtp-1.mimecast.com (us-smtp-1.mimecast.com [207.211.31.81]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id 2339780B91C for ; Mon, 27 Sep 2021 19:31:02 +0000 (UTC) Received: from smtp-relay-internal-0.canonical.com (smtp-relay-internal-0.canonical.com [185.125.188.122]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-279-jZCCN9jJOnKnrM4OQQAa1Q-1; Mon, 27 Sep 2021 15:31:00 -0400 Received: from mail-lf1-f69.google.com (mail-lf1-f69.google.com [209.85.167.69]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by smtp-relay-internal-0.canonical.com (Postfix) with ESMTPS id 8E8BE40816 for ; Mon, 27 Sep 2021 19:30:58 +0000 (UTC) Received: by mail-lf1-f69.google.com with SMTP id i40-20020a0565123e2800b003f53da59009so16812760lfv.16 for ; Mon, 27 Sep 2021 12:30:58 -0700 (PDT) Received: from ws.lan.d-node.is ([95.165.29.203]) by smtp.gmail.com with ESMTPSA id a8sm1688090lfr.256.2021.09.27.12.30.57 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 27 Sep 2021 12:30:57 -0700 (PDT) X-MC-Unique: HTpmku5HOlqMggukKJtIGQ-1 X-MC-Unique: jZCCN9jJOnKnrM4OQQAa1Q-1 X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=q7jXkr2Lxl6EgX0xgbzn4xtMOGD4tWM3fsfROxdvRL0=; b=vObcE+cEuX9Gx18hEAhC+h6sraSGgAxGR8uEB37gybqiX0n2jcPDNtsUm+GNhUylX9 gVBZyUM78ZWHzF4ZIdeqg1V2DNkl1vQJkFL2KJGifa+Ce8Men41GBDaHRTu/IeFumB9i nPp3ilgS4GSMBMmTSK+wTq5lMbRvgjkP5w0lmnwNQFUOYyjSMzv3x7uO7nk1VBdfGGZH IRgHfLiZBVyKaPhMxjTK6/yyN+UPfABdUDenuxaeqeeE50/+fyeQia6pKpbtpZfPpLmn 8+jAjUN30s+UBEGVVRmwXevV98BBXMiQXa2yMW5j/XyhRTOjigKmYYGq7PFXP1EgkCQm y4rA== X-Gm-Message-State: AOAM531BqBM4NRNi+0gy7pzj5ymvTadqsPnXoOXdq+7sukQjwvOc5Njf ICMyRmdgbi0wmDED6ed4XznRnLe0BV3YHXaib/PIQxUJskYXuunYJGBwASnm9HKz9pbYV2XsIrj AiUm7WjQuSviYJ6NzRSmiNlnGnBDb063Igw== X-Received: by 2002:a2e:2201:: with SMTP id i1mr1483335lji.483.1632771057988; Mon, 27 Sep 2021 12:30:57 -0700 (PDT) X-Google-Smtp-Source: ABdhPJzWKapp2WeI5zZnMHMTg6oT5cZHo9V9b7WCTXpP12V+QJ6JUFAykprK0TIFvgABwld4mGu5kw== X-Received: by 2002:a2e:2201:: with SMTP id i1mr1483315lji.483.1632771057759; Mon, 27 Sep 2021 12:30:57 -0700 (PDT) From: Dmitrii Shcherbakov To: libvir-list@redhat.com, dmitrii.shcherbakov@canonical.com Subject: [libvirt PATCH v5 7/7] Add PCI VPD Capability Documentation Date: Mon, 27 Sep 2021 22:30:53 +0300 Message-Id: <20210927193053.109029-8-dmitrii.shcherbakov@canonical.com> In-Reply-To: <20210927193053.109029-1-dmitrii.shcherbakov@canonical.com> References: <20210927193053.109029-1-dmitrii.shcherbakov@canonical.com> MIME-Version: 1.0 X-Mimecast-Impersonation-Protect: Policy=CLT - Impersonation Protection Definition; Similar Internal Domain=false; Similar Monitored External Domain=false; Custom External Domain=false; Mimecast External Domain=false; Newly Observed Domain=false; Internal User Name=false; Custom Display Name List=false; Reply-to Address Mismatch=false; Targeted Threat Dictionary=false; Mimecast Threat Dictionary=false; Custom Threat Dictionary=false X-Scanned-By: MIMEDefang 2.78 on 10.11.54.4 X-MIME-Autoconverted: from quoted-printable to 8bit by lists01.pubmisc.prod.ext.phx2.redhat.com id 18RJVAsK014454 X-loop: libvir-list@redhat.com X-BeenThere: libvir-list@redhat.com X-Mailman-Version: 2.1.12 Precedence: junk List-Id: Development discussions about the libvirt library & tools List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: libvir-list-bounces@redhat.com Errors-To: libvir-list-bounces@redhat.com X-Scanned-By: MIMEDefang 2.79 on 10.5.11.12 Authentication-Results: relay.mimecast.com; auth=pass smtp.auth=CUSA124A263 smtp.mailfrom=libvir-list-bounces@redhat.com X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Content-Transfer-Encoding: quoted-printable X-ZM-MESSAGEID: 1632771097512100001 Content-Type: text/plain; charset="utf-8" Describes the format of the newly added VPD capability and gives and example for a real-world device. Signed-off-by: Dmitrii Shcherbakov --- docs/drvnodedev.html.in | 46 +++++++++++++++++++++++++++++++++++++++++ docs/formatnode.html.in | 24 ++++++++++++++++++++- 2 files changed, 69 insertions(+), 1 deletion(-) diff --git a/docs/drvnodedev.html.in b/docs/drvnodedev.html.in index 70f7e6717d..438d20f2dd 100644 --- a/docs/drvnodedev.html.in +++ b/docs/drvnodedev.html.in @@ -185,6 +185,52 @@ </capability> </device> =20 +

VPD capability

+

+ A device that exposes a PCI/PCIe VPD capability will include a nested + capability vpd which presents all resources (string, re= ad-only + and read-write keyword resources) that may be contained in a valid V= PD + entry. The XML format follows the binary format described in + "I.3. VPD Definitions" in PCI Local Bus (2.2+) and the identical for= mat + in PCIe 4.0+. At the time of writing, the support for exposing this = capability + is only present on Linux-based systems (kernel version v2.6.26 is th= e first one + to expose VPD via sysfs which Libvirt relies on). Reading the VPD co= ntents requires + root privileges, therefore, virsh nodedev-dumpxml must = be executed + accordingly. + A description of the XML format for the vpd capability = can + be found here. +

+

+ The following example shows a VPD representation for a device that e= xposes the + VPD capability with a string resource and a VPD-R resource. Among ot= her things, + the VPD of this particular device includes a unique board serial num= ber in the + "SN" field. +

+
+<device>
+  <!-- ... -->
+  <driver>
+    <name>mlx5_core</name>
+  </driver>
+  <capability type=3D'pci'>
+    <!-- ... -->
+    <capability type=3D'vpd'>
+      <resource type=3D'string'>BlueField-2 DPU 25GbE Dual=
-Port SFP56, 16GB on-board DDR, 1GbE OOB management, Tall Bracket</resou=
rce>
+      <resource type=3D'vpd-r'>
+        <field keyword=3D'SN'>MT2113X00000</field>
+        <field keyword=3D'V3'>d644e35a61d0eb11e000e8cef671=
bf3e</field>
+        <field keyword=3D'VA'>MLX:MN=3DMLNX:CSKU=3DV2:UUID=
=3DV3:PCI=3DV0:MODL=3DBF2H332A</field>
+        <field keyword=3D'PN'>MBF2H332A-AEEOT</field>
+        <field keyword=3D'V2'>MBF2H332A-AEEOT</field>
+        <field keyword=3D'EC'>B1</field>
+        <field keyword=3D'V0'>PCIeGen4 x8</field>
+      </resource>
+    </capability>
+  <!-- ... -->
+  </capability>
+</device>
+
+

Mediated devices (MDEVs)

Mediated devices (Since 3.2.0) are soft= ware diff --git a/docs/formatnode.html.in b/docs/formatnode.html.in index 3b3c3105d4..3e8c771398 100644 --- a/docs/formatnode.html.in +++ b/docs/formatnode.html.in @@ -162,7 +162,13 @@ This device is capable of creating mediated devices. The sub-elements are summarized in mdev_types capability. - + +

vpd
+
+ This device exposes a VPD PCI/PCIe capability. + The sub-elements are summarized in + vpd capability. +
=20 @@ -592,5 +598,21 @@ </device> =20 +

vpd capability

+ +

+ PCI devices can expose a VPD capability w= hich + is optional per PCI Local Bus 2.2+ and PCIe 4.0+ specifications. If + the VPD capability is present, then the parent capability + element with the vpd type will contain a list of + resource elements with contents applicable to a + particular resource type (string or keyword resources). The + resource element will either contain a string + resource value or a list of field elements in + the case of keyword resources. field elements + contain a keyword attribute corresponding to the + 2-byte VPD keyword and text as element data representing the + field value. +

--=20 2.30.2