[PATCH 2/5] target/riscv: Add arch=help to list supported ISA extensions

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 2/5] target/riscv: Add arch=help to list supported ISA extensions
Posted by Kito Cheng 1 month ago
---
 docs/system/target-riscv.rst              | 14 ++++-
 target/riscv/cpu.c                        | 69 +++++++++++++++++++++++
 target/riscv/cpu.h                        |  1 +
 target/riscv/tcg/tcg-cpu.c                |  7 ++-
 tests/functional/riscv32/test_cpu_arch.py | 12 ++++
 tests/functional/riscv64/test_cpu_arch.py | 25 ++++++++
 6 files changed, 124 insertions(+), 4 deletions(-)

diff --git a/docs/system/target-riscv.rst b/docs/system/target-riscv.rst
index 3ec53dbf9e5..9acc51fbc2b 100644
--- a/docs/system/target-riscv.rst
+++ b/docs/system/target-riscv.rst
@@ -90,8 +90,18 @@ Individual ISA extensions can be enabled or disabled using boolean properties::
 The ``arch`` property
 ^^^^^^^^^^^^^^^^^^^^^
 
-The ``arch`` property provides a convenient way to inspect the current ISA
-configuration:
+The ``arch`` property provides convenient ways to discover and inspect ISA
+extensions:
+
+* ``arch=help``
+
+  Print a list of all supported ISA extensions and exit::
+
+    $ qemu-system-riscv64 -M virt -cpu rv64,arch=help
+    $ qemu-riscv64 -cpu rv64,arch=help /bin/true
+
+  This lists standard single-letter extensions (with descriptions), multi-letter
+  extensions, vendor extensions, and experimental extensions.
 
 * ``arch=dump``
 
diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c
index 2886b7ebcdd..b1d8438cd14 100644
--- a/target/riscv/cpu.c
+++ b/target/riscv/cpu.c
@@ -896,6 +896,23 @@ static void riscv_cpu_satp_mode_finalize(RISCVCPU *cpu, Error **errp)
 }
 #endif
 
+/*
+ * Helper function to print single-letter extensions for arch=help.
+ */
+static void riscv_cpu_help_misa_exts(void)
+{
+    qemu_printf("Standard Extensions (single-letter):\n");
+
+    for (int i = 0; misa_bits[i] != 0; i++) {
+        uint32_t bit = misa_bits[i];
+        const char *name = riscv_get_misa_ext_name(bit);
+        const char *desc = riscv_get_misa_ext_description(bit);
+
+        qemu_printf("  %-4s  %s\n", name, desc);
+    }
+    qemu_printf("\n");
+}
+
 static inline const char *riscv_ext_status_str(bool enabled)
 {
     return enabled ? "enabled" : "disabled";
@@ -965,6 +982,58 @@ static void riscv_cpu_dump_priv_implied_exts(RISCVCPU *cpu)
     qemu_printf("\n");
 }
 
+/*
+ * Helper function to print multi-letter extension names for arch=help.
+ * Does not print section header.
+ */
+static void riscv_cpu_help_multiext_entries(const RISCVCPUMultiExtConfig *exts)
+{
+    for (const RISCVCPUMultiExtConfig *prop = exts;
+         prop && prop->name; prop++) {
+        qemu_printf("  %s\n", prop->name);
+    }
+}
+
+/*
+ * Helper function to print multi-letter extensions for arch=help.
+ */
+static void riscv_cpu_help_multiext(const char *title,
+                                    const RISCVCPUMultiExtConfig *exts)
+{
+    qemu_printf("%s:\n", title);
+
+    riscv_cpu_help_multiext_entries(exts);
+    qemu_printf("\n");
+}
+
+/*
+ * Print list of supported ISA extensions and exit.
+ * Called when arch=help is specified.
+ */
+G_NORETURN void riscv_cpu_list_supported_extensions(void)
+{
+    qemu_printf("\n");
+    qemu_printf("Supported RISC-V ISA Extensions\n");
+    qemu_printf("================================\n\n");
+
+    riscv_cpu_help_misa_exts();
+
+    /* Print multi-letter standard extensions (including named features) */
+    qemu_printf("Standard Extensions (multi-letter):\n");
+    riscv_cpu_help_multiext_entries(riscv_cpu_extensions);
+    riscv_cpu_help_multiext_entries(riscv_cpu_named_features);
+    qemu_printf("\n");
+
+    riscv_cpu_help_multiext("Vendor Extensions", riscv_cpu_vendor_exts);
+    riscv_cpu_help_multiext("Experimental Extensions",
+                            riscv_cpu_experimental_exts);
+
+    qemu_printf("Use '-cpu <cpu>,<ext>=true' to enable an extension.\n");
+    qemu_printf("Use '-cpu <cpu>,arch=dump' to show current configuration.\n");
+
+    exit(0);
+}
+
 /*
  * Print detailed ISA configuration and exit.
  * Called when arch=dump is specified.
diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
index 90b3e951053..5c08c2ca4d6 100644
--- a/target/riscv/cpu.h
+++ b/target/riscv/cpu.h
@@ -986,4 +986,5 @@ const char *satp_mode_str(uint8_t satp_mode, bool is_32_bit);
 extern const RISCVCSR th_csr_list[];
 
 const char *priv_spec_to_str(int priv_version);
+G_NORETURN void riscv_cpu_list_supported_extensions(void);
 #endif /* RISCV_CPU_H */
