File Index
Files and directories
Sign in
FILE

coredump.c

File preview with syntax highlighting.

linux-6.19.14/drivers/net/wireless/mediatek/mt76/mt7996/coredump.c C
  1// SPDX-License-Identifier: BSD-3-Clause-Clear
  2/* Copyright (C) 2023 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
 10static bool coredump_memdump;
 11module_param(coredump_memdump, bool, 0644);
 12MODULE_PARM_DESC(coredump_memdump, "Optional ability to dump firmware memory");
 13
 14static const struct mt7996_mem_region mt7996_mem_regions[] = {
 15	{
 16		.start = 0x00800000,
 17		.len = 0x0004ffff,
 18		.name = "ULM0",
 19	},
 20	{
 21		.start = 0x00900000,
 22		.len = 0x00037fff,
 23		.name = "ULM1",
 24	},
 25	{
 26		.start = 0x02200000,
 27		.len = 0x0003ffff,
 28		.name = "ULM2",
 29	},
 30	{
 31		.start = 0x00400000,
 32		.len = 0x00067fff,
 33		.name = "SRAM",
 34	},
 35	{
 36		.start = 0xe0000000,
 37		.len = 0x0015ffff,
 38		.name = "CRAM0",
 39	},
 40	{
 41		.start = 0xe0160000,
 42		.len = 0x0011bfff,
 43		.name = "CRAM1",
 44	},
 45};
 46
 47const struct mt7996_mem_region*
 48mt7996_coredump_get_mem_layout(struct mt7996_dev *dev, u32 *num)
 49{
 50	switch (mt76_chip(&dev->mt76)) {
 51	case MT7996_DEVICE_ID:
 52	case MT7996_DEVICE_ID_2:
 53		*num = ARRAY_SIZE(mt7996_mem_regions);
 54		return &mt7996_mem_regions[0];
 55	default:
 56		return NULL;
 57	}
 58}
 59
 60static int mt7996_coredump_get_mem_size(struct mt7996_dev *dev)
 61{
 62	const struct mt7996_mem_region *mem_region;
 63	size_t size = 0;
 64	u32 num;
 65	int i;
 66
 67	mem_region = mt7996_coredump_get_mem_layout(dev, &num);
 68	if (!mem_region)
 69		return 0;
 70
 71	for (i = 0; i < num; i++) {
 72		size += mem_region->len;
 73		mem_region++;
 74	}
 75
 76	/* reserve space for the headers */
 77	size += num * sizeof(struct mt7996_mem_hdr);
 78	/* make sure it is aligned 4 bytes for debug message print out */
 79	size = ALIGN(size, 4);
 80
 81	return size;
 82}
 83
 84struct mt7996_crash_data *mt7996_coredump_new(struct mt7996_dev *dev)
 85{
 86	struct mt7996_crash_data *crash_data = dev->coredump.crash_data;
 87
 88	lockdep_assert_held(&dev->dump_mutex);
 89
 90	if (coredump_memdump &&
 91	    !mt76_poll_msec(dev, MT_FW_DUMP_STATE, 0x3, 0x2, 500))
 92		return NULL;
 93
 94	guid_gen(&crash_data->guid);
 95	ktime_get_real_ts64(&crash_data->timestamp);
 96
 97	return crash_data;
 98}
 99
