1 // SPDX-License-Identifier: ISC 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 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 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 47 const struct mt7996_mem_region* 48 mt7996_coredump_get_mem_layout(struct mt7996_dev *dev, u32 *num) 49 { 50 switch (mt76_chip(&dev->mt76)) { 51 case 0x7990: 52 case 0x7991: 53 *num = ARRAY_SIZE(mt7996_mem_regions); 54 return &mt7996_mem_regions[0]; 55 default: 56 return NULL; 57 } 58 } 59 60 static 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 84 struct 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 100 static void 101 mt7996_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 117 static void 118 mt7996_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 162 static 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 216 int 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 231 int 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 257 void 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 269