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