[PATCH 4/4] contrib/plugins/uftrace: add riscv64 support

Pierrick Bouvier posted 4 patches 11 hours ago
Maintainers: "Alex Bennée" <alex.bennee@linaro.org>, Pierrick Bouvier <pierrick.bouvier@linaro.org>, Alexandre Iooss <erdnaxe@crans.org>, Mahmoud Mandour <ma.mandourr@gmail.com>
[PATCH 4/4] contrib/plugins/uftrace: add riscv64 support
Posted by Pierrick Bouvier 11 hours ago
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