All of lore.kernel.org
 help / color / mirror / Atom feed
From: Ruslan Ruslichenko <ruslichenko.r@gmail.com>
To: qemu-devel@nongnu.org
Cc: qemu-arm@nongnu.org, artem_mygaiev@epam.com,
	volodymyr_babchuk@epam.com, alex.bennee@linaro.org,
	peter.maydell@linaro.org, pierrick.bouvier@linaro.org,
	philmd@linaro.org, Ruslan_Ruslichenko@epam.com
Subject: [RFC PATCH 4/9] plugins: Introduce fault injection API and core subsystem
Date: Wed, 18 Mar 2026 11:46:35 +0100	[thread overview]
Message-ID: <20260318104640.239752-5-ruslichenko.r@gmail.com> (raw)
In-Reply-To: <20260318104640.239752-1-ruslichenko.r@gmail.com>

From: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>

The patch adds infrastructure of fault injection API to plugins.
The following capabilities introduced:

- MMIO overrides: Allows plugins to register callbacks that intercept
  MMIO accesses.
- IRQ injection: Provides mechanism to raise or pulse hardware irq's
  directly to primary interrupt controller.
- CPU Excheption injection: Provides API for injecting CPU exceptions.
  As of now only ARM targets supported.
- Custom Fault Registry: Implements a registry allowing QEMU device
  models to expose custom, device-specific fault handlers. Plugins
  can trigger these dynamically by name.

Signed-off-by: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>
---
 include/plugins/qemu-plugin.h |  19 ++++++
 include/qemu/plugin.h         |  39 ++++++++++++
 plugins/api.c                 |  27 ++++++++
 plugins/fault.c               | 116 ++++++++++++++++++++++++++++++++++
 plugins/meson.build           |   1 +
 5 files changed, 202 insertions(+)
 create mode 100644 plugins/fault.c

diff --git a/include/plugins/qemu-plugin.h b/include/plugins/qemu-plugin.h
index a68427536f..96e2787788 100644
--- a/include/plugins/qemu-plugin.h
+++ b/include/plugins/qemu-plugin.h
@@ -1255,6 +1255,25 @@ uint64_t qemu_plugin_get_virtual_clock_ns(void);
 QEMU_PLUGIN_API
 void qemu_plugin_timer_virt_ns(uint64_t time, void (*cb)(void*), void *opaque);
 
+typedef bool (*qemu_plugin_mmio_override_cb_t)(uint64_t hwaddr,
+                                               unsigned size,
+                                               bool is_write,
+                                               uint64_t *value);
+
+QEMU_PLUGIN_API
+void qemu_plugin_register_mmio_override_cb(qemu_plugin_id_t id,
+                                           qemu_plugin_mmio_override_cb_t cb);
+
+QEMU_PLUGIN_API
+void qemu_plugin_inject_irq(int irq_num, int cpu, bool pulse);
+
+QEMU_PLUGIN_API
+void qemu_plugin_inject_exception(int excp_index, uint32_t data);
+
+QEMU_PLUGIN_API
+void qemu_plugin_trigger_custom_fault(const char *fault_name, void *target_data,
+                                      void *fault_data);
+
 #ifdef __cplusplus
 } /* extern "C" */
 #endif
diff --git a/include/qemu/plugin.h b/include/qemu/plugin.h
index ddd77bd82c..4cb01b2125 100644
--- a/include/qemu/plugin.h
+++ b/include/qemu/plugin.h
@@ -43,6 +43,11 @@ get_plugin_meminfo_rw(qemu_plugin_meminfo_t i)
     return i >> 16;
 }
 
+typedef void (*plugin_irq_inject_cb) (void *opaque, int irq,
+                                      int cpu, bool pulse);
+
+typedef void (*plugin_custom_fault_cb)(void *target_data, void *fault_data);
+
 #ifdef CONFIG_PLUGIN
 extern QemuOptsList qemu_plugin_opts;
 
@@ -234,6 +239,27 @@ static inline enum qemu_plugin_cb_flags qemu_plugin_get_cb_flags(void)
     return current_cpu->neg.plugin_cb_flags;
 }
 
+void plugin_register_mmio_override_cb(qemu_plugin_id_t id,
+                                      qemu_plugin_mmio_override_cb_t cb);
+
+bool plugin_mmio_override_cb_invoke(uint64_t hwaddr,
+                                    uint64_t size,
+                                    bool is_write,
+                                    uint64_t* data);
+
+void plugin_register_intc(void *opaque, plugin_irq_inject_cb cb);
+
+void plugin_inject_irq(int irq_num, int cpu, bool pulse);
+
+void plugin_inject_exception(int excp_index, uint32_t data);
+
+void plugin_register_custom_fault(const char *fault_name,
+                                  plugin_custom_fault_cb cb);
+
+void plugin_trigger_custom_fault(const char* fault_name, void *target_data,
+                                 void *fault_data);
+
+
 #else /* !CONFIG_PLUGIN */
 
 static inline void qemu_plugin_add_opts(void)
