--- /dev/null
+/*
+ * cpu.c: internal functions for CPU manipulation
+ *
+ * Copyright (C) 2009 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Authors:
+ * Jiri Denemark <jdenemar@redhat.com>
+ */
+
+#include <config.h>
+
+#include "xml.h"
+#include "cpu.h"
+#include "cpu_x86.h"
+#include "cpu_generic.h"
+
+
+#define NR_DRIVERS ARRAY_CARDINALITY(drivers)
+#define VIR_FROM_THIS VIR_FROM_CPU
+
+static struct cpuArchDriver *drivers[] = {
+ &cpuDriverX86,
+ /* generic driver must always be the last one */
+ &cpuDriverGeneric
+};
+
+
+static struct cpuArchDriver *
+cpuGetSubDriver(virConnectPtr conn,
+ const char *arch)
+{
+ unsigned int i;
+ unsigned int j;
+
+ if (arch == NULL) {
+ virCPUReportError(conn, VIR_ERR_INTERNAL_ERROR,
+ _("undefined hardware architecture"));
+ return NULL;
+ }
+
+ for (i = 0; i < NR_DRIVERS - 1; i++) {
+ for (j = 0; j < drivers[i]->narch; j++) {
+ if (STREQ(arch, drivers[i]->arch[j]))
+ return drivers[i];
+ }
+ }
+
+ /* use generic driver by default */
+ return drivers[NR_DRIVERS - 1];
+}
+
+
+virCPUCompareResult
+cpuCompareXML(virConnectPtr conn,
+ virCPUDefPtr host,
+ const char *xml)
+{
+ xmlDocPtr doc = NULL;
+ xmlXPathContextPtr ctxt = NULL;
+ virCPUDefPtr cpu = NULL;
+ virCPUCompareResult ret = VIR_CPU_COMPARE_ERROR;
+
+ doc = xmlParseMemory(xml, strlen(xml));
+
+ if (doc == NULL || (ctxt = xmlXPathNewContext(doc)) == NULL) {
+ virReportOOMError(conn);
+ goto cleanup;
+ }
+
+ ctxt->node = xmlDocGetRootElement(doc);
+
+ cpu = virCPUDefParseXML(conn, ctxt->node, ctxt, VIR_CPU_TYPE_AUTO);
+ if (cpu == NULL)
+ goto cleanup;
+
+ ret = cpuCompare(conn, host, cpu);
+
+cleanup:
+ virCPUDefFree(cpu);
+ xmlXPathFreeContext(ctxt);
+ xmlFreeDoc(doc);
+
+ return ret;
+}
+
+
+virCPUCompareResult
+cpuCompare(virConnectPtr conn,
+ virCPUDefPtr host,
+ virCPUDefPtr cpu)
+{
+ struct cpuArchDriver *driver;
+
+ if ((driver = cpuGetSubDriver(conn, host->arch)) == NULL)
+ return VIR_CPU_COMPARE_ERROR;
+
+ if (driver->compare == NULL) {
+ virCPUReportError(conn, VIR_ERR_NO_SUPPORT,
+ _("cannot compare CPUs of %s architecture"),
+ host->arch);
+ return VIR_CPU_COMPARE_ERROR;
+ }
+
+ return driver->compare(host, cpu);
+}
+
+
+int
+cpuDecode(virConnectPtr conn,
+ virCPUDefPtr cpu,
+ const union cpuData *data,
+ unsigned int nmodels,
+ const char **models)
+{
+ struct cpuArchDriver *driver;
+
+ if (cpu == NULL) {
+ virCPUReportError(conn, VIR_ERR_INTERNAL_ERROR,
+ _("invalid CPU definition"));
+ return -1;
+ }
+
+ if ((driver = cpuGetSubDriver(conn, cpu->arch)) == NULL)
+ return -1;
+
+ if (driver->decode == NULL) {
+ virCPUReportError(conn, VIR_ERR_NO_SUPPORT,
+ _("cannot decode CPU data for %s architecture"),
+ cpu->arch);
+ return -1;
+ }
+
+ return driver->decode(cpu, data, nmodels, models);
+}
+
+
+int
+cpuEncode(virConnectPtr conn,
+ const char *arch,
+ const virCPUDefPtr cpu,
+ union cpuData **forced,
+ union cpuData **required,
+ union cpuData **optional,
+ union cpuData **disabled,
+ union cpuData **forbidden)
+{
+ struct cpuArchDriver *driver;
+
+ if ((driver = cpuGetSubDriver(conn, arch)) == NULL)
+ return -1;
+
+ if (driver->encode == NULL) {
+ virCPUReportError(conn, VIR_ERR_NO_SUPPORT,
+ _("cannot encode CPU data for %s architecture"),
+ arch);
+ return -1;
+ }
+
+ return driver->encode(cpu, forced, required,
+ optional, disabled, forbidden);
+}
+
+
+void
+cpuDataFree(virConnectPtr conn,
+ const char *arch,
+ union cpuData *data)
+{
+ struct cpuArchDriver *driver;
+
+ if (data == NULL)
+ return;
+
+ if ((driver = cpuGetSubDriver(conn, arch)) == NULL)
+ return;
+
+ if (driver->free == NULL) {
+ virCPUReportError(conn, VIR_ERR_NO_SUPPORT,
+ _("cannot free CPU data for %s architecture"),
+ arch);
+ return;
+ }
+
+ driver->free(data);
+}
+
+
+union cpuData *
+cpuNodeData(virConnectPtr conn,
+ const char *arch)
+{
+ struct cpuArchDriver *driver;
+
+ if ((driver = cpuGetSubDriver(conn, arch)) == NULL)
+ return NULL;
+
+ if (driver->nodeData == NULL) {
+ virCPUReportError(conn, VIR_ERR_NO_SUPPORT,
+ _("cannot get node CPU data for %s architecture"),
+ arch);
+ return NULL;
+ }
+
+ return driver->nodeData();
+}
+
+
+virCPUCompareResult
+cpuGuestData(virConnectPtr conn,
+ virCPUDefPtr host,
+ virCPUDefPtr guest,
+ union cpuData **data)
+{
+ struct cpuArchDriver *driver;
+
+ if ((driver = cpuGetSubDriver(conn, host->arch)) == NULL)
+ return VIR_CPU_COMPARE_ERROR;
+
+ if (driver->guestData == NULL) {
+ virCPUReportError(conn, VIR_ERR_NO_SUPPORT,
+ _("cannot compute guest CPU data for %s architecture"),
+ host->arch);
+ return VIR_CPU_COMPARE_ERROR;
+ }
+
+ return driver->guestData(host, guest, data);
+}
--- /dev/null
+/*
+ * cpu_x86.c: CPU driver for CPUs with x86 compatible CPUID instruction
+ *
+ * Copyright (C) 2009 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Authors:
+ * Jiri Denemark <jdenemar@redhat.com>
+ */
+
+#include <config.h>
+
+#include <stdint.h>
+
+#include "logging.h"
+#include "memory.h"
+#include "cpu.h"
+#include "cpu_map.h"
+#include "cpu_x86.h"
+
+
+#define VIR_FROM_THIS VIR_FROM_CPU
+
+static const char *archs[] = { "i686", "x86_64" };
+
+struct x86_feature {
+ char *name;
+ unsigned int ncpuid;
+ struct cpuX86cpuid *cpuid;
+
+ struct x86_feature *next;
+};
+
+struct x86_model {
+ char *name;
+ unsigned int ncpuid;
+ struct cpuX86cpuid *cpuid;
+
+ struct x86_model *next;
+};
+
+struct x86_map {
+ struct x86_feature *features;
+ struct x86_model *models;
+};
+
+
+enum compare_result {
+ SUBSET,
+ EQUAL,
+ SUPERSET,
+ UNRELATED
+};
+
+
+static struct cpuX86cpuid *
+x86cpuidFind(struct cpuX86cpuid *cpuids,
+ unsigned int ncpuids,
+ uint32_t function)
+{
+ unsigned int i;
+
+ for (i = 0; i < ncpuids; i++) {
+ if (cpuids[i].function == function)
+ return cpuids + i;
+ }
+
+ return NULL;
+}
+
+
+static inline int
+x86cpuidMatch(const struct cpuX86cpuid *cpuid1,
+ const struct cpuX86cpuid *cpuid2)
+{
+ return (cpuid1->eax == cpuid2->eax &&
+ cpuid1->ebx == cpuid2->ebx &&
+ cpuid1->ecx == cpuid2->ecx &&
+ cpuid1->edx == cpuid2->edx);
+}
+
+
+static inline int
+x86cpuidMatchMasked(const struct cpuX86cpuid *cpuid,
+ const struct cpuX86cpuid *mask)
+{
+ return ((cpuid->eax & mask->eax) == mask->eax &&
+ (cpuid->ebx & mask->ebx) == mask->ebx &&
+ (cpuid->ecx & mask->ecx) == mask->ecx &&
+ (cpuid->edx & mask->edx) == mask->edx);
+}
+
+
+static inline int
+x86cpuidMatchAny(const struct cpuX86cpuid *cpuid,
+ const struct cpuX86cpuid *mask)
+{
+ return ((cpuid->eax & mask->eax) ||
+ (cpuid->ebx & mask->ebx) ||
+ (cpuid->ecx & mask->ecx) ||
+ (cpuid->edx & mask->edx));
+}
+
+
+static inline void
+x86cpuidSetBits(struct cpuX86cpuid *cpuid,
+ const struct cpuX86cpuid *mask)
+{
+ cpuid->eax |= mask->eax;
+ cpuid->ebx |= mask->ebx;
+ cpuid->ecx |= mask->ecx;
+ cpuid->edx |= mask->edx;
+}
+
+
+static inline void
+x86cpuidClearBits(struct cpuX86cpuid *cpuid,
+ const struct cpuX86cpuid *mask)
+{
+ cpuid->eax &= ~mask->eax;
+ cpuid->ebx &= ~mask->ebx;
+ cpuid->ecx &= ~mask->ecx;
+ cpuid->edx &= ~mask->edx;
+}
+
+
+static struct cpuX86cpuid *
+x86DataCpuid(const union cpuData *data,
+ uint32_t function)
+{
+ struct cpuX86cpuid *cpuids;
+ int len;
+ unsigned int i;
+
+ if (function < CPUX86_EXTENDED) {
+ cpuids = data->x86.basic;
+ len = data->x86.basic_len;
+ i = function;
+ }
+ else {
+ cpuids = data->x86.extended;
+ len = data->x86.extended_len;
+ i = function - CPUX86_EXTENDED;
+ }
+
+ if (i < len)
+ return cpuids + i;
+ else
+ return NULL;
+}
+
+
+static void
+x86DataFree(union cpuData *data)
+{
+ if (data == NULL)
+ return;
+
+ VIR_FREE(data->x86.basic);
+ VIR_FREE(data->x86.extended);
+ VIR_FREE(data);
+}
+
+
+static union cpuData *
+x86DataCopy(const union cpuData *data)
+{
+ union cpuData *copy = NULL;
+ int i;
+
+ if (VIR_ALLOC(copy) < 0
+ || VIR_ALLOC_N(copy->x86.basic, data->x86.basic_len) < 0
+ || VIR_ALLOC_N(copy->x86.extended, data->x86.extended_len) < 0) {
+ x86DataFree(copy);
+ return NULL;
+ }
+
+ copy->x86.basic_len = data->x86.basic_len;
+ for (i = 0; i < data->x86.basic_len; i++)
+ copy->x86.basic[i] = data->x86.basic[i];
+
+ copy->x86.extended_len = data->x86.extended_len;
+ for (i = 0; i < data->x86.extended_len; i++)
+ copy->x86.extended[i] = data->x86.extended[i];
+
+ return copy;
+}
+
+
+static union cpuData *
+x86DataFromModel(const struct x86_model *model)
+{
+ union cpuData *data = NULL;
+ uint32_t basic_len = 0;
+ uint32_t extended_len = 0;
+ struct cpuX86cpuid *cpuid;
+ int i;
+
+ for (i = 0; i < model->ncpuid; i++) {
+ cpuid = model->cpuid + i;
+ if (cpuid->function < CPUX86_EXTENDED) {
+ if (cpuid->function >= basic_len)
+ basic_len = cpuid->function + 1;
+ }
+ else if (cpuid->function - CPUX86_EXTENDED >= extended_len)
+ extended_len = cpuid->function - CPUX86_EXTENDED + 1;
+ }
+
+ if (VIR_ALLOC(data) < 0
+ || VIR_ALLOC_N(data->x86.basic, basic_len) < 0
+ || VIR_ALLOC_N(data->x86.extended, extended_len) < 0) {
+ x86DataFree(data);
+ return NULL;
+ }
+
+ data->x86.basic_len = basic_len;
+ data->x86.extended_len = extended_len;
+
+ for (i = 0; i < model->ncpuid; i++) {
+ cpuid = x86DataCpuid(data, model->cpuid[i].function);
+ *cpuid = model->cpuid[i];
+ }
+
+ return data;
+}
+
+
+static void
+x86FeatureFree(struct x86_feature *feature)
+{
+ if (feature == NULL)
+ return;
+
+ VIR_FREE(feature->name);
+ VIR_FREE(feature->cpuid);
+ VIR_FREE(feature);
+}
+
+
+static struct x86_feature *
+x86FeatureFind(const struct x86_map *map,
+ const char *name)
+{
+ struct x86_feature *feature;
+
+ feature = map->features;
+ while (feature != NULL) {
+ if (STREQ(feature->name, name))
+ return feature;
+
+ feature = feature->next;
+ }
+
+ return NULL;
+}
+
+
+static int
+x86FeatureLoad(xmlXPathContextPtr ctxt,
+ void *data)
+{
+ struct x86_map *map = data;
+ xmlNodePtr *nodes = NULL;
+ struct x86_feature *feature = NULL;
+ int ret = 0;
+ int i;
+ int n;
+
+ if (VIR_ALLOC(feature) < 0)
+ goto no_memory;
+
+ feature->name = virXPathString(NULL, "string(@name)", ctxt);
+ if (feature->name == NULL) {
+ virCPUReportError(NULL, VIR_ERR_INTERNAL_ERROR,
+ "%s", _("Missing CPU feature name"));
+ goto ignore;
+ }
+
+ if (x86FeatureFind(map, feature->name)) {
+ virCPUReportError(NULL, VIR_ERR_INTERNAL_ERROR,
+ _("CPU feature %s already defined"), feature->name);
+ goto ignore;
+ }
+
+ n = virXPathNodeSet(NULL, "./cpuid", ctxt, &nodes);
+ if (n < 0)
+ goto ignore;
+
+ if (n > 0) {
+ if (VIR_ALLOC_N(feature->cpuid, n) < 0)
+ goto no_memory;
+ feature->ncpuid = n;
+ }
+
+ for (i = 0; i < n; i++) {
+ struct cpuX86cpuid *cpuid = feature->cpuid + i;
+ unsigned long fun, eax, ebx, ecx, edx;
+ int ret_fun, ret_eax, ret_ebx, ret_ecx, ret_edx;
+
+ ctxt->node = nodes[i];
+ fun = eax = ebx = ecx = edx = 0;
+ ret_fun = virXPathULongHex(NULL, "string(@function)", ctxt, &fun);
+ ret_eax = virXPathULongHex(NULL, "string(@eax)", ctxt, &eax);
+ ret_ebx = virXPathULongHex(NULL, "string(@ebx)", ctxt, &ebx);
+ ret_ecx = virXPathULongHex(NULL, "string(@ecx)", ctxt, &ecx);
+ ret_edx = virXPathULongHex(NULL, "string(@edx)", ctxt, &edx);
+
+ if (ret_fun < 0 || ret_eax == -2 || ret_ebx == -2
+ || ret_ecx == -2 || ret_edx == -2) {
+ virCPUReportError(NULL, VIR_ERR_INTERNAL_ERROR,
+ _("Invalid cpuid[%d] in %s feature"), i, feature->name);
+ goto ignore;
+ }
+
+ cpuid->function = fun;
+ cpuid->eax = eax;
+ cpuid->ebx = ebx;
+ cpuid->ecx = ecx;
+ cpuid->edx = edx;
+ }
+
+ if (map->features == NULL)
+ map->features = feature;
+ else {
+ feature->next = map->features;
+ map->features = feature;
+ }
+
+out:
+ return ret;
+
+no_memory:
+ virReportOOMError(NULL);
+ ret = -1;
+
+ignore:
+ x86FeatureFree(feature);
+ goto out;
+}
+
+
+static void
+x86ModelFree(struct x86_model *model)
+{
+ if (model == NULL)
+ return;
+
+ VIR_FREE(model->name);
+ VIR_FREE(model->cpuid);
+ VIR_FREE(model);
+}
+
+
+static struct x86_model *
+x86ModelCopy(const struct x86_model *model)
+{
+ struct x86_model *copy;
+ int i;
+
+ if (VIR_ALLOC(copy) < 0
+ || (copy->name = strdup(model->name)) == NULL
+ || VIR_ALLOC_N(copy->cpuid, model->ncpuid) < 0) {
+ x86ModelFree(copy);
+ return NULL;
+ }
+
+ copy->ncpuid = model->ncpuid;
+ for (i = 0; i < model->ncpuid; i++)
+ copy->cpuid[i] = model->cpuid[i];
+
+ return copy;
+}
+
+
+static int
+x86ModelAddCpuid(struct x86_model *model,
+ const struct cpuX86cpuid *cpuid)
+{
+ struct cpuX86cpuid *model_cpuid;
+
+ model_cpuid = x86cpuidFind(model->cpuid, model->ncpuid, cpuid->function);
+
+ if (model_cpuid != NULL)
+ x86cpuidSetBits(model_cpuid, cpuid);
+ else {
+ if (VIR_REALLOC_N(model->cpuid, model->ncpuid + 1) < 0)
+ return -1;
+
+ model->cpuid[model->ncpuid] = *cpuid;
+ model->ncpuid++;
+ }
+
+ return 0;
+}
+
+
+static void
+x86ModelSubtract(struct x86_model *model1,
+ const struct x86_model *model2)
+{
+ int i;
+ struct cpuX86cpuid *cpuid;
+
+ for (i = 0; i < model2->ncpuid; i++) {
+ cpuid = x86cpuidFind(model1->cpuid,
+ model1->ncpuid,
+ model2->cpuid[i].function);
+ if (cpuid != NULL)
+ x86cpuidClearBits(cpuid, model2->cpuid + i);
+ }
+}
+
+
+static int
+x86ModelAdd(struct x86_model *model1,
+ const struct x86_model *model2)
+{
+ int i;
+
+ for (i = 0; i < model2->ncpuid; i++) {
+ if (x86ModelAddCpuid(model1, model2->cpuid + i))
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static struct x86_model *
+x86ModelFind(const struct x86_map *map,
+ const char *name)
+{
+ struct x86_model *model;
+
+ model = map->models;
+ while (model != NULL) {
+ if (STREQ(model->name, name))
+ return model;
+
+ model = model->next;
+ }
+
+ return NULL;
+}
+
+
+static int
+x86ModelMergeFeature(struct x86_model *model,
+ const struct x86_feature *feature)
+{
+ int i;
+
+ if (feature == NULL)
+ return 0;
+
+ for (i = 0; i < feature->ncpuid; i++) {
+ if (x86ModelAddCpuid(model, feature->cpuid + i))
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static struct x86_model *
+x86ModelFromCPU(const virCPUDefPtr cpu,
+ const struct x86_map *map,
+ int policy)
+{
+ struct x86_model *model = NULL;
+ int i;
+
+ if (cpu->type == VIR_CPU_TYPE_HOST
+ || policy == VIR_CPU_FEATURE_REQUIRE) {
+ if ((model = x86ModelFind(map, cpu->model)) == NULL) {
+ virCPUReportError(NULL, VIR_ERR_INTERNAL_ERROR,
+ _("Unknown CPU model %s"), cpu->model);
+ goto error;
+ }
+
+ if ((model = x86ModelCopy(model)) == NULL)
+ goto no_memory;
+ }
+ else if (VIR_ALLOC(model) < 0)
+ goto no_memory;
+
+ for (i = 0; i < cpu->nfeatures; i++) {
+ const struct x86_feature *feature;
+
+ if (cpu->type == VIR_CPU_TYPE_GUEST
+ && cpu->features[i].policy != policy)
+ continue;
+
+ if ((feature = x86FeatureFind(map, cpu->features[i].name)) == NULL) {
+ virCPUReportError(NULL, VIR_ERR_INTERNAL_ERROR,
+ _("Unknown CPU feature %s"), cpu->features[i].name);
+ goto error;
+ }
+
+ if (x86ModelMergeFeature(model, feature))
+ goto no_memory;
+ }
+
+ return model;
+
+no_memory:
+ virReportOOMError(NULL);
+
+error:
+ x86ModelFree(model);
+ return NULL;
+}
+
+
+static enum compare_result
+x86ModelCompare(const struct x86_model *model1,
+ const struct x86_model *model2)
+{
+ enum compare_result result = EQUAL;
+ struct cpuX86cpuid *cpuid1;
+ struct cpuX86cpuid *cpuid2;
+ int i;
+
+ for (i = 0; i < model1->ncpuid; i++) {
+ enum compare_result match = SUPERSET;
+
+ cpuid1 = model1->cpuid + i;
+ cpuid2 = x86cpuidFind(model2->cpuid,
+ model2->ncpuid,
+ cpuid1->function);
+ if (cpuid2 != NULL) {
+ if (x86cpuidMatch(cpuid1, cpuid2))
+ continue;
+ else if (!x86cpuidMatchMasked(cpuid1, cpuid2))
+ match = SUBSET;
+ }
+
+ if (result == EQUAL)
+ result = match;
+ else if (result != match)
+ return UNRELATED;
+ }
+
+ for (i = 0; i < model2->ncpuid; i++) {
+ enum compare_result match = SUBSET;
+
+ cpuid2 = model2->cpuid + i;
+ cpuid1 = x86cpuidFind(model1->cpuid,
+ model1->ncpuid,
+ cpuid2->function);
+ if (cpuid1 != NULL) {
+ if (x86cpuidMatch(cpuid2, cpuid1))
+ continue;
+ else if (!x86cpuidMatchMasked(cpuid2, cpuid1))
+ match = SUPERSET;
+ }
+
+ if (result == EQUAL)
+ result = match;
+ else if (result != match)
+ return UNRELATED;
+ }
+
+ return result;
+}
+
+
+static int
+x86ModelLoad(xmlXPathContextPtr ctxt,
+ void *data)
+{
+ struct x86_map *map = data;
+ xmlNodePtr *nodes = NULL;
+ struct x86_model *model = NULL;
+ int ret = 0;
+ int i;
+ int n;
+
+ if (VIR_ALLOC(model) < 0)
+ goto no_memory;
+
+ model->name = virXPathString(NULL, "string(@name)", ctxt);
+ if (model->name == NULL) {
+ virCPUReportError(NULL, VIR_ERR_INTERNAL_ERROR,
+ "%s", _("Missing CPU model name"));
+ goto ignore;
+ }
+
+ if (virXPathNode(NULL, "./model", ctxt) != NULL) {
+ const struct x86_model *ancestor;
+ char *name;
+
+ name = virXPathString(NULL, "string(./model/@name)", ctxt);
+ if (name == NULL) {
+ virCPUReportError(NULL, VIR_ERR_INTERNAL_ERROR,
+ _("Missing ancestor's name in CPU model %s"),
+ model->name);
+ goto ignore;
+ }
+
+ if ((ancestor = x86ModelFind(map, name)) == NULL) {
+ virCPUReportError(NULL, VIR_ERR_INTERNAL_ERROR,
+ _("Ancestor model %s not found for CPU model %s"),
+ name, model->name);
+ VIR_FREE(name);
+ goto ignore;
+ }
+
+ VIR_FREE(name);
+
+ if (VIR_ALLOC_N(model->cpuid, ancestor->ncpuid) < 0)
+ goto no_memory;
+
+ model->ncpuid = ancestor->ncpuid;
+ memcpy(model->cpuid, ancestor->cpuid,
+ sizeof(*model->cpuid) * model->ncpuid);
+ }
+
+ n = virXPathNodeSet(NULL, "./feature", ctxt, &nodes);
+ if (n < 0)
+ goto ignore;
+
+ for (i = 0; i < n; i++) {
+ const struct x86_feature *feature;
+ char *name;
+
+ if ((name = virXMLPropString(nodes[i], "name")) == NULL) {
+ virCPUReportError(NULL, VIR_ERR_INTERNAL_ERROR,
+ _("Missing feature name for CPU model %s"), model->name);
+ goto ignore;
+ }
+
+ if ((feature = x86FeatureFind(map, name)) == NULL) {
+ virCPUReportError(NULL, VIR_ERR_INTERNAL_ERROR,
+ _("Feature %s required by CPU model %s not found"),
+ name, model->name);
+ VIR_FREE(name);
+ goto ignore;
+ }
+ VIR_FREE(name);
+
+ if (x86ModelMergeFeature(model, feature))
+ goto no_memory;
+ }
+
+ if (map->models == NULL)
+ map->models = model;
+ else {
+ model->next = map->models;
+ map->models = model;
+ }
+
+out:
+ return ret;
+
+no_memory:
+ virReportOOMError(NULL);
+ ret = -1;
+
+ignore:
+ x86ModelFree(model);
+ goto out;
+}
+
+
+static void
+x86MapFree(struct x86_map *map)
+{
+ if (map == NULL)
+ return;
+
+ while (map->features != NULL) {
+ struct x86_feature *feature = map->features;
+ map->features = feature->next;
+ x86FeatureFree(feature);
+ }
+
+ while (map->models != NULL) {
+ struct x86_model *model = map->models;
+ map->models = model->next;
+ x86ModelFree(model);
+ }
+
+ VIR_FREE(map);
+}
+
+
+static struct x86_map *
+x86LoadMap(void)
+{
+ struct x86_map *map;
+
+ if (VIR_ALLOC(map) < 0) {
+ virReportOOMError(NULL);
+ return NULL;
+ }
+
+ if (cpuMapLoad("x86",
+ x86FeatureLoad, map,
+ x86ModelLoad, map) < 0)
+ goto error;
+
+ return map;
+
+error:
+ x86MapFree(map);
+ return NULL;
+}
+
+
+static virCPUCompareResult
+x86Compute(virCPUDefPtr host,
+ virCPUDefPtr cpu,
+ union cpuData **guest)
+{
+ struct cpuX86cpuid cpuid_zero = { 0, 0, 0, 0, 0 };
+ struct x86_map *map = NULL;
+ struct x86_model *host_model = NULL;
+ struct x86_model *cpu_force = NULL;
+ struct x86_model *cpu_require = NULL;
+ struct x86_model *cpu_optional = NULL;
+ struct x86_model *cpu_disable = NULL;
+ struct x86_model *cpu_forbid = NULL;
+ struct x86_model *diff = NULL;
+ struct x86_model *guest_model = NULL;
+ virCPUCompareResult ret;
+ enum compare_result result;
+ int i;
+
+ if (cpu->arch != NULL) {
+ bool found = false;
+
+ for (i = 0; i < ARRAY_CARDINALITY(archs); i++) {
+ if (STREQ(archs[i], cpu->arch)) {
+ found = true;
+ break;
+ }
+ }
+
+ if (!found)
+ return VIR_CPU_COMPARE_INCOMPATIBLE;
+ }
+
+ if ((map = x86LoadMap()) == NULL)
+ goto error;
+
+ if (!(host_model = x86ModelFromCPU(host, map, 0)))
+ goto error;
+
+ if (!(cpu_force = x86ModelFromCPU(cpu, map, VIR_CPU_FEATURE_FORCE)))
+ goto error;
+
+ if (!(cpu_require = x86ModelFromCPU(cpu, map, VIR_CPU_FEATURE_REQUIRE)))
+ goto error;
+
+ if (!(cpu_optional = x86ModelFromCPU(cpu, map, VIR_CPU_FEATURE_OPTIONAL)))
+ goto error;
+
+ if (!(cpu_disable = x86ModelFromCPU(cpu, map, VIR_CPU_FEATURE_DISABLE)))
+ goto error;
+
+ if (!(cpu_forbid = x86ModelFromCPU(cpu, map, VIR_CPU_FEATURE_FORBID)))
+ goto error;
+
+ if ((diff = x86ModelCopy(host_model)) == NULL)
+ goto no_memory;
+
+ x86ModelSubtract(diff, cpu_require);
+ x86ModelSubtract(diff, cpu_optional);
+
+ for (i = 0; i < cpu_forbid->ncpuid; i++) {
+ const struct cpuX86cpuid *cpuid1;
+ const struct cpuX86cpuid *cpuid2;
+
+ cpuid1 = cpu_forbid->cpuid + i;
+ cpuid2 = x86cpuidFind(host_model->cpuid,
+ host_model->ncpuid,
+ cpuid1->function);
+
+ if (cpuid2 != NULL && x86cpuidMatchAny(cpuid2, cpuid1)) {
+ ret = VIR_CPU_COMPARE_INCOMPATIBLE;
+ goto out;
+ }
+ }
+
+ result = x86ModelCompare(host_model, cpu_require);
+ if (result == SUBSET || result == UNRELATED) {
+ ret = VIR_CPU_COMPARE_INCOMPATIBLE;
+ goto out;
+ }
+
+ ret = VIR_CPU_COMPARE_IDENTICAL;
+
+ for (i = 0; i < host_model->ncpuid; i++) {
+ if (!x86cpuidMatch(host_model->cpuid + i, &cpuid_zero)) {
+ ret = VIR_CPU_COMPARE_SUPERSET;
+ break;
+ }
+ }
+
+ if (ret == VIR_CPU_COMPARE_SUPERSET
+ && cpu->type == VIR_CPU_TYPE_GUEST
+ && cpu->match == VIR_CPU_MATCH_STRICT) {
+ ret = VIR_CPU_COMPARE_INCOMPATIBLE;
+ goto out;
+ }
+
+ if (guest != NULL) {
+ if ((guest_model = x86ModelCopy(host_model)) == NULL)
+ goto no_memory;
+
+ if (cpu->type == VIR_CPU_TYPE_GUEST
+ && cpu->match == VIR_CPU_MATCH_EXACT)
+ x86ModelSubtract(guest_model, diff);
+
+ if (x86ModelAdd(guest_model, cpu_force))
+ goto no_memory;
+
+ x86ModelSubtract(guest_model, cpu_disable);
+
+ if ((*guest = x86DataFromModel(guest_model)) == NULL)
+ goto no_memory;
+ }
+
+out:
+ x86MapFree(map);
+ x86ModelFree(host_model);
+ x86ModelFree(diff);
+ x86ModelFree(cpu_force);
+ x86ModelFree(cpu_require);
+ x86ModelFree(cpu_optional);
+ x86ModelFree(cpu_disable);
+ x86ModelFree(cpu_forbid);
+ x86ModelFree(guest_model);
+
+ return ret;
+
+no_memory:
+ virReportOOMError(NULL);
+
+error:
+ ret = VIR_CPU_COMPARE_ERROR;
+ goto out;
+}
+
+
+static virCPUCompareResult
+x86Compare(virCPUDefPtr host,
+ virCPUDefPtr cpu)
+{
+ return x86Compute(host, cpu, NULL);
+}
+
+
+static virCPUCompareResult
+x86GuestData(virCPUDefPtr host,
+ virCPUDefPtr guest,
+ union cpuData **data)
+{
+ return x86Compute(host, guest, data);
+}
+
+
+static int
+x86Decode(virCPUDefPtr cpu,
+ const union cpuData *data,
+ unsigned int nmodels,
+ const char **models)
+{
+ int ret = -1;
+ struct x86_map *map;
+ const struct x86_feature *feature;
+ const struct x86_model *model = NULL;
+ const struct x86_model *candidate;
+ union cpuData *tmp = NULL;
+ struct cpuX86cpuid *cpuid;
+ int i;
+
+ if (data == NULL || (map = x86LoadMap()) == NULL)
+ return -1;
+
+ candidate = map->models;
+ while (candidate != NULL) {
+ for (i = 0; i < candidate->ncpuid; i++) {
+ cpuid = x86DataCpuid(data, candidate->cpuid[i].function);
+ if (cpuid == NULL
+ || !x86cpuidMatchMasked(cpuid, candidate->cpuid + i))
+ goto next;
+ }
+
+ if (model == NULL
+ || x86ModelCompare(model, candidate) == SUBSET) {
+ bool found = false;
+ for (i = 0; i < nmodels; i++) {
+ if (STREQ(models[i], candidate->name)) {
+ found = true;
+ break;
+ }
+ }
+
+ if (nmodels > 0 && !found) {
+ VIR_DEBUG("CPU model %s not allowed by hypervisor; ignoring",
+ candidate->name);
+ }
+ else
+ model = candidate;
+ }
+
+ next:
+ candidate = candidate->next;
+ }
+
+ if (model == NULL) {
+ virCPUReportError(NULL, VIR_ERR_INTERNAL_ERROR,
+ "%s", _("Cannot find suitable CPU model for given data"));
+ goto out;
+ }
+
+ if ((cpu->model = strdup(model->name)) == NULL
+ || (tmp = x86DataCopy(data)) == NULL) {
+ virReportOOMError(NULL);
+ goto out;
+ }
+
+ for (i = 0; i < model->ncpuid; i++) {
+ x86cpuidClearBits(x86DataCpuid(tmp, model->cpuid[i].function),
+ model->cpuid + i);
+ }
+
+ feature = map->features;
+ while (feature != NULL) {
+ for (i = 0; i < feature->ncpuid; i++) {
+ if ((cpuid = x86DataCpuid(tmp, feature->cpuid[i].function))
+ && x86cpuidMatchMasked(cpuid, feature->cpuid + i)) {
+ x86cpuidClearBits(cpuid, feature->cpuid + i);
+ if (virCPUDefAddFeature(NULL, cpu, feature->name,
+ VIR_CPU_FEATURE_REQUIRE) < 0)
+ goto out;
+ }
+ }
+
+ feature = feature->next;
+ }
+
+ ret = 0;
+
+out:
+ x86DataFree(tmp);
+ x86MapFree(map);
+
+ return ret;
+}
+
+
+static union cpuData *
+x86EncodePolicy(const virCPUDefPtr cpu,
+ const struct x86_map *map,
+ enum virCPUFeaturePolicy policy)
+{
+ struct x86_model *model;
+ union cpuData *data = NULL;
+
+ if (!(model = x86ModelFromCPU(cpu, map, policy)))
+ return NULL;
+
+ if (!(data = x86DataFromModel(model)))
+ virReportOOMError(NULL);
+
+ x86ModelFree(model);
+
+ return data;
+}
+
+
+static int
+x86Encode(const virCPUDefPtr cpu,
+ union cpuData **forced,
+ union cpuData **required,
+ union cpuData **optional,
+ union cpuData **disabled,
+ union cpuData **forbidden)
+{
+ struct x86_map *map = NULL;
+ union cpuData *data_forced = NULL;
+ union cpuData *data_required = NULL;
+ union cpuData *data_optional = NULL;
+ union cpuData *data_disabled = NULL;
+ union cpuData *data_forbidden = NULL;
+ int ret = -1;
+
+ if ((map = x86LoadMap()) == NULL)
+ goto error;
+
+ if (forced) {
+ data_forced = x86EncodePolicy(cpu, map, VIR_CPU_FEATURE_FORCE);
+ if (!data_forced)
+ goto error;
+ }
+
+ if (required) {
+ data_required = x86EncodePolicy(cpu, map, VIR_CPU_FEATURE_REQUIRE);
+ if (!data_required)
+ goto error;
+ }
+
+ if (optional) {
+ data_optional = x86EncodePolicy(cpu, map, VIR_CPU_FEATURE_OPTIONAL);
+ if (!data_optional)
+ goto error;
+ }
+
+ if (disabled) {
+ data_disabled = x86EncodePolicy(cpu, map, VIR_CPU_FEATURE_DISABLE);
+ if (!data_disabled)
+ goto error;
+ }
+
+ if (forbidden) {
+ data_forbidden = x86EncodePolicy(cpu, map, VIR_CPU_FEATURE_FORBID);
+ if (!data_forbidden)
+ goto error;
+ }
+
+ if (forced)
+ *forced = data_forced;
+ if (required)
+ *required = data_required;
+ if (optional)
+ *optional = data_optional;
+ if (disabled)
+ *disabled = data_disabled;
+ if (forbidden)
+ *forbidden = data_forbidden;
+
+ ret = 0;
+
+cleanup:
+ x86MapFree(map);
+
+ return ret;
+
+error:
+ x86DataFree(data_forced);
+ x86DataFree(data_required);
+ x86DataFree(data_optional);
+ x86DataFree(data_disabled);
+ x86DataFree(data_forbidden);
+ goto cleanup;
+}
+
+
+#if HAVE_CPUID
+static inline void
+cpuidCall(struct cpuX86cpuid *cpuid)
+{
+#if __x86_64__
+ asm("cpuid"
+ : "=a" (cpuid->eax),
+ "=b" (cpuid->ebx),
+ "=c" (cpuid->ecx),
+ "=d" (cpuid->edx)
+ : "a" (cpuid->function));
+#else
+ /* we need to avoid direct use of ebx for CPUID output as it is used
+ * for global offset table on i386 with -fPIC
+ */
+ asm("push %%ebx;"
+ "cpuid;"
+ "mov %%ebx, %1;"
+ "pop %%ebx;"
+ : "=a" (cpuid->eax),
+ "=r" (cpuid->ebx),
+ "=c" (cpuid->ecx),
+ "=d" (cpuid->edx)
+ : "a" (cpuid->function)
+ : "cc");
+#endif
+}
+
+
+static int
+cpuidSet(uint32_t base, struct cpuX86cpuid **set)
+{
+ uint32_t max;
+ uint32_t i;
+ struct cpuX86cpuid cpuid = { base, 0, 0, 0, 0 };
+
+ cpuidCall(&cpuid);
+ max = cpuid.eax - base;
+
+ if (VIR_ALLOC_N(*set, max + 1) < 0) {
+ virReportOOMError(NULL);
+ return -1;
+ }
+
+ for (i = 0; i <= max; i++) {
+ cpuid.function = base | i;
+ cpuidCall(&cpuid);
+ (*set)[i] = cpuid;
+ }
+
+ return max + 1;
+}
+
+
+static union cpuData *
+x86NodeData(void)
+{
+ union cpuData *data;
+
+ if (VIR_ALLOC(data) < 0) {
+ virReportOOMError(NULL);
+ return NULL;
+ }
+
+ data->x86.basic_len = cpuidSet(CPUX86_BASIC, &data->x86.basic);
+ if (data->x86.basic_len < 0)
+ goto error;
+
+ data->x86.extended_len = cpuidSet(CPUX86_EXTENDED, &data->x86.extended);
+ if (data->x86.extended_len < 0)
+ goto error;
+
+ return data;
+
+error:
+ x86DataFree(data);
+
+ return NULL;
+}
+#endif
+
+
+struct cpuArchDriver cpuDriverX86 = {
+ .name = "x86",
+ .arch = archs,
+ .narch = ARRAY_CARDINALITY(archs),
+ .compare = x86Compare,
+ .decode = x86Decode,
+ .encode = x86Encode,
+ .free = x86DataFree,
+#if HAVE_CPUID
+ .nodeData = x86NodeData,
+#else
+ .nodeData = NULL,
+#endif
+ .guestData = x86GuestData
+};