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

source code of linux/drivers/net/ethernet/netronome/nfp/nfp_net_debugdump.c