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 int error; 414 415 error = 0; 416 switch (cmd) { 417 case MLX5_FWDUMP_GET: 418 if ((fflag & FREAD) == 0) { 419 error = EBADF; 420 break; 421 } 422 fwg = (struct mlx5_fwdump_get *)data; 423 devaddr = &fwg->devaddr; 424 error = mlx5_dbsf_to_core(devaddr, &mdev); 425 if (error != 0) 426 break; 427 error = mlx5_fwdump_copyout(mdev, fwg); 428 break; 429 case MLX5_FWDUMP_RESET: 430 if ((fflag & FWRITE) == 0) { 431 error = EBADF; 432 break; 433 } 434 devaddr = (struct mlx5_tool_addr *)data; 435 error = mlx5_dbsf_to_core(devaddr, &mdev); 436 if (error == 0) 437 error = mlx5_fwdump_reset(mdev); 438 break; 439 case MLX5_FWDUMP_FORCE: 440 if ((fflag & FWRITE) == 0) { 441 error = EBADF; 442 break; 443 } 444 devaddr = (struct mlx5_tool_addr *)data; 445 error = mlx5_dbsf_to_core(devaddr, &mdev); 446 if (error != 0) 447 break; 448 error = mlx5_fwdump(mdev); 449 break; 450 case MLX5_FW_UPDATE: 451 if ((fflag & FWRITE) == 0) { 452 error = EBADF; 453 break; 454 } 455 fu = (struct mlx5_fw_update *)data; 456 if (fu->img_fw_data_len > 10 * 1024 * 1024) { 457 error = EINVAL; 458 break; 459 } 460 devaddr = &fu->devaddr; 461 error = mlx5_dbsf_to_core(devaddr, &mdev); 462 if (error != 0) 463 break; 464 bzero(&fake_fw, sizeof(fake_fw)); 465 fake_fw.name = "umlx_fw_up"; 466 fake_fw.datasize = fu->img_fw_data_len; 467 fake_fw.version = 1; 468 fake_fw.data = (void *)kmem_malloc(fu->img_fw_data_len, 469 M_WAITOK); 470 if (fake_fw.data == NULL) { 471 error = ENOMEM; 472 break; 473 } 474 error = copyin(fu->img_fw_data, __DECONST(void *, fake_fw.data), 475 fu->img_fw_data_len); 476 if (error == 0) 477 error = -mlx5_firmware_flash(mdev, &fake_fw); 478 kmem_free((vm_offset_t)fake_fw.data, fu->img_fw_data_len); 479 break; 480 case MLX5_FW_RESET: 481 if ((fflag & FWRITE) == 0) { 482 error = EBADF; 483 break; 484 } 485 devaddr = (struct mlx5_tool_addr *)data; 486 error = mlx5_dbsf_to_core(devaddr, &mdev); 487 if (error != 0) 488 break; 489 error = mlx5_fw_reset(mdev); 490 break; 491 case MLX5_EEPROM_GET: 492 if ((fflag & FREAD) == 0) { 493 error = EBADF; 494 break; 495 } 496 eeprom_info = (struct mlx5_eeprom_get *)data; 497 devaddr = &eeprom_info->devaddr; 498 error = mlx5_dbsf_to_core(devaddr, &mdev); 499 if (error != 0) 500 break; 501 error = mlx5_eeprom_copyout(mdev, eeprom_info); 502 break; 503 default: 504 error = ENOTTY; 505 break; 506 } 507 return (error); 508 } 509 510 static struct cdevsw mlx5_ctl_devsw = { 511 .d_version = D_VERSION, 512 .d_ioctl = mlx5_ctl_ioctl, 513 }; 514 515 static struct cdev *mlx5_ctl_dev; 516 517 int 518 mlx5_ctl_init(void) 519 { 520 struct make_dev_args mda; 521 int error; 522 523 make_dev_args_init(&mda); 524 mda.mda_flags = MAKEDEV_WAITOK | MAKEDEV_CHECKNAME; 525 mda.mda_devsw = &mlx5_ctl_devsw; 526 mda.mda_uid = UID_ROOT; 527 mda.mda_gid = GID_OPERATOR; 528 mda.mda_mode = 0640; 529 error = make_dev_s(&mda, &mlx5_ctl_dev, "mlx5ctl"); 530 return (-error); 531 } 532 533 void 534 mlx5_ctl_fini(void) 535 { 536 537 if (mlx5_ctl_dev != NULL) 538 destroy_dev(mlx5_ctl_dev); 539 540 } 541