[PATCH 0/6] add LoongArch host model support
Hi all, This patch series adds host model support for LoongArch. The current approach is to read cpucfg information from the physical machine and use it together with the CPU model information and feature information in the XML file to jointly determine which CPU features need to be enabled for the host model. The specific policy is as follows: features supported by both the physical machine and the CPU model in the XML file are enabled; otherwise, they are disabled. Features that exist in feature.xml but are not mentioned by either the physical machine's cpucfg or the CPU model in the XML file are also set to disabled. Cc: Bibo Mao <maobibo@loongson.cn> Xianglai Li (6): cpu_map: add LoongArch CPU map XML files cpu: add LoongArch CPU vendor parsing cpu: add LoongArch CPU feature parsing cpu: add LoongArch CPU model parsing cpu: add LoongArch CPU decode support cpu: add LoongArch host model support po/POTFILES | 1 + src/cpu/cpu.h | 2 + src/cpu/cpu_loongarch.c | 645 ++++++++++++++++++++++++++++- src/cpu/cpu_loongarch_data.h | 34 ++ src/cpu_map/index.xml | 13 + src/cpu_map/loongarch_features.xml | 26 ++ src/cpu_map/loongarch_la464.xml | 10 + src/cpu_map/loongarch_la664.xml | 11 + src/cpu_map/loongarch_vendors.xml | 3 + src/cpu_map/meson.build | 4 + src/qemu/qemu_capabilities.c | 2 +- 11 files changed, 745 insertions(+), 6 deletions(-) create mode 100644 src/cpu/cpu_loongarch_data.h create mode 100644 src/cpu_map/loongarch_features.xml create mode 100644 src/cpu_map/loongarch_la464.xml create mode 100644 src/cpu_map/loongarch_la664.xml create mode 100644 src/cpu_map/loongarch_vendors.xml -- 2.39.1
Add CPU map XML files for the LoongArch architecture, describing the Loongson vendor, supported features, and the la464/la664 CPU models. The following files are added: - loongarch_vendors.xml: Loongson vendor (0x14) - loongarch_features.xml: lbt, lasx, lsx, ptw, pmu, msgint, kvm-pv-ipi, kvm-steal-time - loongarch_la464.xml: Loongson 3A5000 (la464) model - loongarch_la664.xml: Loongson 3A6000 (la664) model index.xml and meson.build are updated to include the new files. Signed-off-by: Xianglai Li <lixianglai@loongson.cn> --- Cc: Bibo Mao <maobibo@loongson.cn> src/cpu_map/index.xml | 13 +++++++++++++ src/cpu_map/loongarch_features.xml | 26 ++++++++++++++++++++++++++ src/cpu_map/loongarch_la464.xml | 10 ++++++++++ src/cpu_map/loongarch_la664.xml | 11 +++++++++++ src/cpu_map/loongarch_vendors.xml | 3 +++ src/cpu_map/meson.build | 4 ++++ 6 files changed, 67 insertions(+) create mode 100644 src/cpu_map/loongarch_features.xml create mode 100644 src/cpu_map/loongarch_la464.xml create mode 100644 src/cpu_map/loongarch_la664.xml create mode 100644 src/cpu_map/loongarch_vendors.xml diff --git a/src/cpu_map/index.xml b/src/cpu_map/index.xml index 50b5cc7ed9..e4de295203 100644 --- a/src/cpu_map/index.xml +++ b/src/cpu_map/index.xml @@ -259,4 +259,17 @@ <include filename='arm_Ampere-1a.xml'/> </group> </arch> + + <arch name='loongarch'> + <include filename='loongarch_vendors.xml'/> + <include filename='loongarch_features.xml'/> + + <group name='3A5000 model'> + <include filename='loongarch_la464.xml'/> + </group> + + <group name='3A6000 model'> + <include filename='loongarch_la664.xml'/> + </group> + </arch> </cpus> diff --git a/src/cpu_map/loongarch_features.xml b/src/cpu_map/loongarch_features.xml new file mode 100644 index 0000000000..b86721d27d --- /dev/null +++ b/src/cpu_map/loongarch_features.xml @@ -0,0 +1,26 @@ +<cpus> + <feature name='lbt'> + <cpucfg index='0xf0000000' value='0x00000008'/> + </feature> + <feature name='lasx'> + <cpucfg index='0x00000002' value='0x00000080'/> + </feature> + <feature name='lsx'> + <cpucfg index='0x00000002' value='0x00000040'/> + </feature> + <feature name='ptw'> + <cpucfg index='0x00000002' value='0x01000000'/> + </feature> + <feature name='pmu'> + <cpucfg index='0x00000006' value='0x00000001'/> + </feature> + <feature name='msgint'> + <cpucfg index='0x00000001' value='0x04000000'/> + </feature> + <feature name='kvm-pv-ipi'> + <cpucfg index='0xf0000000' value='0x00000001'/> + </feature> + <feature name='kvm-steal-time'> + <cpucfg index='0xf0000000' value='0x00000002'/> + </feature> +</cpus> diff --git a/src/cpu_map/loongarch_la464.xml b/src/cpu_map/loongarch_la464.xml new file mode 100644 index 0000000000..df75222d78 --- /dev/null +++ b/src/cpu_map/loongarch_la464.xml @@ -0,0 +1,10 @@ +<cpus> + <model name='la464'> + <vendor name='Loongson'/> + <feature name='series'> + <cpucfg index='0' value='0xc000'/> + </feature> + <feature name='lasx'/> + <feature name='lsx'/> + </model> +</cpus> diff --git a/src/cpu_map/loongarch_la664.xml b/src/cpu_map/loongarch_la664.xml new file mode 100644 index 0000000000..7cf8c8fb06 --- /dev/null +++ b/src/cpu_map/loongarch_la664.xml @@ -0,0 +1,11 @@ +<cpus> + <model name='la664'> + <vendor name='Loongson'/> + <feature name='series'> + <cpucfg index='0' value='0xd000'/> + </feature> + <feature name='ptw'/> + <feature name='lasx'/> + <feature name='lsx'/> + </model> +</cpus> diff --git a/src/cpu_map/loongarch_vendors.xml b/src/cpu_map/loongarch_vendors.xml new file mode 100644 index 0000000000..d40d2d7e51 --- /dev/null +++ b/src/cpu_map/loongarch_vendors.xml @@ -0,0 +1,3 @@ +<cpus> + <vendor name='Loongson' value='0x14'/> +</cpus> diff --git a/src/cpu_map/meson.build b/src/cpu_map/meson.build index fd5d2596ec..3a707c1ff1 100644 --- a/src/cpu_map/meson.build +++ b/src/cpu_map/meson.build @@ -17,6 +17,10 @@ cpumap_data = [ 'arm_ThunderX299xx.xml', 'arm_vendors.xml', 'index.xml', + 'loongarch_features.xml', + 'loongarch_la464.xml', + 'loongarch_la664.xml', + 'loongarch_vendors.xml', 'ppc64_POWER10.xml', 'ppc64_POWER11.xml', 'ppc64_POWER6.xml', -- 2.39.1
Add parsing of LoongArch CPU vendors from the CPU map XML files. This introduces the virCPULoongArchData structure, the CPU map container, vendor lookup helpers. Add memory management for the CPU map and vendor objects, register src/cpu/cpu_loongarch.c in po/POTFILES for translation, and provide a stub decode callback so the driver can be registered. Signed-off-by: Xianglai Li <lixianglai@loongson.cn> --- Cc: Bibo Mao <maobibo@loongson.cn> po/POTFILES | 1 + src/cpu/cpu.h | 2 + src/cpu/cpu_loongarch.c | 163 ++++++++++++++++++++++++++++++++++- src/cpu/cpu_loongarch_data.h | 34 ++++++++ 4 files changed, 199 insertions(+), 1 deletion(-) create mode 100644 src/cpu/cpu_loongarch_data.h diff --git a/po/POTFILES b/po/POTFILES index 3812d6af88..504d5c28bd 100644 --- a/po/POTFILES +++ b/po/POTFILES @@ -74,6 +74,7 @@ src/conf/virsecretobj.c src/conf/virstorageobj.c src/cpu/cpu.c src/cpu/cpu_arm.c +src/cpu/cpu_loongarch.c src/cpu/cpu_map.c src/cpu/cpu_ppc64.c src/cpu/cpu_riscv64.c diff --git a/src/cpu/cpu.h b/src/cpu/cpu.h index 65711ac085..08b49ccefa 100644 --- a/src/cpu/cpu.h +++ b/src/cpu/cpu.h @@ -27,6 +27,7 @@ #include "cpu_x86_data.h" #include "cpu_ppc64_data.h" #include "cpu_arm_data.h" +#include "cpu_loongarch_data.h" typedef struct _virCPUData virCPUData; @@ -36,6 +37,7 @@ struct _virCPUData { virCPUx86Data x86; virCPUppc64Data ppc64; virCPUarmData arm; + virCPULoongArchData loongarch; /* generic driver needs no data */ } data; }; diff --git a/src/cpu/cpu_loongarch.c b/src/cpu/cpu_loongarch.c index 0e2b0b5848..d1ed447881 100644 --- a/src/cpu/cpu_loongarch.c +++ b/src/cpu/cpu_loongarch.c @@ -21,12 +21,30 @@ #include <config.h> #include "virlog.h" #include "cpu.h" +#include "viralloc.h" +#include "cpu_map.h" #define VIR_FROM_THIS VIR_FROM_CPU VIR_LOG_INIT("cpu.cpu_loongarch"); +typedef struct _virCPULoongArchVendor virCPULoongArchVendor; +struct _virCPULoongArchVendor { + char *name; + uint32_t value; +}; + +typedef struct _virCPULoongArchMap virCPULoongArchMap; +struct _virCPULoongArchMap { + size_t nvendors; + virCPULoongArchVendor **vendors; +}; + static const virArch archs[] = { VIR_ARCH_LOONGARCH64 }; +static virCPULoongArchMap *cpuMap; + +int virCPULoongArchDriverOnceInit(void); +VIR_ONCE_GLOBAL_INIT(virCPULoongArchDriver); static virCPUCompareResult virCPULoongArchCompare(virCPUDef *host G_GNUC_UNUSED, @@ -36,6 +54,149 @@ virCPULoongArchCompare(virCPUDef *host G_GNUC_UNUSED, return VIR_CPU_COMPARE_IDENTICAL; } +static void +virCPULoongArchVendorFree(virCPULoongArchVendor *vendor) +{ + if (!vendor) + return; + + g_free(vendor->name); + g_free(vendor); +} + +G_DEFINE_AUTOPTR_CLEANUP_FUNC(virCPULoongArchVendor, virCPULoongArchVendorFree); + +static virCPULoongArchMap * +virCPULoongArchMapNew(void) +{ + virCPULoongArchMap *map; + + map = g_new0(virCPULoongArchMap, 1); + + return map; +} + +static void +virCPULoongArchMapFree(virCPULoongArchMap *map) +{ + size_t i; + + if (!map) + return; + + for (i = 0; i < map->nvendors; i++) + virCPULoongArchVendorFree(map->vendors[i]); + g_free(map->vendors); + g_free(map); +} +G_DEFINE_AUTOPTR_CLEANUP_FUNC(virCPULoongArchMap, virCPULoongArchMapFree); + +static virCPULoongArchVendor * +virCPULoongArchVendorFindByName(virCPULoongArchMap *map, + const char *name) +{ + size_t i; + + for (i = 0; i < map->nvendors; i++) { + if (STREQ(map->vendors[i]->name, name)) + return map->vendors[i]; + } + + return NULL; +} + +static virCPULoongArchVendor * +virCPULoongArchVendorFindByID(virCPULoongArchMap *map, + uint32_t vendor_id) +{ + size_t i; + + for (i = 0; i < map->nvendors; i++) { + if (map->vendors[i]->value == vendor_id) + return map->vendors[i]; + } + + return NULL; +} + +static int +virCPULoongArchMapVendorParse(xmlXPathContextPtr ctxt, + const char *name, + void *data) +{ + virCPULoongArchMap *map = data; + g_autoptr(virCPULoongArchVendor) vendor = NULL; + + vendor = g_new0(virCPULoongArchVendor, 1); + vendor->name = g_strdup(name); + + if (virCPULoongArchVendorFindByName(map, vendor->name)) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("CPU vendor %1$s already defined"), + vendor->name); + return -1; + } + + if (virXMLPropUInt(ctxt->node, "value", 16, VIR_XML_PROP_REQUIRED, + &vendor->value) < 0) + return -1; + + if (virCPULoongArchVendorFindByID(map, vendor->value)) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("CPU vendor value 0x%1$2x already defined"), + vendor->value); + return -1; + } + + VIR_APPEND_ELEMENT(map->vendors, map->nvendors, vendor); + return 0; +} + +static virCPULoongArchMap * +virCPULoongArchLoadMap(void) +{ + g_autoptr(virCPULoongArchMap) map = NULL; + + map = virCPULoongArchMapNew(); + + if (cpuMapLoad("loongarch", virCPULoongArchMapVendorParse, + NULL, NULL, map) < 0) + return NULL; + + return g_steal_pointer(&map); +} + +static virCPULoongArchMap * +virCPULoongArchGetMap(void) +{ + if (virCPULoongArchDriverInitialize() < 0) + return NULL; + + return cpuMap; +} + +int +virCPULoongArchDriverOnceInit(void) +{ + if (!(cpuMap = virCPULoongArchLoadMap())) + return -1; + + return 0; +} + +static int +virCPULoongArchDriverDecode(virCPUDef *cpu G_GNUC_UNUSED, + const virCPUData *cpuData, + virDomainCapsCPUModels *models G_GNUC_UNUSED) +{ + virCPULoongArchMap *map = NULL; + + if (!cpuData || !(map = virCPULoongArchGetMap())) + return -1; + + return 0; +} + static int virCPULoongArchUpdate(virCPUDef *guest G_GNUC_UNUSED, const virCPUDef *host G_GNUC_UNUSED, @@ -50,7 +211,7 @@ struct cpuArchDriver cpuDriverLoongArch = { .arch = archs, .narch = G_N_ELEMENTS(archs), .compare = virCPULoongArchCompare, - .decode = NULL, + .decode = virCPULoongArchDriverDecode, .encode = NULL, .dataFree = NULL, .baseline = NULL, diff --git a/src/cpu/cpu_loongarch_data.h b/src/cpu/cpu_loongarch_data.h new file mode 100644 index 0000000000..673a4b37e6 --- /dev/null +++ b/src/cpu/cpu_loongarch_data.h @@ -0,0 +1,34 @@ +/* + * cpu_loongarch_data.h: 64-bit LoongArch CPU specific data + * + * Copyright (C) 2026 LOONGSON Corporation. + * + * 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, see + * <http://www.gnu.org/licenses/>. + */ + +#pragma once + +typedef struct _virCPULoongArchFeature virCPULoongArchFeature; +struct _virCPULoongArchFeature { + char *name; + uint32_t index; + uint32_t value; + int policy; /* enum virCPUFeaturePolicy */ +}; + +typedef struct _virCPULoongArchData virCPULoongArchData; +struct _virCPULoongArchData { + GPtrArray *features; +}; -- 2.39.1
Add parsing of LoongArch CPU features from the CPU map XML files. Each feature is described by a CPUCFG register index and a value. Feature lookup by name is also added. The feature parsing callback is now passed to cpuMapLoad(). Signed-off-by: Xianglai Li <lixianglai@loongson.cn> --- Cc: Bibo Mao <maobibo@loongson.cn> src/cpu/cpu_loongarch.c | 92 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 90 insertions(+), 2 deletions(-) diff --git a/src/cpu/cpu_loongarch.c b/src/cpu/cpu_loongarch.c index d1ed447881..7d63630615 100644 --- a/src/cpu/cpu_loongarch.c +++ b/src/cpu/cpu_loongarch.c @@ -38,6 +38,7 @@ typedef struct _virCPULoongArchMap virCPULoongArchMap; struct _virCPULoongArchMap { size_t nvendors; virCPULoongArchVendor **vendors; + virCPULoongArchData data; }; static const virArch archs[] = { VIR_ARCH_LOONGARCH64 }; @@ -54,6 +55,25 @@ virCPULoongArchCompare(virCPUDef *host G_GNUC_UNUSED, return VIR_CPU_COMPARE_IDENTICAL; } +static virCPULoongArchFeature * +virCPULoongArchFeatureNew(void) +{ + return g_new0(virCPULoongArchFeature, 1); +} + +static void +virCPULoongArchFeatureFree(virCPULoongArchFeature *feature) +{ + if (!feature) + return; + + g_free(feature->name); + g_free(feature); +} + +G_DEFINE_AUTOPTR_CLEANUP_FUNC(virCPULoongArchFeature, + virCPULoongArchFeatureFree); + static void virCPULoongArchVendorFree(virCPULoongArchVendor *vendor) { @@ -72,7 +92,9 @@ virCPULoongArchMapNew(void) virCPULoongArchMap *map; map = g_new0(virCPULoongArchMap, 1); - + map->data.features = g_ptr_array_new(); + g_ptr_array_set_free_func(map->data.features, + (GDestroyNotify) virCPULoongArchFeatureFree); return map; } @@ -87,10 +109,28 @@ virCPULoongArchMapFree(virCPULoongArchMap *map) for (i = 0; i < map->nvendors; i++) virCPULoongArchVendorFree(map->vendors[i]); g_free(map->vendors); + g_ptr_array_free(map->data.features, TRUE); g_free(map); } G_DEFINE_AUTOPTR_CLEANUP_FUNC(virCPULoongArchMap, virCPULoongArchMapFree); +static virCPULoongArchFeature * +virCPULoongArchMapFeatureFindByName(virCPULoongArchMap *map, + const char *name) +{ + size_t i; + virCPULoongArchFeature *feature; + + for (i = 0; i < map->data.features->len; i++) { + feature = g_ptr_array_index(map->data.features, i); + if (STREQ(feature->name, name)) + return feature; + } + + return NULL; +} + + static virCPULoongArchVendor * virCPULoongArchVendorFindByName(virCPULoongArchMap *map, const char *name) @@ -119,6 +159,53 @@ virCPULoongArchVendorFindByID(virCPULoongArchMap *map, return NULL; } +static int +virCPULoongArchMapCpucfgParse(xmlNodePtr node, + virCPULoongArchFeature *feature) +{ + if (virXMLPropUInt(node, "index", 16, VIR_XML_PROP_REQUIRED, + &feature->index) < 0) + return -1; + if (virXMLPropUInt(node, "value", 16, VIR_XML_PROP_REQUIRED, + &feature->value) < 0) + return -1; + + return 0; +} + +static int +virCPULoongArchMapFeatureParse(xmlXPathContextPtr ctxt, + const char *name, + void *data) +{ + g_autoptr(virCPULoongArchFeature) feature = NULL; + virCPULoongArchMap *map = data; + xmlNodePtr node; + + if (virCPULoongArchMapFeatureFindByName(map, name)) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("CPU feature %1$s already defined"), name); + return -1; + } + + feature = virCPULoongArchFeatureNew(); + feature->name = g_strdup(name); + + node = xmlFirstElementChild(ctxt->node); + if (node) { + if (virXMLNodeNameEqual(node, "cpucfg")) { + if (virCPULoongArchMapCpucfgParse(node, feature) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Invalid cpucfg")); + return -1; + } + } + } + + g_ptr_array_add(map->data.features, g_steal_pointer(&feature)); + return 0; +} + static int virCPULoongArchMapVendorParse(xmlXPathContextPtr ctxt, const char *name, @@ -160,7 +247,8 @@ virCPULoongArchLoadMap(void) map = virCPULoongArchMapNew(); if (cpuMapLoad("loongarch", virCPULoongArchMapVendorParse, - NULL, NULL, map) < 0) + virCPULoongArchMapFeatureParse, + NULL, map) < 0) return NULL; return g_steal_pointer(&map); -- 2.39.1
Add parsing of LoongArch CPU models from the CPU map XML files. A model references a vendor and a list of features. Each feature may either reference a predefined feature or specify its own CPUCFG index and value. Model lookup by name and the model free function are also added. The model parsing callback is now passed to cpuMapLoad(). Signed-off-by: Xianglai Li <lixianglai@loongson.cn> --- Cc: Bibo Mao <maobibo@loongson.cn> src/cpu/cpu_loongarch.c | 147 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 145 insertions(+), 2 deletions(-) diff --git a/src/cpu/cpu_loongarch.c b/src/cpu/cpu_loongarch.c index 7d63630615..3cce682349 100644 --- a/src/cpu/cpu_loongarch.c +++ b/src/cpu/cpu_loongarch.c @@ -34,10 +34,19 @@ struct _virCPULoongArchVendor { uint32_t value; }; +typedef struct _virCPULoongArchModel virCPULoongArchModel; +struct _virCPULoongArchModel { + char *name; + virCPULoongArchVendor *vendor; + virCPULoongArchData data; +}; + typedef struct _virCPULoongArchMap virCPULoongArchMap; struct _virCPULoongArchMap { size_t nvendors; virCPULoongArchVendor **vendors; + size_t nmodels; + virCPULoongArchModel **models; virCPULoongArchData data; }; @@ -55,6 +64,25 @@ virCPULoongArchCompare(virCPUDef *host G_GNUC_UNUSED, return VIR_CPU_COMPARE_IDENTICAL; } +static void +virCPULoongArchDataClear(virCPULoongArchData *data) +{ + if (!data) + return; + + g_clear_pointer(&data->features, g_ptr_array_unref); +} + +static void +virCPULoongArchDataFree(virCPUData *data) +{ + if (!data) + return; + + virCPULoongArchDataClear(&data->data.loongarch); + g_free(data); +} + static virCPULoongArchFeature * virCPULoongArchFeatureNew(void) { @@ -86,6 +114,18 @@ virCPULoongArchVendorFree(virCPULoongArchVendor *vendor) G_DEFINE_AUTOPTR_CLEANUP_FUNC(virCPULoongArchVendor, virCPULoongArchVendorFree); +static void +virCPULoongArchModelFree(virCPULoongArchModel *model) +{ + if (!model) + return; + + virCPULoongArchDataClear(&model->data); + g_free(model->name); + g_free(model); +} +G_DEFINE_AUTOPTR_CLEANUP_FUNC(virCPULoongArchModel, virCPULoongArchModelFree); + static virCPULoongArchMap * virCPULoongArchMapNew(void) { @@ -106,6 +146,10 @@ virCPULoongArchMapFree(virCPULoongArchMap *map) if (!map) return; + for (i = 0; i < map->nmodels; i++) + virCPULoongArchModelFree(map->models[i]); + g_free(map->models); + for (i = 0; i < map->nvendors; i++) virCPULoongArchVendorFree(map->vendors[i]); g_free(map->vendors); @@ -130,6 +174,19 @@ virCPULoongArchMapFeatureFindByName(virCPULoongArchMap *map, return NULL; } +static virCPULoongArchModel * +virCPULoongArchModelFindByName(virCPULoongArchMap *map, + const char *name) +{ + size_t i; + + for (i = 0; i < map->nmodels; i++) { + if (STREQ(map->models[i]->name, name)) + return map->models[i]; + } + + return NULL; +} static virCPULoongArchVendor * virCPULoongArchVendorFindByName(virCPULoongArchMap *map, @@ -206,6 +263,92 @@ virCPULoongArchMapFeatureParse(xmlXPathContextPtr ctxt, return 0; } +static int +virCPULoongArchMapModelParse(xmlXPathContextPtr ctxt, + const char *name, + void *data) +{ + virCPULoongArchMap *map = data; + g_autoptr(virCPULoongArchModel) model = NULL; + xmlNodePtr vendorNode = NULL; + g_autofree xmlNodePtr *nodes = NULL; + size_t i; + int n; + + model = g_new0(virCPULoongArchModel, 1); + model->name = g_strdup(name); + + if (virCPULoongArchModelFindByName(map, model->name)) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("CPU model %1$s already defined"), + model->name); + return -1; + } + + if ((vendorNode = virXPathNode("./vendor", ctxt))) { + g_autofree char *vendor = NULL; + + if (!(vendor = virXMLPropStringRequired(vendorNode, "name"))) + return -1; + + if (!(model->vendor = virCPULoongArchVendorFindByName(map, vendor))) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unknown vendor %1$s referenced by CPU model %2$s"), + vendor, model->name); + return -1; + } + } + + if ((n = virXPathNodeSet("./feature", ctxt, &nodes)) < 0) + return -1; + + if (n == 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Missing feature information for CPU model %1$s"), + model->name); + return -1; + } + + model->data.features = g_ptr_array_new(); + g_ptr_array_set_free_func(model->data.features, + (GDestroyNotify) virCPULoongArchFeatureFree); + + for (i = 0; i < n; i++) { + g_autoptr(virCPULoongArchFeature) feature = NULL; + virCPULoongArchFeature *found; + g_autofree char *ftname = NULL; + xmlNodePtr node = nodes[i]; + xmlNodePtr child; + + if (!(ftname = virXMLPropString(node, "name"))) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Missing feature name for CPU model %1$s"), + model->name); + return -1; + } + + feature = virCPULoongArchFeatureNew(); + feature->name = g_strdup(ftname); + + if ((found = virCPULoongArchMapFeatureFindByName(map, ftname))) { + feature->index = found->index; + feature->value = found->value; + } else if ((child = xmlFirstElementChild(node))) { + virCPULoongArchFeature cpucfg; + if (virXMLNodeNameEqual(child, "cpucfg")) { + if (virCPULoongArchMapCpucfgParse(child, &cpucfg) >= 0) { + feature->index = cpucfg.index; + feature->value = cpucfg.value; + } + } + } + g_ptr_array_add(model->data.features, g_steal_pointer(&feature)); + } + + VIR_APPEND_ELEMENT(map->models, map->nmodels, model); + return 0; +} + static int virCPULoongArchMapVendorParse(xmlXPathContextPtr ctxt, const char *name, @@ -248,7 +391,7 @@ virCPULoongArchLoadMap(void) if (cpuMapLoad("loongarch", virCPULoongArchMapVendorParse, virCPULoongArchMapFeatureParse, - NULL, map) < 0) + virCPULoongArchMapModelParse, map) < 0) return NULL; return g_steal_pointer(&map); @@ -301,7 +444,7 @@ struct cpuArchDriver cpuDriverLoongArch = { .compare = virCPULoongArchCompare, .decode = virCPULoongArchDriverDecode, .encode = NULL, - .dataFree = NULL, + .dataFree = virCPULoongArchDataFree, .baseline = NULL, .update = virCPULoongArchUpdate, .getModels = NULL, -- 2.39.1
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
Add host model support for LoongArch, allowing libvirt to detect the host CPU and expand a host-model CPU definition into a custom model. The following driver callbacks are implemented: - getHost: read CPUCFG registers from the host - update: expand host-model to a custom model - compare: compare guest and host CPU features The QEMU capabilities code is adjusted so that LoongArch no longer falls back to the generic ARM/unknown path. Signed-off-by: Xianglai Li <lixianglai@loongson.cn> --- Cc: Bibo Mao <maobibo@loongson.cn> src/cpu/cpu_loongarch.c | 64 ++++++++++++++++++++++++++++++++++-- src/qemu/qemu_capabilities.c | 2 +- 2 files changed, 62 insertions(+), 4 deletions(-) diff --git a/src/cpu/cpu_loongarch.c b/src/cpu/cpu_loongarch.c index 89ba1a0512..8ce9d53cb0 100644 --- a/src/cpu/cpu_loongarch.c +++ b/src/cpu/cpu_loongarch.c @@ -33,6 +33,7 @@ VIR_LOG_INIT("cpu.cpu_loongarch"); #define PRID_SERIES_MASK 0xf000 #define LOONGARCH_CPUCFG0 0x0 +#define LOONGARCH_CPUCFGMAX 0x14 typedef struct _virCPULoongArchVendor virCPULoongArchVendor; struct _virCPULoongArchVendor { @@ -614,11 +615,67 @@ virCPULoongArchDriverDecode(virCPUDef *cpu, } static int -virCPULoongArchUpdate(virCPUDef *guest G_GNUC_UNUSED, - const virCPUDef *host G_GNUC_UNUSED, - bool relative G_GNUC_UNUSED, +virCPULoongArchGetHost(virCPUDef *cpu, + virDomainCapsCPUModels *models) +{ + g_autoptr(virCPUData) cpuData = NULL; + virCPULoongArchData *data; + uint32_t i, val; + + if (!(cpuData = virCPUDataNew(archs[0]))) + return -1; + + data = &cpuData->data.loongarch; + data->features = g_ptr_array_new(); + g_ptr_array_set_free_func(data->features, + (GDestroyNotify) virCPULoongArchFeatureFree); + + for (i = 0; i < (LOONGARCH_CPUCFGMAX +1); i++) { + g_autoptr(virCPULoongArchFeature) feature = NULL; + val = 0; + +#if defined(__loongarch64) + asm("cpucfg %0, %1" + : "=r" (val) + : "r" (i) + : + ); +#endif + + if (!val) + continue; + + feature = virCPULoongArchFeatureNew(); + feature->index = i; + feature->value = val; + g_ptr_array_add(data->features, g_steal_pointer(&feature)); + } + + return virCPULoongArchDriverDecode(cpu, cpuData, models); +} + +static int +virCPULoongArchUpdate(virCPUDef *guest, + const virCPUDef *host, + bool relative, virCPUFeaturePolicy removedPolicy G_GNUC_UNUSED) { + g_autoptr(virCPUDef) updated = virCPUDefCopyWithoutModel(guest); + + if (!relative || guest->mode != VIR_CPU_MODE_HOST_MODEL) + return 0; + + if (!host) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("unknown host CPU model")); + return -1; + } + + updated->mode = VIR_CPU_MODE_CUSTOM; + virCPUDefCopyModel(updated, host, true); + virCPUDefStealModel(guest, updated, false); + guest->mode = VIR_CPU_MODE_CUSTOM; + guest->match = VIR_CPU_MATCH_EXACT; return 0; } @@ -630,6 +687,7 @@ struct cpuArchDriver cpuDriverLoongArch = { .decode = virCPULoongArchDriverDecode, .encode = NULL, .dataFree = virCPULoongArchDataFree, + .getHost = virCPULoongArchGetHost, .baseline = NULL, .update = virCPULoongArchUpdate, .getModels = NULL, diff --git a/src/qemu/qemu_capabilities.c b/src/qemu/qemu_capabilities.c index bc2b0412dc..43abb00826 100644 --- a/src/qemu/qemu_capabilities.c +++ b/src/qemu/qemu_capabilities.c @@ -4104,7 +4104,7 @@ virQEMUCapsInitCPUModel(virQEMUCaps *qemuCaps, } else if (ARCH_IS_X86(qemuCaps->arch)) { ret = virQEMUCapsInitCPUModelX86(qemuCaps, type, modelInfo, cpu, migratable); - } else if (ARCH_IS_ARM(qemuCaps->arch) || ARCH_IS_LOONGARCH(qemuCaps->arch)) { + } else if (ARCH_IS_ARM(qemuCaps->arch)) { ret = 2; } -- 2.39.1
participants (1)
-
Xianglai Li