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 	/* 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 };
44 static_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 */
48 struct nfp_dumpspec_cpp_isl_id {
49 	u8 target;
50 	u8 action;
51 	u8 token;
52 	u8 island;
53 };
54 
55 struct 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 */
62 struct 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 
68 struct nfp_dumpspec_rtsym {
69 	struct nfp_dump_tl_hdr tl;
70 	char rtsym[];
71 };
72 
73 /* header for register dumpable */
74 struct 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 
82 struct 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 
91 struct nfp_dump_prolog {
92 	struct nfp_dump_tl_hdr tl;
93 	__be32 dump_level;
94 };
95 
96 struct 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 */
104 struct 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 */
110 struct 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 
117 typedef int (*nfp_tlv_visit)(struct nfp_pf *pf, struct nfp_dump_tl *tl,
118 			     void *param);
119 
120 static int
nfp_traverse_tlvs(struct nfp_pf * pf,void * data,u32 data_length,void * param,nfp_tlv_visit tlv_visit)121 nfp_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 
nfp_get_numeric_cpp_id(struct nfp_dumpspec_cpp_isl_id * cpp_id)154 static 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 
160 struct nfp_dumpspec *
nfp_net_dump_load_dumpspec(struct nfp_cpp * cpp,struct nfp_rtsym_table * rtbl)161 nfp_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 
nfp_dump_error_tlv_size(struct nfp_dump_tl * spec)189 static 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 
nfp_calc_fwname_tlv_size(struct nfp_pf * pf)195 static 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 
nfp_calc_hwinfo_field_sz(struct nfp_pf * pf,struct nfp_dump_tl * spec)202 static 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 
nfp_csr_spec_valid(struct nfp_dumpspec_csr * spec_csr)219 static 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 
233 static int
nfp_calc_rtsym_dump_sz(struct nfp_pf * pf,struct nfp_dump_tl * spec)234 nfp_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 
255 static int
nfp_add_tlv_size(struct nfp_pf * pf,struct nfp_dump_tl * tl,void * param)256 nfp_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 
303 static int
nfp_calc_specific_level_size(struct nfp_pf * pf,struct nfp_dump_tl * dump_level,void * param)304 nfp_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 
nfp_net_dump_calculate_size(struct nfp_pf * pf,struct nfp_dumpspec * spec,u32 flag)317 s64 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 
nfp_add_tlv(u32 type,u32 total_tlv_sz,struct nfp_dump_state * dump)334 static 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 
353 static int
nfp_dump_error_tlv(struct nfp_dump_tl * spec,int error,struct nfp_dump_state * dump)354 nfp_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 
nfp_dump_fwname(struct nfp_pf * pf,struct nfp_dump_state * dump)374 static 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 
394 static int
nfp_dump_hwinfo(struct nfp_pf * pf,struct nfp_dump_tl * spec,struct nfp_dump_state * dump)395 nfp_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 
nfp_dump_hwinfo_field(struct nfp_pf * pf,struct nfp_dump_tl * spec,struct nfp_dump_state * dump)416 static 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 
is_xpb_read(struct nfp_dumpspec_cpp_isl_id * cpp_id)447 static 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 
453 static int
nfp_dump_csr_range(struct nfp_pf * pf,struct nfp_dumpspec_csr * spec_csr,struct nfp_dump_state * dump)454 nfp_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  */
510 static int
nfp_read_indirect_csr(struct nfp_cpp * cpp,struct nfp_dumpspec_cpp_isl_id cpp_params,u32 offset,u32 reg_sz,u32 context,void * dest)511 nfp_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 
539 static int
nfp_read_all_indirect_csr_ctx(struct nfp_cpp * cpp,struct nfp_dumpspec_csr * spec_csr,u32 address,u32 reg_sz,void * dest)540 nfp_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 
557 static int
nfp_dump_indirect_csr_range(struct nfp_pf * pf,struct nfp_dumpspec_csr * spec_csr,struct nfp_dump_state * dump)558 nfp_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 
605 static int
nfp_dump_single_rtsym(struct nfp_pf * pf,struct nfp_dumpspec_rtsym * spec,struct nfp_dump_state * dump)606 nfp_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 
664 static int
nfp_dump_for_tlv(struct nfp_pf * pf,struct nfp_dump_tl * tl,void * param)665 nfp_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 
717 static int
nfp_dump_specific_level(struct nfp_pf * pf,struct nfp_dump_tl * dump_level,void * param)718 nfp_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 
nfp_dump_populate_prolog(struct nfp_dump_state * dump)731 static 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 
nfp_net_dump_populate_buffer(struct nfp_pf * pf,struct nfp_dumpspec * spec,struct ethtool_dump * dump_param,void * dest)748 int 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 }
775