The trace encoder and trace ramsink nodes should confirm to the bindings
described in "riscv,trace-component.yaml" in the Linux kernel. That way,
encoder and ramsink devices get populated on the rvtrace bus and perf
record is able to capture the trace data in the auxtrace section as
expected.

Signed-off-by: Daniel Henrique Barboza <[email protected]>
---
 hw/riscv/fdt-common.c         | 70 +++++++++++++++++++++++++++++++++++
 hw/riscv/virt.c               | 47 +++++++++++++++++++++++
 include/hw/riscv/fdt-common.h |  4 ++
 3 files changed, 121 insertions(+)

diff --git a/hw/riscv/fdt-common.c b/hw/riscv/fdt-common.c
index ce2dcb43b4..6130b1bcc1 100644
--- a/hw/riscv/fdt-common.c
+++ b/hw/riscv/fdt-common.c
@@ -105,6 +105,12 @@ void riscv_fdt_create_cpu_socket_subnode(void *fdt,
     qemu_fdt_add_subnode(fdt, "/cpus/cpu-map");
 }
 
+/* Caller must free string after use */
+static char* fdt_get_cpu_nodename(int cpu_id)
+{
+    return g_strdup_printf("/cpus/cpu@%d", cpu_id);
+}
+
 static void
 create_fdt_socket_cpu_internal(void *fdt, char *clust_name, RISCVCPU *cpu_ptr,
                                int cpu, int socket_id, int socket_hartid_base,
@@ -849,3 +855,67 @@ void riscv_create_fdt_rtc(void *fdt, const MemMapEntry 
*rtc_mem,
         qemu_fdt_setprop_cells(fdt, name, "interrupts", rtc_irq, 0x4);
     }
 }
+
+/* Returns the 'out-ports/port/endpoint' nodepath.  Caller
+   must free the string after use.  */
+char *riscv_create_fdt_trace_encoder(void *fdt, hwaddr addr, hwaddr size,
+                                     int cpu_id)
+{
+    static const char * const tr_compat[2] = { "qemu,trace-component",
+                                               "riscv,trace-component" };
+    g_autofree char *cpu_nodename = fdt_get_cpu_nodename(cpu_id);
+    g_autofree char *trencoder_name = NULL;
+    char *endpoint = NULL;
+
+    trencoder_name = g_strdup_printf("/soc/encoder@%"HWADDR_PRIx, addr);
+    qemu_fdt_add_subnode(fdt, trencoder_name);
+
+    qemu_fdt_setprop_sized_cells(fdt, trencoder_name, "reg",
+                                 2, addr,
+                                 2, size);
+
+    qemu_fdt_setprop_string_array(fdt, trencoder_name, "compatible",
+                                  (char **)&tr_compat,
+                                  ARRAY_SIZE(tr_compat));
+
+    qemu_fdt_setprop_phandle(fdt, trencoder_name, "cpus", cpu_nodename);
+
+    endpoint = g_strdup_printf("%s/out-ports/port/endpoint", trencoder_name);
+    qemu_fdt_add_path(fdt, endpoint);
+    qemu_fdt_setprop_cell(fdt, endpoint, "phandle",
+                          qemu_fdt_alloc_phandle(fdt));
+
+    return endpoint;
+}
+
+/* Returns the 'in-ports/port/endpoint' nodepath.  Caller
+   must free the string after use.  */
+char *riscv_create_fdt_trace_ramsink(void *fdt, hwaddr addr, hwaddr size,
+                                     int cpu_id)
+{
+    static const char * const tr_compat[2] = { "qemu,trace-component",
+                                               "riscv,trace-component" };
+    g_autofree char *cpu_nodename = fdt_get_cpu_nodename(cpu_id);
+    g_autofree char *ramsink_name = NULL;
+    char *endpoint = NULL;
+
+    ramsink_name = g_strdup_printf("/soc/ramsink@%"HWADDR_PRIx, addr);
+    qemu_fdt_add_subnode(fdt, ramsink_name);
+
+    qemu_fdt_setprop_sized_cells(fdt, ramsink_name, "reg",
+                                 2, addr,
+                                 2, size);
+
+    qemu_fdt_setprop_string_array(fdt, ramsink_name, "compatible",
+                                  (char **)&tr_compat,
+                                  ARRAY_SIZE(tr_compat));
+
+    qemu_fdt_setprop_phandle(fdt, ramsink_name, "cpus", cpu_nodename);
+
+    endpoint = g_strdup_printf("%s/in-ports/port/endpoint", ramsink_name);
+    qemu_fdt_add_path(fdt, endpoint);
+    qemu_fdt_setprop_cell(fdt, endpoint, "phandle",
+                          qemu_fdt_alloc_phandle(fdt));
+
+    return endpoint;
+}
diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c
index d47b295f0c..6bc8d384ec 100644
--- a/hw/riscv/virt.c
+++ b/hw/riscv/virt.c
@@ -479,6 +479,51 @@ static void create_fdt_iommu(RISCVVirtState *s, uint16_t 
bdf)
     s->pci_iommu_bdf = bdf;
 }
 
