From 2dc7c9bc3dc24d991834f7657cd65cf4b5774ff8 Mon Sep 17 00:00:00 2001
From: Xu Yandong <xuyandong2@huawei.com>
Date: Wed, 14 Aug 2019 21:33:37 +0800
Subject: [PATCH] cpu: introduce cpu baseline for ARM CPU

support vendor and model for ARM CPU

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

diff --git a/src/cpu/cpu_arm.c b/src/cpu/cpu_arm.c
index 65d69c0..0c8cd50 100644
--- a/src/cpu/cpu_arm.c
+++ b/src/cpu/cpu_arm.c
@@ -21,12 +21,23 @@
 
 #include <config.h>
 
+#include "virlog.h"
 #include "viralloc.h"
 #include "cpu.h"
 #include "virstring.h"
+#include "cpu_map.h"
+#include "cpu_arm.h"
+#include "virfile.h"
 
 #define VIR_FROM_THIS VIR_FROM_CPU
 
+VIR_LOG_INIT("cpu.cpu_arm");
+
+static const char *sysinfoCpuinfo = "/proc/cpuinfo";
+
+#define CPUINFO sysinfoCpuinfo
+#define CPUINFO_FILE_LEN (1024*1024)   /* 1MB limit for /proc/cpuinfo file */
+
 static const virArch archs[] = {
     VIR_ARCH_ARMV6L,
     VIR_ARCH_ARMV7B,
@@ -34,6 +45,605 @@ static const virArch archs[] = {
     VIR_ARCH_AARCH64,
 };
 
+typedef struct _virCPUarmVendor virCPUarmVendor;
+typedef virCPUarmVendor *virCPUarmVendorPtr;
+struct _virCPUarmVendor {
+    char *name;
+    unsigned long value;
+};
+
+typedef struct _virCPUarmModel virCPUarmModel;
+typedef virCPUarmModel *virCPUarmModelPtr;
+struct _virCPUarmModel {
+    char *name;
+    virCPUarmVendorPtr vendor;
+    virCPUarmData data;
+};
+
+typedef struct _virCPUarmMap virCPUarmMap;
+typedef virCPUarmMap *virCPUarmMapPtr;
+struct _virCPUarmMap {
+    size_t nvendors;
+    virCPUarmVendorPtr *vendors;
+    size_t nmodels;
+    virCPUarmModelPtr *models;
+};
+
+static virCPUarmMapPtr cpuMap;
+
+int virCPUarmDriverOnceInit(void);
+VIR_ONCE_GLOBAL_INIT(virCPUarmDriver);
+
+
+static void
+virCPUarmDataClear(virCPUarmData *data)
+{
+    if (!data)
+        return;
+
+    VIR_FREE(data->features);
+}
+
+
+static int
+armDataCopy(virCPUarmData *dst, const virCPUarmData *src)
+{
+    if (VIR_STRDUP(dst->features, src->features) < 0)
+        return -1;
+
+    dst->vendor_id = src->vendor_id;
+    dst->pvr = src->pvr;
+
+    return 0;
+}
+
+
+static void
+armDataIntersect(virCPUarmData *data1,
+                 const virCPUarmData *data2)
+{
+    char **features = NULL;
+    char **features1 = NULL;
+    char **features2 = NULL;
+    size_t count = 0;
+    size_t i;
+
+    if (!data1 || !data2)
+        return;
+
+    data1->pvr = MIN(data1->pvr, data2->pvr);
+
+    if (virStringIsEmpty(data1->features) ||
+        virStringIsEmpty(data2->features)) {
+        VIR_FREE(data1->features);
+        return;
+    }
+
+    if (STREQ_NULLABLE(data1->features, data2->features))
+        return;
+
+    if (!(features = virStringSplitCount(data1->features, " ", 0, &count)) ||
+        !(features1 = virStringSplitCount(data1->features, " ", 0, &count)) ||
+        !(features2 = virStringSplit(data2->features, " ", 0)))
+        goto cleanup;
+
+    for (i = 0; i < count; i++) {
+        if (!virStringListHasString((const char**)features2, features1[i]))
+            virStringListRemove(&features, features1[i]);
+    }
+
+    VIR_FREE(data1->features);
+    if (features)
+        data1->features = virStringListJoin((const char**)features, " ");
+
+ cleanup:
+    virStringListFree(features);
+    virStringListFree(features1);
+    virStringListFree(features2);
+    return;
+}
+
+
+static void
+virCPUarmDataFree(virCPUDataPtr cpuData)
+{
+    if (!cpuData)
+        return;
+
+    virCPUarmDataClear(&cpuData->data.arm);
+    VIR_FREE(cpuData);
+}
+
+
+static bool
+armFeaturesIsSub(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 void
+armVendorFree(virCPUarmVendorPtr vendor)
+{
+    if (!vendor)
+        return;
+
+    VIR_FREE(vendor->name);
+    VIR_FREE(vendor);
+}
+
+
+static virCPUarmVendorPtr
+armVendorFindByID(virCPUarmMapPtr map,
+                  unsigned long vendor_id)
+{
+    size_t i;
+
+    for (i = 0; i < map->nvendors; i++) {
+        if (map->vendors[i]->value == vendor_id)
+            return map->vendors[i];
+    }
+
+    return NULL;
+}
+
+
+static virCPUarmVendorPtr
+armVendorFindByName(virCPUarmMapPtr map,
+                    const char *name)
+{
+    size_t i;
+
+    for (i = 0; i < map->nvendors; i++) {
+        if (STREQ(map->vendors[i]->name, name))
+            return map->vendors[i];
+    }
+
+    return NULL;
+}
+
+
+static int
+armVendorParse(xmlXPathContextPtr ctxt,
+               const char *name,
+               void *data)
+{
+    virCPUarmMapPtr map = (virCPUarmMapPtr)data;
+    virCPUarmVendorPtr vendor = NULL;
+    int ret = -1;
+
+    if (VIR_ALLOC(vendor) < 0)
+        return ret;
+
+    if (VIR_STRDUP(vendor->name, name) < 0)
+        goto cleanup;
+
+    if (armVendorFindByName(map, vendor->name)) {
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       _("CPU vendor %s already defined"), vendor->name);
+        goto cleanup;
+    }
+
+    if (virXPathULongHex("string(@value)", ctxt, &vendor->value) < 0) {
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       "%s", _("Missing CPU vendor value"));
+        goto cleanup;
+    }
+
+    if (armVendorFindByID(map, vendor->value)) {
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       _("CPU vendor value 0x%2lx already defined"), vendor->value);
+        goto cleanup;
+    }
+
+    if (VIR_APPEND_ELEMENT(map->vendors, map->nvendors, vendor) < 0)
+        goto cleanup;
+
+    ret = 0;
+
+ cleanup:
+    armVendorFree(vendor);
+    return ret;
+
+}
+
+
+static void
+armModelFree(virCPUarmModelPtr model)
+{
+    if (!model)
+        return;
+
+    virCPUarmDataClear(&model->data);
+    VIR_FREE(model->name);
+    VIR_FREE(model);
+}
+
+
+static virCPUarmModelPtr
+armModelCopy(virCPUarmModelPtr model)
+{
+    virCPUarmModelPtr copy;
+
+    if (VIR_ALLOC(copy) < 0)
+        goto cleanup;
+
+    if (VIR_STRDUP(copy->name, model->name) < 0)
+        goto cleanup;
+
+    if (armDataCopy(&copy->data, &model->data) < 0)
+        goto cleanup;
+
+    copy->vendor = model->vendor;
+
+    return copy;
+
+ cleanup:
+    armModelFree(copy);
+    return NULL;
+}
+
+
+static virCPUarmModelPtr
+armModelFind(virCPUarmMapPtr map,
+             const char *name)
+{
+    size_t i;
+
+    for (i = 0; i < map->nmodels; i++) {
+        if (STREQ(map->models[i]->name, name))
+            return map->models[i];
+    }
+
+    return NULL;
+}
+
+
+static virCPUarmModelPtr
+armModelFindByPVR(virCPUarmMapPtr map,
+                  unsigned long pvr)
+{
+    size_t i;
+
+    for (i = 0; i < map->nmodels; i++) {
+        if (map->models[i]->data.pvr == pvr)
+            return map->models[i];
+    }
+
+    return NULL;
+}
+
+
+static virCPUarmModelPtr
+armModelFromCPU(const virCPUDef *cpu,
+               virCPUarmMapPtr map)
+{
+    virCPUarmModelPtr model = NULL;
+    virCPUarmVendorPtr vendor = NULL;
+    char **features = NULL;
+    size_t i;
+
+    if (!cpu->model) {
+        virReportError(VIR_ERR_INVALID_ARG, "%s",
+                       _("no CPU model specified"));
+        return NULL;
+    }
+
+    if (!(model = armModelFind(map, cpu->model))) {
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       _("Unknown CPU model %s"), cpu->model);
+        return NULL;
+    }
+
+    if (!(model = armModelCopy(model)))
+        goto cleanup;
+
+    if (cpu->vendor) {
+        if (!(vendor = armVendorFindByName(map, cpu->vendor))) {
+            virReportError(VIR_ERR_INTERNAL_ERROR,
+                           _("Unknown CPU vendor %s"), cpu->vendor);
+            goto error;
+        }
+        model->data.vendor_id = vendor->value;
+    }
+
+    if (cpu->nfeatures) {
+        if (VIR_REALLOC_N(features, cpu->nfeatures + 1) < 0)
+            goto cleanup;
+
+        features[cpu->nfeatures] = NULL;
+        for (i = 0; i < cpu->nfeatures; i++) {
+            if (VIR_STRDUP(features[i], cpu->features[i].name) < 0)
+                goto error;
+        }
+
+        VIR_FREE(model->data.features);
+        model->data.features = virStringListJoin((const char **)features, " ");
+    }
+
+ cleanup:
+    virStringListFree(features);
+    return model;
+
+ error:
+    armModelFree(model);
+    model = NULL;
+    goto cleanup;
+}
+
+static int
+armModelParse(xmlXPathContextPtr ctxt,
+              const char *name,
+              void *data)
+{
+    virCPUarmMapPtr map = (virCPUarmMapPtr)data;
+    virCPUarmModel *model;
+    xmlNodePtr *nodes = NULL;
+    char *vendor = NULL;
+    int ret = -1;
+
+    if (VIR_ALLOC(model) < 0)
+        goto error;
+
+    if (VIR_STRDUP(model->name, name) < 0)
+        goto error;
+
+    if (armModelFind(map, model->name)) {
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       _("CPU model %s already defined"), model->name);
+        goto error;
+    }
+
+    if (virXPathBoolean("boolean(./vendor)", ctxt)) {
+        vendor = virXPathString("string(./vendor/@name)", ctxt);
+        if (!vendor) {
+            virReportError(VIR_ERR_INTERNAL_ERROR,
+                           _("Invalid vendor element in CPU model %s"),
+                           model->name);
+            goto error;
+        }
+
+        if (!(model->vendor = armVendorFindByName(map, vendor))) {
+            virReportError(VIR_ERR_INTERNAL_ERROR,
+                           _("Unknown vendor %s referenced by CPU model %s"),
+                           vendor, model->name);
+            goto error;
+        }
+    }
+
+    if (!virXPathBoolean("boolean(./pvr)", ctxt)) {
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       _("Missing PVR information for CPU model %s"),
+                       model->name);
+        goto error;
+    }
+
+    if (virXPathULongHex("string(./pvr/@value)", ctxt, &model->data.pvr) < 0) {
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       _("Missing or invalid PVR value in CPU model %s"),
+                       model->name);
+        goto error;
+    }
+
+    if (VIR_APPEND_ELEMENT(map->models, map->nmodels, model) < 0)
+        goto error;
+
+    ret = 0;
+
+ cleanup:
+    VIR_FREE(vendor);
+    VIR_FREE(nodes);
+    return ret;
+
+ error:
+    armModelFree(model);
+    goto cleanup;
+}
+
+
+static void
+armMapFree(virCPUarmMapPtr map)
+{
+    size_t i;
+
+    if (!map)
+        return;
+
+    for (i = 0; i < map->nmodels; i++)
+        armModelFree(map->models[i]);
+    VIR_FREE(map->models);
+
+    for (i = 0; i < map->nvendors; i++)
+        armVendorFree(map->vendors[i]);
+    VIR_FREE(map->vendors);
+
+    VIR_FREE(map);
+}
+
+
+static virCPUarmMapPtr
+virCPUarmLoadMap(void)
+{
+    virCPUarmMapPtr map;
+
+    if (VIR_ALLOC(map) < 0)
+        goto error;
+
+    if (cpuMapLoad("arm", armVendorParse, NULL, armModelParse, map) < 0)
+        goto error;
+
+    return map;
+
+ error:
+    armMapFree(map);
+    return NULL;
+}
+
+int
+virCPUarmDriverOnceInit(void)
+{
+    if (!(cpuMap = virCPUarmLoadMap()))
+        return -1;
+
+    return 0;
+}
+
+
+static virCPUarmMapPtr
+virCPUarmGetMap(void)
+{
+    if (virCPUarmDriverInitialize() < 0)
+        return NULL;
+
+    return cpuMap;
+}
+
+static virCPUDataPtr
+armMakeCPUData(virArch arch,
+               virCPUarmData *data)
+{
+    virCPUDataPtr cpuData;
+
+    if (VIR_ALLOC(cpuData) < 0)
+        return NULL;
+
+    cpuData->arch = arch;
+
+    if (armDataCopy(&cpuData->data.arm, data) < 0)
+        VIR_FREE(cpuData);
+
+    return cpuData;
+}
+
+
+static int
+armCpuDataParseFeatures(virCPUDefPtr cpu,
+                        const virCPUarmData *cpuData)
+{
+    int ret = -1;
+    size_t i;
+    char **features;
+
+    if (!cpu || !cpuData)
+        return ret;
+
+    if (!(features = virStringSplitCount(cpuData->features, " ",
+                                         0, &cpu->nfeatures)))
+        return ret;
+    if (cpu->nfeatures) {
+        if (VIR_ALLOC_N(cpu->features, cpu->nfeatures) < 0)
+            goto error;
+
+        for (i = 0; i < cpu->nfeatures; i++) {
+            cpu->features[i].policy = VIR_CPU_FEATURE_REQUIRE;
+            if (VIR_STRDUP(cpu->features[i].name, features[i]) < 0)
+                goto error;
+        }
+    }
+
+    ret = 0;
+
+ cleanup:
+    virStringListFree(features);
+    return ret;
+
+ error:
+    for (i = 0; i < cpu->nfeatures; i++)
+        VIR_FREE(cpu->features[i].name);
+    VIR_FREE(cpu->features);
+    cpu->nfeatures = 0;
+    goto cleanup;
+}
+
+
+static int
+armDecode(virCPUDefPtr cpu,
+          const virCPUarmData *cpuData,
+          virDomainCapsCPUModelsPtr models)
+{
+    virCPUarmMapPtr map;
+    virCPUarmModelPtr model;
+    virCPUarmVendorPtr vendor = NULL;
+
+    if (!cpuData || !(map = virCPUarmGetMap()))
+        return -1;
+
+    if (!(model = armModelFindByPVR(map, cpuData->pvr))) {
+        virReportError(VIR_ERR_OPERATION_FAILED,
+                       _("Cannot find CPU model with PVR 0x%03lx"),
+                       cpuData->pvr);
+        return -1;
+    }
+
+    if (!virCPUModelIsAllowed(model->name, models)) {
+        virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+                       _("CPU model %s is not supported by hypervisor"),
+                       model->name);
+        return -1;
+    }
+
+    if (VIR_STRDUP(cpu->model, model->name) < 0)
+        return -1;
+
+    if (cpuData->vendor_id &&
+        !(vendor = armVendorFindByID(map, cpuData->vendor_id))) {
+        virReportError(VIR_ERR_OPERATION_FAILED,
+                       _("Cannot find CPU vendor with vendor id 0x%02lx"),
+                       cpuData->vendor_id);
+        return -1;
+    }
+
+    if (vendor && VIR_STRDUP(cpu->vendor, vendor->name) < 0)
+        return -1;
+
+    if (cpuData->features &&
+        armCpuDataParseFeatures(cpu, cpuData) < 0)
+        return -1;
+
+    return 0;
+}
+
+
+static int
+armDecodeCPUData(virCPUDefPtr cpu,
+                 const virCPUData *data,
+                 virDomainCapsCPUModelsPtr models)
+{
+    return armDecode(cpu, &data->data.arm, models);
+}
+
 
 static int
 virCPUarmUpdate(virCPUDefPtr guest,
-- 
2.19.1

