]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/mlx5/mlx5_core/mlx5_fwdump.c
MFV r359442: bmake: import -fno-common fix build back from upstream
[FreeBSD/FreeBSD.git] / sys / dev / mlx5 / mlx5_core / mlx5_fwdump.c
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 }