[libvirt] [PATCH 03/10] cpu_x86: Add full support for ecx_in CPUID parameter

Jiri Denemark jdenemar at redhat.com
Wed Jun 8 12:41:31 UTC 2016


This patch makes our CPUID handling code up-to-date with the current
specification found in

Intel® 64 and IA-32 Architectures Developer's Manual: Vol. 2A
http://www.intel.com/content/www/us/en/processors/architectures-software-developer-manuals.html

Signed-off-by: Jiri Denemark <jdenemar at redhat.com>
---
 src/cpu/cpu_map.xml |   1 -
 src/cpu/cpu_x86.c   | 278 ++++++++++++++++++++++++++++++++++++++++++++++++++--
 2 files changed, 269 insertions(+), 10 deletions(-)

diff --git a/src/cpu/cpu_map.xml b/src/cpu/cpu_map.xml
index 1ddc55f..14c0e0f 100644
--- a/src/cpu/cpu_map.xml
+++ b/src/cpu/cpu_map.xml
@@ -289,7 +289,6 @@
     </feature>
 
     <!-- cpuid function 0x7 ecx 0x0 features -->
-    <!-- We support only ecx 0x0 now as it's done by a workaround -->
     <feature name='fsgsbase'>
       <cpuid eax_in='0x00000007' ebx='0x00000001'/>
     </feature>
diff --git a/src/cpu/cpu_x86.c b/src/cpu/cpu_x86.c
index ae809de..6823a27 100644
--- a/src/cpu/cpu_x86.c
+++ b/src/cpu/cpu_x86.c
@@ -259,7 +259,7 @@ virCPUx86CPUIDSorter(const void *a, const void *b)
 }
 
 
