[libvirt] [PATCH v3 09/13] CPU selection infrastructure

Jiri Denemark jdenemar at redhat.com
Tue Dec 15 23:04:06 UTC 2009


Each driver supporting CPU selection must fill in host CPU capabilities.
When filling them, drivers for hypervisors running on the same node as
libvirtd can use cpuNodeData() to obtain raw CPU data. Other drivers,
such as VMware, need to implement their own way of getting such data.
Raw data can be decoded into virCPUDefPtr using cpuDecode() function.

When implementing virConnectCompareCPU(), a hypervisor driver can just
call cpuCompareXML() function with host CPU capabilities.

For each guest for which a driver supports selecting CPU models, it must
set the appropriate feature in guest's capabilities:

    virCapabilitiesAddGuestFeature(guest, "cpuselection", 1, 0)

Actions needed when a domain is being created depend on whether the
hypervisor understands raw CPU data (currently CPUID for i686, x86_64
architectures) or symbolic names has to be used.

Typical use by hypervisors which prefer CPUID (such as VMware and Xen):

- convert guest CPU configuration from domain's XML into a set of raw
  data structures each representing one of the feature policies:

    cpuEncode(conn, architecture, guest_cpu_config,
              &forced_data, &required_data, &optional_data,
              &disabled_data, &forbidden_data)

- create a mask or whatever the hypervisor expects to see and pass it
  to the hypervisor

Typical use by hypervisors with symbolic model names (such as QEMU):

- get raw CPU data for a computed guest CPU:

    cpuGuestData(conn, host_cpu, guest_cpu_config, &data)

- decode raw data into virCPUDefPtr with a possible restriction on
  allowed model names:

    cpuDecode(conn, guest, data, n_allowed_models, allowed_models)

- pass guest->model and guest->features to the hypervisor

Signed-off-by: Jiri Denemark <jdenemar at redhat.com>
---
 configure.in             |   20 +
 src/Makefile.am          |   12 +
 src/cpu/cpu.c            |  242 ++++++++++
 src/cpu/cpu.h            |  129 +++++
 src/cpu/cpu_generic.c    |  123 +++++
 src/cpu/cpu_generic.h    |   32 ++
 src/cpu/cpu_map.c        |  130 ++++++
 src/cpu/cpu_map.h        |   41 ++
 src/cpu/cpu_x86.c        | 1159 ++++++++++++++++++++++++++++++++++++++++++++++
 src/cpu/cpu_x86.h        |   31 ++
 src/cpu/cpu_x86_data.h   |   45 ++
 src/libvirt_private.syms |   10 +
 12 files changed, 1974 insertions(+), 0 deletions(-)
 create mode 100644 src/cpu/cpu.c
 create mode 100644 src/cpu/cpu.h
 create mode 100644 src/cpu/cpu_generic.c
 create mode 100644 src/cpu/cpu_generic.h
 create mode 100644 src/cpu/cpu_map.c
 create mode 100644 src/cpu/cpu_map.h
 create mode 100644 src/cpu/cpu_x86.c
 create mode 100644 src/cpu/cpu_x86.h
 create mode 100644 src/cpu/cpu_x86_data.h

diff --git a/configure.in b/configure.in
index 6ed2efd..4b26c4e 100644
--- a/configure.in
+++ b/configure.in
@@ -78,6 +78,26 @@ AC_SUBST(VERSION_SCRIPT_FLAGS)
 
 LIBVIRT_COMPILE_WARNINGS([maximum])
 
