[libvirt PATCH v3] Add RV64 support

Yu Gu guyu2876 at gmail.com
Tue Nov 29 04:52:41 UTC 2022


This patch provides RV64 support for the RISC-V architecture.

Signed-off-by: Yu Gu <guyu2876 at gmail.com>
---
 po/POTFILES                     |   1 +
 src/cpu/cpu.c                   |   2 +
 src/cpu/cpu.h                   |   2 +
 src/cpu/cpu_riscv64.c           | 116 ++++++++++++++++++++++++
 src/cpu/cpu_riscv64.h           |  25 ++++++
 src/cpu/cpu_riscv64_data.h      |  37 ++++++++
 src/cpu/meson.build             |   1 +
 src/cpu_map/index.xml           |   4 +
 src/cpu_map/meson.build         |   1 +
 src/cpu_map/riscv64_vendors.xml |   3 +
 src/util/virarch.c              |   2 +
 src/util/virhostcpu.c           |   2 +-
 src/util/virsysinfo.c           | 150 ++++++++++++++++++++++++++++++++
 src/util/virsysinfopriv.h       |   3 +
 14 files changed, 348 insertions(+), 1 deletion(-)
 create mode 100644 src/cpu/cpu_riscv64.c
 create mode 100644 src/cpu/cpu_riscv64.h
 create mode 100644 src/cpu/cpu_riscv64_data.h
 create mode 100644 src/cpu_map/riscv64_vendors.xml

diff --git a/po/POTFILES b/po/POTFILES
index 169e2a41dc..a52795e7c1 100644
--- a/po/POTFILES
+++ b/po/POTFILES
@@ -72,6 +72,7 @@ src/cpu/cpu_map.c
 src/cpu/cpu_ppc64.c
 src/cpu/cpu_s390.c
 src/cpu/cpu_x86.c
+src/cpu/cpu_riscv64.c
 src/datatypes.c
 src/driver.c
 src/esx/esx_driver.c
diff --git a/src/cpu/cpu.c b/src/cpu/cpu.c
index d97ef5e873..8fdc42e719 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_riscv64.h"
 #include "capabilities.h"
 
 
@@ -39,6 +40,7 @@ static struct cpuArchDriver *drivers[] = {
     &cpuDriverPPC64,
     &cpuDriverS390,
     &cpuDriverArm,
+    &cpuDriverRISCV64,
 };
 
 
diff --git a/src/cpu/cpu.h b/src/cpu/cpu.h
index 41a62ce486..6e0a06fce4 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_riscv64_data.h"
 
 
 typedef struct _virCPUData virCPUData;
@@ -36,6 +37,7 @@ struct _virCPUData {
         virCPUx86Data x86;
         virCPUppc64Data ppc64;
         virCPUarmData arm;
+        virCPUriscv64Data riscv64;
         /* generic driver needs no data */
     } data;
 };
