Signed-off-by: Pierrick Bouvier <pierrick.bouvier@linaro.org>
---
docs/about/emulation.rst | 10 +++--
contrib/plugins/uftrace.c | 87 +++++++++++++++++++++++++++++++++++++++
2 files changed, 94 insertions(+), 3 deletions(-)
diff --git a/docs/about/emulation.rst b/docs/about/emulation.rst
index f547e118eef..76c6ea92ca9 100644
--- a/docs/about/emulation.rst
+++ b/docs/about/emulation.rst
@@ -836,8 +836,8 @@ Uftrace
This plugin generates a binary trace compatible with
`uftrace <https://github.com/namhyung/uftrace>`_.
-Plugin supports aarch64 and x64, and works in user and system mode, allowing to
-trace a system boot, which is not something possible usually.
+Plugin supports aarch64, x64 and riscv64, and works in user and system mode,
+allowing to trace a system boot, which is not something possible usually.
In user mode, the memory mapping is directly copied from ``/proc/self/maps`` at
the end of execution. Uftrace should be able to retrieve symbols by itself,
@@ -872,7 +872,7 @@ Performance wise, overhead compared to normal tcg execution is around x5-x15.
- Description
* - trace-privilege-level=[on|off]
- Generate separate traces for each privilege level (Exception Level +
- Security State on aarch64, Rings on x64).
+ Security State on aarch64, Privilege levels on riscv64 and Rings on x64).
.. list-table:: uftrace_symbols.py arguments
:widths: 20 80
@@ -976,6 +976,10 @@ You can follow the exact same instructions for a x64 system, combining edk2,
Linux, and Ubuntu, simply by switching to
`x86_64 <https://github.com/pbo-linaro/qemu-linux-stack/tree/x86_64>`_ branch.
+You can follow the exact same instructions for a riscv64 system, combining
+opensbi, Linux, and Ubuntu, simply by switching to
+`riscv64 <https://github.com/pbo-linaro/qemu-linux-stack/tree/riscv64>`_ branch.
+
To build and run the system::
# Install dependencies
diff --git a/contrib/plugins/uftrace.c b/contrib/plugins/uftrace.c
index 21ac1402047..e3c65a1c930 100644
--- a/contrib/plugins/uftrace.c
+++ b/contrib/plugins/uftrace.c
@@ -99,6 +99,19 @@ typedef struct {
struct qemu_plugin_register *reg_cr0;
} X64Cpu;
+typedef struct {
+ struct qemu_plugin_register *reg_fp;
+ struct qemu_plugin_register *reg_priv;
+} Riscv64Cpu;
+
+typedef enum {
+ RISCV64_USER,
+ RISCV64_SUPERVISOR,
+ RISCV64_RESERVED,
+ RISCV64_MACHINE,
+ RISCV64_PRIVILEGE_LEVEL_MAX,
+} Riscv64PrivilegeLevel;
+
typedef struct {
uint64_t timestamp;
uint64_t data;
@@ -681,6 +694,78 @@ static CpuOps x64_ops = {
.does_insn_modify_frame_pointer = x64_does_insn_modify_frame_pointer,
};
+static uint8_t riscv64_num_privilege_levels(void)
+{
+ return RISCV64_PRIVILEGE_LEVEL_MAX;
+}
+
+static const char *riscv64_get_privilege_level_name(uint8_t pl)
+{
+ switch (pl) {
+ case RISCV64_USER: return "User";
+ case RISCV64_SUPERVISOR: return "Supervisor";
+ case RISCV64_RESERVED: return "Unknown";
+ case RISCV64_MACHINE: return "Machine";
+ default:
+ g_assert_not_reached();
+ }
+}
+
+static uint8_t riscv64_get_privilege_level(Cpu *cpu_)
+{
+ Riscv64Cpu *cpu = cpu_->arch;
+ return cpu_read_register64(cpu_, cpu->reg_priv);
+}
+
+static uint64_t riscv64_get_frame_pointer(Cpu *cpu_)
+{
+ Riscv64Cpu *cpu = cpu_->arch;
+ return cpu_read_register64(cpu_, cpu->reg_fp);
+}
+
+static uint64_t riscv64_get_next_frame_pointer(Cpu *cpu_, uint64_t fp)
+{
+ return cpu_read_memory64(cpu_, fp - 16);
+}
+
+static uint64_t riscv64_get_next_return_address(Cpu *cpu_, uint64_t fp)
+{
+ return cpu_read_memory64(cpu_, fp - 8);
+}
+
+static void riscv64_init(Cpu *cpu_)
+{
+ Riscv64Cpu *cpu = g_new0(Riscv64Cpu, 1);
+ cpu_->arch = cpu;
+ cpu->reg_fp = plugin_find_register("fp");
+ g_assert(cpu->reg_fp);
+ cpu->reg_priv = plugin_find_register("priv");
+ g_assert(cpu->reg_priv);
+}
+
+static void riscv64_end(Cpu *cpu)
+{
+ g_free(cpu->arch);
+}
+
+static bool riscv64_does_insn_modify_frame_pointer(const char *disas)
+{
+ /* fp is s0 in disassembly */
+ return strstr(disas, "s0");
+}
+
+static CpuOps riscv64_ops = {
+ .init = riscv64_init,
+ .end = riscv64_end,
+ .get_frame_pointer = riscv64_get_frame_pointer,
+ .get_next_frame_pointer = riscv64_get_next_frame_pointer,
+ .get_next_return_address = riscv64_get_next_return_address,
+ .get_privilege_level = riscv64_get_privilege_level,
+ .num_privilege_levels = riscv64_num_privilege_levels,
+ .get_privilege_level_name = riscv64_get_privilege_level_name,
+ .does_insn_modify_frame_pointer = riscv64_does_insn_modify_frame_pointer,
+};
+
static void track_privilege_change(unsigned int cpu_index, void *udata)
{
Cpu *cpu = qemu_plugin_scoreboard_find(score, cpu_index);
@@ -890,6 +975,8 @@ QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id,
arch_ops = aarch64_ops;
} else if (!strcmp(info->target_name, "x86_64")) {
arch_ops = x64_ops;
+ } else if (!strcmp(info->target_name, "riscv64")) {
+ arch_ops = riscv64_ops;
} else {
fprintf(stderr, "plugin uftrace: %s target is not supported\n",
info->target_name);
--
2.47.3
© 2016 - 2026 Red Hat, Inc.