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(&copy->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

