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) 2015 EMC Corporation. 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) 2015 EMC Corporation. 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 Intel Corporation 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 Linux driver 49 * 50 * Contact Information: 51 * Allen Hubbe <Allen.Hubbe@emc.com> 52 */ 53 54/* 55 * How to use this tool, by example. 56 * 57 * Assuming $DBG_DIR is something like: 58 * '/sys/kernel/debug/ntb_tool/0000:00:03.0' 59 * 60 * Eg: check if clearing the doorbell mask generates an interrupt. 61 * 62 * # Set the doorbell mask 63 * root@self# echo 's 1' > $DBG_DIR/mask 64 * 65 * # Ring the doorbell from the peer 66 * root@peer# echo 's 1' > $DBG_DIR/peer_db 67 * 68 * # Clear the doorbell mask 69 * root@self# echo 'c 1' > $DBG_DIR/mask 70 * 71 * Observe debugging output in dmesg or your console. You should see a 72 * doorbell event triggered by clearing the mask. If not, this may indicate an 73 * issue with the hardware that needs to be worked around in the driver. 74 * 75 * Eg: read and write scratchpad registers 76 * 77 * root@peer# echo '0 0x01010101 1 0x7f7f7f7f' > $DBG_DIR/peer_spad 78 * 79 * root@self# cat $DBG_DIR/spad 80 * 81 * Observe that spad 0 and 1 have the values set by the peer. 82 */ 83 84#include <linux/init.h> 85#include <linux/kernel.h> 86#include <linux/module.h> 87 88#include <linux/debugfs.h> 89#include <linux/dma-mapping.h> 90#include <linux/pci.h> 91#include <linux/slab.h> 92 93#include <linux/ntb.h> 94 95#define DRIVER_NAME "ntb_tool" 96#define DRIVER_DESCRIPTION "PCIe NTB Debugging Tool" 97 98#define DRIVER_LICENSE "Dual BSD/GPL" 99#define DRIVER_VERSION "1.0" 100#define DRIVER_RELDATE "22 April 2015" 101#define DRIVER_AUTHOR "Allen Hubbe <Allen.Hubbe@emc.com>" 102 103MODULE_LICENSE(DRIVER_LICENSE); 104MODULE_VERSION(DRIVER_VERSION); 105MODULE_AUTHOR(DRIVER_AUTHOR); 106MODULE_DESCRIPTION(DRIVER_DESCRIPTION); 107 108static struct dentry *tool_dbgfs; 109 110struct tool_ctx { 111 struct ntb_dev *ntb; 112 struct dentry *dbgfs; 113}; 114 115#define SPAD_FNAME_SIZE 0x10 116#define INT_PTR(x) ((void *)(unsigned long)x) 117#define PTR_INT(x) ((int)(unsigned long)x) 118 119#define TOOL_FOPS_RDWR(__name, __read, __write) \ 120 const struct file_operations __name = { \ 121 .owner = THIS_MODULE, \ 122 .open = simple_open, \ 123 .read = __read, \ 124 .write = __write, \ 125 } 126 127static void tool_link_event(void *ctx) 128{ 129 struct tool_ctx *tc = ctx; 130 enum ntb_speed speed; 131 enum ntb_width width; 132 int up; 133 134 up = ntb_link_is_up(tc->ntb, &speed, &width); 135 136 dev_dbg(&tc->ntb->dev, "link is %s speed %d width %d\n", 137 up ? "up" : "down", speed, width); 138} 139 140static void tool_db_event(void *ctx, int vec) 141{ 142 struct tool_ctx *tc = ctx; 143 u64 db_bits, db_mask; 144 145 db_mask = ntb_db_vector_mask(tc->ntb, vec); 146 db_bits = ntb_db_read(tc->ntb); 147 148 dev_dbg(&tc->ntb->dev, "doorbell vec %d mask %#llx bits %#llx\n", 149 vec, db_mask, db_bits); 150} 151 152static const struct ntb_ctx_ops tool_ops = { 153 .link_event = tool_link_event, 154 .db_event = tool_db_event, 155}; 156 157static ssize_t tool_dbfn_read(struct tool_ctx *tc, char __user *ubuf, 158 size_t size, loff_t *offp, 159 u64 (*db_read_fn)(struct ntb_dev *)) 160{ 161 size_t buf_size; 162 char *buf; 163 ssize_t pos, rc; 164 165 if (!db_read_fn) 166 return -EINVAL; 167 168 buf_size = min_t(size_t, size, 0x20); 169 170 buf = kmalloc(buf_size, GFP_KERNEL); 171 if (!buf) 172 return -ENOMEM; 173 174 pos = scnprintf(buf, buf_size, "%#llx\n", 175 db_read_fn(tc->ntb)); 176 177 rc = simple_read_from_buffer(ubuf, size, offp, buf, pos); 178 179 kfree(buf); 180 181 return rc; 182} 183 184static ssize_t tool_dbfn_write(struct tool_ctx *tc, 185 const char __user *ubuf, 186 size_t size, loff_t *offp, 187 int (*db_set_fn)(struct ntb_dev *, u64), 188 int (*db_clear_fn)(struct ntb_dev *, u64)) 189{ 190 u64 db_bits; 191 char *buf, cmd; 192 ssize_t rc; 193 int n; 194 195 buf = kmalloc(size + 1, GFP_KERNEL); 196 if (!buf) 197 return -ENOMEM; 198 199 rc = simple_write_to_buffer(buf, size, offp, ubuf, size); 200 if (rc < 0) { 201 kfree(buf); 202 return rc; 203 } 204 205 buf[size] = 0; 206 207 n = sscanf(buf, "%c %lli", &cmd, &db_bits); 208 209 kfree(buf); 210 211 if (n != 2) { 212 rc = -EINVAL; 213 } else if (cmd == 's') { 214 if (!db_set_fn) 215 rc = -EINVAL; 216 else 217 rc = db_set_fn(tc->ntb, db_bits); 218 } else if (cmd == 'c') { 219 if (!db_clear_fn) 220 rc = -EINVAL; 221 else 222 rc = db_clear_fn(tc->ntb, db_bits); 223 } else { 224 rc = -EINVAL; 225 } 226 227 return rc ? : size; 228} 229 230static ssize_t tool_spadfn_read(struct tool_ctx *tc, char __user *ubuf, 231 size_t size, loff_t *offp, 232 u32 (*spad_read_fn)(struct ntb_dev *, int)) 233{ 234 size_t buf_size; 235 char *buf; 236 ssize_t pos, rc; 237 int i, spad_count; 238 239 if (!spad_read_fn) 240 return -EINVAL; 241 242 buf_size = min_t(size_t, size, 0x100); 243 244 buf = kmalloc(buf_size, GFP_KERNEL); 245 if (!buf) 246 return -ENOMEM; 247 248 pos = 0; 249 250 spad_count = ntb_spad_count(tc->ntb); 251 for (i = 0; i < spad_count; ++i) { 252 pos += scnprintf(buf + pos, buf_size - pos, "%d\t%#x\n", 253 i, spad_read_fn(tc->ntb, i)); 254 } 255 256 rc = simple_read_from_buffer(ubuf, size, offp, buf, pos); 257 258 kfree(buf); 259 260 return rc; 261} 262 263static ssize_t tool_spadfn_write(struct tool_ctx *tc, 264 const char __user *ubuf, 265 size_t size, loff_t *offp, 266 int (*spad_write_fn)(struct ntb_dev *, 267 int, u32)) 268{ 269 int spad_idx; 270 u32 spad_val; 271 char *buf; 272 int pos, n; 273 ssize_t rc; 274 275 if (!spad_write_fn) { 276 dev_dbg(&tc->ntb->dev, "no spad write fn\n"); 277 return -EINVAL; 278 } 279 280 buf = kmalloc(size + 1, GFP_KERNEL); 281 if (!buf) 282 return -ENOMEM; 283 284 rc = simple_write_to_buffer(buf, size, offp, ubuf, size); 285 if (rc < 0) { 286 kfree(buf); 287 return rc; 288 } 289 290 buf[size] = 0; 291 292 n = sscanf(buf, "%d %i%n", &spad_idx, &spad_val, &pos); 293 while (n == 2) { 294 rc = spad_write_fn(tc->ntb, spad_idx, spad_val); 295 if (rc) 296 break; 297 298 n = sscanf(buf + pos, "%d %i%n", &spad_idx, &spad_val, &pos); 299 } 300 301 if (n < 0) 302 rc = n; 303 304 kfree(buf); 305 306 return rc ? : size; 307} 308 309static ssize_t tool_db_read(struct file *filep, char __user *ubuf, 310 size_t size, loff_t *offp) 311{ 312 struct tool_ctx *tc = filep->private_data; 313 314 return tool_dbfn_read(tc, ubuf, size, offp, 315 tc->ntb->ops->db_read); 316} 317 318static ssize_t tool_db_write(struct file *filep, const char __user *ubuf, 319 size_t size, loff_t *offp) 320{ 321 struct tool_ctx *tc = filep->private_data; 322 323 return tool_dbfn_write(tc, ubuf, size, offp, 324 tc->ntb->ops->db_set, 325 tc->ntb->ops->db_clear); 326} 327 328static TOOL_FOPS_RDWR(tool_db_fops, 329 tool_db_read, 330 tool_db_write); 331 332static ssize_t tool_mask_read(struct file *filep, char __user *ubuf, 333 size_t size, loff_t *offp) 334{ 335 struct tool_ctx *tc = filep->private_data; 336 337 return tool_dbfn_read(tc, ubuf, size, offp, 338 tc->ntb->ops->db_read_mask); 339} 340 341static ssize_t tool_mask_write(struct file *filep, const char __user *ubuf, 342 size_t size, loff_t *offp) 343{ 344 struct tool_ctx *tc = filep->private_data; 345 346 return tool_dbfn_write(tc, ubuf, size, offp, 347 tc->ntb->ops->db_set_mask, 348 tc->ntb->ops->db_clear_mask); 349} 350 351static TOOL_FOPS_RDWR(tool_mask_fops, 352 tool_mask_read, 353 tool_mask_write); 354 355static ssize_t tool_peer_db_read(struct file *filep, char __user *ubuf, 356 size_t size, loff_t *offp) 357{ 358 struct tool_ctx *tc = filep->private_data; 359 360 return tool_dbfn_read(tc, ubuf, size, offp, 361 tc->ntb->ops->peer_db_read); 362} 363 364static ssize_t tool_peer_db_write(struct file *filep, const char __user *ubuf, 365 size_t size, loff_t *offp) 366{ 367 struct tool_ctx *tc = filep->private_data; 368 369 return tool_dbfn_write(tc, ubuf, size, offp, 370 tc->ntb->ops->peer_db_set, 371 tc->ntb->ops->peer_db_clear); 372} 373 374static TOOL_FOPS_RDWR(tool_peer_db_fops, 375 tool_peer_db_read, 376 tool_peer_db_write); 377 378static ssize_t tool_peer_mask_read(struct file *filep, char __user *ubuf, 379 size_t size, loff_t *offp) 380{ 381 struct tool_ctx *tc = filep->private_data; 382 383 return tool_dbfn_read(tc, ubuf, size, offp, 384 tc->ntb->ops->peer_db_read_mask); 385} 386 387static ssize_t tool_peer_mask_write(struct file *filep, const char __user *ubuf, 388 size_t size, loff_t *offp) 389{ 390 struct tool_ctx *tc = filep->private_data; 391 392 return tool_dbfn_write(tc, ubuf, size, offp, 393 tc->ntb->ops->peer_db_set_mask, 394 tc->ntb->ops->peer_db_clear_mask); 395} 396 397static TOOL_FOPS_RDWR(tool_peer_mask_fops, 398 tool_peer_mask_read, 399 tool_peer_mask_write); 400 401static ssize_t tool_spad_read(struct file *filep, char __user *ubuf, 402 size_t size, loff_t *offp) 403{ 404 struct tool_ctx *tc = filep->private_data; 405 406 return tool_spadfn_read(tc, ubuf, size, offp, 407 tc->ntb->ops->spad_read); 408} 409 410static ssize_t tool_spad_write(struct file *filep, const char __user *ubuf, 411 size_t size, loff_t *offp) 412{ 413 struct tool_ctx *tc = filep->private_data; 414 415 return tool_spadfn_write(tc, ubuf, size, offp, 416 tc->ntb->ops->spad_write); 417} 418 419static TOOL_FOPS_RDWR(tool_spad_fops, 420 tool_spad_read, 421 tool_spad_write); 422 423static ssize_t tool_peer_spad_read(struct file *filep, char __user *ubuf, 424 size_t size, loff_t *offp) 425{ 426 struct tool_ctx *tc = filep->private_data; 427 428 return tool_spadfn_read(tc, ubuf, size, offp, 429 tc->ntb->ops->peer_spad_read); 430} 431 432static ssize_t tool_peer_spad_write(struct file *filep, const char __user *ubuf, 433 size_t size, loff_t *offp) 434{ 435 struct tool_ctx *tc = filep->private_data; 436 437 return tool_spadfn_write(tc, ubuf, size, offp, 438 tc->ntb->ops->peer_spad_write); 439} 440 441static TOOL_FOPS_RDWR(tool_peer_spad_fops, 442 tool_peer_spad_read, 443 tool_peer_spad_write); 444 445static void tool_setup_dbgfs(struct tool_ctx *tc) 446{ 447 /* This modules is useless without dbgfs... */ 448 if (!tool_dbgfs) { 449 tc->dbgfs = NULL; 450 return; 451 } 452 453 tc->dbgfs = debugfs_create_dir(dev_name(&tc->ntb->dev), 454 tool_dbgfs); 455 if (!tc->dbgfs) 456 return; 457 458 debugfs_create_file("db", S_IRUSR | S_IWUSR, tc->dbgfs, 459 tc, &tool_db_fops); 460 461 debugfs_create_file("mask", S_IRUSR | S_IWUSR, tc->dbgfs, 462 tc, &tool_mask_fops); 463 464 debugfs_create_file("peer_db", S_IRUSR | S_IWUSR, tc->dbgfs, 465 tc, &tool_peer_db_fops); 466 467 debugfs_create_file("peer_mask", S_IRUSR | S_IWUSR, tc->dbgfs, 468 tc, &tool_peer_mask_fops); 469 470 debugfs_create_file("spad", S_IRUSR | S_IWUSR, tc->dbgfs, 471 tc, &tool_spad_fops); 472 473 debugfs_create_file("peer_spad", S_IRUSR | S_IWUSR, tc->dbgfs, 474 tc, &tool_peer_spad_fops); 475} 476 477static int tool_probe(struct ntb_client *self, struct ntb_dev *ntb) 478{ 479 struct tool_ctx *tc; 480 int rc; 481 482 if (ntb_db_is_unsafe(ntb)) 483 dev_dbg(&ntb->dev, "doorbell is unsafe\n"); 484 485 if (ntb_spad_is_unsafe(ntb)) 486 dev_dbg(&ntb->dev, "scratchpad is unsafe\n"); 487 488 tc = kmalloc(sizeof(*tc), GFP_KERNEL); 489 if (!tc) { 490 rc = -ENOMEM; 491 goto err_tc; 492 } 493 494 tc->ntb = ntb; 495 496 tool_setup_dbgfs(tc); 497 498 rc = ntb_set_ctx(ntb, tc, &tool_ops); 499 if (rc) 500 goto err_ctx; 501 502 ntb_link_enable(ntb, NTB_SPEED_AUTO, NTB_WIDTH_AUTO); 503 ntb_link_event(ntb); 504 505 return 0; 506 507err_ctx: 508 debugfs_remove_recursive(tc->dbgfs); 509 kfree(tc); 510err_tc: 511 return rc; 512} 513 514static void tool_remove(struct ntb_client *self, struct ntb_dev *ntb) 515{ 516 struct tool_ctx *tc = ntb->ctx; 517 518 ntb_clear_ctx(ntb); 519 ntb_link_disable(ntb); 520 521 debugfs_remove_recursive(tc->dbgfs); 522 kfree(tc); 523} 524 525static struct ntb_client tool_client = { 526 .ops = { 527 .probe = tool_probe, 528 .remove = tool_remove, 529 }, 530}; 531 532static int __init tool_init(void) 533{ 534 int rc; 535 536 if (debugfs_initialized()) 537 tool_dbgfs = debugfs_create_dir(KBUILD_MODNAME, NULL); 538 539 rc = ntb_register_client(&tool_client); 540 if (rc) 541 goto err_client; 542 543 return 0; 544 545err_client: 546 debugfs_remove_recursive(tool_dbgfs); 547 return rc; 548} 549module_init(tool_init); 550 551static void __exit tool_exit(void) 552{ 553 ntb_unregister_client(&tool_client); 554 debugfs_remove_recursive(tool_dbgfs); 555} 556module_exit(tool_exit); 557