51d9a2
From 05b6b6d0d034e0a2ecb9b826bf5052a3bada218d Mon Sep 17 00:00:00 2001
51d9a2
Message-Id: <05b6b6d0d034e0a2ecb9b826bf5052a3bada218d@dist-git>
51d9a2
From: Erik Skultety <eskultet@redhat.com>
51d9a2
Date: Mon, 20 Aug 2018 17:18:53 +0200
51d9a2
Subject: [PATCH] qemu: caps: Format SEV platform data into qemuCaps cache
51d9a2
MIME-Version: 1.0
51d9a2
Content-Type: text/plain; charset=UTF-8
51d9a2
Content-Transfer-Encoding: 8bit
51d9a2
51d9a2
Since we're not saving the platform-specific data into a cache, we're
51d9a2
not going to populate the structure, which in turn will cause a crash
51d9a2
upon calling virNodeGetSEVInfo because of a NULL pointer dereference.
51d9a2
Ultimately, we should start caching this data along with host-specific
51d9a2
capabilities like NUMA and SELinux stuff into a separate cache, but for
51d9a2
the time being, this is a semi-proper fix for a potential crash.
51d9a2
51d9a2
Backtrace (requires libvirtd restart to load qemu caps from cache):
51d9a2
    #0 qemuGetSEVInfoToParams
51d9a2
    #1 qemuNodeGetSEVInfo
51d9a2
    #2 virNodeGetSEVInfo
51d9a2
    #3 remoteDispatchNodeGetSevInfo
51d9a2
    #4 remoteDispatchNodeGetSevInfoHelper
51d9a2
    #5 virNetServerProgramDispatchCall
51d9a2
    #6 virNetServerProgramDispatch
51d9a2
    #7 virNetServerProcessMsg
51d9a2
    #8 virNetServerHandleJob
51d9a2
    #9 virThreadPoolWorker
51d9a2
    #10 virThreadHelper
51d9a2
51d9a2
https: //bugzilla.redhat.com/show_bug.cgi?id=1612009
51d9a2
Signed-off-by: Erik Skultety <eskultet@redhat.com>
51d9a2
Acked-by: Peter Krempa <pkrempa@redhat.com>
51d9a2
Tested-by: Brijesh Singh <brijesh.singh@amd.com>
51d9a2
(cherry picked from commit 77f51ab52049734d80a8ccb79b80189c7fb95c41)
51d9a2
51d9a2
https://bugzilla.redhat.com/show_bug.cgi?id=1612009
51d9a2
https://bugzilla.redhat.com/show_bug.cgi?id=1619150
51d9a2
51d9a2
Amend:
51d9a2
    - fixed the VIR_AUTOPTR bits which downstream doesn't support
51d9a2
    and wouldn't compile
51d9a2
51d9a2
Signed-off-by: Erik Skultety <eskultet@redhat.com>
51d9a2
Reviewed-by: Ján Tomko <jtomko@redhat.com>
51d9a2
---
51d9a2
 src/qemu/qemu_capabilities.c                  | 109 ++++++++++++++++++
51d9a2
 .../qemu_2.12.0.x86_64.xml                    |   5 +-
51d9a2
 .../caps_2.12.0.x86_64.xml                    |   6 +
51d9a2
 3 files changed, 119 insertions(+), 1 deletion(-)
51d9a2
51d9a2
diff --git a/src/qemu/qemu_capabilities.c b/src/qemu/qemu_capabilities.c
51d9a2
index 470f25a5a6..4f2051a2bb 100644
51d9a2
--- a/src/qemu/qemu_capabilities.c
51d9a2
+++ b/src/qemu/qemu_capabilities.c
51d9a2
@@ -1573,6 +1573,30 @@ virQEMUCapsHostCPUDataClear(virQEMUCapsHostCPUDataPtr cpuData)
51d9a2
 }
51d9a2
 
51d9a2
 
51d9a2
+static int
51d9a2
+virQEMUCapsSEVInfoCopy(virSEVCapabilityPtr *dst,
51d9a2
+                       virSEVCapabilityPtr src)
51d9a2
+{
51d9a2
+    int ret = -1;
51d9a2
+    virSEVCapabilityPtr tmp = NULL;
51d9a2
+
51d9a2
+    if (VIR_ALLOC(tmp) < 0 ||
51d9a2
+        VIR_STRDUP(tmp->pdh, src->pdh) < 0 ||
51d9a2
+        VIR_STRDUP(tmp->cert_chain, src->cert_chain) < 0)
51d9a2
+        goto cleanup;
51d9a2
+
51d9a2
+    tmp->cbitpos = src->cbitpos;
51d9a2
+    tmp->reduced_phys_bits = src->reduced_phys_bits;
51d9a2
+
51d9a2
+    VIR_STEAL_PTR(*dst, tmp);
51d9a2
+
51d9a2
+    ret = 0;
51d9a2
+ cleanup:
51d9a2
+    virSEVCapabilitiesFree(tmp);
51d9a2
+    return ret;
51d9a2
+}
51d9a2
+
51d9a2
+
51d9a2
 virQEMUCapsPtr virQEMUCapsNewCopy(virQEMUCapsPtr qemuCaps)
