]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/mlx5/mlx5_core/mlx5_fwdump.c
Read rege map from crdump scan space in mlx5core.
[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/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         device_t dev;
68         int error, vsc_addr;
69         unsigned i, sz;
70         u32 addr, in, out, next_addr;
71
72         mdev->dump_data = NULL;
73         error = mlx5_vsc_find_cap(mdev);
74         if (error != 0) {
75                 /* Inability to create a firmware dump is not fatal. */
76                 device_printf((&mdev->pdev->dev)->bsddev, "WARN: "
77                     "mlx5_fwdump_prep failed %d\n", error);
78                 return;
79         }
80         error = mlx5_vsc_lock(mdev);
81         if (error != 0)
82                 return;
83         error = mlx5_vsc_set_space(mdev, MLX5_VSC_DOMAIN_SCAN_CRSPACE);
84         if (error != 0) {
85                 mlx5_core_warn(mdev, "VSC scan space is not supported\n");
86                 goto unlock_vsc;
87         }
88         dev = mdev->pdev->dev.bsddev;
89         vsc_addr = mdev->vsc_addr;
90         if (vsc_addr == 0) {
91                 mlx5_core_warn(mdev, "Cannot read vsc, no address\n");
92                 goto unlock_vsc;
93         }
94
95         in = 0;
96         for (sz = 1, addr = 0;;) {
97                 MLX5_VSC_SET(vsc_addr, &in, address, addr);
98                 pci_write_config(dev, vsc_addr + MLX5_VSC_ADDR_OFFSET, in, 4);
99                 error = mlx5_vsc_wait_on_flag(mdev, 1);
100                 if (error != 0) {
101                         mlx5_core_warn(mdev,
102                     "Failed waiting for read complete flag, error %d\n", error);
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         mdev->dump_rege = malloc(sz * sizeof(struct mlx5_crspace_regmap),
115             M_MLX5_DUMP, M_WAITOK | M_ZERO);
116
117         for (i = 0, addr = 0;;) {
118                 MPASS(i < sz);
119                 mdev->dump_rege[i].cnt++;
120                 MLX5_VSC_SET(vsc_addr, &in, address, addr);
121                 pci_write_config(dev, vsc_addr + MLX5_VSC_ADDR_OFFSET, in, 4);
122                 error = mlx5_vsc_wait_on_flag(mdev, 1);
123                 if (error != 0) {
124                         mlx5_core_warn(mdev,
125                     "Failed waiting for read complete flag, error %d\n", error);
126                         free(mdev->dump_rege, M_MLX5_DUMP);
127                         mdev->dump_rege = NULL;
128                         goto unlock_vsc;
129                 }
130                 pci_read_config(dev, vsc_addr + MLX5_VSC_DATA_OFFSET, 4);
131                 out = pci_read_config(dev, vsc_addr + MLX5_VSC_ADDR_OFFSET, 4);
132                 next_addr = MLX5_VSC_GET(vsc_addr, &out, address);
133                 if (next_addr == 0 || next_addr == addr)
134                         break;
135                 if (next_addr != addr + 4)
136                         mdev->dump_rege[++i].addr = next_addr;
137                 addr = next_addr;
138         }
139         KASSERT(i + 1 == sz,
140             ("inconsistent hw crspace reads: sz %u i %u addr %#lx",
141             sz, i, (unsigned long)addr));
142
143         mdev->dump_size = mlx5_fwdump_getsize(mdev->dump_rege);
144         mdev->dump_data = malloc(mdev->dump_size * sizeof(uint32_t),
145             M_MLX5_DUMP, M_WAITOK | M_ZERO);
146         mdev->dump_valid = false;
147         mdev->dump_copyout = false;
148
149 unlock_vsc:
150         mlx5_vsc_unlock(mdev);
151 }
152
153 void
154 mlx5_fwdump(struct mlx5_core_dev *mdev)
155 {
156         const struct mlx5_crspace_regmap *r;
157         uint32_t i, ri;
158         int error;
159
160         dev_info(&mdev->pdev->dev, "Issuing FW dump\n");
161         mtx_lock(&mdev->dump_lock);
162         if (mdev->dump_data == NULL)
163                 goto failed;
164         if (mdev->dump_valid) {
165                 /* only one dump */
166                 dev_warn(&mdev->pdev->dev,
167                     "Only one FW dump can be captured aborting FW dump\n");
168                 goto failed;
169         }
170
171         /* mlx5_vsc already warns, be silent. */
172         error = mlx5_vsc_lock(mdev);
173         if (error != 0)
174                 goto failed;
175         error = mlx5_vsc_set_space(mdev, MLX5_VSC_DOMAIN_PROTECTED_CRSPACE);
176         if (error != 0)
177                 goto unlock_vsc;
178         for (i = 0, r = mdev->dump_rege; r->cnt != 0; r++) {
179                 for (ri = 0; ri < r->cnt; ri++) {
180                         error = mlx5_vsc_read(mdev, r->addr + ri * 4,
181                             &mdev->dump_data[i]);
182                         if (error != 0)
183                                 goto unlock_vsc;
184                         i++;
185                 }
186         }
187         mdev->dump_valid = true;
188 unlock_vsc:
189         mlx5_vsc_unlock(mdev);
190 failed:
191         mtx_unlock(&mdev->dump_lock);
192 }
193
194 void
195 mlx5_fwdump_clean(struct mlx5_core_dev *mdev)
196 {
197
198         mtx_lock(&mdev->dump_lock);
199         while (mdev->dump_copyout)
200                 msleep(&mdev->dump_copyout, &mdev->dump_lock, 0, "mlx5fwc", 0);
201         mlx5_fwdump_destroy_dd(mdev);
202         mtx_unlock(&mdev->dump_lock);
203         free(mdev->dump_rege, M_MLX5_DUMP);
204 }
205
206 static int
207 mlx5_fwdump_reset(struct mlx5_core_dev *mdev)
208 {
209         int error;
210
211         error = 0;
212         mtx_lock(&mdev->dump_lock);
213         if (mdev->dump_data != NULL) {
214                 while (mdev->dump_copyout) {
215                         msleep(&mdev->dump_copyout, &mdev->dump_lock,
216                             0, "mlx5fwr", 0);
217                 }
218                 mdev->dump_valid = false;
219         } else {
220                 error = ENOENT;
221         }
222         mtx_unlock(&mdev->dump_lock);
223         return (error);
224 }
225
226 static int
227 mlx5_dbsf_to_core(const struct mlx5_tool_addr *devaddr,
228     struct mlx5_core_dev **mdev)
229 {
230         device_t dev;
231         struct pci_dev *pdev;
232
233         dev = pci_find_dbsf(devaddr->domain, devaddr->bus, devaddr->slot,
234             devaddr->func);
235         if (dev == NULL)
236                 return (ENOENT);
237         if (device_get_devclass(dev) != mlx5_core_driver.bsdclass)
238                 return (EINVAL);
239         pdev = device_get_softc(dev);
240         *mdev = pci_get_drvdata(pdev);
241         if (*mdev == NULL)
242                 return (ENOENT);
243         return (0);
244 }
245
246 static int
247 mlx5_fwdump_copyout(struct mlx5_core_dev *mdev, struct mlx5_fwdump_get *fwg)
248 {
249         const struct mlx5_crspace_regmap *r;
250         struct mlx5_fwdump_reg rv, *urv;
251         uint32_t i, ri;
252         int error;
253
254         mtx_lock(&mdev->dump_lock);
255         if (mdev->dump_data == NULL) {
256                 mtx_unlock(&mdev->dump_lock);
257                 return (ENOENT);
258         }
259         if (fwg->buf == NULL) {
260                 fwg->reg_filled = mdev->dump_size;
261                 mtx_unlock(&mdev->dump_lock);
262                 return (0);
263         }
264         if (!mdev->dump_valid) {
265                 mtx_unlock(&mdev->dump_lock);
266                 return (ENOENT);
267         }
268         mdev->dump_copyout = true;
269         mtx_unlock(&mdev->dump_lock);
270
271         urv = fwg->buf;
272         for (i = 0, r = mdev->dump_rege; r->cnt != 0; r++) {
273                 for (ri = 0; ri < r->cnt; ri++) {
274                         if (i >= fwg->reg_cnt)
275                                 goto out;
276                         rv.addr = r->addr + ri * 4;
277                         rv.val = mdev->dump_data[i];
278                         error = copyout(&rv, urv, sizeof(rv));
279                         if (error != 0)
280                                 return (error);
281                         urv++;
282                         i++;
283                 }
284         }
285 out:
286         fwg->reg_filled = i;
287         mtx_lock(&mdev->dump_lock);
288         mdev->dump_copyout = false;
289         wakeup(&mdev->dump_copyout);
290         mtx_unlock(&mdev->dump_lock);
291         return (0);
292 }
293
294 static int
295 mlx5_fw_reset(struct mlx5_core_dev *mdev)
296 {
297         device_t dev, bus;
298         int error;
299
300         error = -mlx5_set_mfrl_reg(mdev, MLX5_FRL_LEVEL3);
301         if (error == 0) {
302                 dev = mdev->pdev->dev.bsddev;
303                 mtx_lock(&Giant);
304                 bus = device_get_parent(dev);
305                 error = BUS_RESET_CHILD(device_get_parent(bus), bus,
306                     DEVF_RESET_DETACH);
307                 mtx_unlock(&Giant);
308         }
309         return (error);
310 }
311
312 static int
313 mlx5_ctl_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag,
314     struct thread *td)
315 {
316         struct mlx5_core_dev *mdev;
317         struct mlx5_fwdump_get *fwg;
318         struct mlx5_tool_addr *devaddr;
319         struct mlx5_fw_update *fu;
320         struct firmware fake_fw;
321         int error;
322
323         error = 0;
324         switch (cmd) {
325         case MLX5_FWDUMP_GET:
326                 if ((fflag & FREAD) == 0) {
327                         error = EBADF;
328                         break;
329                 }
330                 fwg = (struct mlx5_fwdump_get *)data;
331                 devaddr = &fwg->devaddr;
332                 error = mlx5_dbsf_to_core(devaddr, &mdev);
333                 if (error != 0)
334                         break;
335                 error = mlx5_fwdump_copyout(mdev, fwg);
336                 break;
337         case MLX5_FWDUMP_RESET:
338                 if ((fflag & FWRITE) == 0) {
339                         error = EBADF;
340                         break;
341                 }
342                 devaddr = (struct mlx5_tool_addr *)data;
343                 error = mlx5_dbsf_to_core(devaddr, &mdev);
344                 if (error == 0)
345                         error = mlx5_fwdump_reset(mdev);
346                 break;
347         case MLX5_FWDUMP_FORCE:
348                 if ((fflag & FWRITE) == 0) {
349                         error = EBADF;
350                         break;
351                 }
352                 devaddr = (struct mlx5_tool_addr *)data;
353                 error = mlx5_dbsf_to_core(devaddr, &mdev);
354                 if (error != 0)
355                         break;
356                 mlx5_fwdump(mdev);
357                 break;
358         case MLX5_FW_UPDATE:
359                 if ((fflag & FWRITE) == 0) {
360                         error = EBADF;
361                         break;
362                 }
363                 fu = (struct mlx5_fw_update *)data;
364                 if (fu->img_fw_data_len > 10 * 1024 * 1024) {
365                         error = EINVAL;
366                         break;
367                 }
368                 devaddr = &fu->devaddr;
369                 error = mlx5_dbsf_to_core(devaddr, &mdev);
370                 if (error != 0)
371                         break;
372                 bzero(&fake_fw, sizeof(fake_fw));
373                 fake_fw.name = "umlx_fw_up";
374                 fake_fw.datasize = fu->img_fw_data_len;
375                 fake_fw.version = 1;
376                 fake_fw.data = (void *)kmem_malloc(fu->img_fw_data_len,
377                     M_WAITOK);
378                 if (fake_fw.data == NULL) {
379                         error = ENOMEM;
380                         break;
381                 }
382                 error = copyin(fu->img_fw_data, __DECONST(void *, fake_fw.data),
383                     fu->img_fw_data_len);
384                 if (error == 0)
385                         error = -mlx5_firmware_flash(mdev, &fake_fw);
386                 kmem_free((vm_offset_t)fake_fw.data, fu->img_fw_data_len);
387                 break;
388         case MLX5_FW_RESET:
389                 if ((fflag & FWRITE) == 0) {
390                         error = EBADF;
391                         break;
392                 }
393                 devaddr = (struct mlx5_tool_addr *)data;
394                 error = mlx5_dbsf_to_core(devaddr, &mdev);
395                 if (error != 0)
396                         break;
397                 error = mlx5_fw_reset(mdev);
398                 break;
399         default:
400                 error = ENOTTY;
401                 break;
402         }
403         return (error);
404 }
405
406 static struct cdevsw mlx5_ctl_devsw = {
407         .d_version =    D_VERSION,
408         .d_ioctl =      mlx5_ctl_ioctl,
409 };
410
411 static struct cdev *mlx5_ctl_dev;
412
413 int
414 mlx5_ctl_init(void)
415 {
416         struct make_dev_args mda;
417         int error;
418
419         make_dev_args_init(&mda);
420         mda.mda_flags = MAKEDEV_WAITOK | MAKEDEV_CHECKNAME;
421         mda.mda_devsw = &mlx5_ctl_devsw;
422         mda.mda_uid = UID_ROOT;
423         mda.mda_gid = GID_OPERATOR;
424         mda.mda_mode = 0640;
425         error = make_dev_s(&mda, &mlx5_ctl_dev, "mlx5ctl");
426         return (-error);
427 }
428
429 void
430 mlx5_ctl_fini(void)
431 {
432
433         if (mlx5_ctl_dev != NULL)
434                 destroy_dev(mlx5_ctl_dev);
435
436 }