[libvirt] [PATCH 3/3] virSysinfo: Introduce SMBIOS type 2 support

Michal Privoznik mprivozn at redhat.com
Tue May 12 14:56:24 UTC 2015


https://bugzilla.redhat.com/show_bug.cgi?id=1220527

This type of information defines attributes of a system
baseboard.  With one caveat: in qemu they call it family, while
in the specification it's referred to as type. I'm sticking with
the latter.

Signed-off-by: Michal Privoznik <mprivozn at redhat.com>
---
 docs/formatdomain.html.in                       |  37 +++++-
 docs/schemas/domaincommon.rng                   |  24 ++++
 src/conf/domain_conf.c                          |  57 +++++++++
 src/libvirt_private.syms                        |   1 +
 src/qemu/qemu_command.c                         |  48 +++++++
 src/util/virsysinfo.c                           | 160 +++++++++++++++++++++++-
 src/util/virsysinfo.h                           |  14 +++
 tests/qemuxml2argvdata/qemuxml2argv-smbios.args |   2 +
 tests/qemuxml2argvdata/qemuxml2argv-smbios.xml  |   9 ++
 9 files changed, 346 insertions(+), 6 deletions(-)

diff --git a/docs/formatdomain.html.in b/docs/formatdomain.html.in
index e0b6ba7..dc92aa3 100644
--- a/docs/formatdomain.html.in
+++ b/docs/formatdomain.html.in
@@ -375,6 +375,13 @@
       <entry name='product'>Virt-Manager</entry>
       <entry name='version'>0.9.4</entry>
     </system>
+    <baseBoard>
+      <entry name='manufacturer'>LENOVO</entry>
+      <entry name='product'>20BE0061MC</entry>
+      <entry name='version'>0B98401 Pro</entry>
+      <entry name='serial'>W1KS427111E</entry>
+      <entry name='type'>Motherboard</entry>
+    </baseBoard>
   </sysinfo>
   ...</pre>
 
@@ -435,11 +442,31 @@
                 <dt><code>family</code></dt>
                 <dd>Identify the family a particular computer belongs to.</dd>
             </dl>
-            NB: Incorrectly supplied entries in either the <code>bios</code>
-            or <code>system</code> blocks will be ignored without error.
-            Other than <code>uuid</code> validation and <code>date</code>
-            format checking, all values are passed as strings to the
-            hypervisor driver.
+          </dd>
+          <dt><code>baseBoard</code></dt>
+          <dd>
+            This is block 2 of SMBIOS, with entry names drawn from:
+            <dl>
+                <dt><code>manufacturer</code></dt>
+                <dd>Manufacturer of BIOS</dd>
+                <dt><code>product</code></dt>
+                <dd>Product Name</dd>
+                <dt><code>version</code></dt>
+                <dd>Version of the product</dd>
+                <dt><code>serial</code></dt>
+                <dd>Serial number</dd>
+                <dt><code>asset</code></dt>
+                <dd>Asset tag</dd>
+                <dt><code>location</code></dt>
+                <dd>Location in chassis</dd>
+                <dt><code>type</code></dt>
+                <dd>Board type</dd>
+            </dl>
+            NB: Incorrectly supplied entries in either the <code>bios</code> or
+            <code>system</code> or <code>baseBoard</code> blocks will be
+            ignored without error.  Other than <code>uuid</code> validation and
+            <code>date</code> format checking, all values are passed as strings
+            to the hypervisor driver.
           </dd>
         </dl>
       </dd>
diff --git a/docs/schemas/domaincommon.rng b/docs/schemas/domaincommon.rng
index c151e92..2bc84b5 100644
--- a/docs/schemas/domaincommon.rng
+++ b/docs/schemas/domaincommon.rng
@@ -4240,6 +4240,18 @@
             </oneOrMore>
           </element>
         </optional>
