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