]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/mlx5/mlx5_core/mlx5_fwdump.c
MFC r347323:
[FreeBSD/FreeBSD.git] / sys / dev / mlx5 / mlx5_core / mlx5_fwdump.c
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 static MALLOC_DEFINE(M_MLX5_DUMP, "MLX5DUMP", "MLX5 Firmware dump");
43
44 static unsigned
45 mlx5_fwdump_getsize(const struct mlx5_crspace_regmap *rege)
46 {
47         const struct mlx5_crspace_regmap *r;
48         unsigned sz;
49
50         for (sz = 0, r = rege; r->cnt != 0; r++)
51                 sz += r->cnt;
52         return (sz);
53 }
54
55 static void
56 mlx5_fwdump_destroy_dd(struct mlx5_core_dev *mdev)
57 {
58
59         mtx_assert(&mdev->dump_lock, MA_OWNED);
60         free(mdev->dump_data, M_MLX5_DUMP);
61         mdev->dump_data = NULL;
62 }
63
64 void
65 mlx5_fwdump_prep(struct mlx5_core_dev *mdev)
66 {
67         int error;
68
69         mdev->dump_data = NULL;
70         error = mlx5_vsc_find_cap(mdev);
71         if (error != 0) {
72                 /* Inability to create a firmware dump is not fatal. */
73                 device_printf((&mdev->pdev->dev)->bsddev, "WARN: "
74                     "mlx5_fwdump_prep failed %d\n", error);
75                 return;
76         }
77         switch (pci_get_device(mdev->pdev->dev.bsddev)) {
78         case 0x1013:
79                 mdev->dump_rege = mlx5_crspace_regmap_mt4115;
80                 break;
81         case 0x1015:
82                 mdev->dump_rege = mlx5_crspace_regmap_mt4117;
83                 break;
84         case 0x1017:
85         case 0x1019:
86                 mdev->dump_rege = mlx5_crspace_regmap_connectx5;
87                 break;
88         default:
89                 return; /* silently fail, do not prevent driver attach */
90         }
91         mdev->dump_size = mlx5_fwdump_getsize(mdev->dump_rege);
92         mdev->dump_data = malloc(mdev->dump_size * sizeof(uint32_t),
93             M_MLX5_DUMP, M_WAITOK | M_ZERO);
94         mdev->dump_valid = false;
95         mdev->dump_copyout = false;
96 }
97
98 void
99 mlx5_fwdump(struct mlx5_core_dev *mdev)
100 {
101         const struct mlx5_crspace_regmap *r;
102         uint32_t i, ri;
103         int error;
104
105         dev_info(&mdev->pdev->dev, "Issuing FW dump\n");
106         mtx_lock(&mdev->dump_lock);
107         if (mdev->dump_data == NULL)
108                 goto failed;
109         if (mdev->dump_valid) {
110                 /* only one dump */
111                 dev_warn(&mdev->pdev->dev,
112                     "Only one FW dump can be captured aborting FW dump\n");
113                 goto failed;
114         }
115
116         /* mlx5_vsc already warns, be silent. */
117         error = mlx5_vsc_lock(mdev);
118         if (error != 0)
119                 goto failed;
120         error = mlx5_vsc_set_space(mdev, MLX5_VSC_DOMAIN_PROTECTED_CRSPACE);
121         if (error != 0)
122                 goto unlock_vsc;
123         for (i = 0, r = mdev->dump_rege; r->cnt != 0; r++) {
124                 for (ri = 0; ri < r->cnt; ri++) {
125                         error = mlx5_vsc_read(mdev, r->addr + ri * 4,
126                             &mdev->dump_data[i]);
127                         if (error != 0)
128                                 goto unlock_vsc;
129                         i++;
130                 }
131         }
132         mdev->dump_valid = true;
133 unlock_vsc:
134         mlx5_vsc_unlock(mdev);
135 failed:
136         mtx_unlock(&mdev->dump_lock);
137 }
138
139 void
140 mlx5_fwdump_clean(struct mlx5_core_dev *mdev)
141 {
142
143         mtx_lock(&mdev->dump_lock);
144         while (mdev->dump_copyout)
145                 msleep(&mdev->dump_copyout, &mdev->dump_lock, 0, "mlx5fwc", 0);
146         mlx5_fwdump_destroy_dd(mdev);
147         mtx_unlock(&mdev->dump_lock);
148 }
149
150 static int
151 mlx5_fwdump_reset(struct mlx5_core_dev *mdev)
152 {
153         int error;
154
155         error = 0;
156         mtx_lock(&mdev->dump_lock);
157         if (mdev->dump_data != NULL) {
158                 while (mdev->dump_copyout) {
159                         msleep(&mdev->dump_copyout, &mdev->dump_lock,
160                             0, "mlx5fwr", 0);
161                 }
162                 mdev->dump_valid = false;
163         } else {
164                 error = ENOENT;
165         }
166         mtx_unlock(&mdev->dump_lock);
167         return (error);
168 }
169
170 static int
171 mlx5_dbsf_to_core(const struct mlx5_tool_addr *devaddr,
172     struct mlx5_core_dev **mdev)
173 {
174         device_t dev;
175         struct pci_dev *pdev;
176
177         dev = pci_find_dbsf(devaddr->domain, devaddr->bus, devaddr->slot,
178             devaddr->func);
179         if (dev == NULL)
180                 return (ENOENT);
181         if (device_get_devclass(dev) != mlx5_core_driver.bsdclass)
182                 return (EINVAL);
183         pdev = device_get_softc(dev);
184         *mdev = pci_get_drvdata(pdev);
185         if (*mdev == NULL)
186                 return (ENOENT);
187         return (0);
188 }
189
190 static int
191 mlx5_fwdump_copyout(struct mlx5_core_dev *mdev, struct mlx5_fwdump_get *fwg)
192 {
193         const struct mlx5_crspace_regmap *r;
194         struct mlx5_fwdump_reg rv, *urv;
195         uint32_t i, ri;
196         int error;
197
198         mtx_lock(&mdev->dump_lock);
199         if (mdev->dump_data == NULL) {
200                 mtx_unlock(&mdev->dump_lock);
201                 return (ENOENT);
202         }
203         if (fwg->buf == NULL) {
204                 fwg->reg_filled = mdev->dump_size;
205                 mtx_unlock(&mdev->dump_lock);
206                 return (0);
207         }
208         if (!mdev->dump_valid) {
209                 mtx_unlock(&mdev->dump_lock);
210                 return (ENOENT);
211         }
212         mdev->dump_copyout = true;
213         mtx_unlock(&mdev->dump_lock);
214
215         urv = fwg->buf;
216         for (i = 0, r = mdev->dump_rege; r->cnt != 0; r++) {
217                 for (ri = 0; ri < r->cnt; ri++) {
218                         if (i >= fwg->reg_cnt)
219                                 goto out;
220                         rv.addr = r->addr + ri * 4;
221                         rv.val = mdev->dump_data[i];
222                         error = copyout(&rv, urv, sizeof(rv));
223                         if (error != 0)
224                                 return (error);
225                         urv++;
226                         i++;
227                 }
228         }
229 out:
230         fwg->reg_filled = i;
231         mtx_lock(&mdev->dump_lock);
232         mdev->dump_copyout = false;
233         wakeup(&mdev->dump_copyout);
234         mtx_unlock(&mdev->dump_lock);
235         return (0);
236 }
237
238 static int
239 mlx5_fw_reset(struct mlx5_core_dev *mdev)
240 {
241         device_t dev, bus;
242         int error;
243
244         error = -mlx5_set_mfrl_reg(mdev, MLX5_FRL_LEVEL3);
245         if (error == 0) {
246                 dev = mdev->pdev->dev.bsddev;
247                 mtx_lock(&Giant);
248                 bus = device_get_parent(dev);
249                 error = BUS_RESET_CHILD(device_get_parent(bus), bus,
250                     DEVF_RESET_DETACH);
251                 mtx_unlock(&Giant);
252         }
253         return (error);
254 }
255
256 static int
257 mlx5_ctl_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag,
258     struct thread *td)
259 {
260         struct mlx5_core_dev *mdev;
261         struct mlx5_fwdump_get *fwg;
262         struct mlx5_tool_addr *devaddr;
263         struct mlx5_fw_update *fu;
264         struct firmware fake_fw;
265         int error;
266
267         error = 0;
268         switch (cmd) {
269         case MLX5_FWDUMP_GET:
270                 if ((fflag & FREAD) == 0) {
271                         error = EBADF;
272                         break;
273                 }
274                 fwg = (struct mlx5_fwdump_get *)data;
275                 devaddr = &fwg->devaddr;
276                 error = mlx5_dbsf_to_core(devaddr, &mdev);
277                 if (error != 0)
278                         break;
279                 error = mlx5_fwdump_copyout(mdev, fwg);
280                 break;
281         case MLX5_FWDUMP_RESET:
282                 if ((fflag & FWRITE) == 0) {
283                         error = EBADF;
284                         break;
285                 }
286                 devaddr = (struct mlx5_tool_addr *)data;
287                 error = mlx5_dbsf_to_core(devaddr, &mdev);
288                 if (error == 0)
289                         error = mlx5_fwdump_reset(mdev);
290                 break;
291         case MLX5_FWDUMP_FORCE:
292                 if ((fflag & FWRITE) == 0) {
293                         error = EBADF;
294                         break;
295                 }
296                 devaddr = (struct mlx5_tool_addr *)data;
297                 error = mlx5_dbsf_to_core(devaddr, &mdev);
298                 if (error != 0)
299                         break;
300                 mlx5_fwdump(mdev);
301                 break;
302         case MLX5_FW_UPDATE:
303                 if ((fflag & FWRITE) == 0) {
304                         error = EBADF;
305                         break;
306                 }
307                 fu = (struct mlx5_fw_update *)data;
308                 if (fu->img_fw_data_len > 10 * 1024 * 1024) {
309                         error = EINVAL;
310                         break;
311                 }
312                 devaddr = &fu->devaddr;
313                 error = mlx5_dbsf_to_core(devaddr, &mdev);
314                 if (error != 0)
315                         break;
316                 bzero(&fake_fw, sizeof(fake_fw));
317                 fake_fw.name = "umlx_fw_up";
318                 fake_fw.datasize = fu->img_fw_data_len;
319                 fake_fw.version = 1;
320                 fake_fw.data = (void *)kmem_malloc(fu->img_fw_data_len,
321                     M_WAITOK);
322                 if (fake_fw.data == NULL) {
323                         error = ENOMEM;
324                         break;
325                 }
326                 error = copyin(fu->img_fw_data, __DECONST(void *, fake_fw.data),
327                     fu->img_fw_data_len);
328                 if (error == 0)
329                         error = -mlx5_firmware_flash(mdev, &fake_fw);
330                 kmem_free((vm_offset_t)fake_fw.data, fu->img_fw_data_len);
331                 break;
332         case MLX5_FW_RESET:
333                 if ((fflag & FWRITE) == 0) {
334                         error = EBADF;
335                         break;
336                 }
337                 devaddr = (struct mlx5_tool_addr *)data;
338                 error = mlx5_dbsf_to_core(devaddr, &mdev);
339                 if (error != 0)
340                         break;
341                 error = mlx5_fw_reset(mdev);
342                 break;
343         default:
344                 error = ENOTTY;
345                 break;
346         }
347         return (error);
348 }
349
350 static struct cdevsw mlx5_ctl_devsw = {
351         .d_version =    D_VERSION,
352         .d_ioctl =      mlx5_ctl_ioctl,
353 };
354
355 static struct cdev *mlx5_ctl_dev;
356
357 int
358 mlx5_ctl_init(void)
359 {
360         struct make_dev_args mda;
361         int error;
362
363         make_dev_args_init(&mda);
364         mda.mda_flags = MAKEDEV_WAITOK | MAKEDEV_CHECKNAME;
365         mda.mda_devsw = &mlx5_ctl_devsw;
366         mda.mda_uid = UID_ROOT;
367         mda.mda_gid = GID_OPERATOR;
368         mda.mda_mode = 0640;
369         error = make_dev_s(&mda, &mlx5_ctl_dev, "mlx5ctl");
370         return (-error);
371 }
372
373 void
374 mlx5_ctl_fini(void)
375 {
376
377         if (mlx5_ctl_dev != NULL)
378                 destroy_dev(mlx5_ctl_dev);
379
380 }