1 // SPDX-License-Identifier: ISC
2 /* Copyright (C) 2022 MediaTek Inc. */
3
4 #include <linux/devcoredump.h>
5 #include <linux/kernel.h>
6 #include <linux/types.h>
7 #include <linux/utsname.h>
8 #include "coredump.h"
9
10 static bool coredump_memdump;
11 module_param(coredump_memdump, bool, 0644);
12 MODULE_PARM_DESC(coredump_memdump, "Optional ability to dump firmware memory");
13
14 static const struct mt7915_mem_region mt7915_mem_regions[] = {
15 {
16 .start = 0xe003b400,
17 .len = 0x00003bff,
18 .name = "CRAM",
19 },
20 };
21
22 static const struct mt7915_mem_region mt7916_mem_regions[] = {
23 {
24 .start = 0x00800000,
25 .len = 0x0005ffff,
26 .name = "ROM",
27 },
28 {
29 .start = 0x00900000,
30 .len = 0x00013fff,
31 .name = "ULM1",
32 },
33 {
34 .start = 0x02200000,
35 .len = 0x0004ffff,
36 .name = "ULM2",
37 },
38 {
39 .start = 0x02300000,
40 .len = 0x0004ffff,
41 .name = "ULM3",
42 },
43 {
44 .start = 0x00400000,
45 .len = 0x00027fff,
46 .name = "SRAM",
47 },
48 {
49 .start = 0xe0000000,
50 .len = 0x00157fff,
51 .name = "CRAM",
52 },
53 };
54
55 static const struct mt7915_mem_region mt7986_mem_regions[] = {
56 {
57 .start = 0x00800000,
58 .len = 0x0005ffff,
59 .name = "ROM",
60 },
61 {
62 .start = 0x00900000,
63 .len = 0x0000ffff,
64 .name = "ULM1",
65 },
66 {
67 .start = 0x02200000,
68 .len = 0x0004ffff,
69 .name = "ULM2",
70 },
71 {
72 .start = 0x02300000,
73 .len = 0x0004ffff,
74 .name = "ULM3",
75 },
76 {
77 .start = 0x00400000,
78 .len = 0x00017fff,
79 .name = "SRAM",
80 },
81 {
82 .start = 0xe0000000,
83 .len = 0x00113fff,
84 .name = "CRAM",
85 },
86 };
87
88 const struct mt7915_mem_region*
mt7915_coredump_get_mem_layout(struct mt7915_dev * dev,u32 * num)89 mt7915_coredump_get_mem_layout(struct mt7915_dev *dev, u32 *num)
90 {
91 switch (mt76_chip(&dev->mt76)) {
92 case 0x7915:
93 *num = ARRAY_SIZE(mt7915_mem_regions);
94 return &mt7915_mem_regions[0];
95 case 0x7986:
96 *num = ARRAY_SIZE(mt7986_mem_regions);
97 return &mt7986_mem_regions[0];
98 case 0x7916:
99 *num = ARRAY_SIZE(mt7916_mem_regions);
100 return &mt7916_mem_regions[0];
101 default:
102 return NULL;
103 }
104 }
105
mt7915_coredump_get_mem_size(struct mt7915_dev * dev)106 static int mt7915_coredump_get_mem_size(struct mt7915_dev *dev)
107 {
108 const struct mt7915_mem_region *mem_region;
109 size_t size = 0;
110 u32 num;
111 int i;
112
113 mem_region = mt7915_coredump_get_mem_layout(dev, &num);
114 if (!mem_region)
115 return 0;
116
117 for (i = 0; i < num; i++) {
118 size += mem_region->len;
119 mem_region++;
120 }
121
122 /* reserve space for the headers */
123 size += num * sizeof(struct mt7915_mem_hdr);
124 /* make sure it is aligned 4 bytes for debug message print out */
125 size = ALIGN(size, 4);
126
127 return size;
128 }
129
mt7915_coredump_new(struct mt7915_dev * dev)130 struct mt7915_crash_data *mt7915_coredump_new(struct mt7915_dev *dev)
131 {
132 struct mt7915_crash_data *crash_data = dev->coredump.crash_data;
133
134 lockdep_assert_held(&dev->dump_mutex);
135
136 guid_gen(&crash_data->guid);
137 ktime_get_real_ts64(&crash_data->timestamp);
138
139 return crash_data;
140 }
141
142 static void
mt7915_coredump_fw_state(struct mt7915_dev * dev,struct mt7915_coredump * dump,bool * exception)143 mt7915_coredump_fw_state(struct mt7915_dev *dev, struct mt7915_coredump *dump,
144 bool *exception)
145 {
146 u32 state, count, type;
147
148 type = (u32)mt76_get_field(dev, MT_FW_EXCEPT_TYPE, GENMASK(7, 0));
149 state = (u32)mt76_get_field(dev, MT_FW_ASSERT_STAT, GENMASK(7, 0));
150 count = is_mt7915(&dev->mt76) ?
151 (u32)mt76_get_field(dev, MT_FW_EXCEPT_COUNT, GENMASK(15, 8)) :
152 (u32)mt76_get_field(dev, MT_FW_EXCEPT_COUNT, GENMASK(7, 0));
153
154 /* normal mode: driver can manually trigger assert for detail info */
155 if (!count)
156 strscpy(dump->fw_state, "normal", sizeof(dump->fw_state));
157 else if (state > 1 && (count == 1) && type == 5)
158 strscpy(dump->fw_state, "assert", sizeof(dump->fw_state));
159 else if ((state > 1 && count == 1) || count > 1)
160 strscpy(dump->fw_state, "exception", sizeof(dump->fw_state));
161
162 *exception = !!count;
163 }
164
165 static void
mt7915_coredump_fw_trace(struct mt7915_dev * dev,struct mt7915_coredump * dump,bool exception)166 mt7915_coredump_fw_trace(struct mt7915_dev *dev, struct mt7915_coredump *dump,
167 bool exception)
168 {
169 u32 n, irq, sch, base = MT_FW_EINT_INFO;
170
171 /* trap or run? */
172 dump->last_msg_id = mt76_rr(dev, MT_FW_LAST_MSG_ID);
173
174 n = is_mt7915(&dev->mt76) ?
175 (u32)mt76_get_field(dev, base, GENMASK(7, 0)) :
176 (u32)mt76_get_field(dev, base, GENMASK(15, 8));
177 dump->eint_info_idx = n;
178
179 irq = mt76_rr(dev, base + 0x8);
180 n = is_mt7915(&dev->mt76) ?
181 FIELD_GET(GENMASK(7, 0), irq) : FIELD_GET(GENMASK(23, 16), irq);
182 dump->irq_info_idx = n;
183
184 sch = mt76_rr(dev, MT_FW_SCHED_INFO);
185 n = is_mt7915(&dev->mt76) ?
186 FIELD_GET(GENMASK(7, 0), sch) : FIELD_GET(GENMASK(15, 8), sch);
187 dump->sched_info_idx = n;
188
189 if (exception) {
190 u32 i, y;
191
192 /* sched trace */
193 n = is_mt7915(&dev->mt76) ?
194 FIELD_GET(GENMASK(15, 8), sch) : FIELD_GET(GENMASK(7, 0), sch);
195 n = n > 60 ? 60 : n;
196
197 strscpy(dump->trace_sched, "(sched_info) id, time",
198 sizeof(dump->trace_sched));
199
200 for (y = dump->sched_info_idx, i = 0; i < n; i++, y++) {
201 mt7915_memcpy_fromio(dev, dump->sched, base + 0xc + y * 12,
202 sizeof(dump->sched));
203 y = y >= n ? 0 : y;
204 }
205
206 /* irq trace */
207 n = is_mt7915(&dev->mt76) ?
208 FIELD_GET(GENMASK(15, 8), irq) : FIELD_GET(GENMASK(7, 0), irq);
209 n = n > 60 ? 60 : n;
210
211 strscpy(dump->trace_irq, "(irq_info) id, time",
212 sizeof(dump->trace_irq));
213
214 for (y = dump->irq_info_idx, i = 0; i < n; i++, y++) {
215 mt7915_memcpy_fromio(dev, dump->irq, base + 0x4 + y * 16,
216 sizeof(dump->irq));
217 y = y >= n ? 0 : y;
218 }
219 }
220 }
221
222 static void
mt7915_coredump_fw_stack(struct mt7915_dev * dev,struct mt7915_coredump * dump,bool exception)223 mt7915_coredump_fw_stack(struct mt7915_dev *dev, struct mt7915_coredump *dump,
224 bool exception)
225 {
226 u32 oldest, i, idx;
227
228 /* stop call stack record */
229 if (!exception)
230 mt76_clear(dev, 0x89050200, BIT(0));
231
232 oldest = (u32)mt76_get_field(dev, 0x89050200, GENMASK(20, 16)) + 2;
233 for (i = 0; i < 16; i++) {
234 idx = ((oldest + 2 * i + 1) % 32);
235 dump->call_stack[i] = mt76_rr(dev, 0x89050204 + idx * 4);
236 }
237
238 /* start call stack record */
239 if (!exception)
240 mt76_set(dev, 0x89050200, BIT(0));
241 }
242
243 static void
mt7915_coredump_fw_task(struct mt7915_dev * dev,struct mt7915_coredump * dump)244 mt7915_coredump_fw_task(struct mt7915_dev *dev, struct mt7915_coredump *dump)
245 {
246 u32 offs = is_mt7915(&dev->mt76) ? 0xe0 : 0x170;
247
248 strscpy(dump->task_qid, "(task queue id) read, write",
249 sizeof(dump->task_qid));
250
251 dump->taskq[0].read = mt76_rr(dev, MT_FW_TASK_QID1);
252 dump->taskq[0].write = mt76_rr(dev, MT_FW_TASK_QID1 - 4);
253 dump->taskq[1].read = mt76_rr(dev, MT_FW_TASK_QID2);
254 dump->taskq[1].write = mt76_rr(dev, MT_FW_TASK_QID2 - 4);
255
256 strscpy(dump->task_info, "(task stack) start, end, size",
257 sizeof(dump->task_info));
258
259 dump->taski[0].start = mt76_rr(dev, MT_FW_TASK_START);
260 dump->taski[0].end = mt76_rr(dev, MT_FW_TASK_END);
261 dump->taski[0].size = mt76_rr(dev, MT_FW_TASK_SIZE);
262 dump->taski[1].start = mt76_rr(dev, MT_FW_TASK_START + offs);
263 dump->taski[1].end = mt76_rr(dev, MT_FW_TASK_END + offs);
264 dump->taski[1].size = mt76_rr(dev, MT_FW_TASK_SIZE + offs);
265 }
266
267 static void
mt7915_coredump_fw_context(struct mt7915_dev * dev,struct mt7915_coredump * dump)268 mt7915_coredump_fw_context(struct mt7915_dev *dev, struct mt7915_coredump *dump)
269 {
270 u32 count, idx, id;
271
272 count = mt76_rr(dev, MT_FW_CIRQ_COUNT);
273
274 /* current context */
275 if (!count) {
276 strscpy(dump->fw_context, "(context) interrupt",
277 sizeof(dump->fw_context));
278
279 idx = is_mt7915(&dev->mt76) ?
280 (u32)mt76_get_field(dev, MT_FW_CIRQ_IDX, GENMASK(31, 16)) :
281 (u32)mt76_get_field(dev, MT_FW_CIRQ_IDX, GENMASK(15, 0));
282 dump->context.idx = idx;
283 dump->context.handler = mt76_rr(dev, MT_FW_CIRQ_LISR);
284 } else {
285 idx = mt76_rr(dev, MT_FW_TASK_IDX);
286 id = mt76_rr(dev, MT_FW_TASK_ID);
287
288 if (!id && idx == 3) {
289 strscpy(dump->fw_context, "(context) idle",
290 sizeof(dump->fw_context));
291 } else if (id && idx != 3) {
292 strscpy(dump->fw_context, "(context) task",
293 sizeof(dump->fw_context));
294
295 dump->context.idx = idx;
296 dump->context.handler = id;
297 }
298 }
299 }
300
mt7915_coredump_build(struct mt7915_dev * dev)301 static struct mt7915_coredump *mt7915_coredump_build(struct mt7915_dev *dev)
302 {
303 struct mt7915_crash_data *crash_data = dev->coredump.crash_data;
304 struct mt7915_coredump *dump;
305 struct mt7915_coredump_mem *dump_mem;
306 size_t len, sofar = 0, hdr_len = sizeof(*dump);
307 unsigned char *buf;
308 bool exception;
309
310 len = hdr_len;
311
312 if (coredump_memdump && crash_data->memdump_buf_len)
313 len += sizeof(*dump_mem) + crash_data->memdump_buf_len;
314
315 sofar += hdr_len;
316
317 /* this is going to get big when we start dumping memory and such,
318 * so go ahead and use vmalloc.
319 */
320 buf = vzalloc(len);
321 if (!buf)
322 return NULL;
323
324 mutex_lock(&dev->dump_mutex);
325
326 dump = (struct mt7915_coredump *)(buf);
327 dump->len = len;
328
329 /* plain text */
330 strscpy(dump->magic, "mt76-crash-dump", sizeof(dump->magic));
331 strscpy(dump->kernel, init_utsname()->release, sizeof(dump->kernel));
332 strscpy(dump->fw_ver, dev->mt76.hw->wiphy->fw_version,
333 sizeof(dump->fw_ver));
334
335 guid_copy(&dump->guid, &crash_data->guid);
336 dump->tv_sec = crash_data->timestamp.tv_sec;
337 dump->tv_nsec = crash_data->timestamp.tv_nsec;
338 dump->device_id = mt76_chip(&dev->mt76);
339
340 mt7915_coredump_fw_state(dev, dump, &exception);
341 mt7915_coredump_fw_trace(dev, dump, exception);
342 mt7915_coredump_fw_task(dev, dump);
343 mt7915_coredump_fw_context(dev, dump);
344 mt7915_coredump_fw_stack(dev, dump, exception);
345
346 /* gather memory content */
347 dump_mem = (struct mt7915_coredump_mem *)(buf + sofar);
348 dump_mem->len = crash_data->memdump_buf_len;
349 if (coredump_memdump && crash_data->memdump_buf_len)
350 memcpy(dump_mem->data, crash_data->memdump_buf,
351 crash_data->memdump_buf_len);
352
353 mutex_unlock(&dev->dump_mutex);
354
355 return dump;
356 }
357
mt7915_coredump_submit(struct mt7915_dev * dev)358 int mt7915_coredump_submit(struct mt7915_dev *dev)
359 {
360 struct mt7915_coredump *dump;
361
362 dump = mt7915_coredump_build(dev);
363 if (!dump) {
364 dev_warn(dev->mt76.dev, "no crash dump data found\n");
365 return -ENODATA;
366 }
367
368 dev_coredumpv(dev->mt76.dev, dump, dump->len, GFP_KERNEL);
369
370 return 0;
371 }
372
mt7915_coredump_register(struct mt7915_dev * dev)373 int mt7915_coredump_register(struct mt7915_dev *dev)
374 {
375 struct mt7915_crash_data *crash_data;
376
377 crash_data = vzalloc(sizeof(*dev->coredump.crash_data));
378 if (!crash_data)
379 return -ENOMEM;
380
381 dev->coredump.crash_data = crash_data;
382
383 if (coredump_memdump) {
384 crash_data->memdump_buf_len = mt7915_coredump_get_mem_size(dev);
385 if (!crash_data->memdump_buf_len)
386 /* no memory content */
387 return 0;
388
389 crash_data->memdump_buf = vzalloc(crash_data->memdump_buf_len);
390 if (!crash_data->memdump_buf) {
391 vfree(crash_data);
392 return -ENOMEM;
393 }
394 }
395
396 return 0;
397 }
398
mt7915_coredump_unregister(struct mt7915_dev * dev)399 void mt7915_coredump_unregister(struct mt7915_dev *dev)
400 {
401 if (dev->coredump.crash_data->memdump_buf) {
402 vfree(dev->coredump.crash_data->memdump_buf);
403 dev->coredump.crash_data->memdump_buf = NULL;
404 dev->coredump.crash_data->memdump_buf_len = 0;
405 }
406
407 vfree(dev->coredump.crash_data);
408 dev->coredump.crash_data = NULL;
409 }
410
411