+static void create_fdt_rvtrace(RISCVVirtState *s)
+{
+    MachineState *ms = MACHINE(s);
+    int socket_count = riscv_socket_count(ms);
+    hwaddr encoder_addr, ramsink_addr, size = 0x100;
+    RISCVCPU *cpu_ptr;
+    int cpu_id;
+
+    for (int i = 0; i < socket_count; i++) {
+        for (int cpu = 0; cpu < s->soc[i].num_harts; cpu++) {
+            g_autofree char *in_endpoint = NULL;
+            g_autofree char *out_endpoint = NULL;
+
+            cpu_ptr = &s->soc[i].harts[cpu];
+            if (!cpu_ptr->trencoder) {
+                continue;
+            }
+
+            encoder_addr = object_property_get_uint(cpu_ptr->trencoder,
+                                                    "baseaddr",
+                                                    &error_abort);
+            ramsink_addr = object_property_get_uint(cpu_ptr->trencoder,
+                                                    "dest-baseaddr",
+                                                    &error_abort);
+            cpu_id = s->soc[i].hartid_base + cpu;
+
+            in_endpoint = riscv_create_fdt_trace_ramsink(ms->fdt,
+                                                         ramsink_addr,
+                                                         size, cpu_id);
+
+            out_endpoint = riscv_create_fdt_trace_encoder(ms->fdt,
+                                                          encoder_addr,
+                                                          size, cpu_id);
+
+            /* The remote-endpoint property is bidirectional, i.e. the
+               in-ports endpoint must point to the out-ports endpoint
+               and vice-versa.  */
+            qemu_fdt_setprop_phandle(ms->fdt, in_endpoint,
+                                     "remote-endpoint", out_endpoint);
+            qemu_fdt_setprop_phandle(ms->fdt, out_endpoint,
+                                     "remote-endpoint", in_endpoint);
+        }
+    }
+}
+
 static void finalize_fdt(RISCVVirtState *s)
 {
     uint32_t phandle = 1, irq_mmio_phandle = 1, msi_pcie_phandle = 1;
@@ -520,6 +565,8 @@ static void finalize_fdt(RISCVVirtState *s)
 
     riscv_create_fdt_rtc(MACHINE(s)->fdt, &s->memmap[VIRT_RTC], RTC_IRQ,
                          s->aia_type, irq_mmio_phandle);
+
+    create_fdt_rvtrace(s);
 }
 
 static void create_fdt(RISCVVirtState *s)
diff --git a/include/hw/riscv/fdt-common.h b/include/hw/riscv/fdt-common.h
index eb9db8278d..fd03a9e0ab 100644
--- a/include/hw/riscv/fdt-common.h
+++ b/include/hw/riscv/fdt-common.h
@@ -136,4 +136,8 @@ void riscv_create_fdt_uart(void *fdt, const MemMapEntry 
*uart_mem,
 void riscv_create_fdt_rtc(void *fdt, const MemMapEntry *rtc_mem,
                           int rtc_irq, int aia_type,
                           uint32_t irq_phandle);
+char *riscv_create_fdt_trace_encoder(void *fdt, hwaddr addr, hwaddr size,
+                                     int cpu_id);
+char *riscv_create_fdt_trace_ramsink(void *fdt, hwaddr addr, hwaddr size,
+                                     int cpu_id);
 #endif
-- 
2.43.0


Reply via email to