From e82aa4bcebf4f14782c20f20d9bacf0c352adbc6 Mon Sep 17 00:00:00 2001
From: Radim Krcmar <rkrcmar@redhat.com>
Date: Fri, 28 Mar 2014 18:00:08 +0100
Subject: [PATCH 22/30] x86: cpuid: reconstruct leaf 0Dh data
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

RH-Author: Radim Krcmar <rkrcmar@redhat.com>
Message-id: <1396029608-8095-3-git-send-email-rkrcmar@redhat.com>
Patchwork-id: 58277
O-Subject: [RHEL6.6 qemu-kvm PATCH 2/2] x86: cpuid: reconstruct leaf 0Dh data
Bugzilla: 1023268
RH-Acked-by: Andrew Jones <drjones@redhat.com>
RH-Acked-by: Eduardo Habkost <ehabkost@redhat.com>
RH-Acked-by: Igor Mammedov <imammedo@redhat.com>

The data in leaf 0Dh depends on information from other feature bits.
Instead of passing it blindly from the host, compute it based on
whether these feature bits are enabled.

Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
Signed-off-by: Gleb Natapov <gleb@redhat.com>
(backported from commit 2560f19f426aceb4f2e809d860b93e7573cb1c4e)

Changes:
  - 'features[FEAT_1_ECX]' -> 'cpuid_ext_features'
  - we don't have nicely abstracted cpu extensions yet, so this patch
    uses a function where two variables sufficed upstream
    ('offsetof()' looked too hacky.)
  - squashed with a cosmetic change:
    commit 33f373d7c56350fd2ec3e31f4f2c46cb49464911
    Author: Liu Jinsong <jinsong.liu@intel.com>
    Date:   Tue Dec 3 04:17:50 2013 +0800

        target-i386: fix cpuid leaf 0x0d

        Fix cpuid leaf 0x0d which incorrectly parsed eax and ebx.

        However, before this patch the CPUID worked fine -- the .offset
        field contained the size _and_ was stored in the register that
        is supposed to hold the size (eax), and likewise the .size field
        contained the offset _and_ was stored in the register trhat is
        supposed to hold the offset (ebx).

        Signed-off-by: Liu Jinsong <jinsong.liu@intel.com>
        Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
  - removed explicit "!= 0" in boolean conditions [OCD]
    (Resisted an urge to remove all extraneous parentheses too.)

Signed-off-by: Radim Krčmář <rkrcmar@redhat.com>
---
 Minipoll: Which one do you prefer?
  (1)  if (esa->detect(env) && (kvm_mask & (1 << count)) != 0) {
  (2)  if (esa->detect(env) && (kvm_mask & (1 << count))) {
  (3)  if (esa->detect(env) && kvm_mask & (1 << count)) {
  (4)  if (esa->detect(env) && kvm_mask & 1 << count) {

 ('!=' having higher precedence that '&' is a bug in C, bit too late to
  fix it though.)

 target-i386/cpuid.c | 68 +++++++++++++++++++++++++++++++++++++++--------------
 1 file changed, 51 insertions(+), 17 deletions(-)

Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com>
---
 target-i386/cpuid.c |   68 ++++++++++++++++++++++++++++++++++++++------------
 1 files changed, 51 insertions(+), 17 deletions(-)

diff --git a/target-i386/cpuid.c b/target-i386/cpuid.c
index 0f4c7a6..de1a620 100644
--- a/target-i386/cpuid.c
+++ b/target-i386/cpuid.c
@@ -81,6 +81,20 @@ static const char *cpuid_7_0_ebx_feature_name[] = {
     NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
 };
 
+typedef struct ExtSaveArea {
+    bool (*enabled)(const CPUX86State *);
+    uint32_t offset, size;
+} ExtSaveArea;
+
+static bool cpu_has_avx(const CPUX86State *env)
+{
+    return env->cpuid_ext_features & CPUID_EXT_AVX;
+}
+
+static const ExtSaveArea ext_save_areas[] = {
+    [2] = { .enabled = cpu_has_avx, .offset = 0x240, .size = 0x100 },
+};
+
 /* collects per-function cpuid data
  */
 typedef struct model_features_t {
@@ -1369,29 +1383,49 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count,
             *edx = 0;
         }
         break;
-    case 0xD:
+    case 0xD: {
+        KVMState *s = env->kvm_state;
+        uint64_t kvm_mask;
+        int i;
+
         /* Processor Extended State */
-        if (!(env->cpuid_ext_features & CPUID_EXT_XSAVE)) {
-            *eax = 0;
-            *ebx = 0;
-            *ecx = 0;
-            *edx = 0;
+        *eax = 0;
+        *ebx = 0;
+        *ecx = 0;
+        *edx = 0;
+        if (!(env->cpuid_ext_features & CPUID_EXT_XSAVE) || !kvm_enabled()) {
             break;
         }
-        if (kvm_enabled()) {
-            KVMState *s = env->kvm_state;
+        kvm_mask =
+            kvm_arch_get_supported_cpuid(s, 0xd, 0, R_EAX) |
+            ((uint64_t)kvm_arch_get_supported_cpuid(s, 0xd, 0, R_EDX) << 32);
 
-            *eax = kvm_arch_get_supported_cpuid(s, 0xd, count, R_EAX);
-            *ebx = kvm_arch_get_supported_cpuid(s, 0xd, count, R_EBX);
-            *ecx = kvm_arch_get_supported_cpuid(s, 0xd, count, R_ECX);
-            *edx = kvm_arch_get_supported_cpuid(s, 0xd, count, R_EDX);
-        } else {
-            *eax = 0;
-            *ebx = 0;
-            *ecx = 0;
-            *edx = 0;
+        if (count == 0) {
+            *ecx = 0x240;
+            for (i = 2; i < ARRAY_SIZE(ext_save_areas); i++) {
+                const ExtSaveArea *esa = &ext_save_areas[i];
+                if (esa->enabled(env) && kvm_mask & (1 << i)) {
+                    if (i < 32) {
+                        *eax |= 1 << i;
+                    } else {
+                        *edx |= 1 << (i - 32);
+                    }
+                    *ecx = MAX(*ecx, esa->offset + esa->size);
+                }
+            }
+            *eax |= kvm_mask & (XSTATE_FP | XSTATE_SSE);
+            *ebx = *ecx;
+        } else if (count == 1) {
+            *eax = kvm_arch_get_supported_cpuid(s, 0xd, 1, R_EAX);
+        } else if (count < ARRAY_SIZE(ext_save_areas)) {
+            const ExtSaveArea *esa = &ext_save_areas[count];
+            if (esa->enabled(env) && kvm_mask & (1 << count)) {
+                *eax = esa->size;
+                *ebx = esa->offset;
+            }
         }
         break;
+    }
     case 0x80000000:
         *eax = env->cpuid_xlevel;
         *ebx = env->cpuid_vendor1;
-- 
1.7.1

