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
17 enum 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 */
36 struct 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 */
43 struct nfp_dumpspec_cpp_isl_id {
44 u8 target;
45 u8 action;
46 u8 token;
47 u8 island;
48 };
49
50 struct 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 */
57 struct nfp_dumpspec_csr {
58 struct nfp_dump_tl tl;
59 struct nfp_dump_common_cpp cpp;
60 __be32 register_width; /* in bits */
61 };
62
63 struct nfp_dumpspec_rtsym {
64 struct nfp_dump_tl tl;
65 char rtsym[];
66 };
67
68 /* header for register dumpable */
69 struct 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
77 struct 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
86 struct nfp_dump_prolog {
87 struct nfp_dump_tl tl;
88 __be32 dump_level;
89 };
90
91 struct 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 */
99 struct 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 */
105 struct 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
112 typedef int (*nfp_tlv_visit)(struct nfp_pf *pf, struct nfp_dump_tl *tl,
113 void *param);
114
115 static int
nfp_traverse_tlvs(struct nfp_pf * pf,void * data,u32 data_length,void * param,nfp_tlv_visit tlv_visit)116 nfp_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
nfp_get_numeric_cpp_id(struct nfp_dumpspec_cpp_isl_id * cpp_id)149 static 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
155 struct nfp_dumpspec *
nfp_net_dump_load_dumpspec(struct nfp_cpp * cpp,struct nfp_rtsym_table * rtbl)156 nfp_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(specsym);
167
168 /* expected size of this buffer is in the order of tens of kilobytes */
169 dumpspec = vmalloc(sizeof(*dumpspec) + sym_size);
170 if (!dumpspec)
171 return NULL;
172 dumpspec->size = sym_size;
173
174 bytes_read = nfp_rtsym_read(cpp, specsym, 0, dumpspec->data, sym_size);
175 if (bytes_read != sym_size) {
176 vfree(dumpspec);
177 nfp_warn(cpp, "Debug dump specification read failed.\n");
178 return NULL;
179 }
180
181 return dumpspec;
182 }
183
nfp_dump_error_tlv_size(struct nfp_dump_tl * spec)184 static 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
nfp_calc_fwname_tlv_size(struct nfp_pf * pf)190 static 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
nfp_calc_hwinfo_field_sz(struct nfp_pf * pf,struct nfp_dump_tl * spec)197 static 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(spec->data, tl_len);
204 if (key_len == tl_len)
205 return nfp_dump_error_tlv_size(spec);
206
207 value = nfp_hwinfo_lookup(pf->hwinfo, 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
nfp_csr_spec_valid(struct nfp_dumpspec_csr * spec_csr)214 static 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
228 static int
nfp_calc_rtsym_dump_sz(struct nfp_pf * pf,struct nfp_dump_tl * spec)229 nfp_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(spec_rtsym->rtsym, tl_len);
239 if (key_len == tl_len)
240 return nfp_dump_error_tlv_size(spec);
241
242 sym = nfp_rtsym_lookup(rtbl, 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
250 static int
nfp_add_tlv_size(struct nfp_pf * pf,struct nfp_dump_tl * tl,void * param)251 nfp_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(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(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, tl);
282 break;
283 case NFP_DUMPSPEC_TYPE_HWINFO:
284 hwinfo_size = nfp_hwinfo_get_packed_str_size(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, tl);
289 break;
290 default:
291 *size += nfp_dump_error_tlv_size(tl);
292 break;
293 }
294
295 return 0;
296 }
297
298 static int
nfp_calc_specific_level_size(struct nfp_pf * pf,struct nfp_dump_tl * dump_level,void * param)299 nfp_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, dump_level->data,
308 be32_to_cpu(dump_level->length),
309 &lev_sz->total_size, nfp_add_tlv_size);
310 }
311
nfp_net_dump_calculate_size(struct nfp_pf * pf,struct nfp_dumpspec * spec,u32 flag)312 s64 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, spec->data, spec->size, &lev_sz,
322 nfp_calc_specific_level_size);
323 if (err)
324 return err;
325
326 return lev_sz.total_size;
327 }
328
nfp_add_tlv(u32 type,u32 total_tlv_sz,struct nfp_dump_state * dump)329 static 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
348 static int
nfp_dump_error_tlv(struct nfp_dump_tl * spec,int error,struct nfp_dump_state * dump)349 nfp_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(NFP_DUMPSPEC_TYPE_ERROR, 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
nfp_dump_fwname(struct nfp_pf * pf,struct nfp_dump_state * dump)369 static 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(pf->mip);
377 fwname_len = strlen(fwname);
378 total_size = sizeof(*dump_header) + ALIGN8(fwname_len + 1);
379
380 err = nfp_add_tlv(NFP_DUMPSPEC_TYPE_FWNAME, total_size, dump);
381 if (err)
382 return err;
383
384 memcpy(dump_header->data, fwname, fwname_len);
385
386 return 0;
387 }
388
389 static int
nfp_dump_hwinfo(struct nfp_pf * pf,struct nfp_dump_tl * spec,struct nfp_dump_state * dump)390 nfp_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(pf->hwinfo);
399 hwinfo_size = nfp_hwinfo_get_packed_str_size(pf->hwinfo);
400 total_size = sizeof(*dump_header) + ALIGN8(hwinfo_size);
401
402 err = nfp_add_tlv(NFP_DUMPSPEC_TYPE_HWINFO, total_size, dump);
403 if (err)
404 return err;
405
406 memcpy(dump_header->data, hwinfo, hwinfo_size);
407
408 return 0;
409 }
410
nfp_dump_hwinfo_field(struct nfp_pf * pf,struct nfp_dump_tl * spec,struct nfp_dump_state * dump)411 static 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(spec->data, tl_len);
422 if (key_len == tl_len)
423 return nfp_dump_error_tlv(spec, -EINVAL, dump);
424
425 key = spec->data;
426 value = nfp_hwinfo_lookup(pf->hwinfo, key);
427 if (!value)
428 return nfp_dump_error_tlv(spec, -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(NFP_DUMPSPEC_TYPE_HWINFO_FIELD, 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
is_xpb_read(struct nfp_dumpspec_cpp_isl_id * cpp_id)442 static 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
448 static int
nfp_dump_csr_range(struct nfp_pf * pf,struct nfp_dumpspec_csr * spec_csr,struct nfp_dump_state * dump)449 nfp_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_csr->tl, -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_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(&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(&spec_csr->cpp.cpp_id)) {
482 err = nfp_xpb_readl(pf->cpp, cpp_rd_addr, (u32 *)dest);
483 } else {
484 bytes_read = nfp_cpp_read(pf->cpp, cpp_id, cpp_rd_addr,
485 dest, 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 */
503 static int
nfp_read_indirect_csr(struct nfp_cpp * cpp,struct nfp_dumpspec_cpp_isl_id cpp_params,u32 offset,u32 reg_sz,u32 context,void * dest)504 nfp_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(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, csr_ctx_ptr_offs, context);
517 if (result)
518 return result;
519
520 cpp_id = nfp_get_numeric_cpp_id(&cpp_params);
521 result = nfp_cpp_read(cpp, cpp_id, csr_ctx_ptr_offs, dest, reg_sz);
522 if (result != reg_sz)
523 return result < 0 ? result : -EIO;
524
525 result = nfp_cpp_read(cpp, cpp_id, offset, dest, reg_sz);
526 if (result != reg_sz)
527 return result < 0 ? result : -EIO;
528
529 return 0;
530 }
531
532 static int
nfp_read_all_indirect_csr_ctx(struct nfp_cpp * cpp,struct nfp_dumpspec_csr * spec_csr,u32 address,u32 reg_sz,void * dest)533 nfp_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, spec_csr->cpp.cpp_id, address,
542 reg_sz, ctx, dest + ctx * reg_sz);
543 if (err)
544 return err;
545 }
546
547 return 0;
548 }
549
550 static int
nfp_dump_indirect_csr_range(struct nfp_pf * pf,struct nfp_dumpspec_csr * spec_csr,struct nfp_dump_state * dump)551 nfp_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_csr->tl, -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_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(pf->cpp, spec_csr,
583 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
596 static int
nfp_dump_single_rtsym(struct nfp_pf * pf,struct nfp_dumpspec_rtsym * spec,struct nfp_dump_state * dump)597 nfp_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(spec->rtsym, tl_len);
612 if (key_len == tl_len)
613 return nfp_dump_error_tlv(&spec->tl, -EINVAL, dump);
614
615 sym = nfp_rtsym_lookup(rtbl, spec->rtsym);
616 if (!sym)
617 return nfp_dump_error_tlv(&spec->tl, -ENOENT, dump);
618
619 sym_size = nfp_rtsym_size(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_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(pf->cpp, sym, 0, dest, 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
653 static int
nfp_dump_for_tlv(struct nfp_pf * pf,struct nfp_dump_tl * tl,void * param)654 nfp_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_rtsym, dump);
684 if (err)
685 return err;
686 break;
687 case NFP_DUMPSPEC_TYPE_HWINFO:
688 err = nfp_dump_hwinfo(pf, tl, dump);
689 if (err)
690 return err;
691 break;
692 case NFP_DUMPSPEC_TYPE_HWINFO_FIELD:
693 err = nfp_dump_hwinfo_field(pf, tl, dump);
694 if (err)
695 return err;
696 break;
697 default:
698 err = nfp_dump_error_tlv(tl, -EOPNOTSUPP, dump);
699 if (err)
700 return err;
701 }
702
703 return 0;
704 }
705
706 static int
nfp_dump_specific_level(struct nfp_pf * pf,struct nfp_dump_tl * dump_level,void * param)707 nfp_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, dump_level->data,
716 be32_to_cpu(dump_level->length), dump,
717 nfp_dump_for_tlv);
718 }
719
nfp_dump_populate_prolog(struct nfp_dump_state * dump)720 static 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(NFP_DUMPSPEC_TYPE_PROLOG, total_size, dump);
729 if (err)
730 return err;
731
732 prolog->dump_level = dump->requested_level;
733
734 return 0;
735 }
736
nfp_net_dump_populate_buffer(struct nfp_pf * pf,struct nfp_dumpspec * spec,struct ethtool_dump * dump_param,void * dest)737 int 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);
749 if (err)
750 return err;
751
752 err = nfp_traverse_tlvs(pf, spec->data, spec->size, &dump,
753 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