xref: /freebsd/sys/dev/ntb/test/ntb_tool.c (revision fdafd315ad0d0f28a11b9fb4476a9ab059c62b92)
1 /*-
2  * This file is provided under a dual BSD/GPLv2 license.  When using or
3  * redistributing this file, you may do so under either license.
4  *
5  * GPL LICENSE SUMMARY
6  *
7  * Copyright (c) 2019 Advanced Micro Devices, Inc. All Rights Reserved.
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of version 2 of the GNU General Public License as
11  * published by the Free Software Foundation.
12  *
13  * This program is distributed in the hope that it will be useful, but
14  * WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * General Public License for more details.
17  *
18  * BSD LICENSE
19  *
20  * Copyright (c) 2019 Advanced Micro Devices, Inc. All Rights Reserved.
21  *
22  * Redistribution and use in source and binary forms, with or without
23  * modification, are permitted provided that the following conditions
24  * are met:
25  *
26  *   * Redistributions of source code must retain the above copyright
27  *     notice, this list of conditions and the following disclaimer.
28  *   * Redistributions in binary form must reproduce the above copy
29  *     notice, this list of conditions and the following disclaimer in
30  *     the documentation and/or other materials provided with the
31  *     distribution.
32  *   * Neither the name of Advanced Micro Devices, Inc nor the names of its
33  *     contributors may be used to endorse or promote products derived
34  *     from this software without specific prior written permission.
35  *
36  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
37  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
38  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
39  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
40  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
41  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
42  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
43  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
44  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
45  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
46  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
47  *
48  * PCIe NTB Debugging Tool FreeBSD driver
49  */
50 
51 /*
52  * How to use this tool, by example.
53  *
54  * List of sysctl for ntb_tool driver.
55  * root@local# sysctl -a | grep ntb_tool
56  * dev.ntb_tool.0.peer0.spad7: 0x0
57  * dev.ntb_tool.0.peer0.spad6: 0x0
58  * dev.ntb_tool.0.peer0.spad5: 0x0
59  * dev.ntb_tool.0.peer0.spad4: 0x0
60  * dev.ntb_tool.0.peer0.spad3: 0x0
61  * dev.ntb_tool.0.peer0.spad2: 0x0
62  * dev.ntb_tool.0.peer0.spad1: 0x0
63  * dev.ntb_tool.0.peer0.spad0: 0x0
64  * dev.ntb_tool.0.peer0.mw_trans2:
65  * dev.ntb_tool.0.peer0.mw_trans1:
66  * dev.ntb_tool.0.peer0.mw_trans0:
67  * dev.ntb_tool.0.peer0.peer_mw2:
68  * dev.ntb_tool.0.peer0.peer_mw1:
69  * dev.ntb_tool.0.peer0.peer_mw0:
70  * dev.ntb_tool.0.peer0.mw2:
71  * dev.ntb_tool.0.peer0.mw1:
72  * dev.ntb_tool.0.peer0.mw0:
73  * dev.ntb_tool.0.peer0.link_event: 0x0
74  * dev.ntb_tool.0.peer0.link: Y
75  * dev.ntb_tool.0.peer0.port: 1
76  * dev.ntb_tool.0.spad7: 0x0
77  * dev.ntb_tool.0.spad6: 0x0
78  * dev.ntb_tool.0.spad5: 0x0
79  * dev.ntb_tool.0.spad4: 0x0
80  * dev.ntb_tool.0.spad3: 0x0
81  * dev.ntb_tool.0.spad2: 0x0
82  * dev.ntb_tool.0.spad1: 0x0
83  * dev.ntb_tool.0.spad0: 0x0
84  * dev.ntb_tool.0.db: 0x0
85  * dev.ntb_tool.0.db_event: 0x0
86  * dev.ntb_tool.0.db_mask: 0xffff
87  * dev.ntb_tool.0.db_valid_mask: 0xffff
88  * dev.ntb_tool.0.peer_db: 0x0
89  * dev.ntb_tool.0.peer_db_mask: 0xffff
90  * dev.ntb_tool.0.link: Y
91  * dev.ntb_tool.0.port: 0
92  *
93  * The above example list shows
94  * 1) three memory windows,
95  * 1) eight scratchpad registers.
96  * 3) doorbell config.
97  * 4) link config.
98  * 2) One peer.
99  *
100  * Based on the underlined ntb_hw driver config & connection topology, these
101  * things might differ.
102  *-----------------------------------------------------------------------------
103  * Eg: check local/peer port information.
104  *
105  * # Get local device port number
106  * root@local# sysctl dev.ntb_tool.0.port
107  *
108  * # Check peer device port number
109  * root@local# sysctl dev.ntb_tool.0.peer0.port
110  *-----------------------------------------------------------------------------
111  * Eg: NTB link tests
112  *
113  * # Set local link up/down
114  * root@local# sysctl dev.ntb_tool.0.link=Y
115  * root@local# sysctl dev.ntb_tool.0.link=N
116  *
117  * # Check if link with peer device is up/down:
118  * root@local# sysctl dev.ntb_tool.0.peer0.link
119  *
120  * # Poll until the link specified as up/down. For up, value needs to be set
121  * depends on peer index, i.e., for peer0 it is 0x1 and for down, value needs
122  * to be set as 0x0.
123  * root@local# sysctl dev.ntb_tool.0.peer0.link_event=0x1
124  * root@local# sysctl dev.ntb_tool.0.peer0.link_event=0x0
125  *-----------------------------------------------------------------------------
126  * Eg: Doorbell registers tests
127  *
128  * # clear/get local doorbell
129  * root@local# sysctl dev.ntb_tool.0.db="c 0x1"
130  * root@local# sysctl dev.ntb_tool.0.db
131  *
132  * # Set/clear/get local doorbell mask
133  * root@local# sysctl dev.ntb_tool.0.db_mask="s 0x1"
134  * root@local# sysctl dev.ntb_tool.0.db_mask="c 0x1"
135  * root@local# sysctl dev.ntb_tool.0.db_mask
136  *
137  * # Ring/clear/get peer doorbell
138  * root@local# sysctl dev.ntb_tool.0.peer_db="s 0x1"
139  * root@local# sysctl dev.ntb_tool.0.peer_db="c 0x1"
140  * root@local# sysctl dev.ntb_tool.0.peer_db
141  *
142  * # Set/clear/get peer doorbell mask (functionality is absent)
143  * root@local# sysctl dev.ntb_tool.0.peer_db_mask="s 0x1"
144  * root@local# sysctl dev.ntb_tool.0.peer_db_mask="c 0x1"
145  * root@local# sysctl dev.ntb_tool.0.peer_db_mask
146  *
147  * # Poll until local doorbell is set with the specified db bits
148  * root@local# dev.ntb_tool.0.db_event=0x1
149  *-----------------------------------------------------------------------------
150  * Eg: Scratchpad registers tests
151  *
152  * # Write/read to/from local scratchpad register #0
153  * root@local# sysctl dev.ntb_tool.0.spad0=0x1023457
154  * root@local# sysctl dev.ntb_tool.0.spad0
155  *
156  * # Write/read to/from peer scratchpad register #0
157  * root@local# sysctl dev.ntb_tool.0.peer0.spad0=0x01020304
158  * root@local# sysctl dev.ntb_tool.0.peer0.spad0
159  *-----------------------------------------------------------------------------
160  * Eg: Memory windows tests (need to configure local mw_trans on both sides)
161  *
162  * # Create inbound memory window buffer of specified size/get its dma address
163  * root@local# sysctl dev.ntb_tool.0.peer0.mw_trans0=16384
164  * root@local# sysctl dev.ntb_tool.0.peer0.mw_trans0
165  *
166  * # Write/read data to/from inbound memory window with specific pattern/random
167  * data.
168  * root@local# sysctl dev.ntb_tool.0.peer0.mw0="W offset 0 nbytes 100 pattern ab"
169  * root@local# sysctl dev.ntb_tool.0.peer0.mw0="R offset 0 nbytes 100"
170  *
171  * # Write/read data to/from outbound memory window on the local device with
172  * specific pattern/random (on peer device)
173  * root@local# sysctl dev.ntb_tool.0.peer0.peer_mw0="W offset 0 nbytes 100 pattern ab"
174  * root@local# sysctl dev.ntb_tool.0.peer0.peer_mw0="R offset 0 nbytes 100"
175  *-----------------------------------------------------------------------------
176  * NOTE: *Message registers are not supported*
177  *-----------------------------------------------------------------------------
178  *
179  * contact information:
180  * Arpan Palit <arpan.palit@amd.com>
181  *
182  */
183 
184 #include <sys/param.h>
185 #include <sys/bus.h>
186 #include <sys/kernel.h>
187 #include <sys/module.h>
188 #include <sys/mbuf.h>
189 #include <sys/sysctl.h>
190 #include <sys/sbuf.h>
191 
192 #include <machine/bus.h>
193 
194 #include <vm/vm.h>
195 
196 #include "../ntb.h"
197 
198 /* Buffer length for User input */
199 #define	TOOL_BUF_LEN 48
200 /* Memory window default command read and write offset. */
201 #define	DEFAULT_MW_OFF  0
202 /* Memory window default size and also max command read size. */
203 #define	DEFAULT_MW_SIZE 1024
204 
205 MALLOC_DEFINE(M_NTB_TOOL, "ntb_tool", "ntb_tool driver memory allocation");
206 
207 /*
208  * Memory windows descriptor structure
209  */
210 struct tool_mw {
211 	struct tool_ctx    *tc;
212 	int                widx;
213 	int                pidx;
214 
215 	/* Rx buff is off virt_addr / dma_base */
216 	bus_addr_t         dma_base;
217 	caddr_t            virt_addr;
218 	bus_dmamap_t       dma_map;
219 	bus_dma_tag_t      dma_tag;
220 
221 	/* Tx buff is off vbase / phys_addr */
222 	caddr_t            mm_base;
223 	vm_paddr_t         phys_addr;
224 	bus_addr_t         addr_limit;
225 	size_t             phys_size;
226 	size_t             xlat_align;
227 	size_t             xlat_align_size;
228 
229 	/* Memory window configured size and limits */
230 	size_t             size;
231 	ssize_t            mw_buf_size;
232 	ssize_t            mw_buf_offset;
233 	ssize_t            mw_peer_buf_size;
234 	ssize_t            mw_peer_buf_offset;
235 
236 	/* options to handle sysctl out */
237 	int                mw_cmd_rw;
238 	int                mw_peer_cmd_rw;
239 };
240 
241 struct tool_spad {
242 	int                sidx;
243 	int                pidx;
244 	struct tool_ctx    *tc;
245 };
246 
247 struct tool_peer {
248 	int                 pidx;
249 	struct tool_ctx     *tc;
250 	int                 inmw_cnt;
251 	struct tool_mw      *inmws;
252 	int                 outspad_cnt;
253 	struct tool_spad    *outspads;
254 	unsigned int        port_no;
255 };
256 
257 struct tool_ctx {
258 	device_t            dev;
259 	struct callout      link_event_timer;
260 	struct callout      db_event_timer;
261 	int                 peer_cnt;
262 	struct tool_peer    *peers;
263 	int                 inmsg_cnt;
264 	struct tool_msg     *inmsgs;
265 	int                 inspad_cnt;
266 	struct tool_spad    *inspads;
267 	unsigned int        unsafe;
268 
269 	/* sysctl read out variables */
270 	char                link_status;
271 	uint64_t            link_bits;
272 	uint64_t            link_mask;
273 	uint64_t            db_valid_mask;
274 	uint64_t            db_mask_val;
275 	uint64_t            db_event_val;
276 	uint64_t            peer_db_val;
277 	uint64_t            peer_db_mask_val;
278 	unsigned int        port_no;
279 };
280 
281 /* structure to save dma_addr after dma load */
282 struct ntb_tool_load_cb_args {
283 	bus_addr_t addr;
284 	int error;
285 };
286 
287 /*
288  * NTB events handlers
289  */
290 static void
tool_link_event(void * ctx)291 tool_link_event(void *ctx)
292 {
293 	struct tool_ctx *tc = ctx;
294 	enum ntb_speed speed = 0;
295 	enum ntb_width width = 0;
296 	int up = 0;
297 
298 	up = ntb_link_is_up(tc->dev, &speed, &width);
299 	if (up)
300 		tc->link_status = 'Y';
301 	else
302 		tc->link_status = 'N';
303 
304 	device_printf(tc->dev, "link is %s speed %d width %d\n",
305 	    up ? "up" : "down", speed, width);
306 }
307 
308 static void
tool_db_event(void * ctx,uint32_t vec)309 tool_db_event(void *ctx, uint32_t vec)
310 {
311 	struct tool_ctx *tc = ctx;
312 	uint64_t db_bits, db_mask;
313 
314 	db_mask = ntb_db_vector_mask(tc->dev, vec);
315 	db_bits = ntb_db_read(tc->dev);
316 
317 	device_printf(tc->dev, "doorbell vec %d mask %#llx bits %#llx\n",
318 	    vec, (unsigned long long)db_mask, (unsigned long long)db_bits);
319 }
320 
321 static const struct ntb_ctx_ops tool_ops = {
322 	.link_event = tool_link_event,
323 	.db_event = tool_db_event,
324 };
325 
326 /*
327  * Callout event methods
328  */
329 static void
tool_link_event_handler(void * arg)330 tool_link_event_handler(void *arg)
331 {
332 	struct tool_ctx *tc = (struct tool_ctx *)arg;
333 	uint64_t val;
334 
335 	val = ntb_link_is_up(tc->dev, NULL, NULL) & tc->link_mask;
336 
337 	if (val == tc->link_bits) {
338 		device_printf(tc->dev, "link_event successful for link val="
339 		    "0x%jx\n", tc->link_bits);
340 		tc->link_bits = 0x0;
341 		tc->link_mask = 0x0;
342 	} else
343 		callout_reset(&tc->link_event_timer, 1, tool_link_event_handler, tc);
344 }
345 
346 static void
tool_db_event_handler(void * arg)347 tool_db_event_handler(void *arg)
348 {
349 	struct tool_ctx *tc = (struct tool_ctx *)arg;
350 	uint64_t db_bits;
351 
352 	db_bits = ntb_db_read(tc->dev);
353 
354 	if (db_bits == tc->db_event_val) {
355 		device_printf(tc->dev, "db_event successful for db val=0x%jx\n",
356 		    tc->db_event_val);
357 		tc->db_event_val = 0x0;
358 	} else
359 		callout_reset(&tc->db_event_timer, 1, tool_db_event_handler, tc);
360 }
361 
362 /*
363  * Common read/write methods
364  */
365 static inline int
get_ubuf(struct sysctl_req * req,char * ubuf)366 get_ubuf(struct sysctl_req *req, char *ubuf)
367 {
368 	int rc;
369 
370 	if (req->newlen >= TOOL_BUF_LEN)
371 		return (EINVAL);
372 
373 	rc = SYSCTL_IN(req, ubuf, req->newlen);
374 	if (rc)
375 		return (rc);
376 	ubuf[req->newlen] = '\0';
377 
378 	return (0);
379 }
380 
381 static int
read_out(struct sysctl_req * req,uint64_t val)382 read_out(struct sysctl_req *req, uint64_t val)
383 {
384 	char ubuf[19];
385 
386 	memset((void *)ubuf, 0, sizeof(ubuf));
387 	snprintf(ubuf, sizeof(ubuf), "0x%jx", val);
388 
389 	return SYSCTL_OUT(req, ubuf, sizeof(ubuf));
390 }
391 
392 static int
tool_fn_read(struct tool_ctx * tc,struct sysctl_req * req,uint64_t (* fn_read)(device_t),uint64_t val)393 tool_fn_read(struct tool_ctx *tc, struct sysctl_req *req,
394     uint64_t (*fn_read)(device_t ), uint64_t val)
395 {
396 	if (fn_read == NULL)
397 		return read_out(req, val);
398 	else if (fn_read)
399 		return read_out(req, (uint64_t)fn_read(tc->dev));
400 	else
401 		return (EINVAL);
402 }
403 
404 static int
tool_fn_write(struct tool_ctx * tc,struct sysctl_oid * oidp,struct sysctl_req * req,char * ubuf,uint64_t * val,bool db_mask_sflag,void (* fn_set)(device_t,uint64_t),void (* fn_clear)(device_t,uint64_t))405 tool_fn_write(struct tool_ctx *tc, struct sysctl_oid *oidp,
406     struct sysctl_req *req, char *ubuf, uint64_t *val, bool db_mask_sflag,
407     void (*fn_set)(device_t , uint64_t), void (*fn_clear)(device_t , uint64_t))
408 {
409 	uint64_t db_valid_mask = tc->db_valid_mask;
410 	uint64_t bits;
411 	char cmd;
412 
413 	if (fn_set == NULL && fn_clear == NULL) {
414 		device_printf(tc->dev, "ERR: Set & Clear both are not supported\n");
415 		return (EINVAL);
416 	}
417 
418 	if (tc->db_valid_mask == 0)
419 		db_valid_mask = tc->db_valid_mask = ntb_db_valid_mask(tc->dev);
420 
421 	bits = 0;
422 	sscanf(ubuf, "%c %jx", &cmd, &bits);
423 	if (cmd == 's') {
424 		if ((bits | db_valid_mask) > db_valid_mask) {
425 			device_printf(tc->dev, "0x%jx value is not supported\n", bits);
426 			return (EINVAL);
427 		}
428 		if (fn_set)
429 			fn_set(tc->dev, bits);
430 		else
431 			return (EINVAL);
432 		if (val)
433 			*val |= bits;
434 	} else if (cmd == 'c') {
435 		if ((bits | db_valid_mask) > db_valid_mask) {
436 			device_printf(tc->dev, "0x%jx value is not supported\n", bits);
437 			return (EINVAL);
438 		}
439 		if (fn_clear)
440 			fn_clear(tc->dev, bits);
441 		if (val)
442 			*val &= ~bits;
443 	} else {
444 		device_printf(tc->dev, "Wrong Write\n");
445 		return (EINVAL);
446 	}
447 
448 	return (0);
449 }
450 
451 static int
parse_mw_buf(char * buf,char * cmd,ssize_t * offset,ssize_t * buf_size,uint64_t * pattern,bool * s_pflag)452 parse_mw_buf(char *buf, char *cmd, ssize_t *offset, ssize_t *buf_size,
453     uint64_t *pattern, bool *s_pflag)
454 {
455 	char op1[8], op2[8], op3[8];
456 	uint64_t val1, val2, val3;
457 	bool vs1, vs2, vs3;
458 	int rc = 0;
459 
460 	vs1 = vs2 = vs3 = false;
461 	sscanf(buf, "%c %s %jx %s %jx %s %jx",
462 	    cmd, op1, &val1, op2, &val2, op3, &val3);
463 
464 	if (*cmd != 'W' && *cmd != 'R')
465 		return (EINVAL);
466 
467 	if (!strcmp(op1, "offset")) {
468 		*offset = val1 ? val1 : DEFAULT_MW_OFF;
469 		vs1 = true;
470 	} else if (!strcmp(op1, "nbytes")) {
471 		*buf_size = val1 ? val1: DEFAULT_MW_SIZE;
472 		vs2 = true;
473 	} else if (!strcmp(op1, "pattern")) {
474 		*pattern = val1;
475 		vs3 = true;
476 	}
477 
478 	if (!vs1 && !strcmp(op2, "offset")) {
479 		*offset = val2 ? val2 : DEFAULT_MW_OFF;
480 		vs1 = true;
481 	} else if (!vs2 && !strcmp(op2, "nbytes")) {
482 		*buf_size = val2 ? val2: DEFAULT_MW_SIZE;
483 		vs2 = true;
484 	} else if (!vs3 && !strcmp(op2, "pattern")) {
485 		*pattern = val2;
486 		vs3 = true;
487 	}
488 
489 	if (!vs1 && !strcmp(op3, "offset")) {
490 		*offset = val3 ? val3 : DEFAULT_MW_OFF;
491 	} else if (!vs2 && !strcmp(op3, "nbytes")) {
492 		*buf_size = val3 ? val3: DEFAULT_MW_SIZE;
493 	} else if (!vs3 && !strcmp(op3, "pattern")) {
494 		*pattern = val3;
495 		vs3 = true;
496 	}
497 
498 	*s_pflag = vs3;
499 	if (vs3 && *cmd == 'R')
500 		printf("NTB_TOOL_WARN: pattern is not supported with read "
501 		    "command\n");
502 
503 	return (rc);
504 }
505 
506 static int
tool_mw_read_fn(struct sysctl_req * req,struct tool_mw * inmw,char * read_addr,int * cmd_op,ssize_t buf_off,ssize_t buf_size,char * type)507 tool_mw_read_fn(struct sysctl_req *req, struct tool_mw *inmw, char *read_addr,
508     int *cmd_op, ssize_t buf_off, ssize_t buf_size, char *type)
509 {
510 	ssize_t index, size;
511 	struct sbuf *sb;
512 	int i, loop, rc;
513 	char *tmp;
514 
515 	/* The below check is made to ignore sysctl read call. */
516 	if (*cmd_op == 0)
517 		return (0);
518 
519 	/* Proceeds only when command R/W is requested using sysctl. */
520 	index = buf_off;
521 	tmp = read_addr;
522 	tmp += index;
523 	loop = ((buf_size == 0) || (buf_size > DEFAULT_MW_SIZE)) ?
524 	    DEFAULT_MW_SIZE : buf_size;
525 	/*
526 	 * 256 bytes of extra buffer has been allocated to print details like
527 	 * summary, size, notes, i.e., excluding data part.
528 	 */
529 	size = loop + 256;
530 	sb = sbuf_new_for_sysctl(NULL, NULL, size, req);
531 	if (sb == NULL) {
532 		rc = sb->s_error;
533 		return (rc);
534 	}
535 
536 	if (!strcmp(type, "mw"))
537 		sbuf_printf(sb, "\nConfigured MW size\t: %zu\n", inmw->size);
538 	else if (!strcmp(type, "peer_mw"))
539 		sbuf_printf(sb, "\nConfigured Peer MW size\t: %zu\n",
540 		    inmw->size);
541 	sbuf_printf(sb, "R/W size\t\t: %zi\nR/W Offset\t\t: %zi\n\nData\n----"
542 	    "->", buf_size, buf_off);
543 
544 	/*
545 	 * Data will be read based on MW size provided by the user using nbytes,
546 	 * which is limited to 1024 bytes if user req bigger size to read, check
547 	 * above loop calculation which is limiting or setting the MW read size.
548 	 * Below for loop prints data where in each line contains 32 bytes data
549 	 * and after each 8 bytes of data we used four spaces which ensures one
550 	 * data block.
551 	 */
552 	for (i = 0 ; i < loop; i++) {
553 		if ((i % 32) == 0) {
554 			sbuf_printf(sb, "\n%08zx:", index);
555 			index += 32;
556 		}
557 		if ((i % 8) == 0)
558 			sbuf_printf(sb, "    ");
559 		sbuf_printf(sb, "%02hhx", *(tmp+i));
560 	}
561 	if (buf_size > DEFAULT_MW_SIZE)
562 		sbuf_printf(sb, "\n\nNOTE: Truncating read size %zi->1024 "
563 		    "bytes\n", buf_size);
564 
565 	/* cmd_op is set to zero after completion of each R/W command. */
566 	*cmd_op -= 1;
567 	rc = sbuf_finish(sb);
568 	sbuf_delete(sb);
569 
570 	return (rc);
571 }
572 
573 static int
tool_mw_write_fn(struct sysctl_oid * oidp,struct sysctl_req * req,struct tool_mw * inmw,char * ubuf,caddr_t write_buf,int * cmd_op,ssize_t * buf_offset,ssize_t * buf_size)574 tool_mw_write_fn(struct sysctl_oid *oidp, struct sysctl_req *req,
575     struct tool_mw *inmw, char *ubuf, caddr_t write_buf, int *cmd_op,
576     ssize_t *buf_offset, ssize_t *buf_size)
577 {
578 	ssize_t data_buf_size;
579 	uint64_t pattern = 0;
580 	bool s_pflag = false;
581 	void *data_buf;
582 	char cmd;
583 	int rc;
584 
585 	if (!write_buf)
586 		return (ENXIO);
587 
588 	/* buf_offset and buf_size set to default in case user does not req */
589 	*buf_offset = DEFAULT_MW_OFF;
590 	*buf_size = DEFAULT_MW_SIZE;
591 	rc = parse_mw_buf(ubuf, &cmd, buf_offset, buf_size, &pattern, &s_pflag);
592 	if (rc) {
593 		device_printf(inmw->tc->dev, "Wrong Command \"%c\" provided\n",
594 		    cmd);
595 		return (rc);
596 	}
597 
598 	/* Check for req size and buffer limit */
599 	if ((*buf_offset + *buf_size) > inmw->size) {
600 		device_printf(inmw->tc->dev, "%s: configured mw size :%zi and "
601 		    "requested size :%zi.\n", __func__, inmw->size,
602 		    (*buf_offset + *buf_size));
603 		*buf_offset = DEFAULT_MW_OFF;
604 		*buf_size = DEFAULT_MW_SIZE;
605 		rc = EINVAL;
606 		goto out;
607 	}
608 
609 	if (cmd == 'R')
610 		goto read_out;
611 	else if (cmd == 'W')
612 		goto write;
613 	else
614 		goto out;
615 
616 write:
617 	data_buf_size = *buf_size;
618 	data_buf = malloc(data_buf_size, M_NTB_TOOL, M_WAITOK | M_ZERO);
619 
620 	if (s_pflag)
621 		memset(data_buf, pattern, data_buf_size);
622 	else
623 		arc4rand(data_buf, data_buf_size, 1);
624 
625 	memcpy(write_buf + *buf_offset, data_buf, data_buf_size);
626 
627 	free(data_buf, M_NTB_TOOL);
628 
629 read_out:
630 	/* cmd_op value is set to two as sysctl read call executes twice */
631 	*cmd_op = 2;
632 out:
633 	return (rc);
634 }
635 
636 /*
637  * Port sysctl read/write methods
638  */
639 static int
sysctl_peer_port_number(SYSCTL_HANDLER_ARGS)640 sysctl_peer_port_number(SYSCTL_HANDLER_ARGS)
641 {
642 	struct tool_ctx *tc = (struct tool_ctx *)arg1;
643 	int rc, pidx = arg2, peer_port;
644 
645 	peer_port = ntb_peer_port_number(tc->dev, pidx);
646 	rc = sysctl_handle_int(oidp, &peer_port, 0, req);
647 	if (rc)
648 		device_printf(tc->dev, "Peer port sysctl set failed with err="
649 		    "(%d).\n", rc);
650 	else
651 		tc->peers[pidx].port_no = peer_port;
652 
653 	return (rc);
654 }
655 
656 static int
sysctl_local_port_number(SYSCTL_HANDLER_ARGS)657 sysctl_local_port_number(SYSCTL_HANDLER_ARGS)
658 {
659 	struct tool_ctx *tc = (struct tool_ctx *)arg1;
660 	int rc, local_port;
661 
662 	local_port = ntb_port_number(tc->dev);
663 	rc = sysctl_handle_int(oidp, &local_port, 0, req);
664 	if (rc)
665 		device_printf(tc->dev, "Local port sysctl set failed with err="
666 		    "(%d).\n", rc);
667 	else
668 		tc->port_no = local_port;
669 
670 	return (rc);
671 }
672 
673 static void
tool_init_peers(struct tool_ctx * tc)674 tool_init_peers(struct tool_ctx *tc)
675 {
676 	int pidx;
677 
678 	tc->peer_cnt = ntb_peer_port_count(tc->dev);
679 	tc->peers = malloc(tc->peer_cnt * sizeof(*tc->peers), M_NTB_TOOL,
680 	    M_WAITOK | M_ZERO);
681 	for (pidx = 0; pidx < tc->peer_cnt; pidx++) {
682 		tc->peers[pidx].pidx = pidx;
683 		tc->peers[pidx].tc = tc;
684 	}
685 }
686 
687 static void
tool_clear_peers(struct tool_ctx * tc)688 tool_clear_peers(struct tool_ctx *tc)
689 {
690 
691 	free(tc->peers, M_NTB_TOOL);
692 }
693 
694 /*
695  * Link state sysctl read/write methods
696  */
697 static int
sysctl_link_handle(SYSCTL_HANDLER_ARGS)698 sysctl_link_handle(SYSCTL_HANDLER_ARGS)
699 {
700 	struct tool_ctx *tc = (struct tool_ctx *)arg1;
701 	char buf[TOOL_BUF_LEN];
702 	int rc;
703 
704 	if (req->newptr == NULL) {
705 		snprintf(buf, 2, "%c", tc->link_status);
706 
707 		return SYSCTL_OUT(req, buf, 2);
708 	}
709 
710 	rc = get_ubuf(req, buf);
711 	if (rc)
712 		return (rc);
713 
714 	if (buf[0] == 'Y')
715 		rc = ntb_link_enable(tc->dev, NTB_SPEED_AUTO, NTB_WIDTH_AUTO);
716 	else if (buf[0] == 'N')
717 		rc = ntb_link_disable(tc->dev);
718 	else
719 		rc = EINVAL;
720 
721 	sscanf(buf, "%c", &tc->link_status);
722 
723 	return (0);
724 }
725 
726 static int
sysctl_peer_link_handle(SYSCTL_HANDLER_ARGS)727 sysctl_peer_link_handle(SYSCTL_HANDLER_ARGS)
728 {
729 	struct tool_ctx *tc = (struct tool_ctx *)arg1;
730 	int up = 0, pidx = arg2;
731 	char buf[TOOL_BUF_LEN];
732 
733 	if (req->newptr)
734 		return (0);
735 
736 	up = ntb_link_is_up(tc->dev, NULL, NULL);
737 	memset((void *)buf, 0, TOOL_BUF_LEN);
738 	if (up & (1UL << pidx))
739 		buf[0] = 'Y';
740 	else
741 		buf[0] = 'N';
742 
743 	return SYSCTL_OUT(req, buf, sizeof(buf));
744 }
745 
746 static int
sysctl_peer_link_event_handle(SYSCTL_HANDLER_ARGS)747 sysctl_peer_link_event_handle(SYSCTL_HANDLER_ARGS)
748 {
749 	struct tool_ctx *tc = (struct tool_ctx *)arg1;
750 	char buf[TOOL_BUF_LEN];
751 	int rc, pidx = arg2;
752 	uint64_t bits;
753 
754 	if (req->newptr == NULL)
755 		return read_out(req, tc->link_bits);
756 
757 	rc = get_ubuf(req, buf);
758 	if (rc)
759 		return (rc);
760 
761 	sscanf(buf, "0x%jx", &bits);
762 	tc->link_bits = bits;
763 	tc->link_mask = (1ULL << ((pidx) % 64));
764 
765 	callout_reset(&tc->link_event_timer, 1, tool_link_event_handler, tc);
766 	return (0);
767 }
768 
769 /*
770  * Memory windows read/write/setting methods
771  */
772 static void
ntb_tool_load_cb(void * arg,bus_dma_segment_t * segs,int nsegs,int error)773 ntb_tool_load_cb(void *arg, bus_dma_segment_t *segs, int nsegs, int error)
774 {
775 	struct ntb_tool_load_cb_args *cba = (struct ntb_tool_load_cb_args *)arg;
776 
777 	if (!(cba->error = error))
778 		cba->addr = segs[0].ds_addr;
779 }
780 
781 static int
sysctl_mw_handle(SYSCTL_HANDLER_ARGS)782 sysctl_mw_handle(SYSCTL_HANDLER_ARGS)
783 {
784 	struct tool_mw *inmw = (struct tool_mw *)arg1;
785 	char buf[TOOL_BUF_LEN];
786 	int rc;
787 
788 	if (req->newptr == NULL)
789 		return tool_mw_read_fn(req, inmw, (char *)inmw->mm_base,
790 		    &inmw->mw_cmd_rw, inmw->mw_buf_offset, inmw->mw_buf_size,
791 		    "mw");
792 
793 	rc = get_ubuf(req, buf);
794 	if (!rc)
795 		return tool_mw_write_fn(oidp, req, inmw, buf, inmw->mm_base,
796 		    &inmw->mw_cmd_rw, &inmw->mw_buf_offset, &inmw->mw_buf_size);
797 
798 	return (rc);
799 }
800 
801 static int
tool_setup_mw(struct tool_ctx * tc,unsigned int pidx,unsigned int widx,size_t req_size)802 tool_setup_mw(struct tool_ctx *tc, unsigned int pidx, unsigned int widx,
803     size_t req_size)
804 {
805 	struct tool_mw *inmw = &tc->peers[pidx].inmws[widx];
806 	struct ntb_tool_load_cb_args cba;
807 	int rc;
808 
809 	if (req_size == 0)
810 		inmw->size = roundup(inmw->phys_size, inmw->xlat_align_size);
811 	else
812 		inmw->size = roundup(req_size, inmw->xlat_align_size);
813 
814 	device_printf(tc->dev, "mw_size %zi req_size %zi buff %zi\n",
815 	    inmw->phys_size, req_size, inmw->size);
816 
817 	if (bus_dma_tag_create(bus_get_dma_tag(tc->dev), inmw->xlat_align, 0,
818 	    inmw->addr_limit, BUS_SPACE_MAXADDR, NULL, NULL, inmw->size, 1,
819 	    inmw->size, 0, NULL, NULL, &inmw->dma_tag)) {
820 		device_printf(tc->dev, "Unable to create MW tag of size "
821 		    "%zu/%zu\n", inmw->phys_size, inmw->size);
822 		rc = ENOMEM;
823 		goto err_free_dma_var;
824 	}
825 
826 	if (bus_dmamem_alloc(inmw->dma_tag, (void **)&inmw->virt_addr,
827 	    BUS_DMA_WAITOK | BUS_DMA_ZERO, &inmw->dma_map)) {
828 		device_printf(tc->dev, "Unable to allocate MW buffer of size "
829 		    "%zu/%zu\n", inmw->phys_size, inmw->size);
830 		rc = ENOMEM;
831 		goto err_free_tag_rem;
832 	}
833 
834 	if (bus_dmamap_load(inmw->dma_tag, inmw->dma_map, inmw->virt_addr,
835 	    inmw->size, ntb_tool_load_cb, &cba, BUS_DMA_NOWAIT) || cba.error) {
836 		device_printf(tc->dev, "Unable to load MW buffer of size "
837 		    "%zu/%zu\n", inmw->phys_size, inmw->size);
838 		rc = ENOMEM;
839 		goto err_free_dma;
840 	}
841 	inmw->dma_base = cba.addr;
842 
843 	rc = ntb_mw_set_trans(tc->dev, widx, inmw->dma_base, inmw->size);
844 	if (rc)
845 		goto err_free_mw;
846 
847 	return (0);
848 
849 err_free_mw:
850 	bus_dmamap_unload(inmw->dma_tag, inmw->dma_map);
851 
852 err_free_dma:
853 	bus_dmamem_free(inmw->dma_tag, inmw->virt_addr, inmw->dma_map);
854 
855 err_free_tag_rem:
856 	bus_dma_tag_destroy(inmw->dma_tag);
857 
858 err_free_dma_var:
859 	inmw->size = 0;
860 	inmw->virt_addr = 0;
861 	inmw->dma_base = 0;
862 	inmw->dma_tag = 0;
863 	inmw->dma_map = 0;
864 
865 	return (rc);
866 }
867 
868 static void
tool_free_mw(struct tool_ctx * tc,int pidx,int widx)869 tool_free_mw(struct tool_ctx *tc, int pidx, int widx)
870 {
871 	struct tool_mw *inmw = &tc->peers[pidx].inmws[widx];
872 
873 	if (inmw->dma_base)
874 		ntb_mw_clear_trans(tc->dev, widx);
875 
876 	if (inmw->virt_addr && inmw->dma_tag) {
877 		bus_dmamap_unload(inmw->dma_tag, inmw->dma_map);
878 		bus_dmamem_free(inmw->dma_tag, inmw->virt_addr, inmw->dma_map);
879 		bus_dma_tag_destroy(inmw->dma_tag);
880 	}
881 
882 	inmw->virt_addr = 0;
883 	inmw->dma_base = 0;
884 	inmw->dma_tag = 0;
885 	inmw->dma_map = 0;
886 	inmw->mm_base = 0;
887 	inmw->size = 0;
888 }
889 
890 static int
tool_mw_trans_read(struct tool_mw * inmw,struct sysctl_req * req)891 tool_mw_trans_read(struct tool_mw *inmw, struct sysctl_req *req)
892 {
893 	ssize_t buf_size = 512;
894 	struct sbuf *sb;
895 	int rc = 0;
896 
897 	sb = sbuf_new_for_sysctl(NULL, NULL, buf_size, req);
898 	if (sb == NULL) {
899 		rc = sb->s_error;
900 		return (rc);
901 	}
902 
903 	sbuf_printf(sb, "\nInbound MW     \t%d\n", inmw->widx);
904 	sbuf_printf(sb, "Port           \t%d (%d)\n",
905 	    ntb_peer_port_number(inmw->tc->dev, inmw->pidx), inmw->pidx);
906 	sbuf_printf(sb, "Window Address \t%p\n", inmw->mm_base);
907 	sbuf_printf(sb, "DMA Address    \t0x%016llx\n", (long long)inmw->dma_base);
908 	sbuf_printf(sb, "Window Size    \t0x%016zx[p]\n", inmw->size);
909 	sbuf_printf(sb, "Alignment      \t0x%016zx[p]\n", inmw->xlat_align);
910 	sbuf_printf(sb, "Size Alignment \t0x%016zx[p]\n",
911 	    inmw->xlat_align_size);
912 	sbuf_printf(sb, "Size Max       \t0x%016zx[p]\n", inmw->phys_size);
913 
914 	rc = sbuf_finish(sb);
915 	sbuf_delete(sb);
916 
917 	return (rc);
918 }
919 
920 static int
tool_mw_trans_write(struct sysctl_oid * oidp,struct sysctl_req * req,struct tool_mw * inmw,size_t wsize)921 tool_mw_trans_write(struct sysctl_oid *oidp, struct sysctl_req *req,
922     struct tool_mw *inmw, size_t wsize)
923 {
924 	struct tool_ctx *tc = inmw->tc;
925 	int rc = 0;
926 
927 	if (wsize == 0)
928 		return (EINVAL);
929 
930 	/* No need to re-setup mw */
931 	if (inmw->size == wsize)
932 		return (0);
933 
934 	/* free mw dma buffer */
935 	if (inmw->size)
936 		tool_free_mw(tc, inmw->pidx, inmw->widx);
937 
938 	rc = tool_setup_mw(tc, inmw->pidx, inmw->widx, wsize);
939 
940 	return (rc);
941 }
942 
943 static int
sysctl_mw_trans_handler(SYSCTL_HANDLER_ARGS)944 sysctl_mw_trans_handler(SYSCTL_HANDLER_ARGS)
945 {
946 	struct tool_mw *inmw = (struct tool_mw *)arg1;
947 	char buf[TOOL_BUF_LEN];
948 	ssize_t wsize;
949 	int rc;
950 
951 	if (req->newptr == NULL)
952 		return tool_mw_trans_read(inmw, req);
953 
954 	rc = get_ubuf(req, buf);
955 	if (rc == 0) {
956 		sscanf(buf, "%zi", &wsize);
957 		return tool_mw_trans_write(oidp, req, inmw, wsize);
958 	}
959 
960 	return (rc);
961 }
962 
963 static int
sysctl_peer_mw_handle(SYSCTL_HANDLER_ARGS)964 sysctl_peer_mw_handle(SYSCTL_HANDLER_ARGS)
965 {
966 	struct tool_mw *inmw = (struct tool_mw *)arg1;
967 	char buf[TOOL_BUF_LEN];
968 	int rc;
969 
970 	if (req->newptr == NULL)
971 		return tool_mw_read_fn(req, inmw, (char *)inmw->virt_addr,
972 		    &inmw->mw_peer_cmd_rw, inmw->mw_peer_buf_offset,
973 		    inmw->mw_peer_buf_size, "mw");
974 
975 	rc = get_ubuf(req, buf);
976 	if (rc == 0)
977 		return tool_mw_write_fn(oidp, req, inmw, buf, inmw->virt_addr,
978 		    &inmw->mw_peer_cmd_rw, &inmw->mw_peer_buf_offset,
979 		    &inmw->mw_peer_buf_size);
980 
981 	return (rc);
982 }
983 
tool_clear_mws(struct tool_ctx * tc)984 static void tool_clear_mws(struct tool_ctx *tc)
985 {
986 	int widx, pidx;
987 
988 	/* Free outbound memory windows */
989 	for (pidx = 0; pidx < tc->peer_cnt; pidx++) {
990 		for (widx = 0; widx < tc->peers[pidx].inmw_cnt; widx++)
991 			tool_free_mw(tc, pidx, widx);
992 		free(tc->peers[pidx].inmws, M_NTB_TOOL);
993 	}
994 }
995 
996 static int
tool_init_mws(struct tool_ctx * tc)997 tool_init_mws(struct tool_ctx *tc)
998 {
999 	struct tool_mw *mw;
1000 	int widx, pidx, rc;
1001 
1002 	/* Initialize inbound memory windows and outbound MWs wrapper */
1003 	for (pidx = 0; pidx < tc->peer_cnt; pidx++) {
1004 		tc->peers[pidx].inmw_cnt = ntb_mw_count(tc->dev);
1005 		tc->peers[pidx].inmws = malloc(tc->peers[pidx].inmw_cnt *
1006 		    sizeof(*tc->peers[pidx].inmws), M_NTB_TOOL,
1007 		    M_WAITOK | M_ZERO);
1008 
1009 		for (widx = 0; widx < tc->peers[pidx].inmw_cnt; widx++) {
1010 			mw = &tc->peers[pidx].inmws[widx];
1011 			memset((void *)mw, 0, sizeof(*mw));
1012 			mw->tc = tc;
1013 			mw->widx = widx;
1014 			mw->pidx = pidx;
1015 			mw->mw_buf_offset = DEFAULT_MW_OFF;
1016 			mw->mw_buf_size = DEFAULT_MW_SIZE;
1017 			/* get the tx buff details for each mw attached with each peer */
1018 			rc = ntb_mw_get_range(tc->dev, widx, &mw->phys_addr,
1019 			    &mw->mm_base, &mw->phys_size, &mw->xlat_align,
1020 			    &mw->xlat_align_size, &mw->addr_limit);
1021 			if (rc)
1022 				goto free_mws;
1023 		}
1024 	}
1025 
1026 	return (0);
1027 
1028 free_mws:
1029 	tool_clear_mws(tc);
1030 	return (rc);
1031 }
1032 
1033 /*
1034  * Doorbell handler for read/write
1035  */
1036 static int
sysctl_db_handle(SYSCTL_HANDLER_ARGS)1037 sysctl_db_handle(SYSCTL_HANDLER_ARGS)
1038 {
1039 	struct tool_ctx *tc = (struct tool_ctx *)arg1;
1040 	char buf[TOOL_BUF_LEN];
1041 	uint64_t db_bits;
1042 	int rc;
1043 
1044 	if (req->newptr == NULL) {
1045 		db_bits = ntb_db_read(tc->dev);
1046 		return read_out(req, db_bits);
1047 	}
1048 
1049 	rc = get_ubuf(req, buf);
1050 	if (rc == 0)
1051 		return tool_fn_write(tc, oidp, req, buf, NULL, false, NULL,
1052 		    ntb_db_clear);
1053 
1054 	return (rc);
1055 }
1056 
1057 static int
sysctl_db_valid_mask_handle(SYSCTL_HANDLER_ARGS)1058 sysctl_db_valid_mask_handle(SYSCTL_HANDLER_ARGS)
1059 {
1060 	struct tool_ctx *tc = (struct tool_ctx *)arg1;
1061 
1062 	tc->db_valid_mask = ntb_db_valid_mask(tc->dev);
1063 	if (!tc->db_valid_mask) {
1064 		device_printf(tc->dev, "Error getting db_valid_mask from "
1065 		    "hw driver\n");
1066 		return (EINVAL);
1067 	} else {
1068 		return read_out(req, tc->db_valid_mask);
1069 	}
1070 }
1071 
1072 static int
sysctl_db_mask_handle(SYSCTL_HANDLER_ARGS)1073 sysctl_db_mask_handle(SYSCTL_HANDLER_ARGS)
1074 {
1075 	struct tool_ctx *tc = (struct tool_ctx *)arg1;
1076 	char buf[TOOL_BUF_LEN];
1077 	int rc;
1078 
1079 	if (req->newptr == NULL) {
1080 		if (tc->db_mask_val == 0)
1081 		     ntb_db_valid_mask(tc->dev);
1082 		return tool_fn_read(tc, req, NULL, tc->db_mask_val);
1083 	}
1084 
1085 	rc = get_ubuf(req, buf);
1086 	if (rc == 0)
1087 		return tool_fn_write(tc, oidp, req, buf, &tc->db_mask_val, true,
1088 		    ntb_db_set_mask, ntb_db_clear_mask);
1089 
1090 	return (rc);
1091 }
1092 
1093 static int
sysctl_peer_db_handle(SYSCTL_HANDLER_ARGS)1094 sysctl_peer_db_handle(SYSCTL_HANDLER_ARGS)
1095 {
1096 	struct tool_ctx *tc = (struct tool_ctx *)arg1;
1097 	char buf[TOOL_BUF_LEN];
1098 	int rc;
1099 
1100 	if (req->newptr == NULL)
1101 		return tool_fn_read(tc, req, NULL, tc->peer_db_val);
1102 
1103 	rc = get_ubuf(req, buf);
1104 	if (rc == 0)
1105 		return tool_fn_write(tc, oidp, req, buf, &tc->peer_db_val,
1106 		    false, ntb_peer_db_set, NULL);
1107 
1108 	return (rc);
1109 }
1110 
1111 static int
sysctl_peer_db_mask_handle(SYSCTL_HANDLER_ARGS)1112 sysctl_peer_db_mask_handle(SYSCTL_HANDLER_ARGS)
1113 {
1114 	struct tool_ctx *tc = (struct tool_ctx *)arg1;
1115 	char buf[TOOL_BUF_LEN];
1116 	int rc;
1117 
1118 	if (req->newptr == NULL){
1119 		if (tc->peer_db_mask_val == 0)
1120 			ntb_db_valid_mask(tc->dev);
1121 		return tool_fn_read(tc, req, NULL, tc->peer_db_mask_val);
1122 	}
1123 
1124 	rc = get_ubuf(req, buf);
1125 	if (rc == 0)
1126 		return tool_fn_write(tc, oidp, req, buf, &tc->peer_db_mask_val,
1127 		    true, NULL, NULL);
1128 
1129 	return (rc);
1130 }
1131 
1132 static int
sysctl_db_event_handle(SYSCTL_HANDLER_ARGS)1133 sysctl_db_event_handle(SYSCTL_HANDLER_ARGS)
1134 {
1135 	struct tool_ctx *tc = (struct tool_ctx *)arg1;
1136 	char buf[TOOL_BUF_LEN];
1137 	uint64_t bits;
1138 	int rc;
1139 
1140 	if (req->newptr == NULL)
1141 		return read_out(req, tc->db_event_val);
1142 
1143 	rc = get_ubuf(req, buf);
1144 	if (rc)
1145 		return (rc);
1146 
1147 	sscanf(buf, "%ju", &bits);
1148 	tc->db_event_val = bits;
1149 	callout_reset(&tc->db_event_timer, 1, tool_db_event_handler, tc);
1150 
1151 	return (0);
1152 }
1153 
1154 /*
1155  * Scratchpads read/write methods
1156  */
1157 static int
sysctl_spad_handle(SYSCTL_HANDLER_ARGS)1158 sysctl_spad_handle(SYSCTL_HANDLER_ARGS)
1159 {
1160 	struct tool_ctx *tc = (struct tool_ctx *)arg1;
1161 	unsigned int sidx = arg2;
1162 	char buf[TOOL_BUF_LEN];
1163 	uint32_t bits;
1164 	int rc;
1165 
1166 	if (req->newptr == NULL) {
1167 		rc = ntb_spad_read(tc->dev, sidx, &bits);
1168 		if (rc)
1169 			return (rc);
1170 		else
1171 			return read_out(req, (uint64_t )bits);
1172 	}
1173 
1174 	rc = get_ubuf(req, buf);
1175 	if (rc == 0) {
1176 		sscanf(buf, "%i", &bits);
1177 		return ntb_spad_write(tc->dev, sidx, bits);
1178 	}
1179 
1180 	return (rc);
1181 }
1182 
1183 static int
sysctl_peer_spad_handle(SYSCTL_HANDLER_ARGS)1184 sysctl_peer_spad_handle(SYSCTL_HANDLER_ARGS)
1185 {
1186 	struct tool_ctx *tc = (struct tool_ctx *)arg1;
1187 	unsigned int sidx = arg2;
1188 	char buf[TOOL_BUF_LEN];
1189 	uint32_t bits;
1190 	int rc;
1191 
1192 	if (req->newptr == NULL) {
1193 		rc = ntb_peer_spad_read(tc->dev, sidx, &bits);
1194 		if (rc)
1195 			return (rc);
1196 		else
1197 			return read_out(req, (uint64_t )bits);
1198 	}
1199 
1200 	rc = get_ubuf(req, buf);
1201 	if (rc == 0) {
1202 		sscanf(buf, "%i", &bits);
1203 		return ntb_peer_spad_write(tc->dev, sidx, bits);
1204 	}
1205 
1206 	return (rc);
1207 }
1208 
1209 static void
tool_init_spads(struct tool_ctx * tc)1210 tool_init_spads(struct tool_ctx *tc)
1211 {
1212 	int sidx, pidx;
1213 
1214 	/* Initialize inbound scratchpad structures */
1215 	tc->inspad_cnt = ntb_spad_count(tc->dev);
1216 	tc->inspads = malloc(tc->inspad_cnt * sizeof(*tc->inspads), M_NTB_TOOL,
1217 	    M_WAITOK | M_ZERO);
1218 
1219 	for (sidx = 0; sidx < tc->inspad_cnt; sidx++) {
1220 		tc->inspads[sidx].sidx = sidx;
1221 		tc->inspads[sidx].pidx = -1;
1222 		tc->inspads[sidx].tc = tc;
1223 	}
1224 
1225 	/* Initialize outbound scratchpad structures */
1226 	for (pidx = 0; pidx < tc->peer_cnt; pidx++) {
1227 		tc->peers[pidx].outspad_cnt = ntb_spad_count(tc->dev);
1228 		tc->peers[pidx].outspads =  malloc(tc->peers[pidx].outspad_cnt *
1229 		    sizeof(*tc->peers[pidx].outspads), M_NTB_TOOL, M_WAITOK |
1230 		    M_ZERO);
1231 
1232 		for (sidx = 0; sidx < tc->peers[pidx].outspad_cnt; sidx++) {
1233 			tc->peers[pidx].outspads[sidx].sidx = sidx;
1234 			tc->peers[pidx].outspads[sidx].pidx = pidx;
1235 			tc->peers[pidx].outspads[sidx].tc = tc;
1236 		}
1237 	}
1238 }
1239 
1240 static void
tool_clear_spads(struct tool_ctx * tc)1241 tool_clear_spads(struct tool_ctx *tc)
1242 {
1243 	int pidx;
1244 
1245 	/* Free local inspads. */
1246 	free(tc->inspads, M_NTB_TOOL);
1247 
1248 	/* Free outspads for each peer. */
1249 	for (pidx = 0; pidx < tc->peer_cnt; pidx++)
1250 		free(tc->peers[pidx].outspads, M_NTB_TOOL);
1251 }
1252 
1253 /*
1254  * Initialization methods
1255  */
1256 static int
tool_check_ntb(struct tool_ctx * tc)1257 tool_check_ntb(struct tool_ctx *tc)
1258 {
1259 
1260 	/* create and initialize link callout handler */
1261 	callout_init(&tc->link_event_timer, 1);
1262 
1263 	/* create and initialize db callout handler */
1264 	callout_init(&tc->db_event_timer, 1);
1265 
1266 	/* Initialize sysctl read out values to default */
1267 	tc->link_status = 'U';
1268 	tc->db_mask_val = 0;
1269 	tc->peer_db_val = 0;
1270 	tc->peer_db_mask_val = 0;
1271 	tc->db_event_val = 0;
1272 	tc->link_bits = 0;
1273 
1274 	return (0);
1275 }
1276 
1277 static void
tool_clear_data(struct tool_ctx * tc)1278 tool_clear_data(struct tool_ctx *tc)
1279 {
1280 
1281 	callout_drain(&tc->link_event_timer);
1282 	callout_drain(&tc->db_event_timer);
1283 }
1284 
1285 static int
tool_init_ntb(struct tool_ctx * tc)1286 tool_init_ntb(struct tool_ctx *tc)
1287 {
1288 
1289 	return ntb_set_ctx(tc->dev, tc, &tool_ops);
1290 }
1291 
1292 static void
tool_clear_ntb(struct tool_ctx * tc)1293 tool_clear_ntb(struct tool_ctx *tc)
1294 {
1295 
1296 	ntb_clear_ctx(tc->dev);
1297 	ntb_link_disable(tc->dev);
1298 }
1299 
1300 /*
1301  *  Current sysctl implementation is made such that it gets attached to the
1302  *  device and while detach it gets cleared automatically.
1303  */
1304 static void
tool_setup_sysctl(struct tool_ctx * tc)1305 tool_setup_sysctl(struct tool_ctx *tc)
1306 {
1307 	char buf[TOOL_BUF_LEN], desc[TOOL_BUF_LEN];
1308 	struct sysctl_oid_list *top, *peer_top;
1309 	struct sysctl_oid *parent, *peer;
1310 	struct sysctl_ctx_list *clist;
1311 	unsigned int pidx, sidx, widx;
1312 
1313 	clist = device_get_sysctl_ctx(tc->dev);
1314 	parent = device_get_sysctl_tree(tc->dev);
1315 	top = SYSCTL_CHILDREN(parent);
1316 
1317 	SYSCTL_ADD_PROC(clist, top, OID_AUTO, "port", CTLTYPE_UINT |
1318 	    CTLFLAG_RDTUN | CTLFLAG_MPSAFE, tc, 0, sysctl_local_port_number,
1319 	    "IU", "local port number");
1320 
1321 	SYSCTL_ADD_PROC(clist, top, OID_AUTO, "link", CTLTYPE_STRING |
1322 	    CTLFLAG_RWTUN | CTLFLAG_MPSAFE, tc, 0, sysctl_link_handle,
1323 	    "IU", "link info");
1324 
1325 	SYSCTL_ADD_PROC(clist, top, OID_AUTO, "db", CTLTYPE_STRING |
1326 	    CTLFLAG_RWTUN | CTLFLAG_MPSAFE, tc, 0, sysctl_db_handle,
1327 	    "A", "db info");
1328 
1329 	SYSCTL_ADD_PROC(clist, top, OID_AUTO, "db_valid_mask", CTLTYPE_STRING |
1330 	    CTLFLAG_RD | CTLFLAG_MPSAFE, tc, 0, sysctl_db_valid_mask_handle,
1331 	    "A", "db valid mask");
1332 
1333 	SYSCTL_ADD_PROC(clist, top, OID_AUTO, "db_mask", CTLTYPE_STRING |
1334 	    CTLFLAG_RWTUN | CTLFLAG_MPSAFE, tc, 0, sysctl_db_mask_handle,
1335 	    "A", "db mask");
1336 
1337 	SYSCTL_ADD_PROC(clist, top, OID_AUTO, "db_event", CTLTYPE_STRING |
1338 	    CTLFLAG_WR | CTLFLAG_MPSAFE, tc, 0, sysctl_db_event_handle,
1339 	    "A", "db event");
1340 
1341 	SYSCTL_ADD_PROC(clist, top, OID_AUTO, "peer_db", CTLTYPE_STRING |
1342 	    CTLFLAG_RWTUN | CTLFLAG_MPSAFE, tc, 0, sysctl_peer_db_handle,
1343 	    "A", "peer db");
1344 
1345 	SYSCTL_ADD_PROC(clist, top, OID_AUTO, "peer_db_mask", CTLTYPE_STRING |
1346 	    CTLFLAG_RWTUN | CTLFLAG_MPSAFE, tc, 0, sysctl_peer_db_mask_handle,
1347 	    "IU", "peer db mask info");
1348 
1349 	if (tc->inspad_cnt != 0) {
1350 		for (sidx = 0; sidx < tc->inspad_cnt; sidx++) {
1351 			snprintf(buf, sizeof(buf), "spad%d", sidx);
1352 			snprintf(desc, sizeof(desc), "spad%d info", sidx);
1353 
1354 			SYSCTL_ADD_PROC(clist, top, OID_AUTO, buf,
1355 			    CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE,
1356 			    tc, sidx, sysctl_spad_handle, "IU", desc);
1357 		}
1358 	}
1359 
1360 	for (pidx = 0; pidx < tc->peer_cnt; pidx++) {
1361 		snprintf(buf, sizeof(buf), "peer%d", pidx);
1362 
1363 		peer = SYSCTL_ADD_NODE(clist, top, OID_AUTO, buf,
1364 		    CTLFLAG_RW | CTLFLAG_MPSAFE, 0, buf);
1365 		peer_top = SYSCTL_CHILDREN(peer);
1366 
1367 		SYSCTL_ADD_PROC(clist, peer_top, OID_AUTO, "port",
1368 		    CTLTYPE_UINT | CTLFLAG_RDTUN | CTLFLAG_MPSAFE, tc, pidx,
1369 		    sysctl_peer_port_number, "IU", "peer port number");
1370 
1371 		SYSCTL_ADD_PROC(clist, peer_top, OID_AUTO, "link",
1372 		    CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, tc, pidx,
1373 		    sysctl_peer_link_handle, "IU", "peer_link info");
1374 
1375 		SYSCTL_ADD_PROC(clist, peer_top, OID_AUTO, "link_event",
1376 		    CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, tc, pidx,
1377 		    sysctl_peer_link_event_handle, "IU", "link event");
1378 
1379 		for (widx = 0; widx < tc->peers[pidx].inmw_cnt; widx++) {
1380 			snprintf(buf, sizeof(buf), "mw_trans%d", widx);
1381 			snprintf(desc, sizeof(desc), "mw trans%d info", widx);
1382 
1383 			SYSCTL_ADD_PROC(clist, peer_top, OID_AUTO, buf,
1384 			    CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE,
1385 			    &tc->peers[pidx].inmws[widx], 0,
1386 			    sysctl_mw_trans_handler, "IU", desc);
1387 
1388 			snprintf(buf, sizeof(buf), "mw%d", widx);
1389 			snprintf(desc, sizeof(desc), "mw%d info", widx);
1390 
1391 			SYSCTL_ADD_PROC(clist, peer_top, OID_AUTO, buf,
1392 			    CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE,
1393 			    &tc->peers[pidx].inmws[widx], 0,
1394 			    sysctl_mw_handle, "IU", desc);
1395 
1396 			snprintf(buf, sizeof(buf), "peer_mw%d", widx);
1397 			snprintf(desc, sizeof(desc), "peer_mw%d info", widx);
1398 
1399 			SYSCTL_ADD_PROC(clist, peer_top, OID_AUTO, buf,
1400 			    CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE,
1401 			    &tc->peers[pidx].inmws[widx], 0,
1402 			    sysctl_peer_mw_handle, "IU", desc);
1403 		}
1404 
1405 		for (sidx = 0; sidx < tc->peers[pidx].outspad_cnt; sidx++) {
1406 			snprintf(buf, sizeof(buf), "spad%d", sidx);
1407 			snprintf(desc, sizeof(desc), "spad%d info", sidx);
1408 
1409 			SYSCTL_ADD_PROC(clist, peer_top, OID_AUTO, buf,
1410 			    CTLTYPE_STRING | CTLFLAG_RWTUN | CTLFLAG_MPSAFE,
1411 			    tc, sidx, sysctl_peer_spad_handle, "IU", desc);
1412 		}
1413 	}
1414 }
1415 
1416 static int
ntb_tool_probe(device_t dev)1417 ntb_tool_probe(device_t dev)
1418 {
1419 	device_set_desc(dev, "NTB TOOL");
1420 	return (0);
1421 }
1422 
1423 static int
ntb_tool_attach(device_t dev)1424 ntb_tool_attach(device_t dev)
1425 {
1426 	struct tool_ctx *tc = device_get_softc(dev);
1427 	int rc = 0;
1428 
1429 	tc->dev = dev;
1430 	rc = tool_check_ntb(tc);
1431 	if (rc)
1432 		goto out;
1433 
1434 	tool_init_peers(tc);
1435 
1436 	rc = tool_init_mws(tc);
1437 	if (rc)
1438 		goto err_clear_data;
1439 
1440 	tool_init_spads(tc);
1441 
1442 	rc = tool_init_ntb(tc);
1443 	if (rc)
1444 		goto err_clear_spads;
1445 
1446 	tool_setup_sysctl(tc);
1447 
1448 	return (0);
1449 
1450 err_clear_spads:
1451 	tool_clear_spads(tc);
1452 	tool_clear_mws(tc);
1453 	tool_clear_peers(tc);
1454 err_clear_data:
1455 	tool_clear_data(tc);
1456 out:
1457 	device_printf(dev, "ntb_tool attached failed with err=(%d).\n", rc);
1458 	return (rc);
1459 }
1460 
1461 static int
ntb_tool_detach(device_t dev)1462 ntb_tool_detach(device_t dev)
1463 {
1464 	struct tool_ctx *tc = device_get_softc(dev);
1465 
1466 	tool_clear_ntb(tc);
1467 
1468 	tool_clear_spads(tc);
1469 
1470 	tool_clear_mws(tc);
1471 
1472 	tool_clear_peers(tc);
1473 
1474 	tool_clear_data(tc);
1475 
1476 	return (0);
1477 }
1478 
1479 static device_method_t ntb_tool_methods[] = {
1480 	/* Device interface */
1481 	DEVMETHOD(device_probe,     ntb_tool_probe),
1482 	DEVMETHOD(device_attach,    ntb_tool_attach),
1483 	DEVMETHOD(device_detach,    ntb_tool_detach),
1484 	DEVMETHOD_END
1485 };
1486 
1487 static DEFINE_CLASS_0(ntb_tool, ntb_tool_driver, ntb_tool_methods,
1488     sizeof(struct tool_ctx));
1489 DRIVER_MODULE(ntb_tool, ntb_hw, ntb_tool_driver, NULL, NULL);
1490 MODULE_DEPEND(ntb_tool, ntb, 1, 1, 1);
1491 MODULE_VERSION(ntb_tool, 1.0);
1492