51d9a2
 {
51d9a2
     virQEMUCapsPtr ret = virQEMUCapsNew();
51d9a2
@@ -1635,6 +1659,11 @@ virQEMUCapsPtr virQEMUCapsNewCopy(virQEMUCapsPtr qemuCaps)
51d9a2
     for (i = 0; i < qemuCaps->ngicCapabilities; i++)
51d9a2
         ret->gicCapabilities[i] = qemuCaps->gicCapabilities[i];
51d9a2
 
51d9a2
+    if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_SEV_GUEST) &&
51d9a2
+        virQEMUCapsSEVInfoCopy(&ret->sevCapabilities,
51d9a2
+                               qemuCaps->sevCapabilities) < 0)
51d9a2
+        goto error;
51d9a2
+
51d9a2
     return ret;
51d9a2
 
51d9a2
  error:
51d9a2
@@ -3273,6 +3302,62 @@ virQEMUCapsCachePrivFree(void *privData)
51d9a2
 }
51d9a2
 
51d9a2
 
51d9a2
+static int
51d9a2
+virQEMUCapsParseSEVInfo(virQEMUCapsPtr qemuCaps, xmlXPathContextPtr ctxt)
51d9a2
+{
51d9a2
+    int ret = -1;
51d9a2
+    virSEVCapabilityPtr sev = NULL;
51d9a2
+
51d9a2
+    if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_SEV_GUEST))
51d9a2
+        return 0;
51d9a2
+
51d9a2
+    if (virXPathBoolean("boolean(./sev)", ctxt) == 0) {
51d9a2
+        virReportError(VIR_ERR_XML_ERROR, "%s",
51d9a2
+                       _("missing SEV platform data in QEMU "
51d9a2
+                         "capabilities cache"));
51d9a2
+        return -1;
51d9a2
+    }
51d9a2
+
51d9a2
+    if (VIR_ALLOC(sev) < 0)
51d9a2
+        return -1;
51d9a2
+
51d9a2
+    if (virXPathUInt("string(./sev/cbitpos)", ctxt, &sev->cbitpos) < 0) {
51d9a2
+        virReportError(VIR_ERR_XML_ERROR, "%s",
51d9a2
+                       _("missing or malformed SEV cbitpos information "
51d9a2
+                         "in QEMU capabilities cache"));
51d9a2
+        goto cleanup;
51d9a2
+    }
51d9a2
+
51d9a2
+    if (virXPathUInt("string(./sev/reducedPhysBits)", ctxt,
51d9a2
+                     &sev->reduced_phys_bits) < 0) {
51d9a2
+        virReportError(VIR_ERR_XML_ERROR, "%s",
51d9a2
+                       _("missing or malformed SEV reducedPhysBits information "
51d9a2
+                         "in QEMU capabilities cache"));
51d9a2
+        goto cleanup;
51d9a2
+    }
51d9a2
+
51d9a2
+    if (!(sev->pdh = virXPathString("string(./sev/pdh)", ctxt)))  {
51d9a2
+        virReportError(VIR_ERR_XML_ERROR, "%s",
51d9a2
+                       _("missing SEV pdh information "
51d9a2
+                         "in QEMU capabilities cache"));
51d9a2
+        goto cleanup;
51d9a2
+    }
51d9a2
+
51d9a2
+    if (!(sev->cert_chain = virXPathString("string(./sev/certChain)", ctxt))) {
51d9a2
+        virReportError(VIR_ERR_XML_ERROR, "%s",
51d9a2
+                       _("missing SEV certChain information "
51d9a2
+                         "in QEMU capabilities cache"));
51d9a2
+        goto cleanup;
51d9a2
+    }
51d9a2
+
51d9a2
+    VIR_STEAL_PTR(qemuCaps->sevCapabilities, sev);
51d9a2
+    ret = 0;
51d9a2
+ cleanup:
51d9a2
+    virSEVCapabilitiesFree(sev);
51d9a2
+    return ret;
51d9a2
+}
51d9a2
+
51d9a2
+
51d9a2
 /*
51d9a2
  * Parsing a doc that looks like
51d9a2
  *
51d9a2
@@ -3521,6 +3606,9 @@ virQEMUCapsLoadCache(virArch hostArch,
51d9a2
     }
51d9a2
     VIR_FREE(nodes);
51d9a2
 
51d9a2
+    if (virQEMUCapsParseSEVInfo(qemuCaps, ctxt) < 0)
51d9a2
+        goto cleanup;
51d9a2
+
51d9a2
     virQEMUCapsInitHostCPUModel(qemuCaps, hostArch, VIR_DOMAIN_VIRT_KVM);
51d9a2
     virQEMUCapsInitHostCPUModel(qemuCaps, hostArch, VIR_DOMAIN_VIRT_QEMU);
51d9a2
 
51d9a2
@@ -3638,6 +3726,24 @@ virQEMUCapsFormatCPUModels(virQEMUCapsPtr qemuCaps,
51d9a2
 }
51d9a2
 
51d9a2
 
51d9a2
+static void
51d9a2
+virQEMUCapsFormatSEVInfo(virQEMUCapsPtr qemuCaps, virBufferPtr buf)
51d9a2
+{
51d9a2
+    virSEVCapabilityPtr sev = virQEMUCapsGetSEVCapabilities(qemuCaps);
51d9a2
+
51d9a2
+    virBufferAddLit(buf, "<sev>\n");
51d9a2
+    virBufferAdjustIndent(buf, 2);
51d9a2
+    virBufferAsprintf(buf, "<cbitpos>%u</cbitpos>\n", sev->cbitpos);
51d9a2
+    virBufferAsprintf(buf, "<reducedPhysBits>%u</reducedPhysBits>\n",
51d9a2
+                      sev->reduced_phys_bits);
51d9a2
+    virBufferEscapeString(buf, "<pdh>%s</pdh>\n", sev->pdh);
51d9a2
+    virBufferEscapeString(buf, "<certChain>%s</certChain>\n",
51d9a2
+                          sev->cert_chain);
51d9a2
+    virBufferAdjustIndent(buf, -2);
51d9a2
+    virBufferAddLit(buf, "</sev>\n");
51d9a2
+}
51d9a2
+
51d9a2
+
51d9a2
 char *
51d9a2
 virQEMUCapsFormatCache(virQEMUCapsPtr qemuCaps)
51d9a2
 {
51d9a2
@@ -3719,6 +3825,9 @@ virQEMUCapsFormatCache(virQEMUCapsPtr qemuCaps)
51d9a2
                           emulated ? "yes" : "no");
51d9a2
     }
51d9a2
 
51d9a2
+    if (qemuCaps->sevCapabilities)
51d9a2
+        virQEMUCapsFormatSEVInfo(qemuCaps, &buf;;
51d9a2
+
51d9a2
     virBufferAdjustIndent(&buf, -2);
51d9a2
     virBufferAddLit(&buf, "</qemuCaps>\n");
51d9a2
 
51d9a2
diff --git a/tests/domaincapsschemadata/qemu_2.12.0.x86_64.xml b/tests/domaincapsschemadata/qemu_2.12.0.x86_64.xml
51d9a2
index 7a1be4c093..a8d6a4d629 100644
51d9a2
--- a/tests/domaincapsschemadata/qemu_2.12.0.x86_64.xml
51d9a2
+++ b/tests/domaincapsschemadata/qemu_2.12.0.x86_64.xml
51d9a2
@@ -142,6 +142,9 @@
51d9a2
     <gic supported='no'/>
51d9a2
     <vmcoreinfo supported='yes'/>
51d9a2
     <genid supported='yes'/>
51d9a2
-    <sev supported='no'/>
51d9a2
+    <sev supported='yes'>
51d9a2
+      <cbitpos>47</cbitpos>
51d9a2
+      <reducedPhysBits>1</reducedPhysBits>
51d9a2
+    </sev>
51d9a2
   </features>
51d9a2
 </domainCapabilities>
51d9a2
diff --git a/tests/qemucapabilitiesdata/caps_2.12.0.x86_64.xml b/tests/qemucapabilitiesdata/caps_2.12.0.x86_64.xml
51d9a2
index 78889facce..f0dc082640 100644
51d9a2
--- a/tests/qemucapabilitiesdata/caps_2.12.0.x86_64.xml
51d9a2
+++ b/tests/qemucapabilitiesdata/caps_2.12.0.x86_64.xml
51d9a2
@@ -1254,4 +1254,10 @@
51d9a2
   <machine name='pc-0.11' hotplugCpus='yes' maxCpus='255'/>
51d9a2
   <machine name='pc-0.12' hotplugCpus='yes' maxCpus='255'/>
51d9a2
   <machine name='pc-0.10' hotplugCpus='yes' maxCpus='255'/>
51d9a2
+  <sev>
51d9a2
+    <cbitpos>47</cbitpos>
51d9a2
+    <reducedPhysBits>1</reducedPhysBits>
51d9a2
+    <pdh>AQAAAAAOAAAAQAAAAAOAAAAQAAAAAOAAAAQAAAAAOAAAAQAAAAAOAAAAQAAAAAOAAA</pdh>
51d9a2
+    <certChain>AQAAAAAOAAAAQAAAAAOAAAAQAAAAAOAAAAQAAAAAOAAAAQAAAAAOAAA</certChain>
51d9a2
+  </sev>
51d9a2
 </qemuCaps>
51d9a2
-- 
51d9a2
2.18.0
51d9a2