xref: /linux/drivers/net/ethernet/netronome/nfp/nfp_net_debugdump.c (revision b8265621f4888af9494e1d685620871ec81bc33d)
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
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 
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 *
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 
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 
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 
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 
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
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
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
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 
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 
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
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 
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
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 
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 
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
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
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
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
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
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
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
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 
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 
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