xref: /linux/drivers/net/ethernet/netronome/nfp/nfp_net_debugdump.c (revision 4152e58cb8c5cd390989f1e96092bce92c27c67a)
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 	u64 sym_size;
192 
193 	specsym = nfp_rtsym_lookup(rtbl, NFP_DUMP_SPEC_RTSYM);
194 	if (!specsym)
195 		return NULL;
196 	sym_size = nfp_rtsym_size(specsym);
197 
198 	/* expected size of this buffer is in the order of tens of kilobytes */
199 	dumpspec = vmalloc(sizeof(*dumpspec) + sym_size);
200 	if (!dumpspec)
201 		return NULL;
202 	dumpspec->size = sym_size;
203 
204 	bytes_read = nfp_rtsym_read(cpp, specsym, 0, dumpspec->data, sym_size);
205 	if (bytes_read != sym_size) {
206 		vfree(dumpspec);
207 		nfp_warn(cpp, "Debug dump specification read failed.\n");
208 		return NULL;
209 	}
210 
211 	return dumpspec;
212 }
213 
214 static int nfp_dump_error_tlv_size(struct nfp_dump_tl *spec)
215 {
216 	return ALIGN8(sizeof(struct nfp_dump_error) + sizeof(*spec) +
217 		      be32_to_cpu(spec->length));
218 }
219 
220 static int nfp_calc_fwname_tlv_size(struct nfp_pf *pf)
221 {
222 	u32 fwname_len = strlen(nfp_mip_name(pf->mip));
223 
224 	return sizeof(struct nfp_dump_tl) + ALIGN8(fwname_len + 1);
225 }
226 
227 static int nfp_calc_hwinfo_field_sz(struct nfp_pf *pf, struct nfp_dump_tl *spec)
228 {
229 	u32 tl_len, key_len;
230 	const char *value;
231 
232 	tl_len = be32_to_cpu(spec->length);
233 	key_len = strnlen(spec->data, tl_len);
234 	if (key_len == tl_len)
235 		return nfp_dump_error_tlv_size(spec);
236 
237 	value = nfp_hwinfo_lookup(pf->hwinfo, spec->data);
238 	if (!value)
239 		return nfp_dump_error_tlv_size(spec);
240 
241 	return sizeof(struct nfp_dump_tl) + ALIGN8(key_len + strlen(value) + 2);
242 }
243 
244 static bool nfp_csr_spec_valid(struct nfp_dumpspec_csr *spec_csr)
245 {
246 	u32 required_read_sz = sizeof(*spec_csr) - sizeof(spec_csr->tl);
247 	u32 available_sz = be32_to_cpu(spec_csr->tl.length);
248 	u32 reg_width;
249 
250 	if (available_sz < required_read_sz)
251 		return false;
252 
253 	reg_width = be32_to_cpu(spec_csr->register_width);
254 
255 	return reg_width == 32 || reg_width == 64;
256 }
257 
258 static int
259 nfp_calc_rtsym_dump_sz(struct nfp_pf *pf, struct nfp_dump_tl *spec)
260 {
261 	struct nfp_rtsym_table *rtbl = pf->rtbl;
262 	struct nfp_dumpspec_rtsym *spec_rtsym;
263 	const struct nfp_rtsym *sym;
264 	u32 tl_len, key_len;
265 
266 	spec_rtsym = (struct nfp_dumpspec_rtsym *)spec;
267 	tl_len = be32_to_cpu(spec->length);
268 	key_len = strnlen(spec_rtsym->rtsym, tl_len);
269 	if (key_len == tl_len)
270 		return nfp_dump_error_tlv_size(spec);
271 
272 	sym = nfp_rtsym_lookup(rtbl, spec_rtsym->rtsym);
273 	if (!sym)
274 		return nfp_dump_error_tlv_size(spec);
275 
276 	return ALIGN8(offsetof(struct nfp_dump_rtsym, rtsym) + key_len + 1) +
277 	       ALIGN8(nfp_rtsym_size(sym));
278 }
279 
280 static int
281 nfp_add_tlv_size(struct nfp_pf *pf, struct nfp_dump_tl *tl, void *param)
282 {
283 	struct nfp_dumpspec_csr *spec_csr;
284 	u32 *size = param;
285 	u32 hwinfo_size;
286 
287 	switch (be32_to_cpu(tl->type)) {
288 	case NFP_DUMPSPEC_TYPE_FWNAME:
289 		*size += nfp_calc_fwname_tlv_size(pf);
290 		break;
291 	case NFP_DUMPSPEC_TYPE_CPP_CSR:
292 	case NFP_DUMPSPEC_TYPE_XPB_CSR:
293 	case NFP_DUMPSPEC_TYPE_ME_CSR:
294 		spec_csr = (struct nfp_dumpspec_csr *)tl;
295 		if (!nfp_csr_spec_valid(spec_csr))
296 			*size += nfp_dump_error_tlv_size(tl);
297 		else
298 			*size += ALIGN8(sizeof(struct nfp_dump_csr)) +
299 				 ALIGN8(be32_to_cpu(spec_csr->cpp.dump_length));
300 		break;
301 	case NFP_DUMPSPEC_TYPE_INDIRECT_ME_CSR:
302 		spec_csr = (struct nfp_dumpspec_csr *)tl;
303 		if (!nfp_csr_spec_valid(spec_csr))
304 			*size += nfp_dump_error_tlv_size(tl);
305 		else
306 			*size += ALIGN8(sizeof(struct nfp_dump_csr)) +
307 				 ALIGN8(be32_to_cpu(spec_csr->cpp.dump_length) *
308 					NFP_IND_NUM_CONTEXTS);
309 		break;
310 	case NFP_DUMPSPEC_TYPE_RTSYM:
311 		*size += nfp_calc_rtsym_dump_sz(pf, tl);
312 		break;
313 	case NFP_DUMPSPEC_TYPE_HWINFO:
314 		hwinfo_size = nfp_hwinfo_get_packed_str_size(pf->hwinfo);
315 		*size += sizeof(struct nfp_dump_tl) + ALIGN8(hwinfo_size);
316 		break;
317 	case NFP_DUMPSPEC_TYPE_HWINFO_FIELD:
318 		*size += nfp_calc_hwinfo_field_sz(pf, tl);
319 		break;
320 	default:
321 		*size += nfp_dump_error_tlv_size(tl);
322 		break;
323 	}
324 
325 	return 0;
326 }
327 
328 static int
329 nfp_calc_specific_level_size(struct nfp_pf *pf, struct nfp_dump_tl *dump_level,
330 			     void *param)
331 {
332 	struct nfp_level_size *lev_sz = param;
333 
334 	if (dump_level->type != lev_sz->requested_level)
335 		return 0;
336 
337 	return nfp_traverse_tlvs(pf, dump_level->data,
338 				 be32_to_cpu(dump_level->length),
339 				 &lev_sz->total_size, nfp_add_tlv_size);
340 }
341 
342 s64 nfp_net_dump_calculate_size(struct nfp_pf *pf, struct nfp_dumpspec *spec,
343 				u32 flag)
344 {
345 	struct nfp_level_size lev_sz;
346 	int err;
347 
348 	lev_sz.requested_level = cpu_to_be32(flag);
349 	lev_sz.total_size = ALIGN8(sizeof(struct nfp_dump_prolog));
350 
351 	err = nfp_traverse_tlvs(pf, spec->data, spec->size, &lev_sz,
352 				nfp_calc_specific_level_size);
353 	if (err)
354 		return err;
355 
356 	return lev_sz.total_size;
357 }
358 
359 static int nfp_add_tlv(u32 type, u32 total_tlv_sz, struct nfp_dump_state *dump)
360 {
361 	struct nfp_dump_tl *tl = dump->p;
362 
363 	if (total_tlv_sz > dump->buf_size)
364 		return -ENOSPC;
365 
366 	if (dump->buf_size - total_tlv_sz < dump->dumped_size)
367 		return -ENOSPC;
368 
369 	tl->type = cpu_to_be32(type);
370 	tl->length = cpu_to_be32(total_tlv_sz - sizeof(*tl));
371 
372 	dump->dumped_size += total_tlv_sz;
373 	dump->p += total_tlv_sz;
374 
375 	return 0;
376 }
377 
378 static int
379 nfp_dump_error_tlv(struct nfp_dump_tl *spec, int error,
380 		   struct nfp_dump_state *dump)
381 {
382 	struct nfp_dump_error *dump_header = dump->p;
383 	u32 total_spec_size, total_size;
384 	int err;
385 
386 	total_spec_size = sizeof(*spec) + be32_to_cpu(spec->length);
387 	total_size = ALIGN8(sizeof(*dump_header) + total_spec_size);
388 
389 	err = nfp_add_tlv(NFP_DUMPSPEC_TYPE_ERROR, total_size, dump);
390 	if (err)
391 		return err;
392 
393 	dump_header->error = cpu_to_be32(error);
394 	memcpy(dump_header->spec, spec, total_spec_size);
395 
396 	return 0;
397 }
398 
399 static int nfp_dump_fwname(struct nfp_pf *pf, struct nfp_dump_state *dump)
400 {
401 	struct nfp_dump_tl *dump_header = dump->p;
402 	u32 fwname_len, total_size;
403 	const char *fwname;
404 	int err;
405 
406 	fwname = nfp_mip_name(pf->mip);
407 	fwname_len = strlen(fwname);
408 	total_size = sizeof(*dump_header) + ALIGN8(fwname_len + 1);
409 
410 	err = nfp_add_tlv(NFP_DUMPSPEC_TYPE_FWNAME, total_size, dump);
411 	if (err)
412 		return err;
413 
414 	memcpy(dump_header->data, fwname, fwname_len);
415 
416 	return 0;
417 }
418 
419 static int
420 nfp_dump_hwinfo(struct nfp_pf *pf, struct nfp_dump_tl *spec,
421 		struct nfp_dump_state *dump)
422 {
423 	struct nfp_dump_tl *dump_header = dump->p;
424 	u32 hwinfo_size, total_size;
425 	char *hwinfo;
426 	int err;
427 
428 	hwinfo = nfp_hwinfo_get_packed_strings(pf->hwinfo);
429 	hwinfo_size = nfp_hwinfo_get_packed_str_size(pf->hwinfo);
430 	total_size = sizeof(*dump_header) + ALIGN8(hwinfo_size);
431 
432 	err = nfp_add_tlv(NFP_DUMPSPEC_TYPE_HWINFO, total_size, dump);
433 	if (err)
434 		return err;
435 
436 	memcpy(dump_header->data, hwinfo, hwinfo_size);
437 
438 	return 0;
439 }
440 
441 static int nfp_dump_hwinfo_field(struct nfp_pf *pf, struct nfp_dump_tl *spec,
442 				 struct nfp_dump_state *dump)
443 {
444 	struct nfp_dump_tl *dump_header = dump->p;
445 	u32 tl_len, key_len, val_len;
446 	const char *key, *value;
447 	u32 total_size;
448 	int err;
449 
450 	tl_len = be32_to_cpu(spec->length);
451 	key_len = strnlen(spec->data, tl_len);
452 	if (key_len == tl_len)
453 		return nfp_dump_error_tlv(spec, -EINVAL, dump);
454 
455 	key = spec->data;
456 	value = nfp_hwinfo_lookup(pf->hwinfo, key);
457 	if (!value)
458 		return nfp_dump_error_tlv(spec, -ENOENT, dump);
459 
460 	val_len = strlen(value);
461 	total_size = sizeof(*dump_header) + ALIGN8(key_len + val_len + 2);
462 	err = nfp_add_tlv(NFP_DUMPSPEC_TYPE_HWINFO_FIELD, total_size, dump);
463 	if (err)
464 		return err;
465 
466 	memcpy(dump_header->data, key, key_len + 1);
467 	memcpy(dump_header->data + key_len + 1, value, val_len + 1);
468 
469 	return 0;
470 }
471 
472 static bool is_xpb_read(struct nfp_dumpspec_cpp_isl_id *cpp_id)
473 {
474 	return cpp_id->target == NFP_CPP_TARGET_ISLAND_XPB &&
475 	       cpp_id->action == 0 && cpp_id->token == 0;
476 }
477 
478 static int
479 nfp_dump_csr_range(struct nfp_pf *pf, struct nfp_dumpspec_csr *spec_csr,
480 		   struct nfp_dump_state *dump)
481 {
482 	struct nfp_dump_csr *dump_header = dump->p;
483 	u32 reg_sz, header_size, total_size;
484 	u32 cpp_rd_addr, max_rd_addr;
485 	int bytes_read;
486 	void *dest;
487 	u32 cpp_id;
488 	int err;
489 
490 	if (!nfp_csr_spec_valid(spec_csr))
491 		return nfp_dump_error_tlv(&spec_csr->tl, -EINVAL, dump);
492 
493 	reg_sz = be32_to_cpu(spec_csr->register_width) / BITS_PER_BYTE;
494 	header_size = ALIGN8(sizeof(*dump_header));
495 	total_size = header_size +
496 		     ALIGN8(be32_to_cpu(spec_csr->cpp.dump_length));
497 	dest = dump->p + header_size;
498 
499 	err = nfp_add_tlv(be32_to_cpu(spec_csr->tl.type), total_size, dump);
500 	if (err)
501 		return err;
502 
503 	dump_header->cpp = spec_csr->cpp;
504 	dump_header->register_width = spec_csr->register_width;
505 
506 	cpp_id = nfp_get_numeric_cpp_id(&spec_csr->cpp.cpp_id);
507 	cpp_rd_addr = be32_to_cpu(spec_csr->cpp.offset);
508 	max_rd_addr = cpp_rd_addr + be32_to_cpu(spec_csr->cpp.dump_length);
509 
510 	while (cpp_rd_addr < max_rd_addr) {
511 		if (is_xpb_read(&spec_csr->cpp.cpp_id)) {
512 			err = nfp_xpb_readl(pf->cpp, cpp_rd_addr, (u32 *)dest);
513 		} else {
514 			bytes_read = nfp_cpp_read(pf->cpp, cpp_id, cpp_rd_addr,
515 						  dest, reg_sz);
516 			err = bytes_read == reg_sz ? 0 : -EIO;
517 		}
518 		if (err) {
519 			dump_header->error = cpu_to_be32(err);
520 			dump_header->error_offset = cpu_to_be32(cpp_rd_addr);
521 			break;
522 		}
523 		cpp_rd_addr += reg_sz;
524 		dest += reg_sz;
525 	}
526 
527 	return 0;
528 }
529 
530 /* Write context to CSRCtxPtr, then read from it. Then the value can be read
531  * from IndCtxStatus.
532  */
533 static int
534 nfp_read_indirect_csr(struct nfp_cpp *cpp,
535 		      struct nfp_dumpspec_cpp_isl_id cpp_params, u32 offset,
536 		      u32 reg_sz, u32 context, void *dest)
537 {
538 	u32 csr_ctx_ptr_offs;
539 	u32 cpp_id;
540 	int result;
541 
542 	csr_ctx_ptr_offs = nfp_get_ind_csr_ctx_ptr_offs(offset);
543 	cpp_id = NFP_CPP_ISLAND_ID(cpp_params.target,
544 				   NFP_IND_ME_REFL_WR_SIG_INIT,
545 				   cpp_params.token, cpp_params.island);
546 	result = nfp_cpp_writel(cpp, cpp_id, csr_ctx_ptr_offs, context);
547 	if (result)
548 		return result;
549 
550 	cpp_id = nfp_get_numeric_cpp_id(&cpp_params);
551 	result = nfp_cpp_read(cpp, cpp_id, csr_ctx_ptr_offs, dest, reg_sz);
552 	if (result != reg_sz)
553 		return result < 0 ? result : -EIO;
554 
555 	result = nfp_cpp_read(cpp, cpp_id, offset, dest, reg_sz);
556 	if (result != reg_sz)
557 		return result < 0 ? result : -EIO;
558 
559 	return 0;
560 }
561 
562 static int
563 nfp_read_all_indirect_csr_ctx(struct nfp_cpp *cpp,
564 			      struct nfp_dumpspec_csr *spec_csr, u32 address,
565 			      u32 reg_sz, void *dest)
566 {
567 	u32 ctx;
568 	int err;
569 
570 	for (ctx = 0; ctx < NFP_IND_NUM_CONTEXTS; ctx++) {
571 		err = nfp_read_indirect_csr(cpp, spec_csr->cpp.cpp_id, address,
572 					    reg_sz, ctx, dest + ctx * reg_sz);
573 		if (err)
574 			return err;
575 	}
576 
577 	return 0;
578 }
579 
580 static int
581 nfp_dump_indirect_csr_range(struct nfp_pf *pf,
582 			    struct nfp_dumpspec_csr *spec_csr,
583 			    struct nfp_dump_state *dump)
584 {
585 	struct nfp_dump_csr *dump_header = dump->p;
586 	u32 reg_sz, header_size, total_size;
587 	u32 cpp_rd_addr, max_rd_addr;
588 	u32 reg_data_length;
589 	void *dest;
590 	int err;
591 
592 	if (!nfp_csr_spec_valid(spec_csr))
593 		return nfp_dump_error_tlv(&spec_csr->tl, -EINVAL, dump);
594 
595 	reg_sz = be32_to_cpu(spec_csr->register_width) / BITS_PER_BYTE;
596 	header_size = ALIGN8(sizeof(*dump_header));
597 	reg_data_length = be32_to_cpu(spec_csr->cpp.dump_length) *
598 			  NFP_IND_NUM_CONTEXTS;
599 	total_size = header_size + ALIGN8(reg_data_length);
600 	dest = dump->p + header_size;
601 
602 	err = nfp_add_tlv(be32_to_cpu(spec_csr->tl.type), total_size, dump);
603 	if (err)
604 		return err;
605 
606 	dump_header->cpp = spec_csr->cpp;
607 	dump_header->register_width = spec_csr->register_width;
608 
609 	cpp_rd_addr = be32_to_cpu(spec_csr->cpp.offset);
610 	max_rd_addr = cpp_rd_addr + be32_to_cpu(spec_csr->cpp.dump_length);
611 	while (cpp_rd_addr < max_rd_addr) {
612 		err = nfp_read_all_indirect_csr_ctx(pf->cpp, spec_csr,
613 						    cpp_rd_addr, reg_sz, dest);
614 		if (err) {
615 			dump_header->error = cpu_to_be32(err);
616 			dump_header->error_offset = cpu_to_be32(cpp_rd_addr);
617 			break;
618 		}
619 		cpp_rd_addr += reg_sz;
620 		dest += reg_sz * NFP_IND_NUM_CONTEXTS;
621 	}
622 
623 	return 0;
624 }
625 
626 static int
627 nfp_dump_single_rtsym(struct nfp_pf *pf, struct nfp_dumpspec_rtsym *spec,
628 		      struct nfp_dump_state *dump)
629 {
630 	struct nfp_dump_rtsym *dump_header = dump->p;
631 	struct nfp_dumpspec_cpp_isl_id cpp_params;
632 	struct nfp_rtsym_table *rtbl = pf->rtbl;
633 	u32 header_size, total_size, sym_size;
634 	const struct nfp_rtsym *sym;
635 	u32 tl_len, key_len;
636 	int bytes_read;
637 	void *dest;
638 	int err;
639 
640 	tl_len = be32_to_cpu(spec->tl.length);
641 	key_len = strnlen(spec->rtsym, tl_len);
642 	if (key_len == tl_len)
643 		return nfp_dump_error_tlv(&spec->tl, -EINVAL, dump);
644 
645 	sym = nfp_rtsym_lookup(rtbl, spec->rtsym);
646 	if (!sym)
647 		return nfp_dump_error_tlv(&spec->tl, -ENOENT, dump);
648 
649 	sym_size = nfp_rtsym_size(sym);
650 	header_size =
651 		ALIGN8(offsetof(struct nfp_dump_rtsym, rtsym) + key_len + 1);
652 	total_size = header_size + ALIGN8(sym_size);
653 	dest = dump->p + header_size;
654 
655 	err = nfp_add_tlv(be32_to_cpu(spec->tl.type), total_size, dump);
656 	if (err)
657 		return err;
658 
659 	dump_header->padded_name_length =
660 		header_size - offsetof(struct nfp_dump_rtsym, rtsym);
661 	memcpy(dump_header->rtsym, spec->rtsym, key_len + 1);
662 	dump_header->cpp.dump_length = cpu_to_be32(sym_size);
663 
664 	if (sym->type != NFP_RTSYM_TYPE_ABS) {
665 		cpp_params.target = sym->target;
666 		cpp_params.action = NFP_CPP_ACTION_RW;
667 		cpp_params.token  = 0;
668 		cpp_params.island = sym->domain;
669 		dump_header->cpp.cpp_id = cpp_params;
670 		dump_header->cpp.offset = cpu_to_be32(sym->addr);
671 	}
672 
673 	bytes_read = nfp_rtsym_read(pf->cpp, sym, 0, dest, sym_size);
674 	if (bytes_read != sym_size) {
675 		if (bytes_read >= 0)
676 			bytes_read = -EIO;
677 		dump_header->error = cpu_to_be32(bytes_read);
678 	}
679 
680 	return 0;
681 }
682 
683 static int
684 nfp_dump_for_tlv(struct nfp_pf *pf, struct nfp_dump_tl *tl, void *param)
685 {
686 	struct nfp_dumpspec_rtsym *spec_rtsym;
687 	struct nfp_dump_state *dump = param;
688 	struct nfp_dumpspec_csr *spec_csr;
689 	int err;
690 
691 	switch (be32_to_cpu(tl->type)) {
692 	case NFP_DUMPSPEC_TYPE_FWNAME:
693 		err = nfp_dump_fwname(pf, dump);
694 		if (err)
695 			return err;
696 		break;
697 	case NFP_DUMPSPEC_TYPE_CPP_CSR:
698 	case NFP_DUMPSPEC_TYPE_XPB_CSR:
699 	case NFP_DUMPSPEC_TYPE_ME_CSR:
700 		spec_csr = (struct nfp_dumpspec_csr *)tl;
701 		err = nfp_dump_csr_range(pf, spec_csr, dump);
702 		if (err)
703 			return err;
704 		break;
705 	case NFP_DUMPSPEC_TYPE_INDIRECT_ME_CSR:
706 		spec_csr = (struct nfp_dumpspec_csr *)tl;
707 		err = nfp_dump_indirect_csr_range(pf, spec_csr, dump);
708 		if (err)
709 			return err;
710 		break;
711 	case NFP_DUMPSPEC_TYPE_RTSYM:
712 		spec_rtsym = (struct nfp_dumpspec_rtsym *)tl;
713 		err = nfp_dump_single_rtsym(pf, spec_rtsym, dump);
714 		if (err)
715 			return err;
716 		break;
717 	case NFP_DUMPSPEC_TYPE_HWINFO:
718 		err = nfp_dump_hwinfo(pf, tl, dump);
719 		if (err)
720 			return err;
721 		break;
722 	case NFP_DUMPSPEC_TYPE_HWINFO_FIELD:
723 		err = nfp_dump_hwinfo_field(pf, tl, dump);
724 		if (err)
725 			return err;
726 		break;
727 	default:
728 		err = nfp_dump_error_tlv(tl, -EOPNOTSUPP, dump);
729 		if (err)
730 			return err;
731 	}
732 
733 	return 0;
734 }
735 
736 static int
737 nfp_dump_specific_level(struct nfp_pf *pf, struct nfp_dump_tl *dump_level,
738 			void *param)
739 {
740 	struct nfp_dump_state *dump = param;
741 
742 	if (dump_level->type != dump->requested_level)
743 		return 0;
744 
745 	return nfp_traverse_tlvs(pf, dump_level->data,
746 				 be32_to_cpu(dump_level->length), dump,
747 				 nfp_dump_for_tlv);
748 }
749 
750 static int nfp_dump_populate_prolog(struct nfp_dump_state *dump)
751 {
752 	struct nfp_dump_prolog *prolog = dump->p;
753 	u32 total_size;
754 	int err;
755 
756 	total_size = ALIGN8(sizeof(*prolog));
757 
758 	err = nfp_add_tlv(NFP_DUMPSPEC_TYPE_PROLOG, total_size, dump);
759 	if (err)
760 		return err;
761 
762 	prolog->dump_level = dump->requested_level;
763 
764 	return 0;
765 }
766 
767 int nfp_net_dump_populate_buffer(struct nfp_pf *pf, struct nfp_dumpspec *spec,
768 				 struct ethtool_dump *dump_param, void *dest)
769 {
770 	struct nfp_dump_state dump;
771 	int err;
772 
773 	dump.requested_level = cpu_to_be32(dump_param->flag);
774 	dump.dumped_size = 0;
775 	dump.p = dest;
776 	dump.buf_size = dump_param->len;
777 
778 	err = nfp_dump_populate_prolog(&dump);
779 	if (err)
780 		return err;
781 
782 	err = nfp_traverse_tlvs(pf, spec->data, spec->size, &dump,
783 				nfp_dump_specific_level);
784 	if (err)
785 		return err;
786 
787 	/* Set size of actual dump, to trigger warning if different from
788 	 * calculated size.
789 	 */
790 	dump_param->len = dump.dumped_size;
791 
792 	return 0;
793 }
794