79c4324644
Change-Id: I2d302dda68298877c65c99147f5bf22186a59aac
995 lines
26 KiB
Diff
995 lines
26 KiB
Diff
From c0b26612cf12d5f0594a9dfa5bd97fcf7acfe9da Mon Sep 17 00:00:00 2001
|
|
From: zhaotianrui <zhaotianrui@loongson.cn>
|
|
Date: Wed, 11 Jan 2023 10:53:08 -0500
|
|
Subject: [PATCH 1/4] Add loongarch cpu support
|
|
|
|
Add loongarch cpu support: Define new cpu type 'loongarch64'
|
|
and implement it's driver functions.
|
|
|
|
Signed-off-by: zhaotianrui <zhaotianrui@loongson.cn>
|
|
---
|
|
docs/schemas/basictypes.rng | 1 +
|
|
po/POTFILES.in | 1 +
|
|
src/cpu/Makefile.inc.am | 3 +
|
|
src/cpu/cpu.c | 2 +
|
|
src/cpu/cpu.h | 3 +-
|
|
src/cpu/cpu_loongarch.c | 739 +++++++++++++++++++++++++++++++++++
|
|
src/cpu/cpu_loongarch.h | 28 ++
|
|
src/cpu/cpu_loongarch_data.h | 40 ++
|
|
src/qemu/qemu_capabilities.c | 1 +
|
|
src/qemu/qemu_domain.c | 4 +
|
|
src/util/virarch.c | 1 +
|
|
src/util/virarch.h | 3 +
|
|
12 files changed, 825 insertions(+), 1 deletion(-)
|
|
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/docs/schemas/basictypes.rng b/docs/schemas/basictypes.rng
|
|
index 81465273c8..34d285db48 100644
|
|
--- a/docs/schemas/basictypes.rng
|
|
+++ b/docs/schemas/basictypes.rng
|
|
@@ -444,6 +444,7 @@
|
|
<value>x86_64</value>
|
|
<value>xtensa</value>
|
|
<value>xtensaeb</value>
|
|
+ <value>loongarch64</value>
|
|
</choice>
|
|
</define>
|
|
|
|
diff --git a/po/POTFILES.in b/po/POTFILES.in
|
|
index 197ff2f3d3..24dc8087d6 100644
|
|
--- a/po/POTFILES.in
|
|
+++ b/po/POTFILES.in
|
|
@@ -60,6 +60,7 @@
|
|
@SRCDIR@/src/cpu/cpu_arm.c
|
|
@SRCDIR@/src/cpu/cpu_map.c
|
|
@SRCDIR@/src/cpu/cpu_ppc64.c
|
|
+@SRCDIR@/src/cpu/cpu_loongarch.c
|
|
@SRCDIR@/src/cpu/cpu_s390.c
|
|
@SRCDIR@/src/cpu/cpu_x86.c
|
|
@SRCDIR@/src/datatypes.c
|
|
diff --git a/src/cpu/Makefile.inc.am b/src/cpu/Makefile.inc.am
|
|
index 1ee1290c2d..e2219bbc1c 100644
|
|
--- a/src/cpu/Makefile.inc.am
|
|
+++ b/src/cpu/Makefile.inc.am
|
|
@@ -15,6 +15,9 @@ CPU_SOURCES = \
|
|
cpu/cpu_ppc64.h \
|
|
cpu/cpu_ppc64.c \
|
|
cpu/cpu_ppc64_data.h \
|
|
+ cpu/cpu_loongarch.h \
|
|
+ cpu/cpu_loongarch.c \
|
|
+ cpu/cpu_loongarch_data.h \
|
|
cpu/cpu_map.h \
|
|
cpu/cpu_map.c \
|
|
$(NULL)
|
|
diff --git a/src/cpu/cpu.c b/src/cpu/cpu.c
|
|
index 89c06aceeb..df78a0d33f 100644
|
|
--- a/src/cpu/cpu.c
|
|
+++ b/src/cpu/cpu.c
|
|
@@ -30,6 +30,7 @@
|
|
#include "cpu_s390.h"
|
|
#include "cpu_arm.h"
|
|
#include "cpu_sw64.h"
|
|
+#include "cpu_loongarch.h"
|
|
#include "capabilities.h"
|
|
#include "virstring.h"
|
|
|
|
@@ -44,6 +45,7 @@ static struct cpuArchDriver *drivers[] = {
|
|
&cpuDriverS390,
|
|
&cpuDriverArm,
|
|
&cpuDriverSW64,
|
|
+ &cpuDriverLoongArch,
|
|
};
|
|
|
|
|
|
diff --git a/src/cpu/cpu.h b/src/cpu/cpu.h
|
|
index ec22a183a1..307c85fb61 100644
|
|
--- a/src/cpu/cpu.h
|
|
+++ b/src/cpu/cpu.h
|
|
@@ -28,7 +28,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;
|
|
typedef virCPUData *virCPUDataPtr;
|
|
@@ -38,6 +38,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..953316bf78
|
|
--- /dev/null
|
|
+++ b/src/cpu/cpu_loongarch.c
|
|
@@ -0,0 +1,739 @@
|
|
+/*
|
|
+ * 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/wait.h>
|
|
+#include <sys/resource.h>
|
|
+#include <sys/stat.h>
|
|
+#include <sys/types.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 {
|
|
+ char *name;
|
|
+} LoongArch_vendor;
|
|
+
|
|
+typedef struct {
|
|
+ char *name;
|
|
+ const LoongArch_vendor *vendor;
|
|
+ virCPULoongArchData data;
|
|
+} LoongArch_model;
|
|
+
|
|
+typedef struct {
|
|
+ size_t nvendors;
|
|
+ LoongArch_vendor **vendors;
|
|
+ size_t nmodels;
|
|
+ LoongArch_model **models;
|
|
+} LoongArch_map;
|
|
+
|
|
+static void
|
|
+LoongArchDataClear(virCPULoongArchData *data)
|
|
+{
|
|
+ if (!data)
|
|
+ return;
|
|
+
|
|
+ VIR_FREE(data->prid);
|
|
+}
|
|
+
|
|
+static int
|
|
+LoongArchDataCopy(virCPULoongArchData *dst, const virCPULoongArchData *src)
|
|
+{
|
|
+ size_t i;
|
|
+
|
|
+ if (VIR_ALLOC_N(dst->prid, src->len) < 0)
|
|
+ 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
|
|
+LoongArchVendorFree(LoongArch_vendor *vendor)
|
|
+{
|
|
+ if (!vendor)
|
|
+ return;
|
|
+
|
|
+ VIR_FREE(vendor->name);
|
|
+ VIR_FREE(vendor);
|
|
+}
|
|
+
|
|
+static LoongArch_vendor *
|
|
+LoongArchVendorFind(const LoongArch_map *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
|
|
+LoongArchModelFree(LoongArch_model *model)
|
|
+{
|
|
+ if (!model)
|
|
+ return;
|
|
+
|
|
+ LoongArchDataClear(&model->data);
|
|
+ VIR_FREE(model->name);
|
|
+ VIR_FREE(model);
|
|
+}
|
|
+
|
|
+static LoongArch_model *
|
|
+LoongArchModelCopy(const LoongArch_model *model)
|
|
+{
|
|
+ LoongArch_model *copy;
|
|
+
|
|
+ if (VIR_ALLOC(copy) < 0)
|
|
+ goto cleanup;
|
|
+
|
|
+ copy->name = g_strdup(model->name);
|
|
+
|
|
+ if (LoongArchDataCopy(©->data, &model->data) < 0)
|
|
+ goto cleanup;
|
|
+
|
|
+ copy->vendor = model->vendor;
|
|
+
|
|
+ return copy;
|
|
+
|
|
+ cleanup:
|
|
+ LoongArchModelFree(copy);
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+static LoongArch_model *
|
|
+LoongArchModelFind(const LoongArch_map *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 LoongArch_model *
|
|
+LoongArchModelFindPrid(const LoongArch_map *map,
|
|
+ uint32_t prid)
|
|
+{
|
|
+ size_t i;
|
|
+ size_t j;
|
|
+
|
|
+ for (i = 0; i < map->nmodels; i++) {
|
|
+ LoongArch_model *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 LoongArch_model *
|
|
+LoongArchModelFromCPU(const virCPUDef *cpu,
|
|
+ const LoongArch_map *map)
|
|
+{
|
|
+ LoongArch_model *model;
|
|
+
|
|
+ if (!cpu->model) {
|
|
+ virReportError(VIR_ERR_INVALID_ARG, "%s",
|
|
+ _("no CPU model specified"));
|
|
+ return NULL;
|
|
+ }
|
|
+
|
|
+ if (!(model = LoongArchModelFind(map, cpu->model))) {
|
|
+ virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
+ _("Unknown CPU model %s"), cpu->model);
|
|
+ return NULL;
|
|
+ }
|
|
+
|
|
+ return LoongArchModelCopy(model);
|
|
+}
|
|
+
|
|
+static void
|
|
+LoongArchMapFree(LoongArch_map *map)
|
|
+{
|
|
+ size_t i;
|
|
+
|
|
+ if (!map)
|
|
+ return;
|
|
+
|
|
+ for (i = 0; i < map->nmodels; i++)
|
|
+ LoongArchModelFree(map->models[i]);
|
|
+ VIR_FREE(map->models);
|
|
+
|
|
+ for (i = 0; i < map->nvendors; i++)
|
|
+ LoongArchVendorFree(map->vendors[i]);
|
|
+ VIR_FREE(map->vendors);
|
|
+
|
|
+ VIR_FREE(map);
|
|
+}
|
|
+
|
|
+static int
|
|
+LoongArchVendorParse(xmlXPathContextPtr ctxt ATTRIBUTE_UNUSED,
|
|
+ const char *name,
|
|
+ void *data)
|
|
+{
|
|
+ LoongArch_map *map = data;
|
|
+ LoongArch_vendor *vendor;
|
|
+ int ret = -1;
|
|
+
|
|
+ if (VIR_ALLOC(vendor) < 0)
|
|
+ return ret;
|
|
+ vendor->name = g_strdup(name);
|
|
+
|
|
+ if (LoongArchVendorFind(map, vendor->name)) {
|
|
+ virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
+ _("CPU vendor %s already defined"), vendor->name);
|
|
+ goto cleanup;
|
|
+ }
|
|
+
|
|
+ if (VIR_APPEND_ELEMENT(map->vendors, map->nvendors, vendor) < 0)
|
|
+ goto cleanup;
|
|
+
|
|
+ ret = 0;
|
|
+
|
|
+ cleanup:
|
|
+ LoongArchVendorFree(vendor);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int
|
|
+LoongArchModelParse(xmlXPathContextPtr ctxt,
|
|
+ const char *name,
|
|
+ void *data)
|
|
+{
|
|
+ LoongArch_map *map = data;
|
|
+ LoongArch_model *model;
|
|
+ xmlNodePtr *nodes = NULL;
|
|
+ char *vendor = NULL;
|
|
+ unsigned long prid;
|
|
+ size_t i;
|
|
+ int n;
|
|
+ int ret = -1;
|
|
+
|
|
+ if (VIR_ALLOC(model) < 0)
|
|
+ goto cleanup;
|
|
+
|
|
+ model->name = g_strdup(name);
|
|
+
|
|
+ if (LoongArchModelFind(map, model->name)) {
|
|
+ virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
+ _("CPU model %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 %s"),
|
|
+ model->name);
|
|
+ goto cleanup;
|
|
+ }
|
|
+
|
|
+ if (!(model->vendor = LoongArchVendorFind(map, vendor))) {
|
|
+ virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
+ _("Unknown vendor %s referenced by CPU model %s"),
|
|
+ vendor, model->name);
|
|
+ goto cleanup;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if ((n = virXPathNodeSet("./prid", ctxt, &nodes)) <= 0) {
|
|
+ virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
+ _("Missing Prid information for CPU model %s"),
|
|
+ model->name);
|
|
+ goto cleanup;
|
|
+ }
|
|
+
|
|
+ if (VIR_ALLOC_N(model->data.prid, n) < 0)
|
|
+ goto cleanup;
|
|
+
|
|
+ model->data.len = n;
|
|
+
|
|
+ for (i = 0; i < n; i++) {
|
|
+ ctxt->node = nodes[i];
|
|
+
|
|
+ if (virXPathULongHex("string(./@value)", ctxt, &prid) < 0) {
|
|
+ virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
+ _("Missing or invalid Prid value in CPU model %s"),
|
|
+ model->name);
|
|
+ goto cleanup;
|
|
+ }
|
|
+ model->data.prid[i].value = prid;
|
|
+
|
|
+ if (virXPathULongHex("string(./@mask)", ctxt, &prid) < 0) {
|
|
+ virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
+ _("Missing or invalid PVR mask in CPU model %s"),
|
|
+ model->name);
|
|
+ goto cleanup;
|
|
+ }
|
|
+ model->data.prid[i].mask = prid;
|
|
+ }
|
|
+
|
|
+ if (VIR_APPEND_ELEMENT(map->models, map->nmodels, model) < 0)
|
|
+ goto cleanup;
|
|
+
|
|
+ ret = 0;
|
|
+
|
|
+ cleanup:
|
|
+ LoongArchModelFree(model);
|
|
+ VIR_FREE(vendor);
|
|
+ VIR_FREE(nodes);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static LoongArch_map *
|
|
+LoongArchLoadMap(void)
|
|
+{
|
|
+ LoongArch_map *map;
|
|
+
|
|
+ if (VIR_ALLOC(map) < 0)
|
|
+ goto cleanup;
|
|
+
|
|
+ if (cpuMapLoad("loongarch64", LoongArchVendorParse, NULL, LoongArchModelParse, map) < 0)
|
|
+ goto cleanup;
|
|
+
|
|
+ return map;
|
|
+
|
|
+ cleanup:
|
|
+ LoongArchMapFree(map);
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+static virCPUDataPtr
|
|
+LoongArchMakeCPUData(virArch arch,
|
|
+ virCPULoongArchData *data)
|
|
+{
|
|
+ virCPUDataPtr cpuData;
|
|
+
|
|
+ if (VIR_ALLOC(cpuData) < 0)
|
|
+ return NULL;
|
|
+
|
|
+ cpuData->arch = arch;
|
|
+
|
|
+ if (LoongArchDataCopy(&cpuData->data.loongarch, data) < 0)
|
|
+ VIR_FREE(cpuData);
|
|
+
|
|
+ return cpuData;
|
|
+}
|
|
+
|
|
+static virCPUCompareResult
|
|
+LoongArchCompute(virCPUDefPtr host,
|
|
+ const virCPUDef *other,
|
|
+ virCPUDataPtr *guestData,
|
|
+ char **message)
|
|
+{
|
|
+ LoongArch_map *map = NULL;
|
|
+ LoongArch_model *host_model = NULL;
|
|
+ LoongArch_model *guest_model = NULL;
|
|
+ virCPUDefPtr 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 %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 %s"), cpu->vendor);
|
|
+ }
|
|
+ ret = VIR_CPU_COMPARE_INCOMPATIBLE;
|
|
+ goto cleanup;
|
|
+ }
|
|
+
|
|
+ if (!(map = LoongArchLoadMap()))
|
|
+ goto cleanup;
|
|
+
|
|
+ /* Host CPU information */
|
|
+ if (!(host_model = LoongArchModelFromCPU(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 = LoongArchModelCopy(host_model);
|
|
+ break;
|
|
+
|
|
+ case VIR_CPU_MODE_CUSTOM:
|
|
+ /* custom:
|
|
+ * look up guest CPU information */
|
|
+ guest_model = LoongArchModelFromCPU(cpu, map);
|
|
+ break;
|
|
+ }
|
|
+ } else {
|
|
+ /* Other host CPU information */
|
|
+ guest_model = LoongArchModelFromCPU(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 %s"),guest_model->name);
|
|
+ }
|
|
+ ret = VIR_CPU_COMPARE_INCOMPATIBLE;
|
|
+ goto cleanup;
|
|
+ }
|
|
+
|
|
+ if (guestData)
|
|
+ if (!(*guestData = LoongArchMakeCPUData(arch, &guest_model->data)))
|
|
+ goto cleanup;
|
|
+
|
|
+ ret = VIR_CPU_COMPARE_IDENTICAL;
|
|
+
|
|
+ cleanup:
|
|
+ virCPUDefFree(cpu);
|
|
+ LoongArchMapFree(map);
|
|
+ LoongArchModelFree(host_model);
|
|
+ LoongArchModelFree(guest_model);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static virCPUCompareResult
|
|
+virCPULoongArchCompare(virCPUDefPtr host,
|
|
+ virCPUDefPtr 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 = LoongArchCompute(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
|
|
+LoongArchDriverDecode(virCPUDefPtr cpu,
|
|
+ const virCPUData *data,
|
|
+ virDomainCapsCPUModelsPtr models)
|
|
+{
|
|
+ int ret = -1;
|
|
+ LoongArch_map *map;
|
|
+ const LoongArch_model *model;
|
|
+
|
|
+ if (!data || !(map = LoongArchLoadMap()))
|
|
+ return -1;
|
|
+
|
|
+ if (!(model = LoongArchModelFindPrid(map, data->data.loongarch.prid[0].value))) {
|
|
+ virReportError(VIR_ERR_OPERATION_FAILED,
|
|
+ _("Cannot find CPU model with Prid 0x%08x"),
|
|
+ data->data.loongarch.prid[0].value);
|
|
+ goto cleanup;
|
|
+ }
|
|
+
|
|
+ if (!virCPUModelIsAllowed(model->name, models)) {
|
|
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
+ _("CPU model %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:
|
|
+ LoongArchMapFree(map);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static void
|
|
+virCPULoongArchDataFree(virCPUDataPtr data)
|
|
+{
|
|
+ if (!data)
|
|
+ return;
|
|
+
|
|
+ LoongArchDataClear(&data->data.loongarch);
|
|
+ VIR_FREE(data);
|
|
+}
|
|
+
|
|
+static int
|
|
+virCPULoongArchGetHostPRID(void)
|
|
+{
|
|
+ return 0x14c010;
|
|
+}
|
|
+
|
|
+static int
|
|
+virCPULoongArchGetHost(virCPUDefPtr cpu,
|
|
+ virDomainCapsCPUModelsPtr models)
|
|
+{
|
|
+ virCPUDataPtr cpuData = NULL;
|
|
+ virCPULoongArchData *data;
|
|
+ int ret = -1;
|
|
+
|
|
+ if (!(cpuData = virCPUDataNew(archs[0])))
|
|
+ goto cleanup;
|
|
+
|
|
+ data = &cpuData->data.loongarch;
|
|
+ if (VIR_ALLOC(data->prid) < 0)
|
|
+ goto cleanup;
|
|
+
|
|
+
|
|
+ data->len = 1;
|
|
+
|
|
+ data->prid[0].value = virCPULoongArchGetHostPRID();
|
|
+ data->prid[0].mask = 0xffff00ul;
|
|
+
|
|
+ ret = LoongArchDriverDecode(cpu, cpuData, models);
|
|
+
|
|
+ cleanup:
|
|
+ virCPULoongArchDataFree(cpuData);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+
|
|
+static int
|
|
+virCPULoongArchUpdate(virCPUDefPtr guest,
|
|
+ const virCPUDef *host ATTRIBUTE_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 virCPUDefPtr
|
|
+LoongArchDriverBaseline(virCPUDefPtr *cpus,
|
|
+ unsigned int ncpus,
|
|
+ virDomainCapsCPUModelsPtr models ATTRIBUTE_UNUSED,
|
|
+ const char **features ATTRIBUTE_UNUSED,
|
|
+ bool migratable ATTRIBUTE_UNUSED)
|
|
+{
|
|
+ LoongArch_map *map;
|
|
+ const LoongArch_model *model;
|
|
+ const LoongArch_vendor *vendor = NULL;
|
|
+ virCPUDefPtr cpu = NULL;
|
|
+ size_t i;
|
|
+
|
|
+ if (!(map = LoongArchLoadMap()))
|
|
+ goto error;
|
|
+
|
|
+ if (!(model = LoongArchModelFind(map, cpus[0]->model))) {
|
|
+ virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
+ _("Unknown CPU model %s"), cpus[0]->model);
|
|
+ goto error;
|
|
+ }
|
|
+
|
|
+ for (i = 0; i < ncpus; i++) {
|
|
+ const LoongArch_vendor *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 = LoongArchVendorFind(map, cpus[i]->vendor))) {
|
|
+ virReportError(VIR_ERR_OPERATION_FAILED,
|
|
+ _("Unknown CPU vendor %s"), cpus[i]->vendor);
|
|
+ goto error;
|
|
+ }
|
|
+
|
|
+ if (model->vendor) {
|
|
+ if (model->vendor != vnd) {
|
|
+ virReportError(VIR_ERR_OPERATION_FAILED,
|
|
+ _("CPU vendor %s of model %s differs from "
|
|
+ "vendor %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:
|
|
+ LoongArchMapFree(map);
|
|
+ return cpu;
|
|
+
|
|
+ error:
|
|
+ virCPUDefFree(cpu);
|
|
+ cpu = NULL;
|
|
+ goto cleanup;
|
|
+}
|
|
+
|
|
+static int
|
|
+virCPULoongArchDriverGetModels(char ***models)
|
|
+{
|
|
+ LoongArch_map *map;
|
|
+ size_t i;
|
|
+ int ret = -1;
|
|
+
|
|
+ if (!(map = LoongArchLoadMap())) {
|
|
+ goto error;
|
|
+ }
|
|
+
|
|
+ if (models) {
|
|
+ if (VIR_ALLOC_N(*models, map->nmodels + 1) < 0)
|
|
+ goto error;
|
|
+
|
|
+ for (i = 0; i < map->nmodels; i++) {
|
|
+ (*models)[i] = g_strdup(map->models[i]->name);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ ret = map->nmodels;
|
|
+
|
|
+ cleanup:
|
|
+ LoongArchMapFree(map);
|
|
+ return ret;
|
|
+
|
|
+ error:
|
|
+ if (models) {
|
|
+ virStringListFree(*models);
|
|
+ *models = NULL;
|
|
+ }
|
|
+ goto cleanup;
|
|
+}
|
|
+
|
|
+struct cpuArchDriver cpuDriverLoongArch = {
|
|
+ .name = "LoongArch",
|
|
+ .arch = archs,
|
|
+ .narch = G_N_ELEMENTS(archs),
|
|
+ .compare = virCPULoongArchCompare,
|
|
+ .decode = LoongArchDriverDecode,
|
|
+ .encode = NULL,
|
|
+ .dataFree = virCPULoongArchDataFree,
|
|
+ .getHost = virCPULoongArchGetHost,
|
|
+ .baseline = LoongArchDriverBaseline,
|
|
+ .update = virCPULoongArchUpdate,
|
|
+ .getModels = virCPULoongArchDriverGetModels,
|
|
+};
|
|
diff --git a/src/cpu/cpu_loongarch.h b/src/cpu/cpu_loongarch.h
|
|
new file mode 100644
|
|
index 0000000000..304af628d4
|
|
--- /dev/null
|
|
+++ b/src/cpu/cpu_loongarch.h
|
|
@@ -0,0 +1,28 @@
|
|
+/*
|
|
+ * 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/>.
|
|
+ */
|
|
+
|
|
+#ifndef __VIR_CPU_LOONGARCH_H__
|
|
+# define __VIR_CPU_LOONGARCH_H__
|
|
+
|
|
+# include "cpu.h"
|
|
+
|
|
+extern struct cpuArchDriver cpuDriverLoongArch;
|
|
+
|
|
+#endif /* __VIR_CPU_LOONGARCH_H__ */
|
|
diff --git a/src/cpu/cpu_loongarch_data.h b/src/cpu/cpu_loongarch_data.h
|
|
new file mode 100644
|
|
index 0000000000..c640a0c6b4
|
|
--- /dev/null
|
|
+++ b/src/cpu/cpu_loongarch_data.h
|
|
@@ -0,0 +1,40 @@
|
|
+/*
|
|
+ * 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/>.
|
|
+ */
|
|
+
|
|
+#ifndef __VIR_CPU_LOONGARCH_DATA_H__
|
|
+# define __VIR_CPU_LOONGARCH_DATA_H__
|
|
+
|
|
+# 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;
|
|
+};
|
|
+
|
|
+#endif /* __VIR_CPU_MIPS64_DATA_H__ */
|
|
diff --git a/src/qemu/qemu_capabilities.c b/src/qemu/qemu_capabilities.c
|
|
index 2e9f2025ba..0c3eb148b2 100644
|
|
--- a/src/qemu/qemu_capabilities.c
|
|
+++ b/src/qemu/qemu_capabilities.c
|
|
@@ -2692,6 +2692,7 @@ static const char *preferredMachines[] =
|
|
"sim", /* VIR_ARCH_XTENSAEB */
|
|
|
|
"core3", /* VIR_ARCH_SW_64 */
|
|
+ "loongson7a", /* 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 70835e4efd..37dac3694b 100644
|
|
--- a/src/qemu/qemu_domain.c
|
|
+++ b/src/qemu/qemu_domain.c
|
|
@@ -4361,6 +4361,10 @@ qemuDomainDefAddDefaultDevices(virDomainDefPtr def,
|
|
addPCIeRoot = 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 653136cc73..decdbdd7ac 100644
|
|
--- a/src/util/virarch.c
|
|
+++ b/src/util/virarch.c
|
|
@@ -85,6 +85,7 @@ static const struct virArchData {
|
|
{ "xtensaeb", 32, VIR_ARCH_BIG_ENDIAN },
|
|
|
|
{ "sw_64", 64, VIR_ARCH_LITTLE_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 5eb146eb1b..a7834ae799 100644
|
|
--- a/src/util/virarch.h
|
|
+++ b/src/util/virarch.h
|
|
@@ -70,6 +70,7 @@ typedef enum {
|
|
VIR_ARCH_XTENSAEB, /* XTensa 32 BE http://en.wikipedia.org/wiki/Xtensa#Processor_Cores */
|
|
|
|
VIR_ARCH_SW_64, /* SW64 64 LE XHB*/
|
|
+ VIR_ARCH_LOONGARCH64, /* LoongArch 64 LE */
|
|
|
|
VIR_ARCH_LAST,
|
|
} virArch;
|
|
@@ -99,6 +100,8 @@ typedef enum {
|
|
|
|
#define ARCH_IS_SW64(arch) ((arch) == VIR_ARCH_SW_64)
|
|
|
|
+#define ARCH_IS_LOONGARCH(arch) ((arch) == VIR_ARCH_LOONGARCH64)
|
|
+
|
|
typedef enum {
|
|
VIR_ARCH_LITTLE_ENDIAN,
|
|
VIR_ARCH_BIG_ENDIAN,
|
|
--
|
|
2.25.1
|
|
|