From 2dc7c9bc3dc24d991834f7657cd65cf4b5774ff8 Mon Sep 17 00:00:00 2001 From: Xu Yandong 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 --- 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 +#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(©->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