1// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
2/* QLogic qed NIC Driver
3 * Copyright (c) 2015-2016  QLogic Corporation
4 * Copyright (c) 2019-2020 Marvell International Ltd.
5 */
6
7#include <linux/crc32.h>
8#include "qed.h"
9#include "qed_dev_api.h"
10#include "qed_mcp.h"
11#include "qed_sp.h"
12#include "qed_selftest.h"
13
14int qed_selftest_memory(struct qed_dev *cdev)
15{
16	int rc = 0, i;
17
18	for_each_hwfn(cdev, i) {
19		rc = qed_sp_heartbeat_ramrod(&cdev->hwfns[i]);
20		if (rc)
21			return rc;
22	}
23
24	return rc;
25}
26
27int qed_selftest_interrupt(struct qed_dev *cdev)
28{
29	int rc = 0, i;
30
31	for_each_hwfn(cdev, i) {
32		rc = qed_sp_heartbeat_ramrod(&cdev->hwfns[i]);
33		if (rc)
34			return rc;
35	}
36
37	return rc;
38}
39
40int qed_selftest_register(struct qed_dev *cdev)
41{
42	struct qed_hwfn *p_hwfn;
43	struct qed_ptt *p_ptt;
44	int rc = 0, i;
45
46	/* although performed by MCP, this test is per engine */
47	for_each_hwfn(cdev, i) {
48		p_hwfn = &cdev->hwfns[i];
49		p_ptt = qed_ptt_acquire(p_hwfn);
50		if (!p_ptt) {
51			DP_ERR(p_hwfn, "failed to acquire ptt\n");
52			return -EBUSY;
53		}
54		rc = qed_mcp_bist_register_test(p_hwfn, p_ptt);
55		qed_ptt_release(p_hwfn, p_ptt);
56		if (rc)
57			break;
58	}
59
60	return rc;
61}
62
63int qed_selftest_clock(struct qed_dev *cdev)
64{
65	struct qed_hwfn *p_hwfn;
66	struct qed_ptt *p_ptt;
67	int rc = 0, i;
68
69	/* although performed by MCP, this test is per engine */
70	for_each_hwfn(cdev, i) {
71		p_hwfn = &cdev->hwfns[i];
72		p_ptt = qed_ptt_acquire(p_hwfn);
73		if (!p_ptt) {
74			DP_ERR(p_hwfn, "failed to acquire ptt\n");
75			return -EBUSY;
76		}
77		rc = qed_mcp_bist_clock_test(p_hwfn, p_ptt);
78		qed_ptt_release(p_hwfn, p_ptt);
79		if (rc)
80			break;
81	}
82
83	return rc;
84}
85
86int qed_selftest_nvram(struct qed_dev *cdev)
87{
88	struct qed_hwfn *p_hwfn = QED_LEADING_HWFN(cdev);
89	struct qed_ptt *p_ptt = qed_ptt_acquire(p_hwfn);
90	u32 num_images, i, j, nvm_crc, calc_crc;
91	struct bist_nvm_image_att image_att;
92	u8 *buf = NULL;
93	__be32 val;
94	int rc;
95
96	if (!p_ptt) {
97		DP_ERR(p_hwfn, "failed to acquire ptt\n");
98		return -EBUSY;
99	}
100
101	/* Acquire from MFW the amount of available images */
102	rc = qed_mcp_bist_nvm_get_num_images(p_hwfn, p_ptt, &num_images);
103	if (rc || !num_images) {
104		DP_ERR(p_hwfn, "Failed getting number of images\n");
105		rc = -EINVAL;
106		goto err0;
107	}
108
109	/* Iterate over images and validate CRC */
110	for (i = 0; i < num_images; i++) {
111		/* This mailbox returns information about the image required for
112		 * reading it.
113		 */
114		rc = qed_mcp_bist_nvm_get_image_att(p_hwfn, p_ptt,
115						    &image_att, i);
116		if (rc) {
117			DP_ERR(p_hwfn,
118			       "Failed getting image index %d attributes\n",
119			       i);
120			goto err0;
121		}
122
123		/* After MFW crash dump is collected - the image's CRC stops
124		 * being valid.
125		 */
126		if (image_att.image_type == NVM_TYPE_MDUMP)
127			continue;
128
129		DP_VERBOSE(p_hwfn, QED_MSG_SP, "image index %d, size %x\n",
130			   i, image_att.len);
131
132		/* Allocate a buffer for holding the nvram image */
133		buf = kzalloc(image_att.len, GFP_KERNEL);
134		if (!buf) {
135			rc = -ENOMEM;
136			goto err0;
137		}
138
139		/* Read image into buffer */
140		rc = qed_mcp_nvm_read(p_hwfn->cdev, image_att.nvm_start_addr,
141				      buf, image_att.len);
142		if (rc) {
143			DP_ERR(p_hwfn,
144			       "Failed reading image index %d from nvm.\n", i);
145			goto err1;
146		}
147
148		/* Convert the buffer into big-endian format (excluding the
149		 * closing 4 bytes of CRC).
150		 */
151		for (j = 0; j < image_att.len - 4; j += 4) {
152			val = cpu_to_be32(*(u32 *)&buf[j]);
153			*(u32 *)&buf[j] = (__force u32)val;
154		}
155
156		/* Calc CRC for the "actual" image buffer, i.e. not including
157		 * the last 4 CRC bytes.
158		 */
159		nvm_crc = *(u32 *)(buf + image_att.len - 4);
160		calc_crc = crc32(0xffffffff, buf, image_att.len - 4);
161		calc_crc = (__force u32)~cpu_to_be32(calc_crc);
162		DP_VERBOSE(p_hwfn, QED_MSG_SP,
163			   "nvm crc 0x%x, calc_crc 0x%x\n", nvm_crc, calc_crc);
164
165		if (calc_crc != nvm_crc) {
166			rc = -EINVAL;
167			goto err1;
168		}
169
170		/* Done with this image; Free to prevent double release
171		 * on subsequent failure.
172		 */
173		kfree(buf);
174		buf = NULL;
175	}
176
177	qed_ptt_release(p_hwfn, p_ptt);
178	return 0;
179
180err1:
181	kfree(buf);
182err0:
183	qed_ptt_release(p_hwfn, p_ptt);
184	return rc;
185}
186