From: lixianglai <lixianglai@loongson.cn>
Add loongarch cpu support, Define new cpu type 'loongarch64'
and implement it's driver functions.
Signed-off-by: lixianglai <lixianglai@loongson.cn>
---
po/POTFILES | 1 +
src/conf/schemas/basictypes.rng | 1 +
src/cpu/cpu.c | 2 +
src/cpu/cpu.h | 2 +
src/cpu/cpu_loongarch.c | 742 ++++++++++++++++++++++++++++++++
src/cpu/cpu_loongarch.h | 25 ++
src/cpu/cpu_loongarch_data.h | 37 ++
src/cpu/meson.build | 1 +
src/qemu/qemu_capabilities.c | 1 +
src/qemu/qemu_domain.c | 4 +
src/util/virarch.c | 2 +
src/util/virarch.h | 4 +
12 files changed, 822 insertions(+)
create mode 100644 src/cpu/cpu_loongarch.c
create mode 100644 src/cpu/cpu_loongarch.h
create mode 100644 src/cpu/cpu_loongarch_data.h
diff --git a/po/POTFILES b/po/POTFILES
index 3a51aea5cb..c0e66d563e 100644
--- a/po/POTFILES
+++ b/po/POTFILES
@@ -70,6 +70,7 @@ src/cpu/cpu.c
src/cpu/cpu_arm.c
src/cpu/cpu_map.c
src/cpu/cpu_ppc64.c
+src/cpu/cpu_loongarch.c
src/cpu/cpu_riscv64.c
src/cpu/cpu_s390.c
src/cpu/cpu_x86.c
diff --git a/src/conf/schemas/basictypes.rng b/src/conf/schemas/basictypes.rng
index 26eb538077..04f032b3ab 100644
--- a/src/conf/schemas/basictypes.rng
+++ b/src/conf/schemas/basictypes.rng
@@ -470,6 +470,7 @@
<value>x86_64</value>
<value>xtensa</value>
<value>xtensaeb</value>
+ <value>loongarch64</value>
</choice>
</define>
diff --git a/src/cpu/cpu.c b/src/cpu/cpu.c
index bc43aa4e93..1e7c879ca5 100644
--- a/src/cpu/cpu.c
+++ b/src/cpu/cpu.c
@@ -27,6 +27,7 @@
#include "cpu_ppc64.h"
#include "cpu_s390.h"
#include "cpu_arm.h"
+#include "cpu_loongarch.h"
#include "cpu_riscv64.h"
#include "capabilities.h"
@@ -41,6 +42,7 @@ static struct cpuArchDriver *drivers[] = {
&cpuDriverS390,
&cpuDriverArm,
&cpuDriverRiscv64,
+ &cpuDriverLoongArch,
};
diff --git a/src/cpu/cpu.h b/src/cpu/cpu.h
index a4cdb37f03..9ec0a109b8 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
new file mode 100644
index 0000000000..0f96535606
--- /dev/null
+++ b/src/cpu/cpu_loongarch.c
@@ -0,0 +1,742 @@
+/*
+ * cpu_loongarch.c: CPU driver for 64-bit LOONGARCH CPUs
+ *
+ * Copyright (C) 2023 Loongson Technology.
+ *
+ * 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/>.
+ */
+
+#include <config.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include "virlog.h"
+#include "viralloc.h"
+#include "cpu.h"
+#include "virstring.h"
+#include "cpu_map.h"
+#include "virbuffer.h"
+
+#define VIR_FROM_THIS VIR_FROM_CPU
+
+VIR_LOG_INIT("cpu.cpu_loongarch");
+
+static const virArch archs[] = { VIR_ARCH_LOONGARCH64 };
+
+typedef struct _virCPULoongArchVendor virCPULoongArchVendor;
+struct _virCPULoongArchVendor {
+ char *name;
+};
+
+typedef struct _virCPULoongArchModel virCPULoongArchModel;
+struct _virCPULoongArchModel {
+ char *name;
+ const virCPULoongArchVendor *vendor;
+ virCPULoongArchData data;
+};
+
+typedef struct _virCPULoongArchMap virCPULoongArchMap;
+struct _virCPULoongArchMap {
+ size_t nvendors;
+ virCPULoongArchVendor **vendors;
+ size_t nmodels;
+ virCPULoongArchModel **models;
+};
+
+static void
+virCPULoongArchDataClear(virCPULoongArchData *data)
+{
+ if (!data)
+ return;
+
+ VIR_FREE(data->prid);
+}
+
+static int
+virCPULoongArchDataCopy(virCPULoongArchData *dst,
+ const virCPULoongArchData *src)
+{
+ size_t i;
+
+ dst->prid = g_new0(virCPULoongArchPrid, src->len);
+ if (!dst->prid)
+ return -1;
+
+ dst->len = src->len;
+
+ for (i = 0; i < src->len; i++) {
+ dst->prid[i].value = src->prid[i].value;
+ dst->prid[i].mask = src->prid[i].mask;
+ }
+
+ return 0;
+}
+
+static void
+virCPULoongArchVendorFree(virCPULoongArchVendor *vendor)
+{
+ if (!vendor)
+ return;
+
+ VIR_FREE(vendor->name);
+ VIR_FREE(vendor);
+}
+
+static virCPULoongArchVendor *
+virCPULoongArchVendorFind(const 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 void
+virCPULoongArchModelFree(virCPULoongArchModel *model)
+{
+ if (!model)
+ return;
+
+ virCPULoongArchDataClear(&model->data);
+ VIR_FREE(model->name);
+ VIR_FREE(model);
+}
+
+static virCPULoongArchModel *
+virCPULoongArchModelCopy(const virCPULoongArchModel *model)
+{
+ virCPULoongArchModel *copy;
+
+ copy = g_new0(virCPULoongArchModel, 1);
+ if (!copy)
+ goto cleanup;
+
+ copy->name = g_strdup(model->name);
+
+ if (virCPULoongArchDataCopy(©->data, &model->data) < 0)
+ goto cleanup;
+
+ copy->vendor = model->vendor;
+
+ return copy;
+
+ cleanup:
+ virCPULoongArchModelFree(copy);
+ return NULL;
+}
+
+static virCPULoongArchModel *
+virCPULoongArchModelFind(const 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 virCPULoongArchModel *
+virCPULoongArchModelFindPrid(const virCPULoongArchMap *map,
+ uint32_t prid)
+{
+ size_t i;
+ size_t j;
+
+ for (i = 0; i < map->nmodels; i++) {
+ virCPULoongArchModel *model = map->models[i];
+ for (j = 0; j < model->data.len; j++) {
+ if ((prid & model->data.prid[j].mask) == model->data.prid[j].value)
+ return model;
+ }
+ }
+
+ return NULL;
+}
+
+static virCPULoongArchModel *
+virCPULoongArchModelFromCPU(const virCPUDef *cpu,
+ const virCPULoongArchMap *map)
+{
+ virCPULoongArchModel *model;
+
+ if (!cpu->model) {
+ virReportError(VIR_ERR_INVALID_ARG, "%s",
+ _("no CPU model specified"));
+ return NULL;
+ }
+
+ if (!(model = virCPULoongArchModelFind(map, cpu->model))) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("Unknown CPU model %1$s"), cpu->model);
+ return NULL;
+ }
+
+ return virCPULoongArchModelCopy(model);
+}
+
+static void
+virCPULoongArchMapFree(virCPULoongArchMap *map)
+{
+ size_t i;
+
+ if (!map)
+ return;
+
+ for (i = 0; i < map->nmodels; i++)
+ virCPULoongArchModelFree(map->models[i]);
+ VIR_FREE(map->models);
+
+ for (i = 0; i < map->nvendors; i++)
+ virCPULoongArchVendorFree(map->vendors[i]);
+ VIR_FREE(map->vendors);
+
+ VIR_FREE(map);
+}
+
+static int
+virCPULoongArchVendorParse(xmlXPathContextPtr ctxt ATTRIBUTE_UNUSED,
+ const char *name,
+ void *data)
+{
+ virCPULoongArchMap *map = data;
+ virCPULoongArchVendor *vendor;
+ int ret = -1;
+
+ vendor = g_new0(virCPULoongArchVendor, 1);
+ if (!vendor)
+ return ret;
+ vendor->name = g_strdup(name);
+
+ if (virCPULoongArchVendorFind(map, vendor->name)) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("CPU vendor %1$s already defined"), vendor->name);
+ goto cleanup;
+ }
+
+ VIR_APPEND_ELEMENT(map->vendors, map->nvendors, vendor);
+
+ ret = 0;
+
+ cleanup:
+ virCPULoongArchVendorFree(vendor);
+ return ret;
+}
+
+static int
+virCPULoongArchModelParse(xmlXPathContextPtr ctxt,
+ const char *name,
+ void *data)
+{
+ virCPULoongArchMap *map = data;
+ virCPULoongArchModel *model;
+ xmlNodePtr *nodes = NULL;
+ char *vendor = NULL;
+ uint32_t prid;
+ size_t i;
+ int n;
+ int ret = -1;
+
+ model = g_new0(virCPULoongArchModel, 1);
+ if (!model)
+ goto cleanup;
+
+ model->name = g_strdup(name);
+
+ if (virCPULoongArchModelFind(map, model->name)) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("CPU model %1$s already defined"), model->name);
+ goto cleanup;
+ }
+
+ if (virXPathBoolean("boolean(./vendor)", ctxt)) {
+ vendor = virXPathString("string(./vendor/@name)", ctxt);
+ if (!vendor) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("Invalid vendor element in CPU model %1$s"),
+ model->name);
+ goto cleanup;
+ }
+
+ if (!(model->vendor = virCPULoongArchVendorFind(map, vendor))) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("Unknown vendor %1$s referenced by CPU model %2$s"),
+ vendor, model->name);
+ goto cleanup;
+ }
+ }
+
+ if ((n = virXPathNodeSet("./prid", ctxt, &nodes)) <= 0) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("Missing Prid information for CPU model %1$s"),
+ model->name);
+ goto cleanup;
+ }
+
+ model->data.prid = g_new0(virCPULoongArchPrid, n);
+ if (!model->data.prid)
+ goto cleanup;
+
+ model->data.len = n;
+
+ for (i = 0; i < n; i++) {
+
+ if (virXMLPropUInt(nodes[i], "value", 16, VIR_XML_PROP_REQUIRED,
+ &prid) < 0)
+ {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("Missing or invalid Prid value in CPU model %1$s"),
+ model->name);
+ goto cleanup;
+ }
+ model->data.prid[i].value = prid;
+
+ if (virXMLPropUInt(nodes[i], "mask", 16, VIR_XML_PROP_REQUIRED,
+ &prid) < 0)
+ {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("Missing or invalid PVR mask in CPU model %1$s"),
+ model->name);
+ goto cleanup;
+ }
+ model->data.prid[i].mask = prid;
+ }
+
+ VIR_APPEND_ELEMENT(map->models, map->nmodels, model);
+
+ ret = 0;
+
+ cleanup:
+ virCPULoongArchModelFree(model);
+ VIR_FREE(vendor);
+ VIR_FREE(nodes);
+ return ret;
+}
+
+static virCPULoongArchMap *
+virCPULoongArchLoadMap(void)
+{
+ virCPULoongArchMap *map;
+
+ map = g_new0(virCPULoongArchMap, 1);
+ if (!map)
+ goto cleanup;
+
+ if (cpuMapLoad("loongarch64", virCPULoongArchVendorParse, NULL,
+ virCPULoongArchModelParse, map) < 0)
+ goto cleanup;
+
+ return map;
+
+ cleanup:
+ virCPULoongArchMapFree(map);
+ return NULL;
+}
+
+static virCPUData *
+virCPULoongArchMakeCPUData(virArch arch,
+ virCPULoongArchData *data)
+{
+ virCPUData * cpuData;
+
+ cpuData = g_new0(virCPUData, 1);
+ if (!cpuData)
+ return NULL;
+
+ cpuData->arch = arch;
+
+ if (virCPULoongArchDataCopy(&cpuData->data.loongarch, data) < 0)
+ VIR_FREE(cpuData);
+
+ return cpuData;
+}
+
+static virCPUCompareResult
+virCPULoongArchCompute(virCPUDef *host,
+ const virCPUDef *other,
+ virCPUData **guestData,
+ char **message)
+{
+ virCPULoongArchMap *map = NULL;
+ virCPULoongArchModel *host_model = NULL;
+ virCPULoongArchModel *guest_model = NULL;
+ virCPUDef *cpu = NULL;
+ virCPUCompareResult ret = VIR_CPU_COMPARE_ERROR;
+ virArch arch;
+ size_t i;
+
+ /* Ensure existing configurations are handled correctly */
+ if (!(cpu = virCPUDefCopy(other)))
+ goto cleanup;
+
+ if (cpu->arch != VIR_ARCH_NONE) {
+ bool found = false;
+
+ for (i = 0; i < G_N_ELEMENTS(archs); i++) {
+ if (archs[i] == cpu->arch) {
+ found = true;
+ break;
+ }
+ }
+
+ if (!found) {
+ VIR_DEBUG("CPU arch %s does not match host arch",
+ virArchToString(cpu->arch));
+ if (message) {
+ *message = g_strdup_printf(_("CPU arch %1$s does not match host arch"),
+ virArchToString(cpu->arch));
+ }
+ ret = VIR_CPU_COMPARE_INCOMPATIBLE;
+ goto cleanup;
+ }
+ arch = cpu->arch;
+ } else {
+ arch = host->arch;
+ }
+
+ if (cpu->vendor &&
+ (!host->vendor || STRNEQ(cpu->vendor, host->vendor))) {
+ VIR_DEBUG("host CPU vendor does not match required CPU vendor %s",
+ cpu->vendor);
+ if (message) {
+ *message = g_strdup_printf(_("host CPU vendor does not match required CPU vendor %1$s"),
+ cpu->vendor);
+ }
+ ret = VIR_CPU_COMPARE_INCOMPATIBLE;
+ goto cleanup;
+ }
+
+ if (!(map = virCPULoongArchLoadMap()))
+ goto cleanup;
+
+ /* Host CPU information */
+ if (!(host_model = virCPULoongArchModelFromCPU(host, map)))
+ goto cleanup;
+
+ if (cpu->type == VIR_CPU_TYPE_GUEST) {
+ /* Guest CPU information */
+ switch (cpu->mode) {
+ case VIR_CPU_MODE_HOST_MODEL:
+ case VIR_CPU_MODE_HOST_PASSTHROUGH:
+ /* host-model and host-passthrough:
+ * the guest CPU is the same as the host */
+ guest_model = virCPULoongArchModelCopy(host_model);
+ break;
+
+ case VIR_CPU_MODE_CUSTOM:
+ /* custom:
+ * look up guest CPU information */
+ guest_model = virCPULoongArchModelFromCPU(cpu, map);
+ break;
+ }
+ } else {
+ /* Other host CPU information */
+ guest_model = virCPULoongArchModelFromCPU(cpu, map);
+ }
+
+ if (!guest_model)
+ goto cleanup;
+
+ if (STRNEQ(guest_model->name, host_model->name)) {
+ VIR_DEBUG("host CPU model does not match required CPU model %s",
+ guest_model->name);
+ if (message) {
+ *message = g_strdup_printf(_("host CPU model does not match required CPU model %1$s"),
+ guest_model->name);
+ }
+ ret = VIR_CPU_COMPARE_INCOMPATIBLE;
+ goto cleanup;
+ }
+
+ if (guestData)
+ if (!(*guestData = virCPULoongArchMakeCPUData(arch, &guest_model->data)))
+ goto cleanup;
+
+ ret = VIR_CPU_COMPARE_IDENTICAL;
+
+ cleanup:
+ virCPUDefFree(cpu);
+ virCPULoongArchMapFree(map);
+ virCPULoongArchModelFree(host_model);
+ virCPULoongArchModelFree(guest_model);
+ return ret;
+}
+
+static virCPUCompareResult
+virCPULoongArchCompare(virCPUDef *host,
+ virCPUDef *cpu,
+ bool failIncompatible)
+{
+ virCPUCompareResult ret;
+ char *message = NULL;
+
+ if (!host || !host->model) {
+ if (failIncompatible) {
+ virReportError(VIR_ERR_CPU_INCOMPATIBLE, "%s",
+ _("unknown host CPU"));
+ } else {
+ VIR_WARN("unknown host CPU");
+ ret = VIR_CPU_COMPARE_INCOMPATIBLE;
+ }
+ return -1;
+ }
+
+ ret = virCPULoongArchCompute(host, cpu, NULL, &message);
+
+ if (failIncompatible && ret == VIR_CPU_COMPARE_INCOMPATIBLE) {
+ ret = VIR_CPU_COMPARE_ERROR;
+ if (message) {
+ virReportError(VIR_ERR_CPU_INCOMPATIBLE, "%s", message);
+ } else {
+ virReportError(VIR_ERR_CPU_INCOMPATIBLE, NULL);
+ }
+ }
+ VIR_FREE(message);
+
+ return ret;
+}
+
+static int
+virCPULoongArchDriverDecode(virCPUDef *cpu,
+ const virCPUData *data,
+ virDomainCapsCPUModels *models)
+{
+ int ret = -1;
+ virCPULoongArchMap *map;
+ const virCPULoongArchModel *model;
+
+ if (!data || !(map = virCPULoongArchLoadMap()))
+ return -1;
+
+ if (!(model = virCPULoongArchModelFindPrid(map, data->data.loongarch.prid[0].value))) {
+ virReportError(VIR_ERR_OPERATION_FAILED,
+ _("Cannot find CPU model with Prid 0x%1$08x"),
+ data->data.loongarch.prid[0].value);
+ goto cleanup;
+ }
+
+ if (!virCPUModelIsAllowed(model->name, models)) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+ _("CPU model %1$s is not supported by hypervisor"),
+ model->name);
+ goto cleanup;
+ }
+
+ cpu->model = g_strdup(model->name);
+ if (model->vendor) {
+ cpu->vendor = g_strdup(model->vendor->name);
+ }
+ ret = 0;
+
+ cleanup:
+ virCPULoongArchMapFree(map);
+
+ return ret;
+}
+
+static void
+virCPULoongArchDataFree(virCPUData *data)
+{
+ if (!data)
+ return;
+
+ virCPULoongArchDataClear(&data->data.loongarch);
+ VIR_FREE(data);
+}
+
+static int
+virCPULoongArchGetHostPRID(void)
+{
+ return 0x14c010;
+}
+
+static int
+virCPULoongArchGetHost(virCPUDef *cpu,
+ virDomainCapsCPUModels *models)
+{
+ virCPUData *cpuData = NULL;
+ virCPULoongArchData *data;
+ int ret = -1;
+
+ if (!(cpuData = virCPUDataNew(archs[0])))
+ goto cleanup;
+
+ data = &cpuData->data.loongarch;
+ data->prid = g_new0(virCPULoongArchPrid, 1);
+ if (!data->prid)
+ goto cleanup;
+
+
+ data->len = 1;
+
+ data->prid[0].value = virCPULoongArchGetHostPRID();
+ data->prid[0].mask = 0xffff00ul;
+
+ ret = virCPULoongArchDriverDecode(cpu, cpuData, models);
+
+ cleanup:
+ virCPULoongArchDataFree(cpuData);
+ return ret;
+}
+
+
+static int
+virCPULoongArchUpdate(virCPUDef *guest,
+ const virCPUDef *host ATTRIBUTE_UNUSED,
+ bool relative G_GNUC_UNUSED)
+{
+ /*
+ * - host-passthrough doesn't even get here
+ * - host-model is used for host CPU running in a compatibility mode and
+ * it needs to remain unchanged
+ * - custom doesn't support any optional features, there's nothing to
+ * update
+ */
+
+ if (guest->mode == VIR_CPU_MODE_CUSTOM)
+ guest->match = VIR_CPU_MATCH_EXACT;
+
+ return 0;
+}
+
+static virCPUDef *
+virCPULoongArchDriverBaseline(virCPUDef **cpus,
+ unsigned int ncpus,
+ virDomainCapsCPUModels *models ATTRIBUTE_UNUSED,
+ const char **features ATTRIBUTE_UNUSED,
+ bool migratable ATTRIBUTE_UNUSED)
+{
+ virCPULoongArchMap *map;
+ const virCPULoongArchModel *model;
+ const virCPULoongArchVendor *vendor = NULL;
+ virCPUDef *cpu = NULL;
+ size_t i;
+
+ if (!(map = virCPULoongArchLoadMap()))
+ goto error;
+
+ if (!(model = virCPULoongArchModelFind(map, cpus[0]->model))) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("Unknown CPU model %1$s"), cpus[0]->model);
+ goto error;
+ }
+
+ for (i = 0; i < ncpus; i++) {
+ const virCPULoongArchVendor *vnd;
+
+ if (STRNEQ(cpus[i]->model, model->name)) {
+ virReportError(VIR_ERR_OPERATION_FAILED, "%s",
+ _("CPUs are incompatible"));
+ goto error;
+ }
+
+ if (!cpus[i]->vendor)
+ continue;
+
+ if (!(vnd = virCPULoongArchVendorFind(map, cpus[i]->vendor))) {
+ virReportError(VIR_ERR_OPERATION_FAILED,
+ _("Unknown CPU vendor %1$s"), cpus[i]->vendor);
+ goto error;
+ }
+
+ if (model->vendor) {
+ if (model->vendor != vnd) {
+ virReportError(VIR_ERR_OPERATION_FAILED,
+ _("CPU vendor %1$s of model %2$s differs from vendor %3$s"),
+ model->vendor->name, model->name,
+ vnd->name);
+ goto error;
+ }
+ } else if (vendor) {
+ if (vendor != vnd) {
+ virReportError(VIR_ERR_OPERATION_FAILED, "%s",
+ _("CPU vendors do not match"));
+ goto error;
+ }
+ } else {
+ vendor = vnd;
+ }
+ }
+
+ cpu = virCPUDefNew();
+ cpu->model = g_strdup(model->name);
+ if (vendor) {
+ cpu->vendor = g_strdup(vendor->name);
+ }
+ cpu->type = VIR_CPU_TYPE_GUEST;
+ cpu->match = VIR_CPU_MATCH_EXACT;
+ cpu->fallback = VIR_CPU_FALLBACK_FORBID;
+
+ cleanup:
+ virCPULoongArchMapFree(map);
+ return cpu;
+
+ error:
+ virCPUDefFree(cpu);
+ cpu = NULL;
+ goto cleanup;
+}
+
+static int
+virCPULoongArchDriverGetModels(char ***models)
+{
+ virCPULoongArchMap *map;
+ size_t i;
+ int ret = -1;
+
+ if (!(map = virCPULoongArchLoadMap())) {
+ return -1;
+ }
+
+ if (models) {
+ *models = g_new0(char *, map->nmodels + 1);
+ if (!(*models))
+ return -1;
+
+ for (i = 0; i < map->nmodels; i++) {
+ (*models)[i] = g_strdup(map->models[i]->name);
+ }
+ }
+
+ ret = map->nmodels;
+
+ return ret;
+}
+
+struct cpuArchDriver cpuDriverLoongArch = {
+ .name = "LoongArch",
+ .arch = archs,
+ .narch = G_N_ELEMENTS(archs),
+ .compare = virCPULoongArchCompare,
+ .decode = virCPULoongArchDriverDecode,
+ .encode = NULL,
+ .dataFree = virCPULoongArchDataFree,
+ .getHost = virCPULoongArchGetHost,
+ .baseline = virCPULoongArchDriverBaseline,
+ .update = virCPULoongArchUpdate,
+ .getModels = virCPULoongArchDriverGetModels,
+};
diff --git a/src/cpu/cpu_loongarch.h b/src/cpu/cpu_loongarch.h
new file mode 100644
index 0000000000..bebc16a242
--- /dev/null
+++ b/src/cpu/cpu_loongarch.h
@@ -0,0 +1,25 @@
+/*
+ * cpu_loongarch.h: CPU driver for 64-bit LOONGARCH CPUs
+ *
+ * Copyright (C) 2023 Loongson Technology.
+ *
+ * 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
+
+# include "cpu.h"
+
+extern struct cpuArchDriver cpuDriverLoongArch;
diff --git a/src/cpu/cpu_loongarch_data.h b/src/cpu/cpu_loongarch_data.h
new file mode 100644
index 0000000000..43ae044838
--- /dev/null
+++ b/src/cpu/cpu_loongarch_data.h
@@ -0,0 +1,37 @@
+/*
+ * cpu_loongarch_data.h: 64-bit LOONGARCH CPU specific data
+ *
+ * Copyright (C) 2023 Loongson Technology.
+ *
+ * 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
+
+# include <stdint.h>
+
+typedef struct _virCPULoongArchPrid virCPULoongArchPrid;
+struct _virCPULoongArchPrid {
+ uint32_t value;
+ uint32_t mask;
+};
+
+# define VIR_CPU_LOONGARCH_DATA_INIT { 0 }
+
+typedef struct _virCPULoongArchData virCPULoongArchData;
+struct _virCPULoongArchData {
+ size_t len;
+ virCPULoongArchPrid *prid;
+};
diff --git a/src/cpu/meson.build b/src/cpu/meson.build
index 55396903b9..254d6b4545 100644
--- a/src/cpu/meson.build
+++ b/src/cpu/meson.build
@@ -6,6 +6,7 @@ cpu_sources = [
'cpu_riscv64.c',
'cpu_s390.c',
'cpu_x86.c',
+ 'cpu_loongarch.c'
]
cpu_lib = static_library(
diff --git a/src/qemu/qemu_capabilities.c b/src/qemu/qemu_capabilities.c
index 83119e871a..118d3429c3 100644
--- a/src/qemu/qemu_capabilities.c
+++ b/src/qemu/qemu_capabilities.c
@@ -2697,6 +2697,7 @@ static const char *preferredMachines[] =
"sim", /* VIR_ARCH_XTENSA */
"sim", /* VIR_ARCH_XTENSAEB */
+ "virt", /* VIR_ARCH_LOONGARCH64 */
};
G_STATIC_ASSERT(G_N_ELEMENTS(preferredMachines) == VIR_ARCH_LAST);
diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c
index 953808fcfe..00e38950b6 100644
--- a/src/qemu/qemu_domain.c
+++ b/src/qemu/qemu_domain.c
@@ -4222,6 +4222,10 @@ qemuDomainDefAddDefaultDevices(virQEMUDriver *driver,
addPCIRoot = true;
break;
+ case VIR_ARCH_LOONGARCH64:
+ addPCIeRoot = true;
+ break;
+
case VIR_ARCH_ARMV7B:
case VIR_ARCH_CRIS:
case VIR_ARCH_ITANIUM:
diff --git a/src/util/virarch.c b/src/util/virarch.c
index 01e520de73..289bd80d90 100644
--- a/src/util/virarch.c
+++ b/src/util/virarch.c
@@ -83,6 +83,8 @@ static const struct virArchData {
{ "xtensa", 32, VIR_ARCH_LITTLE_ENDIAN },
{ "xtensaeb", 32, VIR_ARCH_BIG_ENDIAN },
+
+ { "loongarch64", 64, VIR_ARCH_LITTLE_ENDIAN },
};
G_STATIC_ASSERT(G_N_ELEMENTS(virArchData) == VIR_ARCH_LAST);
diff --git a/src/util/virarch.h b/src/util/virarch.h
index 747f77c48e..638e519fe6 100644
--- a/src/util/virarch.h
+++ b/src/util/virarch.h
@@ -69,6 +69,8 @@ typedef enum {
VIR_ARCH_XTENSA, /* XTensa 32 LE https://en.wikipedia.org/wiki/Xtensa#Processor_Cores */
VIR_ARCH_XTENSAEB, /* XTensa 32 BE https://en.wikipedia.org/wiki/Xtensa#Processor_Cores */
+ VIR_ARCH_LOONGARCH64, /* LoongArch 64 LE */
+
VIR_ARCH_LAST,
} virArch;
@@ -106,6 +108,8 @@ typedef enum {
#define ARCH_IS_SH4(arch) ((arch) == VIR_ARCH_SH4 ||\
(arch) == VIR_ARCH_SH4EB)
+#define ARCH_IS_LOONGARCH(arch) ((arch) == VIR_ARCH_LOONGARCH64)
+
typedef enum {
VIR_ARCH_LITTLE_ENDIAN,
VIR_ARCH_BIG_ENDIAN,
--
2.27.0
_______________________________________________
Devel mailing list -- devel@lists.libvirt.org
To unsubscribe send an email to devel-leave@lists.libvirt.org
On Thu, Dec 14, 2023 at 02:08:45PM +0800, xianglai li wrote:
> From: lixianglai <lixianglai@loongson.cn>
>
> Add loongarch cpu support, Define new cpu type 'loongarch64'
> and implement it's driver functions.
>
> Signed-off-by: lixianglai <lixianglai@loongson.cn>
We usually prefer the full name to be used both for git authorship
and DCO purposes. I believe this would be "Xianglai Li" in your case.
> ---
> po/POTFILES | 1 +
> src/conf/schemas/basictypes.rng | 1 +
> src/cpu/cpu.c | 2 +
> src/cpu/cpu.h | 2 +
> src/cpu/cpu_loongarch.c | 742 ++++++++++++++++++++++++++++++++
> src/cpu/cpu_loongarch.h | 25 ++
> src/cpu/cpu_loongarch_data.h | 37 ++
> src/cpu/meson.build | 1 +
> src/qemu/qemu_capabilities.c | 1 +
> src/qemu/qemu_domain.c | 4 +
> src/util/virarch.c | 2 +
> src/util/virarch.h | 4 +
> 12 files changed, 822 insertions(+)
> create mode 100644 src/cpu/cpu_loongarch.c
> create mode 100644 src/cpu/cpu_loongarch.h
> create mode 100644 src/cpu/cpu_loongarch_data.h
This patch breaks the test suite:
$ make -C .../libvirt/build/build-aux sc_preprocessor_indentation
make: Entering directory '.../libvirt/build/build-aux'
cppi: .../libvirt/src/cpu/cpu_loongarch.h: line 23: not properly indented
cppi: .../libvirt/src/cpu/cpu_loongarch_data.h: line 23: not properly indented
cppi: .../libvirt/src/cpu/cpu_loongarch_data.h: line 31: not properly indented
incorrect preprocessor indentation
make: *** [.../libvirt/build-aux/syntax-check.mk:500:
sc_preprocessor_indentation] Error 1
make: Leaving directory '.../libvirt/build/build-aux'
Should be an easy enough fix.
Please make sure that 'meson test' runs successfully after every
single patch in the series, and that you have optional test tools
such as cppi installed.
> +++ b/src/conf/schemas/basictypes.rng
> @@ -470,6 +470,7 @@
> <value>x86_64</value>
> <value>xtensa</value>
> <value>xtensaeb</value>
> + <value>loongarch64</value>
This list is sorted alphabetically; please ensure that it remains
that way after your changes. Not all lists in libvirt are sorted
alphabetically, but generally speaking if you see one that is you
should keep it that way.
> +++ b/src/cpu/cpu_loongarch.c
> @@ -0,0 +1,742 @@
> +static const virArch archs[] = { VIR_ARCH_LOONGARCH64 };
> +
> +typedef struct _virCPULoongArchVendor virCPULoongArchVendor;
> +struct _virCPULoongArchVendor {
> + char *name;
> +};
> +
> +typedef struct _virCPULoongArchModel virCPULoongArchModel;
> +struct _virCPULoongArchModel {
> + char *name;
> + const virCPULoongArchVendor *vendor;
> + virCPULoongArchData data;
> +};
> +
> +typedef struct _virCPULoongArchMap virCPULoongArchMap;
> +struct _virCPULoongArchMap {
> + size_t nvendors;
> + virCPULoongArchVendor **vendors;
> + size_t nmodels;
> + virCPULoongArchModel **models;
> +};
This CPU driver appears to be directly modeled after the ppc64
driver. I wonder if all the complexity is necessary at this point in
time? Wouldn't it perhaps be better to start with a very bare-bone
CPU driver, modeled after the riscv64 one, and then grow from there
as the demand for more advanced features becomes apparent?
> +static int
> +virCPULoongArchGetHostPRID(void)
> +{
> + return 0x14c010;
> +}
Hardcoding the host CPU's PRID...
> +static int
> +virCPULoongArchGetHost(virCPUDef *cpu,
> + virDomainCapsCPUModels *models)
> +{
> + virCPUData *cpuData = NULL;
> + virCPULoongArchData *data;
> + int ret = -1;
> +
> + if (!(cpuData = virCPUDataNew(archs[0])))
> + goto cleanup;
> +
> + data = &cpuData->data.loongarch;
> + data->prid = g_new0(virCPULoongArchPrid, 1);
> + if (!data->prid)
> + goto cleanup;
> +
> +
> + data->len = 1;
> +
> + data->prid[0].value = virCPULoongArchGetHostPRID();
> + data->prid[0].mask = 0xffff00ul;
... and corresponding mask is definitely not acceptable. You'll need
to implement a function that fetches the value dynamically by using
whatever mechanism is appropriate, and of course ensure that such
code is only ever run on a loongarch64 host.
But again, do we really need that complexity right now? The riscv64
driver doesn't have any of that and is usable for many purposes.
> +static virCPUDef *
> +virCPULoongArchDriverBaseline(virCPUDef **cpus,
> + unsigned int ncpus,
> + virDomainCapsCPUModels *models ATTRIBUTE_UNUSED,
> + const char **features ATTRIBUTE_UNUSED,
> + bool migratable ATTRIBUTE_UNUSED)
The function arguments are not aligned properly here. There are
several other instances of this. Please make sure that things are
aligned correctly throughout.
> diff --git a/src/cpu/meson.build b/src/cpu/meson.build
> index 55396903b9..254d6b4545 100644
> --- a/src/cpu/meson.build
> +++ b/src/cpu/meson.build
> @@ -6,6 +6,7 @@ cpu_sources = [
> 'cpu_riscv64.c',
> 'cpu_s390.c',
> 'cpu_x86.c',
> + 'cpu_loongarch.c'
> ]
This is another list that needs to remain sorted...
> +++ b/src/util/virarch.h
> @@ -69,6 +69,8 @@ typedef enum {
> VIR_ARCH_XTENSA, /* XTensa 32 LE https://en.wikipedia.org/wiki/Xtensa#Processor_Cores */
> VIR_ARCH_XTENSAEB, /* XTensa 32 BE https://en.wikipedia.org/wiki/Xtensa#Processor_Cores */
>
> + VIR_ARCH_LOONGARCH64, /* LoongArch 64 LE */
> +
> VIR_ARCH_LAST,
> } virArch;
... as is this one and those that are derived from it, including
preferredMachines.
--
Andrea Bolognani / Red Hat / Virtualization
_______________________________________________
Devel mailing list -- devel@lists.libvirt.org
To unsubscribe send an email to devel-leave@lists.libvirt.org
Hi Andrea:
> On Thu, Dec 14, 2023 at 02:08:45PM +0800, xianglai li wrote:
>> From: lixianglai <lixianglai@loongson.cn>
>>
>> Add loongarch cpu support, Define new cpu type 'loongarch64'
>> and implement it's driver functions.
>>
>> Signed-off-by: lixianglai <lixianglai@loongson.cn>
> We usually prefer the full name to be used both for git authorship
> and DCO purposes. I believe this would be "Xianglai Li" in your case.
Ok, I'll correct it in the next version :)
>> ---
>> po/POTFILES | 1 +
>> src/conf/schemas/basictypes.rng | 1 +
>> src/cpu/cpu.c | 2 +
>> src/cpu/cpu.h | 2 +
>> src/cpu/cpu_loongarch.c | 742 ++++++++++++++++++++++++++++++++
>> src/cpu/cpu_loongarch.h | 25 ++
>> src/cpu/cpu_loongarch_data.h | 37 ++
>> src/cpu/meson.build | 1 +
>> src/qemu/qemu_capabilities.c | 1 +
>> src/qemu/qemu_domain.c | 4 +
>> src/util/virarch.c | 2 +
>> src/util/virarch.h | 4 +
>> 12 files changed, 822 insertions(+)
>> create mode 100644 src/cpu/cpu_loongarch.c
>> create mode 100644 src/cpu/cpu_loongarch.h
>> create mode 100644 src/cpu/cpu_loongarch_data.h
> This patch breaks the test suite:
>
> $ make -C .../libvirt/build/build-aux sc_preprocessor_indentation
> make: Entering directory '.../libvirt/build/build-aux'
> cppi: .../libvirt/src/cpu/cpu_loongarch.h: line 23: not properly indented
> cppi: .../libvirt/src/cpu/cpu_loongarch_data.h: line 23: not properly indented
> cppi: .../libvirt/src/cpu/cpu_loongarch_data.h: line 31: not properly indented
> incorrect preprocessor indentation
> make: *** [.../libvirt/build-aux/syntax-check.mk:500:
> sc_preprocessor_indentation] Error 1
> make: Leaving directory '.../libvirt/build/build-aux'
>
> Should be an easy enough fix.
>
> Please make sure that 'meson test' runs successfully after every
> single patch in the series, and that you have optional test tools
> such as cppi installed.
Ok, I think it is because I did not install cppi that the problem was
not detected during 'meson test'.
I will install cppi and conduct 'meson test' again for each patch.
>
>> +++ b/src/conf/schemas/basictypes.rng
>> @@ -470,6 +470,7 @@
>> <value>x86_64</value>
>> <value>xtensa</value>
>> <value>xtensaeb</value>
>> + <value>loongarch64</value>
> This list is sorted alphabetically; please ensure that it remains
> that way after your changes. Not all lists in libvirt are sorted
> alphabetically, but generally speaking if you see one that is you
> should keep it that way.
Ok, I'll correct it in the next version.
>
>> +++ b/src/cpu/cpu_loongarch.c
>> @@ -0,0 +1,742 @@
>> +static const virArch archs[] = { VIR_ARCH_LOONGARCH64 };
>> +
>> +typedef struct _virCPULoongArchVendor virCPULoongArchVendor;
>> +struct _virCPULoongArchVendor {
>> + char *name;
>> +};
>> +
>> +typedef struct _virCPULoongArchModel virCPULoongArchModel;
>> +struct _virCPULoongArchModel {
>> + char *name;
>> + const virCPULoongArchVendor *vendor;
>> + virCPULoongArchData data;
>> +};
>> +
>> +typedef struct _virCPULoongArchMap virCPULoongArchMap;
>> +struct _virCPULoongArchMap {
>> + size_t nvendors;
>> + virCPULoongArchVendor **vendors;
>> + size_t nmodels;
>> + virCPULoongArchModel **models;
>> +};
> This CPU driver appears to be directly modeled after the ppc64
> driver. I wonder if all the complexity is necessary at this point in
> time? Wouldn't it perhaps be better to start with a very bare-bone
> CPU driver, modeled after the riscv64 one, and then grow from there
> as the demand for more advanced features becomes apparent?
Well, I think I will try to refer to riscv64 for cpu driver
implementation in the next version.
>> +static int
>> +virCPULoongArchGetHostPRID(void)
>> +{
>> + return 0x14c010;
>> +}
> Hardcoding the host CPU's PRID...
>
>> +static int
>> +virCPULoongArchGetHost(virCPUDef *cpu,
>> + virDomainCapsCPUModels *models)
>> +{
>> + virCPUData *cpuData = NULL;
>> + virCPULoongArchData *data;
>> + int ret = -1;
>> +
>> + if (!(cpuData = virCPUDataNew(archs[0])))
>> + goto cleanup;
>> +
>> + data = &cpuData->data.loongarch;
>> + data->prid = g_new0(virCPULoongArchPrid, 1);
>> + if (!data->prid)
>> + goto cleanup;
>> +
>> +
>> + data->len = 1;
>> +
>> + data->prid[0].value = virCPULoongArchGetHostPRID();
>> + data->prid[0].mask = 0xffff00ul;
> ... and corresponding mask is definitely not acceptable. You'll need
> to implement a function that fetches the value dynamically by using
> whatever mechanism is appropriate, and of course ensure that such
> code is only ever run on a loongarch64 host.
>
> But again, do we really need that complexity right now? The riscv64
> driver doesn't have any of that and is usable for many purposes.
Okay, so the hard coding here is a little bit inappropriate, and I feel
like I can do without the complexity,
I'm not sure, but I can try to simplify this.
>> +static virCPUDef *
>> +virCPULoongArchDriverBaseline(virCPUDef **cpus,
>> + unsigned int ncpus,
>> + virDomainCapsCPUModels *models ATTRIBUTE_UNUSED,
>> + const char **features ATTRIBUTE_UNUSED,
>> + bool migratable ATTRIBUTE_UNUSED)
> The function arguments are not aligned properly here. There are
> several other instances of this. Please make sure that things are
> aligned correctly throughout.
Ok, I will do a code check according to the suggestion.
>
>> diff --git a/src/cpu/meson.build b/src/cpu/meson.build
>> index 55396903b9..254d6b4545 100644
>> --- a/src/cpu/meson.build
>> +++ b/src/cpu/meson.build
>> @@ -6,6 +6,7 @@ cpu_sources = [
>> 'cpu_riscv64.c',
>> 'cpu_s390.c',
>> 'cpu_x86.c',
>> + 'cpu_loongarch.c'
>> ]
> This is another list that needs to remain sorted...
Ok, I'll fix it in the next version.
>> +++ b/src/util/virarch.h
>> @@ -69,6 +69,8 @@ typedef enum {
>> VIR_ARCH_XTENSA, /* XTensa 32 LE https://en.wikipedia.org/wiki/Xtensa#Processor_Cores */
>> VIR_ARCH_XTENSAEB, /* XTensa 32 BE https://en.wikipedia.org/wiki/Xtensa#Processor_Cores */
>>
>> + VIR_ARCH_LOONGARCH64, /* LoongArch 64 LE */
>> +
>> VIR_ARCH_LAST,
>> } virArch;
> ... as is this one and those that are derived from it, including
> preferredMachines.
Ok, I'll fix it in the next version.
Thanks,
Xianglai.
>
_______________________________________________
Devel mailing list -- devel@lists.libvirt.org
To unsubscribe send an email to devel-leave@lists.libvirt.org
© 2016 - 2026 Red Hat, Inc.