[PATCH 4/5] target/riscv: Add arch=PROFILE to configure CPU using RISC-V profiles

Kito Cheng posted 5 patches 1 month ago
Maintainers: Palmer Dabbelt <palmer@dabbelt.com>, Alistair Francis <alistair.francis@wdc.com>, Weiwei Li <liwei1518@gmail.com>, Daniel Henrique Barboza <dbarboza@ventanamicro.com>, Liu Zhiwei <zhiwei_liu@linux.alibaba.com>
[PATCH 4/5] target/riscv: Add arch=PROFILE to configure CPU using RISC-V profiles
Posted by Kito Cheng 1 month ago
Add support for configuring RISC-V CPUs using standard RISC-V profiles
with the arch= property. The format is:

  arch=<profile-name>[_extension]*

Examples:
  -cpu rv64,arch=rva23u64
  -cpu rv64,arch=rva22s64
  -cpu rv64,arch=rva23u64_zbkb_zkne

Key features:
- Profiles are detected by prefix matching (case-insensitive)
- Additional extensions can be appended after the profile name
- Profiles are only available for 64-bit CPUs

Available profiles:
- rva22u64 - RVA22 User-mode profile
- rva22s64 - RVA22 Supervisor-mode profile
- rva23u64 - RVA23 User-mode profile (includes mandatory vector)
- rva23s64 - RVA23 Supervisor-mode profile

Profiles automatically enable all mandatory extensions defined by the
RISC-V profile specification. Additional extensions can be appended
after the profile name using underscores (following the RISC-V toolchain
convention).

The arch=help command now lists available profiles in addition to
extensions.

Signed-off-by: Kito Cheng <kito.cheng@sifive.com>
---
 docs/system/target-riscv.rst              |  24 +++++
 target/riscv/cpu.c                        |   8 ++
 target/riscv/tcg/tcg-cpu.c                | 103 +++++++++++++++++++++-
 tests/functional/riscv64/test_cpu_arch.py |  72 +++++++++++++++
 4 files changed, 203 insertions(+), 4 deletions(-)

diff --git a/docs/system/target-riscv.rst b/docs/system/target-riscv.rst
index a16c17a22c2..12807974a8a 100644
--- a/docs/system/target-riscv.rst
+++ b/docs/system/target-riscv.rst
@@ -167,6 +167,30 @@ extensions:
 
     $ qemu-riscv64 -cpu rv64,arch=rv64gc_zba_zbb,arch=dump /bin/true
 
+* ``arch=<PROFILE-NAME>[_extension]*``
+
+  Configure the CPU using a standard RISC-V profile, optionally with additional
+  extensions. Profiles define sets of mandatory extensions for specific use
+  cases. Available profiles (64-bit only):
+
+  - ``rva22u64`` - RVA22 User-mode profile
+  - ``rva22s64`` - RVA22 Supervisor-mode profile
+  - ``rva23u64`` - RVA23 User-mode profile (includes mandatory vector)
+  - ``rva23s64`` - RVA23 Supervisor-mode profile
+
+  Examples::
+
+    $ qemu-riscv64 -cpu rv64,arch=rva23u64 /bin/true
+    $ qemu-riscv64 -cpu rv64,arch=rva22s64,arch=dump /bin/true
+    $ qemu-riscv64 -cpu rv64,arch=rva23u64_zbkb_zkne /bin/true
+
+  Profiles automatically enable all mandatory extensions defined by the RISC-V
+  profile specification. For example, RVA23 profiles enable the vector extension
+  (V) which was optional in RVA22. Additional extensions can be appended after
+  the profile name using underscores (following the RISC-V toolchain convention).
+
+  Use ``arch=help`` to see all available profiles.
+
 Privilege-implied extensions
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c
index b1d8438cd14..092635b1050 100644
--- a/target/riscv/cpu.c
+++ b/target/riscv/cpu.c
@@ -1028,7 +1028,15 @@ G_NORETURN void riscv_cpu_list_supported_extensions(void)
     riscv_cpu_help_multiext("Experimental Extensions",
                             riscv_cpu_experimental_exts);
 
+    /* Print available profiles */
+    qemu_printf("Profiles (64-bit only):\n");
+    for (int i = 0; riscv_profiles[i] != NULL; i++) {
+        qemu_printf("  %s\n", riscv_profiles[i]->name);
+    }
+    qemu_printf("\n");
+
     qemu_printf("Use '-cpu <cpu>,<ext>=true' to enable an extension.\n");
+    qemu_printf("Use '-cpu <cpu>,arch=<profile>' to enable a profile.\n");
     qemu_printf("Use '-cpu <cpu>,arch=dump' to show current configuration.\n");
 
     exit(0);
