1 /*- 2 * Copyright (c) 2018, 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/mlx5_core/mlx5_core.h> 36 #include <dev/mlx5/mlx5io.h> 37 38 extern const struct mlx5_crspace_regmap mlx5_crspace_regmap_mt4117[]; 39 extern const struct mlx5_crspace_regmap mlx5_crspace_regmap_mt4115[]; 40 extern const struct mlx5_crspace_regmap mlx5_crspace_regmap_connectx5[]; 41 42 struct mlx5_dump_data { 43 const struct mlx5_crspace_regmap *rege; 44 uint32_t *dump; 45 unsigned dump_size; 46 int dump_valid; 47 struct mtx dump_lock; 48 }; 49 50 static MALLOC_DEFINE(M_MLX5_DUMP, "MLX5DUMP", "MLX5 Firmware dump"); 51 52 static unsigned 53 mlx5_fwdump_getsize(const struct mlx5_crspace_regmap *rege) 54 { 55 const struct mlx5_crspace_regmap *r; 56 unsigned sz; 57 58 for (sz = 0, r = rege; r->cnt != 0; r++) 59 sz += r->cnt; 60 return (sz); 61 } 62 63 static void 64 mlx5_fwdump_destroy_dd(struct mlx5_dump_data *dd) 65 { 66 67 mtx_destroy(&dd->dump_lock); 68 free(dd->dump, M_MLX5_DUMP); 69 free(dd, M_MLX5_DUMP); 70 } 71 72 void 73 mlx5_fwdump_prep(struct mlx5_core_dev *mdev) 74 { 75 struct mlx5_dump_data *dd; 76 int error; 77 78 error = mlx5_vsc_find_cap(mdev); 79 if (error != 0) { 80 /* Inability to create a firmware dump is not fatal. */ 81 device_printf((&mdev->pdev->dev)->bsddev, "WARN: " 82 "mlx5_fwdump_prep failed %d\n", error); 83 return; 84 } 85 dd = malloc(sizeof(struct mlx5_dump_data), M_MLX5_DUMP, M_WAITOK); 86 switch (pci_get_device(mdev->pdev->dev.bsddev)) { 87 case 0x1013: 88 dd->rege = mlx5_crspace_regmap_mt4115; 89 break; 90 case 0x1015: 91 dd->rege = mlx5_crspace_regmap_mt4117; 92 break; 93 case 0x1017: 94 case 0x1019: 95 dd->rege = mlx5_crspace_regmap_connectx5; 96 break; 97 default: 98 free(dd, M_MLX5_DUMP); 99 return; /* silently fail, do not prevent driver attach */ 100 } 101 dd->dump_size = mlx5_fwdump_getsize(dd->rege); 102 dd->dump = malloc(dd->dump_size * sizeof(uint32_t), M_MLX5_DUMP, 103 M_WAITOK | M_ZERO); 104 dd->dump_valid = 0; 105 mtx_init(&dd->dump_lock, "mlx5dmp", NULL, MTX_DEF | MTX_NEW); 106 if (atomic_cmpset_rel_ptr((uintptr_t *)&mdev->dump_data, 0, 107 (uintptr_t)dd) == 0) 108 mlx5_fwdump_destroy_dd(dd); 109 } 110 111 void 112 mlx5_fwdump(struct mlx5_core_dev *mdev) 113 { 114 struct mlx5_dump_data *dd; 115 const struct mlx5_crspace_regmap *r; 116 uint32_t i, ri; 117 int error; 118 119 dev_info(&mdev->pdev->dev, "Issuing FW dump\n"); 120 dd = (struct mlx5_dump_data *)atomic_load_acq_ptr((uintptr_t *) 121 &mdev->dump_data); 122 if (dd == NULL) 123 return; 124 mtx_lock(&dd->dump_lock); 125 if (dd->dump_valid) { 126 /* only one dump */ 127 dev_warn(&mdev->pdev->dev, 128 "Only one FW dump can be captured aborting FW dump\n"); 129 goto failed; 130 } 131 132 /* mlx5_vsc already warns, be silent. */ 133 error = mlx5_vsc_lock(mdev); 134 if (error != 0) 135 goto failed; 136 error = mlx5_vsc_set_space(mdev, MLX5_VSC_DOMAIN_PROTECTED_CRSPACE); 137 if (error != 0) 138 goto unlock_vsc; 139 for (i = 0, r = dd->rege; r->cnt != 0; r++) { 140 for (ri = 0; ri < r->cnt; ri++) { 141 error = mlx5_vsc_read(mdev, r->addr + ri * 4, 142 &dd->dump[i]); 143 if (error != 0) 144 goto unlock_vsc; 145 i++; 146 } 147 } 148 atomic_store_rel_int(&dd->dump_valid, 1); 149 unlock_vsc: 150 mlx5_vsc_unlock(mdev); 151 failed: 152 mtx_unlock(&dd->dump_lock); 153 } 154 155 void 156 mlx5_fwdump_clean(struct mlx5_core_dev *mdev) 157 { 158 struct mlx5_dump_data *dd; 159 160 for (;;) { 161 dd = mdev->dump_data; 162 if (dd == NULL) 163 return; 164 if (atomic_cmpset_ptr((uintptr_t *)&mdev->dump_data, 165 (uintptr_t)dd, 0) == 1) { 166 mlx5_fwdump_destroy_dd(dd); 167 return; 168 } 169 } 170 } 171 172 static int 173 mlx5_dbsf_to_core(const struct mlx5_fwdump_addr *devaddr, 174 struct mlx5_core_dev **mdev) 175 { 176 device_t dev; 177 struct pci_dev *pdev; 178 179 dev = pci_find_dbsf(devaddr->domain, devaddr->bus, devaddr->slot, 180 devaddr->func); 181 if (dev == NULL) 182 return (ENOENT); 183 if (device_get_devclass(dev) != mlx5_core_driver.bsdclass) 184 return (EINVAL); 185 pdev = device_get_softc(dev); 186 *mdev = pci_get_drvdata(pdev); 187 if (*mdev == NULL) 188 return (ENOENT); 189 return (0); 190 } 191 192 static int 193 mlx5_fwdump_copyout(struct mlx5_dump_data *dd, struct mlx5_fwdump_get *fwg) 194 { 195 const struct mlx5_crspace_regmap *r; 196 struct mlx5_fwdump_reg rv, *urv; 197 uint32_t i, ri; 198 int error; 199 200 if (dd == NULL) 201 return (ENOENT); 202 if (fwg->buf == NULL) { 203 fwg->reg_filled = dd->dump_size; 204 return (0); 205 } 206 if (atomic_load_acq_int(&dd->dump_valid) == 0) 207 return (ENOENT); 208 209 urv = fwg->buf; 210 for (i = 0, r = dd->rege; r->cnt != 0; r++) { 211 for (ri = 0; ri < r->cnt; ri++) { 212 if (i >= fwg->reg_cnt) 213 goto out; 214 rv.addr = r->addr + ri * 4; 215 rv.val = dd->dump[i]; 216 error = copyout(&rv, urv, sizeof(rv)); 217 if (error != 0) 218 return (error); 219 urv++; 220 i++; 221 } 222 } 223 out: 224 fwg->reg_filled = i; 225 return (0); 226 } 227 228 static int 229 mlx5_fwdump_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag, 230 struct thread *td) 231 { 232 struct mlx5_core_dev *mdev; 233 struct mlx5_fwdump_get *fwg; 234 struct mlx5_fwdump_addr *devaddr; 235 struct mlx5_dump_data *dd; 236 int error; 237 238 error = 0; 239 switch (cmd) { 240 case MLX5_FWDUMP_GET: 241 if ((fflag & FREAD) == 0) { 242 error = EBADF; 243 break; 244 } 245 fwg = (struct mlx5_fwdump_get *)data; 246 devaddr = &fwg->devaddr; 247 error = mlx5_dbsf_to_core(devaddr, &mdev); 248 if (error != 0) 249 break; 250 error = mlx5_fwdump_copyout(mdev->dump_data, fwg); 251 break; 252 case MLX5_FWDUMP_RESET: 253 if ((fflag & FWRITE) == 0) { 254 error = EBADF; 255 break; 256 } 257 devaddr = (struct mlx5_fwdump_addr *)data; 258 error = mlx5_dbsf_to_core(devaddr, &mdev); 259 if (error != 0) 260 break; 261 dd = mdev->dump_data; 262 if (dd != NULL) 263 atomic_store_rel_int(&dd->dump_valid, 0); 264 else 265 error = ENOENT; 266 break; 267 case MLX5_FWDUMP_FORCE: 268 if ((fflag & FWRITE) == 0) { 269 error = EBADF; 270 break; 271 } 272 devaddr = (struct mlx5_fwdump_addr *)data; 273 error = mlx5_dbsf_to_core(devaddr, &mdev); 274 if (error != 0) 275 break; 276 mlx5_fwdump(mdev); 277 break; 278 default: 279 error = ENOTTY; 280 break; 281 } 282 return (error); 283 } 284 285 static struct cdevsw mlx5_fwdump_devsw = { 286 .d_version = D_VERSION, 287 .d_ioctl = mlx5_fwdump_ioctl, 288 }; 289 290 static struct cdev *mlx5_fwdump_dev; 291 292 int 293 mlx5_fwdump_init(void) 294 { 295 struct make_dev_args mda; 296 int error; 297 298 make_dev_args_init(&mda); 299 mda.mda_flags = MAKEDEV_WAITOK | MAKEDEV_CHECKNAME; 300 mda.mda_devsw = &mlx5_fwdump_devsw; 301 mda.mda_uid = UID_ROOT; 302 mda.mda_gid = GID_OPERATOR; 303 mda.mda_mode = 0640; 304 error = make_dev_s(&mda, &mlx5_fwdump_dev, "mlx5ctl"); 305 return (-error); 306 } 307 308 void 309 mlx5_fwdump_fini(void) 310 { 311 312 if (mlx5_fwdump_dev != NULL) 313 destroy_dev(mlx5_fwdump_dev); 314 315 } 316