From: lixianglai <lixianglai(a)loongson.cn>
Add loongarch cpu support, Define new cpu type 'loongarch64'
and implement it's driver functions.
Signed-off-by: lixianglai <lixianglai(a)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