mlx5_fwdump.c revision 347840
1/*-
2 * Copyright (c) 2018, Mellanox Technologies, Ltd.  All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS `AS IS' AND
14 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16 * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
17 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23 * SUCH DAMAGE.
24 */
25
26#include <sys/cdefs.h>
27__FBSDID("$FreeBSD: stable/11/sys/dev/mlx5/mlx5_core/mlx5_fwdump.c 347840 2019-05-16 17:50:15Z hselasky $");
28
29#include <sys/param.h>
30#include <sys/systm.h>
31#include <sys/conf.h>
32#include <sys/fcntl.h>
33#include <dev/mlx5/driver.h>
34#include <dev/mlx5/device.h>
35#include <dev/mlx5/mlx5_core/mlx5_core.h>
36#include <dev/mlx5/mlx5io.h>
37
38extern const struct mlx5_crspace_regmap mlx5_crspace_regmap_mt4117[];
39extern const struct mlx5_crspace_regmap mlx5_crspace_regmap_mt4115[];
40extern const struct mlx5_crspace_regmap mlx5_crspace_regmap_connectx5[];
41
42struct mlx5_dump_data {
43	const struct mlx5_crspace_regmap *rege;
44	uint32_t *dump;
45	unsigned dump_size;
46	int dump_valid;
47	struct mtx dump_lock;
48};
49
50static MALLOC_DEFINE(M_MLX5_DUMP, "MLX5DUMP", "MLX5 Firmware dump");
51
52static unsigned
53mlx5_fwdump_getsize(const struct mlx5_crspace_regmap *rege)
54{
55	const struct mlx5_crspace_regmap *r;
56	unsigned sz;
57
58	for (sz = 0, r = rege; r->cnt != 0; r++)
59		sz += r->cnt;
60	return (sz);
61}
62
63static void
64mlx5_fwdump_destroy_dd(struct mlx5_dump_data *dd)
65{
66
67	mtx_destroy(&dd->dump_lock);
68	free(dd->dump, M_MLX5_DUMP);
69	free(dd, M_MLX5_DUMP);
70}
71
72void
73mlx5_fwdump_prep(struct mlx5_core_dev *mdev)
74{
75	struct mlx5_dump_data *dd;
76	int error;
77
78	error = mlx5_vsc_find_cap(mdev);
79	if (error != 0) {
80		/* Inability to create a firmware dump is not fatal. */
81		device_printf((&mdev->pdev->dev)->bsddev, "WARN: "
82		    "mlx5_fwdump_prep failed %d\n", error);
83		return;
84	}
85	dd = malloc(sizeof(struct mlx5_dump_data), M_MLX5_DUMP, M_WAITOK);
86	switch (pci_get_device(mdev->pdev->dev.bsddev)) {
87	case 0x1013:
88		dd->rege = mlx5_crspace_regmap_mt4115;
89		break;
90	case 0x1015:
91		dd->rege = mlx5_crspace_regmap_mt4117;
92		break;
93	case 0x1017:
94	case 0x1019:
95		dd->rege = mlx5_crspace_regmap_connectx5;
96		break;
97	default:
98		free(dd, M_MLX5_DUMP);
99		return; /* silently fail, do not prevent driver attach */
100	}
101	dd->dump_size = mlx5_fwdump_getsize(dd->rege);
102	dd->dump = malloc(dd->dump_size * sizeof(uint32_t), M_MLX5_DUMP,
103	    M_WAITOK | M_ZERO);
104	dd->dump_valid = 0;
105	mtx_init(&dd->dump_lock, "mlx5dmp", NULL, MTX_DEF | MTX_NEW);
106	if (atomic_cmpset_rel_ptr((uintptr_t *)&mdev->dump_data, 0,
107	    (uintptr_t)dd) == 0)
108		mlx5_fwdump_destroy_dd(dd);
109}
110
111void
112mlx5_fwdump(struct mlx5_core_dev *mdev)
113{
114	struct mlx5_dump_data *dd;
115	const struct mlx5_crspace_regmap *r;
116	uint32_t i, ri;
117	int error;
118
119	dev_info(&mdev->pdev->dev, "Issuing FW dump\n");
120	dd = (struct mlx5_dump_data *)atomic_load_acq_ptr((uintptr_t *)
121	    &mdev->dump_data);
122	if (dd == NULL)
123		return;
124	mtx_lock(&dd->dump_lock);
125	if (dd->dump_valid) {
126		/* only one dump */
127		dev_warn(&mdev->pdev->dev,
128		    "Only one FW dump can be captured aborting FW dump\n");
129		goto failed;
130	}
131
132	/* mlx5_vsc already warns, be silent. */
133	error = mlx5_vsc_lock(mdev);
134	if (error != 0)
135		goto failed;
136	error = mlx5_vsc_set_space(mdev, MLX5_VSC_DOMAIN_PROTECTED_CRSPACE);
137	if (error != 0)
138		goto unlock_vsc;
139	for (i = 0, r = dd->rege; r->cnt != 0; r++) {
140		for (ri = 0; ri < r->cnt; ri++) {
141			error = mlx5_vsc_read(mdev, r->addr + ri * 4,
142			    &dd->dump[i]);
143			if (error != 0)
144				goto unlock_vsc;
145			i++;
146		}
147	}
148	atomic_store_rel_int(&dd->dump_valid, 1);
149unlock_vsc:
150	mlx5_vsc_unlock(mdev);
151failed:
152	mtx_unlock(&dd->dump_lock);
153}
154
155void
156mlx5_fwdump_clean(struct mlx5_core_dev *mdev)
157{
158	struct mlx5_dump_data *dd;
159
160	for (;;) {
161		dd = mdev->dump_data;
162		if (dd == NULL)
163			return;
164		if (atomic_cmpset_ptr((uintptr_t *)&mdev->dump_data,
165		    (uintptr_t)dd, 0) == 1) {
166			mlx5_fwdump_destroy_dd(dd);
167			return;
168		}
169	}
170}
171
172static int
173mlx5_dbsf_to_core(const struct mlx5_tool_addr *devaddr,
174    struct mlx5_core_dev **mdev)
175{
176	device_t dev;
177	struct pci_dev *pdev;
178
179	dev = pci_find_dbsf(devaddr->domain, devaddr->bus, devaddr->slot,
180	    devaddr->func);
181	if (dev == NULL)
182		return (ENOENT);
183	if (device_get_devclass(dev) != mlx5_core_driver.bsdclass)
184		return (EINVAL);
185	pdev = device_get_softc(dev);
186	*mdev = pci_get_drvdata(pdev);
187	if (*mdev == NULL)
188		return (ENOENT);
189	return (0);
190}
191
192static int
193mlx5_fwdump_copyout(struct mlx5_dump_data *dd, struct mlx5_fwdump_get *fwg)
194{
195	const struct mlx5_crspace_regmap *r;
196	struct mlx5_fwdump_reg rv, *urv;
197	uint32_t i, ri;
198	int error;
199
200	if (dd == NULL)
201		return (ENOENT);
202	if (fwg->buf == NULL) {
203		fwg->reg_filled = dd->dump_size;
204		return (0);
205	}
206	if (atomic_load_acq_int(&dd->dump_valid) == 0)
207		return (ENOENT);
208
209	urv = fwg->buf;
210	for (i = 0, r = dd->rege; r->cnt != 0; r++) {
211		for (ri = 0; ri < r->cnt; ri++) {
212			if (i >= fwg->reg_cnt)
213				goto out;
214			rv.addr = r->addr + ri * 4;
215			rv.val = dd->dump[i];
216			error = copyout(&rv, urv, sizeof(rv));
217			if (error != 0)
218				return (error);
219			urv++;
220			i++;
221		}
222	}
223out:
224	fwg->reg_filled = i;
225	return (0);
226}
227
228static int
229mlx5_fwdump_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag,
230    struct thread *td)
231{
232	struct mlx5_core_dev *mdev;
233	struct mlx5_fwdump_get *fwg;
234	struct mlx5_tool_addr *devaddr;
235	struct mlx5_dump_data *dd;
236	int error;
237
238	error = 0;
239	switch (cmd) {
240	case MLX5_FWDUMP_GET:
241		if ((fflag & FREAD) == 0) {
242			error = EBADF;
243			break;
244		}
245		fwg = (struct mlx5_fwdump_get *)data;
246		devaddr = &fwg->devaddr;
247		error = mlx5_dbsf_to_core(devaddr, &mdev);
248		if (error != 0)
249			break;
250		error = mlx5_fwdump_copyout(mdev->dump_data, fwg);
251		break;
252	case MLX5_FWDUMP_RESET:
253		if ((fflag & FWRITE) == 0) {
254			error = EBADF;
255			break;
256		}
257		devaddr = (struct mlx5_tool_addr *)data;
258		error = mlx5_dbsf_to_core(devaddr, &mdev);
259		if (error != 0)
260			break;
261		dd = mdev->dump_data;
262		if (dd != NULL)
263			atomic_store_rel_int(&dd->dump_valid, 0);
264		else
265			error = ENOENT;
266		break;
267	case MLX5_FWDUMP_FORCE:
268		if ((fflag & FWRITE) == 0) {
269			error = EBADF;
270			break;
271		}
272		devaddr = (struct mlx5_tool_addr *)data;
273		error = mlx5_dbsf_to_core(devaddr, &mdev);
274		if (error != 0)
275			break;
276		mlx5_fwdump(mdev);
277		break;
278	default:
279		error = ENOTTY;
280		break;
281	}
282	return (error);
283}
284
285static struct cdevsw mlx5_fwdump_devsw = {
286	.d_version =	D_VERSION,
287	.d_ioctl =	mlx5_fwdump_ioctl,
288};
289
290static struct cdev *mlx5_fwdump_dev;
291
292int
293mlx5_fwdump_init(void)
294{
295	struct make_dev_args mda;
296	int error;
297
298	make_dev_args_init(&mda);
299	mda.mda_flags = MAKEDEV_WAITOK | MAKEDEV_CHECKNAME;
300	mda.mda_devsw = &mlx5_fwdump_devsw;
301	mda.mda_uid = UID_ROOT;
302	mda.mda_gid = GID_OPERATOR;
303	mda.mda_mode = 0640;
304	error = make_dev_s(&mda, &mlx5_fwdump_dev, "mlx5ctl");
305	return (-error);
306}
307
308void
309mlx5_fwdump_fini(void)
310{
311
312	if (mlx5_fwdump_dev != NULL)
313		destroy_dev(mlx5_fwdump_dev);
314
315}
316