linux/drivers/net/ethernet/netronome/nfp/nfp_net_debugdump.c
<<
>>
Prefs
   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}
 764