* [PATCH v3 0/2] plugins: add plugin API to read guest memory
@ 2024-08-27 21:53 Rowan Hart
2024-08-27 21:53 ` [PATCH v3 1/2] " Rowan Hart
` (2 more replies)
0 siblings, 3 replies; 4+ messages in thread
From: Rowan Hart @ 2024-08-27 21:53 UTC (permalink / raw)
To: qemu-devel
Cc: Pierrick Bouvier, Alexandre Iooss, Alex Bennée,
Mahmoud Mandour, Rowan Hart
This patch adds one API function to the QEMU plugin API
bool qemu_plugin_read_memory_vaddr(vaddr, GByteArray *, size_t);
The API allows reading memory from an arbitrary guest virtual
address, which is useful for many things but the motivating examples
are:
* Virtual Machine Introspection (VMI)
* Accurate and easier execution trace extraction
* Debugging and logging tools
An example of its use is added to the existing syscalls plugin,
which now has an option to hexdump the buf argument to any write(2)
syscalls which occur.
For v3, fixed a missing '*' in a comment which caused a doc build issue.
Rowan Hart (2):
plugins: add plugin API to read guest memory
plugins: add option to dump write argument to syscall plugin
docs/about/emulation.rst | 14 ++++-
include/qemu/qemu-plugin.h | 32 +++++++++-
plugins/api.c | 20 ++++++
plugins/qemu-plugins.symbols | 1 +
tests/tcg/plugins/syscall.c | 117 +++++++++++++++++++++++++++++++++++
5 files changed, 182 insertions(+), 2 deletions(-)
--
2.46.0
^ permalink raw reply [flat|nested] 4+ messages in thread
* [PATCH v3 1/2] plugins: add plugin API to read guest memory
2024-08-27 21:53 [PATCH v3 0/2] plugins: add plugin API to read guest memory Rowan Hart
@ 2024-08-27 21:53 ` Rowan Hart
2024-08-27 21:53 ` [PATCH v3 2/2] plugins: add option to dump write argument to syscall plugin Rowan Hart
2024-09-05 15:27 ` [PATCH v3 0/2] plugins: add plugin API to read guest memory Alex Bennée
2 siblings, 0 replies; 4+ messages in thread
From: Rowan Hart @ 2024-08-27 21:53 UTC (permalink / raw)
To: qemu-devel
Cc: Pierrick Bouvier, Alexandre Iooss, Alex Bennée,
Mahmoud Mandour, Rowan Hart
Signed-off-by: Rowan Hart <rowanbhart@gmail.com>
Reviewed-by: Pierrick Bouvier <pierrick.bouvier@linaro.org>
---
include/qemu/qemu-plugin.h | 32 +++++++++++++++++++++++++++++++-
plugins/api.c | 20 ++++++++++++++++++++
plugins/qemu-plugins.symbols | 1 +
3 files changed, 52 insertions(+), 1 deletion(-)
diff --git a/include/qemu/qemu-plugin.h b/include/qemu/qemu-plugin.h
index c71c705b69..e4068f823b 100644
--- a/include/qemu/qemu-plugin.h
+++ b/include/qemu/qemu-plugin.h
@@ -57,11 +57,19 @@ typedef uint64_t qemu_plugin_id_t;
* - Remove qemu_plugin_register_vcpu_{tb, insn, mem}_exec_inline.
* Those functions are replaced by *_per_vcpu variants, which guarantee
* thread-safety for operations.
+ *
+ * version 3:
+ * - modified arguments and return value of qemu_plugin_insn_data to copy
+ * the data into a user-provided buffer instead of returning a pointer
+ * to the data.
+ *
+ * version 4:
+ * - added qemu_plugin_read_memory_vaddr
*/
extern QEMU_PLUGIN_EXPORT int qemu_plugin_version;
-#define QEMU_PLUGIN_VERSION 3
+#define QEMU_PLUGIN_VERSION 4
/**
* struct qemu_info_t - system information for plugins
@@ -852,6 +860,28 @@ typedef struct {
QEMU_PLUGIN_API
GArray *qemu_plugin_get_registers(void);
+/**
+ * qemu_plugin_read_memory_vaddr() - read from memory using a virtual address
+ *
+ * @addr: A virtual address to read from
+ * @data: A byte array to store data into
+ * @len: The number of bytes to read, starting from @addr
+ *
+ * @len bytes of data is read starting at @addr and stored into @data. If @data
+ * is not large enough to hold @len bytes, it will be expanded to the necessary
+ * size, reallocating if necessary. @len must be greater than 0.
+ *
+ * This function does not ensure writes are flushed prior to reading, so
+ * callers should take care when calling this function in plugin callbacks to
+ * avoid attempting to read data which may not yet be written and should use
+ * the memory callback API instead.
+ *
+ * Returns true on success and false on failure.
+ */
+QEMU_PLUGIN_API
+bool qemu_plugin_read_memory_vaddr(uint64_t addr,
+ GByteArray *data, size_t len);
+
/**
* qemu_plugin_read_register() - read register for current vCPU
*
diff --git a/plugins/api.c b/plugins/api.c
index 2ff13d09de..cb7d818918 100644
--- a/plugins/api.c
+++ b/plugins/api.c
@@ -527,6 +527,26 @@ GArray *qemu_plugin_get_registers(void)
return create_register_handles(regs);
}
+bool qemu_plugin_read_memory_vaddr(vaddr addr, GByteArray *data, size_t len)
+{
+ g_assert(current_cpu);
+
+ if (len == 0) {
+ return false;
+ }
+
+ g_byte_array_set_size(data, len);
+
+ int result = cpu_memory_rw_debug(current_cpu, addr, data->data,
+ data->len, 0);
+
+ if (result < 0) {
+ return false;
+ }
+
+ return true;
+}
+
int qemu_plugin_read_register(struct qemu_plugin_register *reg, GByteArray *buf)
{
g_assert(current_cpu);
diff --git a/plugins/qemu-plugins.symbols b/plugins/qemu-plugins.symbols
index ca773d8d9f..3ad479a924 100644
--- a/plugins/qemu-plugins.symbols
+++ b/plugins/qemu-plugins.symbols
@@ -20,6 +20,7 @@
qemu_plugin_num_vcpus;
qemu_plugin_outs;
qemu_plugin_path_to_binary;
+ qemu_plugin_read_memory_vaddr;
qemu_plugin_read_register;
qemu_plugin_register_atexit_cb;
qemu_plugin_register_flush_cb;
--
2.46.0
^ permalink raw reply related [flat|nested] 4+ messages in thread
* [PATCH v3 2/2] plugins: add option to dump write argument to syscall plugin
2024-08-27 21:53 [PATCH v3 0/2] plugins: add plugin API to read guest memory Rowan Hart
2024-08-27 21:53 ` [PATCH v3 1/2] " Rowan Hart
@ 2024-08-27 21:53 ` Rowan Hart
2024-09-05 15:27 ` [PATCH v3 0/2] plugins: add plugin API to read guest memory Alex Bennée
2 siblings, 0 replies; 4+ messages in thread
From: Rowan Hart @ 2024-08-27 21:53 UTC (permalink / raw)
To: qemu-devel
Cc: Pierrick Bouvier, Alexandre Iooss, Alex Bennée,
Mahmoud Mandour, Rowan Hart
Signed-off-by: Rowan Hart <rowanbhart@gmail.com>
Reviewed-by: Pierrick Bouvier <pierrick.bouvier@linaro.org>
Tested-by: Pierrick Bouvier <pierrick.bouvier@linaro.org>
---
docs/about/emulation.rst | 14 ++++-
tests/tcg/plugins/syscall.c | 117 ++++++++++++++++++++++++++++++++++++
2 files changed, 130 insertions(+), 1 deletion(-)
diff --git a/docs/about/emulation.rst b/docs/about/emulation.rst
index eea1261baa..e85d494ff0 100644
--- a/docs/about/emulation.rst
+++ b/docs/about/emulation.rst
@@ -388,6 +388,19 @@ run::
160 1 0
135 1 0
+Behaviour can be tweaked with the following arguments:
+
+.. list-table:: Syscall plugin arguments
+ :widths: 20 80
+ :header-rows: 1
+
+ * - Option
+ - Description
+ * - print=true|false
+ - Print the number of times each syscall is called
+ * - log_writes=true|false
+ - Log the buffer of each write syscall in hexdump format
+
Test inline operations
......................
@@ -777,4 +790,3 @@ Other emulation features
When running system emulation you can also enable deterministic
execution which allows for repeatable record/replay debugging. See
:ref:`Record/Replay<replay>` for more details.
-
diff --git a/tests/tcg/plugins/syscall.c b/tests/tcg/plugins/syscall.c
index 72e1a5bf90..7c92f798b5 100644
--- a/tests/tcg/plugins/syscall.c
+++ b/tests/tcg/plugins/syscall.c
@@ -22,8 +22,57 @@ typedef struct {
int64_t errors;
} SyscallStats;
+struct SyscallInfo {
+ const char *name;
+ int64_t write_sysno;
+};
+
+const struct SyscallInfo arch_syscall_info[] = {
+ { "aarch64", 64 },
+ { "aarch64_be", 64 },
+ { "alpha", 4 },
+ { "arm", 4 },
+ { "armeb", 4 },
+ { "avr", -1 },
+ { "cris", -1 },
+ { "hexagon", 64 },
+ { "hppa", -1 },
+ { "i386", 4 },
+ { "loongarch64", -1 },
+ { "m68k", 4 },
+ { "microblaze", 4 },
+ { "microblazeel", 4 },
+ { "mips", 1 },
+ { "mips64", 1 },
+ { "mips64el", 1 },
+ { "mipsel", 1 },
+ { "mipsn32", 1 },
+ { "mipsn32el", 1 },
+ { "or1k", -1 },
+ { "ppc", 4 },
+ { "ppc64", 4 },
+ { "ppc64le", 4 },
+ { "riscv32", 64 },
+ { "riscv64", 64 },
+ { "rx", -1 },
+ { "s390x", -1 },
+ { "sh4", -1 },
+ { "sh4eb", -1 },
+ { "sparc", 4 },
+ { "sparc32plus", 4 },
+ { "sparc64", 4 },
+ { "tricore", -1 },
+ { "x86_64", 1 },
+ { "xtensa", 13 },
+ { "xtensaeb", 13 },
+ { NULL, -1 },
+};
+
static GMutex lock;
static GHashTable *statistics;
+static GByteArray *memory_buffer;
+static bool do_log_writes;
+static int64_t write_sysno = -1;
static SyscallStats *get_or_create_entry(int64_t num)
{
@@ -39,6 +88,44 @@ static SyscallStats *get_or_create_entry(int64_t num)
return entry;
}
+/*
+ * Hex-dump a GByteArray to the QEMU plugin output in the format:
+ * 61 63 63 65 6c 09 09 20 20 20 66 70 75 09 09 09 | accel.....fpu...
+ * 20 6d 6f 64 75 6c 65 2d 63 6f 6d 6d 6f 6e 2e 63 | .module-common.c
+ */
+static void hexdump(const GByteArray *data)
+{
+ g_autoptr(GString) out = g_string_new("");
+
+ for (guint index = 0; index < data->len; index += 16) {
+ for (guint col = 0; col < 16; col++) {
+ if (index + col < data->len) {
+ g_string_append_printf(out, "%02x ", data->data[index + col]);
+ } else {
+ g_string_append(out, " ");
+ }
+ }
+
+ g_string_append(out, " | ");
+
+ for (guint col = 0; col < 16; col++) {
+ if (index + col >= data->len) {
+ break;
+ }
+
+ if (g_ascii_isgraph(data->data[index + col])) {
+ g_string_append_printf(out, "%c", data->data[index + col]);
+ } else {
+ g_string_append(out, ".");
+ }
+ }
+
+ g_string_append(out, "\n");
+ }
+
+ qemu_plugin_outs(out->str);
+}
+
static void vcpu_syscall(qemu_plugin_id_t id, unsigned int vcpu_index,
int64_t num, uint64_t a1, uint64_t a2,
uint64_t a3, uint64_t a4, uint64_t a5,
@@ -54,6 +141,14 @@ static void vcpu_syscall(qemu_plugin_id_t id, unsigned int vcpu_index,
g_autofree gchar *out = g_strdup_printf("syscall #%" PRIi64 "\n", num);
qemu_plugin_outs(out);
}
+
+ if (do_log_writes && num == write_sysno) {
+ if (qemu_plugin_read_memory_vaddr(a2, memory_buffer, a3)) {
+ hexdump(memory_buffer);
+ } else {
+ fprintf(stderr, "Error reading memory from vaddr %lu\n", a2);
+ }
+ }
}
static void vcpu_syscall_ret(qemu_plugin_id_t id, unsigned int vcpu_idx,
@@ -127,6 +222,10 @@ QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id,
if (!qemu_plugin_bool_parse(tokens[0], tokens[1], &do_print)) {
fprintf(stderr, "boolean argument parsing failed: %s\n", opt);
}
+ } else if (g_strcmp0(tokens[0], "log_writes") == 0) {
+ if (!qemu_plugin_bool_parse(tokens[0], tokens[1], &do_log_writes)) {
+ fprintf(stderr, "boolean argument parsing failed: %s\n", opt);
+ }
} else {
fprintf(stderr, "unsupported argument: %s\n", argv[i]);
return -1;
@@ -137,6 +236,24 @@ QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id,
statistics = g_hash_table_new_full(NULL, g_direct_equal, NULL, g_free);
}
+ if (do_log_writes) {
+ for (const struct SyscallInfo *syscall_info = arch_syscall_info;
+ syscall_info->name != NULL; syscall_info++) {
+
+ if (g_strcmp0(syscall_info->name, info->target_name) == 0) {
+ write_sysno = syscall_info->write_sysno;
+ break;
+ }
+ }
+
+ if (write_sysno == -1) {
+ fprintf(stderr, "write syscall number not found\n");
+ return -1;
+ }
+
+ memory_buffer = g_byte_array_new();
+ }
+
qemu_plugin_register_vcpu_syscall_cb(id, vcpu_syscall);
qemu_plugin_register_vcpu_syscall_ret_cb(id, vcpu_syscall_ret);
qemu_plugin_register_atexit_cb(id, plugin_exit, NULL);
--
2.46.0
^ permalink raw reply related [flat|nested] 4+ messages in thread
* Re: [PATCH v3 0/2] plugins: add plugin API to read guest memory
2024-08-27 21:53 [PATCH v3 0/2] plugins: add plugin API to read guest memory Rowan Hart
2024-08-27 21:53 ` [PATCH v3 1/2] " Rowan Hart
2024-08-27 21:53 ` [PATCH v3 2/2] plugins: add option to dump write argument to syscall plugin Rowan Hart
@ 2024-09-05 15:27 ` Alex Bennée
2 siblings, 0 replies; 4+ messages in thread
From: Alex Bennée @ 2024-09-05 15:27 UTC (permalink / raw)
To: Rowan Hart; +Cc: qemu-devel, Pierrick Bouvier, Alexandre Iooss, Mahmoud Mandour
Rowan Hart <rowanbhart@gmail.com> writes:
> This patch adds one API function to the QEMU plugin API
>
> bool qemu_plugin_read_memory_vaddr(vaddr, GByteArray *, size_t);
>
> The API allows reading memory from an arbitrary guest virtual
> address, which is useful for many things but the motivating examples
> are:
Queued to plugins/next with a minor tweak to the cpu_memory_rw_debug
call.
--
Alex Bennée
Virtualisation Tech Lead @ Linaro
^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2024-09-05 15:27 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-08-27 21:53 [PATCH v3 0/2] plugins: add plugin API to read guest memory Rowan Hart
2024-08-27 21:53 ` [PATCH v3 1/2] " Rowan Hart
2024-08-27 21:53 ` [PATCH v3 2/2] plugins: add option to dump write argument to syscall plugin Rowan Hart
2024-09-05 15:27 ` [PATCH v3 0/2] plugins: add plugin API to read guest memory Alex Bennée
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).