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