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