From 57b8e156e898ccf878842c3cba1ed97a415cff6a Mon Sep 17 00:00:00 2001
From: Xu Yandong <xuyandong2@huawei.com>
Date: Fri, 6 Sep 2019 11:27:33 +0800
Subject: [PATCH] cpu: fix cpu-compare and cpu-baseline for ARM CPU

Signed-off-by: Xu Yandong <xuyandong2@huawei.com>
---
 src/cpu/cpu_arm.c | 241 ++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 234 insertions(+), 7 deletions(-)

diff --git a/src/cpu/cpu_arm.c b/src/cpu/cpu_arm.c
index 0c8cd50..1d0d3b6 100644
--- a/src/cpu/cpu_arm.c
+++ b/src/cpu/cpu_arm.c
@@ -681,12 +681,18 @@ virCPUarmUpdate(virCPUDefPtr guest,
 
 static virCPUDefPtr
 virCPUarmBaseline(virCPUDefPtr *cpus,
-                  unsigned int ncpus ATTRIBUTE_UNUSED,
-                  virDomainCapsCPUModelsPtr models ATTRIBUTE_UNUSED,
+                  unsigned int ncpus,
+                  virDomainCapsCPUModelsPtr models,
                   const char **features ATTRIBUTE_UNUSED,
                   bool migratable ATTRIBUTE_UNUSED)
 {
     virCPUDefPtr cpu = NULL;
+    virCPUarmMapPtr map = NULL;
+    virCPUarmModelPtr model = NULL;
+    virCPUarmModelPtr baseModel = NULL;
+    virCPUarmVendorPtr vendor = NULL;
+    bool outputVendor = true;
+    size_t i;
 
     if (VIR_ALLOC(cpu) < 0 ||
         VIR_STRDUP(cpu->model, cpus[0]->model) < 0) {
@@ -694,27 +700,248 @@ virCPUarmBaseline(virCPUDefPtr *cpus,
         return NULL;
     }
 
+    cpu->arch = cpus[0]->arch;
     cpu->type = VIR_CPU_TYPE_GUEST;
     cpu->match = VIR_CPU_MATCH_EXACT;
+    cpu->fallback = VIR_CPU_FALLBACK_FORBID;
+
+    if (!(map = virCPUarmGetMap()))
+        goto error;
+
+    if (!(baseModel = armModelFromCPU(cpus[0], map)))
+        goto error;
+
+    if (!cpus[0]->vendor) {
+        outputVendor = false;
+    } else if (!(vendor = armVendorFindByName(map, cpus[0]->vendor))) {
+        virReportError(VIR_ERR_OPERATION_FAILED,
+                       _("Unknown CPU vendor %s"), cpus[0]->vendor);
+        goto error;
+    }
+
+    for (i = 1; i < ncpus; i++) {
+        const char *vn = NULL;
+
+        if (!(model = armModelFromCPU(cpus[i], map)))
+            goto error;
+
+        if (cpus[i]->vendor) {
+            vn = cpus[i]->vendor;
+        } else {
+            outputVendor = false;
+        }
+
+        if (vn) {
+            if (!vendor) {
+                if (!(vendor = armVendorFindByName(map, vn))) {
+                    virReportError(VIR_ERR_OPERATION_FAILED,
+                                   _("Unknown CPU vendor %s"), vn);
+                    goto error;
+                }
+            } else if (STRNEQ(vendor->name, vn)) {
+                virReportError(VIR_ERR_OPERATION_FAILED,
+                               "%s", _("CPU vendors do not match"));
+                goto error;
+            }
+        }
+
+        armDataIntersect(&baseModel->data, &model->data);
+        armModelFree(model);
+        model = NULL;
+    }
+
+    if (armDecode(cpu, &baseModel->data, models) < 0)
+        goto error;
+
+    if (!outputVendor)
+        VIR_FREE(cpu->vendor);
+
+    cpu->arch = VIR_ARCH_NONE;
+
+ cleanup:
+    armModelFree(baseModel);
 
     return cpu;
+
+ error:
+    armModelFree(model);
+    virCPUDefFree(cpu);
+    cpu = NULL;
+    goto cleanup;
 }
 
+
 static virCPUCompareResult
-virCPUarmCompare(virCPUDefPtr host ATTRIBUTE_UNUSED,
-                 virCPUDefPtr cpu ATTRIBUTE_UNUSED,
-                 bool failMessages ATTRIBUTE_UNUSED)
+armCompute(virCPUDefPtr host,
+           virCPUDefPtr cpu,
+           virCPUDataPtr *guestData,
+           char **message)
 {
-    return VIR_CPU_COMPARE_IDENTICAL;
+    virCPUarmMapPtr map = NULL;
+    virCPUarmModelPtr hostModel = NULL;
+    virCPUarmModelPtr guestModel = NULL;
+    virCPUCompareResult ret = VIR_CPU_COMPARE_ERROR;
+    virArch arch;
+    size_t i;
+
+    if (cpu->arch != VIR_ARCH_NONE) {
+        bool found = false;
+
+        for (i = 0; i < ARRAY_CARDINALITY(archs); i++) {
+            if (archs[i] == cpu->arch) {
+                found = true;
+                break;
+            }
+        }
+
+        if (!found) {
+            VIR_DEBUG("CPU arch %s does not match host arch",
+                      virArchToString(cpu->arch));
+            if (message &&
+                virAsprintf(message,
+                            _("CPU arch %s does not match host arch"),
+                            virArchToString(cpu->arch)) < 0)
+                goto cleanup;
+
+            ret = VIR_CPU_COMPARE_INCOMPATIBLE;
+            goto cleanup;
+        }
+        arch = cpu->arch;
+    } else {
+        arch = host->arch;
+    }
+
+    if (cpu->vendor &&
+        (!host->vendor || STRNEQ(cpu->vendor, host->vendor))) {
+        VIR_DEBUG("host CPU vendor does not match required CPU vendor %s",
+                  cpu->vendor);
+        if (message &&
+            virAsprintf(message,
+                        _("host CPU vendor does not match required "
+                          "CPU vendor %s"),
+                        cpu->vendor) < 0)
+            goto cleanup;
+
+        ret = VIR_CPU_COMPARE_INCOMPATIBLE;
+        goto cleanup;
+    }
+
+    if (!(map = virCPUarmGetMap()))
+        goto cleanup;
+
+    /* Host CPU information */
+    if (!(hostModel = armModelFromCPU(host, map)))
+        goto cleanup;
+
+    if (cpu->type == VIR_CPU_TYPE_GUEST) {
+        /* Guest CPU information */
+        switch (cpu->mode) {
+            case VIR_CPU_MODE_HOST_MODEL:
+            case VIR_CPU_MODE_HOST_PASSTHROUGH:
+                /* host-model and host-passthrough:
+                 * the guest CPU is the same as the host */
+                guestModel = armModelCopy(hostModel);
+                break;
+
+            case VIR_CPU_MODE_CUSTOM:
+                /* custom:
+                 * look up guest CPU information */
+                guestModel = armModelFromCPU(cpu, map);
+                break;
+        }
+    } else {
+        /* Other host CPU information */
+        guestModel = armModelFromCPU(cpu, map);
+    }
+
+    if (!guestModel)
+        goto cleanup;
+
+    if (STRNEQ(guestModel->name, hostModel->name)) {
+        VIR_DEBUG("host CPU model %s does not match required CPU model %s",
+                  hostModel->name, guestModel->name);
+        if (message &&
+            virAsprintf(message,
+                        _("host CPU model %s does not match required "
+                          "CPU model %s"),
+                        hostModel->name, guestModel->name) < 0)
+            goto cleanup;
+
+        ret = VIR_CPU_COMPARE_INCOMPATIBLE;
+        goto cleanup;
+    }
+
+    if (!armFeaturesIsSub(guestModel->data.features, hostModel->data.features)) {
+        VIR_DEBUG("guest CPU features '%s' is not subset of "
+                  "host CPU features '%s'",
+                  guestModel->data.features, hostModel->data.features);
+        if (message &&
+            virAsprintf(message,
+                        _("guest CPU features '%s' is not subset of "
+                          "host CPU features '%s'"),
+                        guestModel->data.features,
+                        hostModel->data.features) < 0)
+            goto cleanup;
+
+        ret = VIR_CPU_COMPARE_INCOMPATIBLE;
+        goto cleanup;
+    }
+
+    if (guestData &&
+            !(*guestData = armMakeCPUData(arch, &guestModel->data)))
+        goto cleanup;
+
+    ret = VIR_CPU_COMPARE_IDENTICAL;
+
+ cleanup:
+    armModelFree(hostModel);
+    armModelFree(guestModel);
+    return ret;
 }
 
+static virCPUCompareResult
+virCPUarmCompare(virCPUDefPtr host,
+                 virCPUDefPtr cpu,
+                 bool failIncompatible)
+{
+    virCPUCompareResult ret = VIR_CPU_COMPARE_ERROR;
+    char *message = NULL;
+
+    if (!host || !host->model) {
+        if (failIncompatible) {
+            virReportError(VIR_ERR_CPU_INCOMPATIBLE, "%s",
+                           _("unknown host CPU"));
+        } else {
+            VIR_WARN("unknown host CPU");
+            ret = VIR_CPU_COMPARE_INCOMPATIBLE;
+        }
+        return ret;
+    }
+
+    ret = armCompute(host, cpu, NULL, &message);
+
+    if (failIncompatible && ret == VIR_CPU_COMPARE_INCOMPATIBLE) {
+        ret = VIR_CPU_COMPARE_ERROR;
+        if (message) {
+            virReportError(VIR_ERR_CPU_INCOMPATIBLE, "%s", message);
+        } else {
+            virReportError(VIR_ERR_CPU_INCOMPATIBLE, NULL);
+        }
+    }
+    VIR_FREE(message);
+
+    return ret;
+}
+
+
 struct cpuArchDriver cpuDriverArm = {
     .name = "arm",
     .arch = archs,
     .narch = ARRAY_CARDINALITY(archs),
     .compare = virCPUarmCompare,
-    .decode = NULL,
+    .decode = armDecodeCPUData,
     .encode = NULL,
+    .dataFree = virCPUarmDataFree,
     .baseline = virCPUarmBaseline,
     .update = virCPUarmUpdate,
 };
-- 
2.19.1

