brintos

brintos / linux-shallow public Read only

0
0
Text · 6.2 KiB · ccab0d7 Raw
269 lines · c
1// SPDX-License-Identifier: ISC2/* 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 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 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 47const struct mt7996_mem_region*48mt7996_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 60static 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 84struct 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 100static void101mt7996_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	else112		strscpy(dump->fw_state, "exception", sizeof(dump->fw_state));113 114	*exception = !!count;115}116 117static void118mt7996_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 162static 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 216int 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 231int 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 257void 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