diff --git a/src/cpu/cpu_riscv64.c b/src/cpu/cpu_riscv64.c
new file mode 100644
index 0000000000..9b90ae87d5
--- /dev/null
+++ b/src/cpu/cpu_riscv64.c
@@ -0,0 +1,116 @@
+/*
+ * cpu_riscv64.c: CPU driver for riscv64(x) CPUs
+ *
+ * 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 "cpu.h"
+
+#define VIR_FROM_THIS VIR_FROM_CPU
+
+static const virArch archs[] = { VIR_ARCH_RISCV64 };
+
+static virCPUCompareResult
+virCPUriscv64Compare(virCPUDef *host G_GNUC_UNUSED,
+                     virCPUDef *cpu G_GNUC_UNUSED,
+                     bool failMessages G_GNUC_UNUSED)
+{
+    /* riscv64 relies on QEMU to perform all runability checking. Return
+     * VIR_CPU_COMPARE_IDENTICAL to bypass Libvirt checking.
+     */
+    return VIR_CPU_COMPARE_IDENTICAL;
+}
+
+static int
+virCPUriscv64Update(virCPUDef *guest,
+                    const virCPUDef *host,
+                    bool relative)
+{
+    g_autoptr(virCPUDef) updated = NULL;
+    size_t i;
+
+    if (!relative)
+        return 0;
+
+    if (guest->mode == VIR_CPU_MODE_CUSTOM) {
+        if (guest->match == VIR_CPU_MATCH_MINIMUM) {
+            virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+                           _("match mode %s not supported"),
+                           virCPUMatchTypeToString(guest->match));
+        } else {
+            virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+                           _("optional CPU features are not supported"));
+        }
+        return -1;
+    }
+
+    if (!host) {
+        virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+                       _("unknown host CPU model"));
+        return -1;
+    }
+
+    if (!(updated = virCPUDefCopyWithoutModel(guest)))
+        return -1;
+
+    updated->mode = VIR_CPU_MODE_CUSTOM;
+    if (virCPUDefCopyModel(updated, host, true) < 0)
+        return -1;
+
+    for (i = 0; i < guest->nfeatures; i++) {
+       if (virCPUDefUpdateFeature(updated,
+                                  guest->features[i].name,
+                                  guest->features[i].policy) < 0)
+           return -1;
+    }
+
+    virCPUDefStealModel(guest, updated, false);
+    guest->mode = VIR_CPU_MODE_CUSTOM;
+    guest->match = VIR_CPU_MATCH_EXACT;
+
+    return 0;
+}
+
+
+static int
+virCPUriscv64ValidateFeatures(virCPUDef *cpu)
+{
+    size_t i;
+
+    for (i = 0; i < cpu->nfeatures; i++) {
+        if (cpu->features[i].policy == VIR_CPU_FEATURE_OPTIONAL) {
+            virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+                           _("only cpu feature policies 'require' and 'disable' are supported for %s"),
+                           cpu->features[i].name);
+            return -1;
+        }
+    }
+
+    return 0;
+}
+
+struct cpuArchDriver cpuDriverRISCV64 = {
+    .name = "riscv64",
+    .arch = archs,
+    .narch = G_N_ELEMENTS(archs),
+    .compare    = virCPUriscv64Compare,
+    .decode     = NULL,
+    .encode     = NULL,
+    .baseline   = NULL,
+    .update     = virCPUriscv64Update,
+    .validateFeatures = virCPUriscv64ValidateFeatures,
+};
diff --git a/src/cpu/cpu_riscv64.h b/src/cpu/cpu_riscv64.h
new file mode 100644
index 0000000000..2ab43a9f95
--- /dev/null
+++ b/src/cpu/cpu_riscv64.h
@@ -0,0 +1,25 @@
+/*
+ * cpu_riscv64.h: CPU driver for 64-bit RISC-V CPUs
+ *
+ * Copyright (C) Copyright (C) IBM Corporation, 2010
+ *
+ * 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 cpuDriverRISCV64;
diff --git a/src/cpu/cpu_riscv64_data.h b/src/cpu/cpu_riscv64_data.h
new file mode 100644
index 0000000000..71c9dbf832
--- /dev/null
+++ b/src/cpu/cpu_riscv64_data.h
@@ -0,0 +1,37 @@
+/*
+ * cpu_riscv64_data.h: 64-bit riscv64 CPU specific data
+ *
+ * Copyright (C) 2012 IBM Corporation.
+ *
+ * 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 _virCPUriscv64Prid virCPUriscv64Prid;
+struct _virCPUriscv64Prid {
+    uint32_t value;
+    uint32_t mask;
+};
+
+# define VIR_CPU_riscv64_DATA_INIT { 0 }
+
+typedef struct _virCPUriscv64Data virCPUriscv64Data;
+struct _virCPUriscv64Data {
+    size_t len;
+    virCPUriscv64Prid *prid;
+};
diff --git a/src/cpu/meson.build b/src/cpu/meson.build
index b4ad95e46d..eba1d45743 100644
--- a/src/cpu/meson.build
+++ b/src/cpu/meson.build
@@ -5,6 +5,7 @@ cpu_sources = [
   'cpu_ppc64.c',
   'cpu_s390.c',
   'cpu_x86.c',
+  'cpu_riscv64.c',
 ]
 
 cpu_lib = static_library(
diff --git a/src/cpu_map/index.xml b/src/cpu_map/index.xml
index d533a28865..f2c4b1c62a 100644
--- a/src/cpu_map/index.xml
+++ b/src/cpu_map/index.xml
@@ -89,6 +89,10 @@
     <include filename='ppc64_POWERPC_e6500.xml'/>
   </arch>
 
+  <arch name='riscv64'>
+    <include filename='riscv64_vendors.xml'/>
+  </arch>
+
   <arch name='arm'>
     <include filename='arm_vendors.xml'/>
     <include filename='arm_features.xml'/>
diff --git a/src/cpu_map/meson.build b/src/cpu_map/meson.build
index 99264289e2..1a02df8268 100644
--- a/src/cpu_map/meson.build
+++ b/src/cpu_map/meson.build
@@ -19,6 +19,7 @@ cpumap_data = [
   'ppc64_POWERPC_e5500.xml',
   'ppc64_POWERPC_e6500.xml',
   'ppc64_vendors.xml',
+  'riscv64_vendors.xml',
   'x86_486.xml',
   'x86_athlon.xml',
   'x86_Broadwell-IBRS.xml',
diff --git a/src/cpu_map/riscv64_vendors.xml b/src/cpu_map/riscv64_vendors.xml
new file mode 100644
index 0000000000..24476bd20b
--- /dev/null
+++ b/src/cpu_map/riscv64_vendors.xml
@@ -0,0 +1,3 @@
+<cpus>
+  <vendor name='RISC-V'/>
+</cpus>
diff --git a/src/util/virarch.c b/src/util/virarch.c
index 2134dd6a9d..3d14ecd193 100644
--- a/src/util/virarch.c
+++ b/src/util/virarch.c
@@ -190,6 +190,8 @@ virArch virArchFromHost(void)
         return VIR_ARCH_ALPHA;
     case PROCESSOR_ARCHITECTURE_PPC:
         return VIR_ARCH_PPC;
+    case PROCESSOR_ARCHITECTURE_RISCV:
+        return VIR_ARCH_RISCV64;
     case PROCESSOR_ARCHITECTURE_SHX:
         return VIR_ARCH_SH4;
     case PROCESSOR_ARCHITECTURE_ARM:
diff --git a/src/util/virhostcpu.c b/src/util/virhostcpu.c
index c1e8dc8078..08c2290f00 100644
--- a/src/util/virhostcpu.c
+++ b/src/util/virhostcpu.c
@@ -544,7 +544,7 @@ virHostCPUParseFrequency(FILE *cpuinfo,
     char line[1024];
 
     /* No sensible way to retrieve CPU frequency */