+AC_MSG_CHECKING([for CPUID instruction])
+AC_COMPILE_IFELSE(AC_LANG_PROGRAM(
+  [[
+    #include <stdint.h>
+  ]],
+  [[
+    uint32_t eax, ebx, ecx, edx;
+    asm volatile (
+        "cpuid"
+        : "=a" (eax), "=b" (ebx), "=c" (ecx), "=d" (edx)
+        : "a" (eax));
+  ]]),
+  [have_cpuid=yes],
+  [have_cpuid=no])
+if test "x$have_cpuid" = xyes; then
+  AC_DEFINE_UNQUOTED([HAVE_CPUID], 1, [whether CPUID instruction is supported])
+fi
+AC_MSG_RESULT([$have_cpuid])
+
+
 dnl Support large files / 64 bit seek offsets.
 dnl Use --disable-largefile if you don't want this.
 AC_SYS_LARGEFILE
diff --git a/src/Makefile.am b/src/Makefile.am
index 432a66e..471e403 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -16,6 +16,7 @@ INCLUDES =							\
 		-DSBINDIR=\""$(sbindir)"\"			\
 		-DSYSCONF_DIR="\"$(sysconfdir)\""		\
 		-DLOCALEBASEDIR=\""$(datadir)/locale"\"		\
+		-DDATADIR=\""$(datadir)/libvirt"\"		\
 		-DLOCAL_STATE_DIR=\""$(localstatedir)"\"	\
 		-DGETTEXT_PACKAGE=\"$(PACKAGE)\"		\
 		$(WARN_CFLAGS)					\
@@ -280,6 +281,11 @@ NODE_DEVICE_DRIVER_UDEV_SOURCES =				\
 		node_device/node_device_udev.c			\
 		node_device/node_device_udev.h
 
+CPU_SOURCES =							\
+		cpu/cpu.h cpu/cpu.c				\
+		cpu/cpu_generic.h cpu/cpu_generic.c		\
+		cpu/cpu_x86.h cpu/cpu_x86.c cpu/cpu_x86_data.h	\
+		cpu/cpu_map.h cpu/cpu_map.c
 
 #########################
 #
@@ -301,6 +307,12 @@ libvirt_conf_la_SOURCES = $(CONF_SOURCES)
 libvirt_conf_la_CFLAGS =
 libvirt_conf_la_LDFLAGS =
 
+noinst_LTLIBRARIES += libvirt_cpu.la
+libvirt_la_LIBADD += libvirt_cpu.la
+libvirt_cpu_la_CFLAGS = \
+		-I at top_srcdir@/src/conf
+libvirt_cpu_la_SOURCES = $(CPU_SOURCES)
+
 
 noinst_LTLIBRARIES += libvirt_driver.la
 libvirt_la_LIBADD += libvirt_driver.la
