aboutsummaryrefslogtreecommitdiffstats
path: root/roms/skiboot/core/trace.c
diff options
context:
space:
mode:
authorAngelos Mouzakitis <a.mouzakitis@virtualopensystems.com>2023-10-10 14:33:42 +0000
committerAngelos Mouzakitis <a.mouzakitis@virtualopensystems.com>2023-10-10 14:33:42 +0000
commitaf1a266670d040d2f4083ff309d732d648afba2a (patch)
tree2fc46203448ddcc6f81546d379abfaeb323575e9 /roms/skiboot/core/trace.c
parente02cda008591317b1625707ff8e115a4841aa889 (diff)
Add submodule dependency filesHEADmaster
Change-Id: Iaf8d18082d3991dec7c0ebbea540f092188eb4ec
Diffstat (limited to 'roms/skiboot/core/trace.c')
-rw-r--r--roms/skiboot/core/trace.c265
1 files changed, 265 insertions, 0 deletions
diff --git a/roms/skiboot/core/trace.c b/roms/skiboot/core/trace.c
new file mode 100644
index 000000000..561bd79e0
--- /dev/null
+++ b/roms/skiboot/core/trace.c
@@ -0,0 +1,265 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+/*
+ * Trace various things into in-memory buffers
+ *
+ * Copyright 2013-2019 IBM Corp.
+ */
+
+#include <trace.h>
+#include <timebase.h>
+#include <lock.h>
+#include <string.h>
+#include <stdlib.h>
+#include <inttypes.h>
+#include <cpu.h>
+#include <device.h>
+#include <libfdt.h>
+#include <processor.h>
+#include <skiboot.h>
+#include <opal-api.h>
+#include <debug_descriptor.h>
+#include <nvram.h>
+
+#define DEBUG_TRACES
+
+#define MAX_SIZE sizeof(union trace)
+
+/* Smaller trace buffer for early booting */
+#define BOOT_TBUF_SZ 65536
+static struct {
+ struct trace_info trace_info;
+ char buf[BOOT_TBUF_SZ + MAX_SIZE];
+} boot_tracebuf __section(".data.boot_trace");
+
+void init_boot_tracebuf(struct cpu_thread *boot_cpu)
+{
+ init_lock(&boot_tracebuf.trace_info.lock);
+ boot_tracebuf.trace_info.tb.buf_size = cpu_to_be64(BOOT_TBUF_SZ);
+ boot_tracebuf.trace_info.tb.max_size = cpu_to_be32(MAX_SIZE);
+
+ boot_cpu->trace = &boot_tracebuf.trace_info;
+}
+
+static size_t tracebuf_extra(void)
+{
+ /* We make room for the largest possible record */
+ return TBUF_SZ + MAX_SIZE;
+}
+
+/* To avoid bloating each entry, repeats are actually specific entries.
+ * tb->last points to the last (non-repeat) entry. */
+static bool handle_repeat(struct tracebuf *tb, const union trace *trace)
+{
+ struct trace_hdr *prev;
+ struct trace_repeat *rpt;
+ u32 len;
+
+ prev = (void *)tb->buf + be64_to_cpu(tb->last) % be64_to_cpu(tb->buf_size);
+
+ if (prev->type != trace->hdr.type
+ || prev->len_div_8 != trace->hdr.len_div_8
+ || prev->cpu != trace->hdr.cpu)
+ return false;
+
+ len = prev->len_div_8 << 3;
+ if (memcmp(prev + 1, &trace->hdr + 1, len - sizeof(*prev)) != 0)
+ return false;
+
+ /* If they've consumed prev entry, don't repeat. */
+ if (be64_to_cpu(tb->last) < be64_to_cpu(tb->start))
+ return false;
+
+ /* OK, it's a duplicate. Do we already have repeat? */
+ if (be64_to_cpu(tb->last) + len != be64_to_cpu(tb->end)) {
+ u64 pos = be64_to_cpu(tb->last) + len;
+ /* FIXME: Reader is not protected from seeing this! */
+ rpt = (void *)tb->buf + pos % be64_to_cpu(tb->buf_size);
+ assert(pos + rpt->len_div_8*8 == be64_to_cpu(tb->end));
+ assert(rpt->type == TRACE_REPEAT);
+
+ /* If this repeat entry is full, don't repeat. */
+ if (be16_to_cpu(rpt->num) == 0xFFFF)
+ return false;
+
+ rpt->num = cpu_to_be16(be16_to_cpu(rpt->num) + 1);
+ rpt->timestamp = trace->hdr.timestamp;
+ return true;
+ }
+
+ /*
+ * Generate repeat entry: it's the smallest possible entry, so we
+ * must have eliminated old entries.
+ */
+ assert(trace->hdr.len_div_8 * 8 >= sizeof(*rpt));
+
+ rpt = (void *)tb->buf + be64_to_cpu(tb->end) % be64_to_cpu(tb->buf_size);
+ rpt->timestamp = trace->hdr.timestamp;
+ rpt->type = TRACE_REPEAT;
+ rpt->len_div_8 = sizeof(*rpt) >> 3;
+ rpt->cpu = trace->hdr.cpu;
+ rpt->prev_len = cpu_to_be16(trace->hdr.len_div_8 << 3);
+ rpt->num = cpu_to_be16(1);
+ lwsync(); /* write barrier: complete repeat record before exposing */
+ tb->end = cpu_to_be64(be64_to_cpu(tb->end) + sizeof(*rpt));
+ return true;
+}
+
+void trace_add(union trace *trace, u8 type, u16 len)
+{
+ struct trace_info *ti = this_cpu()->trace;
+ unsigned int tsz;
+
+ trace->hdr.type = type;
+ trace->hdr.len_div_8 = (len + 7) >> 3;
+
+ tsz = trace->hdr.len_div_8 << 3;
+
+#ifdef DEBUG_TRACES
+ assert(tsz >= sizeof(trace->hdr));
+ assert(tsz <= sizeof(*trace));
+ assert(trace->hdr.type != TRACE_REPEAT);
+ assert(trace->hdr.type != TRACE_OVERFLOW);
+#endif
+ /* Skip traces not enabled in the debug descriptor */
+ if (trace->hdr.type < (8 * sizeof(debug_descriptor.trace_mask)) &&
+ !((1ul << trace->hdr.type) & be64_to_cpu(debug_descriptor.trace_mask)))
+ return;
+
+ trace->hdr.timestamp = cpu_to_be64(mftb());
+ trace->hdr.cpu = cpu_to_be16(this_cpu()->server_no);
+
+ lock(&ti->lock);
+
+ /* Throw away old entries before we overwrite them. */
+ while ((be64_to_cpu(ti->tb.start) + be64_to_cpu(ti->tb.buf_size))
+ < (be64_to_cpu(ti->tb.end) + tsz)) {
+ struct trace_hdr *hdr;
+
+ hdr = (void *)ti->tb.buf +
+ be64_to_cpu(ti->tb.start) % be64_to_cpu(ti->tb.buf_size);
+ ti->tb.start = cpu_to_be64(be64_to_cpu(ti->tb.start) +
+ (hdr->len_div_8 << 3));
+ }
+
+ /* Must update ->start before we rewrite new entries. */
+ lwsync(); /* write barrier */
+
+ /* Check for duplicates... */
+ if (!handle_repeat(&ti->tb, trace)) {
+ /* This may go off end, and that's why ti->tb.buf is oversize */
+ memcpy(ti->tb.buf + be64_to_cpu(ti->tb.end) % be64_to_cpu(ti->tb.buf_size),
+ trace, tsz);
+ ti->tb.last = ti->tb.end;
+ lwsync(); /* write barrier: write entry before exposing */
+ ti->tb.end = cpu_to_be64(be64_to_cpu(ti->tb.end) + tsz);
+ }
+ unlock(&ti->lock);
+}
+
+void trace_add_dt_props(void)
+{
+ uint64_t boot_buf_phys = (uint64_t) &boot_tracebuf.trace_info;
+ struct dt_node *exports, *traces;
+ unsigned int i;
+ fdt64_t *prop;
+ u64 tmask;
+ char tname[256];
+
+ exports = dt_find_by_path(opal_node, "firmware/exports");
+ if (!exports)
+ return;
+
+ /*
+ * nvram hack to put all the trace buffer exports in the exports
+ * node. This is useful if the kernel doesn't also export subnodes.
+ */
+ if (nvram_query_safe("flat-trace-buf"))
+ traces = exports;
+ else
+ traces = dt_new(exports, "traces");
+
+ prop = malloc(sizeof(u64) * 2 * be32_to_cpu(debug_descriptor.num_traces));
+
+ for (i = 0; i < be32_to_cpu(debug_descriptor.num_traces); i++) {
+ uint64_t addr = be64_to_cpu(debug_descriptor.trace_phys[i]);
+ uint64_t size = be32_to_cpu(debug_descriptor.trace_size[i]);
+ uint32_t pir = be16_to_cpu(debug_descriptor.trace_pir[i]);
+
+ prop[i * 2] = cpu_to_fdt64(addr);
+ prop[i * 2 + 1] = cpu_to_fdt64(size);
+
+ if (addr == boot_buf_phys)
+ snprintf(tname, sizeof(tname), "boot-%x", pir);
+ else
+ snprintf(tname, sizeof(tname), "trace-%x", pir);
+
+ dt_add_property_u64s(traces, tname, addr, size);
+ }
+
+ dt_add_property(opal_node, "ibm,opal-traces",
+ prop, sizeof(u64) * 2 * i);
+ free(prop);
+
+ tmask = (uint64_t)&debug_descriptor.trace_mask;
+ dt_add_property_u64(opal_node, "ibm,opal-trace-mask", tmask);
+}
+
+static void trace_add_desc(struct trace_info *t, uint64_t size, uint16_t pir)
+{
+ unsigned int i = be32_to_cpu(debug_descriptor.num_traces);
+
+ if (i >= DEBUG_DESC_MAX_TRACES) {
+ prerror("TRACE: Debug descriptor trace list full !\n");
+ return;
+ }
+
+ debug_descriptor.num_traces = cpu_to_be32(i + 1);
+ debug_descriptor.trace_phys[i] = cpu_to_be64((uint64_t)t);
+ debug_descriptor.trace_tce[i] = 0; /* populated later */
+ debug_descriptor.trace_size[i] = cpu_to_be32(size);
+ debug_descriptor.trace_pir[i] = cpu_to_be16(pir);
+}
+
+/* Allocate trace buffers once we know memory topology */
+void init_trace_buffers(void)
+{
+ struct cpu_thread *t;
+ struct trace_info *any = &boot_tracebuf.trace_info;
+ uint64_t size;
+
+ /* Boot the boot trace in the debug descriptor */
+ trace_add_desc(any, sizeof(boot_tracebuf), this_cpu()->pir);
+
+ /* Allocate a trace buffer for each primary cpu. */
+ for_each_cpu(t) {
+ if (t->is_secondary)
+ continue;
+
+ /* Use a 64K alignment for TCE mapping */
+ size = ALIGN_UP(sizeof(*t->trace) + tracebuf_extra(), 0x10000);
+ t->trace = local_alloc(t->chip_id, size, 0x10000);
+ if (t->trace) {
+ any = t->trace;
+ memset(t->trace, 0, size);
+ init_lock(&t->trace->lock);
+ t->trace->tb.max_size = cpu_to_be32(MAX_SIZE);
+ t->trace->tb.buf_size = cpu_to_be64(TBUF_SZ);
+ trace_add_desc(any, sizeof(t->trace->tb) +
+ tracebuf_extra(), t->pir);
+ } else
+ prerror("TRACE: cpu 0x%x allocation failed\n", t->pir);
+ }
+
+ /* In case any allocations failed, share trace buffers. */
+ for_each_cpu(t) {
+ if (!t->is_secondary && !t->trace)
+ t->trace = any;
+ }
+
+ /* And copy those to the secondaries. */
+ for_each_cpu(t) {
+ if (!t->is_secondary)
+ continue;
+ t->trace = t->primary->trace;
+ }
+}