mft: Initial copy of mft source

Add initial mft source of version-4.26.1-3 from
https://www.mellanox.com/downloads/MFT/mft-4.26.1-3-arm64-deb.tgz

bug 4192483
bug 4312056

Change-Id: I77c0af297c9833c3dcbcdfc89b316042548b9af8
Signed-off-by: Bharath H S <bhs@nvidia.com>
Reviewed-on: https://git-master.nvidia.com/r/c/linux-nv-oot/+/3069977
(cherry picked from commit d65df2a986469950aab3f323bbee3a3aee0c0308)
Reviewed-on: https://git-master.nvidia.com/r/c/linux-nv-oot/+/3069972
GVS: Gerrit_Virtual_Submit <buildbot_gerritrpt@nvidia.com>
Reviewed-by: Laxman Dewangan <ldewangan@nvidia.com>
This commit is contained in:
Bharath H S
2024-02-02 10:53:24 -08:00
committed by mobile promotions
parent f451b88fd7
commit bf89e2d98e
38 changed files with 5307 additions and 0 deletions

View File

@@ -0,0 +1,378 @@
This software is available to you under a choice of one of two licenses.
You may chose to be licensed under the terms of the the OpenIB.org BSD
license or the GNU General Public License (GPL) Version 2, both included
below.
Copyright (c) 2002-2011 Mellanox Technologies LTD. All rights reserved.
==================================================================
OpenIB.org BSD license
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials provided
with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
==================================================================
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Library General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
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 will 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.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Library General
Public License instead of this License.

View File

@@ -0,0 +1,25 @@
CPU_ARCH ?= $(shell uname -m)
all:
cd mst_backward_compatibility/mst_pci && $(MAKE)
cd mst_backward_compatibility/mst_pciconf && $(MAKE)
ifneq ($(findstring ppc64, $(CPU_ARCH)),)
cd mst_backward_compatibility/mst_ppc && $(MAKE)
endif
ifneq ($(findstring aarch64, $(CPU_ARCH)),)
cd misc_drivers/bf3_livefish && $(MAKE)
endif
clean:
$(MAKE) clean -C mst_backward_compatibility/mst_pci
$(MAKE) clean -C mst_backward_compatibility/mst_pciconf
ifneq ($(findstring ppc64, $(CPU_ARCH)),)
$(MAKE) clean -C mst_backward_compatibility/mst_ppc
endif
ifneq ($(findstring aarch64, $(CPU_ARCH)),)
$(MAKE) clean -C misc_drivers/bf3_livefish
endif

View File

