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