diff mbox series

[18/23] plugins: add an API to read registers

Message ID 20240216163025.424857-19-alex.bennee@linaro.org
State New
Headers show
Series maintainer updates for 9.0 pre-PR (tests, plugin register support) | expand

Commit Message

Alex Bennée Feb. 16, 2024, 4:30 p.m. UTC
We can only request a list of registers once the vCPU has been
initialised so the user needs to use either call the get function on
vCPU initialisation or during the translation phase.

We don't expose the reg number to the plugin instead hiding it behind
an opaque handle. This allows for a bit of future proofing should the
internals need to be changed while also being hashed against the
CPUClass so we can handle different register sets per-vCPU in
hetrogenous situations.

Having an internal state within the plugins also allows us to expand
the interface in future (for example providing callbacks on register
change if the translator can track changes).

Resolves: https://gitlab.com/qemu-project/qemu/-/issues/1706
Cc: Akihiko Odaki <akihiko.odaki@daynix.com>
Message-Id: <20240103173349.398526-39-alex.bennee@linaro.org>
Based-on: <20231025093128.33116-18-akihiko.odaki@daynix.com>
Signed-off-by: Alex Bennée <alex.bennee@linaro.org>
Reviewed-by: Pierrick Bouvier <pierrick.bouvier@linaro.org>

---
v4
  - the get/read_registers functions are now implicitly for current
  vCPU only to accidental cpu != current_cpu uses.
---
 include/qemu/qemu-plugin.h   |  48 +++++++++++++++-
 plugins/api.c                | 107 +++++++++++++++++++++++++++++++++++
 plugins/qemu-plugins.symbols |   2 +
 3 files changed, 155 insertions(+), 2 deletions(-)

Comments

Akihiko Odaki Feb. 17, 2024, 8:01 a.m. UTC | #1
On 2024/02/17 1:30, Alex Bennée wrote:
> We can only request a list of registers once the vCPU has been
> initialised so the user needs to use either call the get function on
> vCPU initialisation or during the translation phase.
> 
> We don't expose the reg number to the plugin instead hiding it behind
> an opaque handle. This allows for a bit of future proofing should the
> internals need to be changed while also being hashed against the
> CPUClass so we can handle different register sets per-vCPU in
> hetrogenous situations.
> 
> Having an internal state within the plugins also allows us to expand
> the interface in future (for example providing callbacks on register
> change if the translator can track changes).
> 
> Resolves: https://gitlab.com/qemu-project/qemu/-/issues/1706
> Cc: Akihiko Odaki <akihiko.odaki@daynix.com>
> Message-Id: <20240103173349.398526-39-alex.bennee@linaro.org>
> Based-on: <20231025093128.33116-18-akihiko.odaki@daynix.com>
> Signed-off-by: Alex Bennée <alex.bennee@linaro.org>
> Reviewed-by: Pierrick Bouvier <pierrick.bouvier@linaro.org>
> 
> ---
> v4
>    - the get/read_registers functions are now implicitly for current
>    vCPU only to accidental cpu != current_cpu uses.
> ---
>   include/qemu/qemu-plugin.h   |  48 +++++++++++++++-
>   plugins/api.c                | 107 +++++++++++++++++++++++++++++++++++
>   plugins/qemu-plugins.symbols |   2 +
>   3 files changed, 155 insertions(+), 2 deletions(-)
> 
> diff --git a/include/qemu/qemu-plugin.h b/include/qemu/qemu-plugin.h
> index 93981f8f89f..3b6b18058d2 100644
> --- a/include/qemu/qemu-plugin.h
> +++ b/include/qemu/qemu-plugin.h
> @@ -11,6 +11,7 @@
>   #ifndef QEMU_QEMU_PLUGIN_H
>   #define QEMU_QEMU_PLUGIN_H
>   
> +#include <glib.h>
>   #include <inttypes.h>
>   #include <stdbool.h>
>   #include <stddef.h>
> @@ -229,8 +230,8 @@ struct qemu_plugin_insn;
>    * @QEMU_PLUGIN_CB_R_REGS: callback reads the CPU's regs
>    * @QEMU_PLUGIN_CB_RW_REGS: callback reads and writes the CPU's regs
>    *
> - * Note: currently unused, plugins cannot read or change system
> - * register state.
> + * Note: currently QEMU_PLUGIN_CB_RW_REGS is unused, plugins cannot change
> + * system register state.
>    */
>   enum qemu_plugin_cb_flags {
>       QEMU_PLUGIN_CB_NO_REGS,
> @@ -707,4 +708,47 @@ uint64_t qemu_plugin_end_code(void);
>   QEMU_PLUGIN_API
>   uint64_t qemu_plugin_entry_code(void);
>   
> +/** struct qemu_plugin_register - Opaque handle for register access */
> +struct qemu_plugin_register;
> +
> +/**
> + * typedef qemu_plugin_reg_descriptor - register descriptions
> + *
> + * @handle: opaque handle for retrieving value with qemu_plugin_read_register
> + * @name: register name
> + * @feature: optional feature descriptor, can be NULL
> + */
> +typedef struct {
> +    struct qemu_plugin_register *handle;
> +    const char *name;
> +    const char *feature;
> +} qemu_plugin_reg_descriptor;
> +
> +/**
> + * qemu_plugin_get_registers() - return register list for current vCPU
> + *
> + * Returns a GArray of qemu_plugin_reg_descriptor or NULL. Caller
> + * frees the array (but not the const strings).
> + *
> + * Should be used from a qemu_plugin_register_vcpu_init_cb() callback
> + * after the vCPU is initialised, i.e. in the vCPU context.
> + */
> +GArray *qemu_plugin_get_registers(void);
> +
> +/**
> + * qemu_plugin_read_register() - read register for current vCPU
> + *
> + * @handle: a @qemu_plugin_reg_handle handle
> + * @buf: A GByteArray for the data owned by the plugin
> + *
> + * This function is only available in a context that register read access is
> + * explicitly requested via the QEMU_PLUGIN_CB_R_REGS flag.
> + *
> + * Returns the size of the read register. The content of @buf is in target byte
> + * order. On failure returns -1
> + */
> +int qemu_plugin_read_register(struct qemu_plugin_register *handle,
> +                              GByteArray *buf);
> +
> +
>   #endif /* QEMU_QEMU_PLUGIN_H */
> diff --git a/plugins/api.c b/plugins/api.c
> index 54df72c1c00..483f04e85e4 100644
> --- a/plugins/api.c
> +++ b/plugins/api.c
> @@ -8,6 +8,7 @@
>    *
>    *  qemu_plugin_tb
>    *  qemu_plugin_insn
> + *  qemu_plugin_register
>    *
>    * Which can then be passed back into the API to do additional things.
>    * As such all the public functions in here are exported in
> @@ -35,10 +36,12 @@
>    */
>   
>   #include "qemu/osdep.h"
> +#include "qemu/main-loop.h"
>   #include "qemu/plugin.h"
>   #include "qemu/log.h"
>   #include "tcg/tcg.h"
>   #include "exec/exec-all.h"
> +#include "exec/gdbstub.h"
>   #include "exec/ram_addr.h"
>   #include "disas/disas.h"
>   #include "plugin.h"
> @@ -410,3 +413,107 @@ uint64_t qemu_plugin_entry_code(void)
>   #endif
>       return entry;
>   }
> +
> +/*
> + * Register handles
> + *
> + * The plugin infrastructure keeps hold of these internal data
> + * structures which are presented to plugins as opaque handles. They
> + * are global to the system and therefor additions to the hash table
> + * must be protected by the @reg_handle_lock.
> + *
> + * In order to future proof for up-coming heterogeneous work we want
> + * different entries for each CPU type while sharing them in the
> + * common case of multiple cores of the same type.
> + */
> +
> +static QemuMutex reg_handle_lock;
> +
> +struct qemu_plugin_register {
> +    const char *name;
> +    int gdb_reg_num;
> +};
> +
> +static GHashTable *reg_handles; /* hash table of PluginReg */
> +
> +/* Generate a stable key - would xxhash be overkill? */
> +static gpointer cpu_plus_reg_to_key(CPUState *cs, int gdb_regnum)
> +{
> +    uintptr_t key = (uintptr_t) cs->cc;
> +    key ^= gdb_regnum;
> +    return GUINT_TO_POINTER(key);
> +}

I have pointed out this is theoretically prone to collisions and unsafe.

> +
> +/*
> + * Create register handles.
> + *
> + * We need to create a handle for each register so the plugin
> + * infrastructure can call gdbstub to read a register. We also
> + * construct a result array with those handles and some ancillary data
> + * the plugin might find useful.
> + */
> +
> +static GArray *create_register_handles(CPUState *cs, GArray *gdbstub_regs)
> +{
> +    GArray *find_data = g_array_new(true, true,
> +                                    sizeof(qemu_plugin_reg_descriptor));
> +
> +    WITH_QEMU_LOCK_GUARD(&reg_handle_lock) {
> +
> +        if (!reg_handles) {
> +            reg_handles = g_hash_table_new(g_direct_hash, g_direct_equal);
> +        }
> +
> +        for (int i = 0; i < gdbstub_regs->len; i++) {
> +            GDBRegDesc *grd = &g_array_index(gdbstub_regs, GDBRegDesc, i);
> +            gpointer key = cpu_plus_reg_to_key(cs, grd->gdb_reg);
> +            struct qemu_plugin_register *val = g_hash_table_lookup(reg_handles,
> +                                                                   key);
> +
> +            /* skip "un-named" regs */
> +            if (!grd->name) {
> +                continue;
> +            }
> +
> +            /* Doesn't exist, create one */
> +            if (!val) {
> +                val = g_new0(struct qemu_plugin_register, 1);
> +                val->gdb_reg_num = grd->gdb_reg;
> +                val->name = g_intern_string(grd->name);
> +
> +                g_hash_table_insert(reg_handles, key, val);
> +            }
> +
> +            /* Create a record for the plugin */
> +            qemu_plugin_reg_descriptor desc = {
> +                .handle = val,
> +                .name = val->name,
> +                .feature = g_intern_string(grd->feature_name)
> +            };
> +            g_array_append_val(find_data, desc);
> +        }
> +    }
> +
> +    return find_data;
> +}
> +
> +GArray *qemu_plugin_get_registers(void)
> +{
> +    g_assert(current_cpu);
> +
> +    g_autoptr(GArray) regs = gdb_get_register_list(current_cpu);
> +    return regs->len ? create_register_handles(current_cpu, regs) : NULL;
> +}
> +
> +int qemu_plugin_read_register(struct qemu_plugin_register *reg, GByteArray *buf)
> +{
> +    g_assert(current_cpu);
> +
> +    return gdb_read_register(current_cpu, buf, reg->gdb_reg_num);
> +}
> +
> +static void __attribute__((__constructor__)) qemu_api_init(void)
> +{
> +    qemu_mutex_init(&reg_handle_lock);
> +
> +}
> diff --git a/plugins/qemu-plugins.symbols b/plugins/qemu-plugins.symbols
> index adb67608598..27fe97239be 100644
> --- a/plugins/qemu-plugins.symbols
> +++ b/plugins/qemu-plugins.symbols
> @@ -3,6 +3,7 @@
>     qemu_plugin_end_code;
>     qemu_plugin_entry_code;
>     qemu_plugin_get_hwaddr;
> +  qemu_plugin_get_registers;
>     qemu_plugin_hwaddr_device_name;
>     qemu_plugin_hwaddr_is_io;
>     qemu_plugin_hwaddr_phys_addr;
> @@ -19,6 +20,7 @@
>     qemu_plugin_num_vcpus;
>     qemu_plugin_outs;
>     qemu_plugin_path_to_binary;
> +  qemu_plugin_read_register;
>     qemu_plugin_register_atexit_cb;
>     qemu_plugin_register_flush_cb;
>     qemu_plugin_register_vcpu_exit_cb;
Alex Bennée Feb. 20, 2024, 2:14 p.m. UTC | #2
Akihiko Odaki <akihiko.odaki@daynix.com> writes:

