--- /dev/null
+#!/bin/bash
+# Copyright (c) 2016 Microsemi. All Rights Reserved.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2 of
+# the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it would be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# Author: Logan Gunthorpe <logang@deltatee.com>
+
+REMOTE_HOST=
+LIST_DEVS=FALSE
+
+DEBUGFS=${DEBUGFS-/sys/kernel/debug}
+
+PERF_RUN_ORDER=32
+MAX_MW_SIZE=0
+RUN_DMA_TESTS=
+DONT_CLEANUP=
+MW_SIZE=65536
+
+function show_help()
+{
+       echo "Usage: $0 [OPTIONS] LOCAL_DEV REMOTE_DEV"
+       echo "Run tests on a pair of NTB endpoints."
+       echo
+       echo "If the NTB device loops back to the same host then,"
+       echo "just specifying the two PCI ids on the command line is"
+       echo "sufficient. Otherwise, if the NTB link spans two hosts"
+       echo "use the -r option to specify the hostname for the remote"
+       echo "device. SSH will then be used to test the remote side."
+       echo "An SSH key between the root users of the host would then"
+       echo "be highly recommended."
+       echo
+       echo "Options:"
+       echo "  -C              don't cleanup ntb modules on exit"
+       echo "  -d              run dma tests"
+       echo "  -h              show this help message"
+       echo "  -l              list available local and remote PCI ids"
+       echo "  -r REMOTE_HOST  specify the remote's hostname to connect"
+        echo "                  to for the test (using ssh)"
+       echo "  -p NUM          ntb_perf run order (default: $PERF_RUN_ORDER)"
+       echo "  -w max_mw_size  maxmium memory window size"
+       echo
+}
+
+function parse_args()
+{
+       OPTIND=0
+       while getopts "Cdhlm:r:p:w:" opt; do
+               case "$opt" in
+               C)  DONT_CLEANUP=1 ;;
+               d)  RUN_DMA_TESTS=1 ;;
+               h)  show_help; exit 0 ;;
+               l)  LIST_DEVS=TRUE ;;
+               m)  MW_SIZE=${OPTARG} ;;
+               r)  REMOTE_HOST=${OPTARG} ;;
+               p)  PERF_RUN_ORDER=${OPTARG} ;;
+               w)  MAX_MW_SIZE=${OPTARG} ;;
+               \?)
+                   echo "Invalid option: -$OPTARG" >&2
+                   exit 1
+                   ;;
+               esac
+       done
+}
+
+parse_args "$@"
+shift $((OPTIND-1))
+LOCAL_DEV=$1
+shift
+parse_args "$@"
+shift $((OPTIND-1))
+REMOTE_DEV=$1
+shift
+parse_args "$@"
+
+set -e
+
+function _modprobe()
+{
+        modprobe "$@"
+}
+
+function split_remote()
+{
+       VPATH=$1
+       REMOTE=
+
+       if [[ "$VPATH" == *":/"* ]]; then
+               REMOTE=${VPATH%%:*}
+               VPATH=${VPATH#*:}
+       fi
+}
+
+function read_file()
+{
+       split_remote $1
+       if [[ "$REMOTE" != "" ]]; then
+               ssh "$REMOTE" cat "$VPATH"
+       else
+               cat "$VPATH"
+       fi
+}
+
+function write_file()
+{
+       split_remote $2
+       VALUE=$1
+
+       if [[ "$REMOTE" != "" ]]; then
+               ssh "$REMOTE" "echo \"$VALUE\" > \"$VPATH\""
+       else
+               echo "$VALUE" > "$VPATH"
+       fi
+}
+
+function link_test()
+{
+       LOC=$1
+       REM=$2
+       EXP=0
+
+       echo "Running link tests on: $(basename $LOC) / $(basename $REM)"
+
+       if ! write_file "N" "$LOC/link" 2> /dev/null; then
+               echo "  Unsupported"
+               return
+       fi
+
+       write_file "N" "$LOC/link_event"
+
+       if [[ $(read_file "$REM/link") != "N" ]]; then
+               echo "Expected remote link to be down in $REM/link" >&2
+               exit -1
+       fi
+
+       write_file "Y" "$LOC/link"
+       write_file "Y" "$LOC/link_event"
+
+       echo "  Passed"
+}
+
+function doorbell_test()
+{
+       LOC=$1
+       REM=$2
+       EXP=0
+
+       echo "Running db tests on: $(basename $LOC) / $(basename $REM)"
+
+       write_file "c 0xFFFFFFFF" "$REM/db"
+
+       for ((i=1; i <= 8; i++)); do
+               let DB=$(read_file "$REM/db") || true
+               if [[ "$DB" != "$EXP" ]]; then
+                       echo "Doorbell doesn't match expected value $EXP " \
+                            "in $REM/db" >&2
+                       exit -1
+               fi
+
+               let "MASK=1 << ($i-1)" || true
+               let "EXP=$EXP | $MASK" || true
+               write_file "s $MASK" "$LOC/peer_db"
+       done
+
+       echo "  Passed"
+}
+
+function read_spad()
+{
+       VPATH=$1
+       IDX=$2
+
+       ROW=($(read_file "$VPATH" | grep -e "^$IDX"))
+       let VAL=${ROW[1]} || true
+       echo $VAL
+}
+
+function scratchpad_test()
+{
+       LOC=$1
+       REM=$2
+       CNT=$(read_file "$LOC/spad" | wc -l)
+
+       echo "Running spad tests on: $(basename $LOC) / $(basename $REM)"
+
+       for ((i = 0; i < $CNT; i++)); do
+               VAL=$RANDOM
+               write_file "$i $VAL" "$LOC/peer_spad"
+               RVAL=$(read_spad "$REM/spad" $i)
+
+               if [[ "$VAL" != "$RVAL" ]]; then
+                       echo "Scratchpad doesn't match expected value $VAL " \
+                            "in $REM/spad, got $RVAL" >&2
+                       exit -1
+               fi
+
+       done
+
+       echo "  Passed"
+}
+
+function write_mw()
+{
+       split_remote $2
+
+       if [[ "$REMOTE" != "" ]]; then
+               ssh "$REMOTE" \
+                       dd if=/dev/urandom "of=$VPATH" 2> /dev/null || true
+       else
+               dd if=/dev/urandom "of=$VPATH" 2> /dev/null || true
+       fi
+}
+
+function mw_test()
+{
+       IDX=$1
+       LOC=$2
+       REM=$3
+
+       echo "Running $IDX tests on: $(basename $LOC) / $(basename $REM)"
+
+       write_mw "$LOC/$IDX"
+
+       split_remote "$LOC/$IDX"
+       if [[ "$REMOTE" == "" ]]; then
+               A=$VPATH
+       else
+               A=/tmp/ntb_test.$$.A
+               ssh "$REMOTE" cat "$VPATH" > "$A"
+       fi
+
+       split_remote "$REM/peer_$IDX"
+       if [[ "$REMOTE" == "" ]]; then
+               B=$VPATH
+       else
+               B=/tmp/ntb_test.$$.B
+               ssh "$REMOTE" cat "$VPATH" > "$B"
+       fi
+
+       cmp -n $MW_SIZE "$A" "$B"
+       if [[ $? != 0 ]]; then
+               echo "Memory window $MW did not match!" >&2
+       fi
+
+       if [[ "$A" == "/tmp/*" ]]; then
+               rm "$A"
+       fi
+
+       if [[ "$B" == "/tmp/*" ]]; then
+               rm "$B"
+       fi
+
+       echo "  Passed"
+}
+
+function pingpong_test()
+{
+       LOC=$1
+       REM=$2
+
+       echo "Running ping pong tests on: $(basename $LOC) / $(basename $REM)"
+
+       LOC_START=$(read_file $LOC/count)
+       REM_START=$(read_file $REM/count)
+
+       sleep 7
+
+       LOC_END=$(read_file $LOC/count)
+       REM_END=$(read_file $REM/count)
+
+       if [[ $LOC_START == $LOC_END ]] || [[ $REM_START == $REM_END ]]; then
+               echo "Ping pong counter not incrementing!" >&2
+               exit 1
+       fi
+
+       echo "  Passed"
+}
+
+function perf_test()
+{
+       USE_DMA=$1
+
+       if [[ $USE_DMA == "1" ]]; then
+               WITH="with"
+       else
+               WITH="without"
+       fi
+
+       _modprobe ntb_perf run_order=$PERF_RUN_ORDER \
+               max_mw_size=$MAX_MW_SIZE use_dma=$USE_DMA
+
+       echo "Running local perf test $WITH DMA"
+       write_file "" $LOCAL_PERF/run
+       echo -n "  "
+       read_file $LOCAL_PERF/run
+       echo "  Passed"
+
+       echo "Running remote perf test $WITH DMA"
+       write_file "" $REMOTE_PERF/run
+       echo -n "  "
+       read_file $LOCAL_PERF/run
+       echo "  Passed"
+
+       _modprobe -r ntb_perf
+}
+
+function ntb_tool_tests()
+{
+       LOCAL_TOOL=$DEBUGFS/ntb_tool/$LOCAL_DEV
+       REMOTE_TOOL=$REMOTE_HOST:$DEBUGFS/ntb_tool/$REMOTE_DEV
+
+       echo "Starting ntb_tool tests..."
+
+       _modprobe ntb_tool
+
+       write_file Y $LOCAL_TOOL/link_event
+       write_file Y $REMOTE_TOOL/link_event
+
+       link_test $LOCAL_TOOL $REMOTE_TOOL
+       link_test $REMOTE_TOOL $LOCAL_TOOL
+
+       for PEER_TRANS in $(ls $LOCAL_TOOL/peer_trans*); do
+               PT=$(basename $PEER_TRANS)
+               write_file $MW_SIZE $LOCAL_TOOL/$PT
+               write_file $MW_SIZE $REMOTE_TOOL/$PT
+       done
+
+       doorbell_test $LOCAL_TOOL $REMOTE_TOOL
+       doorbell_test $REMOTE_TOOL $LOCAL_TOOL
+       scratchpad_test $LOCAL_TOOL $REMOTE_TOOL
+       scratchpad_test $REMOTE_TOOL $LOCAL_TOOL
+
+       for MW in $(ls $LOCAL_TOOL/mw*); do
+               MW=$(basename $MW)
+
+               mw_test $MW $LOCAL_TOOL $REMOTE_TOOL
+               mw_test $MW $REMOTE_TOOL $LOCAL_TOOL
+       done
+
+       _modprobe -r ntb_tool
+}
+
+function ntb_pingpong_tests()
+{
+       LOCAL_PP=$DEBUGFS/ntb_pingpong/$LOCAL_DEV
+       REMOTE_PP=$REMOTE_HOST:$DEBUGFS/ntb_pingpong/$REMOTE_DEV
+
+       echo "Starting ntb_pingpong tests..."
+
+       _modprobe ntb_pingpong
+
+       pingpong_test $LOCAL_PP $REMOTE_PP
+
+       _modprobe -r ntb_pingpong
+}
+
+function ntb_perf_tests()
+{
+       LOCAL_PERF=$DEBUGFS/ntb_perf/$LOCAL_DEV
+       REMOTE_PERF=$REMOTE_HOST:$DEBUGFS/ntb_perf/$REMOTE_DEV
+
+       echo "Starting ntb_perf tests..."
+
+       perf_test 0
+
+       if [[ $RUN_DMA_TESTS ]]; then
+               perf_test 1
+       fi
+}
+
+function cleanup()
+{
+       set +e
+       _modprobe -r ntb_tool 2> /dev/null
+       _modprobe -r ntb_perf 2> /dev/null
+       _modprobe -r ntb_pingpong 2> /dev/null
+       _modprobe -r ntb_transport 2> /dev/null
+       set -e
+}
+
+cleanup
+
+if ! [[ $$DONT_CLEANUP ]]; then
+       trap cleanup EXIT
+fi
+
+if [ "$(id -u)" != "0" ]; then
+       echo "This script must be run as root" 1>&2
+       exit 1
+fi
+
+if [[ "$LIST_DEVS" == TRUE ]]; then
+       echo "Local Devices:"
+       ls -1 /sys/bus/ntb/devices
+       echo
+
+       if [[ "$REMOTE_HOST" != "" ]]; then
+               echo "Remote Devices:"
+               ssh $REMOTE_HOST ls -1 /sys/bus/ntb/devices
+       fi
+
+       exit 0
+fi
+
+if [[ "$LOCAL_DEV" == $"" ]] || [[ "$REMOTE_DEV" == $"" ]]; then
+       show_help
+       exit 1
+fi
+
+ntb_tool_tests
+echo
+ntb_pingpong_tests
+echo
+ntb_perf_tests
+echo