brintos

brintos / linux-shallow public Read only

0
0
Text · 19.5 KiB · 7276e44 Raw
775 lines · 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 to31 * interpret the binary dumpspec and populate the dump data in a deterministic32 * 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 int121nfp_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 int234nfp_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 int256nfp_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		else273			*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		else281			*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 int304nfp_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 int354nfp_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 int395nfp_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 int454nfp_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 read508 * from IndCtxStatus.509 */510static int511nfp_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 int540nfp_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 int558nfp_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 int606nfp_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 int665nfp_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 int718nfp_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 from769	 * calculated size.770	 */771	dump_param->len = dump.dumped_size;772 773	return 0;774}775