[libvirt PATCH 1/5] Add loongarch cpu support

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



From: lixianglai <lixianglai@xxxxxxxxxxx>

Add loongarch cpu support, Define new cpu type 'loongarch64'
and implement it's driver functions.

Signed-off-by: lixianglai <lixianglai@xxxxxxxxxxx>
---
 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(&copy->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@xxxxxxxxxxxxxxxxx
To unsubscribe send an email to devel-leave@xxxxxxxxxxxxxxxxx




[Index of Archives]     [Virt Tools]     [Libvirt Users]     [Lib OS Info]     [Fedora Users]     [Fedora Desktop]     [Fedora SELinux]     [Big List of Linux Books]     [Yosemite News]     [KDE Users]     [Fedora Tools]

  Powered by Linux