diff --git a/target/riscv/tcg/tcg-cpu.c b/target/riscv/tcg/tcg-cpu.c
index 87f1a5c9c73..aa947337cf1 100644
--- a/target/riscv/tcg/tcg-cpu.c
+++ b/target/riscv/tcg/tcg-cpu.c
@@ -1725,9 +1725,99 @@ static bool riscv_cpu_parse_isa_string(RISCVCPU *cpu, const char *isa_str,
     return true;
 }
 
+/*
+ * Find a profile by prefix (case-insensitive).
+ * The profile name must be followed by end of string or underscore.
+ * Returns the profile and sets *end_ptr to point after the profile name.
+ */
+static RISCVCPUProfile *riscv_find_profile_by_prefix(const char *str,
+                                                      const char **end_ptr)
+{
+    for (int i = 0; riscv_profiles[i] != NULL; i++) {
+        size_t len = strlen(riscv_profiles[i]->name);
+        if (g_ascii_strncasecmp(str, riscv_profiles[i]->name, len) == 0) {
+            char next_char = str[len];
+            if (next_char == '\0' || next_char == '_') {
+                if (end_ptr) {
+                    *end_ptr = str + len;
+                }
+                return riscv_profiles[i];
+            }
+        }
+    }
+    return NULL;
+}
+
+/*
+ * Parse profile string with optional extensions.
+ * Format: <profile-name>[_<extension>]*
+ * Examples: rva23u64, rva23u64_zbkb_zkne
+ */
+static bool riscv_cpu_parse_profile_string(RISCVCPU *cpu, const char *str,
+                                           Error **errp)
+{
+    const char *p;
+    RISCVCPUProfile *profile;
+
+    profile = riscv_find_profile_by_prefix(str, &p);
+    if (profile == NULL) {
+        error_setg(errp, "unknown profile in '%s'", str);
+        return false;
+    }
+
+    /* Check CPU compatibility */
+    if (riscv_cpu_is_vendor(OBJECT(cpu))) {
+        error_setg(errp, "Profile %s is not available for vendor CPUs",
+                   profile->name);
+        return false;
+    }
+    if (riscv_cpu_is_32bit(cpu)) {
+        error_setg(errp, "Profile %s is only available for 64-bit CPUs",
+                   profile->name);
+        return false;
+    }
+
+    /* Enable the profile */
+    profile->user_set = true;
+    riscv_cpu_set_profile(cpu, profile, true);
+
+    /* Parse additional extensions after the profile name */
+    while (*p == '_') {
+        p++;  /* Skip underscore */
+
+        if (*p == '\0') {
+            break;  /* Trailing underscore is ok */
+        }
+
+        /* Find the end of this extension name */
+        const char *ext_start = p;
+        while (*p && *p != '_') {
+            p++;
+        }
+
+        /* Extract extension name */
+        size_t ext_len = p - ext_start;
+        g_autofree char *ext_name = g_strndup(ext_start, ext_len);
+
+        /* Look up the extension */
+        const RISCVIsaExtData *edata = riscv_find_ext_data(ext_name);
+        if (edata == NULL) {
+            error_setg(errp, "unknown extension '%s' in profile string",
+                       ext_name);
+            return false;
+        }
+
+        /* Enable the extension */
+        isa_ext_update_enabled(cpu, edata->ext_enable_offset, true);
+    }
+
+    return true;
+}
+
 /*
  * arch= property handler for ISA configuration.
- * Supports: dump, help, or an ISA string like rv64gc_zba_zbb.
+ * Supports: dump, help, ISA string (rv64gc_zba_zbb),
+ * or profile with optional extensions (rva23u64, rva23u64_zbkb_zkne).
  */
 static void cpu_set_arch(Object *obj, Visitor *v, const char *name,
                          void *opaque, Error **errp)
@@ -1743,13 +1833,17 @@ static void cpu_set_arch(Object *obj, Visitor *v, const char *name,
         cpu->cfg.arch_dump_requested = true;
     } else if (g_strcmp0(value, "help") == 0) {
         riscv_cpu_list_supported_extensions();
+    } else if (riscv_find_profile_by_prefix(value, NULL) != NULL) {
+        /* Parse as profile with optional extensions */
+        riscv_cpu_parse_profile_string(cpu, value, errp);
     } else if (g_ascii_strncasecmp(value, "rv", 2) == 0) {
         /* Parse as ISA string */
         riscv_cpu_parse_isa_string(cpu, value, errp);
     } else {
         error_setg(errp, "unknown arch option '%s'. "
-                   "Supported options: dump, help, or ISA string (e.g., "
-                   "rv64gc_zba_zbb)", value);
+                   "Supported options: dump, help, ISA string (e.g., "
+                   "rv64gc_zba_zbb), or profile (e.g., rva23u64, "
+                   "rva23u64_zbkb)", value);
     }
 }
 