+        <optional>
+          <element name="baseBoard">
+            <oneOrMore>
+              <element name="entry">
+                <attribute name="name">
+                  <ref name="sysinfo-baseBoard-name"/>
+                </attribute>
+                <ref name="sysinfo-value"/>
+              </element>
+            </oneOrMore>
+          </element>
+        </optional>
       </interleave>
     </element>
   </define>
@@ -4265,6 +4277,18 @@
     </choice>
   </define>
 
+  <define name="sysinfo-baseBoard-name">
+    <choice>
+      <value>manufacturer</value>
+      <value>product</value>
+      <value>version</value>
+      <value>serial</value>
+      <value>asset</value>
+      <value>location</value>
+      <value>type</value>
+    </choice>
+  </define>
+
   <define name="sysinfo-value">
     <data type="string">
       <param name='pattern'>[a-zA-Z0-9/\-_\. \(\)]+</param>
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
index 209416d..0f41375 100644
--- a/src/conf/domain_conf.c
+++ b/src/conf/domain_conf.c
@@ -10997,6 +10997,52 @@ virSysinfoSystemParseXML(xmlNodePtr node,
     return ret;
 }
 
+static int
+virSysinfoBaseBoardParseXML(xmlNodePtr node,
+                            xmlXPathContextPtr ctxt,
+                            virSysinfoBaseBoardDefPtr *baseBoard)
+{
+    int ret = -1;
+    virSysinfoBaseBoardDefPtr def;
+
+    if (!xmlStrEqual(node->name, BAD_CAST "baseBoard")) {
+        virReportError(VIR_ERR_XML_ERROR, "%s",
+                       _("XML does not contain expected 'baseBoard' element"));
+        return ret;
+    }
+
+    if (VIR_ALLOC(def) < 0)
+        goto cleanup;
+
+    def->manufacturer =
+        virXPathString("string(entry[@name='manufacturer'])", ctxt);
+    def->product =
+        virXPathString("string(entry[@name='product'])", ctxt);
+    def->version =
+        virXPathString("string(entry[@name='version'])", ctxt);
+    def->serial =
+        virXPathString("string(entry[@name='serial'])", ctxt);
+    def->asset =
+        virXPathString("string(entry[@name='asset'])", ctxt);
+    def->location =
+        virXPathString("string(entry[@name='location'])", ctxt);
+    def->type =
+        virXPathString("string(entry[@name='type'])", ctxt);
+
+    if (!def->manufacturer && !def->product && !def->version &&
+        !def->serial && !def->asset && !def->location && !def->type) {
+        virSysinfoBaseBoardDefFree(def);
+        def = NULL;
+    }
+
+    *baseBoard = def;
+    def = NULL;
+    ret = 0;
+ cleanup:
+    virSysinfoBaseBoardDefFree(def);
+    return ret;
+}
+
 static virSysinfoDefPtr
 virSysinfoParseXML(xmlNodePtr node,
                   xmlXPathContextPtr ctxt,
@@ -11051,6 +11097,17 @@ virSysinfoParseXML(xmlNodePtr node,
         ctxt->node = oldnode;
     }
 
+    /* Extract system base board metadata */
+    if ((tmpnode = virXPathNode("./baseBoard[1]", ctxt)) != NULL) {
+        oldnode = ctxt->node;
+        ctxt->node = tmpnode;
+        if (virSysinfoBaseBoardParseXML(tmpnode, ctxt, &def->baseBoard) < 0) {
+            ctxt->node = oldnode;
+            goto error;
+        }
+        ctxt->node = oldnode;
+    }
+
  cleanup:
     VIR_FREE(type);
     return def;
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index bd619d3..3178fe9 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -2176,6 +2176,7 @@ virVasprintfInternal;
 
 
 # util/virsysinfo.h
+virSysinfoBaseBoardDefFree;
 virSysinfoBIOSDefFree;
 virSysinfoDefFree;
 virSysinfoFormat;
diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c
index 489cab3..c13089e 100644
--- a/src/qemu/qemu_command.c
+++ b/src/qemu/qemu_command.c
@@ -6738,6 +6738,48 @@ static char *qemuBuildSmbiosSystemStr(virSysinfoSystemDefPtr def,
     return NULL;
 }
 
+static char *qemuBuildSmbiosBaseBoardStr(virSysinfoBaseBoardDefPtr def)
+{
+    virBuffer buf = VIR_BUFFER_INITIALIZER;
+
+    if (!def)
+        return NULL;
+
+    virBufferAddLit(&buf, "type=2");
+
+    /* 1:Manufacturer */
+    if (def->manufacturer)
+        virBufferAsprintf(&buf, ",manufacturer=%s",
+                          def->manufacturer);
+     /* 1:Product Name */
+    if (def->product)
+        virBufferAsprintf(&buf, ",product=%s", def->product);
+    /* 1:Version */
+    if (def->version)
+        virBufferAsprintf(&buf, ",version=%s", def->version);
+    /* 1:Serial Number */
+    if (def->serial)
+        virBufferAsprintf(&buf, ",serial=%s", def->serial);
+    /* 1:Asset Tag */
+    if (def->asset)
+        virBufferAsprintf(&buf, ",asset=%s", def->asset);
+    /* 1:Location */
+    if (def->location)
+        virBufferAsprintf(&buf, ",location=%s", def->location);
+    /* 1:Type */
+    if (def->type)
+        virBufferAsprintf(&buf, ",family=%s", def->type);
+
+    if (virBufferCheckError(&buf) < 0)
+        goto error;
+
+    return virBufferContentAndReset(&buf);
+
+ error:
+    virBufferFreeAndReset(&buf);
+    return NULL;
+}
+
 static char *
 qemuBuildClockArgStr(virDomainClockDefPtr def)
 {
@@ -8925,6 +8967,12 @@ qemuBuildCommandLine(virConnectPtr conn,
                 virCommandAddArgList(cmd, "-smbios", smbioscmd, NULL);
                 VIR_FREE(smbioscmd);
             }
+
+            smbioscmd = qemuBuildSmbiosBaseBoardStr(source->baseBoard);
+            if (smbioscmd != NULL) {
+                virCommandAddArgList(cmd, "-smbios", smbioscmd, NULL);
+                VIR_FREE(smbioscmd);
+            }
         }
     }
 
