Blob Blame History Raw
From a2fe4ef20df702070ffec676172c19e6c89b8312 Mon Sep 17 00:00:00 2001
Message-Id: <a2fe4ef20df702070ffec676172c19e6c89b8312@dist-git>
From: Paolo Bonzini <pbonzini@redhat.com>
Date: Tue, 12 Dec 2017 16:23:40 +0100
Subject: [PATCH] conf: include x86 microcode version in virsh capabiltiies

A microcode update can cause the CPUID bits to change; an example
from the past was the update that disabled TSX on several Haswell and
Broadwell machines.

In order to track the x86 microcode version in the QEMU capabilities,
we have to fetch it and store it in the host CPU.  This also makes the
version visible in "virsh capabilities", which is a nice side effect.

CVE-2017-5715

Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
Signed-off-by: Jiri Denemark <jdenemar@redhat.com>

Conflicts:
	src/cpu/cpu_x86.c
            - context; x86DecodeCPUData has more parameters in 7.4
---
 src/conf/capabilities.c  | 12 ++++++++++++
 src/conf/capabilities.h  |  2 ++
 src/conf/cpu_conf.c      | 14 ++++++++++++++
 src/conf/cpu_conf.h      |  1 +
 src/cpu/cpu_x86.c        |  9 +++++++++
 src/libvirt_private.syms |  1 +
 6 files changed, 39 insertions(+)

diff --git a/src/conf/capabilities.c b/src/conf/capabilities.c
index 08907aced1..fad6cc1c07 100644
--- a/src/conf/capabilities.c
+++ b/src/conf/capabilities.c
@@ -355,6 +355,18 @@ virCapabilitiesAddHostNUMACell(virCapsPtr caps,
 }
 
 
+/**
+ * virCapabilitiesGetMicrocodeVersion:
+ * @caps: capabilities to access
+ *
+ * Get host CPU microcode version, or 0 if unavailable
+ */
+unsigned int
+virCapabilitiesGetMicrocodeVersion(virCapsPtr caps)
+{
+    return caps->host.cpu ? caps->host.cpu->microcodeVersion : 0;
+}
+
 /**
  * virCapabilitiesSetHostCPU:
  * @caps: capabilities to extend
diff --git a/src/conf/capabilities.h b/src/conf/capabilities.h
index d10eef3afd..75e0748e9e 100644
--- a/src/conf/capabilities.h
+++ b/src/conf/capabilities.h
@@ -299,6 +299,8 @@ virBitmapPtr virCapabilitiesGetCpusForNodemask(virCapsPtr caps,
 
 int virCapabilitiesGetNodeInfo(virNodeInfoPtr nodeinfo);
 
+unsigned int virCapabilitiesGetMicrocodeVersion(virCapsPtr caps);
+
 int virCapabilitiesInitPages(virCapsPtr caps);
 
 int virCapabilitiesInitNUMA(virCapsPtr caps);
diff --git a/src/conf/cpu_conf.c b/src/conf/cpu_conf.c
index e570dffcd2..239f671fa7 100644
--- a/src/conf/cpu_conf.c
+++ b/src/conf/cpu_conf.c
@@ -127,6 +127,7 @@ virCPUDefCopyModelFilter(virCPUDefPtr dst,
         VIR_STRDUP(dst->vendor_id, src->vendor_id) < 0 ||
         VIR_ALLOC_N(dst->features, src->nfeatures) < 0)
         return -1;
+    dst->microcodeVersion = src->microcodeVersion;
     dst->nfeatures_max = src->nfeatures;
     dst->nfeatures = 0;
 
@@ -178,6 +179,7 @@ virCPUDefStealModel(virCPUDefPtr dst,
 
     VIR_STEAL_PTR(dst->model, src->model);
     VIR_STEAL_PTR(dst->features, src->features);
+    dst->microcodeVersion = src->microcodeVersion;
     dst->nfeatures_max = src->nfeatures_max;
     src->nfeatures_max = 0;
     dst->nfeatures = src->nfeatures;
@@ -379,6 +381,14 @@ virCPUDefParseXML(xmlXPathContextPtr ctxt,
             goto cleanup;
         }
         VIR_FREE(arch);
+
+        if (virXPathBoolean("boolean(./microcode[1]/@version)", ctxt) > 0 &&
+            virXPathUInt("string(./microcode[1]/@version)", ctxt,
+                         &def->microcodeVersion) < 0) {
+            virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+                           _("invalid microcode version"));
+            goto cleanup;
+        }
     }
 
     if (!(def->model = virXPathString("string(./model[1])", ctxt)) &&
@@ -720,6 +730,10 @@ virCPUDefFormatBuf(virBufferPtr buf,
     if (formatModel && def->vendor)
         virBufferEscapeString(buf, "<vendor>%s</vendor>\n", def->vendor);
 
+    if (def->type == VIR_CPU_TYPE_HOST && def->microcodeVersion)
+        virBufferAsprintf(buf, "<microcode version='%u'/>\n",
+                          def->microcodeVersion);
+
     if (def->sockets && def->cores && def->threads) {
         virBufferAddLit(buf, "<topology");
         virBufferAsprintf(buf, " sockets='%u'", def->sockets);
diff --git a/src/conf/cpu_conf.h b/src/conf/cpu_conf.h
index 1978814d36..1a13ae6e46 100644
--- a/src/conf/cpu_conf.h
+++ b/src/conf/cpu_conf.h
@@ -133,6 +133,7 @@ struct _virCPUDef {
     char *vendor_id;    /* vendor id returned by CPUID in the guest */
     int fallback;       /* enum virCPUFallback */
     char *vendor;
