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 <linux/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  
28  struct 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  
40  struct nfp_rtsym_table {
41  	struct nfp_cpp *cpp;
42  	int num;
43  	char *strtab;
44  	struct nfp_rtsym symtab[];
45  };
46  
nfp_meid(u8 island_id,u8 menum)47  static 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  
53  static void
nfp_rtsym_sw_entry_init(struct nfp_rtsym_table * cache,u32 strtab_size,struct nfp_rtsym * sw,struct nfp_rtsym_entry * fw)54  nfp_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  
nfp_rtsym_table_read(struct nfp_cpp * cpp)82  struct 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  
94  struct nfp_rtsym_table *
__nfp_rtsym_table_read(struct nfp_cpp * cpp,const struct nfp_mip * mip)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  
149  exit_free_cache:
150  	kfree(cache);
151  exit_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   */
nfp_rtsym_count(struct nfp_rtsym_table * rtbl)162  int 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   */
nfp_rtsym_get(struct nfp_rtsym_table * rtbl,int idx)176  const 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   */
193  const struct nfp_rtsym *
nfp_rtsym_lookup(struct nfp_rtsym_table * rtbl,const char * name)194  nfp_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  
nfp_rtsym_size(const struct nfp_rtsym * sym)208  u64 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  		fallthrough;
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  
225  static int
nfp_rtsym_to_dest(struct nfp_cpp * cpp,const struct nfp_rtsym * sym,u8 action,u8 token,u64 off,u32 * cpp_id,u64 * addr)226  nfp_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  
__nfp_rtsym_read(struct nfp_cpp * cpp,const struct nfp_rtsym * sym,u8 action,u8 token,u64 off,void * buf,size_t len)257  int __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  
nfp_rtsym_read(struct nfp_cpp * cpp,const struct nfp_rtsym * sym,u64 off,void * buf,size_t len)288  int 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  
__nfp_rtsym_readl(struct nfp_cpp * cpp,const struct nfp_rtsym * sym,u8 action,u8 token,u64 off,u32 * value)294  int __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  
nfp_rtsym_readl(struct nfp_cpp * cpp,const struct nfp_rtsym * sym,u64 off,u32 * value)314  int 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  
__nfp_rtsym_readq(struct nfp_cpp * cpp,const struct nfp_rtsym * sym,u8 action,u8 token,u64 off,u64 * value)320  int __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  
nfp_rtsym_readq(struct nfp_cpp * cpp,const struct nfp_rtsym * sym,u64 off,u64 * value)345  int 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  
__nfp_rtsym_write(struct nfp_cpp * cpp,const struct nfp_rtsym * sym,u8 action,u8 token,u64 off,void * buf,size_t len)351  int __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  
nfp_rtsym_write(struct nfp_cpp * cpp,const struct nfp_rtsym * sym,u64 off,void * buf,size_t len)373  int 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  
__nfp_rtsym_writel(struct nfp_cpp * cpp,const struct nfp_rtsym * sym,u8 action,u8 token,u64 off,u32 value)379  int __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  
nfp_rtsym_writel(struct nfp_cpp * cpp,const struct nfp_rtsym * sym,u64 off,u32 value)399  int 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  
__nfp_rtsym_writeq(struct nfp_cpp * cpp,const struct nfp_rtsym * sym,u8 action,u8 token,u64 off,u64 value)405  int __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  
nfp_rtsym_writeq(struct nfp_cpp * cpp,const struct nfp_rtsym * sym,u64 off,u64 value)425  int 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   */
nfp_rtsym_read_le(struct nfp_rtsym_table * rtbl,const char * name,int * error)443  u64 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  
473  exit:
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   */
nfp_rtsym_write_le(struct nfp_rtsym_table * rtbl,const char * name,u64 value)494  int 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  
522  u8 __iomem *
nfp_rtsym_map(struct nfp_rtsym_table * rtbl,const char * name,const char * id,unsigned int min_size,struct nfp_cpp_area ** area)523  nfp_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