100static void
101mt7996_coredump_fw_state(struct mt7996_dev *dev, struct mt7996_coredump *dump,
102			 bool *exception)
103{
104	u32 count;
105
106	count = mt76_rr(dev, MT_FW_ASSERT_CNT);
107
108	/* normal mode: driver can manually trigger assertĀ for detail info */
109	if (!count)
110		strscpy(dump->fw_state, "normal", sizeof(dump->fw_state));
111	else
112		strscpy(dump->fw_state, "exception", sizeof(dump->fw_state));
113
114	*exception = !!count;
115}
116
117static void
118mt7996_coredump_fw_stack(struct mt7996_dev *dev, struct mt7996_coredump *dump,
119			 bool exception)
120{
121	u32 oldest, i, idx;
122
123	strscpy(dump->pc_current, "program counter", sizeof(dump->pc_current));
124
125	/* 0: WM PC log output */
126	mt76_wr(dev, MT_CONN_DBG_CTL_OUT_SEL, 0);
127	/* choose 33th PC log buffer to read current PC index */
128	mt76_wr(dev, MT_CONN_DBG_CTL_PC_LOG_SEL, 0x3f);
129
130	/* read current PC */
131	dump->pc_stack[0] = mt76_rr(dev, MT_CONN_DBG_CTL_PC_LOG);
132
133	/* stop call stack record */
134	if (!exception) {
135		mt76_clear(dev, MT_MCU_WM_EXCP_PC_CTRL, BIT(0));
136		mt76_clear(dev, MT_MCU_WM_EXCP_LR_CTRL, BIT(0));
137	}
138
139	oldest = (u32)mt76_get_field(dev, MT_MCU_WM_EXCP_PC_CTRL,
140				     GENMASK(20, 16)) + 2;
141	for (i = 0; i < 16; i++) {
142		idx = ((oldest + 2 * i + 1) % 32);
143		dump->pc_stack[i + 1] =
144			mt76_rr(dev, MT_MCU_WM_EXCP_PC_LOG + idx * 4);
145	}
146
147	oldest = (u32)mt76_get_field(dev, MT_MCU_WM_EXCP_LR_CTRL,
148				     GENMASK(20, 16)) + 2;
149	for (i = 0; i < 16; i++) {
150		idx = ((oldest + 2 * i + 1) % 32);
151		dump->lr_stack[i] =
152			mt76_rr(dev, MT_MCU_WM_EXCP_LR_LOG + idx * 4);
153	}
154
155	/* start call stack record */
156	if (!exception) {
157		mt76_set(dev, MT_MCU_WM_EXCP_PC_CTRL, BIT(0));
158		mt76_set(dev, MT_MCU_WM_EXCP_LR_CTRL, BIT(0));
159	}
160}
161
162static struct mt7996_coredump *mt7996_coredump_build(struct mt7996_dev *dev)
163{
164	struct mt7996_crash_data *crash_data = dev->coredump.crash_data;
165	struct mt7996_coredump *dump;
166	struct mt7996_coredump_mem *dump_mem;
167	size_t len, sofar = 0, hdr_len = sizeof(*dump);
168	unsigned char *buf;
169	bool exception;
170
171	len = hdr_len;
172
173	if (coredump_memdump && crash_data->memdump_buf_len)
174		len += sizeof(*dump_mem) + crash_data->memdump_buf_len;
175
176	sofar += hdr_len;
177
178	/* this is going to get big when we start dumping memory and such,
179	 * so go ahead and use vmalloc.
180	 */
181	buf = vzalloc(len);
182	if (!buf)
183		return NULL;
184
185	mutex_lock(&dev->dump_mutex);
186
187	dump = (struct mt7996_coredump *)(buf);
188	dump->len = len;
189
190	/* plain text */
191	strscpy(dump->magic, "mt76-crash-dump", sizeof(dump->magic));
192	strscpy(dump->kernel, init_utsname()->release, sizeof(dump->kernel));
193	strscpy(dump->fw_ver, dev->mt76.hw->wiphy->fw_version,
194		sizeof(dump->fw_ver));
195
196	guid_copy(&dump->guid, &crash_data->guid);
197	dump->tv_sec = crash_data->timestamp.tv_sec;
198	dump->tv_nsec = crash_data->timestamp.tv_nsec;
199	dump->device_id = mt76_chip(&dev->mt76);
200
201	mt7996_coredump_fw_state(dev, dump, &exception);
202	mt7996_coredump_fw_stack(dev, dump, exception);
203
204	/* gather memory content */
205	dump_mem = (struct mt7996_coredump_mem *)(buf + sofar);
206	dump_mem->len = crash_data->memdump_buf_len;
207	if (coredump_memdump && crash_data->memdump_buf_len)
208		memcpy(dump_mem->data, crash_data->memdump_buf,
209		       crash_data->memdump_buf_len);
210
211	mutex_unlock(&dev->dump_mutex);
212
213	return dump;
214}
215
216int mt7996_coredump_submit(struct mt7996_dev *dev)
217{
218	struct mt7996_coredump *dump;
219
220	dump = mt7996_coredump_build(dev);
221	if (!dump) {
222		dev_warn(dev->mt76.dev, "no crash dump data found\n");
223		return -ENODATA;
224	}
225
226	dev_coredumpv(dev->mt76.dev, dump, dump->len, GFP_KERNEL);
227
228	return 0;
229}
230
231int mt7996_coredump_register(struct mt7996_dev *dev)
232{
233	struct mt7996_crash_data *crash_data;
234
235	crash_data = vzalloc(sizeof(*dev->coredump.crash_data));
236	if (!crash_data)
237		return -ENOMEM;
238
239	dev->coredump.crash_data = crash_data;
240
241	if (coredump_memdump) {
242		crash_data->memdump_buf_len = mt7996_coredump_get_mem_size(dev);
243		if (!crash_data->memdump_buf_len)
244			/* no memory content */
245			return 0;
246
247		crash_data->memdump_buf = vzalloc(crash_data->memdump_buf_len);
248		if (!crash_data->memdump_buf) {
249			vfree(crash_data);
250			return -ENOMEM;
251		}
252	}
253
254	return 0;
255}
256
257void mt7996_coredump_unregister(struct mt7996_dev *dev)
258{
259	if (dev->coredump.crash_data->memdump_buf) {
260		vfree(dev->coredump.crash_data->memdump_buf);
261		dev->coredump.crash_data->memdump_buf = NULL;
262		dev->coredump.crash_data->memdump_buf_len = 0;
263	}
264
265	vfree(dev->coredump.crash_data);
266	dev->coredump.crash_data = NULL;
267}
268
~/ Read-only file preview.