-/* skips all zero CPUID leafs */
+/* skips all zero CPUID leaves */
 static virCPUx86CPUID *
 x86DataCpuidNext(virCPUx86DataIteratorPtr iterator)
 {
@@ -1862,8 +1862,7 @@ cpuidCall(virCPUx86CPUID *cpuid)
 {
 # if __x86_64__
     asm("xor %%ebx, %%ebx;" /* clear the other registers as some cpuid */
-        "xor %%ecx, %%ecx;" /* functions may use them as additional */
-        "xor %%edx, %%edx;" /* arguments */
+        "xor %%edx, %%edx;" /* functions may use them as additional arguments */
         "cpuid;"
         : "=a" (cpuid->eax),
           "=b" (cpuid->ebx),
@@ -1877,8 +1876,7 @@ cpuidCall(virCPUx86CPUID *cpuid)
      */
     asm("push %%ebx;"
         "xor %%ebx, %%ebx;" /* clear the other registers as some cpuid */
-        "xor %%ecx, %%ecx;" /* functions may use them as additional */
-        "xor %%edx, %%edx;" /* arguments */
+        "xor %%edx, %%edx;" /* functions may use them as additional arguments */
         "cpuid;"
         "mov %%ebx, %1;"
         "pop %%ebx;"
@@ -1893,21 +1891,283 @@ cpuidCall(virCPUx86CPUID *cpuid)
 }
 
 
+/* Leaf 0x4
+ *
+ * Sub leaf n+1 is invalid if eax[4:0] in sub leaf n equals 0.
+ */
+static int
+cpuidSetLeaf4(virCPUx86Data *data,
+              virCPUx86CPUID *subLeaf0)
+{
+    virCPUx86CPUID cpuid = *subLeaf0;
+
+    if (virCPUx86DataAddCPUID(data, subLeaf0) < 0)
+        return -1;
+
+    while (cpuid.eax & 0x1f) {
+        cpuid.ecx_in++;
+        cpuidCall(&cpuid);
+        if (virCPUx86DataAddCPUID(data, &cpuid) < 0)
+            return -1;
+    }
+    return 0;
+}
+
+
+/* Leaf 0x7
+ *
+ * Sub leaf n is invalid if n > eax in sub leaf 0.
+ */
+static int
+cpuidSetLeaf7(virCPUx86Data *data,
+              virCPUx86CPUID *subLeaf0)
+{
+    virCPUx86CPUID cpuid = { .eax_in = 0x7 };
+    uint32_t sub;
+
+    if (virCPUx86DataAddCPUID(data, subLeaf0) < 0)
+        return -1;
+
+    for (sub = 1; sub <= subLeaf0->eax; sub++) {
+        cpuid.ecx_in = sub;
+        cpuidCall(&cpuid);
+        if (virCPUx86DataAddCPUID(data, &cpuid) < 0)
+            return -1;
+    }
+    return 0;
+}
+
+
+/* Leaf 0xb
+ *
+ * Sub leaf n is invalid if it returns 0 in ecx[15:8].
+ * Sub leaf n+1 is invalid if sub leaf n is invalid.
+ * Some output values do not depend on ecx, thus sub leaf 0 provides
+ * meaningful data even if it was (theoretically) considered invalid.
+ */
+static int
+cpuidSetLeafB(virCPUx86Data *data,
+              virCPUx86CPUID *subLeaf0)
+{
+    virCPUx86CPUID cpuid = *subLeaf0;
+
+    while (cpuid.ecx & 0xff00) {
+        if (virCPUx86DataAddCPUID(data, &cpuid) < 0)
+            return -1;
+        cpuid.ecx_in++;
+        cpuidCall(&cpuid);
+    }
+    return 0;
+}
+
+
+/* Leaf 0xd
+ *
+ * Sub leaves 0 and 1 are valid.
+ * Sub leaf n (2 <= n < 32) is invalid if eax[n] from sub leaf 0 is not set
+ * and ecx[n] from sub leaf 1 is not set.
+ * Sub leaf n (32 <= n < 64) is invalid if edx[n-32] from sub leaf 0 is not set
+ * and edx[n-32] from sub leaf 1 is not set.
+ */
+static int
+cpuidSetLeafD(virCPUx86Data *data,
+              virCPUx86CPUID *subLeaf0)
+{
+    virCPUx86CPUID cpuid = { .eax_in = 0xd };
+    virCPUx86CPUID sub0;
+    virCPUx86CPUID sub1;
+    uint32_t sub;
+
+    if (virCPUx86DataAddCPUID(data, subLeaf0) < 0)
+        return -1;
+
+    cpuid.ecx_in = 1;
+    cpuidCall(&cpuid);
+    if (virCPUx86DataAddCPUID(data, &cpuid) < 0)
+        return -1;
+
+    sub0 = *subLeaf0;
+    sub1 = cpuid;
+    for (sub = 2; sub < 64; sub++) {
+        if (sub < 32 &&
+            !(sub0.eax & (1 << sub)) &&
+            !(sub1.ecx & (1 << sub)))
+            continue;
+        if (sub >= 32 &&
+            !(sub0.edx & (1 << (sub - 32))) &&
+            !(sub1.edx & (1 << (sub - 32))))
+            continue;
+
+        cpuid.ecx_in = sub;
+        cpuidCall(&cpuid);
+        if (virCPUx86DataAddCPUID(data, &cpuid) < 0)
+            return -1;
+    }
+    return 0;
+}
+
+
+/* Leaf 0xf, 0x10
+ *
+ * res reports valid resource identification (ResID) starting at bit 1.
+ * Values associated with each valid ResID are reported by ResID sub leaf.
+ *
+ * 0xf:  Sub leaf n is valid if edx[n] (= res[ResID]) from sub leaf 0 is set.
+ * 0x10: Sub leaf n is valid if ebx[n] (= res[ResID]) from sub leaf 0 is set.
+ */
+static int
+cpuidSetLeafResID(virCPUx86Data *data,
+                  virCPUx86CPUID *subLeaf0,
+                  uint32_t res)
+{
+    virCPUx86CPUID cpuid = { .eax_in = subLeaf0->eax_in };
+    uint32_t sub;
+
+    if (virCPUx86DataAddCPUID(data, subLeaf0) < 0)
+        return -1;
+
+    for (sub = 1; sub < 32; sub++) {
+        if (!(res & (1 << sub)))
+            continue;
+        cpuid.ecx_in = sub;
+        cpuidCall(&cpuid);
+        if (virCPUx86DataAddCPUID(data, &cpuid) < 0)
+            return -1;
+    }
+    return 0;
+}
+
+
+/* Leaf 0x12
+ *
+ * Sub leaves 0 and 1 is supported if ebx[2] from leaf 0x7 (SGX) is set.
+ * Sub leaves n >= 2 are valid as long as eax[3:0] != 0.
+ */
+static int
+cpuidSetLeaf12(virCPUx86Data *data,
+               virCPUx86CPUID *subLeaf0)
+{
+    virCPUx86CPUID cpuid = { .eax_in = 0x7 };
+    virCPUx86CPUID *cpuid7;
+
+    if (!(cpuid7 = x86DataCpuid(data, &cpuid)) ||
+        !(cpuid7->ebx & (1 << 2)))
+        return 0;
+
+    if (virCPUx86DataAddCPUID(data, subLeaf0) < 0)
+        return -1;
+
+    cpuid.eax_in = 0x12;
+    cpuid.ecx_in = 1;
+    cpuidCall(&cpuid);
+    if (virCPUx86DataAddCPUID(data, &cpuid) < 0)
+        return -1;
+
+    cpuid.ecx_in = 2;
+    cpuidCall(&cpuid);
+    while (cpuid.eax & 0xf) {
+        if (virCPUx86DataAddCPUID(data, &cpuid) < 0)
+            return -1;
+        cpuid.ecx_in++;
+        cpuidCall(&cpuid);
+    }
+    return 0;
+}
+
+
+/* Leaf 0x14
+ *
+ * Sub leaf 0 reports the maximum supported sub leaf in eax.
+ */
+static int
+cpuidSetLeaf14(virCPUx86Data *data,
+               virCPUx86CPUID *subLeaf0)
+{
+    virCPUx86CPUID cpuid = { .eax_in = 0x14 };
+    uint32_t sub;
+
+    if (virCPUx86DataAddCPUID(data, subLeaf0) < 0)
+        return -1;
+
+    for (sub = 1; sub <= subLeaf0->eax; sub++) {
+        cpuid.ecx_in = sub;
+        cpuidCall(&cpuid);
+        if (virCPUx86DataAddCPUID(data, &cpuid) < 0)
+            return -1;
+    }
+    return 0;
+}
+
+
+/* Leaf 0x17
+ *
+ * Sub leaf 0 is valid if eax >= 3.
+ * Sub leaf 0 reports the maximum supported sub leaf in eax.
+ */
+static int
+cpuidSetLeaf17(virCPUx86Data *data,
+               virCPUx86CPUID *subLeaf0)
+{
+    virCPUx86CPUID cpuid = { .eax_in = 0x17 };
+    uint32_t sub;
+
+    if (subLeaf0->eax < 3)
+        return 0;
+
+    if (virCPUx86DataAddCPUID(data, subLeaf0) < 0)
+        return -1;
+
+    for (sub = 1; sub <= subLeaf0->eax; sub++) {
+        cpuid.ecx_in = sub;
+        cpuidCall(&cpuid);
+        if (virCPUx86DataAddCPUID(data, &cpuid) < 0)
+            return -1;
+    }
+    return 0;
+}
+
+
 static int
 cpuidSet(uint32_t base, virCPUx86Data *data)
 {
+    int rc;
     uint32_t max;
-    uint32_t i;
+    uint32_t leaf;
     virCPUx86CPUID cpuid = { .eax_in = base };
 
     cpuidCall(&cpuid);
     max = cpuid.eax;
 
-    for (i = base; i <= max; i++) {
-        cpuid.eax_in = i;
+    for (leaf = base; leaf <= max; leaf++) {
+        cpuid.eax_in = leaf;
         cpuid.ecx_in = 0;
         cpuidCall(&cpuid);
-        if (virCPUx86DataAddCPUID(data, &cpuid) < 0)
+
+        /* Handle CPUID leaves that depend on previously queried bits or
+         * which provide additional sub leaves for ecx_in > 0
+         */
+        if (leaf == 0x4)
+            rc = cpuidSetLeaf4(data, &cpuid);
+        else if (leaf == 0x7)
+            rc = cpuidSetLeaf7(data, &cpuid);
+        else if (leaf == 0xb)
+            rc = cpuidSetLeafB(data, &cpuid);
+        else if (leaf == 0xd)
+            rc = cpuidSetLeafD(data, &cpuid);
+        else if (leaf == 0xf)
+            rc = cpuidSetLeafResID(data, &cpuid, cpuid.edx);
+        else if (leaf == 0x10)
+            rc = cpuidSetLeafResID(data, &cpuid, cpuid.ebx);
+        else if (leaf == 0x12)
+            rc = cpuidSetLeaf12(data, &cpuid);
+        else if (leaf == 0x14)
+            rc = cpuidSetLeaf14(data, &cpuid);
+        else if (leaf == 0x17)
+            rc = cpuidSetLeaf17(data, &cpuid);
+        else
+            rc = virCPUx86DataAddCPUID(data, &cpuid);
+
+        if (rc < 0)
             return -1;
     }
 
-- 
2.8.4




More information about the libvir-list mailing list