-    if (ARCH_IS_ARM(arch))
+    if (ARCH_IS_ARM(arch) || ARCH_IS_RISCV(arch))
         return 0;
 
     if (ARCH_IS_X86(arch))
diff --git a/src/util/virsysinfo.c b/src/util/virsysinfo.c
index 376d5d4816..afa480ead4 100644
--- a/src/util/virsysinfo.c
+++ b/src/util/virsysinfo.c
@@ -624,6 +624,154 @@ virSysinfoReadS390(void)
 }
 
 
+static const char *
+virSysinfoParseRISCVDelimited(const char *base, const char *name, char **value,
+                             char delim1, char delim2)
+{
+    const char *start;
+    const char *end;
+
+    if (delim1 != delim2 &&
+        (start = strstr(base, name)) &&
+        (start = strchr(start, delim1))) {
+        start += 1;
+        end = strchr(start, delim2);
+        if (!end)
+            end = start + strlen(start);
+        virSkipSpaces(&start);
+        *value = g_strndup(start, end - start);
+        virTrimSpaces(*value, NULL);
+        return end;
+    }
+    return NULL;
+}
+
+static const char *
+virSysinfoParseRISCVLine(const char *base, const char *name, char **value)
+{
+    return virSysinfoParseRISCVDelimited(base, name, value, ':', '\n');
+}
+
+static int
+virSysinfoParseRISCVSystem(const char *base, virSysinfoSystemDef **sysdef)
+{
+    int ret = -1;
+    virSysinfoSystemDef *def;
+
+    def = g_new0(virSysinfoSystemDef, 1);
+
+    if (!virSysinfoParseRISCVLine(base, "Manufacturer", &def->manufacturer))
+        goto cleanup;
+
+    if (!virSysinfoParseRISCVLine(base, "Type", &def->family))
+        goto cleanup;
+    def->manufacturer = g_strndup("Virt-RISC-V", sizeof("Virt RISC-V"));
+
+    if (!def->manufacturer && !def->product && !def->version &&
+        !def->serial && !def->uuid && !def->sku && !def->family) {
+        g_clear_pointer(&def, virSysinfoSystemDefFree);
+    }
+
+    *sysdef = g_steal_pointer(&def);
+    ret = 0;
+ cleanup:
+    virSysinfoSystemDefFree(def);
+    return ret;
+}
+
+static int
+virSysinfoParseRISCVProcessor(const char *base, virSysinfoDef *ret)
+{
+    const char *tmp_base;
+    char *manufacturer = NULL;
+    char *procline = NULL;
+    char *ncpu = NULL;
+    int result = -1;
+    virSysinfoProcessorDef *processor;
+
+    if (!(tmp_base = virSysinfoParseRISCVLine(base, "uarch", &manufacturer)))
+        goto error;
+
+    /* Find processor N: line and gather the processor manufacturer,
+       version, serial number, and family */
+    while ((tmp_base = strstr(tmp_base, "processor "))
+           && (tmp_base = virSysinfoParseRISCVLine(tmp_base, "processor ",
+                                                  &procline))) {
+        VIR_EXPAND_N(ret->processor, ret->nprocessor, 1);
+        processor = &ret->processor[ret->nprocessor - 1];
+        processor->processor_manufacturer = g_strdup(manufacturer);
+
+        VIR_FREE(procline);
+    }
+
+    /* now, for each processor found, extract the frequency information */
+    tmp_base = base;
+
+    while ((tmp_base = strstr(tmp_base, "cpu number")) &&
+           (tmp_base = virSysinfoParseRISCVLine(tmp_base, "cpu number", &ncpu))) {
+        unsigned int n;
+        char *mhz = NULL;
+
+        if (virStrToLong_uip(ncpu, NULL, 10, &n) < 0)
+            goto error;
+
+        if (n >= ret->nprocessor) {
+            VIR_DEBUG("CPU number '%u' out of range", n);
+            goto cleanup;
+        }
+
+        if (!(tmp_base = strstr(tmp_base, "cpu MHz static")) ||
+            !virSysinfoParseRISCVLine(tmp_base, "cpu MHz static", &mhz))
+            goto cleanup;
+
+        ret->processor[n].processor_max_speed = mhz;
+
+        VIR_FREE(ncpu);
+    }
+
+ cleanup:
+    result = 0;
+
+ error:
+    VIR_FREE(manufacturer);
+    VIR_FREE(procline);
+    VIR_FREE(ncpu);
+    return result;
+}
+
+virSysinfoDef *
+virSysinfoReadRISCV(void)
+{
+    g_autoptr(virSysinfoDef) ret = NULL;
+    g_autofree char *outbuf = NULL;
+
+    ret = g_new0(virSysinfoDef, 1);
+
+    /* Gather info from /proc/cpuinfo */
+    if (virFileReadAll(CPUINFO, CPUINFO_FILE_LEN, &outbuf) < 0) {
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       _("Failed to open %s"), CPUINFO);
+        return NULL;
+    }
+
+    if (virSysinfoParseRISCVProcessor(outbuf, ret) < 0)
+        return NULL;
+
+    /* Free buffer before reading next file */
+    VIR_FREE(outbuf);
+
+    /* Gather info from /proc/sysinfo */
+    if (virFileReadAll(SYSINFO, 8192, &outbuf) < 0) {
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       _("Failed to open %s"), SYSINFO);
+        return NULL;
+    }
+
+    if (virSysinfoParseRISCVSystem(outbuf, &ret->system) < 0)
+        return NULL;
+    return g_steal_pointer(&ret);
+}
+
 static int
 virSysinfoParseBIOS(const char *base, virSysinfoBIOSDef **bios)
 {
@@ -1243,6 +1391,8 @@ virSysinfoRead(void)
     return virSysinfoReadPPC();
 #elif defined(__arm__) || defined(__aarch64__)
     return virSysinfoReadARM();
+#elif defined(__riscv) && __riscv_xlen == 64
+    return virSysinfoReadRISCV();
 #elif defined(__s390__) || defined(__s390x__)
     return virSysinfoReadS390();
 #elif !defined(WIN32) && \
diff --git a/src/util/virsysinfopriv.h b/src/util/virsysinfopriv.h
index e47bd3c361..33c8ceeddb 100644
--- a/src/util/virsysinfopriv.h
+++ b/src/util/virsysinfopriv.h
@@ -36,5 +36,8 @@ virSysinfoReadARM(void);
 virSysinfoDef *
 virSysinfoReadS390(void);
 
+virSysinfoDef *
+virSysinfoReadRISCV(void);
+
 virSysinfoDef *
 virSysinfoReadDMI(void);
-- 
2.38.1



More information about the libvir-list mailing list