diff --git a/docs/schemas/domaincommon.rng b/docs/schemas/domaincommon.rng index 912a1a22a899ae4a4a97db785cacdf5c5c29df53..3d205b09ee6e597dc495ecd19cefbb26874b3d09 100644 --- a/docs/schemas/domaincommon.rng +++ b/docs/schemas/domaincommon.rng @@ -2854,6 +2854,13 @@ + + + + [^,]{12} + + + diff --git a/src/conf/cpu_conf.c b/src/conf/cpu_conf.c index b520f7c6417cf2d1ad7e02651e806cbf70dd72f6..7fe3c1ef97eb478eeb10bd214aac768176cf502e 100644 --- a/src/conf/cpu_conf.c +++ b/src/conf/cpu_conf.c @@ -68,6 +68,7 @@ virCPUDefFreeModel(virCPUDefPtr def) VIR_FREE(def->model); VIR_FREE(def->vendor); + VIR_FREE(def->vendor_id); for (i = 0; i < def->nfeatures; i++) VIR_FREE(def->features[i].name); @@ -104,6 +105,7 @@ virCPUDefCopyModel(virCPUDefPtr dst, if ((src->model && !(dst->model = strdup(src->model))) || (src->vendor && !(dst->vendor = strdup(src->vendor))) + || (src->vendor_id && !(dst->vendor_id = strdup(src->vendor_id))) || VIR_ALLOC_N(dst->features, src->nfeatures) < 0) goto no_memory; dst->nfeatures_max = dst->nfeatures = src->nfeatures; @@ -288,18 +290,46 @@ virCPUDefParseXML(const xmlNodePtr node, } if (def->type == VIR_CPU_TYPE_GUEST && - def->mode != VIR_CPU_MODE_HOST_PASSTHROUGH && - virXPathBoolean("boolean(./model[1]/@fallback)", ctxt)) { - const char *fallback; - - fallback = virXPathString("string(./model[1]/@fallback)", ctxt); - if (fallback) { - def->fallback = virCPUFallbackTypeFromString(fallback); - VIR_FREE(fallback); - if (def->fallback < 0) { - virCPUReportError(VIR_ERR_XML_ERROR, "%s", - _("Invalid fallback attribute")); - goto error; + def->mode != VIR_CPU_MODE_HOST_PASSTHROUGH) { + + if (virXPathBoolean("boolean(./model[1]/@fallback)", ctxt)) { + const char *fallback; + + fallback = virXPathString("string(./model[1]/@fallback)", ctxt); + if (fallback) { + def->fallback = virCPUFallbackTypeFromString(fallback); + VIR_FREE(fallback); + if (def->fallback < 0) { + virCPUReportError(VIR_ERR_XML_ERROR, "%s", + _("Invalid fallback attribute")); + goto error; + } + } + + if (virXPathBoolean("boolean(./model[1]/@vendor_id)", ctxt)) { + char *vendor_id; + + vendor_id = virXPathString("string(./model[1]/@vendor_id)", + ctxt); + if (!vendor_id || + strlen(vendor_id) != VIR_CPU_VENDOR_ID_LENGTH) { + virCPUReportError(VIR_ERR_XML_ERROR, + _("vendor_id must be exactly" + " %d characters long"), + VIR_CPU_VENDOR_ID_LENGTH); + VIR_FREE(vendor_id); + goto error; + } + /* ensure that the string can be passed to qemu*/ + for (i = 0; i < strlen(vendor_id); i++) { + if (vendor_id[i]==',') { + virCPUReportError(VIR_ERR_XML_ERROR, "%s", + _("vendor id is invalid")); + VIR_FREE(vendor_id); + goto error; + } + } + def->vendor_id = vendor_id; } } } @@ -588,6 +618,8 @@ virCPUDefFormatBuf(virBufferPtr buf, return -1; } virBufferAsprintf(buf, " fallback='%s'", fallback); + if (def->vendor_id) + virBufferAsprintf(buf, " vendor_id='%s'", def->vendor_id); } if (formatModel && def->model) { virBufferAsprintf(buf, ">%s\n", def->model); @@ -738,6 +770,13 @@ virCPUDefIsEqual(virCPUDefPtr src, goto cleanup; } + if (STRNEQ_NULLABLE(src->vendor_id, dst->vendor_id)) { + virCPUReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Target CPU model %s does not match source %s"), + NULLSTR(dst->vendor_id), NULLSTR(src->vendor_id)); + goto cleanup; + } + if (src->sockets != dst->sockets) { virCPUReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Target CPU sockets %d does not match source %d"), diff --git a/src/conf/cpu_conf.h b/src/conf/cpu_conf.h index f8b7bf95631e713ecbeee16bbacf8965834663d0..2df0a506bf21d010b027e578f1cec361bd98c1a3 100644 --- a/src/conf/cpu_conf.h +++ b/src/conf/cpu_conf.h @@ -28,6 +28,8 @@ # include "buf.h" # include "xml.h" +# define VIR_CPU_VENDOR_ID_LENGTH 12 + enum virCPUType { VIR_CPU_TYPE_HOST, VIR_CPU_TYPE_GUEST, @@ -103,6 +105,7 @@ struct _virCPUDef { int match; /* enum virCPUMatch */ char *arch; char *model; + char *vendor_id; /* vendor id returned by CPUID in the guest */ int fallback; /* enum virCPUFallback */ char *vendor; unsigned int sockets; diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index 6549f577019225d9ff8fa1629c24f00306a3169b..528b18935247b07b28e02a09d83d867ae71c088e 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -3913,7 +3913,9 @@ qemuBuildCpuArgStr(const struct qemud_driver *driver, } virBufferAddLit(&buf, "host"); } else { - if (VIR_ALLOC(guest) < 0 || !(guest->arch = strdup(host->arch))) + if (VIR_ALLOC(guest) < 0 || + !(guest->arch = strdup(host->arch)) || + (cpu->vendor_id && !(guest->vendor_id = strdup(cpu->vendor_id)))) goto no_memory; if (cpu->match == VIR_CPU_MATCH_MINIMUM) @@ -3927,6 +3929,8 @@ qemuBuildCpuArgStr(const struct qemud_driver *driver, goto cleanup; virBufferAdd(&buf, guest->model, -1); + if (guest->vendor_id) + virBufferAsprintf(&buf, ",vendor=%s", guest->vendor_id); for (i = 0; i < guest->nfeatures; i++) { char sign; if (guest->features[i].policy == VIR_CPU_FEATURE_DISABLE) diff --git a/tests/testutilsqemu.c b/tests/testutilsqemu.c index 8d5a3bff3817a105a9df8c7f34554fa00cb83abd..8b7cb3300bd8be2c2c7e5d570ffb435e3d391fa5 100644 --- a/tests/testutilsqemu.c +++ b/tests/testutilsqemu.c @@ -116,6 +116,7 @@ virCapsPtr testQemuCapsInit(void) { 0, /* match */ (char *) "x86_64", /* arch */ (char *) "core2duo", /* model */ + NULL, /* vendor_id */ 0, /* fallback */ (char *) "Intel", /* vendor */ 1, /* sockets */