diff --git a/configure.in b/configure.in index c86ee97d35..bac042aaa4 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 + ]], + [[ + 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/po/POTFILES.in b/po/POTFILES.in index 6fef77d439..1ab085950d 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -10,6 +10,9 @@ src/conf/node_device_conf.c src/conf/secret_conf.c src/conf/storage_conf.c src/conf/storage_encryption_conf.c +src/cpu/cpu.c +src/cpu/cpu_map.c +src/cpu/cpu_x86.c src/datatypes.c src/interface/netcf_driver.c src/libvirt.c diff --git a/src/Makefile.am b/src/Makefile.am index 432a66edd6..471e403149 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@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 0000000000..e822f221e6 --- /dev/null +++ b/src/cpu/cpu.c @@ -0,0 +1,241 @@ +/* + * 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 + */ + +#include + +#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 0000000000..5b1636dcfd --- /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 + */ + +#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 0000000000..b18c1b4832 --- /dev/null +++ b/src/cpu/cpu_generic.c @@ -0,0 +1,122 @@ +/* + * 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 + */ + +#include + +#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 0000000000..8fc952a1ff --- /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 + */ + +#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 0000000000..067f6ff9cc --- /dev/null +++ b/src/cpu/cpu_map.c @@ -0,0 +1,129 @@ +/* + * 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 + */ + +#include + +#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 0000000000..affe534861 --- /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 + */ + +#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 0000000000..af0a65ba40 --- /dev/null +++ b/src/cpu/cpu_x86.c @@ -0,0 +1,1161 @@ +/* + * 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 + */ + +#include + +#include + +#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 +}; diff --git a/src/cpu/cpu_x86.h b/src/cpu/cpu_x86.h new file mode 100644 index 0000000000..58f1aaeb7a --- /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 + */ + +#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 0000000000..8b09d239f5 --- /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 + */ + +#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 9acd06287e..f90f269715 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;