diff --git a/src/cpu/cpu.c b/src/cpu/cpu.c
new file mode 100644
index 0000000..c9ec64b
--- /dev/null
+++ b/src/cpu/cpu.c
@@ -0,0 +1,242 @@
+/*
+ * 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 at 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);
+}
+
diff --git a/src/cpu/cpu.h b/src/cpu/cpu.h
new file mode 100644
index 0000000..5b1636d
--- /dev/null
+++ b/src/cpu/cpu.h
@@ -0,0 +1,129 @@
+/*
+ * cpu.h: 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 at redhat.com>
+ */
+
+#ifndef __VIR_CPU_H__
+#define __VIR_CPU_H__
+
+#include "virterror_internal.h"
+#include "datatypes.h"
+#include "conf/cpu_conf.h"
+#include "cpu_x86_data.h"
+
+
+#define virCPUReportError(conn, code, fmt...)                           \
+        virReportErrorHelper(conn, VIR_FROM_CPU, code, __FILE__,        \
+                             __FUNCTION__, __LINE__, fmt)
+
+
+union cpuData {
+    struct cpuX86Data x86;
+    /* generic driver needs no data */
+};
+
+
+typedef virCPUCompareResult
+(*cpuArchCompare)   (virCPUDefPtr host,
+                     virCPUDefPtr cpu);
+
+typedef int
+(*cpuArchDecode)    (virCPUDefPtr cpu,
+                     const union cpuData *data,
+                     unsigned int nmodels,
+                     const char **models);
+
+typedef int
+(*cpuArchEncode)    (const virCPUDefPtr cpu,
+                     union cpuData **forced,
+                     union cpuData **required,
+                     union cpuData **optional,
+                     union cpuData **disabled,
+                     union cpuData **forbidden);
+
+typedef void
+(*cpuArchDataFree)  (union cpuData *data);
+
+typedef union cpuData *
+(*cpuArchNodeData)  (void);
+
+typedef virCPUCompareResult
+(*cpuArchGuestData) (virCPUDefPtr host,
+                     virCPUDefPtr guest,
+                     union cpuData **data);
+
+
+struct cpuArchDriver {
+    const char *name;
+    const char **arch;
+    unsigned int narch;
+    cpuArchCompare      compare;
+    cpuArchDecode       decode;
+    cpuArchEncode       encode;
+    cpuArchDataFree     free;
+    cpuArchNodeData     nodeData;
+    cpuArchGuestData    guestData;
+};
+
+
+extern virCPUCompareResult
+cpuCompareXML(virConnectPtr conn,
+              virCPUDefPtr host,
+              const char *xml);
+
+extern virCPUCompareResult
+cpuCompare  (virConnectPtr conn,
+             virCPUDefPtr host,
+             virCPUDefPtr cpu);
+
+extern int
+cpuDecode   (virConnectPtr conn,
+             virCPUDefPtr cpu,
+             const union cpuData *data,
+             unsigned int nmodels,
+             const char **models);
+
+extern 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);
+
+extern void
+cpuDataFree (virConnectPtr conn,
+             const char *arch,
+             union cpuData *data);
+
+extern union cpuData *
+cpuNodeData (virConnectPtr conn,
+             const char *arch);
+
+extern virCPUCompareResult
+cpuGuestData(virConnectPtr conn,
+             virCPUDefPtr host,
+             virCPUDefPtr guest,
+             union cpuData **data);
+
+#endif /* __VIR_CPU_H__ */
diff --git a/src/cpu/cpu_generic.c b/src/cpu/cpu_generic.c
new file mode 100644
index 0000000..4caa06a
--- /dev/null
+++ b/src/cpu/cpu_generic.c
@@ -0,0 +1,123 @@
+/*
+ * cpu_generic.c: CPU manipulation driver for architectures which are not
+ * handled by their own driver
+ *
+ * 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 at redhat.com>
+ */
+
+#include <config.h>
+
+#include "hash.h"
+#include "cpu.h"
+#include "cpu_generic.h"
+
+
+#define VIR_FROM_THIS VIR_FROM_CPU
+
+
+static virHashTablePtr
+genericHashFeatures(virCPUDefPtr cpu)
+{
+    virHashTablePtr hash;
+    unsigned int i;
+
+    if ((hash = virHashCreate(cpu->nfeatures)) == NULL)
+        return NULL;
+
+    for (i = 0; i < cpu->nfeatures; i++) {
+        if (virHashAddEntry(hash,
+                            cpu->features[i].name,
+                            cpu->features + i)) {
+            virHashFree(hash, NULL);
+            return NULL;
+        }
+    }
+
+    return hash;
+}
+
+
+static virCPUCompareResult
+genericCompare(virCPUDefPtr host,
+               virCPUDefPtr cpu)
+{
+    virHashTablePtr hash;
+    virCPUCompareResult ret = VIR_CPU_COMPARE_ERROR;
+    unsigned int i;
+    unsigned int reqfeatures;
+
+    if ((cpu->arch && STRNEQ(host->arch, cpu->arch)) ||
+        STRNEQ(host->model, cpu->model))
+        return VIR_CPU_COMPARE_INCOMPATIBLE;
+
+    if ((hash = genericHashFeatures(host)) == NULL) {
+        virReportOOMError(NULL);
+        goto cleanup;
+    }
+
+    reqfeatures = 0;
+    for (i = 0; i < cpu->nfeatures; i++) {
+        void *hval = virHashLookup(hash, cpu->features[i].name);
+
+        if (hval) {
+            if (cpu->type == VIR_CPU_TYPE_GUEST &&
+                cpu->features[i].policy == VIR_CPU_FEATURE_FORBID) {
+                ret = VIR_CPU_COMPARE_INCOMPATIBLE;
+                goto cleanup;
+            }
+            reqfeatures++;
+        }
+        else {
+            if (cpu->type == VIR_CPU_TYPE_HOST ||
+                cpu->features[i].policy == VIR_CPU_FEATURE_REQUIRE) {
+                ret = VIR_CPU_COMPARE_INCOMPATIBLE;
+                goto cleanup;
+            }
+        }
+    }
+
+    if (host->nfeatures > reqfeatures) {
+        if (cpu->type == VIR_CPU_TYPE_GUEST &&
+            cpu->match == VIR_CPU_MATCH_STRICT)
+            ret = VIR_CPU_COMPARE_INCOMPATIBLE;
+        else
+            ret = VIR_CPU_COMPARE_SUPERSET;
+    }
+    else
+        ret = VIR_CPU_COMPARE_IDENTICAL;
+
+cleanup:
+    virHashFree(hash, NULL);
+    return ret;
+}
+
+
+struct cpuArchDriver cpuDriverGeneric = {
+    .name = "generic",
+    .arch = NULL,
+    .narch = 0,
+    .compare    = genericCompare,
+    .decode     = NULL,
+    .encode     = NULL,
+    .free       = NULL,
+    .nodeData   = NULL,
+    .guestData  = NULL
+};
+
diff --git a/src/cpu/cpu_generic.h b/src/cpu/cpu_generic.h
new file mode 100644
index 0000000..8fc952a
--- /dev/null
+++ b/src/cpu/cpu_generic.h
@@ -0,0 +1,32 @@
+/*
+ * cpu_generic.h: CPU manipulation driver for architectures which are not
+ * handled by their own driver
+ *
+ * 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 at redhat.com>
+ */
+
+#ifndef __VIR_CPU_GENERIC_H__
+#define __VIR_CPU_GENERIC_H__
+
+#include "cpu.h"
+
+extern struct cpuArchDriver cpuDriverGeneric;
+
+#endif /* __VIR_CPU_GENERIC_H__ */
diff --git a/src/cpu/cpu_map.c b/src/cpu/cpu_map.c
new file mode 100644
index 0000000..55fb1ea
--- /dev/null
+++ b/src/cpu/cpu_map.c
@@ -0,0 +1,130 @@
+/*
+ * cpu_map.c: internal functions for handling CPU mapping configuration
+ *
+ * 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 at redhat.com>
+ */
+
+#include <config.h>
+
+#include "memory.h"
+#include "cpu.h"
+#include "cpu_map.h"
+
+
+#define VIR_FROM_THIS VIR_FROM_CPU
+
+#define CPUMAPFILE DATADIR "/cpu_map.xml"
+
+
+static int load(xmlXPathContextPtr ctxt,
+                const char *node,
+                cpuMapLoadCallback callback,
+                void *data)
+{
+    int ret = -1;
+    xmlNodePtr ctxt_node;
+    xmlNodePtr cur;
+
+    ctxt_node = ctxt->node;
+
+    cur = ctxt_node->children;
+    while (cur != NULL) {
+        if (cur->type == XML_ELEMENT_NODE &&
+            xmlStrEqual(cur->name, BAD_CAST node)) {
+            ctxt->node = cur;
+            if (callback(ctxt, data) < 0)
+                goto cleanup;
+        }
+
+        cur = cur->next;
+    }
+
+    ret = 0;
+
+cleanup:
+    ctxt->node = ctxt_node;
+
+    return ret;
+}
+
+
+int cpuMapLoad(const char *arch,
+               cpuMapLoadCallback feature_cb,
+               void *model_data,
+               cpuMapLoadCallback model_cb,
+               void *feature_data)
+{
+    xmlDocPtr xml = NULL;
+    xmlXPathContextPtr ctxt = NULL;
+    virBuffer buf = VIR_BUFFER_INITIALIZER;
+    char *xpath = NULL;
+    int ret = -1;
+
+    if (arch == NULL) {
+        virCPUReportError(NULL, VIR_ERR_INTERNAL_ERROR,
+                _("undefined hardware architecture"));
+        return -1;
+    }
+
+    if ((xml = xmlParseFile(CPUMAPFILE)) == NULL) {
+        virCPUReportError(NULL, VIR_ERR_INTERNAL_ERROR,
+                _("cannot parse CPU map file: %s"),
+                CPUMAPFILE);
+        goto cleanup;
+    }
+
+    if ((ctxt = xmlXPathNewContext(xml)) == NULL)
+        goto no_memory;
+
+    virBufferVSprintf(&buf, "./arch[@name='%s']", arch);
+    if (virBufferError(&buf))
+        goto no_memory;
+
+    xpath = virBufferContentAndReset(&buf);
+
+    ctxt->node = xmlDocGetRootElement(xml);
+
+    if ((ctxt->node = virXPathNode(NULL, xpath, ctxt)) == NULL) {
+        virCPUReportError(NULL, VIR_ERR_INTERNAL_ERROR,
+                _("cannot find CPU map for %s architecture"), arch);
+        goto cleanup;
+    }
+
+    if ((feature_cb && load(ctxt, "feature", feature_cb, feature_data) < 0) ||
+        (model_cb && load(ctxt, "model", model_cb, model_data) < 0)) {
+        virCPUReportError(NULL, VIR_ERR_INTERNAL_ERROR,
+                _("cannot parse CPU map for %s architecture"), arch);
+        goto cleanup;
+    }
+
+    ret = 0;
+
+cleanup:
+    xmlXPathFreeContext(ctxt);
+    xmlFreeDoc(xml);
+    VIR_FREE(xpath);
+
+    return ret;
+
+no_memory:
+    virReportOOMError(NULL);
+    goto cleanup;
+}
+
diff --git a/src/cpu/cpu_map.h b/src/cpu/cpu_map.h
new file mode 100644
index 0000000..affe534
--- /dev/null
+++ b/src/cpu/cpu_map.h
@@ -0,0 +1,41 @@
+/*
+ * cpu_map.h: internal functions for handling CPU mapping configuration
+ *
+ * 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 at redhat.com>
+ */
+
+#ifndef __VIR_CPU_MAP_H__
+#define __VIR_CPU_MAP_H__
+
+#include "xml.h"
+
+
+typedef int
+(*cpuMapLoadCallback)  (xmlXPathContextPtr ctxt,
+                        void *data);
+
+extern int
+cpuMapLoad(const char *arch,
+           cpuMapLoadCallback feature_cb,
+           void *model_data,
+           cpuMapLoadCallback model_cb,
+           void *feature_data);
+
+#endif /* __VIR_CPU_MAP_H__ */
diff --git a/src/cpu/cpu_x86.c b/src/cpu/cpu_x86.c
new file mode 100644
index 0000000..3bb0df3
--- /dev/null
+++ b/src/cpu/cpu_x86.c
@@ -0,0 +1,1159 @@
+/*
+ * 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 at 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 *prev;
+    struct x86_feature *next;
+};
+
+struct x86_model {
+    char *name;
+    unsigned int ncpuid;
+    struct cpuX86cpuid *cpuid;
+
+    struct x86_model *prev;
+    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;
+        int fun, eax, ebx, ecx, edx;
+
+        ctxt->node = nodes[i];
+        fun = virXPathULongHex(NULL, "string(@function)", ctxt,
+                               (unsigned long *) &cpuid->function);
+        eax = virXPathULongHex(NULL, "string(@eax)", ctxt,
+                               (unsigned long *) &cpuid->eax);
+        ebx = virXPathULongHex(NULL, "string(@ebx)", ctxt,
+                               (unsigned long *) &cpuid->ebx);
+        ecx = virXPathULongHex(NULL, "string(@ecx)", ctxt,
+                               (unsigned long *) &cpuid->ecx);
+        edx = virXPathULongHex(NULL, "string(@edx)", ctxt,
+                               (unsigned long *) &cpuid->edx);
+
+        if (fun < 0 || eax == -2 || ebx == -2 || ecx == -2 || edx == -2) {
+            virCPUReportError(NULL, VIR_ERR_INTERNAL_ERROR,
+                    _("Invalid cpuid[%d] in %s feature"), i, feature->name);
+            goto ignore;
+        }
+    }
+
+    if (map->features == NULL)
+        map->features = feature;
+    else {
+        feature->next = map->features;
+        map->features->prev = feature;
+        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->prev = model;
+        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 int
+cpuidSet(uint32_t base, struct cpuX86cpuid **set)
+{
+    uint32_t max;
+    uint32_t i;
+
+    asm("cpuid"
+        : "=a" (max)
+        : "a" (base));
+
+    max -= base;
+
+    if (virAllocN(set, sizeof(**set), max + 1) < 0) {
+        virReportOOMError(NULL);
+        return -1;
+    }
+
+    for (i = 0; i <= max; i++) {
+        struct cpuX86cpuid *cpuid = (*set) + i;
+
+        cpuid->function = base | i;
+
+#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
+    }
+
+    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
+};
+
diff --git a/src/cpu/cpu_x86.h b/src/cpu/cpu_x86.h
new file mode 100644
index 0000000..58f1aae
--- /dev/null
+++ b/src/cpu/cpu_x86.h
@@ -0,0 +1,31 @@
+/*
+ * cpu_x86.h: 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 at redhat.com>
+ */
+
+#ifndef __VIR_CPU_X86_H__
+#define __VIR_CPU_X86_H__
+
+#include "cpu.h"
+
+extern struct cpuArchDriver cpuDriverX86;
+
+#endif /* __VIR_CPU_X86_H__ */
diff --git a/src/cpu/cpu_x86_data.h b/src/cpu/cpu_x86_data.h
new file mode 100644
index 0000000..8b09d23
--- /dev/null
+++ b/src/cpu/cpu_x86_data.h
@@ -0,0 +1,45 @@
+/*
+ * cpu_x86_data.h: x86 specific CPU data
+ *
+ * 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 at redhat.com>
+ */
+
+#ifndef __VIR_CPU_X86_DATA_H__
+#define __VIR_CPU_X86_DATA_H__
+
+struct cpuX86cpuid {
+    uint32_t function;
+    uint32_t eax;
+    uint32_t ebx;
+    uint32_t ecx;
+    uint32_t edx;
+};
+
+#define CPUX86_BASIC    0x0
+#define CPUX86_EXTENDED 0x80000000
+
+struct cpuX86Data {
+    int basic_len;
+    struct cpuX86cpuid *basic;
+    int extended_len;
+    struct cpuX86cpuid *extended;
+};
+
+#endif /* __VIR_CPU_X86_DATA_H__ */
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index 9acd062..f90f269 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -71,6 +71,16 @@ virCgroupGetFreezerState;
 virCgroupSetFreezerState;
 
 
+# cpu.h
+cpuCompare;
+cpuCompareXML;
+cpuDataFree;
+cpuDecode;
+cpuEncode;
+cpuGuestData;
+cpuNodeData;
+
+
 # cpu_conf.h
 virCPUDefFree;
 virCPUDefParseXML;
-- 
1.6.5.6




More information about the libvir-list mailing list