@@ -324,6 +350,19 @@ static inline void qemu_plugin_user_prefork_lock(void)
 static inline void qemu_plugin_user_postfork(bool is_child)
 { }
 
+static inline bool plugin_mmio_override_cb_invoke(uint64_t hwaddr,
+                                                  uint64_t size,
+                                                  bool is_write,
+                                                  void* data)
+{ return false; }
+
+static void plugin_register_intc(void *opaque, plugin_irq_inject_cb cb)
+{ }
+
+static void plugin_register_custom_fault(const char *fault_name,
+                                         plugin_custom_fault_cb cb)
+{ }
+
 #endif /* !CONFIG_PLUGIN */
 
 #endif /* QEMU_PLUGIN_H */
diff --git a/plugins/api.c b/plugins/api.c
index fa650e1219..0adeaa0bc3 100644
--- a/plugins/api.c
+++ b/plugins/api.c
@@ -687,3 +687,30 @@ void qemu_plugin_timer_virt_ns(uint64_t time, void (*cb)(void*), void *opaque)
 
     timer_mod(data->timer, time);
 }
+
+uint64_t qemu_plugin_get_virtual_clock_ns(void)
+{
+    return qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+}
+
+void qemu_plugin_register_mmio_override_cb(qemu_plugin_id_t id,
+                                           qemu_plugin_mmio_override_cb_t cb)
+{
+    plugin_register_mmio_override_cb(id, cb);
+}
+
+void qemu_plugin_inject_irq(int irq_num, int cpu, bool pulse)
+{
+    plugin_inject_irq(irq_num, cpu, pulse);
+}
+
+void qemu_plugin_inject_exception(int excp_index, uint32_t data)
+{
+    plugin_inject_exception(excp_index, data);
+}
+
+void qemu_plugin_trigger_custom_fault(const char *fault_name,
+                                      void *target_data, void *fault_data)
+{
+    plugin_trigger_custom_fault(fault_name, target_data, fault_data);
+}
diff --git a/plugins/fault.c b/plugins/fault.c
new file mode 100644
index 0000000000..8f7c1e1333
--- /dev/null
+++ b/plugins/fault.c
@@ -0,0 +1,116 @@
+/*
+ * Fault Injection Core Subsystem
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/log.h"
+#include "cpu.h"
+#include "qemu/main-loop.h"
+#include "hw/core/irq.h"
+#include "qemu/plugin.h"
+
+typedef struct {
+    qemu_plugin_id_t id;
+    qemu_plugin_mmio_override_cb_t cb;
+} MMIOOverrideEntry;
+
+static GArray *mmio_callbacks = NULL;
+
+void *intc_opaque;
+static plugin_irq_inject_cb irq_inject_cb = NULL;
+
+static GHashTable *fault_registry = NULL;
+
+void plugin_register_mmio_override_cb(qemu_plugin_id_t id,
+                                      qemu_plugin_mmio_override_cb_t cb)
+{
+    if (!mmio_callbacks) {
+        mmio_callbacks = g_array_new(FALSE, FALSE,
+                        sizeof(MMIOOverrideEntry));
+    }
+
+    MMIOOverrideEntry entry = { .id = id, .cb = cb };
+    g_array_append_val(mmio_callbacks, entry);
+}
+
+bool plugin_mmio_override_cb_invoke(uint64_t hwaddr,
+                                    uint64_t size,
+                                    bool is_write,
+                                    uint64_t* data)
+{
+    if (!mmio_callbacks) {
+        return false;
+    }
+
+    for (int i = 0; i < mmio_callbacks->len; ++i) {
+        MMIOOverrideEntry *entry = &g_array_index(mmio_callbacks,
+                                                  MMIOOverrideEntry, i);
+        if (entry->cb(hwaddr, size, is_write, data)) {
+            /* Stop on first match */
+            return true;
+        }
+    }
+
+    return false;
+}
+
+void plugin_register_intc(void *opaque, plugin_irq_inject_cb cb)
+{
+    intc_opaque = opaque;
+    irq_inject_cb = cb;
+}
+
+void plugin_inject_irq(int irq_num, int cpu, bool pulse)
+{
+    if (!irq_inject_cb) {
+        return;
+    }
+
+    bool locked = bql_locked();
+
+    if (!locked) {
+        bql_lock();
+    }
+
+    irq_inject_cb(intc_opaque, irq_num, cpu, pulse);
+
+    if (!locked) {
+        bql_unlock();
+    }
+}
+
+void plugin_inject_exception(int excp_index, uint32_t data)
+{
+#if defined (TARGET_ARM)
+    arm_cpu_inject_exception(excp_index, data);
+#else
+    qemu_log_mask(LOG_UNIMP,
+                  "FI: Injecting exception is not supported for this target\n");
+#endif
+}
+
+void plugin_register_custom_fault(const char *fault_name,
+                                  plugin_custom_fault_cb cb){
+    if (!fault_registry) {
+        fault_registry = g_hash_table_new_full(g_str_hash, g_str_equal,
+                                               g_free, NULL);
+    }
+
+    g_hash_table_insert(fault_registry, g_strdup(fault_name), cb);
+}
+
+void plugin_trigger_custom_fault(const char* fault_name, void *target_data,
+                                 void *fault_data)
+{
+    plugin_custom_fault_cb cb = NULL;
+
+    if (fault_registry) {
+        cb = g_hash_table_lookup(fault_registry, fault_name);
+    }
+
+    if (cb) {
+        cb(target_data, fault_data);
+    }
+}
diff --git a/plugins/meson.build b/plugins/meson.build
index 9899f166ee..8995ce5977 100644
--- a/plugins/meson.build
+++ b/plugins/meson.build
@@ -86,3 +86,4 @@ system_ss.add(files('api.c', 'core.c'))
 
 common_ss.add(files('loader.c'))
 
