2 * Copyright (c) 2018, 2019 Mellanox Technologies, Ltd. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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
26 #include <sys/cdefs.h>
27 __FBSDID("$FreeBSD$");
29 #include <sys/param.h>
30 #include <sys/systm.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>
40 static MALLOC_DEFINE(M_MLX5_DUMP, "MLX5DUMP", "MLX5 Firmware dump");
43 mlx5_fwdump_getsize(const struct mlx5_crspace_regmap *rege)
45 const struct mlx5_crspace_regmap *r;
48 for (sz = 0, r = rege; r->cnt != 0; r++)
54 mlx5_fwdump_destroy_dd(struct mlx5_core_dev *mdev)
57 mtx_assert(&mdev->dump_lock, MA_OWNED);
58 free(mdev->dump_data, M_MLX5_DUMP);
59 mdev->dump_data = NULL;
63 mlx5_fwdump_prep(struct mlx5_core_dev *mdev)
68 u32 addr, in, out, next_addr;
70 mdev->dump_data = NULL;
71 error = mlx5_vsc_find_cap(mdev);
73 /* Inability to create a firmware dump is not fatal. */
75 "Failed to find vendor-specific capability, error %d\n",
79 error = mlx5_vsc_lock(mdev);
82 error = mlx5_vsc_set_space(mdev, MLX5_VSC_DOMAIN_SCAN_CRSPACE);
84 mlx5_core_warn(mdev, "VSC scan space is not supported\n");
87 dev = mdev->pdev->dev.bsddev;
88 vsc_addr = mdev->vsc_addr;
90 mlx5_core_warn(mdev, "Cannot read VSC, no address\n");
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);
101 "Failed waiting for read complete flag, error %d addr %#x\n",
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)
110 if (next_addr != addr + 4)
115 mlx5_core_warn(mdev, "no output from scan space\n");
118 mdev->dump_rege = malloc(sz * sizeof(struct mlx5_crspace_regmap),
119 M_MLX5_DUMP, M_WAITOK | M_ZERO);
121 for (i = 0, addr = 0;;) {
123 mdev->dump_rege[i].cnt++;
124 MLX5_VSC_SET(vsc_addr, &in, address, addr);
125 pci_write_config(dev, vsc_addr + MLX5_VSC_ADDR_OFFSET, in, 4);
126 error = mlx5_vsc_wait_on_flag(mdev, 1);
129 "Failed waiting for read complete flag, error %d addr %#x\n",
131 free(mdev->dump_rege, M_MLX5_DUMP);
132 mdev->dump_rege = NULL;
135 pci_read_config(dev, vsc_addr + MLX5_VSC_DATA_OFFSET, 4);
136 out = pci_read_config(dev, vsc_addr + MLX5_VSC_ADDR_OFFSET, 4);
137 next_addr = MLX5_VSC_GET(vsc_addr, &out, address);
138 if (next_addr == 0 || next_addr == addr)
140 if (next_addr != addr + 4)
141 mdev->dump_rege[++i].addr = next_addr;
146 "Inconsistent hw crspace reads: sz %u i %u addr %#lx",
147 sz, i, (unsigned long)addr);
150 mdev->dump_size = mlx5_fwdump_getsize(mdev->dump_rege);
151 mdev->dump_data = malloc(mdev->dump_size * sizeof(uint32_t),
152 M_MLX5_DUMP, M_WAITOK | M_ZERO);
153 mdev->dump_valid = false;
154 mdev->dump_copyout = false;
157 mlx5_vsc_unlock(mdev);
161 mlx5_fwdump(struct mlx5_core_dev *mdev)
163 const struct mlx5_crspace_regmap *r;
167 mlx5_core_info(mdev, "Issuing FW dump\n");
168 mtx_lock(&mdev->dump_lock);
169 if (mdev->dump_data == NULL) {
173 if (mdev->dump_valid) {
176 "Only one FW dump can be captured aborting FW dump\n");
181 /* mlx5_vsc already warns, be silent. */
182 error = mlx5_vsc_lock(mdev);
185 error = mlx5_vsc_set_space(mdev, MLX5_VSC_DOMAIN_PROTECTED_CRSPACE);
188 for (i = 0, r = mdev->dump_rege; r->cnt != 0; r++) {
189 for (ri = 0; ri < r->cnt; ri++) {
190 error = mlx5_vsc_read(mdev, r->addr + ri * 4,
191 &mdev->dump_data[i]);
197 mdev->dump_valid = true;
199 mlx5_vsc_unlock(mdev);
201 mtx_unlock(&mdev->dump_lock);
206 mlx5_fwdump_clean(struct mlx5_core_dev *mdev)
209 mtx_lock(&mdev->dump_lock);
210 while (mdev->dump_copyout)
211 msleep(&mdev->dump_copyout, &mdev->dump_lock, 0, "mlx5fwc", 0);
212 mlx5_fwdump_destroy_dd(mdev);
213 mtx_unlock(&mdev->dump_lock);
214 free(mdev->dump_rege, M_MLX5_DUMP);
218 mlx5_fwdump_reset(struct mlx5_core_dev *mdev)
223 mtx_lock(&mdev->dump_lock);
224 if (mdev->dump_data != NULL) {
225 while (mdev->dump_copyout) {
226 msleep(&mdev->dump_copyout, &mdev->dump_lock,
229 mdev->dump_valid = false;
233 mtx_unlock(&mdev->dump_lock);
238 mlx5_dbsf_to_core(const struct mlx5_tool_addr *devaddr,
239 struct mlx5_core_dev **mdev)
242 struct pci_dev *pdev;
244 dev = pci_find_dbsf(devaddr->domain, devaddr->bus, devaddr->slot,
248 if (device_get_devclass(dev) != mlx5_core_driver.bsdclass)
250 pdev = device_get_softc(dev);
251 *mdev = pci_get_drvdata(pdev);
258 mlx5_fwdump_copyout(struct mlx5_core_dev *mdev, struct mlx5_fwdump_get *fwg)
260 const struct mlx5_crspace_regmap *r;
261 struct mlx5_fwdump_reg rv, *urv;
265 mtx_lock(&mdev->dump_lock);
266 if (mdev->dump_data == NULL) {
267 mtx_unlock(&mdev->dump_lock);
270 if (fwg->buf == NULL) {
271 fwg->reg_filled = mdev->dump_size;
272 mtx_unlock(&mdev->dump_lock);
275 if (!mdev->dump_valid) {
276 mtx_unlock(&mdev->dump_lock);
279 mdev->dump_copyout = true;
280 mtx_unlock(&mdev->dump_lock);
283 for (i = 0, r = mdev->dump_rege; r->cnt != 0; r++) {
284 for (ri = 0; ri < r->cnt; ri++) {
285 if (i >= fwg->reg_cnt)
287 rv.addr = r->addr + ri * 4;
288 rv.val = mdev->dump_data[i];
289 error = copyout(&rv, urv, sizeof(rv));
298 mtx_lock(&mdev->dump_lock);
299 mdev->dump_copyout = false;
300 wakeup(&mdev->dump_copyout);
301 mtx_unlock(&mdev->dump_lock);
306 mlx5_fw_reset(struct mlx5_core_dev *mdev)
311 error = -mlx5_set_mfrl_reg(mdev, MLX5_FRL_LEVEL3);
313 dev = mdev->pdev->dev.bsddev;
315 bus = device_get_parent(dev);
316 error = BUS_RESET_CHILD(device_get_parent(bus), bus,
324 mlx5_eeprom_copyout(struct mlx5_core_dev *dev, struct mlx5_eeprom_get *eeprom_info)
326 struct mlx5_eeprom eeprom;
329 eeprom.i2c_addr = MLX5_I2C_ADDR_LOW;
330 eeprom.device_addr = 0;
331 eeprom.page_num = MLX5_EEPROM_LOW_PAGE;
332 eeprom.page_valid = 0;
334 /* Read three first bytes to get important info */
335 error = mlx5_get_eeprom_info(dev, &eeprom);
338 "Failed reading EEPROM initial information\n");
341 eeprom_info->eeprom_info_page_valid = eeprom.page_valid;
342 eeprom_info->eeprom_info_out_len = eeprom.len;
344 if (eeprom_info->eeprom_info_buf == NULL)
347 * Allocate needed length buffer and additional space for
350 eeprom.data = malloc(eeprom.len + MLX5_EEPROM_PAGE_LENGTH,
351 M_MLX5_EEPROM, M_WAITOK | M_ZERO);
353 /* Read the whole eeprom information */
354 error = mlx5_get_eeprom(dev, &eeprom);
356 mlx5_core_err(dev, "Failed reading EEPROM error = %d\n",
360 * Continue printing partial information in case of
364 error = copyout(eeprom.data, eeprom_info->eeprom_info_buf,
366 free(eeprom.data, M_MLX5_EEPROM);
372 mlx5_ctl_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag,
375 struct mlx5_core_dev *mdev;
376 struct mlx5_fwdump_get *fwg;
377 struct mlx5_tool_addr *devaddr;
378 struct mlx5_fw_update *fu;
379 struct firmware fake_fw;
380 struct mlx5_eeprom_get *eeprom_info;
385 case MLX5_FWDUMP_GET:
386 if ((fflag & FREAD) == 0) {
390 fwg = (struct mlx5_fwdump_get *)data;
391 devaddr = &fwg->devaddr;
392 error = mlx5_dbsf_to_core(devaddr, &mdev);
395 error = mlx5_fwdump_copyout(mdev, fwg);
397 case MLX5_FWDUMP_RESET:
398 if ((fflag & FWRITE) == 0) {
402 devaddr = (struct mlx5_tool_addr *)data;
403 error = mlx5_dbsf_to_core(devaddr, &mdev);
405 error = mlx5_fwdump_reset(mdev);
407 case MLX5_FWDUMP_FORCE:
408 if ((fflag & FWRITE) == 0) {
412 devaddr = (struct mlx5_tool_addr *)data;
413 error = mlx5_dbsf_to_core(devaddr, &mdev);
416 error = mlx5_fwdump(mdev);
419 if ((fflag & FWRITE) == 0) {
423 fu = (struct mlx5_fw_update *)data;
424 if (fu->img_fw_data_len > 10 * 1024 * 1024) {
428 devaddr = &fu->devaddr;
429 error = mlx5_dbsf_to_core(devaddr, &mdev);
432 bzero(&fake_fw, sizeof(fake_fw));
433 fake_fw.name = "umlx_fw_up";
434 fake_fw.datasize = fu->img_fw_data_len;
436 fake_fw.data = (void *)kmem_malloc(fu->img_fw_data_len,
438 if (fake_fw.data == NULL) {
442 error = copyin(fu->img_fw_data, __DECONST(void *, fake_fw.data),
443 fu->img_fw_data_len);
445 error = -mlx5_firmware_flash(mdev, &fake_fw);
446 kmem_free((vm_offset_t)fake_fw.data, fu->img_fw_data_len);
449 if ((fflag & FWRITE) == 0) {
453 devaddr = (struct mlx5_tool_addr *)data;
454 error = mlx5_dbsf_to_core(devaddr, &mdev);
457 error = mlx5_fw_reset(mdev);
459 case MLX5_EEPROM_GET:
460 if ((fflag & FREAD) == 0) {
464 eeprom_info = (struct mlx5_eeprom_get *)data;
465 devaddr = &eeprom_info->devaddr;
466 error = mlx5_dbsf_to_core(devaddr, &mdev);
469 error = mlx5_eeprom_copyout(mdev, eeprom_info);
478 static struct cdevsw mlx5_ctl_devsw = {
479 .d_version = D_VERSION,
480 .d_ioctl = mlx5_ctl_ioctl,
483 static struct cdev *mlx5_ctl_dev;
488 struct make_dev_args mda;
491 make_dev_args_init(&mda);
492 mda.mda_flags = MAKEDEV_WAITOK | MAKEDEV_CHECKNAME;
493 mda.mda_devsw = &mlx5_ctl_devsw;
494 mda.mda_uid = UID_ROOT;
495 mda.mda_gid = GID_OPERATOR;
497 error = make_dev_s(&mda, &mlx5_ctl_dev, "mlx5ctl");
505 if (mlx5_ctl_dev != NULL)
506 destroy_dev(mlx5_ctl_dev);