> On 2024/02/17 1:30, Alex Bennée wrote:
>> We can only request a list of registers once the vCPU has been
>> initialised so the user needs to use either call the get function on
>> vCPU initialisation or during the translation phase.
>> We don't expose the reg number to the plugin instead hiding it
>> behind
>> an opaque handle. This allows for a bit of future proofing should the
>> internals need to be changed while also being hashed against the
>> CPUClass so we can handle different register sets per-vCPU in
>> hetrogenous situations.
>> Having an internal state within the plugins also allows us to expand
>> the interface in future (for example providing callbacks on register
>> change if the translator can track changes).
>> Resolves: https://gitlab.com/qemu-project/qemu/-/issues/1706
>> Cc: Akihiko Odaki <akihiko.odaki@daynix.com>
>> Message-Id: <20240103173349.398526-39-alex.bennee@linaro.org>
>> Based-on: <20231025093128.33116-18-akihiko.odaki@daynix.com>
>> Signed-off-by: Alex Bennée <alex.bennee@linaro.org>
>> Reviewed-by: Pierrick Bouvier <pierrick.bouvier@linaro.org>
<snip>
>> +/*
>> + * Register handles
>> + *
>> + * The plugin infrastructure keeps hold of these internal data
>> + * structures which are presented to plugins as opaque handles. They
>> + * are global to the system and therefor additions to the hash table
>> + * must be protected by the @reg_handle_lock.
>> + *
>> + * In order to future proof for up-coming heterogeneous work we want
>> + * different entries for each CPU type while sharing them in the
>> + * common case of multiple cores of the same type.
>> + */
>> +
>> +static QemuMutex reg_handle_lock;
>> +
>> +struct qemu_plugin_register {
>> +    const char *name;
>> +    int gdb_reg_num;
>> +};
>> +
>> +static GHashTable *reg_handles; /* hash table of PluginReg */
>> +
>> +/* Generate a stable key - would xxhash be overkill? */
>> +static gpointer cpu_plus_reg_to_key(CPUState *cs, int gdb_regnum)
>> +{
>> +    uintptr_t key = (uintptr_t) cs->cc;
>> +    key ^= gdb_regnum;
>> +    return GUINT_TO_POINTER(key);
>> +}
>
> I have pointed out this is theoretically prone to collisions and
> unsafe.

How is it unsafe? The aim is to share handles for the same CPUClass
rather than having a unique handle per register/cpu combo.

Indeed if I add the following:

--8<---------------cut here---------------start------------->8---
   plugins/api.c
@@ -482,6 +482,9 @@ static GArray *create_register_handles(CPUState *cs, GArray *gdbstub_regs)
                 val->name = g_intern_string(grd->name);
 
                 g_hash_table_insert(reg_handles, key, val);
+            } else {
+                /* make sure its not a clash */
+                g_assert(val->gdb_reg_num == grd->gdb_reg);
             }
 
             /* Create a record for the plugin */
modified   tests/plugin/insn.c
@@ -46,6 +46,25 @@ typedef struct {
     char *disas;
 } Instruction;
 
+/*
+ * Initialise a new vcpu with reading the register list
+ */
+static void vcpu_init(qemu_plugin_id_t id, unsigned int vcpu_index)
+{
+    g_autoptr(GArray) reg_list = qemu_plugin_get_registers();
+    g_autoptr(GByteArray) reg_value = g_byte_array_new();
+
+    if (reg_list) {
+        for (int i = 0; i < reg_list->len; i++) {
+            qemu_plugin_reg_descriptor *rd = &g_array_index(
+                reg_list, qemu_plugin_reg_descriptor, i);
+            int count = qemu_plugin_read_register(rd->handle, reg_value);
+            g_assert(count > 0);
+        }
+    }
+}
+
+
 static void vcpu_insn_exec_before(unsigned int cpu_index, void *udata)
 {
     unsigned int i = cpu_index % MAX_CPUS;
@@ -212,6 +231,8 @@ QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id,
         sizes = g_array_new(true, true, sizeof(unsigned long));
     }
 
+    /* Register init, translation block and exit callbacks */
+    qemu_plugin_register_vcpu_init_cb(id, vcpu_init);
     qemu_plugin_register_vcpu_tb_trans_cb(id, vcpu_tb_trans);
     qemu_plugin_register_atexit_cb(id, plugin_exit, NULL);
     return 0;
--8<---------------cut here---------------end--------------->8---

Nothing trips up during check-tcg (after I fixed "gdbstub: Infer number
of core registers from XML" to remove the microblaze check on
cc->gdb_num_core_regs).

>
>> +
>> +/*
>> + * Create register handles.
>> + *
>> + * We need to create a handle for each register so the plugin
>> + * infrastructure can call gdbstub to read a register. We also
>> + * construct a result array with those handles and some ancillary data
>> + * the plugin might find useful.
>> + */
>> +
>> +static GArray *create_register_handles(CPUState *cs, GArray *gdbstub_regs)
>> +{
>> +    GArray *find_data = g_array_new(true, true,
>> +                                    sizeof(qemu_plugin_reg_descriptor));
>> +
>> +    WITH_QEMU_LOCK_GUARD(&reg_handle_lock) {
>> +
>> +        if (!reg_handles) {
>> +            reg_handles = g_hash_table_new(g_direct_hash, g_direct_equal);
>> +        }
>> +
>> +        for (int i = 0; i < gdbstub_regs->len; i++) {
>> +            GDBRegDesc *grd = &g_array_index(gdbstub_regs, GDBRegDesc, i);
>> +            gpointer key = cpu_plus_reg_to_key(cs, grd->gdb_reg);
>> +            struct qemu_plugin_register *val = g_hash_table_lookup(reg_handles,
>> +                                                                   key);
>> +
>> +            /* skip "un-named" regs */
>> +            if (!grd->name) {
>> +                continue;
>> +            }
>> +
>> +            /* Doesn't exist, create one */
>> +            if (!val) {
>> +                val = g_new0(struct qemu_plugin_register, 1);
>> +                val->gdb_reg_num = grd->gdb_reg;
>> +                val->name = g_intern_string(grd->name);
>> +
>> +                g_hash_table_insert(reg_handles, key, val);
>> +            }
>> +
>> +            /* Create a record for the plugin */
>> +            qemu_plugin_reg_descriptor desc = {
>> +                .handle = val,
>> +                .name = val->name,
>> +                .feature = g_intern_string(grd->feature_name)
>> +            };
>> +            g_array_append_val(find_data, desc);
>> +        }
>> +    }
>> +
>> +    return find_data;
>> +}
>> +
>> +GArray *qemu_plugin_get_registers(void)
>> +{
>> +    g_assert(current_cpu);
>> +
>> +    g_autoptr(GArray) regs = gdb_get_register_list(current_cpu);
>> +    return regs->len ? create_register_handles(current_cpu, regs) : NULL;
>> +}
>> +
>> +int qemu_plugin_read_register(struct qemu_plugin_register *reg, GByteArray *buf)
>> +{
>> +    g_assert(current_cpu);
>> +
>> +    return gdb_read_register(current_cpu, buf, reg->gdb_reg_num);
>> +}
>> +
>> +static void __attribute__((__constructor__)) qemu_api_init(void)
>> +{
>> +    qemu_mutex_init(&reg_handle_lock);
>> +
>> +}
>> diff --git a/plugins/qemu-plugins.symbols b/plugins/qemu-plugins.symbols
>> index adb67608598..27fe97239be 100644
>> --- a/plugins/qemu-plugins.symbols
>> +++ b/plugins/qemu-plugins.symbols
>> @@ -3,6 +3,7 @@
>>     qemu_plugin_end_code;
>>     qemu_plugin_entry_code;
>>     qemu_plugin_get_hwaddr;
>> +  qemu_plugin_get_registers;
>>     qemu_plugin_hwaddr_device_name;
>>     qemu_plugin_hwaddr_is_io;
>>     qemu_plugin_hwaddr_phys_addr;
>> @@ -19,6 +20,7 @@
>>     qemu_plugin_num_vcpus;
>>     qemu_plugin_outs;
>>     qemu_plugin_path_to_binary;
>> +  qemu_plugin_read_register;
>>     qemu_plugin_register_atexit_cb;
>>     qemu_plugin_register_flush_cb;
>>     qemu_plugin_register_vcpu_exit_cb;
Akihiko Odaki Feb. 21, 2024, 4:45 a.m. UTC | #3
On 2024/02/20 23:14, Alex Bennée wrote:
> Akihiko Odaki <akihiko.odaki@daynix.com> writes:
> 
>> On 2024/02/17 1:30, Alex Bennée wrote:
>>> We can only request a list of registers once the vCPU has been
>>> initialised so the user needs to use either call the get function on
>>> vCPU initialisation or during the translation phase.
>>> We don't expose the reg number to the plugin instead hiding it
>>> behind
>>> an opaque handle. This allows for a bit of future proofing should the
>>> internals need to be changed while also being hashed against the
>>> CPUClass so we can handle different register sets per-vCPU in
>>> hetrogenous situations.
>>> Having an internal state within the plugins also allows us to expand
>>> the interface in future (for example providing callbacks on register
>>> change if the translator can track changes).
>>> Resolves: https://gitlab.com/qemu-project/qemu/-/issues/1706
>>> Cc: Akihiko Odaki <akihiko.odaki@daynix.com>
>>> Message-Id: <20240103173349.398526-39-alex.bennee@linaro.org>
>>> Based-on: <20231025093128.33116-18-akihiko.odaki@daynix.com>
>>> Signed-off-by: Alex Bennée <alex.bennee@linaro.org>
>>> Reviewed-by: Pierrick Bouvier <pierrick.bouvier@linaro.org>
> <snip>
>>> +/*
>>> + * Register handles
>>> + *
>>> + * The plugin infrastructure keeps hold of these internal data
>>> + * structures which are presented to plugins as opaque handles. They
>>> + * are global to the system and therefor additions to the hash table
>>> + * must be protected by the @reg_handle_lock.
>>> + *
>>> + * In order to future proof for up-coming heterogeneous work we want
>>> + * different entries for each CPU type while sharing them in the
>>> + * common case of multiple cores of the same type.
>>> + */
>>> +
>>> +static QemuMutex reg_handle_lock;
>>> +
>>> +struct qemu_plugin_register {
>>> +    const char *name;
>>> +    int gdb_reg_num;
>>> +};
>>> +
>>> +static GHashTable *reg_handles; /* hash table of PluginReg */
>>> +
>>> +/* Generate a stable key - would xxhash be overkill? */
>>> +static gpointer cpu_plus_reg_to_key(CPUState *cs, int gdb_regnum)
>>> +{
>>> +    uintptr_t key = (uintptr_t) cs->cc;
>>> +    key ^= gdb_regnum;
>>> +    return GUINT_TO_POINTER(key);
>>> +}
>>
>> I have pointed out this is theoretically prone to collisions and
>> unsafe.
> 
> How is it unsafe? The aim is to share handles for the same CPUClass
> rather than having a unique handle per register/cpu combo.

THe intention is legitimate, but the implementation is not safe. It 
assumes (uintptr)cs->cc ^ gdb_regnum is unique, but there is no such 
guarantee. The key of GHashTable must be unique; generating hashes of 
keys should be done with hash_func given to g_hash_table_new().

