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