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