1 // SPDX-License-Identifier: ISC
2 /* Copyright (C) 2023 MediaTek Inc. */
4 #include <linux/devcoredump.h>
5 #include <linux/kernel.h>
6 #include <linux/types.h>
7 #include <linux/utsname.h>
10 static bool coredump_memdump
;
11 module_param(coredump_memdump
, bool, 0644);
12 MODULE_PARM_DESC(coredump_memdump
, "Optional ability to dump firmware memory");
14 static const struct mt7996_mem_region mt7996_mem_regions
[] = {
47 const struct mt7996_mem_region
*
48 mt7996_coredump_get_mem_layout(struct mt7996_dev
*dev
, u32
*num
)
50 switch (mt76_chip(&dev
->mt76
)) {
53 *num
= ARRAY_SIZE(mt7996_mem_regions
);
54 return &mt7996_mem_regions
[0];
60 static int mt7996_coredump_get_mem_size(struct mt7996_dev
*dev
)
62 const struct mt7996_mem_region
*mem_region
;
67 mem_region
= mt7996_coredump_get_mem_layout(dev
, &num
);
71 for (i
= 0; i
< num
; i
++) {
72 size
+= mem_region
->len
;
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);
84 struct mt7996_crash_data
*mt7996_coredump_new(struct mt7996_dev
*dev
)
86 struct mt7996_crash_data
*crash_data
= dev
->coredump
.crash_data
;
88 lockdep_assert_held(&dev
->dump_mutex
);
90 if (coredump_memdump
&&
91 !mt76_poll_msec(dev
, MT_FW_DUMP_STATE
, 0x3, 0x2, 500))
94 guid_gen(&crash_data
->guid
);
95 ktime_get_real_ts64(&crash_data
->timestamp
);
101 mt7996_coredump_fw_state(struct mt7996_dev
*dev
, struct mt7996_coredump
*dump
,
106 count
= mt76_rr(dev
, MT_FW_ASSERT_CNT
);
108 /* normal mode: driver can manually trigger assert for detail info */
110 strscpy(dump
->fw_state
, "normal", sizeof(dump
->fw_state
));
112 strscpy(dump
->fw_state
, "exception", sizeof(dump
->fw_state
));
114 *exception
= !!count
;
118 mt7996_coredump_fw_stack(struct mt7996_dev
*dev
, struct mt7996_coredump
*dump
,
123 strscpy(dump
->pc_current
, "program counter", sizeof(dump
->pc_current
));
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);
130 /* read current PC */
131 dump
->pc_stack
[0] = mt76_rr(dev
, MT_CONN_DBG_CTL_PC_LOG
);
133 /* stop call stack record */
135 mt76_clear(dev
, MT_MCU_WM_EXCP_PC_CTRL
, BIT(0));
136 mt76_clear(dev
, MT_MCU_WM_EXCP_LR_CTRL
, BIT(0));
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);
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);
152 mt76_rr(dev
, MT_MCU_WM_EXCP_LR_LOG
+ idx
* 4);
155 /* start call stack record */
157 mt76_set(dev
, MT_MCU_WM_EXCP_PC_CTRL
, BIT(0));
158 mt76_set(dev
, MT_MCU_WM_EXCP_LR_CTRL
, BIT(0));
162 static struct mt7996_coredump
*mt7996_coredump_build(struct mt7996_dev
*dev
)
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
);
173 if (coredump_memdump
&& crash_data
->memdump_buf_len
)
174 len
+= sizeof(*dump_mem
) + crash_data
->memdump_buf_len
;
178 /* this is going to get big when we start dumping memory and such,
179 * so go ahead and use vmalloc.
185 mutex_lock(&dev
->dump_mutex
);
187 dump
= (struct mt7996_coredump
*)(buf
);
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
));
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
);
201 mt7996_coredump_fw_state(dev
, dump
, &exception
);
202 mt7996_coredump_fw_stack(dev
, dump
, exception
);
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
);
211 mutex_unlock(&dev
->dump_mutex
);
216 int mt7996_coredump_submit(struct mt7996_dev
*dev
)
218 struct mt7996_coredump
*dump
;
220 dump
= mt7996_coredump_build(dev
);
222 dev_warn(dev
->mt76
.dev
, "no crash dump data found\n");
226 dev_coredumpv(dev
->mt76
.dev
, dump
, dump
->len
, GFP_KERNEL
);
231 int mt7996_coredump_register(struct mt7996_dev
*dev
)
233 struct mt7996_crash_data
*crash_data
;
235 crash_data
= vzalloc(sizeof(*dev
->coredump
.crash_data
));
239 dev
->coredump
.crash_data
= crash_data
;
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 */
247 crash_data
->memdump_buf
= vzalloc(crash_data
->memdump_buf_len
);
248 if (!crash_data
->memdump_buf
) {
257 void mt7996_coredump_unregister(struct mt7996_dev
*dev
)
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;
265 vfree(dev
->coredump
.crash_data
);
266 dev
->coredump
.crash_data
= NULL
;