Commit be2573eb08 for qemu.org
commit be2573eb08040f31aaad16b27d553ee09c8c49ab
Author: Matheus Tavares Bernardino <matheus.bernardino@oss.qualcomm.com>
Date: Wed Sep 30 10:55:19 2026 -0700
contrib/plugins/uftrace: add support for Hexagon
Signed-off-by: Matheus Tavares Bernardino <matheus.bernardino@oss.qualcomm.com>
Signed-off-by: Brian Cain <brian.cain@oss.qualcomm.com>
Acked-by: Alex Bennée <alex.bennee@linaro.org>
Reviewed-by: Pierrick Bouvier <pierrick.bouvier@oss.qualcomm.com>
Link: https://lore.kernel.org/qemu-devel/20260930175519.3601312-1-brian.cain@oss.qualcomm.com
Signed-off-by: Pierrick Bouvier <pierrick.bouvier@oss.qualcomm.com>
diff --git a/contrib/plugins/uftrace.c b/contrib/plugins/uftrace.c
index 063e32220b..bf4ca7c689 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 enum {
+ HEX_MONITOR,
+ HEX_GUEST,
+ HEX_USER,
+ HEX_PRIVILEGE_LEVEL_MAX,
+} HexagonPrivilegeLevel;
+
+typedef struct {
+ struct qemu_plugin_register *reg_fp;
+ struct qemu_plugin_register *reg_ssr;
+ struct qemu_plugin_register *reg_framekey;
+} HexagonCPU;
+
typedef struct {
struct qemu_plugin_register *reg_fp;
struct qemu_plugin_register *reg_priv;
@@ -453,6 +466,21 @@ static uint64_t cpu_read_memory64(Cpu *cpu, uint64_t addr)
return *((uint64_t *) buf->data);
}
+static uint32_t cpu_read_memory32(Cpu *cpu, uint64_t addr)
+{
+ if (!addr) {
+ return 0;
+ }
+ GByteArray *buf = cpu->buf;
+ g_byte_array_set_size(buf, 0);
+ bool read = qemu_plugin_read_memory_vaddr(addr, buf, 4);
+ if (!read) {
+ return 0;
+ }
+ g_assert(buf->len == 4);
+ return *((uint32_t *) buf->data);
+}
+
static void cpu_unwind_stack(Cpu *cpu, uint64_t frame_pointer, uint64_t pc)
{
g_assert(callstack_empty(cpu->cs));
@@ -698,6 +726,106 @@ static CpuOps x64_ops = {
.does_insn_modify_frame_pointer = x64_does_insn_modify_frame_pointer,
};
+static uint8_t hexagon_num_privilege_levels(void)
+{
+ return HEX_PRIVILEGE_LEVEL_MAX;
+}
+
+static const char *hexagon_get_privilege_level_name(uint8_t pl)
+{
+ switch (pl) {
+ case HEX_MONITOR: return "monitor";
+ case HEX_GUEST: return "guest";
+ case HEX_USER: return "user";
+ default:
+ g_assert_not_reached();
+ }
+}
+
+#define HEX_SSR_FIELD_UM 16
+#define HEX_SSR_FIELD_EX 17
+#define HEX_SSR_FIELD_GM 19
+
+static uint8_t hexagon_get_privilege_level(Cpu *cpu_)
+{
+ HexagonCPU *cpu = cpu_->arch;
+ uint32_t ssr = cpu_read_register32(cpu_, cpu->reg_ssr);
+ bool ex = (ssr >> HEX_SSR_FIELD_EX) & 1;
+ bool um = (ssr >> HEX_SSR_FIELD_UM) & 1;
+ bool gm = (ssr >> HEX_SSR_FIELD_GM) & 1;
+
+ if (ex || !um) {
+ return HEX_MONITOR;
+ } else if (!ex && um) {
+ return gm ? HEX_GUEST : HEX_USER;
+ }
+ g_assert_not_reached();
+}
+
+static uint64_t hexagon_get_frame_pointer(Cpu *cpu_)
+{
+ HexagonCPU *cpu = cpu_->arch;
+ return cpu_read_register32(cpu_, cpu->reg_fp);
+}
+
+static uint64_t hexagon_get_next_frame_pointer(Cpu *cpu_, uint64_t fp)
+{
+ return cpu_read_memory32(cpu_, fp);
+}
+
+static uint64_t hexagon_get_next_return_address(Cpu *cpu_,
+ uint64_t frame_pointer)
+{
+ HexagonCPU *cpu = cpu_->arch;
+ uint64_t framekey = cpu_read_register32(cpu_, cpu->reg_framekey);
+ uint64_t scrambled_lr = cpu_read_memory32(cpu_, frame_pointer + 4);
+ uint64_t lr = scrambled_lr ^ framekey;
+ return lr;
+}
+
+static void hexagon_init(Cpu *cpu_)
+{
+ HexagonCPU *cpu = g_new0(HexagonCPU, 1);
+ cpu_->arch = cpu;
+ cpu->reg_fp = plugin_find_register("r30");
+ if (!cpu->reg_fp) {
+ fprintf(stderr, "uftrace plugin: frame pointer register (R30) is not "
+ "available.\n");
+ g_abort();
+ }
+ cpu->reg_ssr = plugin_find_register("ssr");
+ if (trace_privilege_level && !cpu->reg_ssr) {
+ fprintf(stderr, "uftrace plugin: trace-privilege-level is not "
+ "supported for hexagon-linux-user.\n");
+ g_abort();
+ }
+ cpu->reg_framekey = plugin_find_register("framekey");
+ g_assert(cpu->reg_framekey);
+}
+
+static void hexagon_end(Cpu *cpu)
+{
+ g_free(cpu->arch);
+}
+
+static bool hexagon_does_insn_modify_frame_pointer(const char *disas)
+{
+ return strstr(disas, "alloc") ||
+ strstr(disas, "R30") || strstr(disas, "R31:30");
+}
+
+static CpuOps hexagon_ops = {
+ .init = hexagon_init,
+ .end = hexagon_end,
+ .get_frame_pointer = hexagon_get_frame_pointer,
+ .get_next_frame_pointer = hexagon_get_next_frame_pointer,
+ .get_next_return_address = hexagon_get_next_return_address,
+ .get_privilege_level = hexagon_get_privilege_level,
+ .num_privilege_levels = hexagon_num_privilege_levels,
+ .get_privilege_level_name = hexagon_get_privilege_level_name,
+ .does_insn_modify_frame_pointer = hexagon_does_insn_modify_frame_pointer,
+};
+
static uint8_t riscv64_num_privilege_levels(void)
{
return RISCV64_PRIVILEGE_LEVEL_MAX;
@@ -984,6 +1112,8 @@ QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id,
arch_ops = x64_ops;
} else if (!strcmp(info->target_name, "riscv64")) {
arch_ops = riscv64_ops;
+ } else if (!strcmp(info->target_name, "hexagon")) {
+ arch_ops = hexagon_ops;
} else {
fprintf(stderr, "plugin uftrace: %s target is not supported\n",
info->target_name);
diff --git a/docs/about/emulation.rst b/docs/about/emulation.rst
index c58149086c..b404c5c5e8 100644
--- a/docs/about/emulation.rst
+++ b/docs/about/emulation.rst
@@ -836,7 +836,7 @@ Uftrace
This plugin generates a binary trace compatible with
`uftrace <https://github.com/namhyung/uftrace>`_.
-Plugin supports aarch64, x64 and riscv64, and works in user and system mode,
+Plugin supports aarch64, x64, riscv64, and Hexagon, 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