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
mlx5_fwdump_getsize(const struct mlx5_crspace_regmap * rege)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
mlx5_fwdump_destroy_dd(struct mlx5_core_dev * mdev)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
mlx5_fwdump_prep(struct mlx5_core_dev * mdev)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 "Unable 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
119 /*
120 * We add a sentinel element at the end of the array to
121 * terminate the read loop in mlx5_fwdump(), so allocate sz + 1.
122 */
123 mdev->dump_rege = malloc((sz + 1) * sizeof(struct mlx5_crspace_regmap),
124 M_MLX5_DUMP, M_WAITOK | M_ZERO);
125
126 for (i = 0, addr = 0;;) {
127 mdev->dump_rege[i].cnt++;
128 MLX5_VSC_SET(vsc_addr, &in, address, addr);
129 pci_write_config(dev, vsc_addr + MLX5_VSC_ADDR_OFFSET, in, 4);
130 error = mlx5_vsc_wait_on_flag(mdev, 1);
131 if (error != 0) {
132 mlx5_core_warn(mdev,
133 "Failed waiting for read complete flag, error %d addr %#x\n",
134 error, addr);
135 free(mdev->dump_rege, M_MLX5_DUMP);
136 mdev->dump_rege = NULL;
137 goto unlock_vsc;
138 }
139 pci_read_config(dev, vsc_addr + MLX5_VSC_DATA_OFFSET, 4);
140 out = pci_read_config(dev, vsc_addr + MLX5_VSC_ADDR_OFFSET, 4);
141 next_addr = MLX5_VSC_GET(vsc_addr, &out, address);
142 if (next_addr == 0 || next_addr == addr)
143 break;
144 if (next_addr != addr + 4) {
145 if (++i == sz) {
146 mlx5_core_err(mdev,
147 "Inconsistent hw crspace reads (1): sz %u i %u addr %#lx",
148 sz, i, (unsigned long)addr);
149 break;
150 }
151 mdev->dump_rege[i].addr = next_addr;
152 }
153 addr = next_addr;
154 }
155 /* i == sz case already reported by loop above */
156 if (i + 1 != sz && i != sz) {
157 mlx5_core_err(mdev,
158 "Inconsistent hw crspace reads (2): sz %u i %u addr %#lx",
159 sz, i, (unsigned long)addr);
160 }
161
162 mdev->dump_size = mlx5_fwdump_getsize(mdev->dump_rege);
163 mdev->dump_data = malloc(mdev->dump_size * sizeof(uint32_t),
164 M_MLX5_DUMP, M_WAITOK | M_ZERO);
165 mdev->dump_valid = false;
166 mdev->dump_copyout = false;
167
168 unlock_vsc:
169 mlx5_vsc_unlock(mdev);
170 }
171
172 int
mlx5_fwdump(struct mlx5_core_dev * mdev)173 mlx5_fwdump(struct mlx5_core_dev *mdev)
174 {
175 const struct mlx5_crspace_regmap *r;
176 uint32_t i, ri;
177 int error;
178
179 mlx5_core_info(mdev, "Issuing FW dump\n");
180 mtx_lock(&mdev->dump_lock);
181 if (mdev->dump_data == NULL) {
182 error = EIO;
183 goto failed;
184 }
185 if (mdev->dump_valid) {
186 /* only one dump */
187 mlx5_core_warn(mdev,
188 "Only one FW dump can be captured aborting FW dump\n");
189 error = EEXIST;
190 goto failed;
191 }
192
193 /* mlx5_vsc already warns, be silent. */
194 error = mlx5_vsc_lock(mdev);
195 if (error != 0)
196 goto failed;
197 error = mlx5_vsc_set_space(mdev, MLX5_VSC_DOMAIN_PROTECTED_CRSPACE);
198 if (error != 0)
199 goto unlock_vsc;
200 for (i = 0, r = mdev->dump_rege; r->cnt != 0; r++) {
201 for (ri = 0; ri < r->cnt; ri++) {
202 error = mlx5_vsc_read(mdev, r->addr + ri * 4,
203 &mdev->dump_data[i]);
204 if (error != 0)
205 goto unlock_vsc;
206 i++;
207 }
208 }
209 mdev->dump_valid = true;
210 unlock_vsc:
211 mlx5_vsc_unlock(mdev);
212 failed:
213 mtx_unlock(&mdev->dump_lock);
214 return (error);
215 }
216
217 void
mlx5_fwdump_clean(struct mlx5_core_dev * mdev)218 mlx5_fwdump_clean(struct mlx5_core_dev *mdev)
219 {
220
221 mtx_lock(&mdev->dump_lock);
222 while (mdev->dump_copyout)
223 msleep(&mdev->dump_copyout, &mdev->dump_lock, 0, "mlx5fwc", 0);
224 mlx5_fwdump_destroy_dd(mdev);
225 mtx_unlock(&mdev->dump_lock);
226 free(mdev->dump_rege, M_MLX5_DUMP);
227 }
228
229 static int
mlx5_fwdump_reset(struct mlx5_core_dev * mdev)230 mlx5_fwdump_reset(struct mlx5_core_dev *mdev)
231 {
232 int error;
233
234 error = 0;
235 mtx_lock(&mdev->dump_lock);
236 if (mdev->dump_data != NULL) {
237 while (mdev->dump_copyout) {
238 msleep(&mdev->dump_copyout, &mdev->dump_lock,
239 0, "mlx5fwr", 0);
240 }
241 mdev->dump_valid = false;
242 } else {
243 error = ENOENT;
244 }
245 mtx_unlock(&mdev->dump_lock);
246 return (error);
247 }
248
249 static int
mlx5_dbsf_to_core(const struct mlx5_tool_addr * devaddr,struct mlx5_core_dev ** mdev)250 mlx5_dbsf_to_core(const struct mlx5_tool_addr *devaddr,
251 struct mlx5_core_dev **mdev)
252 {
253 device_t dev;
254 struct pci_dev *pdev;
255
256 dev = pci_find_dbsf(devaddr->domain, devaddr->bus, devaddr->slot,
257 devaddr->func);
258 if (dev == NULL)
259 return (ENOENT);
260 if (device_get_devclass(dev) != mlx5_core_driver.bsdclass)
261 return (EINVAL);
262 pdev = device_get_softc(dev);
263 *mdev = pci_get_drvdata(pdev);
264 if (*mdev == NULL)
265 return (ENOENT);
266 return (0);
267 }
268
269 static int
mlx5_fwdump_copyout(struct mlx5_core_dev * mdev,struct mlx5_fwdump_get * fwg)270 mlx5_fwdump_copyout(struct mlx5_core_dev *mdev, struct mlx5_fwdump_get *fwg)
271 {
272 const struct mlx5_crspace_regmap *r;
273 struct mlx5_fwdump_reg rv, *urv;
274 uint32_t i, ri;
275 int error;
276
277 mtx_lock(&mdev->dump_lock);
278 if (mdev->dump_data == NULL) {
279 mtx_unlock(&mdev->dump_lock);
280 return (ENOENT);
281 }
282 if (fwg->buf == NULL) {
283 fwg->reg_filled = mdev->dump_size;
284 mtx_unlock(&mdev->dump_lock);
285 return (0);
286 }
287 if (!mdev->dump_valid) {
288 mtx_unlock(&mdev->dump_lock);
289 return (ENOENT);
290 }
291 mdev->dump_copyout = true;
292 mtx_unlock(&mdev->dump_lock);
293
294 urv = fwg->buf;
295 for (i = 0, r = mdev->dump_rege; r->cnt != 0; r++) {
296 for (ri = 0; ri < r->cnt; ri++) {
297 if (i >= fwg->reg_cnt)
298 goto out;
299 rv.addr = r->addr + ri * 4;
300 rv.val = mdev->dump_data[i];
301 error = copyout(&rv, urv, sizeof(rv));
302 if (error != 0)
303 return (error);
304 urv++;
305 i++;
306 }
307 }
308 out:
309 fwg->reg_filled = i;
310 mtx_lock(&mdev->dump_lock);
311 mdev->dump_copyout = false;
312 wakeup(&mdev->dump_copyout);
313 mtx_unlock(&mdev->dump_lock);
314 return (0);
315 }
316
317 static int
mlx5_fw_reset(struct mlx5_core_dev * mdev)318 mlx5_fw_reset(struct mlx5_core_dev *mdev)
319 {
320 device_t dev, bus;
321 int error;
322
323 error = -mlx5_set_mfrl_reg(mdev, MLX5_FRL_LEVEL3);
324 if (error == 0) {
325 dev = mdev->pdev->dev.bsddev;
326 mtx_lock(&Giant);
327 bus = device_get_parent(dev);
328 error = BUS_RESET_CHILD(device_get_parent(bus), bus,
329 DEVF_RESET_DETACH);
330 mtx_unlock(&Giant);
331 }
332 return (error);
333 }
334
335 static int
mlx5_eeprom_copyout(struct mlx5_core_dev * dev,struct mlx5_eeprom_get * eeprom_info)336 mlx5_eeprom_copyout(struct mlx5_core_dev *dev, struct mlx5_eeprom_get *eeprom_info)
337 {
338 struct mlx5_eeprom eeprom;
339 int error;
340
341 eeprom.i2c_addr = MLX5_I2C_ADDR_LOW;
342 eeprom.device_addr = 0;
343 eeprom.page_num = MLX5_EEPROM_LOW_PAGE;
344 eeprom.page_valid = 0;
345
346 /* Read three first bytes to get important info */
347 error = mlx5_get_eeprom_info(dev, &eeprom);
348 if (error != 0) {
349 mlx5_core_err(dev,
350 "Failed reading EEPROM initial information\n");
351 return (error);
352 }
353 eeprom_info->eeprom_info_page_valid = eeprom.page_valid;
354 eeprom_info->eeprom_info_out_len = eeprom.len;
355
356 if (eeprom_info->eeprom_info_buf == NULL)
357 return (0);
358 /*
359 * Allocate needed length buffer and additional space for
360 * page 0x03
361 */
362 eeprom.data = malloc(eeprom.len + MLX5_EEPROM_PAGE_LENGTH,
363 M_MLX5_EEPROM, M_WAITOK | M_ZERO);
364
365 /* Read the whole eeprom information */
366 error = mlx5_get_eeprom(dev, &eeprom);
367 if (error != 0) {
368 mlx5_core_err(dev, "Failed reading EEPROM error = %d\n",
369 error);
370 error = 0;
371 /*
372 * Continue printing partial information in case of
373 * an error
374 */
375 }
376 error = copyout(eeprom.data, eeprom_info->eeprom_info_buf,
377 eeprom.len);
378 free(eeprom.data, M_MLX5_EEPROM);
379
380 return (error);
381 }
382
383 static int
mlx5_ctl_ioctl(struct cdev * dev,u_long cmd,caddr_t data,int fflag,struct thread * td)384 mlx5_ctl_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag,
385 struct thread *td)
386 {
387 struct mlx5_core_dev *mdev;
388 struct mlx5_fwdump_get *fwg;
389 struct mlx5_tool_addr *devaddr;
390 struct mlx5_fw_update *fu;
391 struct firmware fake_fw;
392 struct mlx5_eeprom_get *eeprom_info;
393 int error;
394
395 error = 0;
396 switch (cmd) {
397 case MLX5_FWDUMP_GET:
398 if ((fflag & FREAD) == 0) {
399 error = EBADF;
400 break;
401 }
402 fwg = (struct mlx5_fwdump_get *)data;
403 devaddr = &fwg->devaddr;
404 error = mlx5_dbsf_to_core(devaddr, &mdev);
405 if (error != 0)
406 break;
407 error = mlx5_fwdump_copyout(mdev, fwg);
408 break;
409 case MLX5_FWDUMP_RESET:
410 if ((fflag & FWRITE) == 0) {
411 error = EBADF;
412 break;
413 }
414 devaddr = (struct mlx5_tool_addr *)data;
415 error = mlx5_dbsf_to_core(devaddr, &mdev);
416 if (error == 0)
417 error = mlx5_fwdump_reset(mdev);
418 break;
419 case MLX5_FWDUMP_FORCE:
420 if ((fflag & FWRITE) == 0) {
421 error = EBADF;
422 break;
423 }
424 devaddr = (struct mlx5_tool_addr *)data;
425 error = mlx5_dbsf_to_core(devaddr, &mdev);
426 if (error != 0)
427 break;
428 error = mlx5_fwdump(mdev);
429 break;
430 case MLX5_FW_UPDATE:
431 if ((fflag & FWRITE) == 0) {
432 error = EBADF;
433 break;
434 }
435 fu = (struct mlx5_fw_update *)data;
436 if (fu->img_fw_data_len > 10 * 1024 * 1024) {
437 error = EINVAL;
438 break;
439 }
440 devaddr = &fu->devaddr;
441 error = mlx5_dbsf_to_core(devaddr, &mdev);
442 if (error != 0)
443 break;
444 bzero(&fake_fw, sizeof(fake_fw));
445 fake_fw.name = "umlx_fw_up";
446 fake_fw.datasize = fu->img_fw_data_len;
447 fake_fw.version = 1;
448 fake_fw.data = (void *)kmem_malloc(kmem_arena, fu->img_fw_data_len,
449 M_WAITOK);
450 if (fake_fw.data == NULL) {
451 error = ENOMEM;
452 break;
453 }
454 error = copyin(fu->img_fw_data, __DECONST(void *, fake_fw.data),
455 fu->img_fw_data_len);
456 if (error == 0)
457 error = -mlx5_firmware_flash(mdev, &fake_fw);
458 kmem_free(kmem_arena, (vm_offset_t)fake_fw.data, fu->img_fw_data_len);
459 break;
460 case MLX5_FW_RESET:
461 if ((fflag & FWRITE) == 0) {
462 error = EBADF;
463 break;
464 }
465 devaddr = (struct mlx5_tool_addr *)data;
466 error = mlx5_dbsf_to_core(devaddr, &mdev);
467 if (error != 0)
468 break;
469 error = mlx5_fw_reset(mdev);
470 break;
471 case MLX5_EEPROM_GET:
472 if ((fflag & FREAD) == 0) {
473 error = EBADF;
474 break;
475 }
476 eeprom_info = (struct mlx5_eeprom_get *)data;
477 devaddr = &eeprom_info->devaddr;
478 error = mlx5_dbsf_to_core(devaddr, &mdev);
479 if (error != 0)
480 break;
481 error = mlx5_eeprom_copyout(mdev, eeprom_info);
482 break;
483 default:
484 error = ENOTTY;
485 break;
486 }
487 return (error);
488 }
489
490 static struct cdevsw mlx5_ctl_devsw = {
491 .d_version = D_VERSION,
492 .d_ioctl = mlx5_ctl_ioctl,
493 };
494
495 static struct cdev *mlx5_ctl_dev;
496
497 int
mlx5_ctl_init(void)498 mlx5_ctl_init(void)
499 {
500 struct make_dev_args mda;
501 int error;
502
503 make_dev_args_init(&mda);
504 mda.mda_flags = MAKEDEV_WAITOK | MAKEDEV_CHECKNAME;
505 mda.mda_devsw = &mlx5_ctl_devsw;
506 mda.mda_uid = UID_ROOT;
507 mda.mda_gid = GID_OPERATOR;
508 mda.mda_mode = 0640;
509 error = make_dev_s(&mda, &mlx5_ctl_dev, "mlx5ctl");
510 return (-error);
511 }
512
513 void
mlx5_ctl_fini(void)514 mlx5_ctl_fini(void)
515 {
516
517 if (mlx5_ctl_dev != NULL)
518 destroy_dev(mlx5_ctl_dev);
519
520 }
521