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