> 
> Indeed if I add the following:
> 
> --8<---------------cut here---------------start------------->8---
>     plugins/api.c
> @@ -482,6 +482,9 @@ static GArray *create_register_handles(CPUState *cs, GArray *gdbstub_regs)
>                   val->name = g_intern_string(grd->name);
>   
>                   g_hash_table_insert(reg_handles, key, val);
> +            } else {
> +                /* make sure its not a clash */
> +                g_assert(val->gdb_reg_num == grd->gdb_reg);
>               }
>   
>               /* Create a record for the plugin */
> modified   tests/plugin/insn.c
> @@ -46,6 +46,25 @@ typedef struct {
>       char *disas;
>   } Instruction;
>   
> +/*
> + * Initialise a new vcpu with reading the register list
> + */
> +static void vcpu_init(qemu_plugin_id_t id, unsigned int vcpu_index)
> +{
> +    g_autoptr(GArray) reg_list = qemu_plugin_get_registers();
> +    g_autoptr(GByteArray) reg_value = g_byte_array_new();
> +
> +    if (reg_list) {
> +        for (int i = 0; i < reg_list->len; i++) {
> +            qemu_plugin_reg_descriptor *rd = &g_array_index(
> +                reg_list, qemu_plugin_reg_descriptor, i);
> +            int count = qemu_plugin_read_register(rd->handle, reg_value);
> +            g_assert(count > 0);
> +        }
> +    }
> +}
> +
> +
>   static void vcpu_insn_exec_before(unsigned int cpu_index, void *udata)
>   {
>       unsigned int i = cpu_index % MAX_CPUS;
> @@ -212,6 +231,8 @@ QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id,
>           sizes = g_array_new(true, true, sizeof(unsigned long));
>       }
>   
> +    /* Register init, translation block and exit callbacks */
> +    qemu_plugin_register_vcpu_init_cb(id, vcpu_init);
>       qemu_plugin_register_vcpu_tb_trans_cb(id, vcpu_tb_trans);
>       qemu_plugin_register_atexit_cb(id, plugin_exit, NULL);
>       return 0;
> --8<---------------cut here---------------end--------------->8---
> 
> Nothing trips up during check-tcg (after I fixed "gdbstub: Infer number
> of core registers from XML" to remove the microblaze check on
> cc->gdb_num_core_regs).
> 
>>
>>> +
>>> +/*
>>> + * Create register handles.
>>> + *
>>> + * We need to create a handle for each register so the plugin
>>> + * infrastructure can call gdbstub to read a register. We also
>>> + * construct a result array with those handles and some ancillary data
>>> + * the plugin might find useful.
>>> + */
>>> +
>>> +static GArray *create_register_handles(CPUState *cs, GArray *gdbstub_regs)
>>> +{
>>> +    GArray *find_data = g_array_new(true, true,
>>> +                                    sizeof(qemu_plugin_reg_descriptor));
>>> +
>>> +    WITH_QEMU_LOCK_GUARD(&reg_handle_lock) {
>>> +
>>> +        if (!reg_handles) {
>>> +            reg_handles = g_hash_table_new(g_direct_hash, g_direct_equal);
>>> +        }
>>> +
>>> +        for (int i = 0; i < gdbstub_regs->len; i++) {
>>> +            GDBRegDesc *grd = &g_array_index(gdbstub_regs, GDBRegDesc, i);
>>> +            gpointer key = cpu_plus_reg_to_key(cs, grd->gdb_reg);
>>> +            struct qemu_plugin_register *val = g_hash_table_lookup(reg_handles,
>>> +                                                                   key);
>>> +
>>> +            /* skip "un-named" regs */
>>> +            if (!grd->name) {
>>> +                continue;
>>> +            }
>>> +
>>> +            /* Doesn't exist, create one */
>>> +            if (!val) {
>>> +                val = g_new0(struct qemu_plugin_register, 1);
>>> +                val->gdb_reg_num = grd->gdb_reg;
>>> +                val->name = g_intern_string(grd->name);
>>> +
>>> +                g_hash_table_insert(reg_handles, key, val);
>>> +            }
>>> +
>>> +            /* Create a record for the plugin */
>>> +            qemu_plugin_reg_descriptor desc = {
>>> +                .handle = val,
>>> +                .name = val->name,
>>> +                .feature = g_intern_string(grd->feature_name)
>>> +            };
>>> +            g_array_append_val(find_data, desc);
>>> +        }
>>> +    }
>>> +
>>> +    return find_data;
>>> +}
>>> +
>>> +GArray *qemu_plugin_get_registers(void)
>>> +{
>>> +    g_assert(current_cpu);
>>> +
>>> +    g_autoptr(GArray) regs = gdb_get_register_list(current_cpu);
>>> +    return regs->len ? create_register_handles(current_cpu, regs) : NULL;
>>> +}
>>> +
>>> +int qemu_plugin_read_register(struct qemu_plugin_register *reg, GByteArray *buf)
>>> +{
>>> +    g_assert(current_cpu);
>>> +
>>> +    return gdb_read_register(current_cpu, buf, reg->gdb_reg_num);
>>> +}
>>> +
>>> +static void __attribute__((__constructor__)) qemu_api_init(void)
>>> +{
>>> +    qemu_mutex_init(&reg_handle_lock);
>>> +
>>> +}
>>> diff --git a/plugins/qemu-plugins.symbols b/plugins/qemu-plugins.symbols
>>> index adb67608598..27fe97239be 100644
>>> --- a/plugins/qemu-plugins.symbols
>>> +++ b/plugins/qemu-plugins.symbols
>>> @@ -3,6 +3,7 @@
>>>      qemu_plugin_end_code;
>>>      qemu_plugin_entry_code;
>>>      qemu_plugin_get_hwaddr;
>>> +  qemu_plugin_get_registers;
>>>      qemu_plugin_hwaddr_device_name;
>>>      qemu_plugin_hwaddr_is_io;
>>>      qemu_plugin_hwaddr_phys_addr;
>>> @@ -19,6 +20,7 @@
>>>      qemu_plugin_num_vcpus;
>>>      qemu_plugin_outs;
>>>      qemu_plugin_path_to_binary;
>>> +  qemu_plugin_read_register;
>>>      qemu_plugin_register_atexit_cb;
>>>      qemu_plugin_register_flush_cb;
>>>      qemu_plugin_register_vcpu_exit_cb;
>
Alex Bennée Feb. 21, 2024, 10:02 a.m. UTC | #4
Akihiko Odaki <akihiko.odaki@daynix.com> writes:

> On 2024/02/20 23:14, Alex Bennée wrote:
>> Akihiko Odaki <akihiko.odaki@daynix.com> writes:
>> 
>>> On 2024/02/17 1:30, Alex Bennée wrote:
>>>> We can only request a list of registers once the vCPU has been
>>>> initialised so the user needs to use either call the get function on
>>>> vCPU initialisation or during the translation phase.
>>>> We don't expose the reg number to the plugin instead hiding it
>>>> behind
>>>> an opaque handle. This allows for a bit of future proofing should the
>>>> internals need to be changed while also being hashed against the
>>>> CPUClass so we can handle different register sets per-vCPU in
>>>> hetrogenous situations.
>>>> Having an internal state within the plugins also allows us to expand
>>>> the interface in future (for example providing callbacks on register
>>>> change if the translator can track changes).
>>>> Resolves: https://gitlab.com/qemu-project/qemu/-/issues/1706
>>>> Cc: Akihiko Odaki <akihiko.odaki@daynix.com>
>>>> Message-Id: <20240103173349.398526-39-alex.bennee@linaro.org>
>>>> Based-on: <20231025093128.33116-18-akihiko.odaki@daynix.com>
>>>> Signed-off-by: Alex Bennée <alex.bennee@linaro.org>
>>>> Reviewed-by: Pierrick Bouvier <pierrick.bouvier@linaro.org>
>> <snip>
>>>> +/*
>>>> + * Register handles
>>>> + *
>>>> + * The plugin infrastructure keeps hold of these internal data
>>>> + * structures which are presented to plugins as opaque handles. They
>>>> + * are global to the system and therefor additions to the hash table
>>>> + * must be protected by the @reg_handle_lock.
>>>> + *
>>>> + * In order to future proof for up-coming heterogeneous work we want
>>>> + * different entries for each CPU type while sharing them in the
>>>> + * common case of multiple cores of the same type.
>>>> + */
>>>> +
>>>> +static QemuMutex reg_handle_lock;
>>>> +
>>>> +struct qemu_plugin_register {
>>>> +    const char *name;
>>>> +    int gdb_reg_num;
>>>> +};
>>>> +
>>>> +static GHashTable *reg_handles; /* hash table of PluginReg */
>>>> +
>>>> +/* Generate a stable key - would xxhash be overkill? */
>>>> +static gpointer cpu_plus_reg_to_key(CPUState *cs, int gdb_regnum)
>>>> +{
>>>> +    uintptr_t key = (uintptr_t) cs->cc;
>>>> +    key ^= gdb_regnum;
>>>> +    return GUINT_TO_POINTER(key);
>>>> +}
>>>
>>> I have pointed out this is theoretically prone to collisions and
>>> unsafe.
>> How is it unsafe? The aim is to share handles for the same CPUClass
>> rather than having a unique handle per register/cpu combo.
>
> THe intention is legitimate, but the implementation is not safe. It
> assumes (uintptr)cs->cc ^ gdb_regnum is unique, but there is no such
> guarantee. The key of GHashTable must be unique; generating hashes of
> keys should be done with hash_func given to g_hash_table_new().

This isn't a hash its a non-unique key. It is however unique for
the same register on the same class of CPU so for each vCPU in a system
can share the same opaque handles.

The hashing is done internally by glib. We would assert if there was a
duplicate key referring to a different register.

I'm unsure what you want here? Do you have a suggestion for the key
generation algorithm? As the comment notes I did consider a more complex
mixing algorithm using xxhash but that wouldn't guarantee no clash
either.


>
>> Indeed if I add the following:
>> --8<---------------cut here---------------start------------->8---
>>     plugins/api.c
>> @@ -482,6 +482,9 @@ static GArray *create_register_handles(CPUState *cs, GArray *gdbstub_regs)
>>                   val->name = g_intern_string(grd->name);
>>                     g_hash_table_insert(reg_handles, key, val);
>> +            } else {
>> +                /* make sure its not a clash */
>> +                g_assert(val->gdb_reg_num == grd->gdb_reg);
>>               }
>>                 /* Create a record for the plugin */
>> modified   tests/plugin/insn.c
>> @@ -46,6 +46,25 @@ typedef struct {
>>       char *disas;
>>   } Instruction;
>>   +/*
>> + * Initialise a new vcpu with reading the register list
>> + */
>> +static void vcpu_init(qemu_plugin_id_t id, unsigned int vcpu_index)
>> +{
>> +    g_autoptr(GArray) reg_list = qemu_plugin_get_registers();
>> +    g_autoptr(GByteArray) reg_value = g_byte_array_new();
>> +
>> +    if (reg_list) {
>> +        for (int i = 0; i < reg_list->len; i++) {
>> +            qemu_plugin_reg_descriptor *rd = &g_array_index(
>> +                reg_list, qemu_plugin_reg_descriptor, i);
>> +            int count = qemu_plugin_read_register(rd->handle, reg_value);
>> +            g_assert(count > 0);
>> +        }
>> +    }
>> +}
>> +
>> +
>>   static void vcpu_insn_exec_before(unsigned int cpu_index, void *udata)
>>   {
>>       unsigned int i = cpu_index % MAX_CPUS;
>> @@ -212,6 +231,8 @@ QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id,
>>           sizes = g_array_new(true, true, sizeof(unsigned long));
>>       }
>>   +    /* Register init, translation block and exit callbacks */
>> +    qemu_plugin_register_vcpu_init_cb(id, vcpu_init);
>>       qemu_plugin_register_vcpu_tb_trans_cb(id, vcpu_tb_trans);
>>       qemu_plugin_register_atexit_cb(id, plugin_exit, NULL);
>>       return 0;
>> --8<---------------cut here---------------end--------------->8---
>> Nothing trips up during check-tcg (after I fixed "gdbstub: Infer
>> number
>> of core registers from XML" to remove the microblaze check on
>> cc->gdb_num_core_regs).
<snip>
Akihiko Odaki Feb. 21, 2024, 10:11 a.m. UTC | #5
On 2024/02/21 19:02, Alex Bennée wrote:
> Akihiko Odaki <akihiko.odaki@daynix.com> writes:
> 
>> On 2024/02/20 23:14, Alex Bennée wrote:
>>> Akihiko Odaki <akihiko.odaki@daynix.com> writes:
>>>
>>>> On 2024/02/17 1:30, Alex Bennée wrote:
>>>>> We can only request a list of registers once the vCPU has been
>>>>> initialised so the user needs to use either call the get function on
>>>>> vCPU initialisation or during the translation phase.
>>>>> We don't expose the reg number to the plugin instead hiding it
>>>>> behind
>>>>> an opaque handle. This allows for a bit of future proofing should the
>>>>> internals need to be changed while also being hashed against the
>>>>> CPUClass so we can handle different register sets per-vCPU in
>>>>> hetrogenous situations.
>>>>> Having an internal state within the plugins also allows us to expand
>>>>> the interface in future (for example providing callbacks on register
>>>>> change if the translator can track changes).
>>>>> Resolves: https://gitlab.com/qemu-project/qemu/-/issues/1706
>>>>> Cc: Akihiko Odaki <akihiko.odaki@daynix.com>
>>>>> Message-Id: <20240103173349.398526-39-alex.bennee@linaro.org>
>>>>> Based-on: <20231025093128.33116-18-akihiko.odaki@daynix.com>
>>>>> Signed-off-by: Alex Bennée <alex.bennee@linaro.org>
>>>>> Reviewed-by: Pierrick Bouvier <pierrick.bouvier@linaro.org>
>>> <snip>
>>>>> +/*
>>>>> + * Register handles
>>>>> + *
>>>>> + * The plugin infrastructure keeps hold of these internal data
>>>>> + * structures which are presented to plugins as opaque handles. They
>>>>> + * are global to the system and therefor additions to the hash table
>>>>> + * must be protected by the @reg_handle_lock.
>>>>> + *
>>>>> + * In order to future proof for up-coming heterogeneous work we want
>>>>> + * different entries for each CPU type while sharing them in the
>>>>> + * common case of multiple cores of the same type.
>>>>> + */
>>>>> +
>>>>> +static QemuMutex reg_handle_lock;
>>>>> +
>>>>> +struct qemu_plugin_register {
>>>>> +    const char *name;
>>>>> +    int gdb_reg_num;
>>>>> +};
>>>>> +
>>>>> +static GHashTable *reg_handles; /* hash table of PluginReg */
>>>>> +
>>>>> +/* Generate a stable key - would xxhash be overkill? */
>>>>> +static gpointer cpu_plus_reg_to_key(CPUState *cs, int gdb_regnum)
>>>>> +{
>>>>> +    uintptr_t key = (uintptr_t) cs->cc;
>>>>> +    key ^= gdb_regnum;
>>>>> +    return GUINT_TO_POINTER(key);
>>>>> +}
>>>>
>>>> I have pointed out this is theoretically prone to collisions and
>>>> unsafe.
>>> How is it unsafe? The aim is to share handles for the same CPUClass
>>> rather than having a unique handle per register/cpu combo.
>>
>> THe intention is legitimate, but the implementation is not safe. It
>> assumes (uintptr)cs->cc ^ gdb_regnum is unique, but there is no such
>> guarantee. The key of GHashTable must be unique; generating hashes of
>> keys should be done with hash_func given to g_hash_table_new().
> 
> This isn't a hash its a non-unique key. It is however unique for
> the same register on the same class of CPU so for each vCPU in a system
> can share the same opaque handles.
> 
> The hashing is done internally by glib. We would assert if there was a
> duplicate key referring to a different register.
> 
> I'm unsure what you want here? Do you have a suggestion for the key
> generation algorithm? As the comment notes I did consider a more complex
> mixing algorithm using xxhash but that wouldn't guarantee no clash
> either.

