Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
0002 /* Copyright (C) 2017-2018 Netronome Systems, Inc. */
0003 
0004 #include <linux/ethtool.h>
0005 #include <linux/vmalloc.h>
0006 
0007 #include "nfp_asm.h"
0008 #include "nfp_main.h"
0009 #include "nfpcore/nfp.h"
0010 #include "nfpcore/nfp_nffw.h"
0011 #include "nfpcore/nfp6000/nfp6000.h"
0012 
0013 #define NFP_DUMP_SPEC_RTSYM "_abi_dump_spec"
0014 
0015 #define ALIGN8(x)   ALIGN(x, 8)
0016 
0017 enum nfp_dumpspec_type {
0018     NFP_DUMPSPEC_TYPE_CPP_CSR = 0,
0019     NFP_DUMPSPEC_TYPE_XPB_CSR = 1,
0020     NFP_DUMPSPEC_TYPE_ME_CSR = 2,
0021     NFP_DUMPSPEC_TYPE_INDIRECT_ME_CSR = 3,
0022     NFP_DUMPSPEC_TYPE_RTSYM = 4,
0023     NFP_DUMPSPEC_TYPE_HWINFO = 5,
0024     NFP_DUMPSPEC_TYPE_FWNAME = 6,
0025     NFP_DUMPSPEC_TYPE_HWINFO_FIELD = 7,
0026     NFP_DUMPSPEC_TYPE_PROLOG = 10000,
0027     NFP_DUMPSPEC_TYPE_ERROR = 10001,
0028 };
0029 
0030 /* The following structs must be carefully aligned so that they can be used to
0031  * interpret the binary dumpspec and populate the dump data in a deterministic
0032  * way.
0033  */
0034 
0035 /* generic type plus length */
0036 struct nfp_dump_tl {
0037     __be32 type;
0038     __be32 length;  /* chunk length to follow, aligned to 8 bytes */
0039     char data[];
0040 };
0041 
0042 /* NFP CPP parameters */
0043 struct nfp_dumpspec_cpp_isl_id {
0044     u8 target;
0045     u8 action;
0046     u8 token;
0047     u8 island;
0048 };
0049 
0050 struct nfp_dump_common_cpp {
0051     struct nfp_dumpspec_cpp_isl_id cpp_id;
0052     __be32 offset;      /* address to start dump */
0053     __be32 dump_length; /* total bytes to dump, aligned to reg size */
0054 };
0055 
0056 /* CSR dumpables */
0057 struct nfp_dumpspec_csr {
0058     struct nfp_dump_tl tl;
0059     struct nfp_dump_common_cpp cpp;
0060     __be32 register_width;  /* in bits */
0061 };
0062 
0063 struct nfp_dumpspec_rtsym {
0064     struct nfp_dump_tl tl;
0065     char rtsym[];
0066 };
0067 
0068 /* header for register dumpable */
0069 struct nfp_dump_csr {
0070     struct nfp_dump_tl tl;
0071     struct nfp_dump_common_cpp cpp;
0072     __be32 register_width;  /* in bits */
0073     __be32 error;       /* error code encountered while reading */
0074     __be32 error_offset;    /* offset being read when error occurred */
0075 };
0076 
0077 struct nfp_dump_rtsym {
0078     struct nfp_dump_tl tl;
0079     struct nfp_dump_common_cpp cpp;
0080     __be32 error;       /* error code encountered while reading */
0081     u8 padded_name_length;  /* pad so data starts at 8 byte boundary */
0082     char rtsym[];
0083     /* after padded_name_length, there is dump_length data */
0084 };
0085 
0086 struct nfp_dump_prolog {
0087     struct nfp_dump_tl tl;
0088     __be32 dump_level;
0089 };
0090 
0091 struct nfp_dump_error {
0092     struct nfp_dump_tl tl;
0093     __be32 error;
0094     char padding[4];
0095     char spec[];
0096 };
0097 
0098 /* to track state through debug size calculation TLV traversal */
0099 struct nfp_level_size {
0100     __be32 requested_level; /* input */
0101     u32 total_size;     /* output */
0102 };
0103 
0104 /* to track state during debug dump creation TLV traversal */
0105 struct nfp_dump_state {
0106     __be32 requested_level; /* input param */
0107     u32 dumped_size;    /* adds up to size of dumped data */
0108     u32 buf_size;       /* size of buffer pointer to by p */
0109     void *p;        /* current point in dump buffer */
0110 };
0111 
0112 typedef int (*nfp_tlv_visit)(struct nfp_pf *pf, struct nfp_dump_tl *tl,
0113                  void *param);
0114 
0115 static int
0116 nfp_traverse_tlvs(struct nfp_pf *pf, void *data, u32 data_length, void *param,
0117           nfp_tlv_visit tlv_visit)
0118 {
0119     long long remaining = data_length;
0120     struct nfp_dump_tl *tl;
0121     u32 total_tlv_size;
0122     void *p = data;
0123     int err;
0124 
0125     while (remaining >= sizeof(*tl)) {
0126         tl = p;
0127         if (!tl->type && !tl->length)
0128             break;
0129 
0130         if (be32_to_cpu(tl->length) > remaining - sizeof(*tl))
0131             return -EINVAL;
0132 
0133         total_tlv_size = sizeof(*tl) + be32_to_cpu(tl->length);
0134 
0135         /* Spec TLVs should be aligned to 4 bytes. */
0136         if (total_tlv_size % 4 != 0)
0137             return -EINVAL;
0138 
0139         p += total_tlv_size;
0140         remaining -= total_tlv_size;
0141         err = tlv_visit(pf, tl, param);
0142         if (err)
0143             return err;
0144     }
0145 
0146     return 0;
0147 }
0148 
0149 static u32 nfp_get_numeric_cpp_id(struct nfp_dumpspec_cpp_isl_id *cpp_id)
0150 {
0151     return NFP_CPP_ISLAND_ID(cpp_id->target, cpp_id->action, cpp_id->token,
0152                  cpp_id->island);
0153 }
0154 
0155 struct nfp_dumpspec *
0156 nfp_net_dump_load_dumpspec(struct nfp_cpp *cpp, struct nfp_rtsym_table *rtbl)
0157 {
0158     const struct nfp_rtsym *specsym;
0159     struct nfp_dumpspec *dumpspec;
0160     int bytes_read;
0161     u64 sym_size;
0162 
0163     specsym = nfp_rtsym_lookup(rtbl, NFP_DUMP_SPEC_RTSYM);
0164     if (!specsym)
0165         return NULL;
0166     sym_size = nfp_rtsym_size(specsym);
0167 
0168     /* expected size of this buffer is in the order of tens of kilobytes */
0169     dumpspec = vmalloc(sizeof(*dumpspec) + sym_size);
0170     if (!dumpspec)
0171         return NULL;
0172     dumpspec->size = sym_size;
0173 
0174     bytes_read = nfp_rtsym_read(cpp, specsym, 0, dumpspec->data, sym_size);
0175     if (bytes_read != sym_size) {
0176         vfree(dumpspec);
0177         nfp_warn(cpp, "Debug dump specification read failed.\n");
0178         return NULL;
0179     }
0180 
0181     return dumpspec;
0182 }
0183 
0184 static int nfp_dump_error_tlv_size(struct nfp_dump_tl *spec)
0185 {
0186     return ALIGN8(sizeof(struct nfp_dump_error) + sizeof(*spec) +
0187               be32_to_cpu(spec->length));
0188 }
0189 
0190 static int nfp_calc_fwname_tlv_size(struct nfp_pf *pf)
0191 {
0192     u32 fwname_len = strlen(nfp_mip_name(pf->mip));
0193 
0194     return sizeof(struct nfp_dump_tl) + ALIGN8(fwname_len + 1);
0195 }
0196 
0197 static int nfp_calc_hwinfo_field_sz(struct nfp_pf *pf, struct nfp_dump_tl *spec)
0198 {
0199     u32 tl_len, key_len;
0200     const char *value;
0201 
0202     tl_len = be32_to_cpu(spec->length);
0203     key_len = strnlen(spec->data, tl_len);
0204     if (key_len == tl_len)
0205         return nfp_dump_error_tlv_size(spec);
0206 
0207     value = nfp_hwinfo_lookup(pf->hwinfo, spec->data);
0208     if (!value)
0209         return nfp_dump_error_tlv_size(spec);
0210 
0211     return sizeof(struct nfp_dump_tl) + ALIGN8(key_len + strlen(value) + 2);
0212 }
0213 
0214 static bool nfp_csr_spec_valid(struct nfp_dumpspec_csr *spec_csr)
0215 {
0216     u32 required_read_sz = sizeof(*spec_csr) - sizeof(spec_csr->tl);
0217     u32 available_sz = be32_to_cpu(spec_csr->tl.length);
0218     u32 reg_width;
0219 
0220     if (available_sz < required_read_sz)
0221         return false;
0222 
0223     reg_width = be32_to_cpu(spec_csr->register_width);
0224 
0225     return reg_width == 32 || reg_width == 64;
0226 }
0227 
0228 static int
0229 nfp_calc_rtsym_dump_sz(struct nfp_pf *pf, struct nfp_dump_tl *spec)
0230 {
0231     struct nfp_rtsym_table *rtbl = pf->rtbl;
0232     struct nfp_dumpspec_rtsym *spec_rtsym;
0233     const struct nfp_rtsym *sym;
0234     u32 tl_len, key_len;
0235 
0236     spec_rtsym = (struct nfp_dumpspec_rtsym *)spec;
0237     tl_len = be32_to_cpu(spec->length);
0238     key_len = strnlen(spec_rtsym->rtsym, tl_len);
0239     if (key_len == tl_len)
0240         return nfp_dump_error_tlv_size(spec);
0241 
0242     sym = nfp_rtsym_lookup(rtbl, spec_rtsym->rtsym);
0243     if (!sym)
0244         return nfp_dump_error_tlv_size(spec);
0245 
0246     return ALIGN8(offsetof(struct nfp_dump_rtsym, rtsym) + key_len + 1) +
0247            ALIGN8(nfp_rtsym_size(sym));
0248 }
0249 
0250 static int
0251 nfp_add_tlv_size(struct nfp_pf *pf, struct nfp_dump_tl *tl, void *param)
0252 {
0253     struct nfp_dumpspec_csr *spec_csr;
0254     u32 *size = param;
0255     u32 hwinfo_size;
0256 
0257     switch (be32_to_cpu(tl->type)) {
0258     case NFP_DUMPSPEC_TYPE_FWNAME:
0259         *size += nfp_calc_fwname_tlv_size(pf);
0260         break;
0261     case NFP_DUMPSPEC_TYPE_CPP_CSR:
0262     case NFP_DUMPSPEC_TYPE_XPB_CSR:
0263     case NFP_DUMPSPEC_TYPE_ME_CSR:
0264         spec_csr = (struct nfp_dumpspec_csr *)tl;
0265         if (!nfp_csr_spec_valid(spec_csr))
0266             *size += nfp_dump_error_tlv_size(tl);
0267         else
0268             *size += ALIGN8(sizeof(struct nfp_dump_csr)) +
0269                  ALIGN8(be32_to_cpu(spec_csr->cpp.dump_length));
0270         break;
0271     case NFP_DUMPSPEC_TYPE_INDIRECT_ME_CSR:
0272         spec_csr = (struct nfp_dumpspec_csr *)tl;
0273         if (!nfp_csr_spec_valid(spec_csr))
0274             *size += nfp_dump_error_tlv_size(tl);
0275         else
0276             *size += ALIGN8(sizeof(struct nfp_dump_csr)) +
0277                  ALIGN8(be32_to_cpu(spec_csr->cpp.dump_length) *
0278                     NFP_IND_NUM_CONTEXTS);
0279         break;
0280     case NFP_DUMPSPEC_TYPE_RTSYM:
0281         *size += nfp_calc_rtsym_dump_sz(pf, tl);
0282         break;
0283     case NFP_DUMPSPEC_TYPE_HWINFO:
0284         hwinfo_size = nfp_hwinfo_get_packed_str_size(pf->hwinfo);
0285         *size += sizeof(struct nfp_dump_tl) + ALIGN8(hwinfo_size);
0286         break;
0287     case NFP_DUMPSPEC_TYPE_HWINFO_FIELD:
0288         *size += nfp_calc_hwinfo_field_sz(pf, tl);
0289         break;
0290     default:
0291         *size += nfp_dump_error_tlv_size(tl);
0292         break;
0293     }
0294 
0295     return 0;
0296 }
0297 
0298 static int
0299 nfp_calc_specific_level_size(struct nfp_pf *pf, struct nfp_dump_tl *dump_level,
0300                  void *param)
0301 {
0302     struct nfp_level_size *lev_sz = param;
0303 
0304     if (dump_level->type != lev_sz->requested_level)
0305         return 0;
0306 
0307     return nfp_traverse_tlvs(pf, dump_level->data,
0308                  be32_to_cpu(dump_level->length),
0309                  &lev_sz->total_size, nfp_add_tlv_size);
0310 }
0311 
0312 s64 nfp_net_dump_calculate_size(struct nfp_pf *pf, struct nfp_dumpspec *spec,
0313                 u32 flag)
0314 {
0315     struct nfp_level_size lev_sz;
0316     int err;
0317 
0318     lev_sz.requested_level = cpu_to_be32(flag);
0319     lev_sz.total_size = ALIGN8(sizeof(struct nfp_dump_prolog));
0320 
0321     err = nfp_traverse_tlvs(pf, spec->data, spec->size, &lev_sz,
0322                 nfp_calc_specific_level_size);
0323     if (err)
0324         return err;
0325 
0326     return lev_sz.total_size;
0327 }
0328 
0329 static int nfp_add_tlv(u32 type, u32 total_tlv_sz, struct nfp_dump_state *dump)
0330 {
0331     struct nfp_dump_tl *tl = dump->p;
0332 
0333     if (total_tlv_sz > dump->buf_size)
0334         return -ENOSPC;
0335 
0336     if (dump->buf_size - total_tlv_sz < dump->dumped_size)
0337         return -ENOSPC;
0338 
0339     tl->type = cpu_to_be32(type);
0340     tl->length = cpu_to_be32(total_tlv_sz - sizeof(*tl));
0341 
0342     dump->dumped_size += total_tlv_sz;
0343     dump->p += total_tlv_sz;
0344 
0345     return 0;
0346 }
0347 
0348 static int
0349 nfp_dump_error_tlv(struct nfp_dump_tl *spec, int error,
0350            struct nfp_dump_state *dump)
0351 {
0352     struct nfp_dump_error *dump_header = dump->p;
0353     u32 total_spec_size, total_size;
0354     int err;
0355 
0356     total_spec_size = sizeof(*spec) + be32_to_cpu(spec->length);
0357     total_size = ALIGN8(sizeof(*dump_header) + total_spec_size);
0358 
0359     err = nfp_add_tlv(NFP_DUMPSPEC_TYPE_ERROR, total_size, dump);
0360     if (err)
0361         return err;
0362 
0363     dump_header->error = cpu_to_be32(error);
0364     memcpy(dump_header->spec, spec, total_spec_size);
0365 
0366     return 0;
0367 }
0368 
0369 static int nfp_dump_fwname(struct nfp_pf *pf, struct nfp_dump_state *dump)
0370 {
0371     struct nfp_dump_tl *dump_header = dump->p;
0372     u32 fwname_len, total_size;
0373     const char *fwname;
0374     int err;
0375 
0376     fwname = nfp_mip_name(pf->mip);
0377     fwname_len = strlen(fwname);
0378     total_size = sizeof(*dump_header) + ALIGN8(fwname_len + 1);
0379 
0380     err = nfp_add_tlv(NFP_DUMPSPEC_TYPE_FWNAME, total_size, dump);
0381     if (err)
0382         return err;
0383 
0384     memcpy(dump_header->data, fwname, fwname_len);
0385 
0386     return 0;
0387 }
0388 
0389 static int
0390 nfp_dump_hwinfo(struct nfp_pf *pf, struct nfp_dump_tl *spec,
0391         struct nfp_dump_state *dump)
0392 {
0393     struct nfp_dump_tl *dump_header = dump->p;
0394     u32 hwinfo_size, total_size;
0395     char *hwinfo;
0396     int err;
0397 
0398     hwinfo = nfp_hwinfo_get_packed_strings(pf->hwinfo);
0399     hwinfo_size = nfp_hwinfo_get_packed_str_size(pf->hwinfo);
0400     total_size = sizeof(*dump_header) + ALIGN8(hwinfo_size);
0401 
0402     err = nfp_add_tlv(NFP_DUMPSPEC_TYPE_HWINFO, total_size, dump);
0403     if (err)
0404         return err;
0405 
0406     memcpy(dump_header->data, hwinfo, hwinfo_size);
0407 
0408     return 0;
0409 }
0410 
0411 static int nfp_dump_hwinfo_field(struct nfp_pf *pf, struct nfp_dump_tl *spec,
0412                  struct nfp_dump_state *dump)
0413 {
0414     struct nfp_dump_tl *dump_header = dump->p;
0415     u32 tl_len, key_len, val_len;
0416     const char *key, *value;
0417     u32 total_size;
0418     int err;
0419 
0420     tl_len = be32_to_cpu(spec->length);
0421     key_len = strnlen(spec->data, tl_len);
0422     if (key_len == tl_len)
0423         return nfp_dump_error_tlv(spec, -EINVAL, dump);
0424 
0425     key = spec->data;
0426     value = nfp_hwinfo_lookup(pf->hwinfo, key);
0427     if (!value)
0428         return nfp_dump_error_tlv(spec, -ENOENT, dump);
0429 
0430     val_len = strlen(value);
0431     total_size = sizeof(*dump_header) + ALIGN8(key_len + val_len + 2);
0432     err = nfp_add_tlv(NFP_DUMPSPEC_TYPE_HWINFO_FIELD, total_size, dump);
0433     if (err)
0434         return err;
0435 
0436     memcpy(dump_header->data, key, key_len + 1);
0437     memcpy(dump_header->data + key_len + 1, value, val_len + 1);
0438 
0439     return 0;
0440 }
0441 
0442 static bool is_xpb_read(struct nfp_dumpspec_cpp_isl_id *cpp_id)
0443 {
0444     return cpp_id->target == NFP_CPP_TARGET_ISLAND_XPB &&
0445            cpp_id->action == 0 && cpp_id->token == 0;
0446 }
0447 
0448 static int
0449 nfp_dump_csr_range(struct nfp_pf *pf, struct nfp_dumpspec_csr *spec_csr,
0450            struct nfp_dump_state *dump)
0451 {
0452     struct nfp_dump_csr *dump_header = dump->p;
0453     u32 reg_sz, header_size, total_size;
0454     u32 cpp_rd_addr, max_rd_addr;
0455     int bytes_read;
0456     void *dest;
0457     u32 cpp_id;
0458     int err;
0459 
0460     if (!nfp_csr_spec_valid(spec_csr))
0461         return nfp_dump_error_tlv(&spec_csr->tl, -EINVAL, dump);
0462 
0463     reg_sz = be32_to_cpu(spec_csr->register_width) / BITS_PER_BYTE;
0464     header_size = ALIGN8(sizeof(*dump_header));
0465     total_size = header_size +
0466              ALIGN8(be32_to_cpu(spec_csr->cpp.dump_length));
0467     dest = dump->p + header_size;
0468 
0469     err = nfp_add_tlv(be32_to_cpu(spec_csr->tl.type), total_size, dump);
0470     if (err)
0471         return err;
0472 
0473     dump_header->cpp = spec_csr->cpp;
0474     dump_header->register_width = spec_csr->register_width;
0475 
0476     cpp_id = nfp_get_numeric_cpp_id(&spec_csr->cpp.cpp_id);
0477     cpp_rd_addr = be32_to_cpu(spec_csr->cpp.offset);
0478     max_rd_addr = cpp_rd_addr + be32_to_cpu(spec_csr->cpp.dump_length);
0479 
0480     while (cpp_rd_addr < max_rd_addr) {
0481         if (is_xpb_read(&spec_csr->cpp.cpp_id)) {
0482             err = nfp_xpb_readl(pf->cpp, cpp_rd_addr, (u32 *)dest);
0483         } else {
0484             bytes_read = nfp_cpp_read(pf->cpp, cpp_id, cpp_rd_addr,
0485                           dest, reg_sz);
0486             err = bytes_read == reg_sz ? 0 : -EIO;
0487         }
0488         if (err) {
0489             dump_header->error = cpu_to_be32(err);
0490             dump_header->error_offset = cpu_to_be32(cpp_rd_addr);
0491             break;
0492         }
0493         cpp_rd_addr += reg_sz;
0494         dest += reg_sz;
0495     }
0496 
0497     return 0;
0498 }
0499 
0500 /* Write context to CSRCtxPtr, then read from it. Then the value can be read
0501  * from IndCtxStatus.
0502  */
0503 static int
0504 nfp_read_indirect_csr(struct nfp_cpp *cpp,
0505               struct nfp_dumpspec_cpp_isl_id cpp_params, u32 offset,
0506               u32 reg_sz, u32 context, void *dest)
0507 {
0508     u32 csr_ctx_ptr_offs;
0509     u32 cpp_id;
0510     int result;
0511 
0512     csr_ctx_ptr_offs = nfp_get_ind_csr_ctx_ptr_offs(offset);
0513     cpp_id = NFP_CPP_ISLAND_ID(cpp_params.target,
0514                    NFP_IND_ME_REFL_WR_SIG_INIT,
0515                    cpp_params.token, cpp_params.island);
0516     result = nfp_cpp_writel(cpp, cpp_id, csr_ctx_ptr_offs, context);
0517     if (result)
0518         return result;
0519 
0520     cpp_id = nfp_get_numeric_cpp_id(&cpp_params);
0521     result = nfp_cpp_read(cpp, cpp_id, csr_ctx_ptr_offs, dest, reg_sz);
0522     if (result != reg_sz)
0523         return result < 0 ? result : -EIO;
0524 
0525     result = nfp_cpp_read(cpp, cpp_id, offset, dest, reg_sz);
0526     if (result != reg_sz)
0527         return result < 0 ? result : -EIO;
0528 
0529     return 0;
0530 }
0531 
0532 static int
0533 nfp_read_all_indirect_csr_ctx(struct nfp_cpp *cpp,
0534                   struct nfp_dumpspec_csr *spec_csr, u32 address,
0535                   u32 reg_sz, void *dest)
0536 {
0537     u32 ctx;
0538     int err;
0539 
0540     for (ctx = 0; ctx < NFP_IND_NUM_CONTEXTS; ctx++) {
0541         err = nfp_read_indirect_csr(cpp, spec_csr->cpp.cpp_id, address,
0542                         reg_sz, ctx, dest + ctx * reg_sz);
0543         if (err)
0544             return err;
0545     }
0546 
0547     return 0;
0548 }
0549 
0550 static int
0551 nfp_dump_indirect_csr_range(struct nfp_pf *pf,
0552                 struct nfp_dumpspec_csr *spec_csr,
0553                 struct nfp_dump_state *dump)
0554 {
0555     struct nfp_dump_csr *dump_header = dump->p;
0556     u32 reg_sz, header_size, total_size;
0557     u32 cpp_rd_addr, max_rd_addr;
0558     u32 reg_data_length;
0559     void *dest;
0560     int err;
0561 
0562     if (!nfp_csr_spec_valid(spec_csr))
0563         return nfp_dump_error_tlv(&spec_csr->tl, -EINVAL, dump);
0564 
0565     reg_sz = be32_to_cpu(spec_csr->register_width) / BITS_PER_BYTE;
0566     header_size = ALIGN8(sizeof(*dump_header));
0567     reg_data_length = be32_to_cpu(spec_csr->cpp.dump_length) *
0568               NFP_IND_NUM_CONTEXTS;
0569     total_size = header_size + ALIGN8(reg_data_length);
0570     dest = dump->p + header_size;
0571 
0572     err = nfp_add_tlv(be32_to_cpu(spec_csr->tl.type), total_size, dump);
0573     if (err)
0574         return err;
0575 
0576     dump_header->cpp = spec_csr->cpp;
0577     dump_header->register_width = spec_csr->register_width;
0578 
0579     cpp_rd_addr = be32_to_cpu(spec_csr->cpp.offset);
0580     max_rd_addr = cpp_rd_addr + be32_to_cpu(spec_csr->cpp.dump_length);
0581     while (cpp_rd_addr < max_rd_addr) {
0582         err = nfp_read_all_indirect_csr_ctx(pf->cpp, spec_csr,
0583                             cpp_rd_addr, reg_sz, dest);
0584         if (err) {
0585             dump_header->error = cpu_to_be32(err);
0586             dump_header->error_offset = cpu_to_be32(cpp_rd_addr);
0587             break;
0588         }
0589         cpp_rd_addr += reg_sz;
0590         dest += reg_sz * NFP_IND_NUM_CONTEXTS;
0591     }
0592 
0593     return 0;
0594 }
0595 
0596 static int
0597 nfp_dump_single_rtsym(struct nfp_pf *pf, struct nfp_dumpspec_rtsym *spec,
0598               struct nfp_dump_state *dump)
0599 {
0600     struct nfp_dump_rtsym *dump_header = dump->p;
0601     struct nfp_dumpspec_cpp_isl_id cpp_params;
0602     struct nfp_rtsym_table *rtbl = pf->rtbl;
0603     u32 header_size, total_size, sym_size;
0604     const struct nfp_rtsym *sym;
0605     u32 tl_len, key_len;
0606     int bytes_read;
0607     void *dest;
0608     int err;
0609 
0610     tl_len = be32_to_cpu(spec->tl.length);
0611     key_len = strnlen(spec->rtsym, tl_len);
0612     if (key_len == tl_len)
0613         return nfp_dump_error_tlv(&spec->tl, -EINVAL, dump);
0614 
0615     sym = nfp_rtsym_lookup(rtbl, spec->rtsym);
0616     if (!sym)
0617         return nfp_dump_error_tlv(&spec->tl, -ENOENT, dump);
0618 
0619     sym_size = nfp_rtsym_size(sym);
0620     header_size =
0621         ALIGN8(offsetof(struct nfp_dump_rtsym, rtsym) + key_len + 1);
0622     total_size = header_size + ALIGN8(sym_size);
0623     dest = dump->p + header_size;
0624 
0625     err = nfp_add_tlv(be32_to_cpu(spec->tl.type), total_size, dump);
0626     if (err)
0627         return err;
0628 
0629     dump_header->padded_name_length =
0630         header_size - offsetof(struct nfp_dump_rtsym, rtsym);
0631     memcpy(dump_header->rtsym, spec->rtsym, key_len + 1);
0632     dump_header->cpp.dump_length = cpu_to_be32(sym_size);
0633 
0634     if (sym->type != NFP_RTSYM_TYPE_ABS) {
0635         cpp_params.target = sym->target;
0636         cpp_params.action = NFP_CPP_ACTION_RW;
0637         cpp_params.token  = 0;
0638         cpp_params.island = sym->domain;
0639         dump_header->cpp.cpp_id = cpp_params;
0640         dump_header->cpp.offset = cpu_to_be32(sym->addr);
0641     }
0642 
0643     bytes_read = nfp_rtsym_read(pf->cpp, sym, 0, dest, sym_size);
0644     if (bytes_read != sym_size) {
0645         if (bytes_read >= 0)
0646             bytes_read = -EIO;
0647         dump_header->error = cpu_to_be32(bytes_read);
0648     }
0649 
0650     return 0;
0651 }
0652 
0653 static int
0654 nfp_dump_for_tlv(struct nfp_pf *pf, struct nfp_dump_tl *tl, void *param)
0655 {
0656     struct nfp_dumpspec_rtsym *spec_rtsym;
0657     struct nfp_dump_state *dump = param;
0658     struct nfp_dumpspec_csr *spec_csr;
0659     int err;
0660 
0661     switch (be32_to_cpu(tl->type)) {
0662     case NFP_DUMPSPEC_TYPE_FWNAME:
0663         err = nfp_dump_fwname(pf, dump);
0664         if (err)
0665             return err;
0666         break;
0667     case NFP_DUMPSPEC_TYPE_CPP_CSR:
0668     case NFP_DUMPSPEC_TYPE_XPB_CSR:
0669     case NFP_DUMPSPEC_TYPE_ME_CSR:
0670         spec_csr = (struct nfp_dumpspec_csr *)tl;
0671         err = nfp_dump_csr_range(pf, spec_csr, dump);
0672         if (err)
0673             return err;
0674         break;
0675     case NFP_DUMPSPEC_TYPE_INDIRECT_ME_CSR:
0676         spec_csr = (struct nfp_dumpspec_csr *)tl;
0677         err = nfp_dump_indirect_csr_range(pf, spec_csr, dump);
0678         if (err)
0679             return err;
0680         break;
0681     case NFP_DUMPSPEC_TYPE_RTSYM:
0682         spec_rtsym = (struct nfp_dumpspec_rtsym *)tl;
0683         err = nfp_dump_single_rtsym(pf, spec_rtsym, dump);
0684         if (err)
0685             return err;
0686         break;
0687     case NFP_DUMPSPEC_TYPE_HWINFO:
0688         err = nfp_dump_hwinfo(pf, tl, dump);
0689         if (err)
0690             return err;
0691         break;
0692     case NFP_DUMPSPEC_TYPE_HWINFO_FIELD:
0693         err = nfp_dump_hwinfo_field(pf, tl, dump);
0694         if (err)
0695             return err;
0696         break;
0697     default:
0698         err = nfp_dump_error_tlv(tl, -EOPNOTSUPP, dump);
0699         if (err)
0700             return err;
0701     }
0702 
0703     return 0;
0704 }
0705 
0706 static int
0707 nfp_dump_specific_level(struct nfp_pf *pf, struct nfp_dump_tl *dump_level,
0708             void *param)
0709 {
0710     struct nfp_dump_state *dump = param;
0711 
0712     if (dump_level->type != dump->requested_level)
0713         return 0;
0714 
0715     return nfp_traverse_tlvs(pf, dump_level->data,
0716                  be32_to_cpu(dump_level->length), dump,
0717                  nfp_dump_for_tlv);
0718 }
0719 
0720 static int nfp_dump_populate_prolog(struct nfp_dump_state *dump)
0721 {
0722     struct nfp_dump_prolog *prolog = dump->p;
0723     u32 total_size;
0724     int err;
0725 
0726     total_size = ALIGN8(sizeof(*prolog));
0727 
0728     err = nfp_add_tlv(NFP_DUMPSPEC_TYPE_PROLOG, total_size, dump);
0729     if (err)
0730         return err;
0731 
0732     prolog->dump_level = dump->requested_level;
0733 
0734     return 0;
0735 }
0736 
0737 int nfp_net_dump_populate_buffer(struct nfp_pf *pf, struct nfp_dumpspec *spec,
0738                  struct ethtool_dump *dump_param, void *dest)
0739 {
0740     struct nfp_dump_state dump;
0741     int err;
0742 
0743     dump.requested_level = cpu_to_be32(dump_param->flag);
0744     dump.dumped_size = 0;
0745     dump.p = dest;
0746     dump.buf_size = dump_param->len;
0747 
0748     err = nfp_dump_populate_prolog(&dump);
0749     if (err)
0750         return err;
0751 
0752     err = nfp_traverse_tlvs(pf, spec->data, spec->size, &dump,
0753                 nfp_dump_specific_level);
0754     if (err)
0755         return err;
0756 
0757     /* Set size of actual dump, to trigger warning if different from
0758      * calculated size.
0759      */
0760     dump_param->len = dump.dumped_size;
0761 
0762     return 0;
0763 }