This commit tests the readonly feature for registers exposed via the
plugin API introduced earlier in the patch series.

Signed-off-by: Florian Hofhammer <[email protected]>
---
 tests/tcg/plugins/meson.build |  2 +-
 tests/tcg/plugins/registers.c | 71 +++++++++++++++++++++++++++++++++++
 2 files changed, 72 insertions(+), 1 deletion(-)
 create mode 100644 tests/tcg/plugins/registers.c

diff --git a/tests/tcg/plugins/meson.build b/tests/tcg/plugins/meson.build
index 561584159e..52831a75d4 100644
--- a/tests/tcg/plugins/meson.build
+++ b/tests/tcg/plugins/meson.build
@@ -1,6 +1,6 @@
 t = []
 if get_option('plugins')
-  foreach i : ['bb', 'discons', 'empty', 'inline', 'insn', 'mem', 'reset', 
'syscall', 'patch']
+  foreach i : ['bb', 'discons', 'empty', 'inline', 'insn', 'mem', 'reset', 
'syscall', 'patch', 'registers']
     if host_os == 'windows'
       t += shared_module(i, files(i + '.c') + 
'../../../contrib/plugins/win32_linker.c',
                         include_directories: '../../../include/qemu',
diff --git a/tests/tcg/plugins/registers.c b/tests/tcg/plugins/registers.c
new file mode 100644
index 0000000000..b738971551
--- /dev/null
+++ b/tests/tcg/plugins/registers.c
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2026, Florian Hofhammer <[email protected]>
+ *
+ * License: GNU GPL, version 2 or later.
+ *   See the COPYING file in the top-level directory.
+ */
+#include "glib.h"
+#include <inttypes.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdio.h>
+
+#include <qemu-plugin.h>
+
+QEMU_PLUGIN_EXPORT int qemu_plugin_version = QEMU_PLUGIN_VERSION;
+
+/*
+ * This callback is called when a vCPU is initialized. It tests whether we
+ * successfully read from a register and write value back to it. It also tests
+ * that read-only registers cannot be written to, i.e., we can read a read-only
+ * register but writing to it fails.
+ */
+static void vcpu_init_cb(qemu_plugin_id_t id, unsigned int vcpu_index)
+{
+    g_autoptr(GArray) regs = qemu_plugin_get_registers();
+    g_autoptr(GByteArray) buf = g_byte_array_sized_new(0);
+    int sz = 0;
+
+    /* Make sure we can read and write an arbitrary register */
+    qemu_plugin_reg_descriptor *reg_desc = &g_array_index(regs,
+            qemu_plugin_reg_descriptor, 0);
+    g_assert(reg_desc->is_readonly == false);
+    sz = qemu_plugin_read_register(reg_desc->handle, buf);
+    g_assert(sz > 0);
+    g_assert(sz == buf->len);
+    sz = qemu_plugin_write_register(reg_desc->handle, buf);
+    g_assert(sz > 0);
+    g_assert(sz == buf->len);
+
+    /*
+     * Reset the buffer and find a read-only register. On each architecture, at
+     * least the PC should be read-only because it's only supposed to be
+     * modified via the qemu_plugin_set_pc() function.
+     */
+    g_byte_array_set_size(buf, 0);
+    for (size_t i = 0; i < regs->len; i++) {
+        reg_desc = &g_array_index(regs, qemu_plugin_reg_descriptor, i);
+        if (reg_desc->is_readonly) {
+            sz = qemu_plugin_read_register(reg_desc->handle, buf);
+            g_assert(sz > 0);
+            g_assert(sz == buf->len);
+            break;
+        } else {
+            reg_desc = NULL;
+        }
+    }
+    /* Make sure there is a read-only register and we cannot write to it */
+    g_assert(reg_desc != NULL);
+    sz = qemu_plugin_write_register(reg_desc->handle, buf);
+    g_assert(sz == -1);
+}
+
+QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id,
+                                           const qemu_info_t *info,
+                                           int argc, char **argv)
+{
+    qemu_plugin_register_vcpu_init_cb(id, vcpu_init_cb);
+    return 0;
+}
-- 
2.52.0


Reply via email to