+specific_ss.add(files('fault.c'))
-- 
2.43.0



  parent reply	other threads:[~2026-03-18 10:49 UTC|newest]

Thread overview: 22+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-03-18 10:46 [RFC PATCH 0/9] plugins: Introduce Fault Injection framework and API extensions Ruslan Ruslichenko
2026-03-18 10:46 ` [RFC PATCH 1/9] target/arm: Add API for dynamic exception injection Ruslan Ruslichenko
2026-03-18 10:46 ` [RFC PATCH 2/9] plugins/api: Expose virtual clock timers to plugins Ruslan Ruslichenko
2026-03-18 10:46 ` [RFC PATCH 3/9] plugins: Expose Transaction Block cache flush API " Ruslan Ruslichenko
2026-03-18 10:46 ` Ruslan Ruslichenko [this message]
2026-03-18 10:46 ` [RFC PATCH 5/9] system/memory: Add plugin callbacks to intercept MMIO accesses Ruslan Ruslichenko
2026-03-18 10:46 ` [RFC PATCH 6/9] hw/intc/arm_gic: Register primary GIC for plugin IRQ injection Ruslan Ruslichenko
2026-03-18 10:46 ` [RFC PATCH 7/9] hw/arm/smmuv3: Add plugin fault handler for CMDQ errors Ruslan Ruslichenko
2026-03-18 10:46 ` [RFC PATCH 8/9] contrib/plugins: Add fault injection plugin Ruslan Ruslichenko
2026-03-18 10:46 ` [RFC PATCH 9/9] docs: Add description of fault-injection plugin and subsystem Ruslan Ruslichenko
2026-03-18 17:16 ` [RFC PATCH 0/9] plugins: Introduce Fault Injection framework and API extensions Pierrick Bouvier
2026-03-19 18:20   ` Ruslan Ruslichenko
2026-03-19 19:04     ` Pierrick Bouvier
2026-03-19 22:29       ` Ruslan Ruslichenko
2026-03-20 18:08         ` Pierrick Bouvier
2026-03-25 23:39           ` Ruslan Ruslichenko
2026-03-26  0:17             ` Pierrick Bouvier
2026-03-26 11:45               ` Alex Bennée
2026-03-26 15:59                 ` Pierrick Bouvier
2026-03-27 18:18                   ` Pierrick Bouvier
2026-03-31 20:23                     ` Ruslan Ruslichenko
2026-03-31 21:24                       ` Pierrick Bouvier

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260318104640.239752-5-ruslichenko.r@gmail.com \
    --to=ruslichenko.r@gmail.com \
    --cc=Ruslan_Ruslichenko@epam.com \
    --cc=alex.bennee@linaro.org \
    --cc=artem_mygaiev@epam.com \
    --cc=peter.maydell@linaro.org \
    --cc=philmd@linaro.org \
    --cc=pierrick.bouvier@linaro.org \
    --cc=qemu-arm@nongnu.org \
    --cc=qemu-devel@nongnu.org \
    --cc=volodymyr_babchuk@epam.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.