mlx5_fwdump.c revision 331586
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 331586 2018-03-26 20:59:26Z 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
72int
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		return (error);
81	dd = malloc(sizeof(struct mlx5_dump_data), M_MLX5_DUMP, M_WAITOK);
82	switch (pci_get_device(mdev->pdev->dev.bsddev)) {
83	case 0x1013:
84		dd->rege = mlx5_crspace_regmap_mt4115;
85		break;
86	case 0x1015:
87		dd->rege = mlx5_crspace_regmap_mt4117;
88		break;
89	case 0x1017:
90	case 0x1019:
91		dd->rege = mlx5_crspace_regmap_connectx5;
92		break;
93	default:
94		free(dd, M_MLX5_DUMP);
95		return (0); /* silently fail to not prevent driver attach */
96	}
97	dd->dump_size = mlx5_fwdump_getsize(dd->rege);
98	dd->dump = malloc(dd->dump_size * sizeof(uint32_t), M_MLX5_DUMP,
99	    M_WAITOK | M_ZERO);
100	dd->dump_valid = 0;
101	mtx_init(&dd->dump_lock, "mlx5dmp", NULL, MTX_DEF | MTX_NEW);
102	if (atomic_cmpset_rel_ptr((uintptr_t *)&mdev->dump_data, 0,
103	    (uintptr_t)dd) == 0)
104		mlx5_fwdump_destroy_dd(dd);
105	return (0);
106}
107
108void
109mlx5_fwdump(struct mlx5_core_dev *mdev)
110{
111	struct mlx5_dump_data *dd;
112	const struct mlx5_crspace_regmap *r;
113	uint32_t i, ri;
114	int error;
115
116	dd = (struct mlx5_dump_data *)atomic_load_acq_ptr((uintptr_t *)
117	    &mdev->dump_data);
118	if (dd == NULL)
119		return;
120	mtx_lock(&dd->dump_lock);
121	if (dd->dump_valid)
122		/* only one dump */
123		goto failed;
124
125	/* mlx5_vsc already warns, be silent. */
126	error = mlx5_vsc_lock(mdev);
127	if (error != 0)
128		goto failed;
129	error = mlx5_vsc_set_space(mdev, MLX5_VSC_DOMAIN_PROTECTED_CRSPACE);
130	if (error != 0)
131		goto unlock_vsc;
132	for (i = 0, r = dd->rege; r->cnt != 0; r++) {
133		for (ri = 0; ri < r->cnt; ri++) {
134			error = mlx5_vsc_read(mdev, r->addr + ri * 4,
135			    &dd->dump[i]);
136			if (error != 0)
137				goto unlock_vsc;
138			i++;
139		}
140	}
141	atomic_store_rel_int(&dd->dump_valid, 1);
142unlock_vsc:
143	mlx5_vsc_unlock(mdev);
144failed:
145	mtx_unlock(&dd->dump_lock);
146}
147
148void
149mlx5_fwdump_clean(struct mlx5_core_dev *mdev)
150{
151	struct mlx5_dump_data *dd;
152
153	for (;;) {
154		dd = mdev->dump_data;
155		if (dd == NULL)
156			return;
157		if (atomic_cmpset_ptr((uintptr_t *)&mdev->dump_data,
158		    (uintptr_t)dd, 0) == 1) {
159			mlx5_fwdump_destroy_dd(dd);
160			return;
161		}
162	}
163}
164
165static int
166mlx5_dbsf_to_core(const struct mlx5_fwdump_addr *devaddr,
167    struct mlx5_core_dev **mdev)
168{
169	device_t dev;
170	struct pci_dev *pdev;
171
172	dev = pci_find_dbsf(devaddr->domain, devaddr->bus, devaddr->slot,
173	    devaddr->func);
174	if (dev == NULL)
175		return (ENOENT);
176	if (device_get_devclass(dev) != mlx5_core_driver.bsdclass)
177		return (EINVAL);
178	pdev = device_get_softc(dev);
179	*mdev = pci_get_drvdata(pdev);
180	if (*mdev == NULL)
181		return (ENOENT);
182	return (0);
183}
184
185static int
186mlx5_fwdump_copyout(struct mlx5_dump_data *dd, struct mlx5_fwdump_get *fwg)
187{
188	const struct mlx5_crspace_regmap *r;
189	struct mlx5_fwdump_reg rv, *urv;
190	uint32_t i, ri;
191	int error;
192
193	if (dd == NULL)
194		return (ENOENT);
195	if (fwg->buf == NULL) {
196		fwg->reg_filled = dd->dump_size;
197		return (0);
198	}
199	if (atomic_load_acq_int(&dd->dump_valid) == 0)
200		return (ENOENT);
201
202	urv = fwg->buf;
203	for (i = 0, r = dd->rege; r->cnt != 0; r++) {
204		for (ri = 0; ri < r->cnt; ri++) {
205			if (i >= fwg->reg_cnt)
206				goto out;
207			rv.addr = r->addr + ri * 4;
208			rv.val = dd->dump[i];
209			error = copyout(&rv, urv, sizeof(rv));
210			if (error != 0)
211				return (error);
212			urv++;
213			i++;
214		}
215	}
216out:
217	fwg->reg_filled = i;
218	return (0);
219}
220
221static int
222mlx5_fwdump_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag,
223    struct thread *td)
224{
225	struct mlx5_core_dev *mdev;
226	struct mlx5_fwdump_get *fwg;
227	struct mlx5_fwdump_addr *devaddr;
228	struct mlx5_dump_data *dd;
229	int error;
230
231	error = 0;
232	switch (cmd) {
233	case MLX5_FWDUMP_GET:
234		if ((fflag & FREAD) == 0) {
235			error = EBADF;
236			break;
237		}
238		fwg = (struct mlx5_fwdump_get *)data;
239		devaddr = &fwg->devaddr;
240		error = mlx5_dbsf_to_core(devaddr, &mdev);
241		if (error != 0)
242			break;
243		error = mlx5_fwdump_copyout(mdev->dump_data, fwg);
244		break;
245	case MLX5_FWDUMP_RESET:
246		if ((fflag & FWRITE) == 0) {
247			error = EBADF;
248			break;
249		}
250		devaddr = (struct mlx5_fwdump_addr *)data;
251		error = mlx5_dbsf_to_core(devaddr, &mdev);
252		if (error != 0)
253			break;
254		dd = mdev->dump_data;
255		if (dd != NULL)
256			atomic_store_rel_int(&dd->dump_valid, 0);
257		else
258			error = ENOENT;
259		break;
260	case MLX5_FWDUMP_FORCE:
261		if ((fflag & FWRITE) == 0) {
262			error = EBADF;
263			break;
264		}
265		devaddr = (struct mlx5_fwdump_addr *)data;
266		error = mlx5_dbsf_to_core(devaddr, &mdev);
267		if (error != 0)
268			break;
269		mlx5_fwdump(mdev);
270		break;
271	default:
272		error = ENOTTY;
273		break;
274	}
275	return (error);
276}
277
278static struct cdevsw mlx5_fwdump_devsw = {
279	.d_version =	D_VERSION,
280	.d_ioctl =	mlx5_fwdump_ioctl,
281};
282
283static struct cdev *mlx5_fwdump_dev;
284
285int
286mlx5_fwdump_init(void)
287{
288	struct make_dev_args mda;
289	int error;
290
291	make_dev_args_init(&mda);
292	mda.mda_flags = MAKEDEV_WAITOK | MAKEDEV_CHECKNAME;
293	mda.mda_devsw = &mlx5_fwdump_devsw;
294	mda.mda_uid = UID_ROOT;
295	mda.mda_gid = GID_OPERATOR;
296	mda.mda_mode = 0640;
297	error = make_dev_s(&mda, &mlx5_fwdump_dev, "mlx5ctl");
298	return (-error);
299}
300
301void
302mlx5_fwdump_fini(void)
303{
304
305	if (mlx5_fwdump_dev != NULL)
306		destroy_dev(mlx5_fwdump_dev);
307
308}
309