diff --git a/src/cpu/cpu.c b/src/cpu/cpu.c index b88f9955f3..ed79650459 100644 --- a/src/cpu/cpu.c +++ b/src/cpu/cpu.c @@ -1076,3 +1076,53 @@ virCPUConvertLegacy(virArch arch, VIR_DEBUG("model=%s", NULLSTR(cpu->model)); return 0; } + + +static int +virCPUFeatureCompare(const void *p1, + const void *p2) +{ + const virCPUFeatureDef *f1 = p1; + const virCPUFeatureDef *f2 = p2; + + return strcmp(f1->name, f2->name); +} + + +/** + * virCPUExpandFeatures: + * + * @arch: CPU architecture + * @cpu: CPU definition to be expanded + * + * Add all features implicitly enabled by the CPU model to the list of + * features. The @cpu is expected to be either a host or a guest representation + * of a host CPU, i.e., only VIR_CPU_FEATURE_REQUIRE and + * VIR_CPU_FEATURE_DISABLE policies are supported. + * + * The updated list of features in the CPU definition is sorted. + * + * Return -1 on error, 0 on success. + */ +int +virCPUExpandFeatures(virArch arch, + virCPUDefPtr cpu) +{ + struct cpuArchDriver *driver; + + VIR_DEBUG("arch=%s, cpu=%p, model=%s, nfeatures=%zu", + virArchToString(arch), cpu, NULLSTR(cpu->model), cpu->nfeatures); + + if (!(driver = cpuGetSubDriver(arch))) + return -1; + + if (driver->expandFeatures && + driver->expandFeatures(cpu) < 0) + return -1; + + qsort(cpu->features, cpu->nfeatures, sizeof(*cpu->features), + virCPUFeatureCompare); + + VIR_DEBUG("nfeatures=%zu", cpu->nfeatures); + return 0; +} diff --git a/src/cpu/cpu.h b/src/cpu/cpu.h index c4adc02f55..905c309e6b 100644 --- a/src/cpu/cpu.h +++ b/src/cpu/cpu.h @@ -116,6 +116,9 @@ typedef int typedef int (*virCPUArchConvertLegacy)(virCPUDefPtr cpu); +typedef int +(*virCPUArchExpandFeatures)(virCPUDefPtr cpu); + struct cpuArchDriver { const char *name; const virArch *arch; @@ -135,6 +138,7 @@ struct cpuArchDriver { virCPUArchGetModels getModels; virCPUArchTranslate translate; virCPUArchConvertLegacy convertLegacy; + virCPUArchExpandFeatures expandFeatures; }; @@ -247,6 +251,10 @@ virCPUConvertLegacy(virArch arch, virCPUDefPtr cpu) ATTRIBUTE_NONNULL(2); +int +virCPUExpandFeatures(virArch arch, + virCPUDefPtr cpu); + /* virCPUDataFormat and virCPUDataParse are implemented for unit tests only and * have no real-life usage */ diff --git a/src/cpu/cpu_x86.c b/src/cpu/cpu_x86.c index 9e208b0948..442f0ce44b 100644 --- a/src/cpu/cpu_x86.c +++ b/src/cpu/cpu_x86.c @@ -2891,6 +2891,58 @@ virCPUx86Translate(virCPUDefPtr cpu, } +static int +virCPUx86ExpandFeatures(virCPUDefPtr cpu) +{ + virCPUx86MapPtr map; + virCPUDefPtr expanded = NULL; + virCPUx86ModelPtr model = NULL; + bool host = cpu->type == VIR_CPU_TYPE_HOST; + size_t i; + int ret = -1; + + if (!(map = virCPUx86GetMap())) + goto cleanup; + + if (!(expanded = virCPUDefCopy(cpu))) + goto cleanup; + + virCPUDefFreeFeatures(expanded); + + if (!(model = x86ModelFind(map, cpu->model))) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("unknown CPU model %s"), cpu->model); + goto cleanup; + } + + if (!(model = x86ModelCopy(model)) || + x86DataToCPUFeatures(expanded, host ? -1 : VIR_CPU_FEATURE_REQUIRE, + &model->data, map) < 0) + goto cleanup; + + for (i = 0; i < cpu->nfeatures; i++) { + virCPUFeatureDefPtr f = cpu->features + i; + + if (!host && + f->policy != VIR_CPU_FEATURE_REQUIRE && + f->policy != VIR_CPU_FEATURE_DISABLE) + continue; + + if (virCPUDefUpdateFeature(expanded, f->name, f->policy) < 0) + goto cleanup; + } + + virCPUDefFreeModel(cpu); + + ret = virCPUDefCopyModel(cpu, expanded, false); + + cleanup: + virCPUDefFree(expanded); + x86ModelFree(model); + return ret; +} + + int virCPUx86DataAddCPUID(virCPUDataPtr cpuData, const virCPUx86CPUID *cpuid) @@ -2965,4 +3017,5 @@ struct cpuArchDriver cpuDriverX86 = { .dataParse = virCPUx86DataParse, .getModels = virCPUx86GetModels, .translate = virCPUx86Translate, + .expandFeatures = virCPUx86ExpandFeatures, }; diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 1e160d80e9..d614b38a86 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1021,6 +1021,7 @@ virCPUDataFormat; virCPUDataFree; virCPUDataNew; virCPUDataParse; +virCPUExpandFeatures; virCPUGetHost; virCPUGetModels; virCPUProbeHost;