/* Copyright 2013-2014 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #include #include #include #include "../ccan/endian/endian.h" #include "../ccan/short_types/short_types.h" #include /* Handles trace from debugfs (one record at a time) or file */ static bool get_trace(int fd, union trace *t, int *len) { void *dest = t; int r; /* Move down any extra we read last time. */ if (*len >= sizeof(t->hdr) && *len >= t->hdr.len_div_8 * 8) { u8 rlen = t->hdr.len_div_8 * 8; memmove(dest, dest + rlen, *len - rlen); *len -= rlen; } r = read(fd, dest + *len, sizeof(*t) - *len); if (r < 0) return false; *len += r; /* We should have a complete record. */ return *len >= sizeof(t->hdr) && *len >= t->hdr.len_div_8 * 8; } static void display_header(const struct trace_hdr *h) { static u64 prev_ts; u64 ts = be64_to_cpu(h->timestamp); printf("%16lx (+%8lx) [%03x] : ", ts, prev_ts ? (ts - prev_ts) : 0, h->cpu); prev_ts = ts; } static void dump_fsp_event(struct trace_fsp_event *t) { printf("FSP_EVT [st=%d] ", t->fsp_state); switch(t->event) { case TRACE_FSP_EVT_LINK_DOWN: printf("LINK DOWN"); break; case TRACE_FSP_EVT_DISR_CHG: printf("DISR CHANGE (0x%08x)", t->data[0]); break; case TRACE_FSP_EVT_SOFT_RR: printf("SOFT R&R (DISR=0x%08x)", 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)", t->data[0]); break; case TRACE_FSP_EVT_POLL_IRQ: printf("%s HDIR=%08x CTL=%08x PSI_IRQ=%d", t->data[0] ? "IRQ " : "POLL", t->data[1], t->data[2], t->data[3]); break; default: printf("Unknown %d (d: %08x %08x %08x %08x)", t->event, t->data[0], t->data[1], t->data[2], 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, t->in_count); break; case TRACE_UART_CTX_POLL: printf(": POLL IRQEN=%d IN_CNT=%d\n", !t->irq_state, t->in_count); break; case TRACE_UART_CTX_READ: printf(": READ IRQEN=%d IN_CNT=%d READ=%d\n", !t->irq_state, t->in_count, t->cnt); break; default: printf(": ???? IRQEN=%d IN_CNT=%d\n", !t->irq_state, t->in_count); break; } } int main(int argc, char *argv[]) { int fd, len = 0; union trace t; const char *in = "/sys/kernel/debug/powerpc/opal-trace"; if (argc > 2) errx(1, "Usage: dump_trace [file]"); if (argv[1]) in = argv[1]; fd = open(in, O_RDONLY); if (fd < 0) err(1, "Opening %s", in); while (get_trace(fd, &t, &len)) { display_header(&t.hdr); switch (t.hdr.type) { case TRACE_REPEAT: printf("REPEATS: %u times\n", be32_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; default: printf("UNKNOWN(%u) CPU %u length %u\n", t.hdr.type, be16_to_cpu(t.hdr.cpu), t.hdr.len_div_8 * 8); } } return 0; }