1// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
2/* Copyright (C) 2017-2018 Netronome Systems, Inc. */
3
4#include <linux/ethtool.h>
5#include <linux/vmalloc.h>
6
7#include "nfp_asm.h"
8#include "nfp_main.h"
9#include "nfpcore/nfp.h"
10#include "nfpcore/nfp_nffw.h"
11#include "nfpcore/nfp6000/nfp6000.h"
12
13#define NFP_DUMP_SPEC_RTSYM	"_abi_dump_spec"
14
15#define ALIGN8(x)	ALIGN(x, 8)
16
17enum nfp_dumpspec_type {
18	NFP_DUMPSPEC_TYPE_CPP_CSR = 0,
19	NFP_DUMPSPEC_TYPE_XPB_CSR = 1,
20	NFP_DUMPSPEC_TYPE_ME_CSR = 2,
21	NFP_DUMPSPEC_TYPE_INDIRECT_ME_CSR = 3,
22	NFP_DUMPSPEC_TYPE_RTSYM = 4,
23	NFP_DUMPSPEC_TYPE_HWINFO = 5,
24	NFP_DUMPSPEC_TYPE_FWNAME = 6,
25	NFP_DUMPSPEC_TYPE_HWINFO_FIELD = 7,
26	NFP_DUMPSPEC_TYPE_PROLOG = 10000,
27	NFP_DUMPSPEC_TYPE_ERROR = 10001,
28};
29
30/* The following structs must be carefully aligned so that they can be used to
31 * interpret the binary dumpspec and populate the dump data in a deterministic
32 * way.
33 */
34
35/* generic type plus length */
36struct nfp_dump_tl {
37	__be32 type;
38	__be32 length;	/* chunk length to follow, aligned to 8 bytes */
39	char data[];
40};
41
42/* NFP CPP parameters */
43struct nfp_dumpspec_cpp_isl_id {
44	u8 target;
45	u8 action;
46	u8 token;
47	u8 island;
48};
49
50struct nfp_dump_common_cpp {
51	struct nfp_dumpspec_cpp_isl_id cpp_id;
52	__be32 offset;		/* address to start dump */
53	__be32 dump_length;	/* total bytes to dump, aligned to reg size */
54};
55
56/* CSR dumpables */
57struct nfp_dumpspec_csr {
58	struct nfp_dump_tl tl;
59	struct nfp_dump_common_cpp cpp;
60	__be32 register_width;	/* in bits */
61};
62
63struct nfp_dumpspec_rtsym {
64	struct nfp_dump_tl tl;
65	char rtsym[];
66};
67
68/* header for register dumpable */
69struct nfp_dump_csr {
70	struct nfp_dump_tl tl;
71	struct nfp_dump_common_cpp cpp;
72	__be32 register_width;	/* in bits */
73	__be32 error;		/* error code encountered while reading */
74	__be32 error_offset;	/* offset being read when error occurred */
75};
76
77struct nfp_dump_rtsym {
78	struct nfp_dump_tl tl;
79	struct nfp_dump_common_cpp cpp;
80	__be32 error;		/* error code encountered while reading */
81	u8 padded_name_length;	/* pad so data starts at 8 byte boundary */
82	char rtsym[];
83	/* after padded_name_length, there is dump_length data */
84};
85
86struct nfp_dump_prolog {
87	struct nfp_dump_tl tl;
88	__be32 dump_level;
89};
90
91struct nfp_dump_error {
92	struct nfp_dump_tl tl;
93	__be32 error;
94	char padding[4];
95	char spec[];
96};
97
98/* to track state through debug size calculation TLV traversal */
99struct nfp_level_size {
100	__be32 requested_level;	/* input */
101	u32 total_size;		/* output */
102};
103
104/* to track state during debug dump creation TLV traversal */
105struct nfp_dump_state {
106	__be32 requested_level;	/* input param */
107	u32 dumped_size;	/* adds up to size of dumped data */
108	u32 buf_size;		/* size of buffer pointer to by p */
109	void *p;		/* current point in dump buffer */
110};
111
112typedef int (*nfp_tlv_visit)(struct nfp_pf *pf, struct nfp_dump_tl *tl,
113			     void *param);
114
115static int
116nfp_traverse_tlvs(struct nfp_pf *pf, void *data, u32 data_length, void *param,
117		  nfp_tlv_visit tlv_visit)
118{
119	long long remaining = data_length;
120	struct nfp_dump_tl *tl;
121	u32 total_tlv_size;
122	void *p = data;
123	int err;
124
125	while (remaining >= sizeof(*tl)) {
126		tl = p;
127		if (!tl->type && !tl->length)
128			break;
129
130		if (be32_to_cpu(tl->length) > remaining - sizeof(*tl))
131			return -EINVAL;
132
133		total_tlv_size = sizeof(*tl) + be32_to_cpu(tl->length);
134
135		/* Spec TLVs should be aligned to 4 bytes. */
136		if (total_tlv_size % 4 != 0)
137			return -EINVAL;
138
139		p += total_tlv_size;
140		remaining -= total_tlv_size;
141		err = tlv_visit(pf, tl, param);
142		if (err)
143			return err;
144	}
145
146	return 0;
147}
148
149static u32 nfp_get_numeric_cpp_id(struct nfp_dumpspec_cpp_isl_id *cpp_id)
150{
151	return NFP_CPP_ISLAND_ID(cpp_id->target, cpp_id->action, cpp_id->token,
152				 cpp_id->island);
153}
154
155struct nfp_dumpspec *
156nfp_net_dump_load_dumpspec(struct nfp_cpp *cpp, struct nfp_rtsym_table *rtbl)
157{
158	const struct nfp_rtsym *specsym;
159	struct nfp_dumpspec *dumpspec;
160	int bytes_read;
161	u64 sym_size;
162
163	specsym = nfp_rtsym_lookup(rtbl, NFP_DUMP_SPEC_RTSYM);
164	if (!specsym)
165		return NULL;
166	sym_size = nfp_rtsym_size(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
184static int nfp_dump_error_tlv_size(struct nfp_dump_tl *spec)
185{
186	return ALIGN8(sizeof(struct nfp_dump_error) + sizeof(*spec) +
187		      be32_to_cpu(spec->length));
188}
189
190static int nfp_calc_fwname_tlv_size(struct nfp_pf *pf)
191{
192	u32 fwname_len = strlen(nfp_mip_name(pf->mip));
193
194	return sizeof(struct nfp_dump_tl) + ALIGN8(fwname_len + 1);
195}
196
197static int nfp_calc_hwinfo_field_sz(struct nfp_pf *pf, struct nfp_dump_tl *spec)
198{
199	u32 tl_len, key_len;
200	const char *value;
201
202	tl_len = be32_to_cpu(spec->length);
203	key_len = strnlen(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
214static bool nfp_csr_spec_valid(struct nfp_dumpspec_csr *spec_csr)
215{
216	u32 required_read_sz = sizeof(*spec_csr) - sizeof(spec_csr->tl);
217	u32 available_sz = be32_to_cpu(spec_csr->tl.length);
218	u32 reg_width;
219
220	if (available_sz < required_read_sz)
221		return false;
222
223	reg_width = be32_to_cpu(spec_csr->register_width);
224
225	return reg_width == 32 || reg_width == 64;
226}
227
228static int
229nfp_calc_rtsym_dump_sz(struct nfp_pf *pf, struct nfp_dump_tl *spec)
230{
231	struct nfp_rtsym_table *rtbl = pf->rtbl;
232	struct nfp_dumpspec_rtsym *spec_rtsym;
233	const struct nfp_rtsym *sym;
234	u32 tl_len, key_len;
235
236	spec_rtsym = (struct nfp_dumpspec_rtsym *)spec;
237	tl_len = be32_to_cpu(spec->length);
238	key_len = strnlen(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
250static int
251nfp_add_tlv_size(struct nfp_pf *pf, struct nfp_dump_tl *tl, void *param)
252{
253	struct nfp_dumpspec_csr *spec_csr;
254	u32 *size = param;
255	u32 hwinfo_size;
256
257	switch (be32_to_cpu(tl->type)) {
258	case NFP_DUMPSPEC_TYPE_FWNAME:
259		*size += nfp_calc_fwname_tlv_size(pf);
260		break;
261	case NFP_DUMPSPEC_TYPE_CPP_CSR:
262	case NFP_DUMPSPEC_TYPE_XPB_CSR:
263	case NFP_DUMPSPEC_TYPE_ME_CSR:
264		spec_csr = (struct nfp_dumpspec_csr *)tl;
265		if (!nfp_csr_spec_valid(spec_csr))
266			*size += nfp_dump_error_tlv_size(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
298static int
299nfp_calc_specific_level_size(struct nfp_pf *pf, struct nfp_dump_tl *dump_level,
300			     void *param)
301{
302	struct nfp_level_size *lev_sz = param;
303
304	if (dump_level->type != lev_sz->requested_level)
305		return 0;
306
307	return nfp_traverse_tlvs(pf, dump_level->data,
308				 be32_to_cpu(dump_level->length),
309				 &lev_sz->total_size, nfp_add_tlv_size);
310}
311
312s64 nfp_net_dump_calculate_size(struct nfp_pf *pf, struct nfp_dumpspec *spec,
313				u32 flag)
314{
315	struct nfp_level_size lev_sz;
316	int err;
317
318	lev_sz.requested_level = cpu_to_be32(flag);
319	lev_sz.total_size = ALIGN8(sizeof(struct nfp_dump_prolog));
320
321	err = nfp_traverse_tlvs(pf, 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
329static int nfp_add_tlv(u32 type, u32 total_tlv_sz, struct nfp_dump_state *dump)
330{
331	struct nfp_dump_tl *tl = dump->p;
332
333	if (total_tlv_sz > dump->buf_size)
334		return -ENOSPC;
335
336	if (dump->buf_size - total_tlv_sz < dump->dumped_size)
337		return -ENOSPC;
338
339	tl->type = cpu_to_be32(type);
340	tl->length = cpu_to_be32(total_tlv_sz - sizeof(*tl));
341
342	dump->dumped_size += total_tlv_sz;
343	dump->p += total_tlv_sz;
344
345	return 0;
346}
347
348static int
349nfp_dump_error_tlv(struct nfp_dump_tl *spec, int error,
350		   struct nfp_dump_state *dump)
351{
352	struct nfp_dump_error *dump_header = dump->p;
353	u32 total_spec_size, total_size;
354	int err;
355
356	total_spec_size = sizeof(*spec) + be32_to_cpu(spec->length);
357	total_size = ALIGN8(sizeof(*dump_header) + total_spec_size);
358
359	err = nfp_add_tlv(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
369static int nfp_dump_fwname(struct nfp_pf *pf, struct nfp_dump_state *dump)
370{
371	struct nfp_dump_tl *dump_header = dump->p;
372	u32 fwname_len, total_size;
373	const char *fwname;
374	int err;
375
376	fwname = nfp_mip_name(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
389static int
390nfp_dump_hwinfo(struct nfp_pf *pf, struct nfp_dump_tl *spec,
391		struct nfp_dump_state *dump)
392{
393	struct nfp_dump_tl *dump_header = dump->p;
394	u32 hwinfo_size, total_size;
395	char *hwinfo;
396	int err;
397
398	hwinfo = nfp_hwinfo_get_packed_strings(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
411static int nfp_dump_hwinfo_field(struct nfp_pf *pf, struct nfp_dump_tl *spec,
412				 struct nfp_dump_state *dump)
413{
414	struct nfp_dump_tl *dump_header = dump->p;
415	u32 tl_len, key_len, val_len;
416	const char *key, *value;
417	u32 total_size;
418	int err;
419
420	tl_len = be32_to_cpu(spec->length);
421	key_len = strnlen(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
442static bool is_xpb_read(struct nfp_dumpspec_cpp_isl_id *cpp_id)
443{
444	return cpp_id->target == NFP_CPP_TARGET_ISLAND_XPB &&
445	       cpp_id->action == 0 && cpp_id->token == 0;
446}
447
448static int
449nfp_dump_csr_range(struct nfp_pf *pf, struct nfp_dumpspec_csr *spec_csr,
450		   struct nfp_dump_state *dump)
451{
452	struct nfp_dump_csr *dump_header = dump->p;
453	u32 reg_sz, header_size, total_size;
454	u32 cpp_rd_addr, max_rd_addr;
455	int bytes_read;
456	void *dest;
457	u32 cpp_id;
458	int err;
459
460	if (!nfp_csr_spec_valid(spec_csr))
461		return nfp_dump_error_tlv(&spec_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 */
503static int
504nfp_read_indirect_csr(struct nfp_cpp *cpp,
505		      struct nfp_dumpspec_cpp_isl_id cpp_params, u32 offset,
506		      u32 reg_sz, u32 context, void *dest)
507{
508	u32 csr_ctx_ptr_offs;
509	u32 cpp_id;
510	int result;
511
512	csr_ctx_ptr_offs = nfp_get_ind_csr_ctx_ptr_offs(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
532static int
533nfp_read_all_indirect_csr_ctx(struct nfp_cpp *cpp,
534			      struct nfp_dumpspec_csr *spec_csr, u32 address,
535			      u32 reg_sz, void *dest)
536{
537	u32 ctx;
538	int err;
539
540	for (ctx = 0; ctx < NFP_IND_NUM_CONTEXTS; ctx++) {
541		err = nfp_read_indirect_csr(cpp, 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
550static int
551nfp_dump_indirect_csr_range(struct nfp_pf *pf,
552			    struct nfp_dumpspec_csr *spec_csr,
553			    struct nfp_dump_state *dump)
554{
555	struct nfp_dump_csr *dump_header = dump->p;
556	u32 reg_sz, header_size, total_size;
557	u32 cpp_rd_addr, max_rd_addr;
558	u32 reg_data_length;
559	void *dest;
560	int err;
561
562	if (!nfp_csr_spec_valid(spec_csr))
563		return nfp_dump_error_tlv(&spec_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
596static int
597nfp_dump_single_rtsym(struct nfp_pf *pf, struct nfp_dumpspec_rtsym *spec,
598		      struct nfp_dump_state *dump)
599{
600	struct nfp_dump_rtsym *dump_header = dump->p;
601	struct nfp_dumpspec_cpp_isl_id cpp_params;
602	struct nfp_rtsym_table *rtbl = pf->rtbl;
603	u32 header_size, total_size, sym_size;
604	const struct nfp_rtsym *sym;
605	u32 tl_len, key_len;
606	int bytes_read;
607	void *dest;
608	int err;
609
610	tl_len = be32_to_cpu(spec->tl.length);
611	key_len = strnlen(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
653static int
654nfp_dump_for_tlv(struct nfp_pf *pf, struct nfp_dump_tl *tl, void *param)
655{
656	struct nfp_dumpspec_rtsym *spec_rtsym;
657	struct nfp_dump_state *dump = param;
658	struct nfp_dumpspec_csr *spec_csr;
659	int err;
660
661	switch (be32_to_cpu(tl->type)) {
662	case NFP_DUMPSPEC_TYPE_FWNAME:
663		err = nfp_dump_fwname(pf, dump);
664		if (err)
665			return err;
666		break;
667	case NFP_DUMPSPEC_TYPE_CPP_CSR:
668	case NFP_DUMPSPEC_TYPE_XPB_CSR:
669	case NFP_DUMPSPEC_TYPE_ME_CSR:
670		spec_csr = (struct nfp_dumpspec_csr *)tl;
671		err = nfp_dump_csr_range(pf, spec_csr, dump);
672		if (err)
673			return err;
674		break;
675	case NFP_DUMPSPEC_TYPE_INDIRECT_ME_CSR:
676		spec_csr = (struct nfp_dumpspec_csr *)tl;
677		err = nfp_dump_indirect_csr_range(pf, spec_csr, dump);
678		if (err)
679			return err;
680		break;
681	case NFP_DUMPSPEC_TYPE_RTSYM:
682		spec_rtsym = (struct nfp_dumpspec_rtsym *)tl;
683		err = nfp_dump_single_rtsym(pf, spec_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
706static int
707nfp_dump_specific_level(struct nfp_pf *pf, struct nfp_dump_tl *dump_level,
708			void *param)
709{
710	struct nfp_dump_state *dump = param;
711
712	if (dump_level->type != dump->requested_level)
713		return 0;
714
715	return nfp_traverse_tlvs(pf, dump_level->data,
716				 be32_to_cpu(dump_level->length), dump,
717				 nfp_dump_for_tlv);
718}
719
720static int nfp_dump_populate_prolog(struct nfp_dump_state *dump)
721{
722	struct nfp_dump_prolog *prolog = dump->p;
723	u32 total_size;
724	int err;
725
726	total_size = ALIGN8(sizeof(*prolog));
727
728	err = nfp_add_tlv(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
737int nfp_net_dump_populate_buffer(struct nfp_pf *pf, struct nfp_dumpspec *spec,
738				 struct ethtool_dump *dump_param, void *dest)
739{
740	struct nfp_dump_state dump;
741	int err;
742
743	dump.requested_level = cpu_to_be32(dump_param->flag);
744	dump.dumped_size = 0;
745	dump.p = dest;
746	dump.buf_size = dump_param->len;
747
748	err = nfp_dump_populate_prolog(&dump);
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