xref: /freebsd-src/sys/dev/mlx5/mlx5_core/mlx5_fwdump.c (revision 2eb4d8dc723da3cf7d735a3226ae49da4c8c5dbc)
1 /*-
2  * Copyright (c) 2018, 2019 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/port.h>
36 #include <dev/mlx5/mlx5_core/mlx5_core.h>
37 #include <dev/mlx5/mlx5io.h>
38 #include <dev/mlx5/diagnostics.h>
39 
40 static MALLOC_DEFINE(M_MLX5_DUMP, "MLX5DUMP", "MLX5 Firmware dump");
41 
42 static unsigned
43 mlx5_fwdump_getsize(const struct mlx5_crspace_regmap *rege)
44 {
45 	const struct mlx5_crspace_regmap *r;
46 	unsigned sz;
47 
48 	for (sz = 0, r = rege; r->cnt != 0; r++)
49 		sz += r->cnt;
50 	return (sz);
51 }
52 
53 static void
54 mlx5_fwdump_destroy_dd(struct mlx5_core_dev *mdev)
55 {
56 
57 	mtx_assert(&mdev->dump_lock, MA_OWNED);
58 	free(mdev->dump_data, M_MLX5_DUMP);
59 	mdev->dump_data = NULL;
60 }
61 
62 void
63 mlx5_fwdump_prep(struct mlx5_core_dev *mdev)
64 {
65 	device_t dev;
66 	int error, vsc_addr;
67 	unsigned i, sz;
68 	u32 addr, in, out, next_addr;
69 
70 	mdev->dump_data = NULL;
71 	error = mlx5_vsc_find_cap(mdev);
72 	if (error != 0) {
73 		/* Inability to create a firmware dump is not fatal. */
74 		mlx5_core_warn(mdev,
75 		    "Unable to find vendor-specific capability, error %d\n",
76 		    error);
77 		return;
78 	}
79 	error = mlx5_vsc_lock(mdev);
80 	if (error != 0)
81 		return;
82 	error = mlx5_vsc_set_space(mdev, MLX5_VSC_DOMAIN_SCAN_CRSPACE);
83 	if (error != 0) {
84 		mlx5_core_warn(mdev, "VSC scan space is not supported\n");
85 		goto unlock_vsc;
86 	}
87 	dev = mdev->pdev->dev.bsddev;
88 	vsc_addr = mdev->vsc_addr;
89 	if (vsc_addr == 0) {
90 		mlx5_core_warn(mdev, "Cannot read VSC, no address\n");
91 		goto unlock_vsc;
92 	}
93 
94 	in = 0;
95 	for (sz = 1, addr = 0;;) {
96 		MLX5_VSC_SET(vsc_addr, &in, address, addr);
97 		pci_write_config(dev, vsc_addr + MLX5_VSC_ADDR_OFFSET, in, 4);
98 		error = mlx5_vsc_wait_on_flag(mdev, 1);
99 		if (error != 0) {
100 			mlx5_core_warn(mdev,
101 		    "Failed waiting for read complete flag, error %d addr %#x\n",
102 			    error, addr);
103 			goto unlock_vsc;
104 		}
105 		pci_read_config(dev, vsc_addr + MLX5_VSC_DATA_OFFSET, 4);
106 		out = pci_read_config(dev, vsc_addr + MLX5_VSC_ADDR_OFFSET, 4);
107 		next_addr = MLX5_VSC_GET(vsc_addr, &out, address);
108 		if (next_addr == 0 || next_addr == addr)
109 			break;
110 		if (next_addr != addr + 4)
111 			sz++;
112 		addr = next_addr;
113 	}
114 	if (sz == 1) {
115 		mlx5_core_warn(mdev, "no output from scan space\n");
116 		goto unlock_vsc;
117 	}
118 
119 	/*
120 	 * We add a sentinel element at the end of the array to
121 	 * terminate the read loop in mlx5_fwdump(), so allocate sz + 1.
122 	 */
123 	mdev->dump_rege = malloc((sz + 1) * sizeof(struct mlx5_crspace_regmap),
124 	    M_MLX5_DUMP, M_WAITOK | M_ZERO);
125 
126 	for (i = 0, addr = 0;;) {
127 		mdev->dump_rege[i].cnt++;
128 		MLX5_VSC_SET(vsc_addr, &in, address, addr);
129 		pci_write_config(dev, vsc_addr + MLX5_VSC_ADDR_OFFSET, in, 4);
130 		error = mlx5_vsc_wait_on_flag(mdev, 1);
131 		if (error != 0) {
132 			mlx5_core_warn(mdev,
133 		    "Failed waiting for read complete flag, error %d addr %#x\n",
134 			    error, addr);
135 			free(mdev->dump_rege, M_MLX5_DUMP);
136 			mdev->dump_rege = NULL;
137 			goto unlock_vsc;
138 		}
139 		pci_read_config(dev, vsc_addr + MLX5_VSC_DATA_OFFSET, 4);
140 		out = pci_read_config(dev, vsc_addr + MLX5_VSC_ADDR_OFFSET, 4);
141 		next_addr = MLX5_VSC_GET(vsc_addr, &out, address);
142 		if (next_addr == 0 || next_addr == addr)
143 			break;
144 		if (next_addr != addr + 4) {
145 			if (++i == sz) {
146 				mlx5_core_err(mdev,
147 		    "Inconsistent hw crspace reads (1): sz %u i %u addr %#lx",
148 				    sz, i, (unsigned long)addr);
149 				break;
150 			}
151 			mdev->dump_rege[i].addr = next_addr;
152 		}
153 		addr = next_addr;
154 	}
155 	/* i == sz case already reported by loop above */
156 	if (i + 1 != sz && i != sz) {
157 		mlx5_core_err(mdev,
158 		    "Inconsistent hw crspace reads (2): sz %u i %u addr %#lx",
159 		    sz, i, (unsigned long)addr);
160 	}
161 
162 	mdev->dump_size = mlx5_fwdump_getsize(mdev->dump_rege);
163 	mdev->dump_data = malloc(mdev->dump_size * sizeof(uint32_t),
164 	    M_MLX5_DUMP, M_WAITOK | M_ZERO);
165 	mdev->dump_valid = false;
166 	mdev->dump_copyout = false;
167 
168 unlock_vsc:
169 	mlx5_vsc_unlock(mdev);
170 }
171 
172 int
173 mlx5_fwdump(struct mlx5_core_dev *mdev)
174 {
175 	const struct mlx5_crspace_regmap *r;
176 	uint32_t i, ri;
177 	int error;
178 
179 	mlx5_core_info(mdev, "Issuing FW dump\n");
180 	mtx_lock(&mdev->dump_lock);
181 	if (mdev->dump_data == NULL) {
182 		error = EIO;
183 		goto failed;
184 	}
185 	if (mdev->dump_valid) {
186 		/* only one dump */
187 		mlx5_core_warn(mdev,
188 		    "Only one FW dump can be captured aborting FW dump\n");
189 		error = EEXIST;
190 		goto failed;
191 	}
192 
193 	/* mlx5_vsc already warns, be silent. */
194 	error = mlx5_vsc_lock(mdev);
195 	if (error != 0)
196 		goto failed;
197 	error = mlx5_vsc_set_space(mdev, MLX5_VSC_DOMAIN_PROTECTED_CRSPACE);
198 	if (error != 0)
199 		goto unlock_vsc;
200 	for (i = 0, r = mdev->dump_rege; r->cnt != 0; r++) {
201 		for (ri = 0; ri < r->cnt; ri++) {
202 			error = mlx5_vsc_read(mdev, r->addr + ri * 4,
203 			    &mdev->dump_data[i]);
204 			if (error != 0)
205 				goto unlock_vsc;
206 			i++;
207 		}
208 	}
209 	mdev->dump_valid = true;
210 unlock_vsc:
211 	mlx5_vsc_unlock(mdev);
212 failed:
213 	mtx_unlock(&mdev->dump_lock);
214 	return (error);
215 }
216 
217 void
218 mlx5_fwdump_clean(struct mlx5_core_dev *mdev)
219 {
220 
221 	mtx_lock(&mdev->dump_lock);
222 	while (mdev->dump_copyout)
223 		msleep(&mdev->dump_copyout, &mdev->dump_lock, 0, "mlx5fwc", 0);
224 	mlx5_fwdump_destroy_dd(mdev);
225 	mtx_unlock(&mdev->dump_lock);
226 	free(mdev->dump_rege, M_MLX5_DUMP);
227 }
228 
229 static int
230 mlx5_fwdump_reset(struct mlx5_core_dev *mdev)
231 {
232 	int error;
233 
234 	error = 0;
235 	mtx_lock(&mdev->dump_lock);
236 	if (mdev->dump_data != NULL) {
237 		while (mdev->dump_copyout) {
238 			msleep(&mdev->dump_copyout, &mdev->dump_lock,
239 			    0, "mlx5fwr", 0);
240 		}
241 		mdev->dump_valid = false;
242 	} else {
243 		error = ENOENT;
244 	}
245 	mtx_unlock(&mdev->dump_lock);
246 	return (error);
247 }
248 
249 static int
250 mlx5_dbsf_to_core(const struct mlx5_tool_addr *devaddr,
251     struct mlx5_core_dev **mdev)
252 {
253 	device_t dev;
254 	struct pci_dev *pdev;
255 
256 	dev = pci_find_dbsf(devaddr->domain, devaddr->bus, devaddr->slot,
257 	    devaddr->func);
258 	if (dev == NULL)
259 		return (ENOENT);
260 	if (device_get_devclass(dev) != mlx5_core_driver.bsdclass)
261 		return (EINVAL);
262 	pdev = device_get_softc(dev);
263 	*mdev = pci_get_drvdata(pdev);
264 	if (*mdev == NULL)
265 		return (ENOENT);
266 	return (0);
267 }
268 
269 static int
270 mlx5_fwdump_copyout(struct mlx5_core_dev *mdev, struct mlx5_fwdump_get *fwg)
271 {
272 	const struct mlx5_crspace_regmap *r;
273 	struct mlx5_fwdump_reg rv, *urv;
274 	uint32_t i, ri;
275 	int error;
276 
277 	mtx_lock(&mdev->dump_lock);
278 	if (mdev->dump_data == NULL) {
279 		mtx_unlock(&mdev->dump_lock);
280 		return (ENOENT);
281 	}
282 	if (fwg->buf == NULL) {
283 		fwg->reg_filled = mdev->dump_size;
284 		mtx_unlock(&mdev->dump_lock);
285 		return (0);
286 	}
287 	if (!mdev->dump_valid) {
288 		mtx_unlock(&mdev->dump_lock);
289 		return (ENOENT);
290 	}
291 	mdev->dump_copyout = true;
292 	mtx_unlock(&mdev->dump_lock);
293 
294 	urv = fwg->buf;
295 	for (i = 0, r = mdev->dump_rege; r->cnt != 0; r++) {
296 		for (ri = 0; ri < r->cnt; ri++) {
297 			if (i >= fwg->reg_cnt)
298 				goto out;
299 			rv.addr = r->addr + ri * 4;
300 			rv.val = mdev->dump_data[i];
301 			error = copyout(&rv, urv, sizeof(rv));
302 			if (error != 0)
303 				return (error);
304 			urv++;
305 			i++;
306 		}
307 	}
308 out:
309 	fwg->reg_filled = i;
310 	mtx_lock(&mdev->dump_lock);
311 	mdev->dump_copyout = false;
312 	wakeup(&mdev->dump_copyout);
313 	mtx_unlock(&mdev->dump_lock);
314 	return (0);
315 }
316 
317 static int
318 mlx5_fw_reset(struct mlx5_core_dev *mdev)
319 {
320 	device_t dev, bus;
321 	int error;
322 
323 	error = -mlx5_set_mfrl_reg(mdev, MLX5_FRL_LEVEL3);
324 	if (error == 0) {
325 		dev = mdev->pdev->dev.bsddev;
326 		mtx_lock(&Giant);
327 		bus = device_get_parent(dev);
328 		error = BUS_RESET_CHILD(device_get_parent(bus), bus,
329 		    DEVF_RESET_DETACH);
330 		mtx_unlock(&Giant);
331 	}
332 	return (error);
333 }
334 
335 static int
336 mlx5_eeprom_copyout(struct mlx5_core_dev *dev, struct mlx5_eeprom_get *eeprom_info)
337 {
338 	struct mlx5_eeprom eeprom;
339 	int error;
340 
341 	eeprom.i2c_addr = MLX5_I2C_ADDR_LOW;
342 	eeprom.device_addr = 0;
343 	eeprom.page_num = MLX5_EEPROM_LOW_PAGE;
344 	eeprom.page_valid = 0;
345 
346 	/* Read three first bytes to get important info */
347 	error = mlx5_get_eeprom_info(dev, &eeprom);
348 	if (error != 0) {
349 		mlx5_core_err(dev,
350 		    "Failed reading EEPROM initial information\n");
351 		return (error);
352 	}
353 	eeprom_info->eeprom_info_page_valid = eeprom.page_valid;
354 	eeprom_info->eeprom_info_out_len = eeprom.len;
355 
356 	if (eeprom_info->eeprom_info_buf == NULL)
357 		return (0);
358 	/*
359 	 * Allocate needed length buffer and additional space for
360 	 * page 0x03
361 	 */
362 	eeprom.data = malloc(eeprom.len + MLX5_EEPROM_PAGE_LENGTH,
363 	    M_MLX5_EEPROM, M_WAITOK | M_ZERO);
364 
365 	/* Read the whole eeprom information */
366 	error = mlx5_get_eeprom(dev, &eeprom);
367 	if (error != 0) {
368 		mlx5_core_err(dev, "Failed reading EEPROM error = %d\n",
369 		    error);
370 		error = 0;
371 		/*
372 		 * Continue printing partial information in case of
373 		 * an error
374 		 */
375 	}
376 	error = copyout(eeprom.data, eeprom_info->eeprom_info_buf,
377 	    eeprom.len);
378 	free(eeprom.data, M_MLX5_EEPROM);
379 
380 	return (error);
381 }
382 
383 static int
384 mlx5_ctl_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag,
385     struct thread *td)
386 {
387 	struct mlx5_core_dev *mdev;
388 	struct mlx5_fwdump_get *fwg;
389 	struct mlx5_tool_addr *devaddr;
390 	struct mlx5_fw_update *fu;
391 	struct firmware fake_fw;
392 	struct mlx5_eeprom_get *eeprom_info;
393 	int error;
394 
395 	error = 0;
396 	switch (cmd) {
397 	case MLX5_FWDUMP_GET:
398 		if ((fflag & FREAD) == 0) {
399 			error = EBADF;
400 			break;
401 		}
402 		fwg = (struct mlx5_fwdump_get *)data;
403 		devaddr = &fwg->devaddr;
404 		error = mlx5_dbsf_to_core(devaddr, &mdev);
405 		if (error != 0)
406 			break;
407 		error = mlx5_fwdump_copyout(mdev, fwg);
408 		break;
409 	case MLX5_FWDUMP_RESET:
410 		if ((fflag & FWRITE) == 0) {
411 			error = EBADF;
412 			break;
413 		}
414 		devaddr = (struct mlx5_tool_addr *)data;
415 		error = mlx5_dbsf_to_core(devaddr, &mdev);
416 		if (error == 0)
417 			error = mlx5_fwdump_reset(mdev);
418 		break;
419 	case MLX5_FWDUMP_FORCE:
420 		if ((fflag & FWRITE) == 0) {
421 			error = EBADF;
422 			break;
423 		}
424 		devaddr = (struct mlx5_tool_addr *)data;
425 		error = mlx5_dbsf_to_core(devaddr, &mdev);
426 		if (error != 0)
427 			break;
428 		error = mlx5_fwdump(mdev);
429 		break;
430 	case MLX5_FW_UPDATE:
431 		if ((fflag & FWRITE) == 0) {
432 			error = EBADF;
433 			break;
434 		}
435 		fu = (struct mlx5_fw_update *)data;
436 		if (fu->img_fw_data_len > 10 * 1024 * 1024) {
437 			error = EINVAL;
438 			break;
439 		}
440 		devaddr = &fu->devaddr;
441 		error = mlx5_dbsf_to_core(devaddr, &mdev);
442 		if (error != 0)
443 			break;
444 		bzero(&fake_fw, sizeof(fake_fw));
445 		fake_fw.name = "umlx_fw_up";
446 		fake_fw.datasize = fu->img_fw_data_len;
447 		fake_fw.version = 1;
448 		fake_fw.data = (void *)kmem_malloc(fu->img_fw_data_len,
449 		    M_WAITOK);
450 		if (fake_fw.data == NULL) {
451 			error = ENOMEM;
452 			break;
453 		}
454 		error = copyin(fu->img_fw_data, __DECONST(void *, fake_fw.data),
455 		    fu->img_fw_data_len);
456 		if (error == 0)
457 			error = -mlx5_firmware_flash(mdev, &fake_fw);
458 		kmem_free((vm_offset_t)fake_fw.data, fu->img_fw_data_len);
459 		break;
460 	case MLX5_FW_RESET:
461 		if ((fflag & FWRITE) == 0) {
462 			error = EBADF;
463 			break;
464 		}
465 		devaddr = (struct mlx5_tool_addr *)data;
466 		error = mlx5_dbsf_to_core(devaddr, &mdev);
467 		if (error != 0)
468 			break;
469 		error = mlx5_fw_reset(mdev);
470 		break;
471 	case MLX5_EEPROM_GET:
472 		if ((fflag & FREAD) == 0) {
473 			error = EBADF;
474 			break;
475 		}
476 		eeprom_info = (struct mlx5_eeprom_get *)data;
477 		devaddr = &eeprom_info->devaddr;
478 		error = mlx5_dbsf_to_core(devaddr, &mdev);
479 		if (error != 0)
480 			break;
481 		error = mlx5_eeprom_copyout(mdev, eeprom_info);
482 		break;
483 	default:
484 		error = ENOTTY;
485 		break;
486 	}
487 	return (error);
488 }
489 
490 static struct cdevsw mlx5_ctl_devsw = {
491 	.d_version =	D_VERSION,
492 	.d_ioctl =	mlx5_ctl_ioctl,
493 };
494 
495 static struct cdev *mlx5_ctl_dev;
496 
497 int
498 mlx5_ctl_init(void)
499 {
500 	struct make_dev_args mda;
501 	int error;
502 
503 	make_dev_args_init(&mda);
504 	mda.mda_flags = MAKEDEV_WAITOK | MAKEDEV_CHECKNAME;
505 	mda.mda_devsw = &mlx5_ctl_devsw;
506 	mda.mda_uid = UID_ROOT;
507 	mda.mda_gid = GID_OPERATOR;
508 	mda.mda_mode = 0640;
509 	error = make_dev_s(&mda, &mlx5_ctl_dev, "mlx5ctl");
510 	return (-error);
511 }
512 
513 void
514 mlx5_ctl_fini(void)
515 {
516 
517 	if (mlx5_ctl_dev != NULL)
518 		destroy_dev(mlx5_ctl_dev);
519 
520 }
521