cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

nfp_net_debugdump.c (19435B)


      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}