Implement CPU decoding for LoongArch. The CPUCFG registers are read from the host and matched against the known CPU models to produce a virCPUDef. The decoding logic filters the host features based on the selected CPU model: features supported by both are enabled, while features required by the model but missing on the host are explicitly disabled. Signed-off-by: Xianglai Li <lixianglai@loongson.cn> --- Cc: Bibo Mao <maobibo@loongson.cn> src/cpu/cpu_loongarch.c | 189 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 187 insertions(+), 2 deletions(-) diff --git a/src/cpu/cpu_loongarch.c b/src/cpu/cpu_loongarch.c index 3cce682349..89ba1a0512 100644 --- a/src/cpu/cpu_loongarch.c +++ b/src/cpu/cpu_loongarch.c @@ -28,6 +28,12 @@ VIR_LOG_INIT("cpu.cpu_loongarch"); +#define MAX_CPU_FLAGS 32 +#define BIT(nr) ((uint32_t)(1U) << (nr)) + +#define PRID_SERIES_MASK 0xf000 +#define LOONGARCH_CPUCFG0 0x0 + typedef struct _virCPULoongArchVendor virCPULoongArchVendor; struct _virCPULoongArchVendor { char *name; @@ -158,6 +164,22 @@ virCPULoongArchMapFree(virCPULoongArchMap *map) } G_DEFINE_AUTOPTR_CLEANUP_FUNC(virCPULoongArchMap, virCPULoongArchMapFree); +static virCPULoongArchFeature * +virCPULoongArchFeatureFindByIndex(virCPULoongArchData *data, + virCPULoongArchFeature *find) +{ + size_t i; + virCPULoongArchFeature *feature; + + for (i = 0; i < data->features->len; i++) { + feature = g_ptr_array_index(data->features, i); + if ((feature->index == find->index) && (feature->value == find->value)) + return feature; + } + + return NULL; +} + static virCPULoongArchFeature * virCPULoongArchMapFeatureFindByName(virCPULoongArchMap *map, const char *name) @@ -188,6 +210,26 @@ virCPULoongArchModelFindByName(virCPULoongArchMap *map, return NULL; } +static virCPULoongArchModel* +virCPULoongArchModelFindBySeries(const virCPULoongArchMap *map, + uint32_t series) +{ + size_t i, j; + + for (i = 0; i < map->nmodels; i++) { + virCPULoongArchModel *model = map->models[i]; + for (j = 0; j < model->data.features->len; j++) { + virCPULoongArchFeature *feature = + g_ptr_array_index(model->data.features, j); + if ((feature->index == LOONGARCH_CPUCFG0) && + (series == (feature->value & PRID_SERIES_MASK))) + return model; + } + } + + return NULL; +} + static virCPULoongArchVendor * virCPULoongArchVendorFindByName(virCPULoongArchMap *map, const char *name) @@ -415,16 +457,159 @@ virCPULoongArchDriverOnceInit(void) return 0; } +static void +virCPULoongArchCpuDataFromCpuCfg(virCPULoongArchModel *cpumodel, + virCPULoongArchData *data) +{ + virCPULoongArchFeature *feature = NULL; + g_autoptr(virCPULoongArchFeature) newfeature = NULL; + virCPULoongArchFeature *cpucfg; + virCPULoongArchFeature find; + virCPULoongArchMap *map = NULL; + ssize_t i, j; + + if (!data->features || !(map = virCPULoongArchGetMap())) + return; + + /* + * Only features supported by both cpumodel and data are enabled; + * all other features are not processed. + */ + for (i = (data->features->len -1); i >= 0; i--) { + cpucfg = g_ptr_array_index(data->features, i); + if (cpucfg->index == LOONGARCH_CPUCFG0) + continue; + for (j = 0; j < MAX_CPU_FLAGS; j++) { + find.index = cpucfg->index; + find.value = BIT(j); + feature = virCPULoongArchFeatureFindByIndex( + &cpumodel->data, + &find); + if (feature) { + newfeature = virCPULoongArchFeatureNew(); + newfeature->name = g_strdup(feature->name); + newfeature->index = feature->index; + newfeature->value = feature->value; + newfeature->policy = VIR_CPU_FEATURE_DISABLE; + if (cpucfg->value & BIT(j)) { + newfeature->policy = VIR_CPU_FEATURE_REQUIRE; + } + g_ptr_array_add(data->features, + g_steal_pointer(&newfeature)); + } else { + feature = virCPULoongArchFeatureFindByIndex( + &map->data, + &find); + if (feature) { + newfeature = virCPULoongArchFeatureNew(); + newfeature->name = g_strdup(feature->name); + newfeature->index = feature->index; + newfeature->value = feature->value; + newfeature->policy = VIR_CPU_FEATURE_DISABLE; + g_ptr_array_add(data->features, + g_steal_pointer(&newfeature)); + } + } + } + } + + for (i = (data->features->len -1); i >= 0; i--) { + cpucfg = g_ptr_array_index(data->features, i); + if ((cpucfg->name == NULL) || (cpucfg->index == LOONGARCH_CPUCFG0)) + g_ptr_array_remove(data->features, cpucfg); + } + + /* + * In the cpu data and CPU model XML, features that are not required are set + * to disabled by default. + */ + for (i = (map->data.features->len -1); i >= 0; i--) { + cpucfg = g_ptr_array_index(map->data.features, i); + if (cpucfg->index == LOONGARCH_CPUCFG0) + continue; + + find.index = cpucfg->index; + find.value = cpucfg->value; + feature = virCPULoongArchFeatureFindByIndex(data, &find); + if (!feature) { + newfeature = virCPULoongArchFeatureNew(); + newfeature->name = g_strdup(cpucfg->name); + newfeature->index = cpucfg->index; + newfeature->value = cpucfg->value; + newfeature->policy = VIR_CPU_FEATURE_DISABLE; + g_ptr_array_add(data->features, + g_steal_pointer(&newfeature)); + } + } + + return; +} + +static uint32_t +virCPULoongArchCpuDataGetSeries(const virCPULoongArchData *data) +{ + virCPULoongArchFeature *feature = NULL; + size_t i; + + for (i = 0; i < data->features->len; i++) { + feature = g_ptr_array_index(data->features, i); + if (feature->index == LOONGARCH_CPUCFG0) + return feature->value & PRID_SERIES_MASK; + } + + return 0; +} + static int -virCPULoongArchDriverDecode(virCPUDef *cpu G_GNUC_UNUSED, +virCPULoongArchDriverDecode(virCPUDef *cpu, const virCPUData *cpuData, - virDomainCapsCPUModels *models G_GNUC_UNUSED) + virDomainCapsCPUModels *models) { virCPULoongArchMap *map = NULL; + virCPULoongArchModel *model; + virCPULoongArchData *data; + uint32_t series; + size_t i; if (!cpuData || !(map = virCPULoongArchGetMap())) return -1; + data = (virCPULoongArchData *)&cpuData->data.loongarch; + series = virCPULoongArchCpuDataGetSeries(data); + if (!series) + return -1; + + if (!(model = virCPULoongArchModelFindBySeries(map, series))) { + virReportError(VIR_ERR_OPERATION_FAILED, + _("Cannot find CPU model with cpucfg series 0x%1$08x"), + series); + return -1; + } + + if (!virCPUModelIsAllowed(model->name, models)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("CPU model %1$s is not supported by hypervisor"), + model->name); + return -1; + } + + virCPULoongArchCpuDataFromCpuCfg(model, data); + cpu->model = g_strdup(model->name); + if (model->vendor) + cpu->vendor = g_strdup(model->vendor->name); + + if (data->features) { + cpu->nfeatures = data->features->len; + cpu->features = g_new0(virCPUFeatureDef, cpu->nfeatures); + + for (i = 0; i < cpu->nfeatures; i++) { + virCPULoongArchFeature *feature = + g_ptr_array_index(data->features, i); + cpu->features[i].policy = feature->policy; + cpu->features[i].name = g_strdup(feature->name); + } + } + return 0; } -- 2.39.1