File Index
Files and directories
Sign in
FILE

nfp_net_debugdump.c

File preview with syntax highlighting.

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