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*
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 
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 
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
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
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
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
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
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 
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 
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 
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 
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