brintos

brintos / linux-shallow public Read only

0
0
Text · 10.0 KiB · 5daf225 Raw
412 lines · c
1// SPDX-License-Identifier: ISC2/* 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 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 mt7915_mem_region mt7915_mem_regions[] = {15	{16		.start = 0xe003b400,17		.len = 0x00003bff,18		.name = "CRAM",19	},20};21 22static 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 55static 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 88const struct mt7915_mem_region*89mt7915_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 107static 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 131struct 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 143static void144mt7915_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 166static void167mt7915_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 223static void224mt7915_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 244static void245mt7915_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 268static void269mt7915_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 302static 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 359int 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 374int 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 400void 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