I suggest using a struct that holds both of cs->cc and gdb_regnum, and 
pass g_direct_equal() and g_direct_hash() to g_hash_table_new().
Alex Bennée Feb. 21, 2024, 2:14 p.m. UTC | #6
Akihiko Odaki <akihiko.odaki@daynix.com> writes:

> On 2024/02/21 19:02, Alex Bennée wrote:
>> Akihiko Odaki <akihiko.odaki@daynix.com> writes:
>> 
>>> On 2024/02/20 23:14, Alex Bennée wrote:
>>>> Akihiko Odaki <akihiko.odaki@daynix.com> writes:
>>>>
>>>>> On 2024/02/17 1:30, Alex Bennée wrote:
>>>>>> We can only request a list of registers once the vCPU has been
>>>>>> initialised so the user needs to use either call the get function on
>>>>>> vCPU initialisation or during the translation phase.
>>>>>> We don't expose the reg number to the plugin instead hiding it
>>>>>> behind
>>>>>> an opaque handle. This allows for a bit of future proofing should the
>>>>>> internals need to be changed while also being hashed against the
>>>>>> CPUClass so we can handle different register sets per-vCPU in
>>>>>> hetrogenous situations.
>>>>>> Having an internal state within the plugins also allows us to expand
>>>>>> the interface in future (for example providing callbacks on register
>>>>>> change if the translator can track changes).
>>>>>> Resolves: https://gitlab.com/qemu-project/qemu/-/issues/1706
>>>>>> Cc: Akihiko Odaki <akihiko.odaki@daynix.com>
>>>>>> Message-Id: <20240103173349.398526-39-alex.bennee@linaro.org>
>>>>>> Based-on: <20231025093128.33116-18-akihiko.odaki@daynix.com>
>>>>>> Signed-off-by: Alex Bennée <alex.bennee@linaro.org>
>>>>>> Reviewed-by: Pierrick Bouvier <pierrick.bouvier@linaro.org>
>>>> <snip>
>>>>>> +/*
>>>>>> + * Register handles
>>>>>> + *
>>>>>> + * The plugin infrastructure keeps hold of these internal data
>>>>>> + * structures which are presented to plugins as opaque handles. They
>>>>>> + * are global to the system and therefor additions to the hash table
>>>>>> + * must be protected by the @reg_handle_lock.
>>>>>> + *
>>>>>> + * In order to future proof for up-coming heterogeneous work we want
>>>>>> + * different entries for each CPU type while sharing them in the
>>>>>> + * common case of multiple cores of the same type.
>>>>>> + */
>>>>>> +
>>>>>> +static QemuMutex reg_handle_lock;
>>>>>> +
>>>>>> +struct qemu_plugin_register {
>>>>>> +    const char *name;
>>>>>> +    int gdb_reg_num;
>>>>>> +};
>>>>>> +
>>>>>> +static GHashTable *reg_handles; /* hash table of PluginReg */
>>>>>> +
>>>>>> +/* Generate a stable key - would xxhash be overkill? */
>>>>>> +static gpointer cpu_plus_reg_to_key(CPUState *cs, int gdb_regnum)
>>>>>> +{
>>>>>> +    uintptr_t key = (uintptr_t) cs->cc;
>>>>>> +    key ^= gdb_regnum;
>>>>>> +    return GUINT_TO_POINTER(key);
>>>>>> +}
>>>>>
>>>>> I have pointed out this is theoretically prone to collisions and
>>>>> unsafe.
>>>> How is it unsafe? The aim is to share handles for the same CPUClass
>>>> rather than having a unique handle per register/cpu combo.
>>>
>>> THe intention is legitimate, but the implementation is not safe. It
>>> assumes (uintptr)cs->cc ^ gdb_regnum is unique, but there is no such
>>> guarantee. The key of GHashTable must be unique; generating hashes of
>>> keys should be done with hash_func given to g_hash_table_new().
>> This isn't a hash its a non-unique key. It is however unique for
>> the same register on the same class of CPU so for each vCPU in a system
>> can share the same opaque handles.
>> The hashing is done internally by glib. We would assert if there was
>> a
>> duplicate key referring to a different register.
>> I'm unsure what you want here? Do you have a suggestion for the key
>> generation algorithm? As the comment notes I did consider a more complex
>> mixing algorithm using xxhash but that wouldn't guarantee no clash
>> either.
>
> I suggest using a struct that holds both of cs->cc and gdb_regnum, and
> pass g_direct_equal() and g_direct_hash() to g_hash_table_new().

We already do:

        if (!reg_handles) {
            reg_handles = g_hash_table_new(g_direct_hash, g_direct_equal);
        }

But we can't use g_direct_equal with something that exceeds the width of
gpointer as it is a straight equality test of the key. What you are
suggesting requires allocating memory for each key and de-referencing
with a custom GEqualFunc. 

This seems overkill for something that as I have said doesn't happen.
The reason it doesn't happen is you will never see two CPUClass
instances so close to each other they share all bits apart from where
gdb_regnum is being xor'd. We could assert that is the case with
something like:

  #define MAX_GDBREGS 300

  /* Generate a stable key - would xxhash be overkill? */
  static gpointer cpu_plus_reg_to_key(CPUState *cs, int gdb_regnum)
  {
      uintptr_t key = (uintptr_t) cs->cc;

      qemu_build_assert(sizeof(*cs->cc) >= MAX_GDBREGS);
      g_assert(gdb_regnum < MAX_GDBREGS);

      key ^= gdb_regnum;
      return GUINT_TO_POINTER(key);
  }

