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