linux/drivers/net/ethernet/netronome/nfp/nfp_net_debugdump.c
<<
>>
Prefs
   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
  47enum 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 */
  66struct 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 */
  73struct nfp_dumpspec_cpp_isl_id {
  74        u8 target;
  75        u8 action;
  76        u8 token;
  77        u8 island;
  78};
  79
  80struct 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 */
  87struct nfp_dumpspec_csr {
  88        struct nfp_dump_tl tl;
  89        struct nfp_dump_common_cpp cpp;
  90        __be32 register_width;  /* in bits */
  91};
  92
  93struct nfp_dumpspec_rtsym {
  94        struct nfp_dump_tl tl;
  95        char rtsym[0];
  96};
  97
  98/* header for register dumpable */
  99struct 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
 107struct 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
 116struct nfp_dump_prolog {
 117        struct nfp_dump_tl tl;
 118        __be32 dump_level;
 119};
 120
 121struct 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 */
 129struct 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 */
 135struct 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
 142typedef int (*nfp_tlv_visit)(struct nfp_pf *pf, struct nfp_dump_tl *tl,
 143                             void *param);
 144
 145static int
 146nfp_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
 179static 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
 185struct nfp_dumpspec *
 186nfp_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
 218static 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
 224static 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
 231static 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
 248static 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
 262static int
 263nfp_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
 290static int
 291nfp_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
 338static int
 339nfp_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
 352s64 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
 369static 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
 388static int
 389nfp_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
 409static 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
 429static int
 430nfp_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
 451static 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
 482static 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
 488static int
 489nfp_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 */
 543static int
 544nfp_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
 572static int
 573nfp_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
 590static int
 591nfp_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
 636static int
 637nfp_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
 701static int
 702nfp_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
 754static int
 755nfp_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
 768static 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
 785int 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