linux/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_rtsym.c
<<
>>
Prefs
   1// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
   2/* Copyright (C) 2015-2018 Netronome Systems, Inc. */
   3
   4/*
   5 * nfp_rtsym.c
   6 * Interface for accessing run-time symbol table
   7 * Authors: Jakub Kicinski <jakub.kicinski@netronome.com>
   8 *          Jason McMullan <jason.mcmullan@netronome.com>
   9 *          Espen Skoglund <espen.skoglund@netronome.com>
  10 *          Francois H. Theron <francois.theron@netronome.com>
  11 */
  12
  13#include <asm/unaligned.h>
  14#include <linux/kernel.h>
  15#include <linux/module.h>
  16#include <linux/slab.h>
  17#include <linux/io-64-nonatomic-hi-lo.h>
  18
  19#include "nfp.h"
  20#include "nfp_cpp.h"
  21#include "nfp_nffw.h"
  22#include "nfp6000/nfp6000.h"
  23
  24/* These need to match the linker */
  25#define SYM_TGT_LMEM            0
  26#define SYM_TGT_EMU_CACHE       0x17
  27
  28struct nfp_rtsym_entry {
  29        u8      type;
  30        u8      target;
  31        u8      island;
  32        u8      addr_hi;
  33        __le32  addr_lo;
  34        __le16  name;
  35        u8      menum;
  36        u8      size_hi;
  37        __le32  size_lo;
  38};
  39
  40struct nfp_rtsym_table {
  41        struct nfp_cpp *cpp;
  42        int num;
  43        char *strtab;
  44        struct nfp_rtsym symtab[];
  45};
  46
  47static int nfp_meid(u8 island_id, u8 menum)
  48{
  49        return (island_id & 0x3F) == island_id && menum < 12 ?
  50                (island_id << 4) | (menum + 4) : -1;
  51}
  52
  53static void
  54nfp_rtsym_sw_entry_init(struct nfp_rtsym_table *cache, u32 strtab_size,
  55                        struct nfp_rtsym *sw, struct nfp_rtsym_entry *fw)
  56{
  57        sw->type = fw->type;
  58        sw->name = cache->strtab + le16_to_cpu(fw->name) % strtab_size;
  59        sw->addr = ((u64)fw->addr_hi << 32) | le32_to_cpu(fw->addr_lo);
  60        sw->size = ((u64)fw->size_hi << 32) | le32_to_cpu(fw->size_lo);
  61
  62        switch (fw->target) {
  63        case SYM_TGT_LMEM:
  64                sw->target = NFP_RTSYM_TARGET_LMEM;
  65                break;
  66        case SYM_TGT_EMU_CACHE:
  67                sw->target = NFP_RTSYM_TARGET_EMU_CACHE;
  68                break;
  69        default:
  70                sw->target = fw->target;
  71                break;
  72        }
  73
  74        if (fw->menum != 0xff)
  75                sw->domain = nfp_meid(fw->island, fw->menum);
  76        else if (fw->island != 0xff)
  77                sw->domain = fw->island;
  78        else
  79                sw->domain = -1;
  80}
  81
  82struct nfp_rtsym_table *nfp_rtsym_table_read(struct nfp_cpp *cpp)
  83{
  84        struct nfp_rtsym_table *rtbl;
  85        const struct nfp_mip *mip;
  86
  87        mip = nfp_mip_open(cpp);
  88        rtbl = __nfp_rtsym_table_read(cpp, mip);
  89        nfp_mip_close(mip);
  90
  91        return rtbl;
  92}
  93
  94struct nfp_rtsym_table *
  95__nfp_rtsym_table_read(struct nfp_cpp *cpp, const struct nfp_mip *mip)
  96{
  97        const u32 dram = NFP_CPP_ID(NFP_CPP_TARGET_MU, NFP_CPP_ACTION_RW, 0) |
  98                NFP_ISL_EMEM0;
  99        u32 strtab_addr, symtab_addr, strtab_size, symtab_size;
 100        struct nfp_rtsym_entry *rtsymtab;
 101        struct nfp_rtsym_table *cache;
 102        int err, n, size;
 103
 104        if (!mip)
 105                return NULL;
 106
 107        nfp_mip_strtab(mip, &strtab_addr, &strtab_size);
 108        nfp_mip_symtab(mip, &symtab_addr, &symtab_size);
 109
 110        if (!symtab_size || !strtab_size || symtab_size % sizeof(*rtsymtab))
 111                return NULL;
 112
 113        /* Align to 64 bits */
 114        symtab_size = round_up(symtab_size, 8);
 115        strtab_size = round_up(strtab_size, 8);
 116
 117        rtsymtab = kmalloc(symtab_size, GFP_KERNEL);
 118        if (!rtsymtab)
 119                return NULL;
 120
 121        size = sizeof(*cache);
 122        size += symtab_size / sizeof(*rtsymtab) * sizeof(struct nfp_rtsym);
 123        size += strtab_size + 1;
 124        cache = kmalloc(size, GFP_KERNEL);
 125        if (!cache)
 126                goto exit_free_rtsym_raw;
 127
 128        cache->cpp = cpp;
 129        cache->num = symtab_size / sizeof(*rtsymtab);
 130        cache->strtab = (void *)&cache->symtab[cache->num];
 131
 132        err = nfp_cpp_read(cpp, dram, symtab_addr, rtsymtab, symtab_size);
 133        if (err != symtab_size)
 134                goto exit_free_cache;
 135
 136        err = nfp_cpp_read(cpp, dram, strtab_addr, cache->strtab, strtab_size);
 137        if (err != strtab_size)
 138                goto exit_free_cache;
 139        cache->strtab[strtab_size] = '\0';
 140
 141        for (n = 0; n < cache->num; n++)
 142                nfp_rtsym_sw_entry_init(cache, strtab_size,
 143                                        &cache->symtab[n], &rtsymtab[n]);
 144
 145        kfree(rtsymtab);
 146
 147        return cache;
 148
 149exit_free_cache:
 150        kfree(cache);
 151exit_free_rtsym_raw:
 152        kfree(rtsymtab);
 153        return NULL;
 154}
 155
 156/**
 157 * nfp_rtsym_count() - Get the number of RTSYM descriptors
 158 * @rtbl:       NFP RTsym table
 159 *
 160 * Return: Number of RTSYM descriptors
 161 */
 162int nfp_rtsym_count(struct nfp_rtsym_table *rtbl)
 163{
 164        if (!rtbl)
 165                return -EINVAL;
 166        return rtbl->num;
 167}
 168
 169/**
 170 * nfp_rtsym_get() - Get the Nth RTSYM descriptor
 171 * @rtbl:       NFP RTsym table
 172 * @idx:        Index (0-based) of the RTSYM descriptor
 173 *
 174 * Return: const pointer to a struct nfp_rtsym descriptor, or NULL
 175 */
 176const struct nfp_rtsym *nfp_rtsym_get(struct nfp_rtsym_table *rtbl, int idx)
 177{
 178        if (!rtbl)
 179                return NULL;
 180        if (idx >= rtbl->num)
 181                return NULL;
 182
 183        return &rtbl->symtab[idx];
 184}
 185
 186/**
 187 * nfp_rtsym_lookup() - Return the RTSYM descriptor for a symbol name
 188 * @rtbl:       NFP RTsym table
 189 * @name:       Symbol name
 190 *
 191 * Return: const pointer to a struct nfp_rtsym descriptor, or NULL
 192 */
 193const struct nfp_rtsym *
 194nfp_rtsym_lookup(struct nfp_rtsym_table *rtbl, const char *name)
 195{
 196        int n;
 197
 198        if (!rtbl)
 199                return NULL;
 200
 201        for (n = 0; n < rtbl->num; n++)
 202                if (strcmp(name, rtbl->symtab[n].name) == 0)
 203                        return &rtbl->symtab[n];
 204
 205        return NULL;
 206}
 207
 208u64 nfp_rtsym_size(const struct nfp_rtsym *sym)
 209{
 210        switch (sym->type) {
 211        case NFP_RTSYM_TYPE_NONE:
 212                pr_err("rtsym '%s': type NONE\n", sym->name);
 213                return 0;
 214        default:
 215                pr_warn("rtsym '%s': unknown type: %d\n", sym->name, sym->type);
 216                /* fall through */
 217        case NFP_RTSYM_TYPE_OBJECT:
 218        case NFP_RTSYM_TYPE_FUNCTION:
 219                return sym->size;
 220        case NFP_RTSYM_TYPE_ABS:
 221                return sizeof(u64);
 222        }
 223}
 224
 225static int
 226nfp_rtsym_to_dest(struct nfp_cpp *cpp, const struct nfp_rtsym *sym,
 227                  u8 action, u8 token, u64 off, u32 *cpp_id, u64 *addr)
 228{
 229        if (sym->type != NFP_RTSYM_TYPE_OBJECT) {
 230                nfp_err(cpp, "rtsym '%s': direct access to non-object rtsym\n",
 231                        sym->name);
 232                return -EINVAL;
 233        }
 234
 235        *addr = sym->addr + off;
 236
 237        if (sym->target == NFP_RTSYM_TARGET_EMU_CACHE) {
 238                int locality_off = nfp_cpp_mu_locality_lsb(cpp);
 239
 240                *addr &= ~(NFP_MU_ADDR_ACCESS_TYPE_MASK << locality_off);
 241                *addr |= NFP_MU_ADDR_ACCESS_TYPE_DIRECT << locality_off;
 242
 243                *cpp_id = NFP_CPP_ISLAND_ID(NFP_CPP_TARGET_MU, action, token,
 244                                            sym->domain);
 245        } else if (sym->target < 0) {
 246                nfp_err(cpp, "rtsym '%s': unhandled target encoding: %d\n",
 247                        sym->name, sym->target);
 248                return -EINVAL;
 249        } else {
 250                *cpp_id = NFP_CPP_ISLAND_ID(sym->target, action, token,
 251                                            sym->domain);
 252        }
 253
 254        return 0;
 255}
 256
 257int __nfp_rtsym_read(struct nfp_cpp *cpp, const struct nfp_rtsym *sym,
 258                     u8 action, u8 token, u64 off, void *buf, size_t len)
 259{
 260        u64 sym_size = nfp_rtsym_size(sym);
 261        u32 cpp_id;
 262        u64 addr;
 263        int err;
 264
 265        if (off > sym_size) {
 266                nfp_err(cpp, "rtsym '%s': read out of bounds: off: %lld + len: %zd > size: %lld\n",
 267                        sym->name, off, len, sym_size);
 268                return -ENXIO;
 269        }
 270        len = min_t(size_t, len, sym_size - off);
 271
 272        if (sym->type == NFP_RTSYM_TYPE_ABS) {
 273                u8 tmp[8];
 274
 275                put_unaligned_le64(sym->addr, tmp);
 276                memcpy(buf, &tmp[off], len);
 277
 278                return len;
 279        }
 280
 281        err = nfp_rtsym_to_dest(cpp, sym, action, token, off, &cpp_id, &addr);
 282        if (err)
 283                return err;
 284
 285        return nfp_cpp_read(cpp, cpp_id, addr, buf, len);
 286}
 287
 288int nfp_rtsym_read(struct nfp_cpp *cpp, const struct nfp_rtsym *sym, u64 off,
 289                   void *buf, size_t len)
 290{
 291        return __nfp_rtsym_read(cpp, sym, NFP_CPP_ACTION_RW, 0, off, buf, len);
 292}
 293
 294int __nfp_rtsym_readl(struct nfp_cpp *cpp, const struct nfp_rtsym *sym,
 295                      u8 action, u8 token, u64 off, u32 *value)
 296{
 297        u32 cpp_id;
 298        u64 addr;
 299        int err;
 300
 301        if (off + 4 > nfp_rtsym_size(sym)) {
 302                nfp_err(cpp, "rtsym '%s': readl out of bounds: off: %lld + 4 > size: %lld\n",
 303                        sym->name, off, nfp_rtsym_size(sym));
 304                return -ENXIO;
 305        }
 306
 307        err = nfp_rtsym_to_dest(cpp, sym, action, token, off, &cpp_id, &addr);
 308        if (err)
 309                return err;
 310
 311        return nfp_cpp_readl(cpp, cpp_id, addr, value);
 312}
 313
 314int nfp_rtsym_readl(struct nfp_cpp *cpp, const struct nfp_rtsym *sym, u64 off,
 315                    u32 *value)
 316{
 317        return __nfp_rtsym_readl(cpp, sym, NFP_CPP_ACTION_RW, 0, off, value);
 318}
 319
 320int __nfp_rtsym_readq(struct nfp_cpp *cpp, const struct nfp_rtsym *sym,
 321                      u8 action, u8 token, u64 off, u64 *value)
 322{
 323        u32 cpp_id;
 324        u64 addr;
 325        int err;
 326
 327        if (off + 8 > nfp_rtsym_size(sym)) {
 328                nfp_err(cpp, "rtsym '%s': readq out of bounds: off: %lld + 8 > size: %lld\n",
 329                        sym->name, off, nfp_rtsym_size(sym));
 330                return -ENXIO;
 331        }
 332
 333        if (sym->type == NFP_RTSYM_TYPE_ABS) {
 334                *value = sym->addr;
 335                return 0;
 336        }
 337
 338        err = nfp_rtsym_to_dest(cpp, sym, action, token, off, &cpp_id, &addr);
 339        if (err)
 340                return err;
 341
 342        return nfp_cpp_readq(cpp, cpp_id, addr, value);
 343}
 344
 345int nfp_rtsym_readq(struct nfp_cpp *cpp, const struct nfp_rtsym *sym, u64 off,
 346                    u64 *value)
 347{
 348        return __nfp_rtsym_readq(cpp, sym, NFP_CPP_ACTION_RW, 0, off, value);
 349}
 350
 351int __nfp_rtsym_write(struct nfp_cpp *cpp, const struct nfp_rtsym *sym,
 352                      u8 action, u8 token, u64 off, void *buf, size_t len)
 353{
 354        u64 sym_size = nfp_rtsym_size(sym);
 355        u32 cpp_id;
 356        u64 addr;
 357        int err;
 358
 359        if (off > sym_size) {
 360                nfp_err(cpp, "rtsym '%s': write out of bounds: off: %lld + len: %zd > size: %lld\n",
 361                        sym->name, off, len, sym_size);
 362                return -ENXIO;
 363        }
 364        len = min_t(size_t, len, sym_size - off);
 365
 366        err = nfp_rtsym_to_dest(cpp, sym, action, token, off, &cpp_id, &addr);
 367        if (err)
 368                return err;
 369
 370        return nfp_cpp_write(cpp, cpp_id, addr, buf, len);
 371}
 372
 373int nfp_rtsym_write(struct nfp_cpp *cpp, const struct nfp_rtsym *sym, u64 off,
 374                    void *buf, size_t len)
 375{
 376        return __nfp_rtsym_write(cpp, sym, NFP_CPP_ACTION_RW, 0, off, buf, len);
 377}
 378
 379int __nfp_rtsym_writel(struct nfp_cpp *cpp, const struct nfp_rtsym *sym,
 380                       u8 action, u8 token, u64 off, u32 value)
 381{
 382        u32 cpp_id;
 383        u64 addr;
 384        int err;
 385
 386        if (off + 4 > nfp_rtsym_size(sym)) {
 387                nfp_err(cpp, "rtsym '%s': writel out of bounds: off: %lld + 4 > size: %lld\n",
 388                        sym->name, off, nfp_rtsym_size(sym));
 389                return -ENXIO;
 390        }
 391
 392        err = nfp_rtsym_to_dest(cpp, sym, action, token, off, &cpp_id, &addr);
 393        if (err)
 394                return err;
 395
 396        return nfp_cpp_writel(cpp, cpp_id, addr, value);
 397}
 398
 399int nfp_rtsym_writel(struct nfp_cpp *cpp, const struct nfp_rtsym *sym, u64 off,
 400                     u32 value)
 401{
 402        return __nfp_rtsym_writel(cpp, sym, NFP_CPP_ACTION_RW, 0, off, value);
 403}
 404
 405int __nfp_rtsym_writeq(struct nfp_cpp *cpp, const struct nfp_rtsym *sym,
 406                       u8 action, u8 token, u64 off, u64 value)
 407{
 408        u32 cpp_id;
 409        u64 addr;
 410        int err;
 411
 412        if (off + 8 > nfp_rtsym_size(sym)) {
 413                nfp_err(cpp, "rtsym '%s': writeq out of bounds: off: %lld + 8 > size: %lld\n",
 414                        sym->name, off, nfp_rtsym_size(sym));
 415                return -ENXIO;
 416        }
 417
 418        err = nfp_rtsym_to_dest(cpp, sym, action, token, off, &cpp_id, &addr);
 419        if (err)
 420                return err;
 421
 422        return nfp_cpp_writeq(cpp, cpp_id, addr, value);
 423}
 424
 425int nfp_rtsym_writeq(struct nfp_cpp *cpp, const struct nfp_rtsym *sym, u64 off,
 426                     u64 value)
 427{
 428        return __nfp_rtsym_writeq(cpp, sym, NFP_CPP_ACTION_RW, 0, off, value);
 429}
 430
 431/**
 432 * nfp_rtsym_read_le() - Read a simple unsigned scalar value from symbol
 433 * @rtbl:       NFP RTsym table
 434 * @name:       Symbol name
 435 * @error:      Poniter to error code (optional)
 436 *
 437 * Lookup a symbol, map, read it and return it's value. Value of the symbol
 438 * will be interpreted as a simple little-endian unsigned value. Symbol can
 439 * be 4 or 8 bytes in size.
 440 *
 441 * Return: value read, on error sets the error and returns ~0ULL.
 442 */
 443u64 nfp_rtsym_read_le(struct nfp_rtsym_table *rtbl, const char *name,
 444                      int *error)
 445{
 446        const struct nfp_rtsym *sym;
 447        u32 val32;
 448        u64 val;
 449        int err;
 450
 451        sym = nfp_rtsym_lookup(rtbl, name);
 452        if (!sym) {
 453                err = -ENOENT;
 454                goto exit;
 455        }
 456
 457        switch (nfp_rtsym_size(sym)) {
 458        case 4:
 459                err = nfp_rtsym_readl(rtbl->cpp, sym, 0, &val32);
 460                val = val32;
 461                break;
 462        case 8:
 463                err = nfp_rtsym_readq(rtbl->cpp, sym, 0, &val);
 464                break;
 465        default:
 466                nfp_err(rtbl->cpp,
 467                        "rtsym '%s': unsupported or non-scalar size: %lld\n",
 468                        name, nfp_rtsym_size(sym));
 469                err = -EINVAL;
 470                break;
 471        }
 472
 473exit:
 474        if (error)
 475                *error = err;
 476
 477        if (err)
 478                return ~0ULL;
 479        return val;
 480}
 481
 482/**
 483 * nfp_rtsym_write_le() - Write an unsigned scalar value to a symbol
 484 * @rtbl:       NFP RTsym table
 485 * @name:       Symbol name
 486 * @value:      Value to write
 487 *
 488 * Lookup a symbol and write a value to it. Symbol can be 4 or 8 bytes in size.
 489 * If 4 bytes then the lower 32-bits of 'value' are used. Value will be
 490 * written as simple little-endian unsigned value.
 491 *
 492 * Return: 0 on success or error code.
 493 */
 494int nfp_rtsym_write_le(struct nfp_rtsym_table *rtbl, const char *name,
 495                       u64 value)
 496{
 497        const struct nfp_rtsym *sym;
 498        int err;
 499
 500        sym = nfp_rtsym_lookup(rtbl, name);
 501        if (!sym)
 502                return -ENOENT;
 503
 504        switch (nfp_rtsym_size(sym)) {
 505        case 4:
 506                err = nfp_rtsym_writel(rtbl->cpp, sym, 0, value);
 507                break;
 508        case 8:
 509                err = nfp_rtsym_writeq(rtbl->cpp, sym, 0, value);
 510                break;
 511        default:
 512                nfp_err(rtbl->cpp,
 513                        "rtsym '%s': unsupported or non-scalar size: %lld\n",
 514                        name, nfp_rtsym_size(sym));
 515                err = -EINVAL;
 516                break;
 517        }
 518
 519        return err;
 520}
 521
 522u8 __iomem *
 523nfp_rtsym_map(struct nfp_rtsym_table *rtbl, const char *name, const char *id,
 524              unsigned int min_size, struct nfp_cpp_area **area)
 525{
 526        const struct nfp_rtsym *sym;
 527        u8 __iomem *mem;
 528        u32 cpp_id;
 529        u64 addr;
 530        int err;
 531
 532        sym = nfp_rtsym_lookup(rtbl, name);
 533        if (!sym)
 534                return (u8 __iomem *)ERR_PTR(-ENOENT);
 535
 536        err = nfp_rtsym_to_dest(rtbl->cpp, sym, NFP_CPP_ACTION_RW, 0, 0,
 537                                &cpp_id, &addr);
 538        if (err) {
 539                nfp_err(rtbl->cpp, "rtsym '%s': mapping failed\n", name);
 540                return (u8 __iomem *)ERR_PTR(err);
 541        }
 542
 543        if (sym->size < min_size) {
 544                nfp_err(rtbl->cpp, "rtsym '%s': too small\n", name);
 545                return (u8 __iomem *)ERR_PTR(-EINVAL);
 546        }
 547
 548        mem = nfp_cpp_map_area(rtbl->cpp, id, cpp_id, addr, sym->size, area);
 549        if (IS_ERR(mem)) {
 550                nfp_err(rtbl->cpp, "rtysm '%s': failed to map: %ld\n",
 551                        name, PTR_ERR(mem));
 552                return mem;
 553        }
 554
 555        return mem;
 556}
 557