From 1a70b1e3bdbd8d0dcf9281341bc23b4ff2ffaa3a Mon Sep 17 00:00:00 2001
From: Xu Yandong <xuyandong2@huawei.com>
Date: Mon, 20 Apr 2020 19:33:49 +0800
Subject: cpu/arm: implment cpu compare function

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

diff --git a/src/cpu/cpu_arm.c b/src/cpu/cpu_arm.c
index a7c9523..3ef54b9 100644
--- a/src/cpu/cpu_arm.c
+++ b/src/cpu/cpu_arm.c
@@ -21,6 +21,7 @@
 
 #include <config.h>
 
+#include "virlog.h"
 #include "viralloc.h"
 #include "cpu.h"
 #include "cpu_map.h"
@@ -30,6 +31,8 @@
 
 #define VIR_FROM_THIS VIR_FROM_CPU
 
+VIR_LOG_INIT("cpu.cpu_arm");
+
 static const char *sysinfoCpuinfo = "/proc/cpuinfo";
 
 #define CPUINFO sysinfoCpuinfo
@@ -806,14 +809,201 @@ virCPUarmBaseline(virCPUDefPtr *cpus,
     return g_steal_pointer(&cpu);
 }
 
+static bool
+virCPUarmFeaturesIsSub(char *subFeatures,
+                       char *fullFeatures)
+{
+    bool ret = false;
+    char **sub = NULL;
+    char **full = NULL;
+    size_t subCount = 0;
+    size_t fullCount = 0;
+    size_t i;
+
+    if (virStringIsEmpty(subFeatures))
+        return true;
+
+    if (virStringIsEmpty(fullFeatures))
+        return ret;
+
+    if (STREQ(subFeatures, fullFeatures))
+        return true;
+
+    if (!(sub = virStringSplitCount(subFeatures, " ", 0, &subCount)) ||
+        !(full = virStringSplitCount(fullFeatures, " ", 0, &fullCount)) ||
+        subCount > fullCount)
+        goto cleanup;
+
+    for (i = 0; i < subCount; i++) {
+        if (!virStringListHasString((const char**)full, sub[i]))
+            goto cleanup;
+    }
+
+    ret = true;
+
+ cleanup:
+    virStringListFree(sub);
+    virStringListFree(full);
+    return ret;
+}
+
+static virCPUDataPtr
+armMakeCPUData(virArch arch,
+               virCPUarmData *data)
+{
+    virCPUDataPtr cpuData;
+
+    if (!(cpuData = virCPUDataNew(arch)))
+        return NULL;
+
+    virCPUarmDataCopy(&cpuData->data.arm, data);
+
+    return cpuData;
+}
+
 static virCPUCompareResult
-virCPUarmCompare(virCPUDefPtr host G_GNUC_UNUSED,
-                 virCPUDefPtr cpu G_GNUC_UNUSED,
-                 bool failMessages G_GNUC_UNUSED)
+armCompute(virCPUDefPtr host,
+           virCPUDefPtr cpu,
+           virCPUDataPtr *guestData,
+           char **message)
 {
+    virCPUarmMapPtr map = NULL;
+    g_autoptr(virCPUarmModel) hostModel = NULL;
+    g_autoptr(virCPUarmModel) guestModel = NULL;
+    virArch arch;
+    size_t i;
+
+    if (cpu->arch != VIR_ARCH_NONE) {
+        bool found = false;
+
+        for (i = 0; i < G_N_ELEMENTS(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)
+                *message = g_strdup_printf(_("CPU arch %s does not match host arch"),
+                                           virArchToString(cpu->arch));
+
+            return VIR_CPU_COMPARE_INCOMPATIBLE;
+        }
+        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)
+            *message = g_strdup_printf(_("host CPU vendor does not match required "
+                                       "CPU vendor %s"),
+                        cpu->vendor);
+
+        return VIR_CPU_COMPARE_INCOMPATIBLE;
+    }
+
+    if (!(map = virCPUarmGetMap()))
+        return VIR_CPU_COMPARE_ERROR;
+
+    /* Host CPU information */
+    if (!(hostModel = virCPUarmModelFromCPU(host, map)))
+        return VIR_CPU_COMPARE_ERROR;
+
+    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 = virCPUarmModelCopy(hostModel);
+                break;
+
+            case VIR_CPU_MODE_CUSTOM:
+                /* custom:
+                 * look up guest CPU information */
+                guestModel = virCPUarmModelFromCPU(cpu, map);
+                break;
+        }
+    } else {
+        /* Other host CPU information */
+        guestModel = virCPUarmModelFromCPU(cpu, map);
+    }
+
+    if (!guestModel)
+        return VIR_CPU_COMPARE_ERROR;
+
+    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)
+            *message = g_strdup_printf(_("host CPU model %s does not match required "
+                                       "CPU model %s"),
+                                       hostModel->name, guestModel->name);
+
+        return VIR_CPU_COMPARE_INCOMPATIBLE;
+    }
+
+    if (!virCPUarmFeaturesIsSub(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)
+            *message = g_strdup_printf(_("guest CPU features '%s' is not subset of "
+                                       "host CPU features '%s'"),
+                                       guestModel->data.features,
+                                       hostModel->data.features);
+
+        return VIR_CPU_COMPARE_INCOMPATIBLE;
+    }
+
+    if (guestData &&
+        !(*guestData = armMakeCPUData(arch, &guestModel->data)))
+        return VIR_CPU_COMPARE_ERROR;
+
     return VIR_CPU_COMPARE_IDENTICAL;
 }
 
+static virCPUCompareResult
+virCPUarmCompare(virCPUDefPtr host,
+                 virCPUDefPtr cpu,
+                 bool failMessages)
+{
+    virCPUCompareResult ret = VIR_CPU_COMPARE_ERROR;
+    g_autofree char *message = NULL;
+
+    if (!host || !host->model) {
+        if (failMessages) {
+            virReportError(VIR_ERR_CPU_INCOMPATIBLE, "%s",
+                           _("unknown host CPU"));
+        } else {
+            VIR_WARN("unknown host CPU");
+            return VIR_CPU_COMPARE_INCOMPATIBLE;
+        }
+        return VIR_CPU_COMPARE_ERROR;
+    }
+
+    ret = armCompute(host, cpu, NULL, &message);
+
+    if (failMessages && ret == VIR_CPU_COMPARE_INCOMPATIBLE) {
+        if (message) {
+            virReportError(VIR_ERR_CPU_INCOMPATIBLE, "%s", message);
+        } else {
+            virReportError(VIR_ERR_CPU_INCOMPATIBLE, NULL);
+        }
+        return VIR_CPU_COMPARE_ERROR;
+    }
+
+    return ret;
+}
+
 static int
 virCPUarmValidateFeatures(virCPUDefPtr cpu)
 {
-- 
2.23.0