xref: /freebsd/sys/dev/mlx5/mlx5_core/mlx5_fwdump.c (revision b00ab754)
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$");
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 
38 extern const struct mlx5_crspace_regmap mlx5_crspace_regmap_mt4117[];
39 extern const struct mlx5_crspace_regmap mlx5_crspace_regmap_mt4115[];
40 extern const struct mlx5_crspace_regmap mlx5_crspace_regmap_connectx5[];
41 
42 struct 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 
50 static MALLOC_DEFINE(M_MLX5_DUMP, "MLX5DUMP", "MLX5 Firmware dump");
51 
52 static unsigned
53 mlx5_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 
63 static void
64 mlx5_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 
72 void
73 mlx5_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 
111 void
112 mlx5_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);
149 unlock_vsc:
150 	mlx5_vsc_unlock(mdev);
151 failed:
152 	mtx_unlock(&dd->dump_lock);
153 }
154 
155 void
156 mlx5_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 
172 static int
173 mlx5_dbsf_to_core(const struct mlx5_fwdump_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 
192 static int
193 mlx5_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 	}
223 out:
224 	fwg->reg_filled = i;
225 	return (0);
226 }
227 
228 static int
229 mlx5_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_fwdump_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_fwdump_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_fwdump_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 
285 static struct cdevsw mlx5_fwdump_devsw = {
286 	.d_version =	D_VERSION,
287 	.d_ioctl =	mlx5_fwdump_ioctl,
288 };
289 
290 static struct cdev *mlx5_fwdump_dev;
291 
292 int
293 mlx5_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 
308 void
309 mlx5_fwdump_fini(void)
310 {
311 
312 	if (mlx5_fwdump_dev != NULL)
313 		destroy_dev(mlx5_fwdump_dev);
314 
315 }
316