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