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