Branch data Line data Source code
1 : : // SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
2 : : /*
3 : : * Trace various things into in-memory buffers
4 : : *
5 : : * Copyright 2013-2019 IBM Corp.
6 : : */
7 : :
8 : : #include <trace.h>
9 : : #include <timebase.h>
10 : : #include <lock.h>
11 : : #include <string.h>
12 : : #include <stdlib.h>
13 : : #include <inttypes.h>
14 : : #include <cpu.h>
15 : : #include <device.h>
16 : : #include <libfdt.h>
17 : : #include <processor.h>
18 : : #include <skiboot.h>
19 : : #include <opal-api.h>
20 : : #include <debug_descriptor.h>
21 : : #include <nvram.h>
22 : :
23 : : #define DEBUG_TRACES
24 : :
25 : : #define MAX_SIZE sizeof(union trace)
26 : :
27 : : /* Smaller trace buffer for early booting */
28 : : #define BOOT_TBUF_SZ 65536
29 : : static struct {
30 : : struct trace_info trace_info;
31 : : char buf[BOOT_TBUF_SZ + MAX_SIZE];
32 : : } boot_tracebuf __section(".data.boot_trace");
33 : :
34 : 0 : void init_boot_tracebuf(struct cpu_thread *boot_cpu)
35 : : {
36 : 0 : init_lock(&boot_tracebuf.trace_info.lock);
37 : 0 : boot_tracebuf.trace_info.tb.buf_size = cpu_to_be64(BOOT_TBUF_SZ);
38 : 0 : boot_tracebuf.trace_info.tb.max_size = cpu_to_be32(MAX_SIZE);
39 : :
40 : 0 : boot_cpu->trace = &boot_tracebuf.trace_info;
41 : 0 : }
42 : :
43 : 4 : static size_t tracebuf_extra(void)
44 : : {
45 : : /* We make room for the largest possible record */
46 : 4 : return TBUF_SZ + MAX_SIZE;
47 : : }
48 : :
49 : : /* To avoid bloating each entry, repeats are actually specific entries.
50 : : * tb->last points to the last (non-repeat) entry. */
51 : 6418116 : static bool handle_repeat(struct tracebuf *tb, const union trace *trace)
52 : : {
53 : : struct trace_hdr *prev;
54 : : struct trace_repeat *rpt;
55 : : u32 len;
56 : :
57 : 6418116 : prev = (void *)tb->buf + be64_to_cpu(tb->last) % be64_to_cpu(tb->buf_size);
58 : :
59 : 6418116 : if (prev->type != trace->hdr.type
60 : 3121739 : || prev->len_div_8 != trace->hdr.len_div_8
61 : 3121739 : || prev->cpu != trace->hdr.cpu)
62 : 3296377 : return false;
63 : :
64 : 3121739 : len = prev->len_div_8 << 3;
65 : 3121739 : if (memcmp(prev + 1, &trace->hdr + 1, len - sizeof(*prev)) != 0)
66 : 0 : return false;
67 : :
68 : : /* If they've consumed prev entry, don't repeat. */
69 : 3121739 : if (be64_to_cpu(tb->last) < be64_to_cpu(tb->start))
70 : 0 : return false;
71 : :
72 : : /* OK, it's a duplicate. Do we already have repeat? */
73 : 3121739 : if (be64_to_cpu(tb->last) + len != be64_to_cpu(tb->end)) {
74 : 1985763 : u64 pos = be64_to_cpu(tb->last) + len;
75 : : /* FIXME: Reader is not protected from seeing this! */
76 : 1985763 : rpt = (void *)tb->buf + pos % be64_to_cpu(tb->buf_size);
77 : 1985763 : assert(pos + rpt->len_div_8*8 == be64_to_cpu(tb->end));
78 : 1985763 : assert(rpt->type == TRACE_REPEAT);
79 : :
80 : : /* If this repeat entry is full, don't repeat. */
81 : 1985763 : if (be16_to_cpu(rpt->num) == 0xFFFF)
82 : 16 : return false;
83 : :
84 : 1985747 : rpt->num = cpu_to_be16(be16_to_cpu(rpt->num) + 1);
85 : 1985747 : rpt->timestamp = trace->hdr.timestamp;
86 : 1985747 : return true;
87 : : }
88 : :
89 : : /*
90 : : * Generate repeat entry: it's the smallest possible entry, so we
91 : : * must have eliminated old entries.
92 : : */
93 : 1135976 : assert(trace->hdr.len_div_8 * 8 >= sizeof(*rpt));
94 : :
95 : 1135976 : rpt = (void *)tb->buf + be64_to_cpu(tb->end) % be64_to_cpu(tb->buf_size);
96 : 1135976 : rpt->timestamp = trace->hdr.timestamp;
97 : 1135976 : rpt->type = TRACE_REPEAT;
98 : 1135976 : rpt->len_div_8 = sizeof(*rpt) >> 3;
99 : 1135976 : rpt->cpu = trace->hdr.cpu;
100 : 1135976 : rpt->prev_len = cpu_to_be16(trace->hdr.len_div_8 << 3);
101 : 1135976 : rpt->num = cpu_to_be16(1);
102 : 1135976 : lwsync(); /* write barrier: complete repeat record before exposing */
103 : 1135976 : tb->end = cpu_to_be64(be64_to_cpu(tb->end) + sizeof(*rpt));
104 : 1135976 : return true;
105 : : }
106 : :
107 : 6418116 : void trace_add(union trace *trace, u8 type, u16 len)
108 : : {
109 : 6418116 : struct trace_info *ti = this_cpu()->trace;
110 : : unsigned int tsz;
111 : :
112 : 6418116 : trace->hdr.type = type;
113 : 6418116 : trace->hdr.len_div_8 = (len + 7) >> 3;
114 : :
115 : 6418116 : tsz = trace->hdr.len_div_8 << 3;
116 : :
117 : : #ifdef DEBUG_TRACES
118 : 6418116 : assert(tsz >= sizeof(trace->hdr));
119 : 6418116 : assert(tsz <= sizeof(*trace));
120 : 6418116 : assert(trace->hdr.type != TRACE_REPEAT);
121 : 6418116 : assert(trace->hdr.type != TRACE_OVERFLOW);
122 : : #endif
123 : : /* Skip traces not enabled in the debug descriptor */
124 : 6418116 : if (trace->hdr.type < (8 * sizeof(debug_descriptor.trace_mask)) &&
125 : 3997699 : !((1ul << trace->hdr.type) & be64_to_cpu(debug_descriptor.trace_mask)))
126 : 0 : return;
127 : :
128 : 6418116 : trace->hdr.timestamp = cpu_to_be64(mftb());
129 : 6418116 : trace->hdr.cpu = cpu_to_be16(this_cpu()->server_no);
130 : :
131 : 6418116 : lock(&ti->lock);
132 : :
133 : : /* Throw away old entries before we overwrite them. */
134 : 10572499 : while ((be64_to_cpu(ti->tb.start) + be64_to_cpu(ti->tb.buf_size))
135 : 10572499 : < (be64_to_cpu(ti->tb.end) + tsz)) {
136 : : struct trace_hdr *hdr;
137 : :
138 : 8308766 : hdr = (void *)ti->tb.buf +
139 : 4154383 : be64_to_cpu(ti->tb.start) % be64_to_cpu(ti->tb.buf_size);
140 : 8308766 : ti->tb.start = cpu_to_be64(be64_to_cpu(ti->tb.start) +
141 : 4154383 : (hdr->len_div_8 << 3));
142 : : }
143 : :
144 : : /* Must update ->start before we rewrite new entries. */
145 : 6418116 : lwsync(); /* write barrier */
146 : :
147 : : /* Check for duplicates... */
148 : 6418116 : if (!handle_repeat(&ti->tb, trace)) {
149 : : /* This may go off end, and that's why ti->tb.buf is oversize */
150 : 3296393 : memcpy(ti->tb.buf + be64_to_cpu(ti->tb.end) % be64_to_cpu(ti->tb.buf_size),
151 : : trace, tsz);
152 : 3296393 : ti->tb.last = ti->tb.end;
153 : 3296393 : lwsync(); /* write barrier: write entry before exposing */
154 : 3296393 : ti->tb.end = cpu_to_be64(be64_to_cpu(ti->tb.end) + tsz);
155 : : }
156 : 6418116 : unlock(&ti->lock);
157 : : }
158 : :
159 : 0 : void trace_add_dt_props(void)
160 : : {
161 : 0 : uint64_t boot_buf_phys = (uint64_t) &boot_tracebuf.trace_info;
162 : : struct dt_node *exports, *traces;
163 : : unsigned int i;
164 : : fdt64_t *prop;
165 : : u64 tmask;
166 : : char tname[256];
167 : :
168 : 0 : exports = dt_find_by_path(opal_node, "firmware/exports");
169 : 0 : if (!exports)
170 : 0 : return;
171 : :
172 : : /*
173 : : * nvram hack to put all the trace buffer exports in the exports
174 : : * node. This is useful if the kernel doesn't also export subnodes.
175 : : */
176 : 0 : if (nvram_query_safe("flat-trace-buf"))
177 : 0 : traces = exports;
178 : : else
179 : 0 : traces = dt_new(exports, "traces");
180 : :
181 : 0 : prop = malloc(sizeof(u64) * 2 * be32_to_cpu(debug_descriptor.num_traces));
182 : :
183 : 0 : for (i = 0; i < be32_to_cpu(debug_descriptor.num_traces); i++) {
184 : 0 : uint64_t addr = be64_to_cpu(debug_descriptor.trace_phys[i]);
185 : 0 : uint64_t size = be32_to_cpu(debug_descriptor.trace_size[i]);
186 : 0 : uint32_t pir = be16_to_cpu(debug_descriptor.trace_pir[i]);
187 : :
188 : 0 : prop[i * 2] = cpu_to_fdt64(addr);
189 : 0 : prop[i * 2 + 1] = cpu_to_fdt64(size);
190 : :
191 : 0 : if (addr == boot_buf_phys)
192 : 0 : snprintf(tname, sizeof(tname), "boot-%x", pir);
193 : : else
194 : 0 : snprintf(tname, sizeof(tname), "trace-%x", pir);
195 : :
196 : 0 : dt_add_property_u64s(traces, tname, addr, size);
197 : : }
198 : :
199 : 0 : dt_add_property(opal_node, "ibm,opal-traces",
200 : : prop, sizeof(u64) * 2 * i);
201 : 0 : free(prop);
202 : :
203 : 0 : tmask = (uint64_t)&debug_descriptor.trace_mask;
204 : 0 : dt_add_property_u64(opal_node, "ibm,opal-trace-mask", tmask);
205 : : }
206 : :
207 : 3 : static void trace_add_desc(struct trace_info *t, uint64_t size, uint16_t pir)
208 : : {
209 : 3 : unsigned int i = be32_to_cpu(debug_descriptor.num_traces);
210 : :
211 : 3 : if (i >= DEBUG_DESC_MAX_TRACES) {
212 : 0 : prerror("TRACE: Debug descriptor trace list full !\n");
213 : 0 : return;
214 : : }
215 : :
216 : 3 : debug_descriptor.num_traces = cpu_to_be32(i + 1);
217 : 3 : debug_descriptor.trace_phys[i] = cpu_to_be64((uint64_t)t);
218 : 3 : debug_descriptor.trace_tce[i] = 0; /* populated later */
219 : 3 : debug_descriptor.trace_size[i] = cpu_to_be32(size);
220 : 3 : debug_descriptor.trace_pir[i] = cpu_to_be16(pir);
221 : : }
222 : :
223 : : /* Allocate trace buffers once we know memory topology */
224 : 1 : void init_trace_buffers(void)
225 : : {
226 : : struct cpu_thread *t;
227 : 1 : struct trace_info *any = &boot_tracebuf.trace_info;
228 : : uint64_t size;
229 : :
230 : : /* Boot the boot trace in the debug descriptor */
231 : 1 : trace_add_desc(any, sizeof(boot_tracebuf), this_cpu()->pir);
232 : :
233 : : /* Allocate a trace buffer for each primary cpu. */
234 : 5 : for_each_cpu(t) {
235 : 4 : if (t->is_secondary)
236 : 2 : continue;
237 : :
238 : : /* Use a 64K alignment for TCE mapping */
239 : 2 : size = ALIGN_UP(sizeof(*t->trace) + tracebuf_extra(), 0x10000);
240 : 2 : t->trace = local_alloc(t->chip_id, size, 0x10000);
241 : 2 : if (t->trace) {
242 : 2 : any = t->trace;
243 : 2 : memset(t->trace, 0, size);
244 : 2 : init_lock(&t->trace->lock);
245 : 2 : t->trace->tb.max_size = cpu_to_be32(MAX_SIZE);
246 : 2 : t->trace->tb.buf_size = cpu_to_be64(TBUF_SZ);
247 : 2 : trace_add_desc(any, sizeof(t->trace->tb) +
248 : 2 : tracebuf_extra(), t->pir);
249 : : } else
250 : 0 : prerror("TRACE: cpu 0x%x allocation failed\n", t->pir);
251 : : }
252 : :
253 : : /* In case any allocations failed, share trace buffers. */
254 : 5 : for_each_cpu(t) {
255 : 4 : if (!t->is_secondary && !t->trace)
256 : 0 : t->trace = any;
257 : : }
258 : :
259 : : /* And copy those to the secondaries. */
260 : 5 : for_each_cpu(t) {
261 : 4 : if (!t->is_secondary)
262 : 2 : continue;
263 : 2 : t->trace = t->primary->trace;
264 : : }
265 : 1 : }
|