1330653Shselasky/*-
2353216Shselasky * Copyright (c) 2018, 2019 Mellanox Technologies, Ltd.  All rights reserved.
3330653Shselasky *
4330653Shselasky * Redistribution and use in source and binary forms, with or without
5330653Shselasky * modification, are permitted provided that the following conditions
6330653Shselasky * are met:
7330653Shselasky * 1. Redistributions of source code must retain the above copyright
8330653Shselasky *    notice, this list of conditions and the following disclaimer.
9330653Shselasky * 2. Redistributions in binary form must reproduce the above copyright
10330653Shselasky *    notice, this list of conditions and the following disclaimer in the
11330653Shselasky *    documentation and/or other materials provided with the distribution.
12330653Shselasky *
13330653Shselasky * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS `AS IS' AND
14330653Shselasky * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15330653Shselasky * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16330653Shselasky * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
17330653Shselasky * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18330653Shselasky * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19330653Shselasky * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20330653Shselasky * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21330653Shselasky * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22330653Shselasky * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23330653Shselasky * SUCH DAMAGE.
24330653Shselasky */
25330653Shselasky
26330653Shselasky#include <sys/cdefs.h>
27330653Shselasky__FBSDID("$FreeBSD: stable/11/sys/dev/mlx5/mlx5_core/mlx5_fwdump.c 369096 2021-01-22 12:49:51Z hselasky $");
28330653Shselasky
29330653Shselasky#include <sys/param.h>
30330653Shselasky#include <sys/systm.h>
31330653Shselasky#include <sys/conf.h>
32330653Shselasky#include <sys/fcntl.h>
33330653Shselasky#include <dev/mlx5/driver.h>
34330653Shselasky#include <dev/mlx5/device.h>
35353240Shselasky#include <dev/mlx5/port.h>
36330653Shselasky#include <dev/mlx5/mlx5_core/mlx5_core.h>
37330653Shselasky#include <dev/mlx5/mlx5io.h>
38353240Shselasky#include <dev/mlx5/diagnostics.h>
39330653Shselasky
40330653Shselaskystatic MALLOC_DEFINE(M_MLX5_DUMP, "MLX5DUMP", "MLX5 Firmware dump");
41330653Shselasky
42330653Shselaskystatic unsigned
43330653Shselaskymlx5_fwdump_getsize(const struct mlx5_crspace_regmap *rege)
44330653Shselasky{
45330653Shselasky	const struct mlx5_crspace_regmap *r;
46330653Shselasky	unsigned sz;
47330653Shselasky
48330653Shselasky	for (sz = 0, r = rege; r->cnt != 0; r++)
49330653Shselasky		sz += r->cnt;
50330653Shselasky	return (sz);
51330653Shselasky}
52330653Shselasky
53330653Shselaskystatic void
54347880Shselaskymlx5_fwdump_destroy_dd(struct mlx5_core_dev *mdev)
55330653Shselasky{
56330653Shselasky
57347880Shselasky	mtx_assert(&mdev->dump_lock, MA_OWNED);
58347880Shselasky	free(mdev->dump_data, M_MLX5_DUMP);
59347880Shselasky	mdev->dump_data = NULL;
60330653Shselasky}
61330653Shselasky
62331591Shselaskyvoid
63330653Shselaskymlx5_fwdump_prep(struct mlx5_core_dev *mdev)
64330653Shselasky{
65353216Shselasky	device_t dev;
66353216Shselasky	int error, vsc_addr;
67353216Shselasky	unsigned i, sz;
68353216Shselasky	u32 addr, in, out, next_addr;
69330653Shselasky
70347880Shselasky	mdev->dump_data = NULL;
71330653Shselasky	error = mlx5_vsc_find_cap(mdev);
72331591Shselasky	if (error != 0) {
73331591Shselasky		/* Inability to create a firmware dump is not fatal. */
74353224Shselasky		mlx5_core_warn(mdev,
75369091Shselasky		    "Unable to find vendor-specific capability, error %d\n",
76353260Shselasky		    error);
77331591Shselasky		return;
78331591Shselasky	}
79353216Shselasky	error = mlx5_vsc_lock(mdev);
80353216Shselasky	if (error != 0)
81353216Shselasky		return;
82353216Shselasky	error = mlx5_vsc_set_space(mdev, MLX5_VSC_DOMAIN_SCAN_CRSPACE);
83353216Shselasky	if (error != 0) {
84353216Shselasky		mlx5_core_warn(mdev, "VSC scan space is not supported\n");
85353216Shselasky		goto unlock_vsc;
86330653Shselasky	}
87353216Shselasky	dev = mdev->pdev->dev.bsddev;
88353216Shselasky	vsc_addr = mdev->vsc_addr;
89353216Shselasky	if (vsc_addr == 0) {
90353260Shselasky		mlx5_core_warn(mdev, "Cannot read VSC, no address\n");
91353216Shselasky		goto unlock_vsc;
92353216Shselasky	}
93353216Shselasky
94353216Shselasky	in = 0;
95353216Shselasky	for (sz = 1, addr = 0;;) {
96353216Shselasky		MLX5_VSC_SET(vsc_addr, &in, address, addr);
97353216Shselasky		pci_write_config(dev, vsc_addr + MLX5_VSC_ADDR_OFFSET, in, 4);
98353216Shselasky		error = mlx5_vsc_wait_on_flag(mdev, 1);
99353216Shselasky		if (error != 0) {
100353216Shselasky			mlx5_core_warn(mdev,
101353260Shselasky		    "Failed waiting for read complete flag, error %d addr %#x\n",
102353260Shselasky			    error, addr);
103353216Shselasky			goto unlock_vsc;
104353216Shselasky		}
105353216Shselasky		pci_read_config(dev, vsc_addr + MLX5_VSC_DATA_OFFSET, 4);
106353216Shselasky		out = pci_read_config(dev, vsc_addr + MLX5_VSC_ADDR_OFFSET, 4);
107353216Shselasky		next_addr = MLX5_VSC_GET(vsc_addr, &out, address);
108353216Shselasky		if (next_addr == 0 || next_addr == addr)
109353216Shselasky			break;
110353216Shselasky		if (next_addr != addr + 4)
111353216Shselasky			sz++;
112353216Shselasky		addr = next_addr;
113353216Shselasky	}
114355545Skib	if (sz == 1) {
115355545Skib		mlx5_core_warn(mdev, "no output from scan space\n");
116355545Skib		goto unlock_vsc;
117355545Skib	}
118369096Shselasky
119369096Shselasky	/*
120369096Shselasky	 * We add a sentinel element at the end of the array to
121369096Shselasky	 * terminate the read loop in mlx5_fwdump(), so allocate sz + 1.
122369096Shselasky	 */
123369096Shselasky	mdev->dump_rege = malloc((sz + 1) * sizeof(struct mlx5_crspace_regmap),
124353216Shselasky	    M_MLX5_DUMP, M_WAITOK | M_ZERO);
125353216Shselasky
126353216Shselasky	for (i = 0, addr = 0;;) {
127353216Shselasky		mdev->dump_rege[i].cnt++;
128353216Shselasky		MLX5_VSC_SET(vsc_addr, &in, address, addr);
129353216Shselasky		pci_write_config(dev, vsc_addr + MLX5_VSC_ADDR_OFFSET, in, 4);
130353216Shselasky		error = mlx5_vsc_wait_on_flag(mdev, 1);
131353216Shselasky		if (error != 0) {
132353216Shselasky			mlx5_core_warn(mdev,
133353260Shselasky		    "Failed waiting for read complete flag, error %d addr %#x\n",
134353260Shselasky			    error, addr);
135353216Shselasky			free(mdev->dump_rege, M_MLX5_DUMP);
136353216Shselasky			mdev->dump_rege = NULL;
137353216Shselasky			goto unlock_vsc;
138353216Shselasky		}
139353216Shselasky		pci_read_config(dev, vsc_addr + MLX5_VSC_DATA_OFFSET, 4);
140353216Shselasky		out = pci_read_config(dev, vsc_addr + MLX5_VSC_ADDR_OFFSET, 4);
141353216Shselasky		next_addr = MLX5_VSC_GET(vsc_addr, &out, address);
142353216Shselasky		if (next_addr == 0 || next_addr == addr)
143353216Shselasky			break;
144369096Shselasky		if (next_addr != addr + 4) {
145369096Shselasky			if (++i == sz) {
146369096Shselasky				mlx5_core_err(mdev,
147369096Shselasky		    "Inconsistent hw crspace reads (1): sz %u i %u addr %#lx",
148369096Shselasky				    sz, i, (unsigned long)addr);
149369096Shselasky				break;
150369096Shselasky			}
151369096Shselasky			mdev->dump_rege[i].addr = next_addr;
152369096Shselasky		}
153353216Shselasky		addr = next_addr;
154353216Shselasky	}
155369096Shselasky	/* i == sz case already reported by loop above */
156369096Shselasky	if (i + 1 != sz && i != sz) {
157355544Skib		mlx5_core_err(mdev,
158369096Shselasky		    "Inconsistent hw crspace reads (2): sz %u i %u addr %#lx",
159355544Skib		    sz, i, (unsigned long)addr);
160355544Skib	}
161353216Shselasky
162347880Shselasky	mdev->dump_size = mlx5_fwdump_getsize(mdev->dump_rege);
163347880Shselasky	mdev->dump_data = malloc(mdev->dump_size * sizeof(uint32_t),
164347880Shselasky	    M_MLX5_DUMP, M_WAITOK | M_ZERO);
165347880Shselasky	mdev->dump_valid = false;
166347880Shselasky	mdev->dump_copyout = false;
167353216Shselasky
168353216Shselaskyunlock_vsc:
169353216Shselasky	mlx5_vsc_unlock(mdev);
170330653Shselasky}
171330653Shselasky
172353246Shselaskyint
173330653Shselaskymlx5_fwdump(struct mlx5_core_dev *mdev)
174330653Shselasky{
175330653Shselasky	const struct mlx5_crspace_regmap *r;
176330653Shselasky	uint32_t i, ri;
177330653Shselasky	int error;
178330653Shselasky
179353224Shselasky	mlx5_core_info(mdev, "Issuing FW dump\n");
180347880Shselasky	mtx_lock(&mdev->dump_lock);
181353246Shselasky	if (mdev->dump_data == NULL) {
182353246Shselasky		error = EIO;
183347880Shselasky		goto failed;
184353246Shselasky	}
185347880Shselasky	if (mdev->dump_valid) {
186330653Shselasky		/* only one dump */
187353224Shselasky		mlx5_core_warn(mdev,
188331914Shselasky		    "Only one FW dump can be captured aborting FW dump\n");
189353246Shselasky		error = EEXIST;
190330653Shselasky		goto failed;
191331914Shselasky	}
192330653Shselasky
193330653Shselasky	/* mlx5_vsc already warns, be silent. */
194330653Shselasky	error = mlx5_vsc_lock(mdev);
195330653Shselasky	if (error != 0)
196330653Shselasky		goto failed;
197330653Shselasky	error = mlx5_vsc_set_space(mdev, MLX5_VSC_DOMAIN_PROTECTED_CRSPACE);
198330653Shselasky	if (error != 0)
199330653Shselasky		goto unlock_vsc;
200347880Shselasky	for (i = 0, r = mdev->dump_rege; r->cnt != 0; r++) {
201330653Shselasky		for (ri = 0; ri < r->cnt; ri++) {
202330653Shselasky			error = mlx5_vsc_read(mdev, r->addr + ri * 4,
203347880Shselasky			    &mdev->dump_data[i]);
204330653Shselasky			if (error != 0)
205330653Shselasky				goto unlock_vsc;
206330653Shselasky			i++;
207330653Shselasky		}
208330653Shselasky	}
209347880Shselasky	mdev->dump_valid = true;
210330653Shselaskyunlock_vsc:
211330653Shselasky	mlx5_vsc_unlock(mdev);
212330653Shselaskyfailed:
213347880Shselasky	mtx_unlock(&mdev->dump_lock);
214353246Shselasky	return (error);
215330653Shselasky}
216330653Shselasky
217330653Shselaskyvoid
218330653Shselaskymlx5_fwdump_clean(struct mlx5_core_dev *mdev)
219330653Shselasky{
220330653Shselasky
221347880Shselasky	mtx_lock(&mdev->dump_lock);
222347880Shselasky	while (mdev->dump_copyout)
223347880Shselasky		msleep(&mdev->dump_copyout, &mdev->dump_lock, 0, "mlx5fwc", 0);
224347880Shselasky	mlx5_fwdump_destroy_dd(mdev);
225347880Shselasky	mtx_unlock(&mdev->dump_lock);
226353216Shselasky	free(mdev->dump_rege, M_MLX5_DUMP);
227347880Shselasky}
228347880Shselasky
229347880Shselaskystatic int
230347880Shselaskymlx5_fwdump_reset(struct mlx5_core_dev *mdev)
231347880Shselasky{
232347880Shselasky	int error;
233347880Shselasky
234347880Shselasky	error = 0;
235347880Shselasky	mtx_lock(&mdev->dump_lock);
236347880Shselasky	if (mdev->dump_data != NULL) {
237347880Shselasky		while (mdev->dump_copyout) {
238347880Shselasky			msleep(&mdev->dump_copyout, &mdev->dump_lock,
239347880Shselasky			    0, "mlx5fwr", 0);
240330653Shselasky		}
241347880Shselasky		mdev->dump_valid = false;
242347880Shselasky	} else {
243347880Shselasky		error = ENOENT;
244330653Shselasky	}
245347880Shselasky	mtx_unlock(&mdev->dump_lock);
246347880Shselasky	return (error);
247330653Shselasky}
248330653Shselasky
249330653Shselaskystatic int
250347840Shselaskymlx5_dbsf_to_core(const struct mlx5_tool_addr *devaddr,
251330653Shselasky    struct mlx5_core_dev **mdev)
252330653Shselasky{
253330653Shselasky	device_t dev;
254330653Shselasky	struct pci_dev *pdev;
255330653Shselasky
256330653Shselasky	dev = pci_find_dbsf(devaddr->domain, devaddr->bus, devaddr->slot,
257330653Shselasky	    devaddr->func);
258330653Shselasky	if (dev == NULL)
259330653Shselasky		return (ENOENT);
260330653Shselasky	if (device_get_devclass(dev) != mlx5_core_driver.bsdclass)
261330653Shselasky		return (EINVAL);
262330653Shselasky	pdev = device_get_softc(dev);
263330653Shselasky	*mdev = pci_get_drvdata(pdev);
264330653Shselasky	if (*mdev == NULL)
265330653Shselasky		return (ENOENT);
266330653Shselasky	return (0);
267330653Shselasky}
268330653Shselasky
269330653Shselaskystatic int
270347880Shselaskymlx5_fwdump_copyout(struct mlx5_core_dev *mdev, struct mlx5_fwdump_get *fwg)
271330653Shselasky{
272330653Shselasky	const struct mlx5_crspace_regmap *r;
273330653Shselasky	struct mlx5_fwdump_reg rv, *urv;
274330653Shselasky	uint32_t i, ri;
275330653Shselasky	int error;
276330653Shselasky
277347880Shselasky	mtx_lock(&mdev->dump_lock);
278347880Shselasky	if (mdev->dump_data == NULL) {
279347880Shselasky		mtx_unlock(&mdev->dump_lock);
280330653Shselasky		return (ENOENT);
281347880Shselasky	}
282330653Shselasky	if (fwg->buf == NULL) {
283347880Shselasky		fwg->reg_filled = mdev->dump_size;
284347880Shselasky		mtx_unlock(&mdev->dump_lock);
285330653Shselasky		return (0);
286330653Shselasky	}
287347880Shselasky	if (!mdev->dump_valid) {
288347880Shselasky		mtx_unlock(&mdev->dump_lock);
289330653Shselasky		return (ENOENT);
290347880Shselasky	}
291347880Shselasky	mdev->dump_copyout = true;
292347880Shselasky	mtx_unlock(&mdev->dump_lock);
293330653Shselasky
294330653Shselasky	urv = fwg->buf;
295347880Shselasky	for (i = 0, r = mdev->dump_rege; r->cnt != 0; r++) {
296330653Shselasky		for (ri = 0; ri < r->cnt; ri++) {
297330653Shselasky			if (i >= fwg->reg_cnt)
298330653Shselasky				goto out;
299330653Shselasky			rv.addr = r->addr + ri * 4;
300347880Shselasky			rv.val = mdev->dump_data[i];
301330653Shselasky			error = copyout(&rv, urv, sizeof(rv));
302330653Shselasky			if (error != 0)
303330653Shselasky				return (error);
304330653Shselasky			urv++;
305330653Shselasky			i++;
306330653Shselasky		}
307330653Shselasky	}
308330653Shselaskyout:
309330653Shselasky	fwg->reg_filled = i;
310347880Shselasky	mtx_lock(&mdev->dump_lock);
311347880Shselasky	mdev->dump_copyout = false;
312347880Shselasky	wakeup(&mdev->dump_copyout);
313347880Shselasky	mtx_unlock(&mdev->dump_lock);
314330653Shselasky	return (0);
315330653Shselasky}
316330653Shselasky
317330653Shselaskystatic int
318347869Shselaskymlx5_fw_reset(struct mlx5_core_dev *mdev)
319347869Shselasky{
320347869Shselasky	device_t dev, bus;
321347869Shselasky	int error;
322347869Shselasky
323347869Shselasky	error = -mlx5_set_mfrl_reg(mdev, MLX5_FRL_LEVEL3);
324347869Shselasky	if (error == 0) {
325347869Shselasky		dev = mdev->pdev->dev.bsddev;
326347869Shselasky		mtx_lock(&Giant);
327347869Shselasky		bus = device_get_parent(dev);
328347869Shselasky		error = BUS_RESET_CHILD(device_get_parent(bus), bus,
329347869Shselasky		    DEVF_RESET_DETACH);
330347869Shselasky		mtx_unlock(&Giant);
331347869Shselasky	}
332347869Shselasky	return (error);
333347869Shselasky}
334347869Shselasky
335347869Shselaskystatic int
336353240Shselaskymlx5_eeprom_copyout(struct mlx5_core_dev *dev, struct mlx5_eeprom_get *eeprom_info)
337353240Shselasky{
338353240Shselasky	struct mlx5_eeprom eeprom;
339353240Shselasky	int error;
340353240Shselasky
341353240Shselasky	eeprom.i2c_addr = MLX5_I2C_ADDR_LOW;
342353240Shselasky	eeprom.device_addr = 0;
343353240Shselasky	eeprom.page_num = MLX5_EEPROM_LOW_PAGE;
344353240Shselasky	eeprom.page_valid = 0;
345353240Shselasky
346353240Shselasky	/* Read three first bytes to get important info */
347353240Shselasky	error = mlx5_get_eeprom_info(dev, &eeprom);
348353240Shselasky	if (error != 0) {
349353240Shselasky		mlx5_core_err(dev,
350353240Shselasky		    "Failed reading EEPROM initial information\n");
351353240Shselasky		return (error);
352353240Shselasky	}
353353240Shselasky	eeprom_info->eeprom_info_page_valid = eeprom.page_valid;
354353240Shselasky	eeprom_info->eeprom_info_out_len = eeprom.len;
355353240Shselasky
356353240Shselasky	if (eeprom_info->eeprom_info_buf == NULL)
357353240Shselasky		return (0);
358353240Shselasky	/*
359353240Shselasky	 * Allocate needed length buffer and additional space for
360353240Shselasky	 * page 0x03
361353240Shselasky	 */
362353240Shselasky	eeprom.data = malloc(eeprom.len + MLX5_EEPROM_PAGE_LENGTH,
363353240Shselasky	    M_MLX5_EEPROM, M_WAITOK | M_ZERO);
364353240Shselasky
365353240Shselasky	/* Read the whole eeprom information */
366353240Shselasky	error = mlx5_get_eeprom(dev, &eeprom);
367353240Shselasky	if (error != 0) {
368353240Shselasky		mlx5_core_err(dev, "Failed reading EEPROM error = %d\n",
369353240Shselasky		    error);
370353240Shselasky		error = 0;
371353240Shselasky		/*
372353240Shselasky		 * Continue printing partial information in case of
373353240Shselasky		 * an error
374353240Shselasky		 */
375353240Shselasky	}
376353240Shselasky	error = copyout(eeprom.data, eeprom_info->eeprom_info_buf,
377353240Shselasky	    eeprom.len);
378353240Shselasky	free(eeprom.data, M_MLX5_EEPROM);
379353240Shselasky
380353240Shselasky	return (error);
381353240Shselasky}
382353240Shselasky
383353240Shselaskystatic int
384347871Shselaskymlx5_ctl_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag,
385330653Shselasky    struct thread *td)
386330653Shselasky{
387330653Shselasky	struct mlx5_core_dev *mdev;
388330653Shselasky	struct mlx5_fwdump_get *fwg;
389347840Shselasky	struct mlx5_tool_addr *devaddr;
390347841Shselasky	struct mlx5_fw_update *fu;
391347841Shselasky	struct firmware fake_fw;
392353240Shselasky	struct mlx5_eeprom_get *eeprom_info;
393330653Shselasky	int error;
394330653Shselasky
395330653Shselasky	error = 0;
396330653Shselasky	switch (cmd) {
397330653Shselasky	case MLX5_FWDUMP_GET:
398330653Shselasky		if ((fflag & FREAD) == 0) {
399330653Shselasky			error = EBADF;
400330653Shselasky			break;
401330653Shselasky		}
402330653Shselasky		fwg = (struct mlx5_fwdump_get *)data;
403330653Shselasky		devaddr = &fwg->devaddr;
404330653Shselasky		error = mlx5_dbsf_to_core(devaddr, &mdev);
405330653Shselasky		if (error != 0)
406330653Shselasky			break;
407347880Shselasky		error = mlx5_fwdump_copyout(mdev, fwg);
408330653Shselasky		break;
409330653Shselasky	case MLX5_FWDUMP_RESET:
410330653Shselasky		if ((fflag & FWRITE) == 0) {
411330653Shselasky			error = EBADF;
412330653Shselasky			break;
413330653Shselasky		}
414347840Shselasky		devaddr = (struct mlx5_tool_addr *)data;
415330653Shselasky		error = mlx5_dbsf_to_core(devaddr, &mdev);
416347880Shselasky		if (error == 0)
417347880Shselasky			error = mlx5_fwdump_reset(mdev);
418330653Shselasky		break;
419330653Shselasky	case MLX5_FWDUMP_FORCE:
420330653Shselasky		if ((fflag & FWRITE) == 0) {
421330653Shselasky			error = EBADF;
422330653Shselasky			break;
423330653Shselasky		}
424347840Shselasky		devaddr = (struct mlx5_tool_addr *)data;
425330653Shselasky		error = mlx5_dbsf_to_core(devaddr, &mdev);
426330653Shselasky		if (error != 0)
427330653Shselasky			break;
428353246Shselasky		error = mlx5_fwdump(mdev);
429330653Shselasky		break;
430347841Shselasky	case MLX5_FW_UPDATE:
431347841Shselasky		if ((fflag & FWRITE) == 0) {
432347841Shselasky			error = EBADF;
433347841Shselasky			break;
434347841Shselasky		}
435347841Shselasky		fu = (struct mlx5_fw_update *)data;
436347841Shselasky		if (fu->img_fw_data_len > 10 * 1024 * 1024) {
437347841Shselasky			error = EINVAL;
438347841Shselasky			break;
439347841Shselasky		}
440347841Shselasky		devaddr = &fu->devaddr;
441347841Shselasky		error = mlx5_dbsf_to_core(devaddr, &mdev);
442347841Shselasky		if (error != 0)
443347841Shselasky			break;
444347841Shselasky		bzero(&fake_fw, sizeof(fake_fw));
445347841Shselasky		fake_fw.name = "umlx_fw_up";
446347841Shselasky		fake_fw.datasize = fu->img_fw_data_len;
447347841Shselasky		fake_fw.version = 1;
448347844Shselasky		fake_fw.data = (void *)kmem_malloc(kmem_arena, fu->img_fw_data_len,
449347841Shselasky		    M_WAITOK);
450347841Shselasky		if (fake_fw.data == NULL) {
451347841Shselasky			error = ENOMEM;
452347841Shselasky			break;
453347841Shselasky		}
454347841Shselasky		error = copyin(fu->img_fw_data, __DECONST(void *, fake_fw.data),
455347841Shselasky		    fu->img_fw_data_len);
456347841Shselasky		if (error == 0)
457347841Shselasky			error = -mlx5_firmware_flash(mdev, &fake_fw);
458347844Shselasky		kmem_free(kmem_arena, (vm_offset_t)fake_fw.data, fu->img_fw_data_len);
459347841Shselasky		break;
460347869Shselasky	case MLX5_FW_RESET:
461347869Shselasky		if ((fflag & FWRITE) == 0) {
462347869Shselasky			error = EBADF;
463347869Shselasky			break;
464347869Shselasky		}
465347869Shselasky		devaddr = (struct mlx5_tool_addr *)data;
466347869Shselasky		error = mlx5_dbsf_to_core(devaddr, &mdev);
467347869Shselasky		if (error != 0)
468347869Shselasky			break;
469347869Shselasky		error = mlx5_fw_reset(mdev);
470347869Shselasky		break;
471353240Shselasky	case MLX5_EEPROM_GET:
472353240Shselasky		if ((fflag & FREAD) == 0) {
473353240Shselasky			error = EBADF;
474353240Shselasky			break;
475353240Shselasky		}
476353240Shselasky		eeprom_info = (struct mlx5_eeprom_get *)data;
477353240Shselasky		devaddr = &eeprom_info->devaddr;
478353240Shselasky		error = mlx5_dbsf_to_core(devaddr, &mdev);
479353240Shselasky		if (error != 0)
480353240Shselasky			break;
481353240Shselasky		error = mlx5_eeprom_copyout(mdev, eeprom_info);
482353240Shselasky		break;
483330653Shselasky	default:
484330653Shselasky		error = ENOTTY;
485330653Shselasky		break;
486330653Shselasky	}
487330653Shselasky	return (error);
488330653Shselasky}
489330653Shselasky
490347871Shselaskystatic struct cdevsw mlx5_ctl_devsw = {
491330653Shselasky	.d_version =	D_VERSION,
492347871Shselasky	.d_ioctl =	mlx5_ctl_ioctl,
493330653Shselasky};
494330653Shselasky
495347871Shselaskystatic struct cdev *mlx5_ctl_dev;
496330653Shselasky
497330653Shselaskyint
498347871Shselaskymlx5_ctl_init(void)
499330653Shselasky{
500330653Shselasky	struct make_dev_args mda;
501330653Shselasky	int error;
502330653Shselasky
503330653Shselasky	make_dev_args_init(&mda);
504330653Shselasky	mda.mda_flags = MAKEDEV_WAITOK | MAKEDEV_CHECKNAME;
505347871Shselasky	mda.mda_devsw = &mlx5_ctl_devsw;
506330653Shselasky	mda.mda_uid = UID_ROOT;
507330653Shselasky	mda.mda_gid = GID_OPERATOR;
508330653Shselasky	mda.mda_mode = 0640;
509347871Shselasky	error = make_dev_s(&mda, &mlx5_ctl_dev, "mlx5ctl");
510330653Shselasky	return (-error);
511330653Shselasky}
512330653Shselasky
513330653Shselaskyvoid
514347871Shselaskymlx5_ctl_fini(void)
515330653Shselasky{
516330653Shselasky
517347871Shselasky	if (mlx5_ctl_dev != NULL)
518347871Shselasky		destroy_dev(mlx5_ctl_dev);
519330653Shselasky
520330653Shselasky}
521