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
© 2016 - 2026 Red Hat, Inc.