GNU Linux-libre 4.19.245-gnu1
[releases.git] / drivers / net / ethernet / netronome / nfp / nfp_net_debugdump.c
1 /*
2  * Copyright (C) 2017 Netronome Systems, Inc.
3  *
4  * This software is dual licensed under the GNU General License Version 2,
5  * June 1991 as shown in the file COPYING in the top-level directory of this
6  * source tree or the BSD 2-Clause License provided below.  You have the
7  * option to license this software under the complete terms of either license.
8  *
9  * The BSD 2-Clause License:
10  *
11  *     Redistribution and use in source and binary forms, with or
12  *     without modification, are permitted provided that the following
13  *     conditions are met:
14  *
15  *      1. Redistributions of source code must retain the above
16  *         copyright notice, this list of conditions and the following
17  *         disclaimer.
18  *
19  *      2. Redistributions in binary form must reproduce the above
20  *         copyright notice, this list of conditions and the following
21  *         disclaimer in the documentation and/or other materials
22  *         provided with the distribution.
23  *
24  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
25  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
26  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
27  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
28  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
29  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
30  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
31  * SOFTWARE.
32  */
33
34 #include <linux/ethtool.h>
35 #include <linux/vmalloc.h>
36
37 #include "nfp_asm.h"
38 #include "nfp_main.h"
39 #include "nfpcore/nfp.h"
40 #include "nfpcore/nfp_nffw.h"
41 #include "nfpcore/nfp6000/nfp6000.h"
42
43 #define NFP_DUMP_SPEC_RTSYM     "_abi_dump_spec"
44
45 #define ALIGN8(x)       ALIGN(x, 8)
46
47 enum nfp_dumpspec_type {
48         NFP_DUMPSPEC_TYPE_CPP_CSR = 0,
49         NFP_DUMPSPEC_TYPE_XPB_CSR = 1,
50         NFP_DUMPSPEC_TYPE_ME_CSR = 2,
51         NFP_DUMPSPEC_TYPE_INDIRECT_ME_CSR = 3,
52         NFP_DUMPSPEC_TYPE_RTSYM = 4,
53         NFP_DUMPSPEC_TYPE_HWINFO = 5,
54         NFP_DUMPSPEC_TYPE_FWNAME = 6,
55         NFP_DUMPSPEC_TYPE_HWINFO_FIELD = 7,
56         NFP_DUMPSPEC_TYPE_PROLOG = 10000,
57         NFP_DUMPSPEC_TYPE_ERROR = 10001,
58 };
59
60 /* The following structs must be carefully aligned so that they can be used to
61  * interpret the binary dumpspec and populate the dump data in a deterministic
62  * way.
63  */
64
65 /* generic type plus length */
66 struct nfp_dump_tl {
67         __be32 type;
68         __be32 length;  /* chunk length to follow, aligned to 8 bytes */
69         char data[0];
70 };
71
72 /* NFP CPP parameters */
73 struct nfp_dumpspec_cpp_isl_id {
74         u8 target;
75         u8 action;
76         u8 token;
77         u8 island;
78 };
79
80 struct nfp_dump_common_cpp {
81         struct nfp_dumpspec_cpp_isl_id cpp_id;
82         __be32 offset;          /* address to start dump */
83         __be32 dump_length;     /* total bytes to dump, aligned to reg size */
84 };
85
86 /* CSR dumpables */
87 struct nfp_dumpspec_csr {
88         struct nfp_dump_tl tl;
89         struct nfp_dump_common_cpp cpp;
90         __be32 register_width;  /* in bits */
91 };
92
93 struct nfp_dumpspec_rtsym {
94         struct nfp_dump_tl tl;
95         char rtsym[0];
96 };
97
98 /* header for register dumpable */
99 struct nfp_dump_csr {
100         struct nfp_dump_tl tl;
101         struct nfp_dump_common_cpp cpp;
102         __be32 register_width;  /* in bits */
103         __be32 error;           /* error code encountered while reading */
104         __be32 error_offset;    /* offset being read when error occurred */
105 };
106
107 struct nfp_dump_rtsym {
108         struct nfp_dump_tl tl;
109         struct nfp_dump_common_cpp cpp;
110         __be32 error;           /* error code encountered while reading */
111         u8 padded_name_length;  /* pad so data starts at 8 byte boundary */
112         char rtsym[0];
113         /* after padded_name_length, there is dump_length data */
114 };
115
116 struct nfp_dump_prolog {
117         struct nfp_dump_tl tl;
118         __be32 dump_level;
119 };
120
121 struct nfp_dump_error {
122         struct nfp_dump_tl tl;
123         __be32 error;
124         char padding[4];
125         char spec[0];
126 };
127
128 /* to track state through debug size calculation TLV traversal */
129 struct nfp_level_size {
130         __be32 requested_level; /* input */
131         u32 total_size;         /* output */
132 };
133
134 /* to track state during debug dump creation TLV traversal */
135 struct nfp_dump_state {
136         __be32 requested_level; /* input param */
137         u32 dumped_size;        /* adds up to size of dumped data */
138         u32 buf_size;           /* size of buffer pointer to by p */
139         void *p;                /* current point in dump buffer */
140 };
141
142 typedef int (*nfp_tlv_visit)(struct nfp_pf *pf, struct nfp_dump_tl *tl,
143                              void *param);
144
145 static int
146 nfp_traverse_tlvs(struct nfp_pf *pf, void *data, u32 data_length, void *param,
147                   nfp_tlv_visit tlv_visit)
148 {
149         long long remaining = data_length;
150         struct nfp_dump_tl *tl;
151         u32 total_tlv_size;
152         void *p = data;
153         int err;
154
155         while (remaining >= sizeof(*tl)) {
156                 tl = p;
157                 if (!tl->type && !tl->length)
158                         break;
159
160                 if (be32_to_cpu(tl->length) > remaining - sizeof(*tl))
161                         return -EINVAL;
162
163                 total_tlv_size = sizeof(*tl) + be32_to_cpu(tl->length);
164
165                 /* Spec TLVs should be aligned to 4 bytes. */
166                 if (total_tlv_size % 4 != 0)
167                         return -EINVAL;
168
169                 p += total_tlv_size;
170                 remaining -= total_tlv_size;
171                 err = tlv_visit(pf, tl, param);
172                 if (err)
173                         return err;
174         }
175
176         return 0;
177 }
178
179 static u32 nfp_get_numeric_cpp_id(struct nfp_dumpspec_cpp_isl_id *cpp_id)
180 {
181         return NFP_CPP_ISLAND_ID(cpp_id->target, cpp_id->action, cpp_id->token,
182                                  cpp_id->island);
183 }
184
185 struct nfp_dumpspec *
186 nfp_net_dump_load_dumpspec(struct nfp_cpp *cpp, struct nfp_rtsym_table *rtbl)
187 {
188         const struct nfp_rtsym *specsym;
189         struct nfp_dumpspec *dumpspec;
190         int bytes_read;
191         u32 cpp_id;
192
193         specsym = nfp_rtsym_lookup(rtbl, NFP_DUMP_SPEC_RTSYM);
194         if (!specsym)
195                 return NULL;
196
197         /* expected size of this buffer is in the order of tens of kilobytes */
198         dumpspec = vmalloc(sizeof(*dumpspec) + specsym->size);
199         if (!dumpspec)
200                 return NULL;
201
202         dumpspec->size = specsym->size;
203
204         cpp_id = NFP_CPP_ISLAND_ID(specsym->target, NFP_CPP_ACTION_RW, 0,
205                                    specsym->domain);
206
207         bytes_read = nfp_cpp_read(cpp, cpp_id, specsym->addr, dumpspec->data,
208                                   specsym->size);
209         if (bytes_read != specsym->size) {
210                 vfree(dumpspec);
211                 nfp_warn(cpp, "Debug dump specification read failed.\n");
212                 return NULL;
213         }
214
215         return dumpspec;
216 }
217
218 static int nfp_dump_error_tlv_size(struct nfp_dump_tl *spec)
219 {
220         return ALIGN8(sizeof(struct nfp_dump_error) + sizeof(*spec) +
221                       be32_to_cpu(spec->length));
222 }
223
224 static int nfp_calc_fwname_tlv_size(struct nfp_pf *pf)
225 {
226         u32 fwname_len = strlen(nfp_mip_name(pf->mip));
227
228         return sizeof(struct nfp_dump_tl) + ALIGN8(fwname_len + 1);
229 }
230
231 static int nfp_calc_hwinfo_field_sz(struct nfp_pf *pf, struct nfp_dump_tl *spec)
232 {
233         u32 tl_len, key_len;
234         const char *value;
235
236         tl_len = be32_to_cpu(spec->length);
237         key_len = strnlen(spec->data, tl_len);
238         if (key_len == tl_len)
239                 return nfp_dump_error_tlv_size(spec);
240
241         value = nfp_hwinfo_lookup(pf->hwinfo, spec->data);
242         if (!value)
243                 return nfp_dump_error_tlv_size(spec);
244
245         return sizeof(struct nfp_dump_tl) + ALIGN8(key_len + strlen(value) + 2);
246 }
247
248 static bool nfp_csr_spec_valid(struct nfp_dumpspec_csr *spec_csr)
249 {
250         u32 required_read_sz = sizeof(*spec_csr) - sizeof(spec_csr->tl);
251         u32 available_sz = be32_to_cpu(spec_csr->tl.length);
252         u32 reg_width;
253
254         if (available_sz < required_read_sz)
255                 return false;
256
257         reg_width = be32_to_cpu(spec_csr->register_width);
258
259         return reg_width == 32 || reg_width == 64;
260 }
261
262 static int
263 nfp_calc_rtsym_dump_sz(struct nfp_pf *pf, struct nfp_dump_tl *spec)
264 {
265         struct nfp_rtsym_table *rtbl = pf->rtbl;
266         struct nfp_dumpspec_rtsym *spec_rtsym;
267         const struct nfp_rtsym *sym;
268         u32 tl_len, key_len;
269         u32 size;
270
271         spec_rtsym = (struct nfp_dumpspec_rtsym *)spec;
272         tl_len = be32_to_cpu(spec->length);
273         key_len = strnlen(spec_rtsym->rtsym, tl_len);
274         if (key_len == tl_len)
275                 return nfp_dump_error_tlv_size(spec);
276
277         sym = nfp_rtsym_lookup(rtbl, spec_rtsym->rtsym);
278         if (!sym)
279                 return nfp_dump_error_tlv_size(spec);
280
281         if (sym->type == NFP_RTSYM_TYPE_ABS)
282                 size = sizeof(sym->addr);
283         else
284                 size = sym->size;
285
286         return ALIGN8(offsetof(struct nfp_dump_rtsym, rtsym) + key_len + 1) +
287                ALIGN8(size);
288 }
289
290 static int
291 nfp_add_tlv_size(struct nfp_pf *pf, struct nfp_dump_tl *tl, void *param)
292 {
293         struct nfp_dumpspec_csr *spec_csr;
294         u32 *size = param;
295         u32 hwinfo_size;
296
297         switch (be32_to_cpu(tl->type)) {
298         case NFP_DUMPSPEC_TYPE_FWNAME:
299                 *size += nfp_calc_fwname_tlv_size(pf);
300                 break;
301         case NFP_DUMPSPEC_TYPE_CPP_CSR:
302         case NFP_DUMPSPEC_TYPE_XPB_CSR:
303         case NFP_DUMPSPEC_TYPE_ME_CSR:
304                 spec_csr = (struct nfp_dumpspec_csr *)tl;
305                 if (!nfp_csr_spec_valid(spec_csr))
306                         *size += nfp_dump_error_tlv_size(tl);
307                 else
308                         *size += ALIGN8(sizeof(struct nfp_dump_csr)) +
309                                  ALIGN8(be32_to_cpu(spec_csr->cpp.dump_length));
310                 break;
311         case NFP_DUMPSPEC_TYPE_INDIRECT_ME_CSR:
312                 spec_csr = (struct nfp_dumpspec_csr *)tl;
313                 if (!nfp_csr_spec_valid(spec_csr))
314                         *size += nfp_dump_error_tlv_size(tl);
315                 else
316                         *size += ALIGN8(sizeof(struct nfp_dump_csr)) +
317                                  ALIGN8(be32_to_cpu(spec_csr->cpp.dump_length) *
318                                         NFP_IND_NUM_CONTEXTS);
319                 break;
320         case NFP_DUMPSPEC_TYPE_RTSYM:
321                 *size += nfp_calc_rtsym_dump_sz(pf, tl);
322                 break;
323         case NFP_DUMPSPEC_TYPE_HWINFO:
324                 hwinfo_size = nfp_hwinfo_get_packed_str_size(pf->hwinfo);
325                 *size += sizeof(struct nfp_dump_tl) + ALIGN8(hwinfo_size);
326                 break;
327         case NFP_DUMPSPEC_TYPE_HWINFO_FIELD:
328                 *size += nfp_calc_hwinfo_field_sz(pf, tl);
329                 break;
330         default:
331                 *size += nfp_dump_error_tlv_size(tl);
332                 break;
333         }
334
335         return 0;
336 }
337
338 static int
339 nfp_calc_specific_level_size(struct nfp_pf *pf, struct nfp_dump_tl *dump_level,
340                              void *param)
341 {
342         struct nfp_level_size *lev_sz = param;
343
344         if (dump_level->type != lev_sz->requested_level)
345                 return 0;
346
347         return nfp_traverse_tlvs(pf, dump_level->data,
348                                  be32_to_cpu(dump_level->length),
349                                  &lev_sz->total_size, nfp_add_tlv_size);
350 }
351
352 s64 nfp_net_dump_calculate_size(struct nfp_pf *pf, struct nfp_dumpspec *spec,
353                                 u32 flag)
354 {
355         struct nfp_level_size lev_sz;
356         int err;
357
358         lev_sz.requested_level = cpu_to_be32(flag);
359         lev_sz.total_size = ALIGN8(sizeof(struct nfp_dump_prolog));
360
361         err = nfp_traverse_tlvs(pf, spec->data, spec->size, &lev_sz,
362                                 nfp_calc_specific_level_size);
363         if (err)
364                 return err;
365
366         return lev_sz.total_size;
367 }
368
369 static int nfp_add_tlv(u32 type, u32 total_tlv_sz, struct nfp_dump_state *dump)
370 {
371         struct nfp_dump_tl *tl = dump->p;
372
373         if (total_tlv_sz > dump->buf_size)
374                 return -ENOSPC;
375
376         if (dump->buf_size - total_tlv_sz < dump->dumped_size)
377                 return -ENOSPC;
378
379         tl->type = cpu_to_be32(type);
380         tl->length = cpu_to_be32(total_tlv_sz - sizeof(*tl));
381
382         dump->dumped_size += total_tlv_sz;
383         dump->p += total_tlv_sz;
384
385         return 0;
386 }
387
388 static int
389 nfp_dump_error_tlv(struct nfp_dump_tl *spec, int error,
390                    struct nfp_dump_state *dump)
391 {
392         struct nfp_dump_error *dump_header = dump->p;
393         u32 total_spec_size, total_size;
394         int err;
395
396         total_spec_size = sizeof(*spec) + be32_to_cpu(spec->length);
397         total_size = ALIGN8(sizeof(*dump_header) + total_spec_size);
398
399         err = nfp_add_tlv(NFP_DUMPSPEC_TYPE_ERROR, total_size, dump);
400         if (err)
401                 return err;
402
403         dump_header->error = cpu_to_be32(error);
404         memcpy(dump_header->spec, spec, total_spec_size);
405
406         return 0;
407 }
408
409 static int nfp_dump_fwname(struct nfp_pf *pf, struct nfp_dump_state *dump)
410 {
411         struct nfp_dump_tl *dump_header = dump->p;
412         u32 fwname_len, total_size;
413         const char *fwname;
414         int err;
415
416         fwname = nfp_mip_name(pf->mip);
417         fwname_len = strlen(fwname);
418         total_size = sizeof(*dump_header) + ALIGN8(fwname_len + 1);
419
420         err = nfp_add_tlv(NFP_DUMPSPEC_TYPE_FWNAME, total_size, dump);
421         if (err)
422                 return err;
423
424         memcpy(dump_header->data, fwname, fwname_len);
425
426         return 0;
427 }
428
429 static int
430 nfp_dump_hwinfo(struct nfp_pf *pf, struct nfp_dump_tl *spec,
431                 struct nfp_dump_state *dump)
432 {
433         struct nfp_dump_tl *dump_header = dump->p;
434         u32 hwinfo_size, total_size;
435         char *hwinfo;
436         int err;
437
438         hwinfo = nfp_hwinfo_get_packed_strings(pf->hwinfo);
439         hwinfo_size = nfp_hwinfo_get_packed_str_size(pf->hwinfo);
440         total_size = sizeof(*dump_header) + ALIGN8(hwinfo_size);
441
442         err = nfp_add_tlv(NFP_DUMPSPEC_TYPE_HWINFO, total_size, dump);
443         if (err)
444                 return err;
445
446         memcpy(dump_header->data, hwinfo, hwinfo_size);
447
448         return 0;
449 }
450
451 static int nfp_dump_hwinfo_field(struct nfp_pf *pf, struct nfp_dump_tl *spec,
452                                  struct nfp_dump_state *dump)
453 {
454         struct nfp_dump_tl *dump_header = dump->p;
455         u32 tl_len, key_len, val_len;
456         const char *key, *value;
457         u32 total_size;
458         int err;
459
460         tl_len = be32_to_cpu(spec->length);
461         key_len = strnlen(spec->data, tl_len);
462         if (key_len == tl_len)
463                 return nfp_dump_error_tlv(spec, -EINVAL, dump);
464
465         key = spec->data;
466         value = nfp_hwinfo_lookup(pf->hwinfo, key);
467         if (!value)
468                 return nfp_dump_error_tlv(spec, -ENOENT, dump);
469
470         val_len = strlen(value);
471         total_size = sizeof(*dump_header) + ALIGN8(key_len + val_len + 2);
472         err = nfp_add_tlv(NFP_DUMPSPEC_TYPE_HWINFO_FIELD, total_size, dump);
473         if (err)
474                 return err;
475
476         memcpy(dump_header->data, key, key_len + 1);
477         memcpy(dump_header->data + key_len + 1, value, val_len + 1);
478
479         return 0;
480 }
481
482 static bool is_xpb_read(struct nfp_dumpspec_cpp_isl_id *cpp_id)
483 {
484         return cpp_id->target == NFP_CPP_TARGET_ISLAND_XPB &&
485                cpp_id->action == 0 && cpp_id->token == 0;
486 }
487
488 static int
489 nfp_dump_csr_range(struct nfp_pf *pf, struct nfp_dumpspec_csr *spec_csr,
490                    struct nfp_dump_state *dump)
491 {
492         struct nfp_dump_csr *dump_header = dump->p;
493         u32 reg_sz, header_size, total_size;
494         u32 cpp_rd_addr, max_rd_addr;
495         int bytes_read;
496         void *dest;
497         u32 cpp_id;
498         int err;
499
500         if (!nfp_csr_spec_valid(spec_csr))
501                 return nfp_dump_error_tlv(&spec_csr->tl, -EINVAL, dump);
502
503         reg_sz = be32_to_cpu(spec_csr->register_width) / BITS_PER_BYTE;
504         header_size = ALIGN8(sizeof(*dump_header));
505         total_size = header_size +
506                      ALIGN8(be32_to_cpu(spec_csr->cpp.dump_length));
507         dest = dump->p + header_size;
508
509         err = nfp_add_tlv(be32_to_cpu(spec_csr->tl.type), total_size, dump);
510         if (err)
511                 return err;
512
513         dump_header->cpp = spec_csr->cpp;
514         dump_header->register_width = spec_csr->register_width;
515
516         cpp_id = nfp_get_numeric_cpp_id(&spec_csr->cpp.cpp_id);
517         cpp_rd_addr = be32_to_cpu(spec_csr->cpp.offset);
518         max_rd_addr = cpp_rd_addr + be32_to_cpu(spec_csr->cpp.dump_length);
519
520         while (cpp_rd_addr < max_rd_addr) {
521                 if (is_xpb_read(&spec_csr->cpp.cpp_id)) {
522                         err = nfp_xpb_readl(pf->cpp, cpp_rd_addr, (u32 *)dest);
523                 } else {
524                         bytes_read = nfp_cpp_read(pf->cpp, cpp_id, cpp_rd_addr,
525                                                   dest, reg_sz);
526                         err = bytes_read == reg_sz ? 0 : -EIO;
527                 }
528                 if (err) {
529                         dump_header->error = cpu_to_be32(err);
530                         dump_header->error_offset = cpu_to_be32(cpp_rd_addr);
531                         break;
532                 }
533                 cpp_rd_addr += reg_sz;
534                 dest += reg_sz;
535         }
536
537         return 0;
538 }
539
540 /* Write context to CSRCtxPtr, then read from it. Then the value can be read
541  * from IndCtxStatus.
542  */
543 static int
544 nfp_read_indirect_csr(struct nfp_cpp *cpp,
545                       struct nfp_dumpspec_cpp_isl_id cpp_params, u32 offset,
546                       u32 reg_sz, u32 context, void *dest)
547 {
548         u32 csr_ctx_ptr_offs;
549         u32 cpp_id;
550         int result;
551
552         csr_ctx_ptr_offs = nfp_get_ind_csr_ctx_ptr_offs(offset);
553         cpp_id = NFP_CPP_ISLAND_ID(cpp_params.target,
554                                    NFP_IND_ME_REFL_WR_SIG_INIT,
555                                    cpp_params.token, cpp_params.island);
556         result = nfp_cpp_writel(cpp, cpp_id, csr_ctx_ptr_offs, context);
557         if (result)
558                 return result;
559
560         cpp_id = nfp_get_numeric_cpp_id(&cpp_params);
561         result = nfp_cpp_read(cpp, cpp_id, csr_ctx_ptr_offs, dest, reg_sz);
562         if (result != reg_sz)
563                 return result < 0 ? result : -EIO;
564
565         result = nfp_cpp_read(cpp, cpp_id, offset, dest, reg_sz);
566         if (result != reg_sz)
567                 return result < 0 ? result : -EIO;
568
569         return 0;
570 }
571
572 static int
573 nfp_read_all_indirect_csr_ctx(struct nfp_cpp *cpp,
574                               struct nfp_dumpspec_csr *spec_csr, u32 address,
575                               u32 reg_sz, void *dest)
576 {
577         u32 ctx;
578         int err;
579
580         for (ctx = 0; ctx < NFP_IND_NUM_CONTEXTS; ctx++) {
581                 err = nfp_read_indirect_csr(cpp, spec_csr->cpp.cpp_id, address,
582                                             reg_sz, ctx, dest + ctx * reg_sz);
583                 if (err)
584                         return err;
585         }
586
587         return 0;
588 }
589
590 static int
591 nfp_dump_indirect_csr_range(struct nfp_pf *pf,
592                             struct nfp_dumpspec_csr *spec_csr,
593                             struct nfp_dump_state *dump)
594 {
595         struct nfp_dump_csr *dump_header = dump->p;
596         u32 reg_sz, header_size, total_size;
597         u32 cpp_rd_addr, max_rd_addr;
598         u32 reg_data_length;
599         void *dest;
600         int err;
601
602         if (!nfp_csr_spec_valid(spec_csr))
603                 return nfp_dump_error_tlv(&spec_csr->tl, -EINVAL, dump);
604
605         reg_sz = be32_to_cpu(spec_csr->register_width) / BITS_PER_BYTE;
606         header_size = ALIGN8(sizeof(*dump_header));
607         reg_data_length = be32_to_cpu(spec_csr->cpp.dump_length) *
608                           NFP_IND_NUM_CONTEXTS;
609         total_size = header_size + ALIGN8(reg_data_length);
610         dest = dump->p + header_size;
611
612         err = nfp_add_tlv(be32_to_cpu(spec_csr->tl.type), total_size, dump);
613         if (err)
614                 return err;
615
616         dump_header->cpp = spec_csr->cpp;
617         dump_header->register_width = spec_csr->register_width;
618
619         cpp_rd_addr = be32_to_cpu(spec_csr->cpp.offset);
620         max_rd_addr = cpp_rd_addr + be32_to_cpu(spec_csr->cpp.dump_length);
621         while (cpp_rd_addr < max_rd_addr) {
622                 err = nfp_read_all_indirect_csr_ctx(pf->cpp, spec_csr,
623                                                     cpp_rd_addr, reg_sz, dest);
624                 if (err) {
625                         dump_header->error = cpu_to_be32(err);
626                         dump_header->error_offset = cpu_to_be32(cpp_rd_addr);
627                         break;
628                 }
629                 cpp_rd_addr += reg_sz;
630                 dest += reg_sz * NFP_IND_NUM_CONTEXTS;
631         }
632
633         return 0;
634 }
635
636 static int
637 nfp_dump_single_rtsym(struct nfp_pf *pf, struct nfp_dumpspec_rtsym *spec,
638                       struct nfp_dump_state *dump)
639 {
640         struct nfp_dump_rtsym *dump_header = dump->p;
641         struct nfp_dumpspec_cpp_isl_id cpp_params;
642         struct nfp_rtsym_table *rtbl = pf->rtbl;
643         u32 header_size, total_size, sym_size;
644         const struct nfp_rtsym *sym;
645         u32 tl_len, key_len;
646         int bytes_read;
647         u32 cpp_id;
648         void *dest;
649         int err;
650
651         tl_len = be32_to_cpu(spec->tl.length);
652         key_len = strnlen(spec->rtsym, tl_len);
653         if (key_len == tl_len)
654                 return nfp_dump_error_tlv(&spec->tl, -EINVAL, dump);
655
656         sym = nfp_rtsym_lookup(rtbl, spec->rtsym);
657         if (!sym)
658                 return nfp_dump_error_tlv(&spec->tl, -ENOENT, dump);
659
660         if (sym->type == NFP_RTSYM_TYPE_ABS)
661                 sym_size = sizeof(sym->addr);
662         else
663                 sym_size = sym->size;
664
665         header_size =
666                 ALIGN8(offsetof(struct nfp_dump_rtsym, rtsym) + key_len + 1);
667         total_size = header_size + ALIGN8(sym_size);
668         dest = dump->p + header_size;
669
670         err = nfp_add_tlv(be32_to_cpu(spec->tl.type), total_size, dump);
671         if (err)
672                 return err;
673
674         dump_header->padded_name_length =
675                 header_size - offsetof(struct nfp_dump_rtsym, rtsym);
676         memcpy(dump_header->rtsym, spec->rtsym, key_len + 1);
677         dump_header->cpp.dump_length = cpu_to_be32(sym_size);
678
679         if (sym->type == NFP_RTSYM_TYPE_ABS) {
680                 *(u64 *)dest = sym->addr;
681         } else {
682                 cpp_params.target = sym->target;
683                 cpp_params.action = NFP_CPP_ACTION_RW;
684                 cpp_params.token  = 0;
685                 cpp_params.island = sym->domain;
686                 cpp_id = nfp_get_numeric_cpp_id(&cpp_params);
687                 dump_header->cpp.cpp_id = cpp_params;
688                 dump_header->cpp.offset = cpu_to_be32(sym->addr);
689                 bytes_read = nfp_cpp_read(pf->cpp, cpp_id, sym->addr, dest,
690                                           sym_size);
691                 if (bytes_read != sym_size) {
692                         if (bytes_read >= 0)
693                                 bytes_read = -EIO;
694                         dump_header->error = cpu_to_be32(bytes_read);
695                 }
696         }
697
698         return 0;
699 }
700
701 static int
702 nfp_dump_for_tlv(struct nfp_pf *pf, struct nfp_dump_tl *tl, void *param)
703 {
704         struct nfp_dumpspec_rtsym *spec_rtsym;
705         struct nfp_dump_state *dump = param;
706         struct nfp_dumpspec_csr *spec_csr;
707         int err;
708
709         switch (be32_to_cpu(tl->type)) {
710         case NFP_DUMPSPEC_TYPE_FWNAME:
711                 err = nfp_dump_fwname(pf, dump);
712                 if (err)
713                         return err;
714                 break;
715         case NFP_DUMPSPEC_TYPE_CPP_CSR:
716         case NFP_DUMPSPEC_TYPE_XPB_CSR:
717         case NFP_DUMPSPEC_TYPE_ME_CSR:
718                 spec_csr = (struct nfp_dumpspec_csr *)tl;
719                 err = nfp_dump_csr_range(pf, spec_csr, dump);
720                 if (err)
721                         return err;
722                 break;
723         case NFP_DUMPSPEC_TYPE_INDIRECT_ME_CSR:
724                 spec_csr = (struct nfp_dumpspec_csr *)tl;
725                 err = nfp_dump_indirect_csr_range(pf, spec_csr, dump);
726                 if (err)
727                         return err;
728                 break;
729         case NFP_DUMPSPEC_TYPE_RTSYM:
730                 spec_rtsym = (struct nfp_dumpspec_rtsym *)tl;
731                 err = nfp_dump_single_rtsym(pf, spec_rtsym, dump);
732                 if (err)
733                         return err;
734                 break;
735         case NFP_DUMPSPEC_TYPE_HWINFO:
736                 err = nfp_dump_hwinfo(pf, tl, dump);
737                 if (err)
738                         return err;
739                 break;
740         case NFP_DUMPSPEC_TYPE_HWINFO_FIELD:
741                 err = nfp_dump_hwinfo_field(pf, tl, dump);
742                 if (err)
743                         return err;
744                 break;
745         default:
746                 err = nfp_dump_error_tlv(tl, -EOPNOTSUPP, dump);
747                 if (err)
748                         return err;
749         }
750
751         return 0;
752 }
753
754 static int
755 nfp_dump_specific_level(struct nfp_pf *pf, struct nfp_dump_tl *dump_level,
756                         void *param)
757 {
758         struct nfp_dump_state *dump = param;
759
760         if (dump_level->type != dump->requested_level)
761                 return 0;
762
763         return nfp_traverse_tlvs(pf, dump_level->data,
764                                  be32_to_cpu(dump_level->length), dump,
765                                  nfp_dump_for_tlv);
766 }
767
768 static int nfp_dump_populate_prolog(struct nfp_dump_state *dump)
769 {
770         struct nfp_dump_prolog *prolog = dump->p;
771         u32 total_size;
772         int err;
773
774         total_size = ALIGN8(sizeof(*prolog));
775
776         err = nfp_add_tlv(NFP_DUMPSPEC_TYPE_PROLOG, total_size, dump);
777         if (err)
778                 return err;
779
780         prolog->dump_level = dump->requested_level;
781
782         return 0;
783 }
784
785 int nfp_net_dump_populate_buffer(struct nfp_pf *pf, struct nfp_dumpspec *spec,
786                                  struct ethtool_dump *dump_param, void *dest)
787 {
788         struct nfp_dump_state dump;
789         int err;
790
791         dump.requested_level = cpu_to_be32(dump_param->flag);
792         dump.dumped_size = 0;
793         dump.p = dest;
794         dump.buf_size = dump_param->len;
795
796         err = nfp_dump_populate_prolog(&dump);
797         if (err)
798                 return err;
799
800         err = nfp_traverse_tlvs(pf, spec->data, spec->size, &dump,
801                                 nfp_dump_specific_level);
802         if (err)
803                 return err;
804
805         /* Set size of actual dump, to trigger warning if different from
806          * calculated size.
807          */
808         dump_param->len = dump.dumped_size;
809
810         return 0;
811 }