aboutsummaryrefslogtreecommitdiffstats
path: root/roms/skiboot/external/trace
diff options
context:
space:
mode:
Diffstat (limited to 'roms/skiboot/external/trace')
-rw-r--r--roms/skiboot/external/trace/Makefile8
-rw-r--r--roms/skiboot/external/trace/dump_trace.c366
-rw-r--r--roms/skiboot/external/trace/trace.c110
-rw-r--r--roms/skiboot/external/trace/trace.h28
4 files changed, 512 insertions, 0 deletions
diff --git a/roms/skiboot/external/trace/Makefile b/roms/skiboot/external/trace/Makefile
new file mode 100644
index 000000000..737397bca
--- /dev/null
+++ b/roms/skiboot/external/trace/Makefile
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+HOSTEND=$(shell uname -m | sed -e 's/^i.*86$$/LITTLE/' -e 's/^x86.*/LITTLE/' -e 's/^ppc64le/LITTLE/' -e 's/^ppc.*/BIG/')
+CFLAGS=-g -Wall -DHAVE_$(HOSTEND)_ENDIAN -I../../include -I../../
+
+dump_trace: dump_trace.c trace.c ../../ccan/heap/heap.c
+
+clean:
+ rm -f dump_trace *.o
diff --git a/roms/skiboot/external/trace/dump_trace.c b/roms/skiboot/external/trace/dump_trace.c
new file mode 100644
index 000000000..f54dfa081
--- /dev/null
+++ b/roms/skiboot/external/trace/dump_trace.c
@@ -0,0 +1,366 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+/*
+ * Dump the content of an OPAL trace
+ *
+ * Copyright 2013-2019 IBM Corp.
+ */
+
+#include <trace.h>
+#include <err.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <fcntl.h>
+#include <string.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include "../../ccan/endian/endian.h"
+#include "../../ccan/short_types/short_types.h"
+#include "../../ccan/heap/heap.h"
+#include "trace.h"
+
+
+struct trace_entry {
+ int index;
+ union trace t;
+ struct list_node link;
+};
+
+static int follow;
+static long poll_msecs;
+
+static void *ezalloc(size_t size)
+{
+ void *p;
+
+ p = calloc(size, 1);
+ if (!p)
+ err(1, "Allocating memory");
+ return p;
+}
+
+#define TB_HZ 512000000ul
+
+static void display_header(const struct trace_hdr *h)
+{
+ static u64 prev_ts;
+ u64 ts = be64_to_cpu(h->timestamp);
+
+ printf("[%5lu.%09lu,%d] (+%8lx) [%03x] : ",
+ ts / TB_HZ, /* match the usual skiboot log header */
+ ts % TB_HZ,
+ h->type, /* hey why not */
+ prev_ts ? (ts - prev_ts) % TB_HZ : 0, be16_to_cpu(h->cpu));
+ prev_ts = ts;
+}
+
+static void dump_fsp_event(struct trace_fsp_event *t)
+{
+ printf("FSP_EVT [st=%d] ", be16_to_cpu(t->fsp_state));
+
+ switch(be16_to_cpu(t->event)) {
+ case TRACE_FSP_EVT_LINK_DOWN:
+ printf("LINK DOWN");
+ break;
+ case TRACE_FSP_EVT_DISR_CHG:
+ printf("DISR CHANGE (0x%08x)", be32_to_cpu(t->data[0]));
+ break;
+ case TRACE_FSP_EVT_SOFT_RR:
+ printf("SOFT R&R (DISR=0x%08x)", be32_to_cpu(t->data[0]));
+ break;
+ case TRACE_FSP_EVT_RR_COMPL:
+ printf("R&R COMPLETE");
+ break;
+ case TRACE_FSP_EVT_HDES_CHG:
+ printf("HDES CHANGE (0x%08x)", be32_to_cpu(t->data[0]));
+ break;
+ case TRACE_FSP_EVT_POLL_IRQ:
+ printf("%s HDIR=%08x CTL=%08x PSI_IRQ=%d",
+ t->data[0] ? "IRQ " : "POLL", be32_to_cpu(t->data[1]),
+ be32_to_cpu(t->data[2]), be32_to_cpu(t->data[3]));
+ break;
+ default:
+ printf("Unknown %d (d: %08x %08x %08x %08x)",
+ be16_to_cpu(t->event), be32_to_cpu(t->data[0]),
+ be32_to_cpu(t->data[1]), be32_to_cpu(t->data[2]),
+ be32_to_cpu(t->data[3]));
+ }
+ printf("\n");
+}
+
+static void dump_opal_call(struct trace_opal *t)
+{
+ unsigned int i, n;
+
+ printf("OPAL CALL %"PRIu64, be64_to_cpu(t->token));
+ printf(" LR=0x%016"PRIx64" SP=0x%016"PRIx64,
+ be64_to_cpu(t->lr), be64_to_cpu(t->sp));
+ n = (t->hdr.len_div_8 * 8 - offsetof(union trace, opal.r3_to_11))
+ / sizeof(u64);
+ for (i = 0; i < n; i++)
+ printf(" R%u=0x%016"PRIx64,
+ i+3, be64_to_cpu(t->r3_to_11[i]));
+ printf("\n");
+}
+
+static void dump_fsp_msg(struct trace_fsp_msg *t)
+{
+ unsigned int i;
+
+ printf("FSP_MSG: CMD %u SEQ %u MOD %u SUB %u DLEN %u %s [",
+ be32_to_cpu(t->word0) & 0xFFFF,
+ be32_to_cpu(t->word0) >> 16,
+ be32_to_cpu(t->word1) >> 8,
+ be32_to_cpu(t->word1) & 0xFF,
+ t->dlen,
+ t->dir == TRACE_FSP_MSG_IN ? "IN" :
+ (t->dir == TRACE_FSP_MSG_OUT ? "OUT" : "UNKNOWN"));
+
+ for (i = 0; i < t->dlen; i++)
+ printf("%s%02x", i ? " " : "", t->data[i]);
+ printf("]\n");
+}
+
+static void dump_uart(struct trace_uart *t)
+{
+ switch(t->ctx) {
+ case TRACE_UART_CTX_IRQ:
+ printf(": IRQ IRQEN=%d IN_CNT=%d\n",
+ !t->irq_state, be16_to_cpu(t->in_count));
+ break;
+ case TRACE_UART_CTX_POLL:
+ printf(": POLL IRQEN=%d IN_CNT=%d\n",
+ !t->irq_state, be16_to_cpu(t->in_count));
+ break;
+ case TRACE_UART_CTX_READ:
+ printf(": READ IRQEN=%d IN_CNT=%d READ=%d\n",
+ !t->irq_state, be16_to_cpu(t->in_count), t->cnt);
+ break;
+ default:
+ printf(": ???? IRQEN=%d IN_CNT=%d\n",
+ !t->irq_state, be16_to_cpu(t->in_count));
+ break;
+ }
+}
+
+static void dump_i2c(struct trace_i2c *t)
+{
+ uint16_t type = be16_to_cpu(t->type);
+
+ printf("I2C: bus: %d dev: %02x len: %x ",
+ be16_to_cpu(t->bus),
+ be16_to_cpu(t->i2c_addr),
+ be16_to_cpu(t->size)
+ );
+
+ switch (type & 0x3) {
+ case 0:
+ printf("read");
+ break;
+ case 1:
+ printf("write");
+ break;
+ case 2:
+ printf("smbus read from %x", be16_to_cpu(t->smbus_reg));
+ break;
+ case 3:
+ printf("smbus write to %x", be16_to_cpu(t->smbus_reg));
+ break;
+ default:
+ printf("u wot?");
+ }
+
+ printf(", rc = %hd\n", (int16_t) be16_to_cpu(t->rc));
+}
+
+static void load_traces(struct trace_reader *trs, int count)
+{
+ struct trace_entry *te;
+ union trace t;
+ int i;
+
+ for (i = 0; i < count; i++) {
+ while (trace_get(&t, &trs[i])) {
+ te = ezalloc(sizeof(struct trace_entry));
+ memcpy(&te->t, &t, sizeof(union trace));
+ te->index = i;
+ list_add_tail(&trs[i].traces, &te->link);
+ }
+ }
+}
+
+static void print_trace(union trace *t)
+{
+ display_header(&t->hdr);
+ switch (t->hdr.type) {
+ case TRACE_REPEAT:
+ printf("REPEATS: %u times\n",
+ be16_to_cpu(t->repeat.num));
+ break;
+ case TRACE_OVERFLOW:
+ printf("**OVERFLOW**: %"PRIu64" bytes missed\n",
+ be64_to_cpu(t->overflow.bytes_missed));
+ break;
+ case TRACE_OPAL:
+ dump_opal_call(&t->opal);
+ break;
+ case TRACE_FSP_MSG:
+ dump_fsp_msg(&t->fsp_msg);
+ break;
+ case TRACE_FSP_EVENT:
+ dump_fsp_event(&t->fsp_evt);
+ break;
+ case TRACE_UART:
+ dump_uart(&t->uart);
+ break;
+ case TRACE_I2C:
+ dump_i2c(&t->i2c);
+ break;
+ default:
+ printf("UNKNOWN(%u) CPU %u length %u\n",
+ t->hdr.type, be16_to_cpu(t->hdr.cpu),
+ t->hdr.len_div_8 * 8);
+ }
+}
+
+/* Gives a min heap */
+bool earlier_entry(const void *va, const void *vb)
+{
+ struct trace_entry *a, *b;
+
+ a = (struct trace_entry *) va;
+ b = (struct trace_entry *) vb;
+
+ if (!a)
+ return false;
+ if (!b)
+ return true;
+ return be64_to_cpu(a->t.hdr.timestamp) < be64_to_cpu(b->t.hdr.timestamp);
+}
+
+static void display_traces(struct trace_reader *trs, int count)
+{
+ struct trace_entry *current, *next;
+ struct heap *h;
+ int i;
+
+ h = heap_init(earlier_entry);
+ if (!h)
+ err(1, "Allocating memory");
+
+ for (i = 0; i < count; i++) {
+ current = list_pop(&trs[i].traces, struct trace_entry, link);
+ /* no need to add empty ones */
+ if (current)
+ heap_push(h, current);
+ }
+
+ while (h->len) {
+ current = heap_pop(h);
+ if (!current)
+ break;
+
+ print_trace(&current->t);
+
+ next = list_pop(&trs[current->index].traces, struct trace_entry,
+ link);
+ heap_push(h, next);
+ free(current);
+ }
+ heap_free(h);
+}
+
+
+/* Can't poll for 0 msec, so use 0 to signify failure */
+static long get_mseconds(char *s)
+{
+ char *end;
+ long ms;
+
+ errno = 0;
+ ms = strtol(s, &end, 10);
+ if (errno || *end || ms < 0)
+ return 0;
+ return ms;
+}
+
+static void usage(void)
+{
+ errx(1, "Usage: dump_trace [-f [-s msecs]] file...");
+}
+
+int main(int argc, char *argv[])
+{
+ struct trace_reader *trs;
+ struct trace_info *ti;
+ bool no_mmap = false;
+ struct stat sb;
+ int fd, opt, i;
+
+ poll_msecs = 1000;
+ while ((opt = getopt(argc, argv, "fs:")) != -1) {
+ switch (opt) {
+ case 'f':
+ follow++;
+ break;
+ case 's':
+ poll_msecs = get_mseconds(optarg);
+ if (follow && poll_msecs)
+ break;
+ /* fallthru */
+ default:
+ usage();
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc < 1)
+ usage();
+
+ trs = ezalloc(sizeof(struct trace_reader) * argc);
+
+ for (i = 0; i < argc; i++) {
+ fd = open(argv[i], O_RDONLY);
+ if (fd < 0)
+ err(1, "Opening %s", argv[i]);
+
+ if (fstat(fd, &sb) < 0)
+ err(1, "Stating %s", argv[1]);
+
+ ti = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
+ if (ti == MAP_FAILED) {
+ no_mmap = true;
+
+ ti = ezalloc(sb.st_size);
+ if (!ti)
+ err(1, "allocating memory for %s", argv[i]);
+
+ if (read(fd, ti, sb.st_size) == -1)
+ err(1, "reading from %s", argv[i]);
+ }
+
+ trs[i].tb = &ti->tb;
+ list_head_init(&trs[i].traces);
+ }
+
+ if (no_mmap) {
+ fprintf(stderr, "disabling follow mode: can't mmap() OPAL export files\n");
+ follow = 0;
+ }
+
+ do {
+ load_traces(trs, argc);
+ display_traces(trs, argc);
+ if (follow)
+ usleep(poll_msecs * 1000);
+ } while (follow);
+
+ return 0;
+}
diff --git a/roms/skiboot/external/trace/trace.c b/roms/skiboot/external/trace/trace.c
new file mode 100644
index 000000000..97cec7d9d
--- /dev/null
+++ b/roms/skiboot/external/trace/trace.c
@@ -0,0 +1,110 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+/*
+ * This example code shows how to read from the trace buffer.
+ *
+ * Copyright 2013-2019 IBM Corp.
+ */
+
+#include <external/trace/trace.h>
+#include "../ccan/endian/endian.h"
+#include "../ccan/short_types/short_types.h"
+#include "trace.h"
+#include <trace_types.h>
+#include <errno.h>
+
+#if defined(__powerpc__) || defined(__powerpc64__)
+#define rmb() lwsync()
+#else
+#define rmb()
+#endif
+
+bool trace_empty(const struct trace_reader *tr)
+{
+ const struct trace_repeat *rep;
+
+ if (tr->rpos == be64_to_cpu(tr->tb->end))
+ return true;
+
+ /*
+ * If we have a single element only, and it's a repeat buffer
+ * we've already seen every repeat for (yet which may be
+ * incremented in future), we're also empty.
+ */
+ rep = (void *)tr->tb->buf + tr->rpos % be64_to_cpu(tr->tb->buf_size);
+ if (be64_to_cpu(tr->tb->end) != tr->rpos + sizeof(*rep))
+ return false;
+
+ if (rep->type != TRACE_REPEAT)
+ return false;
+
+ if (be16_to_cpu(rep->num) != tr->last_repeat)
+ return false;
+
+ return true;
+}
+
+/* You can't read in parallel, so some locking required in caller. */
+bool trace_get(union trace *t, struct trace_reader *tr)
+{
+ u64 start, rpos;
+ size_t len;
+
+ len = sizeof(*t) < be32_to_cpu(tr->tb->max_size) ? sizeof(*t) :
+ be32_to_cpu(tr->tb->max_size);
+
+ if (trace_empty(tr))
+ return false;
+
+again:
+ /*
+ * The actual buffer is slightly larger than tbsize, so this
+ * memcpy is always valid.
+ */
+ memcpy(t, tr->tb->buf + tr->rpos % be64_to_cpu(tr->tb->buf_size), len);
+
+ rmb(); /* read barrier, so we read tr->tb->start after copying record. */
+
+ start = be64_to_cpu(tr->tb->start);
+ rpos = tr->rpos;
+
+ /* Now, was that overwritten? */
+ if (rpos < start) {
+ /* Create overflow record. */
+ t->overflow.unused64 = 0;
+ t->overflow.type = TRACE_OVERFLOW;
+ t->overflow.len_div_8 = sizeof(t->overflow) / 8;
+ t->overflow.bytes_missed = cpu_to_be64(start - rpos);
+ tr->rpos = start;
+ return true;
+ }
+
+ /* Repeat entries need special handling */
+ if (t->hdr.type == TRACE_REPEAT) {
+ u32 num = be16_to_cpu(t->repeat.num);
+
+ /* In case we've read some already... */
+ t->repeat.num = cpu_to_be16(num - tr->last_repeat);
+
+ /* Record how many repeats we saw this time. */
+ tr->last_repeat = num;
+
+ /* Don't report an empty repeat buffer. */
+ if (t->repeat.num == 0) {
+ /*
+ * This can't be the last buffer, otherwise
+ * trace_empty would have returned true.
+ */
+ assert(be64_to_cpu(tr->tb->end) >
+ rpos + t->hdr.len_div_8 * 8);
+ /* Skip to next entry. */
+ tr->rpos = rpos + t->hdr.len_div_8 * 8;
+ tr->last_repeat = 0;
+ goto again;
+ }
+ } else {
+ tr->last_repeat = 0;
+ tr->rpos = rpos + t->hdr.len_div_8 * 8;
+ }
+
+ return true;
+}
diff --git a/roms/skiboot/external/trace/trace.h b/roms/skiboot/external/trace/trace.h
new file mode 100644
index 000000000..1a5a138ae
--- /dev/null
+++ b/roms/skiboot/external/trace/trace.h
@@ -0,0 +1,28 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+/*
+ * Copyright 2013-2019 IBM Corp.
+ */
+#ifndef E_TRACE_H
+#define E_TRACE_H
+
+#include <stdbool.h>
+#include <types.h>
+#include <trace.h>
+#include <trace_types.h>
+
+struct trace_reader {
+ /* This is where the reader is up to. */
+ u64 rpos;
+ /* If the last one we read was a repeat, this shows how many. */
+ u32 last_repeat;
+ struct list_head traces;
+ struct tracebuf *tb;
+};
+
+/* Is this tracebuf empty? */
+bool trace_empty(const struct trace_reader *tr);
+
+/* Get the next trace from this buffer (false if empty). */
+bool trace_get(union trace *t, struct trace_reader *tr);
+
+#endif