diff --git a/target/riscv/tcg/tcg-cpu.c b/target/riscv/tcg/tcg-cpu.c
index f7187472cd2..c9600a52e1c 100644
--- a/target/riscv/tcg/tcg-cpu.c
+++ b/target/riscv/tcg/tcg-cpu.c
@@ -1567,9 +1567,11 @@ static void cpu_set_arch(Object *obj, Visitor *v, const char *name,
 
     if (g_strcmp0(value, "dump") == 0) {
         cpu->cfg.arch_dump_requested = true;
+    } else if (g_strcmp0(value, "help") == 0) {
+        riscv_cpu_list_supported_extensions();
     } else {
         error_setg(errp, "unknown arch option '%s'. "
-                   "Supported options: dump", value);
+                   "Supported options: dump, help", value);
     }
 }
 
@@ -1579,7 +1581,8 @@ static void riscv_cpu_add_arch_property(Object *obj)
                         NULL, cpu_set_arch,
                         NULL, NULL);
     object_property_set_description(obj, "arch",
-        "ISA configuration string (write-only). Use 'dump' to print ISA config.");
+        "ISA configuration (write-only). "
+        "Use 'help' to list extensions, 'dump' to show current config.");
 }
 
 /*
diff --git a/tests/functional/riscv32/test_cpu_arch.py b/tests/functional/riscv32/test_cpu_arch.py
index 7b2f87cad88..b2042f1e5d8 100644
--- a/tests/functional/riscv32/test_cpu_arch.py
+++ b/tests/functional/riscv32/test_cpu_arch.py
@@ -47,6 +47,18 @@ def test_arch_dump_shows_enabled_extensions(self):
         self.assertRegex(res.stdout, r'd\s+enabled')
         self.assertRegex(res.stdout, r'c\s+enabled')
 
+    def test_arch_help(self):
+        """Test arch=help prints list of supported extensions and exits"""
+        res = self.run_qemu('rv32,arch=help')
+
+        self.assertEqual(res.returncode, 0,
+                         f"arch=help should exit with 0, got {res.returncode}")
+
+        # Check for expected output sections
+        self.assertIn('Supported RISC-V ISA Extensions', res.stdout)
+        self.assertIn('Standard Extensions (single-letter):', res.stdout)
+        self.assertIn('Standard Extensions (multi-letter):', res.stdout)
+
     def test_arch_invalid_option(self):
         """Test invalid arch= option shows error with supported options"""
         res = self.run_qemu('rv32,arch=invalid')
diff --git a/tests/functional/riscv64/test_cpu_arch.py b/tests/functional/riscv64/test_cpu_arch.py
index b0af8991397..4ec807b7276 100644
--- a/tests/functional/riscv64/test_cpu_arch.py
+++ b/tests/functional/riscv64/test_cpu_arch.py
@@ -66,6 +66,31 @@ def test_arch_dump_position_independence(self):
         self.assertRegex(res1.stdout, r'v\s+enabled')
         self.assertRegex(res2.stdout, r'v\s+enabled')
 
+    def test_arch_help(self):
+        """Test arch=help prints list of supported extensions and exits"""
+        res = self.run_qemu('rv64,arch=help')
+
+        self.assertEqual(res.returncode, 0,
+                         f"arch=help should exit with 0, got {res.returncode}")
+
+        # Check for expected output sections
+        self.assertIn('Supported RISC-V ISA Extensions', res.stdout)
+        self.assertIn('Standard Extensions (single-letter):', res.stdout)
+        self.assertIn('Standard Extensions (multi-letter):', res.stdout)
+        self.assertIn('Vendor Extensions:', res.stdout)
+
+    def test_arch_help_shows_extensions(self):
+        """Test arch=help lists common extensions"""
+        res = self.run_qemu('rv64,arch=help')
+
+        # Check single-letter extensions with descriptions
+        self.assertIn('Base integer instruction set', res.stdout)
+        self.assertIn('Vector operations', res.stdout)
+
+        # Check multi-letter extensions are listed
+        self.assertIn('zba', res.stdout)
+        self.assertIn('zbb', res.stdout)
+
     def test_arch_invalid_option(self):
         """Test invalid arch= option shows error with supported options"""
         res = self.run_qemu('rv64,arch=invalid')
-- 
2.52.0