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