@@ -0,0 +1,296 @@
#!/bin/sh
set -e
uname_s=$(uname -s)
_get_kernel_dir() {
KVER=$1
case ${uname_s} in
Linux) DIR="/lib/modules/$KVER/build" ;;
GNU/kFreeBSD) DIR="/usr/src/kfreebsd-headers-$KVER/sys" ;;
esac
echo $DIR
}
_check_kernel_dir() {
DIR=$(_get_kernel_dir $1)
case ${uname_s} in
Linux) test -e $DIR/include ;;
GNU/kFreeBSD) test -e $DIR/kern && test -e $DIR/conf/kmod.mk ;;
*) return 1 ;;
esac
return $?
}
# Check the existence of a kernel named as $1
_is_kernel_name_correct() {
CORRECT="no"
KERNEL_NAME=$1
for kernel in /boot/config-*; do
KERNEL=${kernel#*-}
if [ "${KERNEL}" = "${KERNEL_NAME}" ]; then
CORRECT="yes"
break
fi
done
echo $CORRECT
}
# Get the most recent kernel on Debian based systems. This keeps
# into account both the version and the ABI. If the current kernel
# is the most recent kernel then the function will print a null string.
_get_newest_kernel_debian() {
NEWEST_KERNEL=
NEWEST_VERSION=
NEWEST_ABI=
for kernel in /boot/config-*; do
KERNEL=${kernel#*-}
KERNEL_VERSION=${KERNEL%%-*}
ABI=${KERNEL#*-}
ABI=${ABI%%-*}
if [ -z "$NEWEST_KERNEL" ]; then
# The 1st time get a version which is bigger than $1
COMPARE_TO=$1
else
# Get the biggest version
COMPARE_TO="$NEWEST_VERSION-$NEWEST_ABI"
fi
# Make sure that kernel exists before comparing it
if [ ! -e /lib/modules/$KERNEL ]; then
continue
fi
# if $kernel is greater than $COMPARE_TO
if [ `dpkg --compare-versions "$KERNEL_VERSION-$ABI" gt "$COMPARE_TO" && echo "yes" || \
echo "no"` = "yes" ]; then
NEWEST_KERNEL=$KERNEL
NEWEST_VERSION=$KERNEL_VERSION
NEWEST_ABI=$ABI
fi
done
echo "$NEWEST_KERNEL"
}
# Get the most recent kernel in Rhel based systems. If the current kernel
# is the most recent kernel then the function will print a null string.
_get_newest_kernel_rhel() {
NEWEST_KERNEL=
LAST_INSTALLED_KERNEL=$(rpm -q --whatprovides kernel --last | grep kernel -m1 | cut -f1 -d' ')
LIK_FORMATTED_NAME=$(rpm -q $LAST_INSTALLED_KERNEL --queryformat="%{VERSION}-%{RELEASE}.%{ARCH}\n")
if [ `echo $LIK_FORMATTED_NAME | grep 2.6 >/dev/null` ]; then
# Fedora and Suse
NEWEST_KERNEL=$LIK_FORMATTED_NAME
else
# Hack for Mandriva where $LIK_FORMATTED_NAME is broken
LIK_NAME=$(rpm -q $LAST_INSTALLED_KERNEL --queryformat="%{NAME}\n")
LIK_TYPE=${LIK_NAME#kernel-}
LIK_TYPE=${LIK_TYPE%%-*}
LIK_STRIPPED=${LIK_NAME#kernel-}
LIK_STRIPPED=${LIK_STRIPPED#$LIK_TYPE-}
LIK_STRIPPED_BASE=${LIK_STRIPPED%%-*}
LIK_STRIPPED_END=${LIK_STRIPPED#$LIK_STRIPPED_BASE-}
LIK_FINAL=$LIK_STRIPPED_BASE-$LIK_TYPE-$LIK_STRIPPED_END
NEWEST_KERNEL=$LIK_FINAL
fi
echo $NEWEST_KERNEL
}
# Get the newest kernel on Debian and Rhel based systems.
get_newest_kernel() {
NEWEST_KERNEL=
# Try Debian first as rpm can be installed in Debian based distros
if [ -e /usr/bin/dpkg ]; then
# If DEB based
CURRENT_KERNEL=$1
CURRENT_VERSION=${CURRENT_KERNEL%%-*}
CURRENT_ABI=${CURRENT_KERNEL#*-}
CURRENT_FLAVOUR=${CURRENT_ABI#*-}
CURRENT_ABI=${CURRENT_ABI%%-*}
NEWEST_KERNEL=$(_get_newest_kernel_debian "$CURRENT_VERSION-$CURRENT_ABI")
elif [ `which rpm >/dev/null` ]; then
# If RPM based
NEWEST_KERNEL=$(_get_newest_kernel_rhel)
fi
# Make sure that kernel name that we extracted corresponds to an installed
# kernel
if [ -n "$NEWEST_KERNEL" ] && [ `_is_kernel_name_correct $NEWEST_KERNEL` = "no" ]; then
NEWEST_KERNEL=
fi
echo $NEWEST_KERNEL
}
NAME=$1
VERSION=$2
TARBALL_ROOT=$3
ARCH=$4
UPGRADE=$5
if [ -z "$NAME" ] || [ -z "$VERSION" ]; then
echo "Need NAME, and VERSION defined"
echo "ARCH is optional"
exit 1
fi
KERNELS=$(ls /lib/modules/ 2>/dev/null || true)
CURRENT_KERNEL=$(uname -r)
#We never want to keep an older version side by side to prevent conflicts
if [ -e "/var/lib/dkms/$NAME/$VERSION" ]; then
echo "Removing old $NAME-$VERSION DKMS files..."
dkms remove -m $NAME -v $VERSION --all
fi
#Load new files, by source package and by tarball
if [ -f "$TARBALL_ROOT/$NAME-$VERSION.dkms.tar.gz" ]; then
if ! dkms ldtarball --archive "$TARBALL_ROOT/$NAME-$VERSION.dkms.tar.gz"; then
echo ""
echo ""
echo "Unable to load DKMS tarball $TARBALL_ROOT/$NAME-$VERSION.dkms.tar.gz."
echo "Common causes include: "
echo " - You must be using DKMS 2.1.0.0 or later to support binaries only"
echo " distribution specific archives."
echo " - Corrupt distribution specific archive"
echo ""
echo ""
exit 2
fi
elif [ -d "/usr/src/$NAME-$VERSION" ]; then
echo "Loading new $NAME-$VERSION DKMS files..."
dkms add -m $NAME -v $VERSION > /dev/null
fi
# On 1st installation, let us look for a directory
# in /lib/modules which matches `uname -r`. If none
# is found it is possible that buildd is being used
# and that uname -r is giving us the name of the
# kernel used by the buildd machine.
#
# If this is the case we try to build the kernel
# module for each kernel which has a directory in
# /lib/modules. Furthermore we will have to tell
# DKMS which architecture it should build the module
# for (e.g. if the buildd machine is using a
# 2.6.24-23-xen 64bit kernel).
#
# NOTE: if the headers are not installed then the
# module won't be built, as usual
if [ -z "$UPGRADE" ]; then
echo "First Installation: checking all kernels..."
for KERNEL in $KERNELS; do
if [ ${KERNEL} = ${CURRENT_KERNEL} ]; then
# Kernel found
KERNELS=$CURRENT_KERNEL
break
fi
done
else
KERNELS=$CURRENT_KERNEL
fi
# Here we look for the most recent kernel so that we can
# build the module for it (in addition to doing it for the
# current kernel.
NEWEST_KERNEL=$(get_newest_kernel "$KERNELS")
# If the current kernel doesn't come from the host of a chroot
if [ `_is_kernel_name_correct $CURRENT_KERNEL` = "yes" ]; then
# See if it's worth building the module for both the newest kernel
# and for the current kernel
if [ -n "$NEWEST_KERNEL" ] && [ ${CURRENT_KERNEL} != ${NEWEST_KERNEL} ]; then
echo "Building for $CURRENT_KERNEL and $NEWEST_KERNEL"
KERNELS="$CURRENT_KERNEL $NEWEST_KERNEL"
else
echo "Building only for $CURRENT_KERNEL"
fi
# The current kernel is not useful as it's the host's
else
echo "It is likely that $CURRENT_KERNEL belongs to a chroot's host"
# Let's use only the newest kernel
if [ -n "$NEWEST_KERNEL" ]; then
KERNELS="$NEWEST_KERNEL"
echo "Building only for $NEWEST_KERNEL"
fi
fi
if [ -n "$ARCH" ]; then
if which lsb_release >/dev/null && [ $(lsb_release -s -i) = "Ubuntu" ]; then
case $ARCH in
amd64)
ARCH="x86_64"
;;
lpia|i?86)
ARCH="i686"
;;
esac
fi
echo "Building for architecture $ARCH"
ARCH="-a $ARCH"
fi
for KERNEL in $KERNELS; do
dkms_status=`dkms status -m $NAME -v $VERSION -k $KERNEL $ARCH`
if [ `echo $KERNEL | grep -c "BOOT"` -gt 0 ]; then
echo ""
echo "Module build and install for $KERNEL was skipped as "
echo "it is a BOOT variant"
continue
fi
#if the module isn't yet built, try to build it
if [ `echo $dkms_status | grep -c ": built"` -eq 0 ]; then
if [ ! -L /var/lib/dkms/$NAME/$VERSION/source ]; then
echo "This package appears to be a binaries-only package"
echo " you will not be able to build against kernel $KERNEL"
echo " since the package source was not provided"
continue
fi
if _check_kernel_dir $KERNEL; then
echo "Building initial module for $KERNEL"
set +e
dkms build -m $NAME -v $VERSION -k $KERNEL $ARCH > /dev/null
case $? in
9)
set -e
echo "Skipped."
continue
;;
0)
set -e
echo "Done."
;;
*)
exit $?
;;
esac
dkms_status=`dkms status -m $NAME -v $VERSION -k $KERNEL $ARCH`
else
echo "Module build for the currently running kernel was skipped since the"
echo "kernel source for this kernel does not seem to be installed."
fi
fi
#if the module is built (either pre-built or just now), install it
if [ `echo $dkms_status | grep -c ": built"` -eq 1 ] &&
[ `echo $dkms_status | grep -c ": installed"` -eq 0 ]; then
dkms install -m $NAME -v $VERSION -k $KERNEL $ARCH --force
fi
done

View File

@@ -0,0 +1,21 @@
PACKAGE_NAME=kernel-mft-dkms
PACKAGE_VERSION=4.26.1
MAKE[0]="make all KPVER=$kernelver"
CLEAN="make -C src/ clean"
BUILT_MODULE_NAME[0]=mst_pci
BUILT_MODULE_NAME[1]=mst_pciconf
BUILT_MODULE_LOCATION[0]=./mst_backward_compatibility/mst_pci
BUILT_MODULE_LOCATION[1]=./mst_backward_compatibility/mst_pciconf
DEST_MODULE_LOCATION[0]="/updates"
DEST_MODULE_LOCATION[1]="/updates"
if [ $(arch) = aarch64 ]; then
BUILT_MODULE_NAME[2]=bf3_livefish
BUILT_MODULE_LOCATION[2]=./misc_drivers/bf3_livefish/
DEST_MODULE_LOCATION[2]="/updates"
fi
#REMAKE_INITRD=yes
# Rebuild and autoinstall automatically when dkms_autoinstaller runs for a new kernel
AUTOINSTALL="yes"

View File

@@ -0,0 +1,11 @@
KPVER ?= $(shell uname -r)
KSRC ?= /lib/modules/$(KPVER)/build
obj-m += bf3_livefish.o
all:
make -C $(KSRC) M=$(PWD) CONFIG_CTF= CONFIG_CC_STACKPROTECTOR_STRONG= $(WITH_MAKE_PARAMS) modules
clean:
make -C $(KSRC) M=$(PWD) clean

View File

@@ -0,0 +1,357 @@
/*
* Copyright (c) 2023 NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED.
*
* This software product is a proprietary product of Nvidia Corporation and its affiliates
* (the "Company") and all right, title, and interest in and to the software
* product, including all associated intellectual property rights, are and
* shall remain exclusively with the Company.
*
* This software product is governed by the End User License Agreement
* provided with the software product.
*/
#include "bf3_livefish.h"
#include <linux/acpi.h>
#include <linux/errno.h>
#include <linux/io.h>
#include <linux/miscdevice.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/uaccess.h>
static __iomem void *hca_va;
/*
* A valid I/O must be entirely within CR space and not extend into
* any unmapped areas of CR space. We don't truncate I/O that extends
* past the end of the CR space region (unlike the behavior of, for
* example, simple_read_from_buffer) but instead just call the whole
* I/O invalid. We also enforce 4-byte alignment for all I/O.
*/
static bool valid_range(loff_t offset, size_t len)
{
if ((offset % 4 != 0) || (len % 4 != 0)) {
// Unaligned,
return false;
}
if ((offset >= 0) && (offset + len <= CRSPACE_SIZE)) {
// Inside the HCA space.
return true;
}
return false;
}
static bool set_lock(void)
{
int retries;
for (retries = 0; retries < 100; retries++) {
// Reading the lock value from g_gw_arm_nonsecure.lock.
u32 lock_dword = readl_relaxed(hca_va);
if (!(lock_dword & 0x80000000)) {
return true;
}
}
return false;
}
static void release_lock(void)
{
// Reading the lock value from g_gw_arm_nonsecure.lock.
u32 lock_dword = readl_relaxed(hca_va);
lock_dword &= 0x7fffffff;
writel_relaxed(lock_dword, hca_va);
}
static bool set_busy(void)
{
int retries;
// Set busy bit.
u32 busy_dword = readl_relaxed(hca_va);
busy_dword |= 1UL << 30;
writel_relaxed(busy_dword, hca_va);
for (retries = 0; retries < 1000; retries++) {
// Reading the bust value from g_gw_arm_nonsecure.busy.
u32 busy = readl_relaxed(hca_va);
if (!(busy & 0x40000000)) {
return true;
}
}
return false;
}
static u32 crspace_read(int offset)
{
u32 data, new_offset;
if (!set_lock()) {
return -EINVAL;
}
// Write the address to the GA: g_gw_arm_nonsecure.desc0.addr.
new_offset = offset >> 2; // HW expects addr[25:2] in that register.
writel_relaxed(new_offset, hca_va + 0x18);
// Set read operation.
writel_relaxed(1, hca_va + 0x0c);
if (!set_busy()) {
release_lock();
return -EINVAL;
}
// Reading the value of the desired address from
// gw_cr_64b.g_gw_arm_nonsecure.desc0.data_31_0 plus offset 0x14.
data = readl_relaxed(hca_va + 0x14);
release_lock();
return data;
}
static void crspace_write(u32 data, int offset)
{
u32 new_offset;
if (!set_lock()) {
return;
}
// Write the address to the GA: g_gw_arm_nonsecure.desc0.addr.
new_offset = offset >> 2; // HW expects addr[25:2] in that register.
writel_relaxed(new_offset, hca_va + 0x18);
// Writing the value of the desired address from
// gw_cr_64b.g_gw_arm_nonsecure.desc0.data_31_0 plus offset 0x14.
writel_relaxed(data, hca_va + 0x14);
// Set write operation.
writel_relaxed(0, hca_va + 0x0c);
if (!set_busy()) {
release_lock();
return;
}
release_lock();
}
/*
* Note that you can seek to illegal areas within the livefish device,
* but you won't be able to read or write there.
*/
static loff_t livefish_llseek(struct file *filp, loff_t offset, int whence)
{
if (offset % 4 != 0) {
return -EINVAL;
}
return fixed_size_llseek(filp, offset, whence, CRSPACE_SIZE);
}
static ssize_t livefish_read(struct file *filp, char __user *to,
size_t len, loff_t *ppos)
{
loff_t pos = *ppos;
size_t counter;
int word;
if (!valid_range(pos, len)) {
return -EINVAL;
}
if (len == 0) {
return 0;
}
for (counter = 0; counter < len; counter += 4, pos += 4) {
word = crspace_read(pos);
if (put_user(word, (int __user *)(to + counter)) != 0) {
break;
}
}
*ppos = pos;
return counter ?: -EFAULT;
}
static ssize_t livefish_write(struct file *filp, const char __user *from,
size_t len, loff_t *ppos)
{
loff_t pos = *ppos;
size_t counter;
int word;
if (!valid_range(pos, len)) {
return -EINVAL;
}
if (len == 0) {
return 0;
}
for (counter = 0; counter < len; counter += 4, pos += 4) {
if (get_user(word, (int __user *)(from + counter)) != 0) {
break;
}
crspace_write(word, pos);
}
*ppos = pos;
return counter ?: -EFAULT;
}
static const struct file_operations livefish_fops = {
.owner = THIS_MODULE,
.llseek = livefish_llseek,
.read = livefish_read,
.write = livefish_write,
};
/* This name causes the correct semantics for the Mellanox MST tools. */
static struct miscdevice livefish_dev = {
.minor = MISC_DYNAMIC_MINOR,
.name = "bf3-livefish",
.mode = 0600,
.fops = &livefish_fops
};
/* Release any VA or PA mappings that have been set up. */
static void livefish_cleanup_mappings(void)
{
if (hca_va) {
iounmap(hca_va);
}
}
static int livefish_probe(struct platform_device *pdev)
{
struct acpi_device *acpi_dev = ACPI_COMPANION(&pdev->dev);
const char *hid = acpi_device_hid(acpi_dev);
struct resource *res;
int error = 0;
// Device ID validation.
if (strcmp(hid, "MLNXBF45") != 0) {
dev_err(&pdev->dev, "Invalid device ID %s\n", hid);
error = -ENODEV;
goto ReturnOnError;
}
// Find and map the HCA region.
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (res == NULL) {
error = -ENODEV;
goto ReturnOnError;
}
if (request_mem_region(res->start, resource_size(res),
"LiveFish (HCA)") == NULL) {
error = -ENODEV;
goto ReturnOnError;
}
hca_va = ioremap(res->start, resource_size(res));
if (!hca_va) {
error = -EINVAL;
goto ReturnOnError;
}
dev_info(&pdev->dev, "HCA Region PA: 0x%llx Size: 0x%llx\n",
res->start, resource_size(res));
error = misc_register(&livefish_dev);
if (error) {
goto ReturnOnError;
}
dev_info(&pdev->dev, "probed\n");
return error;
ReturnOnError:
livefish_cleanup_mappings();
return error;
}
static int livefish_remove(struct platform_device *pdev)
{
misc_deregister(&livefish_dev);
livefish_cleanup_mappings();
return 0;
}
static const struct of_device_id livefish_of_match[] = {
{ .compatible = "mellanox,mlxbf-livefish" },
{},
};
MODULE_DEVICE_TABLE(of, livefish_of_match);
static const struct acpi_device_id livefish_acpi_match[] = {
{ "MLNXBF45", 0 },
{},
};
MODULE_DEVICE_TABLE(acpi, livefish_acpi_match);
static struct platform_driver livefish_driver = {
.driver = {
.name = "mlxbf-livefish",
.of_match_table = livefish_of_match,
.acpi_match_table = ACPI_PTR(livefish_acpi_match),
},
.probe = livefish_probe,
.remove = livefish_remove,
};
module_platform_driver(livefish_driver);
MODULE_LICENSE("Dual BSD/GPL");
MODULE_DESCRIPTION("BlueField 3 LiveFish driver");
MODULE_AUTHOR("Itay Avraham <itayavr@nvidia.com>");
MODULE_VERSION(STRINGIFY(DRIVER_VERSION));

View File

@@ -0,0 +1,9 @@
#ifndef BF3_LIVEFISH_H
#define BF3_LIVEFISH_H
#define DRIVER_VERSION 1.0
#define STRINGIFY(s) #s
#define CRSPACE_SIZE (2 * 1024 * 1024)
#endif // BF3_LIVEFISH_H

View File

@@ -0,0 +1,32 @@
KPVER ?= $(shell uname -r)
KSRC ?= /lib/modules/$(KPVER)/build
# Oracle Linux OS.
ifneq ($(shell if (echo $(KPVER) | grep -qE 'uek'); then \
echo "YES"; else echo ""; fi),)
override WITH_MAKE_PARAMS += ctf-dir=$(CWD)/.ctf
endif
NNT_DRIVER_LOCATION = ../../nnt_driver
PACKAGE_NAME = nnt-driver
PACKAGE_VERSION = 1.0.0
PACKAGE_RC = 1
%: %.in
sed \
-e 's/@PACKAGE_NAME@/$(PACKAGE_NAME)/g' \
-e 's/@PACKAGE_VERSION@/$(PACKAGE_VERSION)/g' \
-e 's/@PACKAGE_RC@/$(PACKAGE_RC)/g' \
<$< >$@
obj-m += mst_pci.o
EXTRA_CFLAGS= -I$(PWD)/$(NNT_DRIVER_LOCATION)
mst_pci-objs += $(NNT_DRIVER_LOCATION)/nnt_device.o $(NNT_DRIVER_LOCATION)/nnt_dma.o $(NNT_DRIVER_LOCATION)/nnt_pci_conf_access.o \
$(NNT_DRIVER_LOCATION)/nnt_pci_conf_access_no_vsec.o $(NNT_DRIVER_LOCATION)/nnt_memory_access.o \
$(NNT_DRIVER_LOCATION)/nnt_ioctl.o mst_pci_bc.o
all:
make -C $(KSRC) M=$(PWD) CONFIG_CTF= CONFIG_CC_STACKPROTECTOR_STRONG= $(WITH_MAKE_PARAMS) modules
clean:
make -C $(KSRC) M=$(PWD) clean

View File

@@ -0,0 +1,3 @@
mst_pciconf.ko external
mst_pci.ko external
mst_ppc_pci_reset.ko external

View File

@@ -0,0 +1,436 @@
#include <linux/module.h>
#include <linux/init.h>
#include <linux/semaphore.h>
#include <linux/uaccess.h>
#include <linux/kernel.h>
#include "nnt_ioctl.h"
#include "nnt_defs.h"
#include "nnt_device.h"
#include "nnt_ioctl_defs.h"
#include "nnt_pci_conf_access.h"
#include "mst_pci_bc.h"
MODULE_AUTHOR("Itay Avraham <itayavr@nvidia.com>");
MODULE_DESCRIPTION("NNT Linux driver (NVIDIA® networking tools driver), this is the backward compatibility driver");
MODULE_LICENSE("Dual BSD/GPL");
struct driver_info nnt_driver_info;
static int major_number = -1;
static char* name = "mst_pci";
#define INIT PCI_INIT
#define STOP PCI_STOP
#define PCI_PARAMS_ PCI_PARAMS
#define CONNECTX_WA PCI_CONNECTX_WA
struct mst_device_data
{
char buffer[MST_BC_BUFFER_SIZE];
int buffer_used;
};
static struct mst_device_data mst_devices[MST_BC_MAX_MINOR];
static int mst_pci_bc_open(struct inode* inode, struct file* file)
{
if (file->private_data)
{
return 0;
}
set_private_data_open(file);
return 0;
}
static ssize_t mst_pci_bc_read(struct file* file, char* buf, size_t count, loff_t* f_pos)
{
struct mst_device_data* mst_device = NULL;
struct nnt_device* nnt_device = NULL;
int* buffer_used = NULL;
char* buffer = NULL;
int minor = 0;
int error = 0;
/* Get the nnt device structure */
error = get_nnt_device(file, &nnt_device);
if (error)
{
minor = iminor(file_inode(file));
mst_device = &mst_devices[minor];
buffer = mst_device->buffer;
buffer_used = &mst_device->buffer_used;
}
else
{
buffer = nnt_device->buffer_bc;
buffer_used = &nnt_device->buffer_used_bc;
error = mutex_lock_nnt(file);
CHECK_ERROR(error);
}
if (*f_pos >= *buffer_used)
{
count = 0;
goto MutexUnlock;
}
if (*f_pos + count > *buffer_used)
{
count = *buffer_used - *f_pos;
}
if (copy_to_user(buf, buffer + *f_pos, count))
{
count = -EFAULT;
goto MutexUnlock;
}
*f_pos += count;
MutexUnlock:
if (nnt_device)
{
mutex_unlock_nnt(file);
}
ReturnOnFinished:
return count;
}
static ssize_t mst_pci_bc_write(struct file* file, const char* buf, size_t count, loff_t* f_pos)
{
struct mst_device_data* mst_device = NULL;
struct nnt_device* nnt_device = NULL;
int* buffer_used = NULL;
char* buffer = NULL;
int minor = 0;
int error = 0;
/* Get the nnt device structure */
error = get_nnt_device(file, &nnt_device);
if (error)
{
minor = iminor(file_inode(file));
mst_device = &mst_devices[minor];
buffer = mst_device->buffer;
buffer_used = &mst_device->buffer_used;
}
else
{
buffer = nnt_device->buffer_bc;
buffer_used = &nnt_device->buffer_used_bc;
error = mutex_lock_nnt(file);
CHECK_ERROR(error);
}
if (*f_pos >= MST_BC_BUFFER_SIZE)
{
count = 0;
goto MutexUnlock;
}
if (*f_pos + count > MST_BC_BUFFER_SIZE)
{
count = MST_BC_BUFFER_SIZE - *f_pos;
}
if (copy_from_user(buffer + *f_pos, buf, count))
{
count = -EFAULT;
goto MutexUnlock;
}
*f_pos += count;
if (*buffer_used < *f_pos)
{
*buffer_used = *f_pos;
}
MutexUnlock:
if (nnt_device)
{
mutex_unlock_nnt(file);
}
ReturnOnFinished:
return count;
}
static inline int noncached_address(unsigned long addr)
{
return addr >= __pa(high_memory);
}
static int mst_pci_mmap(struct file* file, struct vm_area_struct* vma)
{
struct nnt_device* nnt_device = NULL;
unsigned long long offset = 0;
unsigned long vsize = 0;
unsigned long off = 0;
int error = 0;
/* Get the nnt device structure */
error = get_nnt_device(file, &nnt_device);
if (error)
{
goto ReturnOnFinished;
}
off = vma->vm_pgoff << PAGE_SHIFT;
vsize = vma->vm_end - vma->vm_start;
if ((nnt_device->device_pci.bar_size <= off) || (nnt_device->device_pci.bar_size < off + vsize))
{
error = -EINVAL;
goto ReturnOnFinished;
}
offset = nnt_device->device_pci.bar_address + off;
/* Accessing memory above the top the kernel knows about or through
a file pointer that was marked O_SYNC will be done non-cached. */
if (noncached_address(offset) || (file->f_flags & O_SYNC))
{
vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
}
error = io_remap_pfn_range(vma, vma->vm_start, offset >> PAGE_SHIFT, vsize, vma->vm_page_prot);
ReturnOnFinished:
return error;
}
static long ioctl(struct file* file, unsigned int command, unsigned long argument)
{
void* user_buffer = (void*)argument;
struct nnt_device* nnt_device = NULL;
int error = 0;
/* By convention, any user gets read access
* and is allowed to use the device.
* Commands with no direction are administration
* commands, and you need write permission
* for this */
if (_IOC_DIR(command) == _IOC_NONE)
{
if (!(file->f_mode & FMODE_WRITE))
{
return -EPERM;
}
}
else
{
if (!(file->f_mode & FMODE_READ))
{
return -EPERM;
}
}
if (command != INIT)
{
error = mutex_lock_nnt(file);
if (error)
{
return 0;
}
CHECK_ERROR(error);
/* Get the nnt device structure */
error = get_nnt_device(file, &nnt_device);
if (error)
{
error = 0;
goto ReturnOnFinished;
}
}
switch (command)
{
case INIT:
{
struct mst_pci_init_st mst_init;
struct pci_bus* bus = NULL;
/* Copy the request from user space. */
if (copy_from_user(&mst_init, user_buffer, sizeof(struct mst_pci_init_st)))
{
return -EFAULT;
}
error = set_private_data_bc(file, mst_init.bus, mst_init.devfn, mst_init.domain);
if (error)
{
return 0;
}
error = mutex_lock_nnt(file);
CHECK_ERROR(error);
/* Get the nnt device structure */
error = get_nnt_device(file, &nnt_device);
if (error)
{
goto ReturnOnFinished;
}
bus = pci_find_bus(mst_init.domain, mst_init.bus);
if (!bus)
{
printk(KERN_ERR "unable to find pci bus for domain: %x and bus: %x\n", mst_init.domain, mst_init.bus);
error = -ENXIO;
goto ReturnOnFinished;
}
nnt_device->pci_device = NULL;
nnt_device->pci_device = pci_get_slot(bus, mst_init.devfn);
if (!nnt_device->pci_device)
{
printk(KERN_ERR "missing pci device");
error = -ENXIO;
goto ReturnOnFinished;
}
if (mst_init.bar >= DEVICE_COUNT_RESOURCE)
{
printk(KERN_ERR "bar offset is too large");
error = -ENXIO;
goto ReturnOnFinished;
}
nnt_device->device_pci.bar_address = nnt_device->pci_device->resource[mst_init.bar].start;
nnt_device->device_pci.bar_size = nnt_device->pci_device->resource[mst_init.bar].end + 1 -
nnt_device->pci_device->resource[mst_init.bar].start;
if (nnt_device->device_pci.bar_size == 1)
{
nnt_device->device_pci.bar_size = 0;
}
nnt_device->buffer_used_bc = 0;
break;
}
case PCI_PARAMS_:
{
struct mst_pci_params_st params;
params.bar = nnt_device->device_pci.bar_address;
params.size = nnt_device->device_pci.bar_size;
if (copy_to_user(user_buffer, &params, sizeof(struct mst_pci_params_st)))
{
error = -EFAULT;
goto ReturnOnFinished;
}
break;
}
case CONNECTX_WA:
{
struct nnt_connectx_wa connectx_wa;
error = pci_connectx_wa(&connectx_wa, nnt_device);
/* Copy the data to the user space. */
if (copy_to_user(user_buffer, &connectx_wa, sizeof(struct nnt_connectx_wa)) != 0)
{
error = -EFAULT;
goto ReturnOnFinished;
}
break;
}
case STOP:
error = destroy_nnt_device_bc(nnt_device);
break;
default:
error = -EINVAL;
break;
}
ReturnOnFinished:
mutex_unlock_nnt(file);
return error;
}
static int mst_release(struct inode* inode, struct file* file)
{
struct nnt_device* nnt_device = NULL;
int error = 0;
/* Get the nnt device structure */
error = get_nnt_device(file, &nnt_device);
if (error)
{
error = 0;
goto ReturnOnFinished;
}
if (nnt_device->memory_device.connectx_wa_slot_p1)
{
unsigned int mask = 0;
error = mutex_lock_nnt(file);
CHECK_ERROR(error);
mask = ~(1 << (nnt_device->memory_device.connectx_wa_slot_p1 - 1));
nnt_device->memory_device.connectx_wa_slot_p1 &= mask; // Fix me
nnt_device->memory_device.connectx_wa_slot_p1 = 0;
mutex_unlock_nnt(file);
}
ReturnOnFinished:
return 0;
}
struct file_operations fop = {.unlocked_ioctl = ioctl,
.open = mst_pci_bc_open,
.write = mst_pci_bc_write,
.read = mst_pci_bc_read,
.mmap = mst_pci_mmap,
.release = mst_release,
.owner = THIS_MODULE};
int with_unknown = 0;
module_param(with_unknown, int, S_IRUSR | S_IWUSR);
static int __init mst_pci_init_module(void)
{
dev_t device_number = -1;
int is_alloc_chrdev_region = 0;
int error = 0;
/* Allocate char driver region and assign major number */
major_number = register_chrdev(0, name, &fop);
if (major_number <= 0)
{
printk(KERN_ERR "Unable to register character mst pci driver.\n");
error = -EINVAL;
}
/* Create device files for MFT. */
error = create_nnt_devices(device_number, is_alloc_chrdev_region, &fop, NNT_PCI_DEVICES, NNT_MELLANOX_PCI_VENDOR,
with_unknown) ||
create_nnt_devices(device_number, is_alloc_chrdev_region, &fop, NNT_PCI_DEVICES, NNT_NVIDIA_PCI_VENDOR,
with_unknown);
return error;
}
static void __exit mst_pci_cleanup_module(void)
{
int is_alloc_chrdev_region = 0;
destroy_nnt_devices(is_alloc_chrdev_region);
unregister_chrdev(major_number, name);
}
module_init(mst_pci_init_module);
module_exit(mst_pci_cleanup_module);

View File

@@ -0,0 +1,31 @@
#ifndef MST_PCI_H
#define MST_PCI_H
/* These will be specific for PCI */
#define PCI_MAGIC 0xD1
#define PCI_INIT _IOC(_IOC_NONE,PCI_MAGIC,0,sizeof(struct mst_pci_init_st))
struct mst_pci_init_st {
unsigned int domain;
unsigned int bus;
unsigned int devfn;
int bar;
};
#define PCI_STOP _IOC(_IOC_NONE,PCI_MAGIC,1,0)
#define PCI_PARAMS _IOR(PCI_MAGIC,2, struct mst_pci_params_st)
struct mst_pci_params_st {
unsigned long long __attribute__((packed)) bar;
unsigned long long __attribute__((packed)) size;
};
#define CONNECTX_WA_BASE 0xf0384 // SEM BASE ADDR. SEM 0xf0380 is reserved for external tools usage.
#define CONNECTX_WA_SIZE 3 // Size in entries
#define PCI_CONNECTX_WA _IOR(PCI_MAGIC,3, u_int32_t)
#endif

View File

@@ -0,0 +1,32 @@
KPVER ?= $(shell uname -r)
KSRC ?= /lib/modules/$(KPVER)/build
# Oracle Linux OS.
ifneq ($(shell if (echo $(KPVER) | grep -qE 'uek'); then \
echo "YES"; else echo ""; fi),)
override WITH_MAKE_PARAMS += ctf-dir=$(CWD)/.ctf
endif
NNT_DRIVER_LOCATION = ../../nnt_driver
PACKAGE_NAME = nnt-driver
PACKAGE_VERSION = 1.0.0
PACKAGE_RC = 1
%: %.in
sed \
-e 's/@PACKAGE_NAME@/$(PACKAGE_NAME)/g' \
-e 's/@PACKAGE_VERSION@/$(PACKAGE_VERSION)/g' \
-e 's/@PACKAGE_RC@/$(PACKAGE_RC)/g' \
<$< >$@
obj-m += mst_pciconf.o
EXTRA_CFLAGS= -I$(PWD)/$(NNT_DRIVER_LOCATION)
mst_pciconf-objs += $(NNT_DRIVER_LOCATION)/nnt_device.o $(NNT_DRIVER_LOCATION)/nnt_dma.o $(NNT_DRIVER_LOCATION)/nnt_pci_conf_access.o \
$(NNT_DRIVER_LOCATION)/nnt_pci_conf_access_no_vsec.o $(NNT_DRIVER_LOCATION)/nnt_memory_access.o \
$(NNT_DRIVER_LOCATION)/nnt_ioctl.o mst_pciconf_bc.o
all:
make -C $(KSRC) M=$(PWD) CONFIG_CTF= CONFIG_CC_STACKPROTECTOR_STRONG= $(WITH_MAKE_PARAMS) modules
clean:
make -C $(KSRC) M=$(PWD) clean

View File

@@ -0,0 +1,3 @@
mst_pciconf.ko external
mst_pci.ko external
mst_ppc_pci_reset.ko external

View File

@@ -0,0 +1,635 @@
#include <linux/module.h>
#include <linux/init.h>
#include <linux/semaphore.h>
#include <linux/uaccess.h>
#include <linux/kernel.h>
#include "nnt_ioctl.h"
#include "nnt_defs.h"
#include "nnt_device.h"
#include "nnt_ioctl_defs.h"
#include "nnt_pci_conf_access.h"
#include "mst_pciconf_bc.h"
MODULE_AUTHOR("Itay Avraham <itayavr@nvidia.com>");
MODULE_DESCRIPTION("NNT Linux driver (NVIDIA® networking tools driver), this is the backward compatibility driver");
MODULE_LICENSE("Dual BSD/GPL");
struct driver_info nnt_driver_info;
static int major_number = -1;
static char* name = "mst_pciconf";
#define INIT PCICONF_INIT
#define STOP PCICONF_STOP
#define READ4 PCICONF_READ4
#define READ4_NEW PCICONF_READ4_NEW
#define WRITE4 PCICONF_WRITE4
#define WRITE4_NEW PCICONF_WRITE4_NEW
#define MODIFY PCICONF_MODIFY
#define READ4_BUFFER PCICONF_READ4_BUFFER
#define READ4_BUFFER_EX PCICONF_READ4_BUFFER_EX
#define WRITE4_BUFFER PCICONF_WRITE4_BUFFER
#define MST_PARAMS PCICONF_MST_PARAMS
#define MST_META_DATA PCICONF_MST_META_DATA
#define GET_DMA_PAGES PCICONF_GET_DMA_PAGES
#define RELEASE_DMA_PAGES PCICONF_RELEASE_DMA_PAGES
#define READ_DWORD_FROM_CONFIG_SPACE PCICONF_READ_DWORD_FROM_CONFIG_SPACE
static int mst_pciconf_bc_open(struct inode* inode, struct file* file)
{
if (file->private_data)
{
return 0;
}
set_private_data_open(file);
return 0;
}
static ssize_t mst_pciconf_bc_read(struct file* file, char* buf, size_t count, loff_t* f_pos)
{
struct nnt_device* nnt_device = NULL;
int error = 0;
/* Get the nnt device structure */
error = get_nnt_device(file, &nnt_device);
if (error)
{
count = -EFAULT;
goto ReturnOnFinished;
}
error = mutex_lock_nnt(file);
if (*f_pos >= nnt_device->buffer_used_bc)
{
count = 0;
goto MutexUnlock;
}
if (*f_pos + count > nnt_device->buffer_used_bc)
{
count = nnt_device->buffer_used_bc - *f_pos;
}
if (copy_to_user(buf, nnt_device->buffer_bc + *f_pos, count))
{
count = -EFAULT;
goto MutexUnlock;
}
*f_pos += count;
MutexUnlock:
mutex_unlock_nnt(file);
ReturnOnFinished:
return count;
}
static ssize_t mst_pciconf_bc_write(struct file* file, const char* buf, size_t count, loff_t* f_pos)
{
struct nnt_device* nnt_device = NULL;
int error = 0;
/* Get the nnt device structure */
error = get_nnt_device(file, &nnt_device);
if (error)
{
count = -EFAULT;
goto ReturnOnFinished;
}
error = mutex_lock_nnt(file);
if (*f_pos >= MST_BC_BUFFER_SIZE)
{
count = 0;
goto MutexUnlock;
}
if (*f_pos + count > MST_BC_BUFFER_SIZE)
{
count = MST_BC_BUFFER_SIZE - *f_pos;
}
if (copy_from_user(nnt_device->buffer_bc + *f_pos, buf, count))
{
count = -EFAULT;
goto MutexUnlock;
}
*f_pos += count;
if (nnt_device->buffer_used_bc < *f_pos)
{
nnt_device->buffer_used_bc = *f_pos;
}
MutexUnlock:
mutex_unlock_nnt(file);
ReturnOnFinished:
return count;
}
static long ioctl(struct file* file, unsigned int command, unsigned long argument)
{
void* user_buffer = (void*)argument;
struct nnt_device* nnt_device = NULL;
int error = 0;
/* By convention, any user gets read access
* and is allowed to use the device.
* Commands with no direction are administration
* commands, and you need write permission
* for this */
if (_IOC_DIR(command) == _IOC_NONE)
{
if (!(file->f_mode & FMODE_WRITE))
{
return -EPERM;
}
}
else
{
if (!(file->f_mode & FMODE_READ))
{
return -EPERM;
}
}
if (command != INIT)
{
error = mutex_lock_nnt(file);
CHECK_ERROR(error);
/* Get the nnt device structure */
error = get_nnt_device(file, &nnt_device);
if (error)
{
goto ReturnOnFinished;
}
}
switch (command)
{
case INIT:
{
struct nnt_pciconf_init nnt_init;
struct mst_pciconf_init_st mst_init;
struct nnt_device* nnt_device = NULL;
if (copy_from_user(&mst_init, user_buffer, sizeof(struct mst_pciconf_init_st)))
{
error = -EFAULT;
goto ReturnOnFinished;
}
error = set_private_data_bc(file, mst_init.bus, mst_init.devfn, mst_init.domain);
if (error)
{
goto ReturnOnFinished;
}
error = mutex_lock_nnt(file);
CHECK_ERROR(error);
/* Get the nnt device structure */
error = get_nnt_device(file, &nnt_device);
if (error)
{
goto ReturnOnFinished;
}
nnt_init.address_register = mst_init.addr_reg;
nnt_init.address_data_register = mst_init.data_reg;
/* Truncate to 0 length on open for writing. */
if (file->f_flags & O_APPEND)
{
file->f_pos = nnt_device->buffer_used_bc;
}
else if ((file->f_flags & O_TRUNC) || (file->f_flags & O_WRONLY))
{
nnt_device->buffer_used_bc = 0;
}
error = nnt_device->access.init(nnt_device);
break;
}
case WRITE4:
{
struct nnt_rw_operation rw_operation;
struct mst_write4_st mst_write;
/* Copy the request from user space. */
if (copy_from_user(&mst_write, user_buffer, sizeof(struct mst_write4_st)))
{
error = -EFAULT;
goto ReturnOnFinished;
}
rw_operation.data[0] = mst_write.data;
rw_operation.offset = mst_write.offset;
rw_operation.size = 4;
error = nnt_device->access.write(nnt_device, &rw_operation);
break;
}
case WRITE4_NEW:
{
struct nnt_rw_operation rw_operation;
struct mst_write4_new_st mst_write;
/* Copy the request from user space. */
if (copy_from_user(&mst_write, user_buffer, sizeof(struct mst_write4_new_st)))
{
error = -EFAULT;
goto ReturnOnFinished;
}
rw_operation.data[0] = mst_write.data;
rw_operation.offset = mst_write.offset;
rw_operation.address_space = mst_write.address_space;
rw_operation.size = 4;
error = nnt_device->access.write(nnt_device, &rw_operation);
break;
}
case WRITE4_BUFFER:
{
struct mst_write4_buffer_st mst_write;
/* Copy the request from user space. */
if (copy_from_user(&mst_write, user_buffer, sizeof(struct mst_write4_buffer_st)))
{
error = -EFAULT;
goto ReturnOnFinished;
}
error = nnt_device->access.write(nnt_device, (struct nnt_rw_operation*)&mst_write);
if (error)
{
goto ReturnOnFinished;
}
/* No error, return the requested data length. */
error = mst_write.size;
break;
}
case READ4:
{
struct nnt_rw_operation rw_operation;
struct mst_read4_st mst_read;
/* Copy the request from user space. */
if (copy_from_user(&mst_read, user_buffer, sizeof(struct mst_read4_st)))
{
error = -EFAULT;
goto ReturnOnFinished;
}
rw_operation.offset = mst_read.offset;
rw_operation.size = 4;
error = nnt_device->access.read(nnt_device, &rw_operation);
if (error)
{
goto ReturnOnFinished;
}
mst_read.data = rw_operation.data[0];
/* Copy the data to the user space. */
if (copy_to_user(user_buffer, &mst_read, sizeof(struct mst_read4_st)) != 0)
{
error = -EFAULT;
goto ReturnOnFinished;
}
break;
}
case READ4_NEW:
{
struct nnt_rw_operation rw_operation;
struct mst_read4_new_st mst_read;
/* Copy the request from user space. */
if (copy_from_user(&mst_read, user_buffer, sizeof(struct mst_read4_new_st)))
{
error = -EFAULT;
goto ReturnOnFinished;
}
rw_operation.offset = mst_read.offset;
rw_operation.address_space = mst_read.address_space;
rw_operation.size = 4;
error = nnt_device->access.read(nnt_device, &rw_operation);
if (error)
{
goto ReturnOnFinished;
}
mst_read.data = rw_operation.data[0];
/* Copy the data to the user space. */
if (copy_to_user(user_buffer, &mst_read, sizeof(struct mst_read4_new_st)) != 0)
{
error = -EFAULT;
goto ReturnOnFinished;
}
break;
}
case READ4_BUFFER_EX:
case READ4_BUFFER:
{
struct mst_read4_buffer_st mst_read;
/* Copy the request from user space. */
if (copy_from_user(&mst_read, user_buffer, sizeof(struct mst_read4_buffer_st)))
{
error = -EFAULT;
goto ReturnOnFinished;
}
error = nnt_device->access.read(nnt_device, (struct nnt_rw_operation*)&mst_read);
if (error)
{
goto ReturnOnFinished;
}
/* Copy the data to the user space. */
if (copy_to_user(user_buffer, &mst_read, sizeof(struct mst_read4_buffer_st)) != 0)
{
error = -EFAULT;
goto ReturnOnFinished;
}
/* No error, return the requested data length. */
error = mst_read.size;
break;
}
case PCICONF_VPD_READ4:
{
int vpd_default_timeout = 2000;
struct mst_vpd_read4_st mst_vpd_read;
struct nnt_vpd nnt_vpd;
if (!nnt_device->vpd_capability_address)
{
printk(KERN_ERR "Device %s not support Vital Product Data\n", nnt_device->device_name);
error = -ENODEV;
goto ReturnOnFinished;
}
/* Copy the request from user space. */
if (copy_from_user(&mst_vpd_read, user_buffer, sizeof(struct mst_vpd_read4_st)) != 0)
{
error = -EFAULT;
goto ReturnOnFinished;
}
nnt_vpd.offset = mst_vpd_read.offset;
nnt_vpd.data = mst_vpd_read.data;
if (!nnt_vpd.timeout)
{
nnt_vpd.timeout = vpd_default_timeout;
}
error = vpd_read(&nnt_vpd, nnt_device);
if (error)
{
goto ReturnOnFinished;
}
mst_vpd_read.offset = nnt_vpd.offset;
mst_vpd_read.data = nnt_vpd.data;
/* Copy the data to the user space. */
if (copy_to_user(user_buffer, &mst_vpd_read, sizeof(struct mst_vpd_read4_st)) != 0)
{
error = -EFAULT;
goto ReturnOnFinished;
}
break;
}
case PCICONF_VPD_WRITE4:
{
int vpd_default_timeout = 2000;
struct mst_vpd_write4_st mst_vpd_write;
struct nnt_vpd nnt_vpd;
if (!nnt_device->vpd_capability_address)
{
printk(KERN_ERR "Device %s not support Vital Product Data\n", nnt_device->device_name);
error = -ENODEV;
goto ReturnOnFinished;
}
/* Copy the request from user space. */
if (copy_from_user(&mst_vpd_write, user_buffer, sizeof(struct mst_vpd_write4_st)) != 0)
{
error = -EFAULT;
goto ReturnOnFinished;
}
nnt_vpd.offset = mst_vpd_write.offset;
nnt_vpd.data = mst_vpd_write.data;
if (!nnt_vpd.timeout)
{
nnt_vpd.timeout = vpd_default_timeout;
}
error = vpd_write(&nnt_vpd, nnt_device);
if (error)
{
goto ReturnOnFinished;
}
mst_vpd_write.offset = nnt_vpd.offset;
mst_vpd_write.data = nnt_vpd.data;
/* Copy the data to the user space. */
if (copy_to_user(user_buffer, &mst_vpd_write, sizeof(struct mst_vpd_write4_st)) != 0)
{
error = -EFAULT;
goto ReturnOnFinished;
}
break;
}
case GET_DMA_PAGES:
{
error = dma_pages_ioctl(NNT_GET_DMA_PAGES, user_buffer, nnt_device);
break;
}
case RELEASE_DMA_PAGES:
{
error = dma_pages_ioctl(NNT_RELEASE_DMA_PAGES, user_buffer, nnt_device);
break;
}
case READ_DWORD_FROM_CONFIG_SPACE:
{
struct nnt_read_dword_from_config_space nnt_read_from_cspace = {0};
/* Copy the request from user space. */
if (copy_from_user(&nnt_read_from_cspace, user_buffer, sizeof(struct nnt_read_dword_from_config_space)))
{
error = -EFAULT;
goto ReturnOnFinished;
}
/* Read the dword. */
if (read_dword(&nnt_read_from_cspace, nnt_device))
{
goto ReturnOnFinished;
}
/* Copy the data to the user space. */
if (copy_to_user(user_buffer, &nnt_read_from_cspace, sizeof(struct nnt_read_dword_from_config_space)) != 0)
{
error = -EFAULT;
goto ReturnOnFinished;
}
break;
}
case MST_META_DATA:
{
struct mst_meta_data meta_data;
struct mst_hdr hdr;
memset(&meta_data, 0, sizeof(meta_data));
/* Copy the request from user space. */
if (copy_from_user(&hdr, user_buffer, sizeof(struct mst_hdr)))
{
error = -EFAULT;
goto ReturnOnFinished;
}
if (hdr.payload_version_major != MST_META_DATA_VERSION_MAJOR || hdr.payload_len < sizeof(meta_data.data))
{
error = -EINVAL;
goto ReturnOnFinished;
}
// fill meta_data hdr
meta_data.hdr.hdr_version = MST_HDR_VERSION;
meta_data.hdr.hdr_len = sizeof(meta_data.hdr);
meta_data.hdr.payload_len = sizeof(meta_data.data);
meta_data.hdr.payload_version_major = MST_META_DATA_VERSION_MAJOR;
meta_data.hdr.payload_version_minor = MST_META_DATA_VERSION_MINOR;
// fill payload
meta_data.data.api_version_major = MST_API_VERSION_MAJOR;
meta_data.data.api_version_minor = MST_API_VERSION_MINOR;
/* Copy the data to the user space. */
if (copy_to_user(user_buffer, &meta_data, sizeof(meta_data)) != 0)
{
error = -EFAULT;
goto ReturnOnFinished;
}
break;
}
case MST_PARAMS:
{
struct nnt_device_parameters nnt_parameters;
struct mst_params_st mst_params;
error = get_nnt_device_parameters(&nnt_parameters, nnt_device);
if (error)
{
goto ReturnOnFinished;
}
mst_params.bus = nnt_parameters.bus;
mst_params.bar = 0;
mst_params.domain = nnt_parameters.domain;
mst_params.func = nnt_parameters.function;
mst_params.slot = nnt_parameters.slot;
mst_params.device = nnt_parameters.device;
mst_params.vendor = nnt_parameters.vendor;
mst_params.subsystem_device = nnt_parameters.subsystem_device;
mst_params.subsystem_vendor = nnt_parameters.subsystem_vendor;
mst_params.vendor_specific_cap = nnt_parameters.vendor_specific_capability;
mst_params.multifunction = nnt_parameters.multifunction;
mst_params.vsec_cap_mask = nnt_parameters.vsec_capability_mask;
/* Copy the data to the user space. */
if (copy_to_user(user_buffer, &mst_params, sizeof(struct mst_params_st)) != 0)
{
error = -EFAULT;
goto ReturnOnFinished;
}
break;
}
case STOP:
{
error = destroy_nnt_device_bc(nnt_device);
break;
}
case PCICONF_DMA_PROPS:
case PCICONF_MEM_ACCESS:
case MODIFY:
break;
default:
error = -EINVAL;
break;
}
ReturnOnFinished:
mutex_unlock_nnt(file);
return error;
}
struct file_operations fop = {.unlocked_ioctl = ioctl,
.open = mst_pciconf_bc_open,
.write = mst_pciconf_bc_write,
.read = mst_pciconf_bc_read,
.owner = THIS_MODULE};
int with_unknown = 0;
module_param(with_unknown, int, S_IRUSR | S_IWUSR);
static int __init mst_pciconf_init_module(void)
{
dev_t device_number = -1;
int is_alloc_chrdev_region = 0;
int error = 0;
/* Allocate char driver region and assign major number */
major_number = register_chrdev(0, name, &fop);
if (major_number <= 0)
{
printk(KERN_ERR "Unable to register character mst pciconf driver.\n");
error = -EINVAL;
}
/* Create device files for MFT. */
error = create_nnt_devices(device_number, is_alloc_chrdev_region, &fop, NNT_PCICONF_DEVICES,
NNT_MELLANOX_PCI_VENDOR, with_unknown) ||
create_nnt_devices(device_number, is_alloc_chrdev_region, &fop, NNT_PCI_DEVICES, NNT_NVIDIA_PCI_VENDOR,
with_unknown);
return error;
}
static void __exit mst_pciconf_cleanup_module(void)
{
int is_alloc_chrdev_region = 0;
destroy_nnt_devices(is_alloc_chrdev_region);
unregister_chrdev(major_number, name);
}
module_init(mst_pciconf_init_module);
module_exit(mst_pciconf_cleanup_module);

View File

@@ -0,0 +1,193 @@
#ifndef MST_PCICONF_H
#define MST_PCICONF_H
/* These will be specific for PCI CONF*/
#define PCICONF_MAGIC 0xD2
#define PCICONF_MAX_BUFFER_SIZE 256
#define PCICONF_MAX_MEMACCESS_SIZE 1024
#define PCICONF_MAX_PAGES_SIZE 8
#define PCICONF_CAP_VEC_LEN 16
/* Versions */
#define MST_HDR_VERSION 1
#define MST_META_DATA_VERSION_MAJOR 1
#define MST_META_DATA_VERSION_MINOR 0
#define MST_API_VERSION_MAJOR 1
#define MST_API_VERSION_MINOR 0
/* Common Structs*/
struct mst_hdr {
unsigned short hdr_version;
unsigned short hdr_len;
unsigned short payload_version_major;
unsigned int payload_version_minor;
unsigned int payload_len;
};
#define PCICONF_INIT _IOC(_IOC_NONE,PCICONF_MAGIC,0,sizeof(struct mst_pciconf_init_st))
struct mst_pciconf_init_st {
unsigned int domain;
unsigned int bus;
unsigned int devfn;
/* Byte offsets in configuration space */
unsigned int addr_reg;
unsigned int data_reg;
};
#define PCICONF_STOP _IOC (_IOC_NONE,PCICONF_MAGIC,1,0)
#define PCICONF_READ4 _IOR (PCICONF_MAGIC,1,struct mst_read4_st)
struct mst_read4_st {
unsigned int offset;
unsigned int data; /*OUT*/
};
#define PCICONF_WRITE4 _IOW (PCICONF_MAGIC,2,struct mst_write4_st)
struct mst_write4_st {
unsigned int offset;
unsigned int data;
};
#define PCICONF_MODIFY _IOWR(PCICONF_MAGIC,3,struct mst_modify_st)
struct mst_modify_st {
unsigned int address_space;
unsigned int offset;
unsigned int data;
unsigned int mask;
unsigned int old_data; /*OUT*/
};
#define PCICONF_READ4_BUFFER _IOR (PCICONF_MAGIC,4,struct mst_read4_st)
#define PCICONF_READ4_BUFFER_EX _IOR (PCICONF_MAGIC,4,struct mst_read4_buffer_st)
struct mst_read4_buffer_st {
unsigned int address_space;
unsigned int offset;
int size;
unsigned int data[PCICONF_MAX_BUFFER_SIZE/4]; /*OUT*/
};
#define PCICONF_WRITE4_BUFFER _IOW (PCICONF_MAGIC,5,struct mst_write4_buffer_st)
struct mst_write4_buffer_st {
unsigned int address_space;
unsigned int offset;
int size;
unsigned int data[PCICONF_MAX_BUFFER_SIZE/4]; /*IN*/
};
#define PCICONF_MST_PARAMS _IOR (PCICONF_MAGIC,6,struct mst_params_st)
struct mst_params_st {
unsigned int domain;
unsigned int bus;
unsigned int slot;
unsigned int func;
unsigned int bar;
unsigned int device;
unsigned int vendor;
unsigned int subsystem_device;
unsigned int subsystem_vendor;
unsigned int vendor_specific_cap;
u_int32_t vsec_cap_mask;
unsigned int multifunction;
};
#define PCICONF_READ4_NEW _IOR (PCICONF_MAGIC,7,struct mst_read4_new_st)
struct mst_read4_new_st {
unsigned int address_space;
unsigned int offset;
unsigned int data; /*OUT*/
};
/****************************************************/
/* VPD ACCESS */
#define PCICONF_VPD_READ4 _IOR(PCICONF_MAGIC, 7, struct mst_vpd_read4_st)
struct mst_vpd_read4_st {
unsigned int offset; /* IN - must be aligned to DWORD */
unsigned int data; /* OUT */
};
#define PCICONF_WRITE4_NEW _IOW (PCICONF_MAGIC,8,struct mst_write4_new_st)
struct mst_write4_new_st {
unsigned int address_space;
unsigned int offset;
unsigned int data;
};
#define PCICONF_VPD_WRITE4 _IOW(PCICONF_MAGIC, 8, struct mst_vpd_write4_st)
struct mst_vpd_write4_st {
unsigned int offset; /* IN - must be aligned to DWORD */
unsigned int data; /* IN */
};
/*
* MEM_ACCESS
*/
typedef enum {
MST_DMA_ICMD,
MST_DMA_END=32
} mst_dma_type_t;
#define PCICONF_MEM_ACCESS _IOWR(PCICONF_MAGIC, 10, struct mst_mem_access_st)
struct mst_mem_access_st {
mst_dma_type_t mem_type;
unsigned int _rw; /* READ: 0, WRITE: 1 */
unsigned int offset;
unsigned int size;
unsigned char data[PCICONF_MAX_MEMACCESS_SIZE];
};
#define PCICONF_DMA_PROPS _IOR (PCICONF_MAGIC, 11, struct mst_dma_props_st)
struct dma_prop {
unsigned long long int dma_pa;
unsigned int mem_size;
};
struct mst_dma_props_st {
struct dma_prop dma_props[MST_DMA_END];
};
#define PCICONF_MST_META_DATA _IOR (PCICONF_MAGIC, 12, struct mst_meta_data)
struct mst_meta_data_payload {
unsigned short api_version_major;
unsigned int api_version_minor;
unsigned int cap_vector[PCICONF_CAP_VEC_LEN];
};
struct mst_meta_data {
struct mst_hdr hdr;
struct mst_meta_data_payload data;
};
#define PCICONF_GET_DMA_PAGES _IOR (PCICONF_MAGIC, 13, struct page_info_st)
#define PCICONF_RELEASE_DMA_PAGES _IOR (PCICONF_MAGIC, 14, struct page_info_st)
struct page_address_st {
u_int64_t dma_address;
u_int64_t virtual_address;
};
struct page_info_st {
unsigned int page_amount;
unsigned long page_pointer_start;
struct page_address_st page_address_array[PCICONF_MAX_PAGES_SIZE];
};
#define PCICONF_READ_DWORD_FROM_CONFIG_SPACE _IOR (PCICONF_MAGIC, 15, struct read_dword_from_config_space)
struct read_dword_from_config_space {
unsigned int offset;
unsigned int data;
};
#endif

View File

@@ -0,0 +1,32 @@
KPVER ?= $(shell uname -r)
KSRC ?= /lib/modules/$(KPVER)/build
CPU_ARCH ?= $(shell uname -m)
# Oracle Linux OS.
ifneq ($(shell if (echo $(KPVER) | grep -qE 'uek'); then \
echo "YES"; else echo ""; fi),)
override WITH_MAKE_PARAMS += ctf-dir=$(CWD)/.ctf
endif
NNT_DRIVER_LOCATION = ../../nnt_driver
PACKAGE_NAME = nnt-driver
PACKAGE_VERSION = 1.0.0
PACKAGE_RC = 1
%: %.in
sed \
-e 's/@PACKAGE_NAME@/$(PACKAGE_NAME)/g' \
-e 's/@PACKAGE_VERSION@/$(PACKAGE_VERSION)/g' \
-e 's/@PACKAGE_RC@/$(PACKAGE_RC)/g' \
<$< >$@
ifneq ($(findstring ppc64, $(CPU_ARCH)),)
obj-m += mst_ppc_pci_reset.o
EXTRA_CFLAGS= -I$(PWD)/$(NNT_DRIVER_LOCATION)
endif
all:
make -C $(KSRC) M=$(PWD) CONFIG_CTF= CONFIG_CC_STACKPROTECTOR_STRONG= $(WITH_MAKE_PARAMS) modules
clean:
make -C $(KSRC) M=$(PWD) clean

View File

@@ -0,0 +1,306 @@
#include <linux/pci.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kmod.h>
#include <linux/delay.h>
#include "nnt_ppc_device_list.h"
#include "nnt_ppc_driver_defs.h"
#include "nnt_defs.h"
MODULE_AUTHOR("Itay Avraham <itayavr@nvidia.com>");
MODULE_DESCRIPTION("NNT PPC driver (NVIDIA® networking tools driver)");
MODULE_LICENSE("Dual BSD/GPL");
/* Passing PCI devices (DBDF addresses), separated by comma, for example:
* 0000:00:08.0,0000:00:08.1 */
char pci_device_list[NNT_DEVICE_LIST_SIZE];
/* Create the file in sysfs. */
module_param_string(pci_dev, pci_device_list, sizeof(pci_device_list), 0444);
struct nnt_ppc_reset_info nnt_ppc_reset;
void restore_pci_configuration_space(void)
{
struct nnt_ppc_device* nnt_pci_device;
list_for_each_entry(nnt_pci_device, &nnt_device_list,
entry) {
/* Restore the saved state of a PCI device. */
pci_restore_state(nnt_pci_device->pci_device);
}
}
int wait_for_response(void)
{
unsigned short device_id = NNT_UNKNOWN_DEVICE_ID;
struct nnt_ppc_device* nnt_pci_device;
int polling_counter = 0;
int error = 0;
list_for_each_entry(nnt_pci_device, &nnt_device_list,
entry) {
struct pci_dev* pci_device = nnt_pci_device->pci_device;
/* Device id still unknown ? */
while(device_id != pci_device->device) {
/* 100ms is the minimum time that prevents error logs on
dmesg (device is not ready for PCI configuration cycles). */
msleep(NNT_MINIMUM_WAITING_TIME);
/* Read the device id.
Access can fail (if device is not ready) and
as a result we might get errors in dmesg. */
pci_read_config_word(pci_device, PCI_DEVICE_ID,
&device_id);
/* Polling counter violation. */
if (polling_counter > NNT_MAXIMUM_POLLING_NUMBER) {
printk(KERN_ERR "%s Polling on device id failed: reached max value of polling failures for device: %s\n",
dev_driver_string(&pci_device->dev), dev_name(&pci_device->dev));
error = -EINVAL;
goto ReturnOnFinished;
}
polling_counter++;
}
}
ReturnOnFinished:
return error;
}
int set_reset_state(enum pcie_reset_state state)
{
struct nnt_ppc_device* nnt_pci_device;
int error = 0;
list_for_each_entry(nnt_pci_device, &nnt_device_list,
entry) {
struct pci_dev* pci_device = nnt_pci_device->pci_device;
if (PCI_FUNC(pci_device->devfn) == 0) {
/* Set reset state for device devce. */
printk(KERN_DEBUG "%s Send hot reset to the device: %s\n",dev_driver_string(&pci_device->dev), dev_name(&pci_device->dev));
error = pci_set_pcie_reset_state(pci_device, state);
if (error) {
printk(KERN_ERR "%s Set reset state for device failed for device: %s - error: %d\n",
dev_driver_string(&pci_device->dev), dev_name(&pci_device->dev), error);
goto ReturnOnFinished;
}
}
}
ReturnOnFinished:
return error;
}
int save_pci_configucation_space(void)
{
struct nnt_ppc_device* nnt_pci_device = NULL;
int error = 0;
list_for_each_entry(nnt_pci_device, &nnt_device_list,
entry) {
struct pci_dev* pci_device = nnt_pci_device->pci_device;
/* Initialize device before it's used by a driver. */
error = pci_enable_device(pci_device);
if (error) {
printk(KERN_ERR "%s Reset failed for device: %s - error: %d\n",
dev_driver_string(&pci_device->dev), dev_name(&pci_device->dev), error);
goto ReturnOnFinished;
}
/* Enables bus-mastering for device device. */
pci_set_master(pci_device);
/* Save the PCI configuration space of a device before sending hot reset. */
error = pci_save_state(pci_device);
if (error) {
printk(KERN_ERR "%s Reset failed for device: %s - error: %d\n",
dev_driver_string(&pci_device->dev), dev_name(&pci_device->dev), error);
goto ReturnOnFinished;
}
}
ReturnOnFinished:
return error;
}
int pci_devices_reset(void)
{
int error = 0;
if (nnt_ppc_reset.reset_was_done) {
goto ReturnOnFinished;
}
/* Save configuration space for all devices. */
error = save_pci_configucation_space();
CHECK_ERROR(error);
/* Disable the link by sending the hot reset. */
error = set_reset_state(pcie_hot_reset);
CHECK_ERROR(error);
msleep(jiffies_to_msecs(HZ));
/* Enable the link by sending the hot reset. */
error = set_reset_state(pcie_deassert_reset);
CHECK_ERROR(error);
/* Wait for the device to response to PCI configuration cycles. */
error = wait_for_response();
CHECK_ERROR(error);
/* Restore PCI configuration space for all PCI devices. */
restore_pci_configuration_space();
nnt_ppc_reset.reset_was_done = 1;
ReturnOnFinished:
return error;
}
static int init_pci_device(struct pci_dev *pdev, const struct pci_device_id *id)
{
struct nnt_ppc_device* nnt_pci_device;
list_for_each_entry(nnt_pci_device, &nnt_device_list,
entry) {
if (!strcmp(nnt_pci_device->pci_device_dbdf_name, dev_name(&pdev->dev))) {
nnt_pci_device->pci_device = pdev;
nnt_ppc_reset.number_of_found_pci_device++;
}
}
if (nnt_ppc_reset.number_of_requested_pci_device == nnt_ppc_reset.number_of_found_pci_device) {
return pci_devices_reset();
}
return 0;
}
static void remove_pci_device(struct pci_dev *pdev)
{
struct nnt_ppc_device* nnt_pci_device;
list_for_each_entry(nnt_pci_device, &nnt_device_list,
entry) {
if (!strcmp(nnt_pci_device->pci_device_dbdf_name, dev_name(&pdev->dev))) {
pci_clear_master(pdev);
pci_disable_device(pdev);
return;
}
}
}
int ppc_device_structure_init(struct nnt_ppc_device** nnt_pci_device, unsigned int pci_device_name_length)
{
/* Allocate nnt device structure. */
*nnt_pci_device=
kzalloc(sizeof(struct nnt_ppc_device),GFP_KERNEL);
if (!(*nnt_pci_device)) {
return -ENOMEM;
}
/* initialize nnt structure. */
memset(*nnt_pci_device, 0, sizeof(struct nnt_ppc_device));
(*nnt_pci_device)->pci_device_dbdf_name =
kzalloc(pci_device_name_length,GFP_KERNEL);
if (!(*nnt_pci_device)->pci_device_dbdf_name) {
return -ENOMEM;
}
return 0;
}
int parse_pci_devices_string(void)
{
struct nnt_ppc_device* nnt_pci_device;
char buffer[NNT_DEVICE_LIST_SIZE];
char* pci_device_dbdf_name = NULL;
char* dbdf_list = NULL;
int error;
strncpy(buffer, pci_device_list, NNT_DEVICE_LIST_SIZE);
dbdf_list = buffer;
/* Add the pci device name (DBDF) to the list. */
while ((pci_device_dbdf_name = strsep(&dbdf_list, ",")) != NULL) {
/* Allocate ppc device info structure. */
unsigned int pci_device_name_length = strlen(pci_device_dbdf_name);
nnt_pci_device = NULL;
error = ppc_device_structure_init(&nnt_pci_device, pci_device_name_length);
CHECK_ERROR(error);
/* Copy the device name string. */
strncpy(nnt_pci_device->pci_device_dbdf_name, pci_device_dbdf_name,
pci_device_name_length);
/* Create a device entry in the list. */
list_add_tail(&nnt_pci_device->entry, &nnt_device_list);
nnt_ppc_reset.number_of_requested_pci_device++;
}
ReturnOnFinished:
return error;
}
void init_members(void)
{
memset(&nnt_ppc_reset, 0, sizeof(struct nnt_ppc_reset_info));
}
static struct pci_driver nnt_ppc_driver = {
.name = "nnt_ppc_driver",
.id_table = pciconf_devices,
.probe = init_pci_device,
.remove = remove_pci_device,
};
static int __init init(void)
{
int error;
init_members();
/* Parse the parameters from the user space. */
error = parse_pci_devices_string();
CHECK_ERROR(error);
/* Register the NNT PPC driver. */
return pci_register_driver(&nnt_ppc_driver);
ReturnOnFinished:
return error;
}
static void __exit cleanup(void)
{
/* Unregister the NNT PPC driver. */
pci_unregister_driver(&nnt_ppc_driver);
}
module_init(init);
module_exit(cleanup);

View File

@@ -0,0 +1,34 @@
KVERSION ?= $(shell uname -r)
CPU_ARCH ?= $(shell uname -m)
# Oracle Linux OS.
ifneq ($(shell if (echo $(KVERSION) | grep -qE 'uek'); then \
echo "YES"; else echo ""; fi),)
override WITH_MAKE_PARAMS += ctf-dir=$(CWD)/.ctf
endif
PACKAGE_NAME = nnt-driver
PACKAGE_VERSION = 1.0.0
PACKAGE_RC = 1
%: %.in
sed \
-e 's/@PACKAGE_NAME@/$(PACKAGE_NAME)/g' \
-e 's/@PACKAGE_VERSION@/$(PACKAGE_VERSION)/g' \
-e 's/@PACKAGE_RC@/$(PACKAGE_RC)/g' \
<$< >$@
ifneq ($(findstring ppc64, $(CPU_ARCH)),)
obj-m += mst_ppc_pci_reset.o
endif
obj-m += nnt_driver.o
nnt_driver-objs += nnt_device.o nnt_dma.o nnt_pci_conf_access.o \
nnt_pci_conf_access_no_vsec.o nnt_memory_access.o \
nnt_ioctl.o
all:
make -C /lib/modules/$(KVERSION)/build M=$(PWD) CONFIG_CTF= CONFIG_CC_STACKPROTECTOR_STRONG= $(WITH_MAKE_PARAMS) modules
clean:
make -C /lib/modules/$(KVERSION)/build M=$(PWD) clean

View File

@@ -0,0 +1,42 @@
#ifndef NNT_DEFS_H
#define NNT_DEFS_H
#include <linux/kernel.h>
#include <linux/fs.h>
/* Passing MFT flag argument */
extern int is_mft_package;
extern struct driver_info nnt_driver_info;
#define NNT_DRIVER_NAME "nnt_driver"
#define NNT_CLASS_NAME "nnt_class"
#define NNT_DEVICE_PREFIX "mt"
#define NNT_DRIVER "NNT Driver::"
#define CHECK_PCI_READ_ERROR(error, address) \
if (error) { \
printk(KERN_ERR "Failed to read from address: %x\n", address); \
goto ReturnOnFinished; \
}
#define CHECK_PCI_WRITE_ERROR(error, address, data) \
if (error) { \
printk(KERN_ERR "Failed to write to address: %x, data: %x\n", address, data); \
goto ReturnOnFinished; \
}
#define CHECK_ERROR(error) \
if (error) { \
goto ReturnOnFinished; \
}
struct driver_info {
dev_t device_number;
int contiguous_device_numbers;
struct class* class_driver;
};
#endif

View File

@@ -0,0 +1,626 @@
#include "nnt_device.h"
#include "nnt_device_defs.h"
#include "nnt_defs.h"
#include "nnt_pci_conf_access.h"
#include "nnt_pci_conf_access_no_vsec.h"
#include "nnt_memory_access.h"
#include <linux/module.h>
MODULE_LICENSE("GPL");
/* Device list to check if device is available
since it could be removed by hotplug event. */
LIST_HEAD(nnt_device_list);
int get_nnt_device(struct file* file, struct nnt_device** nnt_device)
{
int error_code = 0;
if (!file->private_data)
{
error_code = -EINVAL;
}
else
{
*nnt_device = file->private_data;
}
return error_code;
}
void set_private_data_open(struct file* file)
{
struct nnt_device* current_nnt_device = NULL;
struct nnt_device* temp_nnt_device = NULL;
int minor = iminor(file_inode(file));
/* Set private data to nnt structure. */
list_for_each_entry_safe(current_nnt_device, temp_nnt_device, &nnt_device_list, entry)
{
if ((minor == current_nnt_device->device_minor_number) && current_nnt_device->device_enabled)
{
file->private_data = current_nnt_device;
return;
}
}
}
int set_private_data_bc(struct file* file, unsigned int bus, unsigned int devfn, unsigned int domain)
{
struct nnt_device* current_nnt_device = NULL;
struct nnt_device* temp_nnt_device = NULL;
int minor = iminor(file_inode(file));
unsigned int current_function;
unsigned int current_device;
/* Set private data to nnt structure. */
list_for_each_entry_safe(current_nnt_device, temp_nnt_device, &nnt_device_list, entry)
{
struct pci_bus* pci_bus = pci_find_bus(current_nnt_device->dbdf.domain, current_nnt_device->dbdf.bus);
if (!pci_bus)
{
return -ENXIO;
}
current_nnt_device->pci_device = pci_get_slot(pci_bus, current_nnt_device->dbdf.devfn);
if (!current_nnt_device->pci_device)
{
return -ENXIO;
}
current_function = PCI_FUNC(current_nnt_device->dbdf.devfn);
current_device = PCI_SLOT(current_nnt_device->dbdf.devfn);
if ((current_nnt_device->dbdf.bus == bus) && (current_device == PCI_SLOT(devfn)) &&
(current_function == PCI_FUNC(devfn)) && (current_nnt_device->dbdf.domain == domain))
{
current_nnt_device->device_minor_number = minor;
current_nnt_device->device_enabled = true;
file->private_data = current_nnt_device;
return 0;
}
}
return -EINVAL;
}
int set_private_data(struct file* file)
{
struct nnt_device* current_nnt_device = NULL;
struct nnt_device* temp_nnt_device = NULL;
int minor = iminor(file_inode(file));
/* Set private data to nnt structure. */
list_for_each_entry_safe(current_nnt_device, temp_nnt_device, &nnt_device_list, entry)
{
if (current_nnt_device->device_minor_number == minor)
{
file->private_data = current_nnt_device;
return 0;
}
}
printk(KERN_ERR "failed to find device with minor=%d\n", minor);
return -EINVAL;
}
int create_file_name_mstflint(struct pci_dev* pci_device, struct nnt_device* nnt_dev, enum nnt_device_type device_type)
{
sprintf(nnt_dev->device_name, "%4.4x:%2.2x:%2.2x.%1.1x_%s", pci_domain_nr(pci_device->bus), pci_device->bus->number,
PCI_SLOT(pci_device->devfn), PCI_FUNC(pci_device->devfn),
(device_type == NNT_PCICONF) ? MSTFLINT_PCICONF_DEVICE_NAME : MSTFLINT_MEMORY_DEVICE_NAME);
printk(KERN_DEBUG
"MSTFlint device name created: id: %d, slot id: %d, device name: /dev/%s domain: 0x%x bus: 0x%x\n",
pci_device->device, PCI_FUNC(pci_device->devfn), nnt_dev->device_name, pci_domain_nr(pci_device->bus),
pci_device->bus->number);
return 0;
}
int create_file_name_mft(struct pci_dev* pci_device, struct nnt_device* nnt_dev, enum nnt_device_type device_type)
{
sprintf(nnt_dev->device_name, "mst/mt%d_%s0.%x", pci_device->device,
(device_type == NNT_PCICONF) ? MFT_PCICONF_DEVICE_NAME : MFT_MEMORY_DEVICE_NAME,
PCI_FUNC(pci_device->devfn));
printk(KERN_DEBUG "MFT device name created: id: %d, slot id: %d, device name: /dev/%s domain: 0x%x bus: 0x%x\n",
pci_device->device, PCI_FUNC(pci_device->devfn), nnt_dev->device_name, pci_domain_nr(pci_device->bus),
pci_device->bus->number);
return 0;
}
int nnt_device_structure_init(struct nnt_device** nnt_device)
{
/* Allocate nnt device structure. */
*nnt_device = kzalloc(sizeof(struct nnt_device), GFP_KERNEL);
if (!(*nnt_device))
{
return -ENOMEM;
}
/* initialize nnt structure. */
memset(*nnt_device, 0, sizeof(struct nnt_device));
return 0;
}
int create_nnt_device(struct pci_dev* pci_device, enum nnt_device_type device_type, int is_alloc_chrdev_region)
{
struct nnt_device* nnt_device = NULL;
int error_code = 0;
/* Allocate nnt device info structure. */
if ((error_code = nnt_device_structure_init(&nnt_device)) != 0)
goto ReturnOnError;
if (is_alloc_chrdev_region)
{
/* Build the device file name of MSTFlint. */
if ((error_code = create_file_name_mstflint(pci_device, nnt_device, device_type)) != 0)
goto ReturnOnError;
}
else
{
/* Build the device file name of MFT. */
if ((error_code = create_file_name_mft(pci_device, nnt_device, device_type)) != 0)
goto ReturnOnError;
}
nnt_device->dbdf.bus = pci_device->bus->number;
nnt_device->dbdf.devfn = pci_device->devfn;
nnt_device->dbdf.domain = pci_domain_nr(pci_device->bus);
nnt_device->pci_device = pci_device;
nnt_device->device_type = device_type;
/* Add the nnt device structure to the list. */
list_add_tail(&nnt_device->entry, &nnt_device_list);
return error_code;
ReturnOnError:
if (nnt_device)
{
kfree(nnt_device);
}
return error_code;
}
int check_pci_id_range(unsigned short pci_device_id, unsigned short id_range_start)
{
return (pci_device_id >= id_range_start) && (pci_device_id <= (id_range_start + 100));
}
int is_connectx(unsigned short pci_device_id)
{
return check_pci_id_range(pci_device_id, CONNECTX3_PCI_ID);
}
int is_connectx3(unsigned short pci_device_id)
{
return pci_device_id == CONNECTX3_PCI_ID || pci_device_id == CONNECTX3PRO_PCI_ID;
}
int is_bluefield(unsigned short pci_device_id)
{
return check_pci_id_range(pci_device_id, BLUEFIELD_PCI_ID) ||
check_pci_id_range(pci_device_id, BLUEFIELD_DPU_AUX_PCI_ID);
}
int is_pcie_switch(unsigned short pci_device_id)
{
return check_pci_id_range(pci_device_id, SCHRODINGER_PCI_ID);
}
int is_quantum(unsigned short pci_device_id)
{
return check_pci_id_range(pci_device_id, QUANTUM_PCI_ID);
}
int is_spectrum(unsigned short pci_device_id)
{
return (pci_device_id == SPECTRUM_PCI_ID) || (check_pci_id_range(pci_device_id, SPECTRUM2_PCI_ID));
}
int is_switch_ib(unsigned short pci_device_id)
{
return pci_device_id == SWITCHIB_PCI_ID || pci_device_id == SWITCHIB2_PCI_ID;
}
int is_bw00(unsigned short pci_device_id)
{
return check_pci_id_range(pci_device_id, BW00_PCI_ID);
}
int is_bw02(unsigned short pci_device_id)
{
return check_pci_id_range(pci_device_id, BW02_PCI_ID);
}
int is_livefish_device(unsigned short pci_device_id)
{
return pci_device_id >= CONNECTX3_LIVEFISH_ID && pci_device_id < CONNECTX3_PCI_ID;
}
int is_nic(unsigned short pci_device_id)
{
return is_connectx(pci_device_id) || is_bluefield(pci_device_id);
}
int is_switch(unsigned short pci_device_id)
{
return is_pcie_switch(pci_device_id) || is_quantum(pci_device_id) || is_spectrum(pci_device_id) ||
is_switch_ib(pci_device_id) || is_bw00(pci_device_id) || is_bw02(pci_device_id);
}
int is_toolspf(unsigned short pci_device_id)
{
return is_nic(pci_device_id - 4000) || is_switch(pci_device_id - 4000);
}
int is_pciconf_device(unsigned short pci_device_id)
{
return is_nic(pci_device_id) || is_toolspf(pci_device_id) || is_livefish_device(pci_device_id) ||
is_switch(pci_device_id);
}
int is_pcicr_device(unsigned short pci_device_id)
{
return (is_switch(pci_device_id) || is_toolspf(pci_device_id) || is_connectx3(pci_device_id)) &&
(!is_livefish_device(pci_device_id));
}
int create_device_file(struct nnt_device* current_nnt_device,
dev_t device_number,
int minor,
struct file_operations* fop,
int is_alloc_chrdev_region)
{
struct device* device = NULL;
int error = 0;
int count = 1;
/* NNT driver will create the device file
once we stop support backward compatibility. */
current_nnt_device->device_minor_number = -1;
current_nnt_device->device_number = device_number;
current_nnt_device->mcdev.owner = THIS_MODULE;
mutex_init(&current_nnt_device->lock);
if (!is_alloc_chrdev_region)
{
goto ReturnOnFinished;
}
// Create device with a new minor number.
current_nnt_device->device_minor_number = minor;
current_nnt_device->device_number = MKDEV(MAJOR(device_number), minor);
current_nnt_device->device_enabled = true;
current_nnt_device->connectx_wa_slot_p1 = 0;
/* Create device node. */
device = device_create(nnt_driver_info.class_driver, NULL, current_nnt_device->device_number, NULL,
current_nnt_device->device_name);
if (!device)
{
printk(KERN_ERR "Device creation failed\n");
error = -EINVAL;
goto ReturnOnFinished;
}
/* Init new device. */
cdev_init(&current_nnt_device->mcdev, fop);
/* Add device to the system. */
error = cdev_add(&current_nnt_device->mcdev, current_nnt_device->device_number, count);
if (error)
{
goto ReturnOnFinished;
}
ReturnOnFinished:
return error;
}
int check_if_vsec_supported(struct nnt_device* nnt_device)
{
int error = 0;
error = nnt_device->access.init(nnt_device);
CHECK_ERROR(error);
if (!nnt_device->pciconf_device.vsec_fully_supported)
{
nnt_device->device_type = NNT_PCICONF_NO_VSEC;
nnt_device->access.read = read_pciconf_no_vsec;
nnt_device->access.write = write_pciconf_no_vsec;
nnt_device->access.init = init_pciconf_no_vsec;
}
ReturnOnFinished:
return error;
}
int is_mellanox_vendor_type(struct nnt_device* current_nnt_device, unsigned int vsec_address)
{
u_int8_t vendor_type = 0;
int error = 0;
/* Read the capability type field */
if ((error = pci_read_config_byte(current_nnt_device->pci_device, (vsec_address + PCI_TYPE_OFFSET), &vendor_type)))
{
printk(KERN_ERR "Reading VSEC type failed with error %d\n", error);
return 0;
}
if (vendor_type == MELLANOX_VSEC_TYPE)
{
return 1;
}
return 0;
}
unsigned int get_mellanox_vsec_address(struct nnt_device* current_nnt_device)
{
unsigned int vsec_address = 0;
/* Look for the Mellanox VSEC address. if Mellanox VSEC isn't supported the address should be 0 */
vsec_address = pci_find_capability(current_nnt_device->pci_device, VSEC_CAPABILITY_ADDRESS);
if (!vsec_address || is_mellanox_vendor_type(current_nnt_device, vsec_address))
{
return vsec_address;
}
/* if found a non-Mellanox type VSEC, iterate of the next available VSECs*/
while (
(vsec_address = pci_find_next_capability(current_nnt_device->pci_device, vsec_address, VSEC_CAPABILITY_ADDRESS)))
{
if (is_mellanox_vendor_type(current_nnt_device, vsec_address))
{
return vsec_address;
}
}
/* if Mellanox VSEC address was not found, return 0 */
return 0;
}
int create_devices(dev_t device_number, struct file_operations* fop, int is_alloc_chrdev_region)
{
struct nnt_device* current_nnt_device = NULL;
struct nnt_device* temp_nnt_device = NULL;
int minor = 0;
int error = 0;
/* Create necessary number of the devices. */
list_for_each_entry_safe(current_nnt_device, temp_nnt_device, &nnt_device_list, entry)
{
/* Create the device file. */
if ((error = create_device_file(current_nnt_device, device_number, minor, fop, is_alloc_chrdev_region)) != 0)
goto ReturnOnFinished;
/* Members initialization. */
current_nnt_device->pciconf_device.vendor_specific_capability = get_mellanox_vsec_address(current_nnt_device);
current_nnt_device->vpd_capability_address =
pci_find_capability(current_nnt_device->pci_device, PCI_CAP_ID_VPD);
if (!current_nnt_device->pciconf_device.vendor_specific_capability)
{
current_nnt_device->device_type = NNT_PCICONF_NO_VSEC;
}
switch (current_nnt_device->device_type)
{
case NNT_PCICONF:
current_nnt_device->access.read = read_pciconf;
current_nnt_device->access.write = write_pciconf;
current_nnt_device->access.init = init_pciconf;
error = check_if_vsec_supported(current_nnt_device);
CHECK_ERROR(error);
break;
case NNT_PCICONF_NO_VSEC:
current_nnt_device->access.read = read_pciconf_no_vsec;
current_nnt_device->access.write = write_pciconf_no_vsec;
current_nnt_device->access.init = init_pciconf_no_vsec;
break;
case NNT_PCI_MEMORY:
current_nnt_device->access.read = read_memory;
current_nnt_device->access.write = write_memory;
current_nnt_device->access.init = init_memory;
break;
}
if (is_alloc_chrdev_region)
{
error = current_nnt_device->access.init(current_nnt_device);
}
minor++;
}
ReturnOnFinished:
return error;
}
int create_nnt_devices(dev_t device_number,
int is_alloc_chrdev_region,
struct file_operations* fop,
enum nnt_device_type_flag nnt_device_flag,
unsigned int vendor_id,
int with_unknown)
{
struct pci_dev* pci_device = NULL;
int error_code = 0;
/* Find all Nvidia PCI devices. */
while ((pci_device = pci_get_device(vendor_id, PCI_ANY_ID, pci_device)) != NULL)
{
if ((nnt_device_flag == NNT_PCICONF_DEVICES) || (nnt_device_flag == NNT_ALL_DEVICES))
{
/* Create pciconf device. */
if (with_unknown || is_pciconf_device(pci_device->device))
{
if ((error_code = create_nnt_device(pci_device, NNT_PCICONF, is_alloc_chrdev_region)) != 0)
{
printk(KERN_ERR "Failed to create pci conf device\n");
goto ReturnOnFinished;
}
}
}
if ((nnt_device_flag == NNT_PCI_DEVICES) || (nnt_device_flag == NNT_ALL_DEVICES))
{
/* Create pci memory device. */
if (with_unknown || is_pcicr_device(pci_device->device))
{
if ((error_code = create_nnt_device(pci_device, NNT_PCI_MEMORY, is_alloc_chrdev_region)) != 0)
{
printk(KERN_ERR "Failed to create pci memory device\n");
goto ReturnOnFinished;
}
}
}
}
/* Create the devices. */
if ((error_code = create_devices(device_number, fop, is_alloc_chrdev_region)) != 0)
{
return error_code;
}
ReturnOnFinished:
return error_code;
}
int find_all_vendor_devices(unsigned int vendor_id)
{
struct pci_dev* pci_device = NULL;
int contiguous_device_numbers = 0;
while ((pci_device = pci_get_device(vendor_id, PCI_ANY_ID, pci_device)) != NULL)
{
contiguous_device_numbers++;
}
return contiguous_device_numbers;
}
int get_amount_of_nvidia_devices(void)
{
int contiguous_device_numbers = 0;
/* Find all Mellanox & Nvidia PCI devices. */
contiguous_device_numbers +=
find_all_vendor_devices(NNT_MELLANOX_PCI_VENDOR) + find_all_vendor_devices(NNT_NVIDIA_PCI_VENDOR);
return contiguous_device_numbers;
}
int mutex_lock_nnt(struct file* file)
{
struct nnt_device* nnt_device;
if (!file)
{
return 1;
}
nnt_device = file->private_data;
if (!nnt_device)
{
return -EINVAL;
}
mutex_lock(&nnt_device->lock);
return 0;
}
void mutex_unlock_nnt(struct file* file)
{
struct nnt_device* nnt_device = file->private_data;
if (nnt_device)
{
mutex_unlock(&nnt_device->lock);
}
}
void destroy_nnt_devices(int is_alloc_chrdev_region)
{
struct nnt_device* current_nnt_device;
struct nnt_device* temp_nnt_device;
/* free all nnt_devices */
list_for_each_entry_safe(current_nnt_device, temp_nnt_device, &nnt_device_list, entry)
{
/* Character device is no longer, it must be properly destroyed. */
if (is_alloc_chrdev_region)
{
cdev_del(&current_nnt_device->mcdev);
device_destroy(nnt_driver_info.class_driver, current_nnt_device->device_number);
}
list_del(&current_nnt_device->entry);
kfree(current_nnt_device);
}
}
void destroy_nnt_devices_bc(void)
{
struct nnt_device* current_nnt_device;
struct nnt_device* temp_nnt_device;
/* free all nnt_devices */
list_for_each_entry_safe(current_nnt_device, temp_nnt_device, &nnt_device_list, entry)
{
/* Character device is no longer, it must be properly destroyed. */
list_del(&current_nnt_device->entry);
kfree(current_nnt_device);
}
}
int destroy_nnt_device_bc(struct nnt_device* nnt_device)
{
struct nnt_device* current_nnt_device;
struct nnt_device* temp_nnt_device;
unsigned int current_function;
unsigned int current_device;
/* Set private data to nnt structure. */
list_for_each_entry_safe(current_nnt_device, temp_nnt_device, &nnt_device_list, entry)
{
struct pci_bus* pci_bus = pci_find_bus(current_nnt_device->dbdf.domain, current_nnt_device->dbdf.bus);
if (!pci_bus)
{
return -ENXIO;
}
current_nnt_device->pci_device = pci_get_slot(pci_bus, current_nnt_device->dbdf.devfn);
if (!current_nnt_device->pci_device)
{
return -ENXIO;
}
current_function = PCI_FUNC(current_nnt_device->dbdf.devfn);
current_device = PCI_SLOT(current_nnt_device->dbdf.devfn);
if ((current_nnt_device->dbdf.bus == nnt_device->dbdf.bus) &&
(current_device == PCI_SLOT(nnt_device->dbdf.devfn)) &&
(current_function == PCI_FUNC(nnt_device->dbdf.devfn)) &&
(current_nnt_device->dbdf.domain == nnt_device->dbdf.domain))
{
/* Character device is no longer, it must be properly disabled. */
current_nnt_device->device_enabled = false;
printk(KERN_DEBUG "Device removed: domain: %d, bus: %d, device:%d, function:%d \n",
current_nnt_device->dbdf.domain, current_nnt_device->dbdf.bus, current_device, current_function);
return 0;
}
}
return 0;
}

View File

@@ -0,0 +1,26 @@
#ifndef NNT_DEVICE_H
#define NNT_DEVICE_H
#include <linux/pci.h>
#include "nnt_device_defs.h"
int create_nnt_devices(dev_t device_number,
int is_alloc_chrdev_region,
struct file_operations* fop,
enum nnt_device_type_flag nnt_device_flag,
unsigned int vendor_id,
int with_unknown);
void destroy_nnt_devices(int is_alloc_chrdev_region);
void destroy_nnt_devices_bc(void);
int destroy_nnt_device_bc(struct nnt_device* nnt_device);
int is_pciconf_device(unsigned short pci_device_id);
int is_pcicr_device(unsigned short pci_device_id);
int get_amount_of_nvidia_devices(void);
int set_private_data(struct file* file);
void set_private_data_open(struct file* file);
int set_private_data_bc(struct file* file, unsigned int bus, unsigned int devfn, unsigned int domain);
int get_nnt_device(struct file* file, struct nnt_device** nnt_device);
int mutex_lock_nnt(struct file* file);
void mutex_unlock_nnt(struct file* file);
#endif

View File

@@ -0,0 +1,169 @@
#ifndef NNT_DEVICE_DEFS_H
#define NNT_DEVICE_DEFS_H
#include <linux/pci.h>
#include <linux/list.h>
#include <linux/cdev.h>
#include "nnt_ioctl_defs.h"
#define NNT_DEVICE_ID_OFFSET 0xf0014
#define NNT_WO_REG_ADDR_DATA 0xbadacce5
#define NNT_NAME_SIZE 75
#define NNT_CONF_ADDRES_REGISETER 88
#define NNT_CONF_DATA_REGISTER 92
#define MELLANOX_VSEC_TYPE 0
#define PCI_TYPE_OFFSET 0x03
#define PCI_SEMAPHORE_OFFSET 0x0c
#define PCI_ADDRESS_OFFSET 0x10
#define PCI_DATA_OFFSET 0x14
#define NNT_MEMORY_SIZE 1024 * 1024
#define VSEC_CAPABILITY_ADDRESS 0x9
#define MSTFLINT_PCICONF_DEVICE_NAME "mstconf"
#define MSTFLINT_MEMORY_DEVICE_NAME "mstcr"
#define MFT_PCICONF_DEVICE_NAME "pciconf"
#define MFT_MEMORY_DEVICE_NAME "pci_cr"
#define MST_BC_BUFFER_SIZE 256
#define MST_BC_MAX_MINOR 256
// Mellanox Vendor ID.
#define NNT_MELLANOX_PCI_VENDOR 0x15b3
// NVIDIA Vendor ID.
#define NNT_NVIDIA_PCI_VENDOR 0x10de
// Livefish Device ID range.
#define CONNECTX3_LIVEFISH_ID 502
// PCI Device IDs.
#define CONNECTX3_PCI_ID 4099
#define CONNECTX3PRO_PCI_ID 4103
#define CONNECTIB_PCI_ID 4113
#define CONNECTX4_PCI_ID 4115
#define CONNECTX4LX_PCI_ID 4117
#define CONNECTX5_PCI_ID 4119
#define CONNECTX5EX_PCI_ID 4121
#define CONNECTX6_PCI_ID 4123
#define CONNECTX6DX_PCI_ID 4125
#define CONNECTX6LX_PCI_ID 4127
#define CONNECTX7_PCI_ID 4129
#define CONNECTX8_PCI_ID 4131
#define SCHRODINGER_PCI_ID 6518
#define FREYSA_PCI_ID 6521
#define BLUEFIELD_PCI_ID 41682
#define BLUEFIELD2_PCI_ID 41686
#define BLUEFIELD_DPU_AUX_PCI_ID 49873
#define BLUEFIELD3_PCI_ID 41692
#define BLUEFIELD4_PCI_ID 41694
#define SWITCHIB_PCI_ID 52000
#define SWITCHIB2_PCI_ID 53000
#define QUANTUM_PCI_ID 54000
#define QUANTUM2_PCI_ID 54002
#define QUANTUM3_PCI_ID 54004
#define SPECTRUM_PCI_ID 52100
#define SPECTRUM2_PCI_ID 53100
#define SPECTRUM3_PCI_ID 53104
#define SPECTRUM4_PCI_ID 53120
#define BW00_PCI_ID 10496
#define BW02_PCI_ID 10624
enum nnt_device_type_flag
{
NNT_PCICONF_DEVICES = 0x01,
NNT_PCI_DEVICES,
NNT_ALL_DEVICES
};
struct nnt_dma_page
{
struct page** page_list;
dma_addr_t* dma_address_list;
};
enum nnt_device_type
{
NNT_PCICONF,
NNT_PCICONF_NO_VSEC,
NNT_PCI_MEMORY
};
struct supported_address_space
{
int cr_space;
int icmd;
int semaphore;
};
/* Forward declaration */
struct nnt_device;
typedef int (*callback_read)(struct nnt_device* nnt_device, struct nnt_rw_operation* read_operation);
typedef int (*callback_write)(struct nnt_device* nnt_device, struct nnt_rw_operation* write_operation);
typedef int (*callback_init)(struct nnt_device* nnt_device);
struct nnt_access_callback
{
callback_read read;
callback_write write;
callback_init init;
};
struct nnt_device_pciconf
{
struct supported_address_space address_space;
unsigned int address_register;
unsigned int data_register;
unsigned int semaphore_offset;
unsigned int data_offset;
unsigned int address_offset;
unsigned int vendor_specific_capability;
unsigned int vsec_capability_mask;
unsigned int vsec_fully_supported;
};
struct nnt_device_pci
{
unsigned long long bar_address;
unsigned int bar_size;
};
struct nnt_device_memory
{
unsigned int pci_memory_bar_address;
unsigned int connectx_wa_slot_p1;
void* hardware_memory_address;
};
struct nnt_device_dbdf
{
unsigned int domain;
unsigned int bus;
unsigned int devfn;
};
struct nnt_device
{
struct nnt_device_pciconf pciconf_device;
struct nnt_device_memory memory_device;
struct nnt_access_callback access;
struct nnt_device_pci device_pci;
struct pci_dev* pci_device;
struct nnt_dma_page dma_page;
struct list_head entry;
struct cdev mcdev;
struct mutex lock;
struct nnt_device_dbdf dbdf;
enum nnt_device_type device_type;
int vpd_capability_address;
int device_minor_number;
int wo_address;
int buffer_used_bc;
int device_enabled;
char device_name[NNT_NAME_SIZE];
char buffer_bc[MST_BC_BUFFER_SIZE];
unsigned int connectx_wa_slot_p1;
unsigned char connectx_wa_slots;
dev_t device_number;
};
#endif

View File

@@ -0,0 +1,196 @@
#include <linux/kernel.h>
#include <linux/pci.h>
#include "nnt_device_defs.h"
#include "nnt_pci_conf_access_defs.h"
#include "nnt_pci_conf_access.h"
#include "nnt_defs.h"
#include "nnt_ioctl_defs.h"
int dma_mapping_page(unsigned int total_pinned, unsigned int page_mapped_counter,
struct nnt_device* nnt_device, struct page** current_pages,
struct nnt_page_info* page_info)
{
int current_page = total_pinned + page_mapped_counter;
int error_code = 0;
/* Get the dma address. */
nnt_device->dma_page.dma_address_list[current_page] =
dma_map_page(&nnt_device->pci_device->dev, current_pages[current_page],
0, PAGE_SIZE,
DMA_BIDIRECTIONAL);
/* Do we get a valid dma address ? */
if (dma_mapping_error(&nnt_device->pci_device->dev, nnt_device->dma_page.dma_address_list[current_page])) {
printk(KERN_ERR "Failed to get DMA addresses\n");
error_code = -EINVAL;
goto ReturnOnFinsihed;
}
page_info->page_address_array[current_page].dma_address =
nnt_device->dma_page.dma_address_list[current_page];
ReturnOnFinsihed:
return error_code;
}
int pin_user_pages_in_kernel_space(unsigned int total_pages, unsigned int total_pinned,
int* pinned_pages, struct nnt_device* nnt_device,
struct nnt_page_info* page_info, struct page*** current_pages)
{
unsigned long page_pointer_start = page_info->page_pointer_start;
int error_code = 0;
/* Remaining pages to pin. */
int remaining_pages = total_pages - total_pinned;
/* Point to the current offset. */
uint64_t current_page_pointer = page_pointer_start + (total_pinned * PAGE_SIZE);
/* Save the current page. */
*current_pages = nnt_device->dma_page.page_list + total_pinned;
/* Returns number of pages pinned - this may be fewer than the number requested
or -errno in case of error. */
*pinned_pages = get_user_pages_fast(current_page_pointer, remaining_pages,
FOLL_WRITE, *current_pages);
if (*pinned_pages < 1) {
kfree(nnt_device->dma_page.page_list);
error_code = -EFAULT;
}
/* Allocate dma addresses structure. */
if ((nnt_device->dma_page.dma_address_list =
kcalloc(total_pages, sizeof(dma_addr_t), GFP_KERNEL)) == NULL) {
error_code = -ENOMEM;
}
return error_code;
}
int pin_user_memory_in_kernel_space(unsigned int total_pages, struct nnt_device* nnt_device,
struct nnt_page_info* page_info)
{
unsigned int page_mapped_counter= 0;
unsigned int total_pinned = 0;
unsigned int pinned_pages;
struct page** current_pages = NULL;
int error_code = 0;
while (total_pinned < total_pages) {
/* Pinning user pages in kernel space. */
if((error_code =
pin_user_pages_in_kernel_space(total_pages, total_pinned,
&pinned_pages, nnt_device,
page_info, &current_pages)) != 0)
goto ReturnOnFinished;
/* When the parameter 'inter_iommu' is on, we need to set up
a mapping on a pages in order to access the physical address. */
while(page_mapped_counter < pinned_pages)
{
if((error_code =
dma_mapping_page(total_pinned, page_mapped_counter,
nnt_device, current_pages,
page_info)) != 0)
goto ReturnOnFinished;
page_mapped_counter++;
}
/* Advance the memory that already pinned. */
total_pinned += pinned_pages;
}
/* There is a page not pinned in the kernel space ? */
if (total_pinned != total_pages) {
return -EFAULT;
}
ReturnOnFinished:
return error_code;
}
int map_dma_pages(struct nnt_page_info* page_info, struct nnt_device* nnt_device)
{
unsigned int total_pages = page_info->total_pages;
int page_counter = 0;
int error_code = 0;
/* Check if we allow locking memory. */
if (!can_do_mlock()) {
error_code = -EPERM;
goto ReturnOnFinished;
}
/* Allocate the page list. */
if ((nnt_device->dma_page.page_list =
kcalloc(total_pages, sizeof(struct page *), GFP_KERNEL)) == NULL) {
error_code = -ENOMEM;
goto ReturnOnFinished;
}
/* Go over the user memory buffer and pin user pages in kernel space */
if((error_code =
pin_user_memory_in_kernel_space(total_pages, nnt_device,
page_info)) != 0)
goto ReturnOnFinished;
for (page_counter = 0;
page_counter < total_pages;
page_counter++) {
printk(KERN_INFO "Page address structure number: %d, device: %04x:%02x:%02x.%0x\n",
page_counter, pci_domain_nr(nnt_device->pci_device->bus),
nnt_device->pci_device->bus->number, PCI_SLOT(nnt_device->pci_device->devfn),
PCI_FUNC(nnt_device->pci_device->devfn));
}
ReturnOnFinished:
return error_code;
}
int release_dma_pages(struct nnt_page_info* page_info, struct nnt_device* nnt_device)
{
int page_counter;
if (nnt_device->dma_page.page_list == NULL) {
return 0;
}
/* Deallocate the pages. */
for (page_counter = 0;
page_counter < page_info->total_pages;
page_counter++) {
/* DMA activity is finished. */
dma_unmap_page(&nnt_device->pci_device->dev, nnt_device->dma_page.dma_address_list[page_counter],
PAGE_SIZE, DMA_BIDIRECTIONAL);
/* Release the page list. */
set_page_dirty(nnt_device->dma_page.page_list[page_counter]);
put_page(nnt_device->dma_page.page_list[page_counter]);
nnt_device->dma_page.page_list[page_counter] = NULL;
nnt_device->dma_page.dma_address_list[page_counter] = 0;
printk(KERN_INFO "Page structure number: %d was released. device:%04x:%02x:%02x.%0x\n",
page_counter, pci_domain_nr(nnt_device->pci_device->bus),
nnt_device->pci_device->bus->number, PCI_SLOT(nnt_device->pci_device->devfn),
PCI_FUNC(nnt_device->pci_device->devfn));
}
// All the pages are clean.
nnt_device->dma_page.page_list = NULL;
return 0;
}

View File

@@ -0,0 +1,10 @@
#ifndef NNT_DMA_H
#define NNT_DMA_H
#include "nnt_ioctl_defs.h"
#include "nnt_device_defs.h"
int map_dma_pages(struct nnt_page_info* page_info, struct nnt_device* nnt_device);
int release_dma_pages(struct nnt_page_info* page_info, struct nnt_device* nnt_device);
#endif

View File

@@ -0,0 +1,230 @@
#include <linux/kernel.h>
#include <linux/pci.h>
#include <linux/uaccess.h>
#include <linux/highmem.h>
#include <linux/sched.h>
#include "nnt_dma.h"
#include "nnt_defs.h"
#include "nnt_pci_conf_access.h"
unsigned int mask = 0x7fff;
unsigned int msb_mask = 0x8000;
int dma_pages_ioctl(unsigned int command, void* user_buffer,
struct nnt_device* nnt_device)
{
struct nnt_page_info page_info;
int error_code = 0;
/* Copy the page info structure from user space. */
if (copy_from_user(&page_info, user_buffer,
sizeof(struct nnt_page_info))) {
error_code = -EFAULT;
goto ReturnOnFinished;
}
if (command == NNT_GET_DMA_PAGES) {
if (map_dma_pages(&page_info, nnt_device)) {
goto ReturnOnFinished;
}
/* Return the physical address to the user */
if (copy_to_user(user_buffer, &page_info,
sizeof(struct nnt_page_info)) != 0) {
error_code = -EFAULT;
goto ReturnOnFinished;
}
} else {
error_code =
release_dma_pages(&page_info, nnt_device);
}
ReturnOnFinished:
return error_code;
}
int read_dword_ioctl(void* user_buffer, struct nnt_device* nnt_device)
{
struct nnt_read_dword_from_config_space read_from_cspace;
int error_code = 0;
/* Copy the request from user space. */
if (copy_from_user(&read_from_cspace, user_buffer,
sizeof(struct nnt_read_dword_from_config_space)) != 0) {
error_code = -EFAULT;
goto ReturnOnFinished;
}
/* Read the dword. */
if (read_dword(&read_from_cspace, nnt_device)) {
goto ReturnOnFinished;
}
/* Copy the data to the user space. */
if (copy_to_user(user_buffer, &read_from_cspace,
sizeof(struct nnt_read_dword_from_config_space)) != 0) {
error_code = -EFAULT;
goto ReturnOnFinished;
}
ReturnOnFinished:
return error_code;
}
int get_nnt_device_parameters(struct nnt_device_parameters* nnt_parameters, struct nnt_device* nnt_device)
{
int error = 0;
nnt_parameters->domain = pci_domain_nr(nnt_device->pci_device->bus);
nnt_parameters->bus = nnt_device->pci_device->bus->number;
nnt_parameters->slot = PCI_SLOT(nnt_device->pci_device->devfn);
nnt_parameters->function = PCI_FUNC(nnt_device->pci_device->devfn);
nnt_parameters->pci_memory_bar_address= nnt_device->memory_device.pci_memory_bar_address;
nnt_parameters->device = nnt_device->pci_device->device;
nnt_parameters->vendor = nnt_device->pci_device->vendor;
nnt_parameters->subsystem_device = nnt_device->pci_device->subsystem_device;
nnt_parameters->multifunction = nnt_device->pci_device->multifunction;
nnt_parameters->subsystem_vendor = nnt_device->pci_device->subsystem_vendor;
check_address_space_support(nnt_device);
if (nnt_device->pciconf_device.vendor_specific_capability &&
(nnt_device->pciconf_device.address_space.icmd || nnt_device->pciconf_device.address_space.cr_space ||
nnt_device->pciconf_device.address_space.semaphore)) {
nnt_parameters->vendor_specific_capability = nnt_device->pciconf_device.vendor_specific_capability;
nnt_parameters->vsec_capability_mask = nnt_device->pciconf_device.vsec_capability_mask;
} else {
nnt_parameters->vendor_specific_capability = 0;
}
return error;
}
int pci_connectx_wa(struct nnt_connectx_wa* connectx_wa, struct nnt_device* nnt_device)
{
unsigned int slot_mask;
int error = 0;
/* Is this slot exists ? */
if (nnt_device->memory_device.connectx_wa_slot_p1) {
printk(KERN_DEBUG "Slot exits for file %s, slot:0x%x\n",
nnt_device->device_name, nnt_device->memory_device.connectx_wa_slot_p1);
error = 0;
goto ReturnOnFinished;
}
/* Find first un(set) bit. and remember the slot */
nnt_device->memory_device.connectx_wa_slot_p1= ffs(~nnt_device->memory_device.connectx_wa_slot_p1);
if (nnt_device->memory_device.connectx_wa_slot_p1 == 0 ||
nnt_device->memory_device.connectx_wa_slot_p1 > NNT_CONNECTX_WA_SIZE) {
error = -ENOLCK;
goto ReturnOnFinished;
}
slot_mask = 1 << (nnt_device->memory_device.connectx_wa_slot_p1 - 1);
/* set the slot as taken */
nnt_device->memory_device.connectx_wa_slot_p1 |= slot_mask;
connectx_wa->connectx_wa_slot_p1 = nnt_device->memory_device.connectx_wa_slot_p1;
ReturnOnFinished:
return error;
}
int vpd_read(struct nnt_vpd* vpd, struct nnt_device* nnt_device)
{
unsigned long jiffies_time;
unsigned int address;
unsigned short data;
int is_bit_set = 0;
int error;
/* Sets F bit to zero and write VPD address. */
address = mask & vpd->offset;
error = pci_write_config_word(nnt_device->pci_device, nnt_device->vpd_capability_address + PCI_VPD_ADDR,
address);
CHECK_PCI_WRITE_ERROR(error, nnt_device->vpd_capability_address + PCI_VPD_ADDR,
address);
/* Wait for data until F bit is set with one */
jiffies_time = msecs_to_jiffies(vpd->timeout) + jiffies;
while (time_before(jiffies, jiffies_time)) {
error = pci_read_config_word(nnt_device->pci_device, nnt_device->vpd_capability_address + PCI_VPD_ADDR,
&data);
CHECK_PCI_READ_ERROR(error, nnt_device->vpd_capability_address + PCI_VPD_ADDR);
if (data & msb_mask) {
is_bit_set = 1;
break;
}
cond_resched();
}
if (!is_bit_set) {
printk(KERN_ERR "Failed to retrieve valid data\n");
return -ETIMEDOUT;
}
/* read data */
error = pci_read_config_dword(nnt_device->pci_device, nnt_device->vpd_capability_address + PCI_VPD_DATA,
&vpd->data);
CHECK_PCI_READ_ERROR(error, nnt_device->vpd_capability_address + PCI_VPD_DATA);
ReturnOnFinished:
return error;
}
int vpd_write(struct nnt_vpd* vpd, struct nnt_device* nnt_device)
{
unsigned long jiffies_time;
unsigned int address;
unsigned short data;
int is_bit_set = 0;
int error;
/* Write the user data */
error = pci_write_config_dword(nnt_device->pci_device, nnt_device->vpd_capability_address + PCI_VPD_DATA,
vpd->data);
CHECK_PCI_WRITE_ERROR(error, nnt_device->vpd_capability_address + PCI_VPD_DATA,
vpd->data);
/* sets F bit to one and write VPD addr */
address = msb_mask | (mask & vpd->offset);
error = pci_write_config_word(nnt_device->pci_device, nnt_device->vpd_capability_address + PCI_VPD_ADDR,
address);
CHECK_PCI_WRITE_ERROR(error, nnt_device->vpd_capability_address + PCI_VPD_ADDR,
address);
/* wait for data until F bit is set with zero */
jiffies_time = msecs_to_jiffies(vpd->timeout) + jiffies;
while (time_before(jiffies, jiffies_time)) {
error = pci_read_config_word(nnt_device->pci_device, nnt_device->vpd_capability_address + PCI_VPD_ADDR,
&data);
CHECK_PCI_READ_ERROR(error, nnt_device->vpd_capability_address + PCI_VPD_ADDR);
if (!(data & msb_mask)) {
is_bit_set = 1;
break;
}
cond_resched();
}
if (!is_bit_set) {
printk(KERN_ERR "Failed to retrieve valid data\n");
return -ETIMEDOUT;
}
ReturnOnFinished:
return error;
}

View File

@@ -0,0 +1,14 @@
#ifndef NNT_IOCTL_H
#define NNT_IOCTL_H
#include "nnt_device_defs.h"
int dma_pages_ioctl(unsigned int command, void* user_buffer,
struct nnt_device* nnt_device);
int read_dword_ioctl(unsigned int command, void* user_buffer,
struct nnt_device* nnt_device);
int get_nnt_device_parameters(struct nnt_device_parameters* nnt_parameters, struct nnt_device* nnt_device);
int pci_connectx_wa(struct nnt_connectx_wa* connectx_wa, struct nnt_device* nnt_device);
int vpd_read(struct nnt_vpd* vpd, struct nnt_device* nnt_device);
int vpd_write(struct nnt_vpd* vpd, struct nnt_device* nnt_device);
#endif

View File

@@ -0,0 +1,83 @@
#ifndef NNT_IOCTL_DEFS_H
#define NNT_IOCTL_DEFS_H
#define NNT_MAGIC_NUMBER 0xD3
#define MAX_BUFFER_BLOCK_SIZE 256
#define NNT_MAX_PAGES_SIZE 8
#define NNT_CONNECTX_WA_SIZE 3
#define NNT_WRITE _IOW (NNT_MAGIC_NUMBER, 1, struct nnt_rw_operation)
#define NNT_READ _IOW (NNT_MAGIC_NUMBER, 2, struct nnt_rw_operation)
#define NNT_GET_DMA_PAGES _IOR (NNT_MAGIC_NUMBER, 3, struct nnt_page_info)
#define NNT_RELEASE_DMA_PAGES _IOR (NNT_MAGIC_NUMBER, 4, struct nnt_page_info)
#define NNT_READ_DWORD_FROM_CONFIG_SPACE _IOR (NNT_MAGIC_NUMBER, 5, struct nnt_read_dword_from_config_space)
#define NNT_GET_DEVICE_PARAMETERS _IOR (NNT_MAGIC_NUMBER, 6, struct nnt_device_parameters)
#define NNT_INIT _IOR (NNT_MAGIC_NUMBER, 7, struct nnt_pciconf_init)
#define NNT_PCI_CONNECTX_WA _IOR (NNT_MAGIC_NUMBER, 8, u_int32_t)
#define NNT_VPD_READ _IOR (NNT_MAGIC_NUMBER, 9, struct nnt_vpd)
#define NNT_VPD_WRITE _IOW (NNT_MAGIC_NUMBER, 10, struct nnt_vpd)
struct nnt_vpd {
unsigned int offset;
unsigned int timeout;
unsigned int data;
};
struct nnt_pciconf_init {
unsigned int address_register;
unsigned int address_data_register;
};
struct nnt_device_parameters {
unsigned int domain;
unsigned int bus;
unsigned int slot;
unsigned int function;
unsigned int pci_memory_bar_address;
unsigned int device;
unsigned int vendor;
unsigned int subsystem_device;
unsigned int subsystem_vendor;
unsigned int multifunction;
unsigned int vendor_specific_capability;
unsigned int vsec_capability_mask;
};
struct nnt_page_address {
u_int64_t dma_address;
u_int64_t virtual_address;
};
struct nnt_page_info {
unsigned int total_pages;
unsigned long page_pointer_start;
struct nnt_page_address page_address_array[NNT_MAX_PAGES_SIZE];
};
struct nnt_read_dword_from_config_space {
unsigned int offset;
unsigned int data;
};
struct nnt_rw_operation {
unsigned int address_space;
unsigned int offset;
int size;
unsigned int data[MAX_BUFFER_BLOCK_SIZE / 4];
};
struct nnt_connectx_wa {
unsigned int connectx_wa_slot_p1;
};
#endif

View File

@@ -0,0 +1,295 @@
/*
* Copyright (c) 2022 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
* BSD-3-Clause
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/semaphore.h>
#include <linux/uaccess.h>
#include <linux/kernel.h>
#include "nnt_defs.h"
#include "nnt_device.h"
#include "nnt_ioctl.h"
#include "nnt_ioctl_defs.h"
MODULE_AUTHOR("Itay Avraham <itayavr@nvidia.com>");
MODULE_DESCRIPTION("NNT Linux driver (NVIDIA® networking tools driver)");
/* Passing MFT flag argument */
int mft_package = 0;
/* Create the file in sysfs. */
module_param(mft_package, int, S_IRUSR);
struct driver_info nnt_driver_info;
static long nnt_ioctl(struct file* file, unsigned int command, unsigned long argument)
{
void* user_buffer = (void*)argument;
struct nnt_device* nnt_device = NULL;
int error;
/* By convention, any user gets read access
* and is allowed to use the device.
* Commands with no direction are administration
* commands, and you need write permission for this */
if (_IOC_DIR(command) == _IOC_NONE)
{
if (!(file->f_mode & FMODE_WRITE))
{
return -EPERM;
}
}
else
{
if (!(file->f_mode & FMODE_READ))
{
return -EPERM;
}
}
error = mutex_lock_nnt(file);
CHECK_ERROR(error);
/* Get the nnt device structure */
error = get_nnt_device(file, &nnt_device);
if (error)
{
goto ReturnOnFinished;
}
switch (command)
{
case NNT_GET_DMA_PAGES:
case NNT_RELEASE_DMA_PAGES:
error = dma_pages_ioctl(command, user_buffer, nnt_device);
break;
case NNT_READ_DWORD_FROM_CONFIG_SPACE:
error = read_dword_ioctl(command, user_buffer, nnt_device);
break;
case NNT_WRITE:
case NNT_READ:
{
struct nnt_rw_operation rw_operation;
/* Copy the request from user space. */
if (copy_from_user(&rw_operation, user_buffer, sizeof(struct nnt_rw_operation)) != 0)
{
error = -EFAULT;
goto ReturnOnFinished;
}
switch (command)
{
case NNT_WRITE:
nnt_device->access.write(nnt_device, &rw_operation);
break;
case NNT_READ:
nnt_device->access.read(nnt_device, &rw_operation);
break;
}
/* Copy the data to the user space. */
if (copy_to_user(user_buffer, &rw_operation, sizeof(struct nnt_rw_operation)) != 0)
{
error = -EFAULT;
goto ReturnOnFinished;
}
break;
}
case NNT_GET_DEVICE_PARAMETERS:
{
struct nnt_device_parameters nnt_parameters;
error = get_nnt_device_parameters(&nnt_parameters, nnt_device);
/* Copy the data to the user space. */
if (copy_to_user(user_buffer, &nn_parameters, sizeof(struct device_parameters)) != 0)
{
error = -EFAULT;
goto ReturnOnFinished;
}
break;
}
case NNT_INIT:
{
struct nnt_pciconf_init init;
/* Copy the request from user space. */
if (copy_from_user(&init, user_buffer, sizeof(struct nnt_pciconf_init)) != 0)
{
error = -EFAULT;
goto ReturnOnFinished;
}
error = nnt_device->access.init(&init, nnt_device);
break;
}
case NNT_PCI_CONNECTX_WA:
{
struct nnt_connectx_wa connectx_wa;
error = pci_connectx_wa(&connectx_wa, nnt_device);
/* Copy the data to the user space. */
if (copy_to_user(user_buffer, &connectx_wa, sizeof(struct nnt_connectx_wa)) != 0)
{
error = -EFAULT;
goto ReturnOnFinished;
}
break;
}
case NNT_VPD_READ:
case NNT_VPD_WRITE:
{
int vpd_default_timeout = 2000;
struct nnt_vpd vpd;
if (!nnt_device->vpd_capability_address)
{
printk(KERN_ERR "Device %s not support Vital Product Data\n", nnt_device->device_name);
return -ENODEV;
}
/* Copy the request from user space. */
if (copy_from_user(&vpd, user_buffer, sizeof(struct nnt_vpd)) != 0)
{
error = -EFAULT;
goto ReturnOnFinished;
}
if (!vpd.timeout)
{
vpd.timeout = vpd_default_timeout;
}
switch (command)
{
case NNT_VPD_READ:
error = vpd_read(command, &vpd, nnt_device);
break;
case NNT_VPD_WRITE:
error = vpd_write(command, &vpd, nnt_device);
break;
}
break;
}
default:
printk(KERN_ERR "ioctl not implemented, command id: %x\n", command);
error = -EINVAL;
break;
}
ReturnOnFinished:
mutex_unlock_nnt(file);
return error;
}
static int nnt_open(struct inode* inode, struct file* file)
{
if (file->private_data)
{
return 0;
}
return set_private_data(inode, file);
}
struct file_operations fop = {.unlocked_ioctl = ioctl, .open = nnt_open, .owner = THIS_MODULE};
int with_unknown = 0;
module_param(with_unknown, int, S_IRUSR | S_IWUSR);
static int __init nnt_init_module(void)
{
int first_minor_number = 0;
int error = 0;
dev_t device_numbers;
/* Get the amount of the Nvidia devices. */
if ((nnt_driver_info.contiguous_device_numbers = get_amount_of_nvidia_devices()) == 0)
{
printk(KERN_ERR "No devices found\n");
goto ReturnOnFinished;
}
/* Allocate char driver region and assign major number */
if ((error = alloc_chrdev_region(&device_numbers, first_minor_number, nnt_driver_info.contiguous_device_numbers,
NNT_DRIVER_NAME)) != 0)
{
printk(KERN_ERR "failed to allocate chrdev_region\n");
goto CharDeviceAllocated;
}
nnt_driver_info.driver_major_number = MAJOR(device_numbers);
/* create sysfs class. */
if ((nnt_driver_info.class_driver = class_create(THIS_MODULE, NNT_CLASS_NAME)) == NULL)
{
printk(KERN_ERR "Class creation failed\n");
error = -EFAULT;
goto DriverClassAllocated;
}
/* Create device files for MSTflint and MFT */
error = create_nnt_devices(nnt_driver_info.contiguous_device_numbers, device_numbers, mft_package,
NNT_MELLANOX_PCI_VENDOR, &fop) ||
create_nnt_devices(nnt_driver_info.contiguous_device_numbers, device_numbers, mft_package,
NNT_NVIDIA_PCI_VENDOR, &fop);
if ((error) == 0)
{
goto ReturnOnFinished;
}
DriverClassAllocated:
destroy_nnt_devices();
class_destroy(nnt_driver_info.class_driver);
CharDeviceAllocated:
unregister_chrdev_region(nnt_driver_info.driver_major_number, nnt_driver_info.contiguous_device_numbers);
ReturnOnFinished:
return error;
}
static void __exit nnt_cleanup_module(void)
{
destroy_nnt_devices();
class_destroy(nnt_driver_info.class_driver);
unregister_chrdev_region(nnt_driver_info.driver_major_number, nnt_driver_info.contiguous_device_numbers);
}
module_init(nnt_init_module);
module_exit(nnt_cleanup_module);

View File

@@ -0,0 +1,46 @@
#include "nnt_device_defs.h"
#include "nnt_pci_conf_access_defs.h"
#include "nnt_pci_conf_access.h"
#include "nnt_defs.h"
int write_memory(struct nnt_device* nnt_device, struct nnt_rw_operation* write_operation)
{
/* Endianness conversion. */
cpu_to_be32s(write_operation->data);
write_operation->data[0] = cpu_to_le32(write_operation->data[0]);
/* Write to the hardware memory address. */
iowrite32(write_operation->data[0], nnt_device->memory_device.hardware_memory_address + write_operation->offset);
return 0;
}
int read_memory(struct nnt_device* nnt_device, struct nnt_rw_operation* read_operation)
{
/* Read from the hardware memory address. */
read_operation->data[0] = ioread32(nnt_device->memory_device.hardware_memory_address + read_operation->offset);
/* Endianness conversion */
be32_to_cpus(read_operation->data);
read_operation->data[0] = cpu_to_le32(read_operation->data[0]);
return 0;
}
int init_memory(struct nnt_device* nnt_device)
{
nnt_device->memory_device.connectx_wa_slot_p1 = 0;
nnt_device->memory_device.hardware_memory_address =
ioremap(pci_resource_start(nnt_device->pci_device, nnt_device->memory_device.pci_memory_bar_address),
NNT_MEMORY_SIZE);
if (nnt_device->memory_device.hardware_memory_address <= 0) {
printk(KERN_ERR "could not map device memory\n");
}
return 0;
}

View File

@@ -0,0 +1,7 @@
#ifndef NNT_MEMORY_ACCESS_H
#define NNT_MEMORY_ACCESS_H
int read_memory(struct nnt_device* nnt_device, struct nnt_rw_operation* read_operation);
int write_memory(struct nnt_device* nnt_device, struct nnt_rw_operation* write_operation);
int init_memory(struct nnt_device* nnt_device);
#endif

View File

@@ -0,0 +1,430 @@
#include "nnt_device_defs.h"
#include "nnt_pci_conf_access_defs.h"
#include "nnt_pci_conf_access.h"
#include "nnt_defs.h"
#include <linux/delay.h>
#include <linux/uaccess.h>
int clear_vsec_semaphore(struct nnt_device* nnt_device)
{
/* Clear the semaphore. */
int error = pci_write_config_dword(nnt_device->pci_device, nnt_device->pciconf_device.semaphore_offset,
0);
CHECK_PCI_WRITE_ERROR(error, nnt_device->pciconf_device.semaphore_offset,
0)
ReturnOnFinished:
return error;
}
int get_semaphore_ticket(struct nnt_device* nnt_device, unsigned int* lock_value,
unsigned int* counter)
{
unsigned int counter_offset = nnt_device->pciconf_device.vendor_specific_capability + PCI_COUNTER_OFFSET;
int error = 0;
/* Read ticket. */
error = pci_read_config_dword(nnt_device->pci_device, counter_offset,
counter);
CHECK_PCI_READ_ERROR(error, counter_offset);
/* Write to semaphore ticket. */
error = pci_write_config_dword(nnt_device->pci_device, nnt_device->pciconf_device.semaphore_offset,
*counter);
CHECK_PCI_WRITE_ERROR(error, nnt_device->pciconf_device.semaphore_offset,
*counter);
/* Read back semaphore to make sure the
* ticket is equal to semphore */
error = pci_read_config_dword(nnt_device->pci_device, nnt_device->pciconf_device.semaphore_offset,
lock_value);
CHECK_PCI_READ_ERROR(error, nnt_device->pciconf_device.semaphore_offset);
ReturnOnFinished:
return error;
}
int lock_vsec_semaphore(struct nnt_device* nnt_device)
{
unsigned int lock_value = -1;
unsigned int counter = 0;
unsigned int retries = 0;
int error = 0;
do {
if (retries > SEMAPHORE_MAX_RETRIES) {
return -EINVAL;
}
/* Read the semaphore, until we will get 0. */
error = pci_read_config_dword(nnt_device->pci_device, nnt_device->pciconf_device.semaphore_offset,
&lock_value);
CHECK_PCI_READ_ERROR(error, nnt_device->pciconf_device.semaphore_offset);
/* Is semaphore taken ? */
if (lock_value) {
retries++;
udelay(1000);
continue;
}
/* Get semaphore ticket */
error = get_semaphore_ticket(nnt_device, &lock_value,
&counter);
CHECK_ERROR(error);
} while (counter != lock_value);
ReturnOnFinished:
return error;
}
int read_dword(struct nnt_read_dword_from_config_space* read_from_cspace, struct nnt_device* nnt_device)
{
int error = 0;
/* Take semaphore. */
error = lock_vsec_semaphore(nnt_device);
CHECK_ERROR(error);
/* Read dword from config space. */
error = pci_read_config_dword(nnt_device->pci_device, read_from_cspace->offset,
&read_from_cspace->data);
CHECK_PCI_READ_ERROR(error, read_from_cspace->offset);
ReturnOnFinished:
/* Clear semaphore. */
clear_vsec_semaphore(nnt_device);
return error;
}
int wait_on_flag(struct nnt_device* nnt_device, u8 expected_val)
{
unsigned int flag = 0;
int retries = 0;
int error = -1;
for (retries = 0; retries < IFC_MAX_RETRIES; retries++) {
/* Read the flag. */
error = pci_read_config_dword(nnt_device->pci_device, nnt_device->pciconf_device.address_offset,
&flag);
CHECK_PCI_READ_ERROR(error, nnt_device->pciconf_device.address_offset);
flag = EXTRACT(flag, PCI_FLAG_BIT_OFFSET,
1);
if (flag == expected_val) {
return 0;
}
}
ReturnOnFinished:
return error;
}
int set_address_space(struct nnt_device* nnt_device, unsigned int address_space)
{
unsigned int control_offset = nnt_device->pciconf_device.vendor_specific_capability + PCI_CONTROL_OFFSET;
unsigned int value = 0;
int error = 0;
/* Read value from control offset. */
error = pci_read_config_dword(nnt_device->pci_device, control_offset,
&value);
CHECK_PCI_READ_ERROR(error, control_offset);
/* Set the bit address_space indication and write it back. */
value = MERGE(value, address_space,
PCI_SPACE_BIT_OFFSET, PCI_SPACE_BIT_LENGTH);
error = pci_write_config_dword(nnt_device->pci_device, control_offset,
value);
CHECK_PCI_WRITE_ERROR(error, control_offset,
value);
/* Read status and make sure address_space is supported. */
error = pci_read_config_dword(nnt_device->pci_device, control_offset,
&value);
CHECK_PCI_READ_ERROR(error, control_offset);
if (EXTRACT(value, PCI_STATUS_BIT_OFFSET,
PCI_STATUS_BIT_LEN) == 0) {
error = -EINVAL;
}
ReturnOnFinished:
return error;
}
int check_address_space_support(struct nnt_device* nnt_device)
{
int error = 0;
if ((!nnt_device->pciconf_device.vendor_specific_capability) || (!nnt_device->pci_device)) {
return 0;
}
/* Get semaphore ticket */
error = lock_vsec_semaphore(nnt_device);
CHECK_ERROR(error);
/* Is ICMD address space supported ?*/
if(set_address_space(nnt_device, ADDRESS_SPACE_ICMD) == 0) {
nnt_device->pciconf_device.address_space.icmd = 1;
}
/* Is CR Space address space supported ?*/
if(set_address_space(nnt_device, ADDRESS_SPACE_CR_SPACE) == 0) {
nnt_device->pciconf_device.address_space.cr_space = 1;
}
/* Is semaphore address space supported ?*/
if(set_address_space(nnt_device, ADDRESS_SPACE_SEMAPHORE) == 0) {
nnt_device->pciconf_device.address_space.semaphore = 1;
}
ReturnOnFinished:
/* Clear semaphore. */
clear_vsec_semaphore(nnt_device);
return 0;
}
int set_rw_address(unsigned int* offset, unsigned int rw)
{
u32 address = *offset;
/* Last 2 bits must be zero as we only allow 30 bits addresses. */
if (EXTRACT(address, 30,
2)) {
return -1;
}
address = MERGE(address, rw,
PCI_FLAG_BIT_OFFSET, 1);
*offset = address;
return 0;
}
int read(struct nnt_device* nnt_device, unsigned int offset,
unsigned int* data)
{
int error = set_rw_address(&offset, READ_OPERATION);
CHECK_ERROR(error);
/* Write address. */
error = pci_write_config_dword(nnt_device->pci_device, nnt_device->pciconf_device.address_offset,
offset);
CHECK_PCI_WRITE_ERROR(error, nnt_device->pciconf_device.address_offset,
offset);
error = wait_on_flag(nnt_device, 1);
CHECK_ERROR(error);
/* Read data. */
error = pci_read_config_dword(nnt_device->pci_device, nnt_device->pciconf_device.data_offset,
data);
CHECK_PCI_READ_ERROR(error, nnt_device->pciconf_device.data_offset);
ReturnOnFinished:
return error;
}
int read_pciconf(struct nnt_device* nnt_device, struct nnt_rw_operation* read_operation)
{
int counter = 0;
int error = 0;
/* Lock semaphore. */
error = lock_vsec_semaphore(nnt_device);
CHECK_ERROR(error);
/* Is CR Space address space supported ?*/
error = set_address_space(nnt_device, read_operation->address_space);
CHECK_ERROR(error);
for (counter = 0; counter < read_operation->size; counter += 4) {
if (read(nnt_device, read_operation->offset + counter,
&read_operation->data[counter >> 2])) {
error = counter;
goto ReturnOnFinished;
}
}
ReturnOnFinished:
/* Clear semaphore. */
clear_vsec_semaphore(nnt_device);
return error;
}
int write(struct nnt_device* nnt_device, unsigned int offset,
unsigned int data)
{
int error = set_rw_address(&offset, WRITE_OPERATION);
CHECK_ERROR(error);
/* Write data. */
error = pci_write_config_dword(nnt_device->pci_device, nnt_device->pciconf_device.data_offset,
data);
CHECK_PCI_WRITE_ERROR(error,nnt_device->pciconf_device.data_offset,
data);
/* Write address. */
error = pci_write_config_dword(nnt_device->pci_device, nnt_device->pciconf_device.address_offset,
offset);
CHECK_PCI_WRITE_ERROR(error, nnt_device->pciconf_device.address_offset,
offset);
error = wait_on_flag(nnt_device, 0);
ReturnOnFinished:
return error;
}
int write_pciconf(struct nnt_device* nnt_device, struct nnt_rw_operation* write_operation)
{
int counter = 0;
int error = 0;
/* Lock semaphore. */
error = lock_vsec_semaphore(nnt_device);
CHECK_ERROR(error);
/* Is CR Space address space supported ?*/
error = set_address_space(nnt_device, write_operation->address_space);
CHECK_ERROR(error);
for (counter = 0; counter < write_operation->size; counter += 4) {
if (write(nnt_device, write_operation->offset + counter,
write_operation->data[counter >> 2])) {
error = counter;
goto ReturnOnFinished;
}
}
ReturnOnFinished:
/* Clear semaphore. */
clear_vsec_semaphore(nnt_device);
return error;
}
int address_space_to_capability(u_int16_t address_space)
{
switch (address_space) {
case NNT_SPACE_ICMD:
return NNT_VSEC_ICMD_SPACE_SUPPORTED;
case NNT_SPACE_CR_SPACE:
return NNT_VSEC_CRSPACE_SPACE_SUPPORTED;
case NNT_SPACE_ALL_ICMD:
return NNT_VSEC_ALL_ICMD_SPACE_SUPPORTED;
case NNT_SPACE_NODNIC_INIT_SEG:
return NNT_VSEC_NODNIC_INIT_SEG_SPACE_SUPPORTED;
case NNT_SPACE_EXPANSION_ROM:
return NNT_VSEC_EXPANSION_ROM_SPACE_SUPPORTED;
case NNT_SPACE_ND_CR_SPACE:
return NNT_VSEC_ND_CRSPACE_SPACE_SUPPORTED;
case NNT_SPACE_SCAN_CR_SPACE:
return NNT_VSEC_SCAN_CRSPACE_SPACE_SUPPORTED;
case NNT_SPACE_GLOBAL_SEMAPHORE:
return NNT_VSEC_GLOBAL_SEMAPHORE_SPACE_SUPPORTED;
case NNT_SPACE_MAC:
return NNT_VSEC_MAC_SPACE_SUPPORTED;
default:
return 0;
}
}
int get_space_support_status(struct nnt_device* nnt_device, u_int16_t address_space)
{
int status = 0;
if(set_address_space(nnt_device, address_space) == 0) {
status = 1;
}
nnt_device->pciconf_device.vsec_capability_mask |=
(status << address_space_to_capability(address_space));
return status;
}
int init_vsec_capability_mask(struct nnt_device* nnt_device)
{
int error = 0;
/* Lock semaphore. */
error = lock_vsec_semaphore(nnt_device);
CHECK_ERROR(error);
get_space_support_status(nnt_device, NNT_SPACE_ICMD);
get_space_support_status(nnt_device, NNT_SPACE_CR_SPACE);
get_space_support_status(nnt_device, NNT_SPACE_ALL_ICMD);
get_space_support_status(nnt_device, NNT_SPACE_NODNIC_INIT_SEG);
get_space_support_status(nnt_device, NNT_SPACE_EXPANSION_ROM);
get_space_support_status(nnt_device, NNT_SPACE_ND_CR_SPACE);
get_space_support_status(nnt_device, NNT_SPACE_SCAN_CR_SPACE);
get_space_support_status(nnt_device, NNT_SPACE_GLOBAL_SEMAPHORE);
get_space_support_status(nnt_device, NNT_SPACE_MAC);
nnt_device->pciconf_device.vsec_capability_mask |= (1 << NNT_VSEC_INITIALIZED);
ReturnOnFinished:
/* Clear semaphore. */
clear_vsec_semaphore(nnt_device);
return 0;
}
void check_vsec_minimum_support(struct nnt_device* nnt_device)
{
if ((nnt_device->pciconf_device.vsec_capability_mask & (1 << NNT_VSEC_INITIALIZED)) &&
(nnt_device->pciconf_device.vsec_capability_mask & (1 << NNT_VSEC_ICMD_SPACE_SUPPORTED)) &&
(nnt_device->pciconf_device.vsec_capability_mask & (1 << NNT_VSEC_CRSPACE_SPACE_SUPPORTED)) &&
(nnt_device->pciconf_device.vsec_capability_mask & (1 << NNT_VSEC_GLOBAL_SEMAPHORE_SPACE_SUPPORTED))) {
nnt_device->pciconf_device.vsec_fully_supported = 1;
}
}
int init_pciconf(struct nnt_device* nnt_device)
{
int error = 0;
nnt_device->pciconf_device.semaphore_offset =
nnt_device->pciconf_device.vendor_specific_capability + PCI_SEMAPHORE_OFFSET;
nnt_device->pciconf_device.data_offset =
nnt_device->pciconf_device.vendor_specific_capability + PCI_DATA_OFFSET;
nnt_device->pciconf_device.address_offset =
nnt_device->pciconf_device.vendor_specific_capability + PCI_ADDRESS_OFFSET;
error = init_vsec_capability_mask(nnt_device);
check_vsec_minimum_support(nnt_device);
return error;
}

View File

@@ -0,0 +1,14 @@
#ifndef NNT_PCICONF_H
#define NNT_PCICONF_H
#include "nnt_device_defs.h"
#include "nnt_ioctl_defs.h"
int read_pciconf(struct nnt_device* nnt_device, struct nnt_rw_operation* read_operation);
int write_pciconf(struct nnt_device* nnt_device, struct nnt_rw_operation* write_operation);
int init_pciconf(struct nnt_device* nnt_device);
int read_dword(struct nnt_read_dword_from_config_space* read_from_cspace, struct nnt_device* nnt_device);
int check_address_space_support(struct nnt_device* nnt_device);
#endif

View File

@@ -0,0 +1,64 @@
#ifndef NNT_PCICONF_DEFS_H
#define NNT_PCICONF_DEFS_H
#define ADDRESS_SPACE_CR_SPACE 0x2
#define ADDRESS_SPACE_ICMD 0x3
#define ADDRESS_SPACE_SEMAPHORE 0xa
#define PCI_CONTROL_OFFSET 0x04
#define PCI_COUNTER_OFFSET 0x08
#define SEMAPHORE_MAX_RETRIES 0x1000
#define PCI_SPACE_BIT_OFFSET 0
#define PCI_SPACE_BIT_LENGTH 16
#define PCI_STATUS_BIT_OFFSET 29
#define PCI_STATUS_BIT_LEN 3
#define PCI_FLAG_BIT_OFFSET 31
#define READ_OPERATION 0
#define WRITE_OPERATION 1
#define IFC_MAX_RETRIES 0x10000
#define SEMAPHORE_MAX_RETRIES 0x1000
typedef enum nnt_space_address {
NNT_SPACE_ICMD = 0x1,
NNT_SPACE_CR_SPACE = 0x2,
NNT_SPACE_ALL_ICMD = 0x3,
NNT_SPACE_NODNIC_INIT_SEG = 0x4,
NNT_SPACE_EXPANSION_ROM = 0x5,
NNT_SPACE_ND_CR_SPACE = 0x6,
NNT_SPACE_SCAN_CR_SPACE = 0x7,
NNT_SPACE_GLOBAL_SEMAPHORE = 0xa,
NNT_SPACE_MAC = 0xf
} NNT_SPACE_ADDRESS;
typedef enum nnt_vsec_capability {
NNT_VSEC_INITIALIZED = 0,
NNT_VSEC_ICMD_SPACE_SUPPORTED,
NNT_VSEC_CRSPACE_SPACE_SUPPORTED,
NNT_VSEC_ALL_ICMD_SPACE_SUPPORTED,
NNT_VSEC_NODNIC_INIT_SEG_SPACE_SUPPORTED,
NNT_VSEC_EXPANSION_ROM_SPACE_SUPPORTED,
NNT_VSEC_ND_CRSPACE_SPACE_SUPPORTED,
NNT_VSEC_SCAN_CRSPACE_SPACE_SUPPORTED,
NNT_VSEC_GLOBAL_SEMAPHORE_SPACE_SUPPORTED,
NNT_VSEC_MAC_SPACE_SUPPORTED,
} NNT_VSEC_CAPABILITY;
// BIT Slicing macros
#define ONES32(size) ((size)?(0xffffffff>>(32-(size))):0)
#define MASK32(offset, size) ( ONES32(size)<<(offset))
#define EXTRACT_C(source, offset, size) ((((unsigned int)(source))>>(offset)) & ONES32(size))
#define EXTRACT(src, start, len) (((len) == 32)?(src):EXTRACT_C(src, start, len))
#define MERGE_C(rsrc1, rsrc2, start, len) ((((rsrc2)<<(start)) & (MASK32((start), (len)))) | ((rsrc1) & (~MASK32((start), (len)))))
#define MERGE(rsrc1, rsrc2, start, len) (((len) == 32)?(rsrc2):MERGE_C(rsrc1, rsrc2, start, len))
#endif

View File

@@ -0,0 +1,132 @@
#include "nnt_device_defs.h"
#include "nnt_pci_conf_access_no_vsec.h"
#include "nnt_defs.h"
int read_no_vsec(struct nnt_device* nnt_device, unsigned int offset,
unsigned int* data)
{
int error = 0;
if (nnt_device->wo_address) {
offset |= 0x1;
}
/* Write the wanted address to address register. */
error = pci_write_config_dword(nnt_device->pci_device, nnt_device->pciconf_device.address_register,
offset);
CHECK_PCI_WRITE_ERROR(error, nnt_device->pciconf_device.address_register,
offset);
/* Read the result from data register */
error = pci_read_config_dword(nnt_device->pci_device, nnt_device->pciconf_device.data_register,
data);
CHECK_PCI_READ_ERROR(error, nnt_device->pciconf_device.data_register);
ReturnOnFinished:
return error;
}
int read_pciconf_no_vsec(struct nnt_device* nnt_device, struct nnt_rw_operation* read_operation)
{
int counter = 0;
int error = 0;
for (counter = 0; counter < read_operation->size; counter += 4) {
if (read_no_vsec(nnt_device, read_operation->offset + counter,
&read_operation->data[counter >> 2])) {
error = counter;
goto ReturnOnFinished;
}
}
ReturnOnFinished:
return error;
}
int write_no_vsec(struct nnt_device* nnt_device, unsigned int offset,
unsigned int data)
{
int error = 0;
if (nnt_device->wo_address) {
/* write the data to the data register. */
error = pci_write_config_dword(nnt_device->pci_device, nnt_device->pciconf_device.data_register,
data);
CHECK_PCI_WRITE_ERROR(error, nnt_device->pciconf_device.data_register,
data);
/* Write the destination address to address register. */
error = pci_write_config_dword(nnt_device->pci_device, nnt_device->pciconf_device.address_register,
offset);
CHECK_PCI_WRITE_ERROR(error, nnt_device->pciconf_device.address_register,
offset);
} else {
/* Write the destination address to address register. */
error = pci_write_config_dword(nnt_device->pci_device, nnt_device->pciconf_device.address_register,
offset);
CHECK_PCI_WRITE_ERROR(error, nnt_device->pciconf_device.address_register,
offset);
/* write the data to the data register. */
error = pci_write_config_dword(nnt_device->pci_device, nnt_device->pciconf_device.data_register,
data);
CHECK_PCI_WRITE_ERROR(error, nnt_device->pciconf_device.data_register,
data);
}
ReturnOnFinished:
return error;
}
int write_pciconf_no_vsec(struct nnt_device* nnt_device, struct nnt_rw_operation* write_operation)
{
int counter = 0;
int error = 0;
for (counter = 0; counter < write_operation->size; counter += 4) {
if (write_no_vsec(nnt_device, write_operation->offset + counter,
write_operation->data[counter >> 2])) {
error = counter;
goto ReturnOnFinished;
}
}
ReturnOnFinished:
return error;
}
int is_wo_gw(struct nnt_device* nnt_device)
{
unsigned int data = 0;
int error = 0;
error = pci_write_config_dword(nnt_device->pci_device, nnt_device->pciconf_device.address_register,
NNT_DEVICE_ID_OFFSET);
CHECK_PCI_WRITE_ERROR(error, nnt_device->pciconf_device.address_register,
NNT_DEVICE_ID_OFFSET);
/* Read the result from data register */
error = pci_read_config_dword(nnt_device->pci_device, nnt_device->pciconf_device.address_register,
&data);
CHECK_PCI_READ_ERROR(error, nnt_device->pciconf_device.address_register);
if (data == NNT_WO_REG_ADDR_DATA) {
error = 1;
}
ReturnOnFinished:
return error;
}
int init_pciconf_no_vsec(struct nnt_device* nnt_device)
{
nnt_device->pciconf_device.address_register = NNT_CONF_ADDRES_REGISETER;
nnt_device->pciconf_device.data_register = NNT_CONF_DATA_REGISTER;
nnt_device->wo_address = is_wo_gw(nnt_device);
return 0;
}

View File

@@ -0,0 +1,12 @@
#ifndef NNT_PCICONF_NO_VSEC_H
#define NNT_PCICONF_NO_VSEC_H
#include "nnt_device_defs.h"
#include "nnt_ioctl_defs.h"
int read_pciconf_no_vsec(struct nnt_device* nnt_device, struct nnt_rw_operation* read_operation);
int write_pciconf_no_vsec(struct nnt_device* nnt_device, struct nnt_rw_operation* write_operation);
int init_pciconf_no_vsec(struct nnt_device* nnt_device);
#endif

View File

@@ -0,0 +1,38 @@
#ifndef NNT_DEVICE_LIST_H
#define NNT_DEVICE_LIST_H
#include "nnt_device_defs.h"
static struct pci_device_id pciconf_devices[] = {{PCI_DEVICE(NNT_MELLANOX_PCI_VENDOR, CONNECTX3_PCI_ID)},
{PCI_DEVICE(NNT_MELLANOX_PCI_VENDOR, CONNECTX3PRO_PCI_ID)},
{PCI_DEVICE(NNT_MELLANOX_PCI_VENDOR, CONNECTIB_PCI_ID)},
{PCI_DEVICE(NNT_MELLANOX_PCI_VENDOR, CONNECTX4_PCI_ID)},
{PCI_DEVICE(NNT_MELLANOX_PCI_VENDOR, CONNECTX4LX_PCI_ID)},
{PCI_DEVICE(NNT_MELLANOX_PCI_VENDOR, CONNECTX5_PCI_ID)},
{PCI_DEVICE(NNT_MELLANOX_PCI_VENDOR, CONNECTX5EX_PCI_ID)},
{PCI_DEVICE(NNT_MELLANOX_PCI_VENDOR, CONNECTX6_PCI_ID)},
{PCI_DEVICE(NNT_MELLANOX_PCI_VENDOR, CONNECTX6DX_PCI_ID)},
{PCI_DEVICE(NNT_MELLANOX_PCI_VENDOR, CONNECTX6LX_PCI_ID)},
{PCI_DEVICE(NNT_MELLANOX_PCI_VENDOR, CONNECTX7_PCI_ID)},
{PCI_DEVICE(NNT_MELLANOX_PCI_VENDOR, CONNECTX8_PCI_ID)},
{PCI_DEVICE(NNT_MELLANOX_PCI_VENDOR, SCHRODINGER_PCI_ID)},
{PCI_DEVICE(NNT_MELLANOX_PCI_VENDOR, FREYSA_PCI_ID)},
{PCI_DEVICE(NNT_MELLANOX_PCI_VENDOR, BLUEFIELD_PCI_ID)},
{PCI_DEVICE(NNT_MELLANOX_PCI_VENDOR, BLUEFIELD2_PCI_ID)},
{PCI_DEVICE(NNT_MELLANOX_PCI_VENDOR, BLUEFIELD3_PCI_ID)},
{PCI_DEVICE(NNT_MELLANOX_PCI_VENDOR, BLUEFIELD4_PCI_ID)},
{PCI_DEVICE(NNT_MELLANOX_PCI_VENDOR, SWITCHIB_PCI_ID)},
{PCI_DEVICE(NNT_MELLANOX_PCI_VENDOR, SWITCHIB2_PCI_ID)},
{PCI_DEVICE(NNT_MELLANOX_PCI_VENDOR, QUANTUM_PCI_ID)},
{PCI_DEVICE(NNT_MELLANOX_PCI_VENDOR, QUANTUM2_PCI_ID)},
{PCI_DEVICE(NNT_MELLANOX_PCI_VENDOR, QUANTUM3_PCI_ID)},
{PCI_DEVICE(NNT_MELLANOX_PCI_VENDOR, SPECTRUM_PCI_ID)},
{PCI_DEVICE(NNT_MELLANOX_PCI_VENDOR, SPECTRUM2_PCI_ID)},
{PCI_DEVICE(NNT_MELLANOX_PCI_VENDOR, SPECTRUM3_PCI_ID)},
{PCI_DEVICE(NNT_MELLANOX_PCI_VENDOR, SPECTRUM4_PCI_ID)},
{PCI_DEVICE(NNT_MELLANOX_PCI_VENDOR, BW00_PCI_ID)},
{
0,
}};
#endif // NNT_DEVICE_LIST_H

View File

@@ -0,0 +1,26 @@
#ifndef NNT_DRIVER_PPC_H
#define NNT_DRIVER_PPC_H
LIST_HEAD(nnt_device_list);
#define NNT_MAXIMUM_DEVICE_NAME_LENGTH 128
#define NNT_MAXIMUM_NUMBER_OF_DEVICES 8
#define NNT_MAXIMUM_POLLING_NUMBER 100
#define NNT_UNKNOWN_DEVICE_ID 0xffff
#define NNT_MINIMUM_WAITING_TIME 100
#define NNT_DEVICE_LIST_SIZE NNT_MAXIMUM_DEVICE_NAME_LENGTH * NNT_MAXIMUM_NUMBER_OF_DEVICES
struct nnt_ppc_device {
struct list_head entry;
struct pci_dev* pci_device;
char* pci_device_dbdf_name;
};
struct nnt_ppc_reset_info {
unsigned int number_of_found_pci_device;
unsigned int number_of_requested_pci_device;
int reset_was_done;
};
#endif

View File

@@ -0,0 +1,13 @@
#!/bin/bash
set -e
kernel_version="`modinfo -Fvermagic ./mst_backward_compatibility/mst_ppc/mst_ppc_pci_reset.ko | awk '{ print $1 }'`"
path_to_build="`pwd`"
path_to_build="$path_to_build/../build"
cd $path_to_build
mkdir -p /etc/mft/mlxfwreset/$kernel_version
/bin/cp -f ./mst_backward_compatibility/mst_ppc/mst_ppc_pci_reset.ko /etc/mft/mlxfwreset/$kernel_version/
cd -