@@ -1761,7 +1855,8 @@ static void riscv_cpu_add_arch_property(Object *obj)
     object_property_set_description(obj, "arch",
         "ISA configuration (write-only). "
         "Use 'help' to list extensions, 'dump' to show current config, "
-        "or provide an ISA string (e.g., rv64gc_zba_zbb).");
+        "provide an ISA string (e.g., rv64gc_zba_zbb), "
+        "or use a profile (e.g., rva23u64).");
 }
 
 /*
diff --git a/tests/functional/riscv64/test_cpu_arch.py b/tests/functional/riscv64/test_cpu_arch.py
index 204247cdb73..c12e1c7fce4 100644
--- a/tests/functional/riscv64/test_cpu_arch.py
+++ b/tests/functional/riscv64/test_cpu_arch.py
@@ -266,6 +266,78 @@ def test_arch_isa_string_underscore_separated_single(self):
         self.assertRegex(res.stdout, r'd\s+enabled')
         self.assertRegex(res.stdout, r'c\s+enabled')
 
+    def test_arch_profile_rva23u64(self):
+        """Test arch=rva23u64 enables RVA23 profile extensions"""
+        res = self.run_qemu('rv64,arch=rva23u64,arch=dump')
+
+        self.assertEqual(res.returncode, 0)
+
+        # RVA23U64 mandates vector extension
+        self.assertRegex(res.stdout, r'\bv\s+enabled')
+
+        # RVA23U64 mandates these extensions
+        self.assertRegex(res.stdout, r'zicond\s+enabled')
+        self.assertRegex(res.stdout, r'zimop\s+enabled')
+        self.assertRegex(res.stdout, r'zcmop\s+enabled')
+        self.assertRegex(res.stdout, r'zcb\s+enabled')
+        self.assertRegex(res.stdout, r'zfa\s+enabled')
+        self.assertRegex(res.stdout, r'zvbb\s+enabled')
+
+    def test_arch_profile_rva22u64(self):
+        """Test arch=rva22u64 enables RVA22 profile extensions"""
+        res = self.run_qemu('rv64,arch=rva22u64,arch=dump')
+
+        self.assertEqual(res.returncode, 0)
+
+        # RVA22U64 mandates these MISA extensions
+        self.assertRegex(res.stdout, r'\bi\s+enabled')
+        self.assertRegex(res.stdout, r'm\s+enabled')
+        self.assertRegex(res.stdout, r'a\s+enabled')
+        self.assertRegex(res.stdout, r'f\s+enabled')
+        self.assertRegex(res.stdout, r'd\s+enabled')
+        self.assertRegex(res.stdout, r'c\s+enabled')
+
+        # RVA22U64 mandates zicsr and zifencei
+        self.assertRegex(res.stdout, r'zicsr\s+enabled')
+        self.assertRegex(res.stdout, r'zifencei\s+enabled')
+
+    def test_arch_profile_case_insensitive(self):
+        """Test arch=PROFILE is case-insensitive"""
+        res = self.run_qemu('rv64,arch=RVA23U64,arch=dump')
+
+        self.assertEqual(res.returncode, 0)
+        # Should enable vector like lowercase version
+        self.assertRegex(res.stdout, r'\bv\s+enabled')
+
+    def test_arch_help_shows_profiles(self):
+        """Test arch=help lists available profiles"""
+        res = self.run_qemu('rv64,arch=help')
+
+        self.assertEqual(res.returncode, 0)
+        self.assertIn('Profiles', res.stdout)
+        self.assertIn('rva22u64', res.stdout)
+        self.assertIn('rva22s64', res.stdout)
+        self.assertIn('rva23u64', res.stdout)
+        self.assertIn('rva23s64', res.stdout)
+
+    def test_arch_profile_with_extensions(self):
+        """Test arch=PROFILE_EXT enables profile plus additional extensions"""
+        res = self.run_qemu('rv64,arch=rva23u64_zbkb_zkne,arch=dump')
+
+        self.assertEqual(res.returncode, 0)
+        # Profile extensions should be enabled
+        self.assertRegex(res.stdout, r'\bv\s+enabled')
+        # Additional extensions should also be enabled
+        self.assertRegex(res.stdout, r'zbkb\s+enabled')
+        self.assertRegex(res.stdout, r'zkne\s+enabled')
+
+    def test_arch_profile_with_unknown_extension(self):
+        """Test arch=PROFILE_EXT rejects unknown extensions"""
+        res = self.run_qemu('rv64,arch=rva23u64_unknown')
+
+        self.assertNotEqual(res.returncode, 0)
+        self.assertIn("unknown extension 'unknown'", res.stderr)
+
 
 if __name__ == '__main__':
     QemuUserTest.main()
-- 
2.52.0