although MAX_GDBREGS is currently a guess based on aarch64. In practice
though there are so many allocations thing are much farther apart. As we
can see in the one heterogeneous model we support at the moment (the
last 2 CPUs are cortex-r5f's):

  ./qemu-system-aarch64 -M xlnx-zcu102 -audio none -smp 6 -serial mon:stdio -s -S -smp 6
  cpu_common_class_init: k = 0x5565bebf10f0
  arm_cpu_initfn: 0x7f32ee0a8360 -> klass = 0x5565bee50e00
  aarch64_cpu_instance_init: 0x7f32ee0a8360 -> klass = 0x5565bee50e00
  arm_cpu_initfn: 0x7f32ee0be1f0 -> klass = 0x5565bee50e00
  aarch64_cpu_instance_init: 0x7f32ee0be1f0 -> klass = 0x5565bee50e00
  arm_cpu_initfn: 0x7f32ee0d4080 -> klass = 0x5565bee50e00
  aarch64_cpu_instance_init: 0x7f32ee0d4080 -> klass = 0x5565bee50e00
  arm_cpu_initfn: 0x7f32ee0e9f10 -> klass = 0x5565bee50e00
  aarch64_cpu_instance_init: 0x7f32ee0e9f10 -> klass = 0x5565bee50e00
  arm_cpu_initfn: 0x7f32ee0ffda0 -> klass = 0x5565bed0fad0
  arm_cpu_initfn: 0x7f32ee115c30 -> klass = 0x5565bed0fad0
Akihiko Odaki Feb. 22, 2024, 6:37 a.m. UTC | #7
On 2024/02/21 23:14, Alex Bennée wrote:
> Akihiko Odaki <akihiko.odaki@daynix.com> writes:
> 
>> On 2024/02/21 19:02, Alex Bennée wrote:
>>> Akihiko Odaki <akihiko.odaki@daynix.com> writes:
>>>
>>>> On 2024/02/20 23:14, Alex Bennée wrote:
>>>>> Akihiko Odaki <akihiko.odaki@daynix.com> writes:
>>>>>
>>>>>> On 2024/02/17 1:30, Alex Bennée wrote:
>>>>>>> We can only request a list of registers once the vCPU has been
>>>>>>> initialised so the user needs to use either call the get function on
>>>>>>> vCPU initialisation or during the translation phase.
>>>>>>> We don't expose the reg number to the plugin instead hiding it
>>>>>>> behind
>>>>>>> an opaque handle. This allows for a bit of future proofing should the
>>>>>>> internals need to be changed while also being hashed against the
>>>>>>> CPUClass so we can handle different register sets per-vCPU in
>>>>>>> hetrogenous situations.
>>>>>>> Having an internal state within the plugins also allows us to expand
>>>>>>> the interface in future (for example providing callbacks on register
>>>>>>> change if the translator can track changes).
>>>>>>> Resolves: https://gitlab.com/qemu-project/qemu/-/issues/1706
>>>>>>> Cc: Akihiko Odaki <akihiko.odaki@daynix.com>
>>>>>>> Message-Id: <20240103173349.398526-39-alex.bennee@linaro.org>
>>>>>>> Based-on: <20231025093128.33116-18-akihiko.odaki@daynix.com>
>>>>>>> Signed-off-by: Alex Bennée <alex.bennee@linaro.org>
>>>>>>> Reviewed-by: Pierrick Bouvier <pierrick.bouvier@linaro.org>
>>>>> <snip>
>>>>>>> +/*
>>>>>>> + * Register handles
>>>>>>> + *
>>>>>>> + * The plugin infrastructure keeps hold of these internal data
>>>>>>> + * structures which are presented to plugins as opaque handles. They
>>>>>>> + * are global to the system and therefor additions to the hash table
>>>>>>> + * must be protected by the @reg_handle_lock.
>>>>>>> + *
>>>>>>> + * In order to future proof for up-coming heterogeneous work we want
>>>>>>> + * different entries for each CPU type while sharing them in the
>>>>>>> + * common case of multiple cores of the same type.
>>>>>>> + */
>>>>>>> +
>>>>>>> +static QemuMutex reg_handle_lock;
>>>>>>> +
>>>>>>> +struct qemu_plugin_register {
>>>>>>> +    const char *name;
>>>>>>> +    int gdb_reg_num;
>>>>>>> +};
>>>>>>> +
>>>>>>> +static GHashTable *reg_handles; /* hash table of PluginReg */
>>>>>>> +
>>>>>>> +/* Generate a stable key - would xxhash be overkill? */
>>>>>>> +static gpointer cpu_plus_reg_to_key(CPUState *cs, int gdb_regnum)
>>>>>>> +{
>>>>>>> +    uintptr_t key = (uintptr_t) cs->cc;
>>>>>>> +    key ^= gdb_regnum;
>>>>>>> +    return GUINT_TO_POINTER(key);
>>>>>>> +}
>>>>>>
>>>>>> I have pointed out this is theoretically prone to collisions and
>>>>>> unsafe.
>>>>> How is it unsafe? The aim is to share handles for the same CPUClass
>>>>> rather than having a unique handle per register/cpu combo.
>>>>
>>>> THe intention is legitimate, but the implementation is not safe. It
>>>> assumes (uintptr)cs->cc ^ gdb_regnum is unique, but there is no such
>>>> guarantee. The key of GHashTable must be unique; generating hashes of
>>>> keys should be done with hash_func given to g_hash_table_new().
>>> This isn't a hash its a non-unique key. It is however unique for
>>> the same register on the same class of CPU so for each vCPU in a system
>>> can share the same opaque handles.
>>> The hashing is done internally by glib. We would assert if there was
>>> a
>>> duplicate key referring to a different register.
>>> I'm unsure what you want here? Do you have a suggestion for the key
>>> generation algorithm? As the comment notes I did consider a more complex
>>> mixing algorithm using xxhash but that wouldn't guarantee no clash
>>> either.
>>
>> I suggest using a struct that holds both of cs->cc and gdb_regnum, and
>> pass g_direct_equal() and g_direct_hash() to g_hash_table_new().
> 
> We already do:
> 
>          if (!reg_handles) {
>              reg_handles = g_hash_table_new(g_direct_hash, g_direct_equal);
>          }
> 
> But we can't use g_direct_equal with something that exceeds the width of
> gpointer as it is a straight equality test of the key. What you are
> suggesting requires allocating memory for each key and de-referencing
> with a custom GEqualFunc.

My bad. I wrongly remembered g_direct_equal() and g_direct_hash(). It 
indeed seems to need a more complicated solution.

It is possible to write a GEqualFunc and a GHashFunc that consumes a 
struct but it is a chore. How about having a two-level GHashTable? 
reg_handles will be a GHashTable keyed with cs->cc, and another 
GHashTable will be keyed with gdb_regnum.
Alex Bennée Feb. 22, 2024, 10:20 a.m. UTC | #8
Akihiko Odaki <akihiko.odaki@daynix.com> writes:

> On 2024/02/21 23:14, Alex Bennée wrote:
>> Akihiko Odaki <akihiko.odaki@daynix.com> writes:
>> 
>>> On 2024/02/21 19:02, Alex Bennée wrote:
>>>> Akihiko Odaki <akihiko.odaki@daynix.com> writes:
>>>>
>>>>> On 2024/02/20 23:14, Alex Bennée wrote:
>>>>>> Akihiko Odaki <akihiko.odaki@daynix.com> writes:
>>>>>>
>>>>>>> On 2024/02/17 1:30, Alex Bennée wrote:
>>>>>>>> We can only request a list of registers once the vCPU has been
>>>>>>>> initialised so the user needs to use either call the get function on
>>>>>>>> vCPU initialisation or during the translation phase.
>>>>>>>> We don't expose the reg number to the plugin instead hiding it
>>>>>>>> behind
>>>>>>>> an opaque handle. This allows for a bit of future proofing should the
>>>>>>>> internals need to be changed while also being hashed against the
>>>>>>>> CPUClass so we can handle different register sets per-vCPU in
>>>>>>>> hetrogenous situations.
>>>>>>>> Having an internal state within the plugins also allows us to expand
>>>>>>>> the interface in future (for example providing callbacks on register
>>>>>>>> change if the translator can track changes).
>>>>>>>> Resolves: https://gitlab.com/qemu-project/qemu/-/issues/1706
>>>>>>>> Cc: Akihiko Odaki <akihiko.odaki@daynix.com>
>>>>>>>> Message-Id: <20240103173349.398526-39-alex.bennee@linaro.org>
>>>>>>>> Based-on: <20231025093128.33116-18-akihiko.odaki@daynix.com>
>>>>>>>> Signed-off-by: Alex Bennée <alex.bennee@linaro.org>
>>>>>>>> Reviewed-by: Pierrick Bouvier <pierrick.bouvier@linaro.org>
>>>>>> <snip>
>>>>>>>> +/*
>>>>>>>> + * Register handles
>>>>>>>> + *
>>>>>>>> + * The plugin infrastructure keeps hold of these internal data
>>>>>>>> + * structures which are presented to plugins as opaque handles. They
>>>>>>>> + * are global to the system and therefor additions to the hash table
>>>>>>>> + * must be protected by the @reg_handle_lock.
>>>>>>>> + *
>>>>>>>> + * In order to future proof for up-coming heterogeneous work we want
>>>>>>>> + * different entries for each CPU type while sharing them in the
>>>>>>>> + * common case of multiple cores of the same type.
>>>>>>>> + */
>>>>>>>> +
>>>>>>>> +static QemuMutex reg_handle_lock;
>>>>>>>> +
>>>>>>>> +struct qemu_plugin_register {
>>>>>>>> +    const char *name;
>>>>>>>> +    int gdb_reg_num;
>>>>>>>> +};
>>>>>>>> +
>>>>>>>> +static GHashTable *reg_handles; /* hash table of PluginReg */
>>>>>>>> +
>>>>>>>> +/* Generate a stable key - would xxhash be overkill? */
>>>>>>>> +static gpointer cpu_plus_reg_to_key(CPUState *cs, int gdb_regnum)
>>>>>>>> +{
>>>>>>>> +    uintptr_t key = (uintptr_t) cs->cc;
>>>>>>>> +    key ^= gdb_regnum;
>>>>>>>> +    return GUINT_TO_POINTER(key);
>>>>>>>> +}
>>>>>>>
>>>>>>> I have pointed out this is theoretically prone to collisions and
>>>>>>> unsafe.
>>>>>> How is it unsafe? The aim is to share handles for the same CPUClass
>>>>>> rather than having a unique handle per register/cpu combo.
>>>>>
>>>>> THe intention is legitimate, but the implementation is not safe. It
>>>>> assumes (uintptr)cs->cc ^ gdb_regnum is unique, but there is no such
>>>>> guarantee. The key of GHashTable must be unique; generating hashes of
>>>>> keys should be done with hash_func given to g_hash_table_new().
>>>> This isn't a hash its a non-unique key. It is however unique for
>>>> the same register on the same class of CPU so for each vCPU in a system
>>>> can share the same opaque handles.
>>>> The hashing is done internally by glib. We would assert if there was
>>>> a
>>>> duplicate key referring to a different register.
>>>> I'm unsure what you want here? Do you have a suggestion for the key
>>>> generation algorithm? As the comment notes I did consider a more complex
>>>> mixing algorithm using xxhash but that wouldn't guarantee no clash
>>>> either.
>>>
>>> I suggest using a struct that holds both of cs->cc and gdb_regnum, and
>>> pass g_direct_equal() and g_direct_hash() to g_hash_table_new().
>> We already do:
>>          if (!reg_handles) {
>>              reg_handles = g_hash_table_new(g_direct_hash, g_direct_equal);
>>          }
>> But we can't use g_direct_equal with something that exceeds the
>> width of
>> gpointer as it is a straight equality test of the key. What you are
>> suggesting requires allocating memory for each key and de-referencing
>> with a custom GEqualFunc.
>
> My bad. I wrongly remembered g_direct_equal() and g_direct_hash(). It
> indeed seems to need a more complicated solution.
>
> It is possible to write a GEqualFunc and a GHashFunc that consumes a
> struct but it is a chore. How about having a two-level GHashTable?
> reg_handles will be a GHashTable keyed with cs->cc, and another
> GHashTable will be keyed with gdb_regnum.

That still seems overkill for a clash that can't happen. What do you
think about the following:

  /*
   * Generate a stable key shared across CPUs of the same class
   *
   * In order to future proof for up-coming heterogeneous work we want
   * different entries for each CPU type while sharing them in the
   * common case of multiple cores of the same type. This makes the
   * assumption you won't see two CPUClass pointers that are similar
   * enough that the low bits mixed with different registers numbers
   * will give you the same key.
   *
   * The build time assert will fire if CPUClass goes on a sudden diet
   * and we assert further down if we detect two keys representing
   * different regnums. In practice allocations of CPUClass are much
   * farther apart making clashes practically impossible.
   */
  static gpointer cpu_plus_reg_to_key(CPUState *cs, int gdb_regnum)
  {
      uintptr_t key = (uintptr_t) cs->cc;

      /* this protects some of the assumptions above */
      qemu_build_assert(sizeof(*cs->cc) >= 256);

      key ^= gdb_regnum;
      return GUINT_TO_POINTER(key);
  }
Akihiko Odaki Feb. 22, 2024, 1:22 p.m. UTC | #9
On 2024/02/22 19:20, Alex Bennée wrote:
> Akihiko Odaki <akihiko.odaki@daynix.com> writes:
> 
>> On 2024/02/21 23:14, Alex Bennée wrote:
>>> Akihiko Odaki <akihiko.odaki@daynix.com> writes:
>>>
>>>> On 2024/02/21 19:02, Alex Bennée wrote:
>>>>> Akihiko Odaki <akihiko.odaki@daynix.com> writes:
>>>>>
>>>>>> On 2024/02/20 23:14, Alex Bennée wrote:
>>>>>>> Akihiko Odaki <akihiko.odaki@daynix.com> writes:
>>>>>>>
>>>>>>>> On 2024/02/17 1:30, Alex Bennée wrote:
>>>>>>>>> We can only request a list of registers once the vCPU has been
>>>>>>>>> initialised so the user needs to use either call the get function on
>>>>>>>>> vCPU initialisation or during the translation phase.
>>>>>>>>> We don't expose the reg number to the plugin instead hiding it
>>>>>>>>> behind
>>>>>>>>> an opaque handle. This allows for a bit of future proofing should the
>>>>>>>>> internals need to be changed while also being hashed against the
>>>>>>>>> CPUClass so we can handle different register sets per-vCPU in
>>>>>>>>> hetrogenous situations.
>>>>>>>>> Having an internal state within the plugins also allows us to expand
>>>>>>>>> the interface in future (for example providing callbacks on register
>>>>>>>>> change if the translator can track changes).
>>>>>>>>> Resolves: https://gitlab.com/qemu-project/qemu/-/issues/1706
>>>>>>>>> Cc: Akihiko Odaki <akihiko.odaki@daynix.com>
>>>>>>>>> Message-Id: <20240103173349.398526-39-alex.bennee@linaro.org>
>>>>>>>>> Based-on: <20231025093128.33116-18-akihiko.odaki@daynix.com>
>>>>>>>>> Signed-off-by: Alex Bennée <alex.bennee@linaro.org>
>>>>>>>>> Reviewed-by: Pierrick Bouvier <pierrick.bouvier@linaro.org>
>>>>>>> <snip>
>>>>>>>>> +/*
>>>>>>>>> + * Register handles
>>>>>>>>> + *
>>>>>>>>> + * The plugin infrastructure keeps hold of these internal data
>>>>>>>>> + * structures which are presented to plugins as opaque handles. They
>>>>>>>>> + * are global to the system and therefor additions to the hash table
>>>>>>>>> + * must be protected by the @reg_handle_lock.
>>>>>>>>> + *
>>>>>>>>> + * In order to future proof for up-coming heterogeneous work we want
>>>>>>>>> + * different entries for each CPU type while sharing them in the
>>>>>>>>> + * common case of multiple cores of the same type.
>>>>>>>>> + */
>>>>>>>>> +
>>>>>>>>> +static QemuMutex reg_handle_lock;
>>>>>>>>> +
>>>>>>>>> +struct qemu_plugin_register {
>>>>>>>>> +    const char *name;
>>>>>>>>> +    int gdb_reg_num;
>>>>>>>>> +};
>>>>>>>>> +
>>>>>>>>> +static GHashTable *reg_handles; /* hash table of PluginReg */
>>>>>>>>> +
>>>>>>>>> +/* Generate a stable key - would xxhash be overkill? */
>>>>>>>>> +static gpointer cpu_plus_reg_to_key(CPUState *cs, int gdb_regnum)
>>>>>>>>> +{
>>>>>>>>> +    uintptr_t key = (uintptr_t) cs->cc;
>>>>>>>>> +    key ^= gdb_regnum;
>>>>>>>>> +    return GUINT_TO_POINTER(key);
>>>>>>>>> +}
>>>>>>>>
>>>>>>>> I have pointed out this is theoretically prone to collisions and
>>>>>>>> unsafe.
>>>>>>> How is it unsafe? The aim is to share handles for the same CPUClass
>>>>>>> rather than having a unique handle per register/cpu combo.
>>>>>>
>>>>>> THe intention is legitimate, but the implementation is not safe. It
>>>>>> assumes (uintptr)cs->cc ^ gdb_regnum is unique, but there is no such
>>>>>> guarantee. The key of GHashTable must be unique; generating hashes of
>>>>>> keys should be done with hash_func given to g_hash_table_new().
>>>>> This isn't a hash its a non-unique key. It is however unique for
>>>>> the same register on the same class of CPU so for each vCPU in a system
>>>>> can share the same opaque handles.
>>>>> The hashing is done internally by glib. We would assert if there was
>>>>> a
>>>>> duplicate key referring to a different register.
>>>>> I'm unsure what you want here? Do you have a suggestion for the key
>>>>> generation algorithm? As the comment notes I did consider a more complex
>>>>> mixing algorithm using xxhash but that wouldn't guarantee no clash
>>>>> either.
>>>>
>>>> I suggest using a struct that holds both of cs->cc and gdb_regnum, and
>>>> pass g_direct_equal() and g_direct_hash() to g_hash_table_new().
>>> We already do:
>>>           if (!reg_handles) {
>>>               reg_handles = g_hash_table_new(g_direct_hash, g_direct_equal);
>>>           }
>>> But we can't use g_direct_equal with something that exceeds the
>>> width of
>>> gpointer as it is a straight equality test of the key. What you are
>>> suggesting requires allocating memory for each key and de-referencing
>>> with a custom GEqualFunc.
>>
>> My bad. I wrongly remembered g_direct_equal() and g_direct_hash(). It
>> indeed seems to need a more complicated solution.
>>
>> It is possible to write a GEqualFunc and a GHashFunc that consumes a
>> struct but it is a chore. How about having a two-level GHashTable?
>> reg_handles will be a GHashTable keyed with cs->cc, and another
>> GHashTable will be keyed with gdb_regnum.
> 
> That still seems overkill for a clash that can't happen. What do you
> think about the following:
> 
>    /*
>     * Generate a stable key shared across CPUs of the same class
>     *
>     * In order to future proof for up-coming heterogeneous work we want
>     * different entries for each CPU type while sharing them in the
>     * common case of multiple cores of the same type. This makes the
>     * assumption you won't see two CPUClass pointers that are similar
>     * enough that the low bits mixed with different registers numbers
>     * will give you the same key.
>     *
>     * The build time assert will fire if CPUClass goes on a sudden diet
>     * and we assert further down if we detect two keys representing
>     * different regnums. In practice allocations of CPUClass are much
>     * farther apart making clashes practically impossible.
>     */
>    static gpointer cpu_plus_reg_to_key(CPUState *cs, int gdb_regnum)
>    {
>        uintptr_t key = (uintptr_t) cs->cc;
> 
>        /* this protects some of the assumptions above */
>        qemu_build_assert(sizeof(*cs->cc) >= 256);
> 
>        key ^= gdb_regnum;
>        return GUINT_TO_POINTER(key);
>    }


I think the assertion and comments are overkill. Doesn't having a nested 
GHashTable save some words you have to wrote for the assumption?

I'm also not quite convinced that the comments and assertions are enough 
to say this hack is safe; what if some sort of pointer authentication is 
added and shuffles bits? Will this hack be compatible with static and 
dynamic checkers we may have in the future?
Alex Bennée Feb. 22, 2024, 5:27 p.m. UTC | #10
Akihiko Odaki <akihiko.odaki@daynix.com> writes:

> On 2024/02/22 19:20, Alex Bennée wrote:
>> Akihiko Odaki <akihiko.odaki@daynix.com> writes:
>> 
>>> On 2024/02/21 23:14, Alex Bennée wrote:
>>>> Akihiko Odaki <akihiko.odaki@daynix.com> writes:
>>>>
>>>>> On 2024/02/21 19:02, Alex Bennée wrote:
>>>>>> Akihiko Odaki <akihiko.odaki@daynix.com> writes:
>>>>>>
>>>>>>> On 2024/02/20 23:14, Alex Bennée wrote:
>>>>>>>> Akihiko Odaki <akihiko.odaki@daynix.com> writes:
>>>>>>>>
>>>>>>>>> On 2024/02/17 1:30, Alex Bennée wrote:
>>>>>>>>>> We can only request a list of registers once the vCPU has been
>>>>>>>>>> initialised so the user needs to use either call the get function on
>>>>>>>>>> vCPU initialisation or during the translation phase.
>>>>>>>>>> We don't expose the reg number to the plugin instead hiding it
>>>>>>>>>> behind
>>>>>>>>>> an opaque handle. This allows for a bit of future proofing should the
>>>>>>>>>> internals need to be changed while also being hashed against the
>>>>>>>>>> CPUClass so we can handle different register sets per-vCPU in
>>>>>>>>>> hetrogenous situations.
>>>>>>>>>> Having an internal state within the plugins also allows us to expand
>>>>>>>>>> the interface in future (for example providing callbacks on register
>>>>>>>>>> change if the translator can track changes).
>>>>>>>>>> Resolves: https://gitlab.com/qemu-project/qemu/-/issues/1706
>>>>>>>>>> Cc: Akihiko Odaki <akihiko.odaki@daynix.com>
>>>>>>>>>> Message-Id: <20240103173349.398526-39-alex.bennee@linaro.org>
>>>>>>>>>> Based-on: <20231025093128.33116-18-akihiko.odaki@daynix.com>
>>>>>>>>>> Signed-off-by: Alex Bennée <alex.bennee@linaro.org>
>>>>>>>>>> Reviewed-by: Pierrick Bouvier <pierrick.bouvier@linaro.org>
>>>>>>>> <snip>
>>>>>>>>>> +/*
>>>>>>>>>> + * Register handles
>>>>>>>>>> + *
>>>>>>>>>> + * The plugin infrastructure keeps hold of these internal data
>>>>>>>>>> + * structures which are presented to plugins as opaque handles. They
>>>>>>>>>> + * are global to the system and therefor additions to the hash table
>>>>>>>>>> + * must be protected by the @reg_handle_lock.
>>>>>>>>>> + *
>>>>>>>>>> + * In order to future proof for up-coming heterogeneous work we want
>>>>>>>>>> + * different entries for each CPU type while sharing them in the
>>>>>>>>>> + * common case of multiple cores of the same type.
>>>>>>>>>> + */
>>>>>>>>>> +
>>>>>>>>>> +static QemuMutex reg_handle_lock;
>>>>>>>>>> +
>>>>>>>>>> +struct qemu_plugin_register {
>>>>>>>>>> +    const char *name;
>>>>>>>>>> +    int gdb_reg_num;
>>>>>>>>>> +};
>>>>>>>>>> +
>>>>>>>>>> +static GHashTable *reg_handles; /* hash table of PluginReg */
>>>>>>>>>> +
>>>>>>>>>> +/* Generate a stable key - would xxhash be overkill? */
>>>>>>>>>> +static gpointer cpu_plus_reg_to_key(CPUState *cs, int gdb_regnum)
>>>>>>>>>> +{
>>>>>>>>>> +    uintptr_t key = (uintptr_t) cs->cc;
>>>>>>>>>> +    key ^= gdb_regnum;
>>>>>>>>>> +    return GUINT_TO_POINTER(key);
>>>>>>>>>> +}
>>>>>>>>>
>>>>>>>>> I have pointed out this is theoretically prone to collisions and
>>>>>>>>> unsafe.
>>>>>>>> How is it unsafe? The aim is to share handles for the same CPUClass
>>>>>>>> rather than having a unique handle per register/cpu combo.
>>>>>>>
>>>>>>> THe intention is legitimate, but the implementation is not safe. It
>>>>>>> assumes (uintptr)cs->cc ^ gdb_regnum is unique, but there is no such
>>>>>>> guarantee. The key of GHashTable must be unique; generating hashes of
>>>>>>> keys should be done with hash_func given to g_hash_table_new().
>>>>>> This isn't a hash its a non-unique key. It is however unique for
>>>>>> the same register on the same class of CPU so for each vCPU in a system
>>>>>> can share the same opaque handles.
>>>>>> The hashing is done internally by glib. We would assert if there was
>>>>>> a
>>>>>> duplicate key referring to a different register.
>>>>>> I'm unsure what you want here? Do you have a suggestion for the key
>>>>>> generation algorithm? As the comment notes I did consider a more complex
>>>>>> mixing algorithm using xxhash but that wouldn't guarantee no clash
>>>>>> either.
>>>>>
>>>>> I suggest using a struct that holds both of cs->cc and gdb_regnum, and
>>>>> pass g_direct_equal() and g_direct_hash() to g_hash_table_new().
>>>> We already do:
>>>>           if (!reg_handles) {
>>>>               reg_handles = g_hash_table_new(g_direct_hash, g_direct_equal);
>>>>           }
>>>> But we can't use g_direct_equal with something that exceeds the
>>>> width of
>>>> gpointer as it is a straight equality test of the key. What you are
>>>> suggesting requires allocating memory for each key and de-referencing
>>>> with a custom GEqualFunc.
>>>
>>> My bad. I wrongly remembered g_direct_equal() and g_direct_hash(). It
>>> indeed seems to need a more complicated solution.
>>>
>>> It is possible to write a GEqualFunc and a GHashFunc that consumes a
>>> struct but it is a chore. How about having a two-level GHashTable?
>>> reg_handles will be a GHashTable keyed with cs->cc, and another
>>> GHashTable will be keyed with gdb_regnum.
>> That still seems overkill for a clash that can't happen. What do you
>> think about the following:
>>    /*
>>     * Generate a stable key shared across CPUs of the same class
>>     *
>>     * In order to future proof for up-coming heterogeneous work we want
>>     * different entries for each CPU type while sharing them in the
>>     * common case of multiple cores of the same type. This makes the
>>     * assumption you won't see two CPUClass pointers that are similar
>>     * enough that the low bits mixed with different registers numbers
>>     * will give you the same key.
>>     *
>>     * The build time assert will fire if CPUClass goes on a sudden diet
>>     * and we assert further down if we detect two keys representing
>>     * different regnums. In practice allocations of CPUClass are much
>>     * farther apart making clashes practically impossible.
>>     */
>>    static gpointer cpu_plus_reg_to_key(CPUState *cs, int gdb_regnum)
>>    {
>>        uintptr_t key = (uintptr_t) cs->cc;
>>        /* this protects some of the assumptions above */
>>        qemu_build_assert(sizeof(*cs->cc) >= 256);
>>        key ^= gdb_regnum;
>>        return GUINT_TO_POINTER(key);
>>    }
>
>
> I think the assertion and comments are overkill. Doesn't having a
> nested GHashTable save some words you have to wrote for the
> assumption?

A nested hash table for a single entry is overkill.

> I'm also not quite convinced that the comments and assertions are
> enough to say this hack is safe; what if some sort of pointer
> authentication is added and shuffles bits? Will this hack be
> compatible with static and dynamic checkers we may have in the future?

We are not using the value as a pointer so that should be irrelevant
although generally those bits tend to be at the top of pointers so they
can be masked off.

I'm not sure what we are trying to achieve here. I've got something that
works, doesn't fail any tests and has some guards in for potential
future problems. At the same time I'm not prepared to over-engineer the
solution for a theoretical future problem we haven't got yet.

What about if I just key based of gdb_regnum and we accept that that
might break the one heterogeneous system we model today?
Akihiko Odaki Feb. 23, 2024, 10:58 a.m. UTC | #11
On 2024/02/23 2:27, Alex Bennée wrote:
> Akihiko Odaki <akihiko.odaki@daynix.com> writes:
> 
>> On 2024/02/22 19:20, Alex Bennée wrote:
>>> Akihiko Odaki <akihiko.odaki@daynix.com> writes:
>>>
>>>> On 2024/02/21 23:14, Alex Bennée wrote:
>>>>> Akihiko Odaki <akihiko.odaki@daynix.com> writes:
>>>>>
>>>>>> On 2024/02/21 19:02, Alex Bennée wrote:
>>>>>>> Akihiko Odaki <akihiko.odaki@daynix.com> writes:
>>>>>>>
>>>>>>>> On 2024/02/20 23:14, Alex Bennée wrote:
>>>>>>>>> Akihiko Odaki <akihiko.odaki@daynix.com> writes:
>>>>>>>>>
>>>>>>>>>> On 2024/02/17 1:30, Alex Bennée wrote:
>>>>>>>>>>> We can only request a list of registers once the vCPU has been
>>>>>>>>>>> initialised so the user needs to use either call the get function on
>>>>>>>>>>> vCPU initialisation or during the translation phase.
>>>>>>>>>>> We don't expose the reg number to the plugin instead hiding it
>>>>>>>>>>> behind
>>>>>>>>>>> an opaque handle. This allows for a bit of future proofing should the
>>>>>>>>>>> internals need to be changed while also being hashed against the
>>>>>>>>>>> CPUClass so we can handle different register sets per-vCPU in
>>>>>>>>>>> hetrogenous situations.
>>>>>>>>>>> Having an internal state within the plugins also allows us to expand
>>>>>>>>>>> the interface in future (for example providing callbacks on register
>>>>>>>>>>> change if the translator can track changes).
>>>>>>>>>>> Resolves: https://gitlab.com/qemu-project/qemu/-/issues/1706
>>>>>>>>>>> Cc: Akihiko Odaki <akihiko.odaki@daynix.com>
>>>>>>>>>>> Message-Id: <20240103173349.398526-39-alex.bennee@linaro.org>
>>>>>>>>>>> Based-on: <20231025093128.33116-18-akihiko.odaki@daynix.com>
>>>>>>>>>>> Signed-off-by: Alex Bennée <alex.bennee@linaro.org>
>>>>>>>>>>> Reviewed-by: Pierrick Bouvier <pierrick.bouvier@linaro.org>
>>>>>>>>> <snip>
>>>>>>>>>>> +/*
>>>>>>>>>>> + * Register handles
>>>>>>>>>>> + *
>>>>>>>>>>> + * The plugin infrastructure keeps hold of these internal data
>>>>>>>>>>> + * structures which are presented to plugins as opaque handles. They
>>>>>>>>>>> + * are global to the system and therefor additions to the hash table
>>>>>>>>>>> + * must be protected by the @reg_handle_lock.
>>>>>>>>>>> + *
>>>>>>>>>>> + * In order to future proof for up-coming heterogeneous work we want
>>>>>>>>>>> + * different entries for each CPU type while sharing them in the
>>>>>>>>>>> + * common case of multiple cores of the same type.
>>>>>>>>>>> + */
>>>>>>>>>>> +
>>>>>>>>>>> +static QemuMutex reg_handle_lock;
>>>>>>>>>>> +
>>>>>>>>>>> +struct qemu_plugin_register {
>>>>>>>>>>> +    const char *name;
>>>>>>>>>>> +    int gdb_reg_num;
>>>>>>>>>>> +};
>>>>>>>>>>> +
>>>>>>>>>>> +static GHashTable *reg_handles; /* hash table of PluginReg */
>>>>>>>>>>> +
>>>>>>>>>>> +/* Generate a stable key - would xxhash be overkill? */
>>>>>>>>>>> +static gpointer cpu_plus_reg_to_key(CPUState *cs, int gdb_regnum)
>>>>>>>>>>> +{
>>>>>>>>>>> +    uintptr_t key = (uintptr_t) cs->cc;
>>>>>>>>>>> +    key ^= gdb_regnum;
>>>>>>>>>>> +    return GUINT_TO_POINTER(key);
>>>>>>>>>>> +}
>>>>>>>>>>
>>>>>>>>>> I have pointed out this is theoretically prone to collisions and
>>>>>>>>>> unsafe.
>>>>>>>>> How is it unsafe? The aim is to share handles for the same CPUClass
>>>>>>>>> rather than having a unique handle per register/cpu combo.
>>>>>>>>
>>>>>>>> THe intention is legitimate, but the implementation is not safe. It
>>>>>>>> assumes (uintptr)cs->cc ^ gdb_regnum is unique, but there is no such
>>>>>>>> guarantee. The key of GHashTable must be unique; generating hashes of
>>>>>>>> keys should be done with hash_func given to g_hash_table_new().
>>>>>>> This isn't a hash its a non-unique key. It is however unique for
>>>>>>> the same register on the same class of CPU so for each vCPU in a system
>>>>>>> can share the same opaque handles.
>>>>>>> The hashing is done internally by glib. We would assert if there was
>>>>>>> a
>>>>>>> duplicate key referring to a different register.
>>>>>>> I'm unsure what you want here? Do you have a suggestion for the key
>>>>>>> generation algorithm? As the comment notes I did consider a more complex
>>>>>>> mixing algorithm using xxhash but that wouldn't guarantee no clash
>>>>>>> either.
>>>>>>
>>>>>> I suggest using a struct that holds both of cs->cc and gdb_regnum, and
>>>>>> pass g_direct_equal() and g_direct_hash() to g_hash_table_new().
>>>>> We already do:
>>>>>            if (!reg_handles) {
>>>>>                reg_handles = g_hash_table_new(g_direct_hash, g_direct_equal);
>>>>>            }
>>>>> But we can't use g_direct_equal with something that exceeds the
>>>>> width of
>>>>> gpointer as it is a straight equality test of the key. What you are
>>>>> suggesting requires allocating memory for each key and de-referencing
>>>>> with a custom GEqualFunc.
>>>>
>>>> My bad. I wrongly remembered g_direct_equal() and g_direct_hash(). It
>>>> indeed seems to need a more complicated solution.
>>>>
>>>> It is possible to write a GEqualFunc and a GHashFunc that consumes a
>>>> struct but it is a chore. How about having a two-level GHashTable?
>>>> reg_handles will be a GHashTable keyed with cs->cc, and another
>>>> GHashTable will be keyed with gdb_regnum.
>>> That still seems overkill for a clash that can't happen. What do you
>>> think about the following:
>>>     /*
>>>      * Generate a stable key shared across CPUs of the same class
>>>      *
>>>      * In order to future proof for up-coming heterogeneous work we want
>>>      * different entries for each CPU type while sharing them in the
>>>      * common case of multiple cores of the same type. This makes the
>>>      * assumption you won't see two CPUClass pointers that are similar
>>>      * enough that the low bits mixed with different registers numbers
>>>      * will give you the same key.
>>>      *
>>>      * The build time assert will fire if CPUClass goes on a sudden diet
>>>      * and we assert further down if we detect two keys representing
>>>      * different regnums. In practice allocations of CPUClass are much
>>>      * farther apart making clashes practically impossible.
>>>      */
>>>     static gpointer cpu_plus_reg_to_key(CPUState *cs, int gdb_regnum)
>>>     {
>>>         uintptr_t key = (uintptr_t) cs->cc;
>>>         /* this protects some of the assumptions above */
>>>         qemu_build_assert(sizeof(*cs->cc) >= 256);
>>>         key ^= gdb_regnum;
>>>         return GUINT_TO_POINTER(key);
>>>     }
>>
>>
>> I think the assertion and comments are overkill. Doesn't having a
>> nested GHashTable save some words you have to wrote for the
>> assumption?
> 
> A nested hash table for a single entry is overkill.

You mean that the first level will be indexed by only one CPUClass (or 
few if we support a heterogeneous system).

I think it's still OK though. It's not like we will need more code when 
having few entries.

> 
>> I'm also not quite convinced that the comments and assertions are
>> enough to say this hack is safe; what if some sort of pointer
>> authentication is added and shuffles bits? Will this hack be
>> compatible with static and dynamic checkers we may have in the future?
> 
> We are not using the value as a pointer so that should be irrelevant
> although generally those bits tend to be at the top of pointers so they
> can be masked off.
> 
> I'm not sure what we are trying to achieve here. I've got something that
> works, doesn't fail any tests and has some guards in for potential
> future problems. At the same time I'm not prepared to over-engineer the
> solution for a theoretical future problem we haven't got yet.
> 
> What about if I just key based of gdb_regnum and we accept that that
> might break the one heterogeneous system we model today?
> 

That's the best option in my opinion. gdbstub won't work well with such 
a system anyway, and fixing it will need something similar to 
GHashTable. But if I would fix gdbstub for a heterogeneous system, I 
would add a field to CPUClass instead of having a GHashTable keyed with 
tuples of CPUClass pointers and register numbers. It should be fine 
considering that CPUState already has gdbstub-specific fields like gdb_regs.
Alex Bennée Feb. 23, 2024, 11:44 a.m. UTC | #12
Akihiko Odaki <akihiko.odaki@daynix.com> writes:

> On 2024/02/23 2:27, Alex Bennée wrote:
>> Akihiko Odaki <akihiko.odaki@daynix.com> writes:
>> 
>>> On 2024/02/22 19:20, Alex Bennée wrote:
>>>> Akihiko Odaki <akihiko.odaki@daynix.com> writes:
>>>>
>>>>> On 2024/02/21 23:14, Alex Bennée wrote:
>>>>>> Akihiko Odaki <akihiko.odaki@daynix.com> writes:
>>>>>>
>>>>>>> On 2024/02/21 19:02, Alex Bennée wrote:
>>>>>>>> Akihiko Odaki <akihiko.odaki@daynix.com> writes:
>>>>>>>>
>>>>>>>>> On 2024/02/20 23:14, Alex Bennée wrote:
>>>>>>>>>> Akihiko Odaki <akihiko.odaki@daynix.com> writes:
>>>>>>>>>>
>>>>>>>>>>> On 2024/02/17 1:30, Alex Bennée wrote:
>>>>>>>>>>>> We can only request a list of registers once the vCPU has been
>>>>>>>>>>>> initialised so the user needs to use either call the get function on
>>>>>>>>>>>> vCPU initialisation or during the translation phase.
>>>>>>>>>>>> We don't expose the reg number to the plugin instead hiding it
>>>>>>>>>>>> behind
>>>>>>>>>>>> an opaque handle. This allows for a bit of future proofing should the
>>>>>>>>>>>> internals need to be changed while also being hashed against the
>>>>>>>>>>>> CPUClass so we can handle different register sets per-vCPU in
>>>>>>>>>>>> hetrogenous situations.
>>>>>>>>>>>> Having an internal state within the plugins also allows us to expand
>>>>>>>>>>>> the interface in future (for example providing callbacks on register
>>>>>>>>>>>> change if the translator can track changes).
>>>>>>>>>>>> Resolves: https://gitlab.com/qemu-project/qemu/-/issues/1706
>>>>>>>>>>>> Cc: Akihiko Odaki <akihiko.odaki@daynix.com>
>>>>>>>>>>>> Message-Id: <20240103173349.398526-39-alex.bennee@linaro.org>
>>>>>>>>>>>> Based-on: <20231025093128.33116-18-akihiko.odaki@daynix.com>
>>>>>>>>>>>> Signed-off-by: Alex Bennée <alex.bennee@linaro.org>
>>>>>>>>>>>> Reviewed-by: Pierrick Bouvier <pierrick.bouvier@linaro.org>
>>>>>>>>>> <snip>
>>>>>>>>>>>> +/*
>>>>>>>>>>>> + * Register handles
>>>>>>>>>>>> + *
>>>>>>>>>>>> + * The plugin infrastructure keeps hold of these internal data
>>>>>>>>>>>> + * structures which are presented to plugins as opaque handles. They
>>>>>>>>>>>> + * are global to the system and therefor additions to the hash table
>>>>>>>>>>>> + * must be protected by the @reg_handle_lock.
>>>>>>>>>>>> + *
>>>>>>>>>>>> + * In order to future proof for up-coming heterogeneous work we want
>>>>>>>>>>>> + * different entries for each CPU type while sharing them in the
>>>>>>>>>>>> + * common case of multiple cores of the same type.
>>>>>>>>>>>> + */
>>>>>>>>>>>> +
>>>>>>>>>>>> +static QemuMutex reg_handle_lock;
>>>>>>>>>>>> +
>>>>>>>>>>>> +struct qemu_plugin_register {
>>>>>>>>>>>> +    const char *name;
>>>>>>>>>>>> +    int gdb_reg_num;
>>>>>>>>>>>> +};
>>>>>>>>>>>> +
>>>>>>>>>>>> +static GHashTable *reg_handles; /* hash table of PluginReg */
>>>>>>>>>>>> +
>>>>>>>>>>>> +/* Generate a stable key - would xxhash be overkill? */
>>>>>>>>>>>> +static gpointer cpu_plus_reg_to_key(CPUState *cs, int gdb_regnum)
>>>>>>>>>>>> +{
>>>>>>>>>>>> +    uintptr_t key = (uintptr_t) cs->cc;
>>>>>>>>>>>> +    key ^= gdb_regnum;
>>>>>>>>>>>> +    return GUINT_TO_POINTER(key);
>>>>>>>>>>>> +}
>>>>>>>>>>>
>>>>>>>>>>> I have pointed out this is theoretically prone to collisions and
>>>>>>>>>>> unsafe.
>>>>>>>>>> How is it unsafe? The aim is to share handles for the same CPUClass
>>>>>>>>>> rather than having a unique handle per register/cpu combo.
>>>>>>>>>
>>>>>>>>> THe intention is legitimate, but the implementation is not safe. It
>>>>>>>>> assumes (uintptr)cs->cc ^ gdb_regnum is unique, but there is no such
>>>>>>>>> guarantee. The key of GHashTable must be unique; generating hashes of
>>>>>>>>> keys should be done with hash_func given to g_hash_table_new().
>>>>>>>> This isn't a hash its a non-unique key. It is however unique for
>>>>>>>> the same register on the same class of CPU so for each vCPU in a system
>>>>>>>> can share the same opaque handles.
>>>>>>>> The hashing is done internally by glib. We would assert if there was
>>>>>>>> a
>>>>>>>> duplicate key referring to a different register.
>>>>>>>> I'm unsure what you want here? Do you have a suggestion for the key
>>>>>>>> generation algorithm? As the comment notes I did consider a more complex
>>>>>>>> mixing algorithm using xxhash but that wouldn't guarantee no clash
>>>>>>>> either.
>>>>>>>
>>>>>>> I suggest using a struct that holds both of cs->cc and gdb_regnum, and
>>>>>>> pass g_direct_equal() and g_direct_hash() to g_hash_table_new().
>>>>>> We already do:
>>>>>>            if (!reg_handles) {
>>>>>>                reg_handles = g_hash_table_new(g_direct_hash, g_direct_equal);
>>>>>>            }
>>>>>> But we can't use g_direct_equal with something that exceeds the
>>>>>> width of
>>>>>> gpointer as it is a straight equality test of the key. What you are
>>>>>> suggesting requires allocating memory for each key and de-referencing
>>>>>> with a custom GEqualFunc.
>>>>>
>>>>> My bad. I wrongly remembered g_direct_equal() and g_direct_hash(). It
>>>>> indeed seems to need a more complicated solution.
>>>>>
>>>>> It is possible to write a GEqualFunc and a GHashFunc that consumes a
>>>>> struct but it is a chore. How about having a two-level GHashTable?
>>>>> reg_handles will be a GHashTable keyed with cs->cc, and another
>>>>> GHashTable will be keyed with gdb_regnum.
>>>> That still seems overkill for a clash that can't happen. What do you
>>>> think about the following:
>>>>     /*
>>>>      * Generate a stable key shared across CPUs of the same class
>>>>      *
>>>>      * In order to future proof for up-coming heterogeneous work we want
>>>>      * different entries for each CPU type while sharing them in the
>>>>      * common case of multiple cores of the same type. This makes the
>>>>      * assumption you won't see two CPUClass pointers that are similar
>>>>      * enough that the low bits mixed with different registers numbers
>>>>      * will give you the same key.
>>>>      *
>>>>      * The build time assert will fire if CPUClass goes on a sudden diet
>>>>      * and we assert further down if we detect two keys representing
>>>>      * different regnums. In practice allocations of CPUClass are much
>>>>      * farther apart making clashes practically impossible.
>>>>      */
>>>>     static gpointer cpu_plus_reg_to_key(CPUState *cs, int gdb_regnum)
>>>>     {
>>>>         uintptr_t key = (uintptr_t) cs->cc;
>>>>         /* this protects some of the assumptions above */
>>>>         qemu_build_assert(sizeof(*cs->cc) >= 256);
>>>>         key ^= gdb_regnum;
>>>>         return GUINT_TO_POINTER(key);
>>>>     }
>>>
>>>
>>> I think the assertion and comments are overkill. Doesn't having a
>>> nested GHashTable save some words you have to wrote for the
>>> assumption?
>> A nested hash table for a single entry is overkill.
>
> You mean that the first level will be indexed by only one CPUClass (or
> few if we support a heterogeneous system).
>
> I think it's still OK though. It's not like we will need more code
> when having few entries.
>
>> 
>>> I'm also not quite convinced that the comments and assertions are
>>> enough to say this hack is safe; what if some sort of pointer
>>> authentication is added and shuffles bits? Will this hack be
>>> compatible with static and dynamic checkers we may have in the future?
>> We are not using the value as a pointer so that should be irrelevant
>> although generally those bits tend to be at the top of pointers so they
>> can be masked off.
>> I'm not sure what we are trying to achieve here. I've got something
>> that
>> works, doesn't fail any tests and has some guards in for potential
>> future problems. At the same time I'm not prepared to over-engineer the
>> solution for a theoretical future problem we haven't got yet.
>> What about if I just key based of gdb_regnum and we accept that that
>> might break the one heterogeneous system we model today?
>> 
>
> That's the best option in my opinion. gdbstub won't work well with
> such a system anyway, and fixing it will need something similar to
> GHashTable. But if I would fix gdbstub for a heterogeneous system, I
> would add a field to CPUClass instead of having a GHashTable keyed
> with tuples of CPUClass pointers and register numbers. It should be
> fine considering that CPUState already has gdbstub-specific fields
> like gdb_regs.

It would be nice to move all register code into CPUClass to avoid
repeating ourselves but I suspect that is quite an invasive change for a
later series. Currently all the CPUClass values are set on init and
shouldn't really be changed after that otherwise we'll have to start
messing with locking.
Alex Bennée Feb. 23, 2024, 4:24 p.m. UTC | #13
Alex Bennée <alex.bennee@linaro.org> writes:

> Akihiko Odaki <akihiko.odaki@daynix.com> writes:
<snip>
>>> What about if I just key based of gdb_regnum and we accept that that
>>> might break the one heterogeneous system we model today?
>>> 
>>
>> That's the best option in my opinion. gdbstub won't work well with
>> such a system anyway, and fixing it will need something similar to
>> GHashTable. But if I would fix gdbstub for a heterogeneous system, I
>> would add a field to CPUClass instead of having a GHashTable keyed
>> with tuples of CPUClass pointers and register numbers. It should be
>> fine considering that CPUState already has gdbstub-specific fields
>> like gdb_regs.
>
> It would be nice to move all register code into CPUClass to avoid
> repeating ourselves but I suspect that is quite an invasive change for a
> later series. Currently all the CPUClass values are set on init and
> shouldn't really be changed after that otherwise we'll have to start
> messing with locking.

Peter pointed out we can see different register sets for the same
CPUClass but with different features enabled which kills that idea. I've
just sent v2 which re-factors the plugin data a little and stores a per
CPUPluginState hash table.
diff mbox series

Patch

diff --git a/include/qemu/qemu-plugin.h b/include/qemu/qemu-plugin.h
index 93981f8f89f..3b6b18058d2 100644
--- a/include/qemu/qemu-plugin.h
+++ b/include/qemu/qemu-plugin.h
@@ -11,6 +11,7 @@ 
 #ifndef QEMU_QEMU_PLUGIN_H
 #define QEMU_QEMU_PLUGIN_H
 
+#include <glib.h>
 #include <inttypes.h>
 #include <stdbool.h>
 #include <stddef.h>
@@ -229,8 +230,8 @@  struct qemu_plugin_insn;
  * @QEMU_PLUGIN_CB_R_REGS: callback reads the CPU's regs
  * @QEMU_PLUGIN_CB_RW_REGS: callback reads and writes the CPU's regs
  *
- * Note: currently unused, plugins cannot read or change system
- * register state.
+ * Note: currently QEMU_PLUGIN_CB_RW_REGS is unused, plugins cannot change
+ * system register state.
  */
 enum qemu_plugin_cb_flags {
     QEMU_PLUGIN_CB_NO_REGS,
@@ -707,4 +708,47 @@  uint64_t qemu_plugin_end_code(void);
 QEMU_PLUGIN_API
 uint64_t qemu_plugin_entry_code(void);
 
+/** struct qemu_plugin_register - Opaque handle for register access */
+struct qemu_plugin_register;
+
+/**
+ * typedef qemu_plugin_reg_descriptor - register descriptions
+ *
+ * @handle: opaque handle for retrieving value with qemu_plugin_read_register
+ * @name: register name
+ * @feature: optional feature descriptor, can be NULL
+ */
+typedef struct {
+    struct qemu_plugin_register *handle;
+    const char *name;
+    const char *feature;
+} qemu_plugin_reg_descriptor;
+
+/**
+ * qemu_plugin_get_registers() - return register list for current vCPU
+ *
+ * Returns a GArray of qemu_plugin_reg_descriptor or NULL. Caller
+ * frees the array (but not the const strings).
+ *
+ * Should be used from a qemu_plugin_register_vcpu_init_cb() callback
+ * after the vCPU is initialised, i.e. in the vCPU context.
+ */
+GArray *qemu_plugin_get_registers(void);
+
+/**
+ * qemu_plugin_read_register() - read register for current vCPU
+ *
+ * @handle: a @qemu_plugin_reg_handle handle
+ * @buf: A GByteArray for the data owned by the plugin
+ *
+ * This function is only available in a context that register read access is
+ * explicitly requested via the QEMU_PLUGIN_CB_R_REGS flag.
+ *
+ * Returns the size of the read register. The content of @buf is in target byte
+ * order. On failure returns -1
+ */
+int qemu_plugin_read_register(struct qemu_plugin_register *handle,
+                              GByteArray *buf);
+
+
 #endif /* QEMU_QEMU_PLUGIN_H */
diff --git a/plugins/api.c b/plugins/api.c
index 54df72c1c00..483f04e85e4 100644
--- a/plugins/api.c
+++ b/plugins/api.c
@@ -8,6 +8,7 @@ 
  *
  *  qemu_plugin_tb
  *  qemu_plugin_insn
+ *  qemu_plugin_register
  *
  * Which can then be passed back into the API to do additional things.
  * As such all the public functions in here are exported in
@@ -35,10 +36,12 @@ 
  */
 
 #include "qemu/osdep.h"
+#include "qemu/main-loop.h"
 #include "qemu/plugin.h"
 #include "qemu/log.h"
 #include "tcg/tcg.h"
 #include "exec/exec-all.h"
+#include "exec/gdbstub.h"
 #include "exec/ram_addr.h"
 #include "disas/disas.h"
 #include "plugin.h"
@@ -410,3 +413,107 @@  uint64_t qemu_plugin_entry_code(void)
 #endif
     return entry;
 }
+
+/*
+ * Register handles
+ *
+ * The plugin infrastructure keeps hold of these internal data
+ * structures which are presented to plugins as opaque handles. They
+ * are global to the system and therefor additions to the hash table
+ * must be protected by the @reg_handle_lock.
+ *
+ * In order to future proof for up-coming heterogeneous work we want
+ * different entries for each CPU type while sharing them in the
+ * common case of multiple cores of the same type.
+ */
+
+static QemuMutex reg_handle_lock;
+
+struct qemu_plugin_register {
+    const char *name;
+    int gdb_reg_num;
+};
+
+static GHashTable *reg_handles; /* hash table of PluginReg */
+
+/* Generate a stable key - would xxhash be overkill? */
+static gpointer cpu_plus_reg_to_key(CPUState *cs, int gdb_regnum)
+{
+    uintptr_t key = (uintptr_t) cs->cc;
+    key ^= gdb_regnum;
+    return GUINT_TO_POINTER(key);
+}
+
+/*
+ * Create register handles.
+ *
+ * We need to create a handle for each register so the plugin
+ * infrastructure can call gdbstub to read a register. We also
+ * construct a result array with those handles and some ancillary data
+ * the plugin might find useful.
+ */
+
+static GArray *create_register_handles(CPUState *cs, GArray *gdbstub_regs)
+{
+    GArray *find_data = g_array_new(true, true,
+                                    sizeof(qemu_plugin_reg_descriptor));
+
+    WITH_QEMU_LOCK_GUARD(&reg_handle_lock) {
+
+        if (!reg_handles) {
+            reg_handles = g_hash_table_new(g_direct_hash, g_direct_equal);
+        }
+
+        for (int i = 0; i < gdbstub_regs->len; i++) {
+            GDBRegDesc *grd = &g_array_index(gdbstub_regs, GDBRegDesc, i);
+            gpointer key = cpu_plus_reg_to_key(cs, grd->gdb_reg);
+            struct qemu_plugin_register *val = g_hash_table_lookup(reg_handles,
+                                                                   key);
+
+            /* skip "un-named" regs */
+            if (!grd->name) {
+                continue;
+            }
+
+            /* Doesn't exist, create one */
+            if (!val) {
+                val = g_new0(struct qemu_plugin_register, 1);
+                val->gdb_reg_num = grd->gdb_reg;
+                val->name = g_intern_string(grd->name);
+
+                g_hash_table_insert(reg_handles, key, val);
+            }
+
+            /* Create a record for the plugin */
+            qemu_plugin_reg_descriptor desc = {
+                .handle = val,
+                .name = val->name,
+                .feature = g_intern_string(grd->feature_name)
+            };
+            g_array_append_val(find_data, desc);
+        }
+    }
+
+    return find_data;
+}
+
+GArray *qemu_plugin_get_registers(void)
+{
+    g_assert(current_cpu);
+
+    g_autoptr(GArray) regs = gdb_get_register_list(current_cpu);
+    return regs->len ? create_register_handles(current_cpu, regs) : NULL;
+}
+
+int qemu_plugin_read_register(struct qemu_plugin_register *reg, GByteArray *buf)
+{
+    g_assert(current_cpu);
+
+    return gdb_read_register(current_cpu, buf, reg->gdb_reg_num);
+}
+
+static void __attribute__((__constructor__)) qemu_api_init(void)
+{
+    qemu_mutex_init(&reg_handle_lock);
+
+}
diff --git a/plugins/qemu-plugins.symbols b/plugins/qemu-plugins.symbols
index adb67608598..27fe97239be 100644
--- a/plugins/qemu-plugins.symbols
+++ b/plugins/qemu-plugins.symbols
@@ -3,6 +3,7 @@ 
   qemu_plugin_end_code;
   qemu_plugin_entry_code;
   qemu_plugin_get_hwaddr;
+  qemu_plugin_get_registers;
   qemu_plugin_hwaddr_device_name;
   qemu_plugin_hwaddr_is_io;
   qemu_plugin_hwaddr_phys_addr;
@@ -19,6 +20,7 @@ 
   qemu_plugin_num_vcpus;
   qemu_plugin_outs;
   qemu_plugin_path_to_binary;
+  qemu_plugin_read_register;
   qemu_plugin_register_atexit_cb;
   qemu_plugin_register_flush_cb;
   qemu_plugin_register_vcpu_exit_cb;