diff --git a/src/util/virsysinfo.c b/src/util/virsysinfo.c
index 4c939ec..bb21e1b 100644
--- a/src/util/virsysinfo.c
+++ b/src/util/virsysinfo.c
@@ -91,6 +91,20 @@ void virSysinfoSystemDefFree(virSysinfoSystemDefPtr def)
     VIR_FREE(def);
 }
 
+void virSysinfoBaseBoardDefFree(virSysinfoBaseBoardDefPtr def)
+{
+    if (def == NULL)
+        return;
+
+    VIR_FREE(def->manufacturer);
+    VIR_FREE(def->product);
+    VIR_FREE(def->version);
+    VIR_FREE(def->serial);
+    VIR_FREE(def->asset);
+    VIR_FREE(def->location);
+    VIR_FREE(def->type);
+    VIR_FREE(def);
+}
 
 /**
  * virSysinfoDefFree:
@@ -108,6 +122,7 @@ void virSysinfoDefFree(virSysinfoDefPtr def)
 
     virSysinfoBIOSDefFree(def->bios);
     virSysinfoSystemDefFree(def->system);
+    virSysinfoBaseBoardDefFree(def->baseBoard);
 
     for (i = 0; i < def->nprocessor; i++) {
         VIR_FREE(def->processor[i].processor_socket_destination);
@@ -719,6 +734,77 @@ virSysinfoParseSystem(const char *base, virSysinfoSystemDefPtr *system)
 }
 
 static int
+virSysinfoParseBaseBoard(const char *base, virSysinfoBaseBoardDefPtr *baseBoard)
+{
+    int ret = -1;
+    const char *cur, *eol = NULL;
+    virSysinfoBaseBoardDefPtr def;
+
+    if ((cur = strstr(base, "Base Board Information")) == NULL)
+        return 0;
+
+    if (VIR_ALLOC(def) < 0)
+        return ret;
+
+    base = cur;
+    if ((cur = strstr(base, "Manufacturer: ")) != NULL) {
+        cur += 14;
+        eol = strchr(cur, '\n');
+        if (eol && VIR_STRNDUP(def->manufacturer, cur, eol - cur) < 0)
+            goto cleanup;
+    }
+    if ((cur = strstr(base, "Product Name: ")) != NULL) {
+        cur += 14;
+        eol = strchr(cur, '\n');
+        if (eol && VIR_STRNDUP(def->product, cur, eol - cur) < 0)
+            goto cleanup;
+    }
+    if ((cur = strstr(base, "Version: ")) != NULL) {
+        cur += 9;
+        eol = strchr(cur, '\n');
+        if (eol && VIR_STRNDUP(def->version, cur, eol - cur) < 0)
+            goto cleanup;
+    }
+    if ((cur = strstr(base, "Serial Number: ")) != NULL) {
+        cur += 15;
+        eol = strchr(cur, '\n');
+        if (eol && VIR_STRNDUP(def->serial, cur, eol - cur) < 0)
+            goto cleanup;
+    }
+    if ((cur = strstr(base, "Asset Tag: ")) != NULL) {
+        cur += 11;
+        eol = strchr(cur, '\n');
+        if (eol && VIR_STRNDUP(def->asset, cur, eol - cur) < 0)
+            goto cleanup;
+    }
+    if ((cur = strstr(base, "Location In Chassis: ")) != NULL) {
+        cur += 21;
+        eol = strchr(cur, '\n');
+        if (eol && VIR_STRNDUP(def->location, cur, eol - cur) < 0)
+            goto cleanup;
+    }
+    if ((cur = strstr(base, "Type: ")) != NULL) {
+        cur += 6;
+        eol = strchr(cur, '\n');
+        if (eol && VIR_STRNDUP(def->type, cur, eol - cur) < 0)
+            goto cleanup;
+    }
+
+    if (!def->manufacturer && !def->product && !def->version &&
+        !def->serial && !def->asset && !def->location && !def->type) {
+        virSysinfoBaseBoardDefFree(def);
+        def = NULL;
+    }
+
+    *baseBoard = def;
+    def = NULL;
+    ret = 0;
+ cleanup:
+    virSysinfoBaseBoardDefFree(def);
+    return ret;
+}
+
+static int
 virSysinfoParseProcessor(const char *base, virSysinfoDefPtr ret)
 {
     const char *cur, *tmp_base;
@@ -940,7 +1026,7 @@ virSysinfoRead(void)
         return NULL;
     }
 
-    cmd = virCommandNewArgList(path, "-q", "-t", "0,1,4,17", NULL);
+    cmd = virCommandNewArgList(path, "-q", "-t", "0,1,2,4,17", NULL);
     VIR_FREE(path);
     virCommandSetOutputBuffer(cmd, &outbuf);
     if (virCommandRun(cmd, NULL) < 0)
@@ -957,6 +1043,9 @@ virSysinfoRead(void)
     if (virSysinfoParseSystem(outbuf, &ret->system) < 0)
         goto error;
 
+    if (virSysinfoParseBaseBoard(outbuf, &ret->baseBoard) < 0)
+        goto error;
+
     ret->nprocessor = 0;
     ret->processor = NULL;
     if (virSysinfoParseProcessor(outbuf, ret) < 0)
@@ -1027,6 +1116,32 @@ virSysinfoSystemFormat(virBufferPtr buf, virSysinfoSystemDefPtr def)
 }
 
 static void
+virSysinfoBaseBoardFormat(virBufferPtr buf, virSysinfoBaseBoardDefPtr def)
+{
+    if (!def)
+        return;
+
+    virBufferAddLit(buf, "<baseBoard>\n");
+    virBufferAdjustIndent(buf, 2);
+    virBufferEscapeString(buf, "<entry name='manufacturer'>%s</entry>\n",
+                          def->manufacturer);
+    virBufferEscapeString(buf, "<entry name='product'>%s</entry>\n",
+                          def->product);
+    virBufferEscapeString(buf, "<entry name='version'>%s</entry>\n",
+                          def->version);
+    virBufferEscapeString(buf, "<entry name='serial'>%s</entry>\n",
+                          def->serial);
+    virBufferEscapeString(buf, "<entry name='asset'>%s</entry>\n",
+                          def->asset);
+    virBufferEscapeString(buf, "<entry name='location'>%s</entry>\n",
+                          def->location);
+    virBufferEscapeString(buf, "<entry name='type'>%s</entry>\n",
+                          def->type);
+    virBufferAdjustIndent(buf, -2);
+    virBufferAddLit(buf, "</baseBoard>\n");
+}
+
+static void
 virSysinfoProcessorFormat(virBufferPtr buf, virSysinfoDefPtr def)
 {
     size_t i;
@@ -1157,6 +1272,7 @@ virSysinfoFormat(virBufferPtr buf, virSysinfoDefPtr def)
 
     virSysinfoBIOSFormat(buf, def->bios);
     virSysinfoSystemFormat(buf, def->system);
+    virSysinfoBaseBoardFormat(buf, def->baseBoard);
     virSysinfoProcessorFormat(buf, def);
     virSysinfoMemoryFormat(buf, def);
 
@@ -1227,6 +1343,7 @@ virSysinfoSystemIsEqual(virSysinfoSystemDefPtr src,
                            desc, NULLSTR(src->name), NULLSTR(dst->name)); \
         }                                                               \
     } while (0)
+
     CHECK_FIELD(manufacturer, "system vendor");
     CHECK_FIELD(product, "system product");
     CHECK_FIELD(version, "system version");
@@ -1241,6 +1358,44 @@ virSysinfoSystemIsEqual(virSysinfoSystemDefPtr src,
     return identical;
 }
 
+static bool
+virSysinfoBaseBoardIsEqual(virSysinfoBaseBoardDefPtr src,
+                           virSysinfoBaseBoardDefPtr dst)
+{
+    bool identical = false;
+
+    if (!src && !dst)
+        return true;
+
+    if ((src && !dst) || (!src && dst)) {
+        virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+                       _("Target base board does not match source"));
+        goto cleanup;
+    }
+
+#define CHECK_FIELD(name, desc)                                         \
+    do {                                                                \
+        if (STRNEQ_NULLABLE(src->name, dst->name)) {                    \
+            virReportError(VIR_ERR_CONFIG_UNSUPPORTED,                  \
+                           _("Target sysinfo %s %s does not match source %s"), \
+                           desc, NULLSTR(src->name), NULLSTR(dst->name)); \
+        }                                                               \
+    } while (0)
+
+    CHECK_FIELD(manufacturer, "base board vendor");
+    CHECK_FIELD(product, "base board product");
+    CHECK_FIELD(version, "base board version");
+    CHECK_FIELD(serial, "base board serial");
+    CHECK_FIELD(asset, "base board asset");
+    CHECK_FIELD(location, "base board location");
+    CHECK_FIELD(type, "base board type");
+#undef CHECK_FIELD
+
+    identical = true;
+ cleanup:
+    return identical;
+}
+
 bool virSysinfoIsEqual(virSysinfoDefPtr src,
                        virSysinfoDefPtr dst)
 {
@@ -1269,6 +1424,9 @@ bool virSysinfoIsEqual(virSysinfoDefPtr src,
     if (!virSysinfoSystemIsEqual(src->system, dst->system))
         goto cleanup;
 
+    if (!virSysinfoBaseBoardIsEqual(src->baseBoard, dst->baseBoard))
+        goto cleanup;
+
     identical = true;
 
  cleanup:
diff --git a/src/util/virsysinfo.h b/src/util/virsysinfo.h
index c8cc1e8..fd727a6 100644
--- a/src/util/virsysinfo.h
+++ b/src/util/virsysinfo.h
@@ -86,6 +86,18 @@ struct _virSysinfoSystemDef {
     char *family;
 };
 
+typedef struct _virSysinfoBaseBoardDef virSysinfoBaseBoardDef;
+typedef virSysinfoBaseBoardDef *virSysinfoBaseBoardDefPtr;
+struct _virSysinfoBaseBoardDef {
+    char *manufacturer;
+    char *product;
+    char *version;
+    char *serial;
+    char *asset;
+    char *location;
+    char *type;
+};
+
 typedef struct _virSysinfoDef virSysinfoDef;
 typedef virSysinfoDef *virSysinfoDefPtr;
 struct _virSysinfoDef {
@@ -93,6 +105,7 @@ struct _virSysinfoDef {
 
     virSysinfoBIOSDefPtr bios;
     virSysinfoSystemDefPtr system;
+    virSysinfoBaseBoardDefPtr baseBoard;
 
     size_t nprocessor;
     virSysinfoProcessorDefPtr processor;
@@ -105,6 +118,7 @@ virSysinfoDefPtr virSysinfoRead(void);
 
 void virSysinfoBIOSDefFree(virSysinfoBIOSDefPtr def);
 void virSysinfoSystemDefFree(virSysinfoSystemDefPtr def);
+void virSysinfoBaseBoardDefFree(virSysinfoBaseBoardDefPtr def);
 void virSysinfoDefFree(virSysinfoDefPtr def);
 
 int virSysinfoFormat(virBufferPtr buf, virSysinfoDefPtr def)
diff --git a/tests/qemuxml2argvdata/qemuxml2argv-smbios.args b/tests/qemuxml2argvdata/qemuxml2argv-smbios.args
index e939aca..7336cbe 100644
--- a/tests/qemuxml2argvdata/qemuxml2argv-smbios.args
+++ b/tests/qemuxml2argvdata/qemuxml2argv-smbios.args
@@ -4,5 +4,7 @@ pc -m 214 -smp 1 -smbios 'type=0,vendor=LENOVO,version=6FET82WW (3.12 )' \
 -smbios 'type=1,manufacturer=Fedora,product=Virt-Manager,version=0.8.2-3.fc14,\
 serial=32dfcb37-5af1-552b-357c-be8c3aa38310,\
 uuid=c7a5fdbd-edaf-9455-926a-d65c16db1809,sku=1234567890,family=Red Hat' \
+-smbios 'type=2,manufacturer=Hewlett-Packard,product=0B4Ch,version=D,\
+serial=CZC1065993,asset=CZC1065993,location=Upside down,family=Motherboard' \
 -nographic -monitor unix:/tmp/test-monitor,server,nowait -no-acpi -boot c -usb -hda \
 /dev/HostVG/QEMUGuest1 -net none -serial none -parallel none
diff --git a/tests/qemuxml2argvdata/qemuxml2argv-smbios.xml b/tests/qemuxml2argvdata/qemuxml2argv-smbios.xml
index a2caeea..924596c 100644
--- a/tests/qemuxml2argvdata/qemuxml2argv-smbios.xml
+++ b/tests/qemuxml2argvdata/qemuxml2argv-smbios.xml
@@ -18,6 +18,15 @@
       <entry name='sku'>1234567890</entry>
       <entry name='family'>Red Hat</entry>
     </system>
+    <baseBoard>
+      <entry name='manufacturer'>Hewlett-Packard</entry>
+      <entry name='product'>0B4Ch</entry>
+      <entry name='version'>D</entry>
+      <entry name='serial'>CZC1065993</entry>
+      <entry name='asset'>CZC1065993</entry>
+      <entry name='location'>Upside down</entry>
+      <entry name='type'>Motherboard</entry>
+    </baseBoard>
   </sysinfo>
   <os>
     <type arch='i686' machine='pc'>hvm</type>
-- 
2.3.6




More information about the libvir-list mailing list