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 <[email protected]>
---
Cc: Bibo Mao <[email protected]>

 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

Reply via email to