diff mbox series

[RFC,v2,17/96] cl8k: add debug.c

Message ID 20220524113502.1094459-18-viktor.barna@celeno.com
State New
Headers show
Series wireless: cl8k driver for Celeno IEEE 802.11ax devices | expand

Commit Message

Viktor Barna May 24, 2022, 11:33 a.m. UTC
From: Viktor Barna <viktor.barna@celeno.com>

(Part of the split. Please, take a look at the cover letter for more
details).

Signed-off-by: Viktor Barna <viktor.barna@celeno.com>
---
 drivers/net/wireless/celeno/cl8k/debug.c | 442 +++++++++++++++++++++++
 1 file changed, 442 insertions(+)
 create mode 100644 drivers/net/wireless/celeno/cl8k/debug.c
diff mbox series

Patch

diff --git a/drivers/net/wireless/celeno/cl8k/debug.c b/drivers/net/wireless/celeno/cl8k/debug.c
new file mode 100644
index 000000000000..f8a438747ac3
--- /dev/null
+++ b/drivers/net/wireless/celeno/cl8k/debug.c
@@ -0,0 +1,442 @@ 
+// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause
+/* Copyright(c) 2019-2022, Celeno Communications Ltd. */
+
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/kmod.h>
+#include <linux/string.h>
+#include <linux/list.h>
+#include <linux/ctype.h>
+
+#include "chip.h"
+#include "hw.h"
+#include "utils.h"
+#include "debug.h"
+
+const char *cl_dbgfile_get_msg_txt(struct cl_dbg_data *dbg_data, u16 file_id, u16 line)
+{
+	/* Get the message text from the .dbg file by file_id & line number */
+	int remaining_bytes = dbg_data->size;
+	const char *str = dbg_data->str;
+	char id_str[32];
+	int idstr_len;
+
+	if (!str || 0 == remaining_bytes)
+		return NULL;
+
+	idstr_len = snprintf(id_str, sizeof(id_str), "%u:%u:", file_id, line);
+
+	/* Skip hash */
+	while (*str++ != '\n')
+		;
+
+	remaining_bytes -= (str - (char *)dbg_data->str);
+
+	while (remaining_bytes > 0) {
+		if (strncmp(id_str, str, idstr_len) == 0) {
+			str += idstr_len;
+			while (*str == ' ')
+				++str;
+			return (const char *)str;
+		}
+
+		str += strnlen(str, 512) + 1;
+		remaining_bytes = dbg_data->size - (str - (char *)dbg_data->str);
+	}
+
+	/* No match found */
+	pr_err("error: file_id=%d line=%d not found in debug print file\n", file_id, line);
+	return NULL;
+}
+
+void cl_dbgfile_parse(struct cl_hw *cl_hw, void *edata, u32 esize)
+{
+	/* Parse & store the firmware debug file */
+	struct cl_dbg_data *dbg_data = &cl_hw->dbg_data;
+
+	dbg_data->size = esize;
+	dbg_data->str = edata;
+}
+
+void cl_dbgfile_release_mem(struct cl_dbg_data *dbg_data,
+			    struct cl_str_offload_env *str_offload_env)
+{
+	dbg_data->str = NULL;
+
+	str_offload_env->enabled = false;
+	str_offload_env->block1 = NULL;
+	str_offload_env->block2 = NULL;
+}
+
+/*
+ * Store debug print offload data
+ * - part 1: offloaded block that does not exist on target
+ * - part 2: resident block that remains on target [optional]
+ */
+int cl_dbgfile_store_offload_data(struct cl_chip *chip, struct cl_hw *cl_hw,
+				  void *data1, u32 size1, u32 base1,
+				  void *data2, u32 size2, u32 base2,
+				  void *data3, u32 size3, u32 base3)
+{
+	u32 u = size1;
+	struct cl_str_offload_env *str_offload_env = &cl_hw->str_offload_env;
+
+	if (u > 200000)
+		goto err_too_big;
+
+	/* TODO we modify offload data! if caller checks integrity, make a copy? */
+	str_offload_env->block1 = data1;
+	str_offload_env->size1 = size1;
+	str_offload_env->base1 = base1;
+
+	str_offload_env->block2 = data2;
+	str_offload_env->size2 = size2;
+	str_offload_env->base2 = base2;
+
+	str_offload_env->block3 = data3;
+	str_offload_env->size3 = size3;
+	str_offload_env->base3 = base3;
+
+	str_offload_env->enabled = true;
+
+	cl_dbg_info(cl_hw, "%cmac%u: FW prints offload memory use = %uK\n",
+		    cl_hw->fw_prefix, chip->idx, (size1 + size2 + 1023) / 1024);
+
+	return 0;
+
+err_too_big:
+	pr_err("%s: size too big: %u\n", __func__, u);
+	return 1;
+}
+
+static void cl_fw_do_print_n(struct cl_hw *cl_hw, const char *str, int n)
+{
+	/* Print formatted string with "band" prefix */
+	if (n < 0 || n > 256) {
+		cl_dbg_err(cl_hw, "%cmac%u: *** FW PRINT - BAD SIZE: %d\n",
+			   cl_hw->fw_prefix, cl_hw->chip->idx, n);
+		return;
+	}
+
+	cl_dbg_verbose(cl_hw, "%cmac%u: %.*s\n", cl_hw->fw_prefix, cl_hw->chip->idx, n, str);
+}
+
+static void cl_fw_do_hex_dump_bytes(struct cl_hw *cl_hw, u32 addr, void *data, u32 count)
+{
+	cl_dbg_verbose(cl_hw, "%cmac%u: hex dump:\n", cl_hw->fw_prefix, cl_hw->chip->idx);
+	cl_hex_dump(NULL, data, count, addr, true);
+}
+
+#define MAGIC_PRINT_OFFLOAD   0xFA  /* 1st (low) byte of signature */
+/* 2nd signature byte */
+#define MAGIC_PRINT_OFF_XDUMP 0xD0  /* Hex dump, by bytes */
+#define MAGIC_PRINT_OFF_LIT   0x01  /* Literal/preformatted string */
+#define MAGIC_PRINT_OFF_PRINT 0x02  /* Print with 'virtual' format string */
+
+static int cl_fw_offload_print(struct cl_str_offload_env *str_offload_env,
+			       char *fmt, const char *params)
+{
+	static char buf[1024] = {0};
+	const char *cur_prm = params;
+	char tmp;
+	char *fmt_end = fmt;
+	size_t size = sizeof(int);
+	int len = 0;
+
+	union v {
+		u32 val32;
+		u64 val64;
+		ptrdiff_t str;
+	} v;
+
+	while ((fmt_end = strchr(fmt_end, '%'))) {
+		fmt_end++;
+
+		/* Skip '%%'. */
+		if (*fmt_end == '%') {
+			fmt_end++;
+			continue;
+		}
+
+		/* Skip flags. */
+		while (strchr("-+ 0#", *fmt_end))
+			fmt_end++;
+
+		/* Skip width. */
+		while (isdigit(*fmt_end))
+			fmt_end++;
+
+		/* Skip precision. */
+		if (*fmt_end == '.') {
+			while (*fmt_end == '-' || *fmt_end == '+')
+				fmt_end++;
+
+			while (isdigit(*fmt_end))
+				fmt_end++;
+		}
+
+		/* Get size. */
+		if (*fmt_end == 'z') {
+			/* Remove 'z' from %zu, %zd, %zx and %zX,
+			 * because sizeof(size_t) == 4 in the firmware.
+			 * 'z' can only appear in front of 'd', 'u', 'x' or 'X'.
+			 */
+			if (!strchr("duxX", *(fmt_end + 1)))
+				return -1;
+
+			fmt_end++;
+			size = 4;
+		} else if (*fmt_end == 'l') {
+			fmt_end++;
+
+			if (*fmt_end == 'l') {
+				fmt_end++;
+				size = sizeof(long long);
+			} else {
+				size = sizeof(long);
+			}
+
+			if (*fmt_end == 'p') /* %p can't get 'l' or 'll' modifiers. */
+				return -1;
+		} else {
+			size = 4;
+		}
+
+		/* Get parameter. */
+		switch (*fmt_end) {
+		case 'p': /* Replace %p with %x, because the firmware's pointers are 32 bit wide */
+			*fmt_end = 'x';
+			fallthrough;
+		case 'd':
+		case 'u':
+		case 'x':
+		case 'X':
+			if (size == 4)
+				v.val32 = __le32_to_cpu(*(__le32 *)cur_prm);
+			else
+				v.val64 = __le64_to_cpu(*(__le64 *)cur_prm);
+			cur_prm += size;
+			break;
+		case 's':
+			v.str = __le32_to_cpu(*(__le32 *)cur_prm);
+			cur_prm += 4;
+			size = sizeof(ptrdiff_t);
+
+			if (v.str >= str_offload_env->base3 &&
+			    v.str < str_offload_env->base3 + str_offload_env->size3) {
+				v.str -= str_offload_env->base3;
+				v.str += (ptrdiff_t)str_offload_env->block3;
+			} else if (v.str >= str_offload_env->base2 &&
+				   v.str < str_offload_env->base2 + str_offload_env->size2) {
+				v.str -= str_offload_env->base2;
+				v.str += (ptrdiff_t)str_offload_env->block2;
+			} else {
+				return -1;
+			}
+
+			break;
+		default:
+			return -1;
+		}
+
+		/* Print into buffer. */
+		fmt_end++;
+		tmp = *fmt_end; /* Truncate the format to the current point and then restore. */
+		*fmt_end = 0;
+		len += snprintf(buf + len, sizeof(buf) - len, fmt, size == 4 ? v.val32 : v.val64);
+		*fmt_end = tmp;
+		fmt = fmt_end;
+	}
+
+	snprintf(buf + len, sizeof(buf) - len, "%s", fmt);
+
+	pr_debug("%s", buf);
+
+	return 0;
+}
+
+struct cl_pr_off_desc {
+	u8 file_id;
+	u8 flag;
+	__le16 line_num;
+	char fmt[];
+};
+
+char *cl_fw_print_normalize(char *s, char old, char new)
+{
+	for (; *s; ++s)
+		if (*s == old)
+			*s = new;
+	return s;
+}
+
+static int cl_fw_do_dprint(struct cl_hw *cl_hw, u32 fmtaddr, u32 nparams, u32 *params)
+{
+	/*
+	 * fmtaddr - virtual address of format descriptor in firmware,
+	 *           must be in the offloaded segment
+	 * nparams - size of parameters array in u32; min=0, max=MAX_PRINT_OFF_PARAMS
+	 * params  - array of parameters[nparams]
+	 */
+	struct cl_str_offload_env *str_offload_env = &cl_hw->str_offload_env;
+	struct cl_pr_off_desc *pfmt = NULL;
+
+	if (!str_offload_env->enabled)
+		return -1;
+
+	if (fmtaddr & 0x3)
+		cl_dbg_warn(cl_hw, "FW PRINT - format not aligned on 4? %8.8X\n", fmtaddr);
+
+	if (fmtaddr > str_offload_env->base1 &&
+	    fmtaddr < (str_offload_env->base1 + str_offload_env->size1)) {
+		pfmt = (void *)((fmtaddr - str_offload_env->base1) + str_offload_env->block1);
+	} else {
+		cl_dbg_err(cl_hw, "FW PRINT - format not in allowed area %8.8X\n", fmtaddr);
+		return -1;
+	}
+
+	/*
+	 * Current string sent by firmware is #mac@ where # is '253' and @ is '254'
+	 * Replace '253' with 'l' or 's' according to the fw_prefix.
+	 * Replace '254' with '0' or '1' according to chip index.
+	 */
+	cl_fw_print_normalize(pfmt->fmt, (char)253, cl_hw->fw_prefix);
+	cl_fw_print_normalize(pfmt->fmt, (char)254, (cl_hw->chip->idx == CHIP0) ? '0' : '1');
+
+	if (cl_fw_offload_print(str_offload_env, pfmt->fmt, (char *)params) < 0) {
+		cl_dbg_err(cl_hw, "FW PRINT - ERROR in format! (file %u:%u)\n",
+			   pfmt->file_id, pfmt->line_num);
+		cl_hex_dump(NULL, (void *)pfmt, 48, fmtaddr, true); /* $$$ dbg dump the struct */
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int cl_fw_do_offload(struct cl_hw *cl_hw, u8 *data, int bytes_remaining)
+{
+	u8 magic2 = data[1];
+	u32 nb = data[2] + (data[3] << 8); /* Following size in bytes */
+	/* DATA IS UNALIGNED! REVISE if alignment required or BIG ENDIAN! */
+	__le32 *dp = (__le32 *)data;
+	int bytes_consumed = 4; /* 1 + 1 + 2 */
+
+	/* Data: [0] u8 magic1, u8 magic2, u16 following size in bytes */
+	if (bytes_remaining < 8) {
+		cl_dbg_err(cl_hw, "*** FW PRINT - OFFLOAD PACKET TOO SHORT: %d\n",
+			   bytes_remaining);
+		return bytes_remaining;
+	}
+
+	if (bytes_remaining < (nb + bytes_consumed)) {
+		cl_dbg_err(cl_hw, "*** FW PRINT - OFFLOAD PACKET %u > remainder %d??\n",
+			   nb, bytes_remaining);
+		return bytes_remaining;
+	}
+
+	switch (magic2) {
+	case MAGIC_PRINT_OFF_PRINT: {
+		/*
+		 * [1] u32 format descriptor ptr
+		 * [2] u32[] parameters
+		 */
+		u32 fmtp = le32_to_cpu(dp[1]);
+		u32 np = (nb - 4) / 4; /* Number of printf parameters */
+
+		if (nb < 4 || nb & 3)  {
+			cl_dbg_err(cl_hw, "*** FW PRINT - bad pkt size: %u\n", nb);
+			goto err;
+		}
+
+		cl_fw_do_dprint(cl_hw, fmtp, np, (u32 *)&dp[2]);
+
+		bytes_consumed += nb; /* Already padded to 4 bytes */
+		}
+		break;
+
+	case MAGIC_PRINT_OFF_LIT: {
+		/* [1] Remaining bytes: literal string */
+		cl_fw_do_print_n(cl_hw, (char *)&dp[1], nb);
+		bytes_consumed += ((nb + 3) / 4) * 4; /* Padding to 4 bytes */
+		}
+		break;
+
+	case MAGIC_PRINT_OFF_XDUMP:
+		/* [1] bytes[nb] */
+		if (nb >= 1)
+			cl_fw_do_hex_dump_bytes(cl_hw, 0, &dp[1], nb);
+
+		bytes_consumed += ((nb + 3) / 4) * 4; /* Padding to 4 bytes */
+		break;
+
+	default:
+		cl_dbg_err(cl_hw, "*** FW PRINT - BAD TYPE: %4.4X\n", magic2);
+		goto err;
+	}
+
+	return bytes_consumed;
+
+err:
+	return bytes_remaining; /* Skip all */
+}
+
+void cl_dbgfile_print_fw_str(struct cl_hw *cl_hw, u8 *str, int max_size)
+{
+	/* Handler for firmware debug prints */
+	int bytes_remaining = max_size;
+	int i;
+	u8 delim = 0;
+
+	while (bytes_remaining > 0) {
+		/* Scan for normal print data: */
+		for (i = 0; i < bytes_remaining; i++) {
+			if (str[i] < ' ' || str[i] >= 0x7F) {
+				if (str[i] == '\t')
+					continue;
+				delim = str[i];
+				break;
+			}
+		}
+
+		if (i > 0) {
+			if (delim == '\n') {
+				bytes_remaining -= i + 1;
+				cl_fw_do_print_n(cl_hw, str, i);
+				str += i + 1;
+				continue;
+			}
+
+			if (delim != MAGIC_PRINT_OFFLOAD) {
+				cl_fw_do_print_n(cl_hw, str, i);
+				bytes_remaining -= i;
+				return; /* Better stop parsing this */
+			}
+			/* Found offload packet but previous string not terminated: */
+			cl_fw_do_print_n(cl_hw, str, i);
+			cl_dbg_err(cl_hw, "*** FW PRINT - NO LINE END2\n");
+			bytes_remaining -= i;
+			str += i;
+			continue;
+		}
+
+		/* Delimiter at offset 0 */
+		switch (delim) {
+		case '\n':
+			cl_fw_do_print_n(cl_hw, " ", 1); /* Print empty line */
+			str++;
+			bytes_remaining--;
+			continue;
+		case 0:
+			return;
+		case MAGIC_PRINT_OFFLOAD:
+			i = cl_fw_do_offload(cl_hw, str, bytes_remaining);
+			bytes_remaining -= i;
+			str += i;
+			break;
+		default:
+			cl_dbg_err(cl_hw, "*** FW PRINT - BAD BYTE=%2.2X ! rem=%d\n",
+				   delim, bytes_remaining);
+			return; /* Better stop parsing this */
+		}
+	}
+}