+    unsigned int microcodeVersion;
     unsigned int sockets;
     unsigned int cores;
     unsigned int threads;
diff --git a/src/cpu/cpu_x86.c b/src/cpu/cpu_x86.c
index 8af0a1a2b7..efef7f235d 100644
--- a/src/cpu/cpu_x86.c
+++ b/src/cpu/cpu_x86.c
@@ -33,6 +33,7 @@
 #include "virbuffer.h"
 #include "virendian.h"
 #include "virstring.h"
+#include "virhostcpu.h"
 
 #define VIR_FROM_THIS VIR_FROM_CPU
 
@@ -153,6 +154,8 @@ struct _virCPUx86Map {
 };
 
 static virCPUx86MapPtr cpuMap;
+static unsigned int microcodeVersion;
+
 int virCPUx86DriverOnceInit(void);
 VIR_ONCE_GLOBAL_INIT(virCPUx86Driver);
 
@@ -1391,6 +1394,8 @@ virCPUx86DriverOnceInit(void)
     if (!(cpuMap = virCPUx86LoadMap()))
         return -1;
 
+    microcodeVersion = virHostCPUGetMicrocodeVersion();
+
     return 0;
 }
 
@@ -2408,6 +2413,9 @@ virCPUx86GetHost(virCPUDefPtr cpu,
     virCPUDataPtr cpuData = NULL;
     int ret = -1;
 
+    if (virCPUx86DriverInitialize() < 0)
+        goto cleanup;
+
     if (!(cpuData = virCPUDataNew(archs[0])))
         goto cleanup;
 
@@ -2416,6 +2424,7 @@ virCPUx86GetHost(virCPUDefPtr cpu,
         goto cleanup;
 
     ret = x86DecodeCPUData(cpu, cpuData, models, nmodels, NULL);
+    cpu->microcodeVersion = microcodeVersion;
 
  cleanup:
     virCPUx86DataFree(cpuData);
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index c50b6c2f77..c7834115fd 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -57,6 +57,7 @@ virCapabilitiesFreeGuest;
 virCapabilitiesFreeMachines;
 virCapabilitiesFreeNUMAInfo;
 virCapabilitiesGetCpusForNodemask;
+virCapabilitiesGetMicrocodeVersion;
 virCapabilitiesGetNodeInfo;
 virCapabilitiesHostSecModelAddBaseLabel;
 virCapabilitiesInitNUMA;
-- 
2.15.1