OP-TEE: add optee driver from GitHub: optee_linuxdriver
authorZhang Zhijie <zhangzj@rock-chips.com>
Tue, 20 Oct 2015 02:47:31 +0000 (10:47 +0800)
committerZhang Zhijie <zhangzj@rock-chips.com>
Tue, 20 Oct 2015 02:47:31 +0000 (10:47 +0800)
Commit 4136b9d5a139(Fix TEESMC{32,64}_FASTCALL_RETURN_FROM_RPC)

Change-Id: I389e4f79270e3bc6e8844ec81758f8b5546192a1
Signed-off-by: Zhang Zhijie <zhangzj@rock-chips.com>
42 files changed:
security/optee_linuxdriver/Kconfig [new file with mode: 0644]
security/optee_linuxdriver/LICENSE [new file with mode: 0644]
security/optee_linuxdriver/Makefile [new file with mode: 0644]
security/optee_linuxdriver/Notice.md [new file with mode: 0644]
security/optee_linuxdriver/README.md [new file with mode: 0644]
security/optee_linuxdriver/armtz/Makefile [new file with mode: 0644]
security/optee_linuxdriver/armtz/handle.c [new file with mode: 0644]
security/optee_linuxdriver/armtz/handle.h [new file with mode: 0644]
security/optee_linuxdriver/armtz/tee_mem.c [new file with mode: 0644]
security/optee_linuxdriver/armtz/tee_mem.h [new file with mode: 0644]
security/optee_linuxdriver/armtz/tee_smc-arm.S [new file with mode: 0644]
security/optee_linuxdriver/armtz/tee_smc-arm64.S [new file with mode: 0644]
security/optee_linuxdriver/armtz/tee_tz_drv.c [new file with mode: 0644]
security/optee_linuxdriver/armtz/tee_tz_op.h [new file with mode: 0644]
security/optee_linuxdriver/armtz/tee_tz_priv.h [new file with mode: 0644]
security/optee_linuxdriver/core/Makefile [new file with mode: 0644]
security/optee_linuxdriver/core/tee_context.c [new file with mode: 0644]
security/optee_linuxdriver/core/tee_core.c [new file with mode: 0644]
security/optee_linuxdriver/core/tee_core_priv.h [new file with mode: 0644]
security/optee_linuxdriver/core/tee_debugfs.c [new file with mode: 0644]
security/optee_linuxdriver/core/tee_debugfs.h [new file with mode: 0644]
security/optee_linuxdriver/core/tee_kernel_api.c [new file with mode: 0644]
security/optee_linuxdriver/core/tee_mutex_wait.c [new file with mode: 0644]
security/optee_linuxdriver/core/tee_mutex_wait.h [new file with mode: 0644]
security/optee_linuxdriver/core/tee_session.c [new file with mode: 0644]
security/optee_linuxdriver/core/tee_shm.c [new file with mode: 0644]
security/optee_linuxdriver/core/tee_shm.h [new file with mode: 0644]
security/optee_linuxdriver/core/tee_supp_com.c [new file with mode: 0644]
security/optee_linuxdriver/core/tee_supp_com.h [new file with mode: 0644]
security/optee_linuxdriver/core/tee_sysfs.c [new file with mode: 0644]
security/optee_linuxdriver/core/tee_sysfs.h [new file with mode: 0644]
security/optee_linuxdriver/core/tee_wait_queue.c [new file with mode: 0644]
security/optee_linuxdriver/core/tee_wait_queue.h [new file with mode: 0644]
security/optee_linuxdriver/fdts/fvp-foundation-gicv2-psci.dts [new file with mode: 0644]
security/optee_linuxdriver/fdts/fvp-foundation-motherboard.dtsi [new file with mode: 0644]
security/optee_linuxdriver/fdts/readme.txt [new file with mode: 0644]
security/optee_linuxdriver/include/arm_common/teesmc.h [new file with mode: 0644]
security/optee_linuxdriver/include/arm_common/teesmc_st.h [new file with mode: 0644]
security/optee_linuxdriver/include/linux/tee_client_api.h [new file with mode: 0644]
security/optee_linuxdriver/include/linux/tee_core.h [new file with mode: 0644]
security/optee_linuxdriver/include/linux/tee_ioc.h [new file with mode: 0644]
security/optee_linuxdriver/include/linux/tee_kernel_api.h [new file with mode: 0644]

diff --git a/security/optee_linuxdriver/Kconfig b/security/optee_linuxdriver/Kconfig
new file mode 100644 (file)
index 0000000..a3f0714
--- /dev/null
@@ -0,0 +1,20 @@
+#
+# Copyright (c) 2014, STMicroelectronics International N.V.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License Version 2 as
+# published by the Free Software Foundation.
+#
+# 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.
+#
+
+# Trursted Execution Environment Configuration
+config TEE_SUPPORT
+       bool "Trusted Execution Environment Support"
+       default y
+       ---help---
+         This implements the Trusted Execution Environment (TEE) Client
+         API Specification from GlobalPlatform Device Technology.
diff --git a/security/optee_linuxdriver/LICENSE b/security/optee_linuxdriver/LICENSE
new file mode 100644 (file)
index 0000000..d159169
--- /dev/null
@@ -0,0 +1,339 @@
+                    GNU GENERAL PUBLIC LICENSE
+                       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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 Lesser 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.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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 Lesser General
+Public License instead of this License.
diff --git a/security/optee_linuxdriver/Makefile b/security/optee_linuxdriver/Makefile
new file mode 100644 (file)
index 0000000..02a32aa
--- /dev/null
@@ -0,0 +1,2 @@
+obj-y += core/
+obj-y += armtz/
diff --git a/security/optee_linuxdriver/Notice.md b/security/optee_linuxdriver/Notice.md
new file mode 100644 (file)
index 0000000..88c6cfa
--- /dev/null
@@ -0,0 +1,4 @@
+To avoid duplicating information we link to the Notice.md file in optee_os which
+states the contributor agreement rules etc, i.e, optee_linuxdriver follows what
+is written in the Notice.md on the URL below
+https://github.com/OP-TEE/optee_os/blob/master/Notice.md
diff --git a/security/optee_linuxdriver/README.md b/security/optee_linuxdriver/README.md
new file mode 100644 (file)
index 0000000..d2a0cb4
--- /dev/null
@@ -0,0 +1,91 @@
+# OP-TEE Linux Driver
+The optee_linuxdriver git, containing the source code for the TEE driver 
+module in Linux.
+It is distributed under the GPLv2 open-source license. For a general
+overview of OP-TEE, please see the [Notice.md](Notice.md) file.
+
+In this git, the module to build is optee.ko.
+It allows communication between the Rich OS Client Application (unsecure
+world), the Trusted OS (secure world) and the tee-supplicant (unsecure
+world) which is a daemon serving the Trusted OS in secure world with
+miscellaneous features, such as file system access.
+
+## License
+The software is provided under the
+[GPL-2.0](http://opensource.org/licenses/GPL-2.0) license.
+
+## Platforms supported
+This software has hardware dependencies.
+The software has been tested using:
+
+- STMicroelectronics b2020-h416 (orly-2) hardware (32-bits)
+- Some initial testing has been done using
+[Foundation FVP](http://www.arm.com/fvp), which can be downloaded free of
+charge.
+
+## Get and build the software
+### Get the compiler
+We will strive to use the latest available compiler from Linaro. Start by
+downloading and unpacking the compiler. Then export the PATH to the bin folder.
+
+       $ cd $HOME
+       $ mkdir toolchains
+       $ cd toolchains
+       $ wget http://releases.linaro.org/14.05/components/toolchain/binaries/gcc-linaro-arm-linux-gnueabihf-4.9-2014.05_linux.tar.xz
+       $ tar xvf gcc-linaro-arm-linux-gnueabihf-4.9-2014.05_linux.tar.xz
+       $ export PATH=$HOME/toolchains/gcc-linaro-arm-linux-gnueabihf-4.9-2014.05_linux/bin:$PATH
+
+### Get the Linux kernel (from www.kernel.org)
+       $ cd $HOME
+       $ mkdir devel
+       $ cd devel
+       $ tar xf linux-3.10.32.tar.xz
+       $ mv linux-3.10.32 linux
+
+### Download the source code
+       $ cd $HOME
+       $ cd devel
+       $ git clone https://github.com/OP-TEE/optee_linuxdriver.git
+
+### Build
+       $ cd $HOME/devel/linux
+       $ make -j3 ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- mrproper
+       $ make -j3 ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- defconfig
+       $ make -j3 ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- all
+       $ make -j3 ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- M=$HOME/devel/optee_linuxdriver modules
+
+#### Compiler flags
+To be able to see the full command when building you could build using following
+flag:
+
+`$ make V=1`
+
+## Coding standards
+In this project we are trying to adhere to the same coding convention as used in
+the Linux kernel (see
+[CodingStyle](https://www.kernel.org/doc/Documentation/CodingStyle)). We achieve this by running
+[checkpatch](http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/scripts/checkpatch.pl) from Linux kernel.
+However there are a few exceptions that we had to make since the code also
+follows GlobalPlatform standards. The exceptions are as follows:
+
+- CamelCase for GlobalPlatform types are allowed.
+- And we also exclude checking third party code that we might use in this
+  project, such as LibTomCrypt, MPA, newlib (not in this particular git, but
+  those are also part of the complete TEE solution). The reason for excluding
+  and not fixing third party code is because we would probably deviate too much
+  from upstream and therefore it would be hard to rebase against those projects
+  later on (and we don't expect that it is easy to convince other software
+  projects to change coding style).
+
+### checkpatch
+Since checkpatch is licensed under the terms of GNU GPL License Version 2, we
+cannot include this script directly into this project. Therefore we have
+written the Makefile so you need to explicitly point to the script by exporting
+an environment variable, namely CHECKPATCH. So, suppose that the source code for
+the Linux kernel is at `$HOME/devel/linux`, then you have to export like follows:
+
+       $ export CHECKPATCH=$HOME/devel/linux/scripts/checkpatch.pl
+thereafter it should be possible to use one of the different checkpatch targets
+in the [Makefile](Makefile). There are targets for checking all files, checking
+against latest commit, against a certain base-commit etc. For the details, read
+the [Makefile](Makefile).
diff --git a/security/optee_linuxdriver/armtz/Makefile b/security/optee_linuxdriver/armtz/Makefile
new file mode 100644 (file)
index 0000000..af2b10c
--- /dev/null
@@ -0,0 +1,40 @@
+
+#########################################################################
+# Set Internal Variables                                                #
+# May be modified to match your setup                                   #
+#########################################################################
+CFG_TEE_DRV_DEBUGFS?=0
+CFG_TEE_CORE_LOG_LEVEL?=2
+CFG_TEE_TA_LOG_LEVEL?=2
+
+ccflags-y+=-Werror
+ccflags-y+=-I$(M)/include/arm_common
+ccflags-y+=-I$(M)/include/linux
+ccflags-y+=-I$(M)/include
+ccflags-y+=-I$(M)/core
+
+ccflags-y+=-DCFG_TEE_DRV_DEBUGFS=${CFG_TEE_DRV_DEBUGFS}
+ccflags-y+=-DCFG_TEE_CORE_LOG_LEVEL=${CFG_TEE_CORE_LOG_LEVEL}
+ccflags-y+=-DCFG_TEE_TA_LOG_LEVEL=${CFG_TEE_TA_LOG_LEVEL}
+
+obj-m += optee_armtz.o
+
+optee_armtz-objs:=   \
+               tee_tz_drv.o \
+               tee_mem.o \
+               handle.o
+
+
+ifeq ($(CONFIG_ARM),y)
+# "smc" assembly intruction requires dedicated "armv7 secure extension"
+secext := $(call as-instr,.arch_extension sec,+sec)
+AFLAGS_tee_smc-arm.o := -Wa,-march=armv7-a$(secext)
+optee_armtz-objs += \
+       tee_smc-arm.o
+endif
+
+ifeq ($(CONFIG_ARM64),y)
+optee_armtz-objs += \
+       tee_smc-arm64.o
+endif
+
diff --git a/security/optee_linuxdriver/armtz/handle.c b/security/optee_linuxdriver/armtz/handle.c
new file mode 100644 (file)
index 0000000..f40479f
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2014, Linaro Limited
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License Version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ */
+#include <linux/slab.h>
+#include <linux/string.h>
+#include "handle.h"
+
+/*
+ * Define the initial capacity of the database. It should be a low number
+ * multiple of 2 since some databases a likely to only use a few handles.
+ * Since the algorithm is to doubles up when growing it shouldn't cause a
+ * noticable overhead on large databases.
+ */
+#define HANDLE_DB_INITIAL_MAX_PTRS     4
+
+void handle_db_destroy(struct handle_db *db)
+{
+       if (db) {
+               kfree(db->ptrs);
+               db->ptrs = NULL;
+               db->max_ptrs = 0;
+       }
+}
+
+int handle_get(struct handle_db *db, void *ptr)
+{
+       unsigned n;
+       void *p;
+       unsigned new_max_ptrs;
+
+       if (!db || !ptr)
+               return -1;
+
+       /* Try to find an empty location */
+       for (n = 0; n < db->max_ptrs; n++) {
+               if (!db->ptrs[n]) {
+                       db->ptrs[n] = ptr;
+                       return n;
+               }
+       }
+
+       /* No location available, grow the ptrs array */
+       if (db->max_ptrs)
+               new_max_ptrs = db->max_ptrs * 2;
+       else
+               new_max_ptrs = HANDLE_DB_INITIAL_MAX_PTRS;
+       p = krealloc(db->ptrs, new_max_ptrs * sizeof(void *), GFP_KERNEL);
+       if (!p)
+               return -1;
+       db->ptrs = p;
+       memset(db->ptrs + db->max_ptrs, 0,
+              (new_max_ptrs - db->max_ptrs) * sizeof(void *));
+       db->max_ptrs = new_max_ptrs;
+
+       /* Since n stopped at db->max_ptrs there is an empty location there */
+       db->ptrs[n] = ptr;
+       return n;
+}
+
+void *handle_put(struct handle_db *db, int handle)
+{
+       void *p;
+
+       if (!db || handle < 0 || (unsigned)handle >= db->max_ptrs)
+               return NULL;
+
+       p = db->ptrs[handle];
+       db->ptrs[handle] = NULL;
+       return p;
+}
+
+void *handle_lookup(struct handle_db *db, int handle)
+{
+       if (!db || handle < 0 || (unsigned)handle >= db->max_ptrs)
+               return NULL;
+
+       return db->ptrs[handle];
+}
diff --git a/security/optee_linuxdriver/armtz/handle.h b/security/optee_linuxdriver/armtz/handle.h
new file mode 100644 (file)
index 0000000..0d20534
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2014, Linaro Limited
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License Version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ */
+#ifndef HANDLE_H
+#define HANDLE_H
+
+struct handle_db {
+       void **ptrs;
+       unsigned max_ptrs;
+};
+
+#define HANDLE_DB_INITIALIZER { NULL, 0 }
+
+/*
+ * Frees all internal data structures of the database, but does not free
+ * the db pointer. The database is safe to reuse after it's destroyed, it
+ * just be empty again.
+ */
+void handle_db_destroy(struct handle_db *db);
+
+/*
+ * Allocates a new handle and assigns the supplied pointer to it,
+ * ptr must not be NULL.
+ * The function returns
+ * >= 0 on success and
+ * -1 on failure
+ */
+int handle_get(struct handle_db *db, void *ptr);
+
+/*
+ * Deallocates a handle. Returns the associated pointer of the handle
+ * the the handle was valid or NULL if it's invalid.
+ */
+void *handle_put(struct handle_db *db, int handle);
+
+/*
+ * Returns the associated pointer of the handle if the handle is a valid
+ * handle.
+ * Returns NULL on failure.
+ */
+void *handle_lookup(struct handle_db *db, int handle);
+
+#endif /*HANDLE_H*/
diff --git a/security/optee_linuxdriver/armtz/tee_mem.c b/security/optee_linuxdriver/armtz/tee_mem.c
new file mode 100644 (file)
index 0000000..bbad023
--- /dev/null
@@ -0,0 +1,675 @@
+/*
+ * Copyright (c) 2014, STMicroelectronics International N.V.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License Version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ */
+/**
+ * \file tee_mem.c
+ * \brief Functions to manage a pool of memory chunks.
+ *
+ * This module provides basic functions to manage dynamically a fixed amount
+ * of memory (memory region).  For this current implementation the provided
+ * memory region used for the allocations should be physically AND logically
+ * contiguous (only one region is supported for a given allocator).
+ *
+ * Principle of the allocator algorithm: "best fit"
+ *
+ */
+
+#include <linux/slab.h>
+
+#include "tee_mem.h"
+
+#define _DUMP_INFO_ALLOCATOR 0
+#define USE_DEVM_ALLOC 1
+
+#ifndef USE_DEVM_ALLOC
+#define _KMALLOC(s, f) kmalloc(s, f)
+#define _KFREE(a)     kfree(a)
+#else
+#define _KMALLOC(s, f) devm_kzalloc(dev, s, f)
+#define _KFREE(a)     devm_kfree(dev, a)
+#endif
+
+/**
+ * \struct mem_chunk
+ * \brief Elementary descriptor of an allocated memory block
+ *
+ * \param node      Node for linked list
+ * \param counter   Reference counter
+ *                  (0 indicates that the block is not used/freed)
+ * \param size      Total size in bytes
+ * \param paddr     Physical base address
+ *
+ * Elementary memory block definition
+ */
+struct mem_chunk {
+       struct list_head node;
+       uint32_t counter;
+       size_t size;
+       unsigned long paddr;
+};
+
+/**
+ * \struct shm_pool
+ * \brief Main structure to describe a shared memory pool
+ *
+ * \param size    Total size in bytes of the associated memory region
+ * \param vaddr   Logical base address
+ * \param paddr   Physical base address
+ * \param used    Total size in bytes of the used memory
+ * \param mchunks     List head for handle the elementary memory blocks
+ *
+ * Shared memory pool structure definition
+ */
+struct shm_pool {
+       struct mutex lock;
+       size_t size;            /* Size of pool/heap memory segment */
+       size_t used;            /* Number of bytes allocated */
+       void *vaddr;            /* Associated Virtual address */
+       unsigned long paddr;    /* Associated Physical address */
+       bool cached;            /* true if pool is cacheable */
+       struct list_head mchunks;       /* Head of memory chunk/block list */
+};
+
+#define __CALCULATE_RATIO_MEM_USED(a) (((a->used)*100)/(a->size))
+
+/**
+ * \brief Dumps the information of the shared memory pool
+ *
+ * \param pool           Pointer on the pool
+ * \param detailforced   Flag to force the log for the detailed informations
+ *
+ * Dump/log the meta data of the shared memory pool on the standard output.
+ *
+ */
+void tee_shm_pool_dump(struct device *dev, struct shm_pool *pool, bool forced)
+{
+       struct mem_chunk *chunk;
+
+       if (WARN_ON(!dev || !pool))
+               return;
+
+       dev_info(dev,
+                "tee_shm_pool_dump() poolH(0x%p) pAddr=0x%p vAddr=0x%p size=%zu used=%zu(%zu%%)\n",
+                (void *)pool,
+                (void *)pool->paddr,
+                (void *)pool->vaddr,
+                pool->size, pool->used, __CALCULATE_RATIO_MEM_USED(pool));
+
+       if ((pool->used != 0) || (forced == true)) {
+               dev_info(dev, "  \\ HEAD next:[0x%p] prev:[0x%p]\n",
+                        (void *)pool->mchunks.next,
+                        (void *)pool->mchunks.prev);
+
+               dev_info(dev,
+                        "  |-[@]        next       prev       pAddr      size     refCount\n");
+
+               list_for_each_entry(chunk, &pool->mchunks, node) {
+                       dev_info(dev, "  | [0x%p] 0x%p 0x%p 0x%p %08zu %d\n",
+                                (void *)chunk,
+                                (void *)chunk->node.next,
+                                (void *)chunk->node.prev,
+                                (void *)chunk->paddr,
+                                chunk->size, chunk->counter);
+               }
+       }
+}
+
+bool tee_shm_pool_is_cached(struct shm_pool *pool)
+{
+       return pool->cached;
+}
+
+void tee_shm_pool_set_cached(struct shm_pool *pool)
+{
+       pool->cached = true;
+}
+
+/**
+ * \brief Creates and returns a new shared memory pool manager structure
+ *
+ * \param shm_size      Size of the associated memory chunk
+ * \param shm_vaddr     Virtual/logical base address
+ * \param shm_paddr     Physical base address
+ *
+ * \return Reference of the created shared memory pool manager
+ *
+ * Create and initialize a shared memory pool manager.
+ * The description of the memory region (shm_size, shm_vaddr, shm_paddr)
+ * which is passed should be a physically AND virtually contiguous
+ * (no check is performed by the function).
+ * If a error is detected returned pool is NULL.
+ */
+struct shm_pool *tee_shm_pool_create(struct device *dev, size_t shm_size,
+                                    void *shm_vaddr, unsigned long shm_paddr)
+{
+       struct mem_chunk *chunk = NULL;
+       struct shm_pool *pool = NULL;
+
+       if (WARN_ON(!dev))
+               goto alloc_failed;
+
+       dev_dbg(dev, "> vaddr=0x%p, paddr=0x%p, size=%zuKiB\n",
+               shm_vaddr, (void *)shm_paddr, shm_size / 1024);
+
+       /* Alloc and initialize the shm_pool structure */
+       pool = _KMALLOC(sizeof(struct shm_pool), GFP_KERNEL);
+       if (!pool) {
+               dev_err(dev, "kmalloc <struct shm_pool> failed\n");
+               goto alloc_failed;
+       }
+       memset(pool, 0, sizeof(*pool));
+       mutex_init(&pool->lock);
+       mutex_lock(&pool->lock);
+
+       INIT_LIST_HEAD(&(pool->mchunks));
+       pool->size = shm_size;
+       pool->vaddr = shm_vaddr;
+       pool->paddr = shm_paddr;
+
+       /* Create the initial elementary memory chunk    */
+       /* which handles the whole memory region         */
+       chunk = _KMALLOC(sizeof(struct mem_chunk), GFP_KERNEL);
+       if (!chunk) {
+               dev_err(dev, "kmalloc <struct MemChunk> failed\n");
+               goto alloc_failed;
+       }
+       memset(chunk, 0, sizeof(*chunk));
+       chunk->paddr = shm_paddr;
+       chunk->size = shm_size;
+
+       /* Adds the new entry immediately after the list head */
+       list_add(&(chunk->node), &(pool->mchunks));
+
+#if defined(_DUMP_INFO_ALLOCATOR) && (_DUMP_INFO_ALLOCATOR > 0)
+       tee_shm_pool_dump(dev, pool, true);
+#endif
+       dev_dbg(dev, "< poolH(0x%p) chunkH=0x%p\n",
+               (void *)(pool), (void *)chunk);
+
+       mutex_unlock(&pool->lock);
+       return pool;
+
+alloc_failed:
+       if (chunk)
+               _KFREE(chunk);
+
+       if (pool)
+               _KFREE(pool);
+
+       return NULL;
+}
+
+/**
+ * Local helper function to check that the physical address is valid
+ */
+static inline int is_valid_paddr(struct shm_pool *pool, unsigned long paddr)
+{
+       return (paddr >= pool->paddr && paddr < (pool->paddr + pool->size));
+}
+
+/**
+ * Local helper function to check that the virtual address is valid
+ */
+static inline int is_valid_vaddr(struct shm_pool *pool, void *vaddr)
+{
+       return (vaddr >= pool->vaddr && vaddr < (pool->vaddr + pool->size));
+}
+
+/**
+ * \brief Destroy the shared memory pool manager
+ *
+ * \param pool           Pointer on the pool
+ *
+ * Destroy a memory pool manager
+ *
+ */
+void tee_shm_pool_destroy(struct device *dev, struct shm_pool *pool)
+{
+       struct mem_chunk *chunk;
+
+       if (WARN_ON(!dev || !pool))
+               return;
+
+       dev_dbg(dev, "> poolH(0x%p)\n", (void *)pool);
+
+#if defined(_DUMP_INFO_ALLOCATOR) && (_DUMP_INFO_ALLOCATOR > 0)
+       tee_shm_pool_dump(dev, pool, true);
+#endif
+
+       tee_shm_pool_reset(dev, pool);
+
+       chunk = list_first_entry(&pool->mchunks, struct mem_chunk, node);
+       dev_dbg(dev, "free chunkH=0x%p\n", (void *)chunk);
+       _KFREE(chunk);
+       _KFREE(pool);
+
+       dev_dbg(dev, "<\n");
+}
+
+/**
+ * \brief Free all reserved chunk if any, and set pool at it initial state
+ *
+ * \param pool  Pointer on the pool
+ *
+ */
+void tee_shm_pool_reset(struct device *dev, struct shm_pool *pool)
+{
+       struct mem_chunk *chunk;
+       struct mem_chunk *tmp;
+       struct mem_chunk *first = NULL;
+
+       if (WARN_ON(!dev || !pool))
+               return;
+
+       mutex_lock(&pool->lock);
+
+       list_for_each_entry_safe(chunk, tmp, &pool->mchunks, node) {
+               if (first != NULL) {
+                       dev_err(dev, "Free lost chunkH=0x%p\n", (void *)chunk);
+                       list_del(&chunk->node);
+                       _KFREE(chunk);
+               } else {
+                       first = chunk;
+               }
+       }
+
+       first->counter = 0;
+       first->paddr = pool->paddr;
+       first->size = pool->size;
+       pool->used = 0;
+
+       mutex_unlock(&pool->lock);
+}
+
+/**
+ * \brief Return the logical address
+ *
+ * \param pool          Pointer on the pool
+ * \param paddr         Physical address
+ *
+ * \return Virtual/logical address
+ *
+ * Return the associated virtual/logical address. The address should be inside
+ * the range of addresses managed by the shm pool.
+ *
+ */
+void *tee_shm_pool_p2v(struct device *dev, struct shm_pool *pool,
+                      unsigned long paddr)
+{
+       if (WARN_ON(!dev || !pool))
+               return NULL;
+
+       mutex_lock(&pool->lock);
+       if (!is_valid_paddr(pool, paddr)) {
+               mutex_unlock(&pool->lock);
+               dev_err(dev,
+                       "tee_shm_pool_p2v() paddr=0x%p not in the shm pool\n",
+                       (void *)paddr);
+               return NULL;
+       } else {
+               unsigned long offset = paddr - pool->paddr;
+               void *p = (void *)((unsigned long)pool->vaddr + offset);
+
+               mutex_unlock(&pool->lock);
+               return p;
+       }
+}
+
+/**
+ * \brief Return the physical address
+ *
+ * \param pool          Pointer on the pool
+ * \param vaddr         Logical/Virtual address
+ *
+ * \return Physical address
+ *
+ * Return the associated physical address. The address should be inside
+ * the range of addresses managed by the pool.
+ *
+ */
+unsigned long tee_shm_pool_v2p(struct device *dev, struct shm_pool *pool,
+                              void *vaddr)
+{
+       if (WARN_ON(!dev || !pool))
+               return 0UL;
+
+       mutex_lock(&pool->lock);
+       if (!is_valid_vaddr(pool, vaddr)) {
+               dev_err(dev,
+                       "tee_shm_pool_v2p() vaddr=0x%p not in shm pool\n",
+                       (void *)vaddr);
+               mutex_unlock(&pool->lock);
+               return 0UL;
+       } else {
+               unsigned long offset = vaddr - pool->vaddr;
+               unsigned long p = pool->paddr + offset;
+
+               mutex_unlock(&pool->lock);
+               return p;
+       }
+}
+
+/**
+ * \brief Allocate a new block of memory
+ *
+ * \param pool          Pointer on the pool
+ * \param size          Expected size (in byte)
+ * \param alignment     Alignment constraint (in byte)
+ *
+ * \return Physical base address of the allocated block
+ *
+ * Allocate a memory chunk inside the memory region managed by the pool.
+ *
+ */
+unsigned long tee_shm_pool_alloc(struct device *dev,
+                                struct shm_pool *pool,
+                                size_t size, size_t alignment)
+{
+       struct mem_chunk *chunk;
+       struct mem_chunk *betterchunk = NULL;
+       struct mem_chunk *prev_chunk = NULL;
+       struct mem_chunk *next_chunk = NULL;
+       unsigned long begAddr;
+       unsigned long endAddr;
+
+       if (WARN_ON(!dev || !pool))
+               return 0UL;
+
+       dev_dbg(dev, "> poolH(%p:%p:%x) size=0x%zx align=0x%zx\n",
+               pool, (void *)pool->paddr, (unsigned int)pool->size, size,
+               alignment);
+
+       /* Align on cache line of the target */
+       /* \todo(jmd) Should be defined by a global target specific parameter */
+       /*  size = (size + (32-1)) & ~(32-1)            */
+
+       if (ALIGN(size, 0x20) < size)
+               goto failed_out;
+
+       if (alignment == 0)
+               alignment = 1;
+
+       size = ALIGN(size, 0x20);
+
+       alignment = ALIGN(alignment, 0x20);
+
+       if (size > (pool->size - pool->used))
+               goto failed_out;
+
+       mutex_lock(&pool->lock);
+
+       /**
+        * Algorithm: Smallest waste (best fit): We choose the block that has the
+        * smallest waste. In other words we choose the block so that
+        * size(b) - size is as small as possible.
+        */
+       list_for_each_entry(chunk, &pool->mchunks, node) {
+               if (chunk->counter == 0) {      /* Free chunk */
+                       begAddr = ALIGN(chunk->paddr, alignment);
+                       endAddr = begAddr + size;
+
+                       if (begAddr >= chunk->paddr
+                           && endAddr <= (chunk->paddr + chunk->size)
+                           && (betterchunk == NULL
+                               /* Always split smaller block */
+                               || chunk->size < betterchunk->size))
+                               betterchunk = chunk;
+               }
+       }
+
+       /**
+        * Update the linked list
+        */
+       if (betterchunk != NULL) {
+               prev_chunk = _KMALLOC(sizeof(struct mem_chunk), GFP_KERNEL);
+               next_chunk = _KMALLOC(sizeof(struct mem_chunk), GFP_KERNEL);
+
+               if ((!prev_chunk) || (!next_chunk))
+                       goto failed_out_unlock;
+
+               begAddr = ALIGN(betterchunk->paddr, alignment);
+               endAddr = begAddr + size;
+
+               if (betterchunk->paddr < begAddr) {
+                       /* memory between begin of chunk and begin
+                        * of created memory => create a free chunk */
+                       prev_chunk->counter = 0;
+                       prev_chunk->paddr = betterchunk->paddr;
+                       prev_chunk->size = begAddr - betterchunk->paddr;
+
+                       betterchunk->paddr = begAddr;
+                       betterchunk->size -= prev_chunk->size;
+
+                       dev_dbg(dev,
+                               "create p_chunkH=0x%p paddr=0x%p (s=%zu)\n",
+                               (void *)prev_chunk,
+                               (void *)prev_chunk->paddr, prev_chunk->size);
+
+                       list_add_tail(&(prev_chunk->node),
+                                     &(betterchunk->node));
+                       prev_chunk = NULL;
+               } else {
+                       _KFREE(prev_chunk);
+               }
+
+               if (betterchunk->paddr + betterchunk->size > endAddr) {
+                       /* memory between end of chunk and end of
+                        * created memory => create a free chunk */
+                       next_chunk->counter = 0;
+                       next_chunk->paddr = endAddr;
+                       next_chunk->size = betterchunk->size - size;
+
+                       dev_dbg(dev,
+                               "create n_chunkH=0x%p paddr=0x%p (s=%zu)\n",
+                               (void *)next_chunk,
+                               (void *)next_chunk->paddr, next_chunk->size);
+
+                       betterchunk->size = size;
+
+                       list_add(&(next_chunk->node), &(betterchunk->node));
+                       next_chunk = NULL;
+               } else {
+                       _KFREE(next_chunk);
+               }
+
+               betterchunk->counter = 1;
+               pool->used += size;
+
+               mutex_unlock(&pool->lock);
+
+#if defined(_DUMP_INFO_ALLOCATOR) && (_DUMP_INFO_ALLOCATOR > 1)
+               tee_shm_pool_dump(dev, pool, false);
+#endif
+
+               dev_dbg(dev,
+                       "< chunkH=0x%p paddr=%p (s=%zu) align=0x%zx\n",
+                       (void *)betterchunk,
+                       (void *)betterchunk->paddr,
+                       betterchunk->size, alignment);
+
+               return betterchunk->paddr;
+       }
+
+failed_out_unlock:
+       mutex_unlock(&pool->lock);
+failed_out:
+       if (prev_chunk)
+               _KFREE(prev_chunk);
+       if (next_chunk)
+               _KFREE(next_chunk);
+
+       dev_err(dev,
+               "tee_shm_pool_alloc() FAILED, size=0x%zx, align=0x%zx free=%zu\n",
+               size, alignment, pool->size - pool->used);
+
+#if defined(_DUMP_INFO_ALLOCATOR) && (_DUMP_INFO_ALLOCATOR > 1)
+       tee_shm_pool_dump(dev, pool, true);
+#endif
+
+       return 0UL;
+}
+
+/**
+ * \brief Release a allocated block of memory
+ *
+ * \param pool          Pointer on the pool
+ * \param paddr         Physical @ of the block which must be released
+ * \param size          Reference to return the size of the block
+ *
+ * Free a allocated memory block inside
+ * the memory region managed by the pool.
+ *
+ */
+int tee_shm_pool_free(struct device *dev, struct shm_pool *pool,
+                     unsigned long paddr, size_t *size)
+{
+       struct mem_chunk *chunk;
+
+       if (WARN_ON(!dev || !pool))
+               return -EINVAL;
+
+       dev_dbg(dev, "> Try to free ... poolH(0x%p) paddr=0x%p\n",
+               (void *)pool, (void *)paddr);
+
+#if defined(_DUMP_INFO_ALLOCATOR) && (_DUMP_INFO_ALLOCATOR > 1)
+       tee_shm_pool_dump(dev, pool, false);
+#endif
+
+       mutex_lock(&pool->lock);
+
+       if (!is_valid_paddr(pool, paddr))
+               goto out_failed;
+
+       list_for_each_entry(chunk, &pool->mchunks, node) {
+               if (chunk->paddr == paddr) {
+                       if (size != NULL)
+                               *size = chunk->size;
+
+                       if (chunk->counter == 0) {
+                               dev_warn(dev,
+                                        "< tee_shm_pool_free() WARNING, paddr=0x%p already released\n",
+                                        (void *)paddr);
+                               return -EINVAL;
+                       } else if (--chunk->counter == 0) {
+                               dev_dbg(dev, "paddr=%p\n", (void *)paddr);
+
+                               pool->used -= chunk->size;
+
+                               /* Merge with previous */
+                               if (chunk->node.prev != &pool->mchunks) {
+                                       struct mem_chunk *prev =
+                                           list_entry(chunk->node.prev,
+                                                      struct mem_chunk, node);
+                                       if (prev->counter == 0) {
+                                               dev_dbg(dev,
+                                                       "chunkH=0x%p paddr=0x%p free ok\n",
+                                                       (void *)chunk,
+                                                       (void *)paddr);
+                                               prev->size += chunk->size;
+                                               list_del(&chunk->node);
+                                               _KFREE(chunk);
+                                               chunk = prev;
+                                       }
+                               }
+                               /* Merge with next */
+                               if (chunk->node.next != &pool->mchunks) {
+                                       struct mem_chunk *next =
+                                           list_entry(chunk->node.next,
+                                                      struct mem_chunk, node);
+                                       if (next->counter == 0) {
+                                               dev_dbg(dev,
+                                                       "chunkH=0x%p paddr=0x%p free ok\n",
+                                                       (void *)chunk,
+                                                       (void *)paddr);
+                                               chunk->size += next->size;
+                                               list_del(&next->node);
+                                               _KFREE(next);
+                                       }
+                               }
+                               mutex_unlock(&pool->lock);
+
+#if defined(_DUMP_INFO_ALLOCATOR) && (_DUMP_INFO_ALLOCATOR > 1)
+                               tee_shm_pool_dump(dev, pool, false);
+#endif
+                               dev_dbg(dev, "< freed\n");
+                               return 0;
+
+                       } else {
+                               mutex_unlock(&pool->lock);
+                               dev_dbg(dev,
+                                       "< paddr=0x%p  (--) refcounter is decremented ret=1\n",
+                                       (void *)paddr);
+                               return 1;
+                       }
+               }
+       }
+
+out_failed:
+       mutex_unlock(&pool->lock);
+#if defined(_DUMP_INFO_ALLOCATOR) && (_DUMP_INFO_ALLOCATOR > 1)
+       tee_shm_pool_dump(dev, pool, false);
+#endif
+       dev_err(dev,
+               "< tee_shm_pool_free() FAILED, pAddr=0x%p not found\n",
+               (void *)paddr);
+       return -EINVAL;
+}
+
+/**
+ * \brief Increase the reference count of the memory chunk
+ *
+ * \param pool          Pointer on the pool
+ * \param paddr         Physical address
+ *
+ * \return true if successful (false otherwise)
+ *
+ * Increment the reference count of the allocated block of memory.
+ * paddr should a valid address returned by the tee_shm_pool_alloc().
+ *
+ */
+bool tee_shm_pool_incref(struct device *dev, struct shm_pool *pool,
+                        unsigned long paddr)
+{
+       struct mem_chunk *chunk;
+
+       if (WARN_ON(!dev || !pool))
+               return false;
+
+       mutex_lock(&pool->lock);
+
+       if (!is_valid_paddr(pool, paddr))
+               goto out_failed;
+
+       list_for_each_entry(chunk, &pool->mchunks, node) {
+               if (chunk->paddr == paddr) {
+                       dev_dbg(dev,
+                               "pAddr=%p (++) refcounter is incremented\n",
+                               (void *)paddr);
+                       chunk->counter++;
+
+#if defined(_DUMP_INFO_ALLOCATOR) && (_DUMP_INFO_ALLOCATOR > 0)
+                       tee_shm_pool_dump(dev, pool, false);
+#endif
+                       mutex_unlock(&pool->lock);
+                       return true;
+               }
+       }
+
+out_failed:
+       mutex_unlock(&pool->lock);
+
+       dev_err(dev,
+               "tee_shm_pool_incref() FAILED, pAddr=%p is not a valid @\n",
+               (void *)paddr);
+
+       return false;
+}
diff --git a/security/optee_linuxdriver/armtz/tee_mem.h b/security/optee_linuxdriver/armtz/tee_mem.h
new file mode 100644 (file)
index 0000000..01d08a8
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2014, STMicroelectronics International N.V.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License Version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ */
+#ifndef TEE_MEM_H
+#define TEE_MEM_H
+
+#include <linux/types.h>
+#include <linux/device.h>
+
+struct shm_pool;
+
+struct shm_pool *tee_shm_pool_create(struct device *dev, size_t shm_size,
+                                    void *shm_vaddr, unsigned long shm_paddr);
+
+void tee_shm_pool_destroy(struct device *dev, struct shm_pool *pool);
+
+void *tee_shm_pool_p2v(struct device *dev, struct shm_pool *pool,
+                      unsigned long paddr);
+
+unsigned long tee_shm_pool_v2p(struct device *dev, struct shm_pool *pool,
+                              void *vaddr);
+
+unsigned long tee_shm_pool_alloc(struct device *dev,
+                                struct shm_pool *pool,
+                                size_t size, size_t alignment);
+
+int tee_shm_pool_free(struct device *dev, struct shm_pool *pool,
+                     unsigned long paddr, size_t *size);
+
+bool tee_shm_pool_incref(struct device *dev, struct shm_pool *pool,
+                        unsigned long paddr);
+
+void tee_shm_pool_dump(struct device *dev, struct shm_pool *pool, bool forced);
+
+void tee_shm_pool_reset(struct device *dev, struct shm_pool *pool);
+
+bool tee_shm_pool_is_cached(struct shm_pool *pool);
+
+void tee_shm_pool_set_cached(struct shm_pool *pool);
+
+#endif
diff --git a/security/optee_linuxdriver/armtz/tee_smc-arm.S b/security/optee_linuxdriver/armtz/tee_smc-arm.S
new file mode 100644 (file)
index 0000000..5d1d2ea
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2014, STMicroelectronics International N.V.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License Version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ */
+#include <linux/linkage.h>
+
+.text
+.balign 4
+.code 32
+
+       /* void tee_smc_call(struct smc_param *param); */
+       .globl  tee_smc_call
+ENTRY(tee_smc_call)
+       push    {r4-r8, lr}
+       mov     r8, r0
+       ldm     r8, {r0-r7}
+.arch_extension sec
+       smc     #0
+       stm     r8, {r0-r7}
+       pop     {r4-r8, pc}
+ENDPROC(tee_smc_call)
diff --git a/security/optee_linuxdriver/armtz/tee_smc-arm64.S b/security/optee_linuxdriver/armtz/tee_smc-arm64.S
new file mode 100644 (file)
index 0000000..2722402
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2014, Linaro Limited
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License Version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ */
+#include <linux/linkage.h>
+
+       .text
+
+#define SMC_PARAM_X0_OFFS      0
+#define SMC_PARAM_X2_OFFS      16
+#define SMC_PARAM_X4_OFFS      32
+#define SMC_PARAM_X6_OFFS      48
+
+       /* void tee_smc_call(struct smc_param *param); */
+       .globl  tee_smc_call
+ENTRY(tee_smc_call)
+       stp     x28, x30, [sp, #-16]!
+       mov     x28, x0
+       ldp     x0, x1, [x28, #SMC_PARAM_X0_OFFS]
+       ldp     x2, x3, [x28, #SMC_PARAM_X2_OFFS]
+       ldp     x4, x5, [x28, #SMC_PARAM_X4_OFFS]
+       ldp     x6, x7, [x28, #SMC_PARAM_X6_OFFS]
+       smc     #0
+       stp     x0, x1, [x28, #SMC_PARAM_X0_OFFS]
+       stp     x2, x3, [x28, #SMC_PARAM_X2_OFFS]
+       ldp     x28, x30, [sp], #16
+       ret
+ENDPROC(tee_smc_call)
diff --git a/security/optee_linuxdriver/armtz/tee_tz_drv.c b/security/optee_linuxdriver/armtz/tee_tz_drv.c
new file mode 100644 (file)
index 0000000..6541b02
--- /dev/null
@@ -0,0 +1,1369 @@
+/*
+ * Copyright (c) 2014, STMicroelectronics International N.V.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License Version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ */
+/* #define DEBUG */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/sched.h>
+#include <linux/jiffies.h>
+
+#include <linux/tee_core.h>
+#include <linux/tee_ioc.h>
+
+#include <tee_shm.h>
+#include <tee_supp_com.h>
+#include <tee_mutex_wait.h>
+#include <tee_wait_queue.h>
+
+#include <arm_common/teesmc.h>
+#include <arm_common/teesmc_st.h>
+
+#include "tee_mem.h"
+#include "tee_tz_op.h"
+#include "tee_tz_priv.h"
+#include "handle.h"
+
+#define _TEE_TZ_NAME "armtz"
+#define DEV (ptee->tee->dev)
+
+/* #define TEE_STRESS_OUTERCACHE_FLUSH */
+
+/* magic config: bit 1 is set, Secure TEE shall handler NSec IRQs */
+#define SEC_ROM_NO_FLAG_MASK   0x0000
+#define SEC_ROM_IRQ_ENABLE_MASK        0x0001
+#define SEC_ROM_DEFAULT                SEC_ROM_IRQ_ENABLE_MASK
+#define TEE_RETURN_BUSY                0x3
+#define ALLOC_ALIGN            SZ_4K
+
+#define CAPABLE(tee) !(tee->conf & TEE_CONF_FW_NOT_CAPABLE)
+
+static struct tee_tz *tee_tz;
+
+static struct handle_db shm_handle_db = HANDLE_DB_INITIALIZER;
+
+
+/* Temporary workaround until we're only using post 3.13 kernels */
+#ifdef ioremap_cached
+#define ioremap_cache  ioremap_cached
+#endif
+
+
+/*******************************************************************
+ * Calling TEE
+ *******************************************************************/
+
+static void e_lock_teez(struct tee_tz *ptee)
+{
+       mutex_lock(&ptee->mutex);
+}
+
+static void e_lock_wait_completion_teez(struct tee_tz *ptee)
+{
+       /*
+        * Release the lock until "something happens" and then reacquire it
+        * again.
+        *
+        * This is needed when TEE returns "busy" and we need to try again
+        * later.
+        */
+       ptee->c_waiters++;
+       mutex_unlock(&ptee->mutex);
+       /*
+        * Wait at most one second. Secure world is normally never busy
+        * more than that so we should normally never timeout.
+        */
+       wait_for_completion_timeout(&ptee->c, HZ);
+       mutex_lock(&ptee->mutex);
+       ptee->c_waiters--;
+}
+
+static void e_unlock_teez(struct tee_tz *ptee)
+{
+       /*
+        * If at least one thread is waiting for "something to happen" let
+        * one thread know that "something has happened".
+        */
+       if (ptee->c_waiters)
+               complete(&ptee->c);
+       mutex_unlock(&ptee->mutex);
+}
+
+static void handle_rpc_func_cmd_mutex_wait(struct tee_tz *ptee,
+                                               struct teesmc32_arg *arg32)
+{
+       struct teesmc32_param *params;
+
+       if (arg32->num_params != 2)
+               goto bad;
+
+       params = TEESMC32_GET_PARAMS(arg32);
+
+       if ((params[0].attr & TEESMC_ATTR_TYPE_MASK) !=
+                       TEESMC_ATTR_TYPE_VALUE_INPUT)
+               goto bad;
+       if ((params[1].attr & TEESMC_ATTR_TYPE_MASK) !=
+                       TEESMC_ATTR_TYPE_VALUE_INPUT)
+               goto bad;
+
+       switch (params[0].u.value.a) {
+       case TEE_MUTEX_WAIT_SLEEP:
+               tee_mutex_wait_sleep(DEV, &ptee->mutex_wait,
+                                    params[1].u.value.a,
+                                    params[1].u.value.b);
+               break;
+       case TEE_MUTEX_WAIT_WAKEUP:
+               tee_mutex_wait_wakeup(DEV, &ptee->mutex_wait,
+                                     params[1].u.value.a,
+                                     params[1].u.value.b);
+               break;
+       case TEE_MUTEX_WAIT_DELETE:
+               tee_mutex_wait_delete(DEV, &ptee->mutex_wait,
+                                     params[1].u.value.a);
+               break;
+       default:
+               goto bad;
+       }
+
+       arg32->ret = TEEC_SUCCESS;
+       return;
+bad:
+       arg32->ret = TEEC_ERROR_BAD_PARAMETERS;
+}
+
+static void handle_rpc_func_cmd_wait_queue(struct tee_tz *ptee,
+                                               struct teesmc32_arg *arg32)
+{
+       struct teesmc32_param *params;
+
+       if (arg32->num_params != 2)
+               goto bad;
+
+       params = TEESMC32_GET_PARAMS(arg32);
+
+       if ((params[0].attr & TEESMC_ATTR_TYPE_MASK) !=
+                       TEESMC_ATTR_TYPE_VALUE_INPUT)
+               goto bad;
+       if ((params[1].attr & TEESMC_ATTR_TYPE_MASK) !=
+                       TEESMC_ATTR_TYPE_NONE)
+               goto bad;
+
+       switch (arg32->cmd) {
+       case TEE_RPC_WAIT_QUEUE_SLEEP:
+               tee_wait_queue_sleep(DEV, &ptee->wait_queue,
+                                    params[0].u.value.a);
+               break;
+       case TEE_RPC_WAIT_QUEUE_WAKEUP:
+               tee_wait_queue_wakeup(DEV, &ptee->wait_queue,
+                                     params[0].u.value.a);
+               break;
+       default:
+               goto bad;
+       }
+
+       arg32->ret = TEEC_SUCCESS;
+       return;
+bad:
+       arg32->ret = TEEC_ERROR_BAD_PARAMETERS;
+}
+
+
+
+static void handle_rpc_func_cmd_wait(struct teesmc32_arg *arg32)
+{
+       struct teesmc32_param *params;
+       u32 msec_to_wait;
+
+       if (arg32->num_params != 1)
+               goto bad;
+
+       params = TEESMC32_GET_PARAMS(arg32);
+       msec_to_wait = params[0].u.value.a;
+
+       /* set task's state to interruptible sleep */
+       set_current_state(TASK_INTERRUPTIBLE);
+
+       /* take a nap */
+       schedule_timeout(msecs_to_jiffies(msec_to_wait));
+
+       arg32->ret = TEEC_SUCCESS;
+       return;
+bad:
+       arg32->ret = TEEC_ERROR_BAD_PARAMETERS;
+}
+
+static void handle_rpc_func_cmd_to_supplicant(struct tee_tz *ptee,
+                                               struct teesmc32_arg *arg32)
+{
+       struct teesmc32_param *params;
+       struct tee_rpc_invoke inv;
+       size_t n;
+       uint32_t ret;
+
+       if (arg32->num_params > TEE_RPC_BUFFER_NUMBER) {
+               arg32->ret = TEEC_ERROR_GENERIC;
+               return;
+       }
+
+       params = TEESMC32_GET_PARAMS(arg32);
+
+       memset(&inv, 0, sizeof(inv));
+       inv.cmd = arg32->cmd;
+       /*
+        * Set a suitable error code in case tee-supplicant
+        * ignores the request.
+        */
+       inv.res = TEEC_ERROR_NOT_IMPLEMENTED;
+       inv.nbr_bf = arg32->num_params;
+       for (n = 0; n < arg32->num_params; n++) {
+               switch (params[n].attr & TEESMC_ATTR_TYPE_MASK) {
+               case TEESMC_ATTR_TYPE_VALUE_INPUT:
+               case TEESMC_ATTR_TYPE_VALUE_INOUT:
+                       inv.cmds[n].fd = (int)params[n].u.value.a;
+                       /* Fall through */
+               case TEESMC_ATTR_TYPE_VALUE_OUTPUT:
+                       inv.cmds[n].type = TEE_RPC_VALUE;
+                       break;
+               case TEESMC_ATTR_TYPE_MEMREF_INPUT:
+               case TEESMC_ATTR_TYPE_MEMREF_OUTPUT:
+               case TEESMC_ATTR_TYPE_MEMREF_INOUT:
+                       inv.cmds[n].buffer =
+                               (void *)(uintptr_t)params[n].u.memref.buf_ptr;
+                       inv.cmds[n].size = params[n].u.memref.size;
+                       inv.cmds[n].type = TEE_RPC_BUFFER;
+                       break;
+               default:
+                       arg32->ret = TEEC_ERROR_GENERIC;
+                       return;
+               }
+       }
+
+       ret = tee_supp_cmd(ptee->tee, TEE_RPC_ICMD_INVOKE,
+                                 &inv, sizeof(inv));
+       if (ret == TEEC_RPC_OK)
+               arg32->ret = inv.res;
+
+       for (n = 0; n < arg32->num_params; n++) {
+               switch (params[n].attr & TEESMC_ATTR_TYPE_MASK) {
+               case TEESMC_ATTR_TYPE_MEMREF_OUTPUT:
+               case TEESMC_ATTR_TYPE_MEMREF_INOUT:
+                       /*
+                        * Allow supplicant to assign a new pointer
+                        * to an out-buffer. Needed when the
+                        * supplicant allocates a new buffer, for
+                        * instance when loading a TA.
+                        */
+                       params[n].u.memref.buf_ptr =
+                                       (uint32_t)(uintptr_t)inv.cmds[n].buffer;
+                       params[n].u.memref.size = inv.cmds[n].size;
+                       break;
+               case TEESMC_ATTR_TYPE_VALUE_OUTPUT:
+               case TEESMC_ATTR_TYPE_VALUE_INOUT:
+                       params[n].u.value.a = inv.cmds[n].fd;
+                       break;
+               default:
+                       break;
+               }
+       }
+}
+
+static void handle_rpc_func_cmd(struct tee_tz *ptee, u32 parg32)
+{
+       struct teesmc32_arg *arg32;
+
+       arg32 = tee_shm_pool_p2v(DEV, ptee->shm_pool, parg32);
+       if (!arg32)
+               return;
+
+       switch (arg32->cmd) {
+       case TEE_RPC_MUTEX_WAIT:
+               handle_rpc_func_cmd_mutex_wait(ptee, arg32);
+               break;
+       case TEE_RPC_WAIT_QUEUE_SLEEP:
+       case TEE_RPC_WAIT_QUEUE_WAKEUP:
+               handle_rpc_func_cmd_wait_queue(ptee, arg32);
+               break;
+       case TEE_RPC_WAIT:
+               handle_rpc_func_cmd_wait(arg32);
+               break;
+       default:
+               handle_rpc_func_cmd_to_supplicant(ptee, arg32);
+       }
+}
+
+static struct tee_shm *handle_rpc_alloc(struct tee_tz *ptee, size_t size)
+{
+       struct tee_rpc_alloc rpc_alloc;
+
+       rpc_alloc.size = size;
+       tee_supp_cmd(ptee->tee, TEE_RPC_ICMD_ALLOCATE,
+                    &rpc_alloc, sizeof(rpc_alloc));
+       return rpc_alloc.shm;
+}
+
+static void handle_rpc_free(struct tee_tz *ptee, struct tee_shm *shm)
+{
+       struct tee_rpc_free rpc_free;
+
+       if (!shm)
+               return;
+       rpc_free.shm = shm;
+       tee_supp_cmd(ptee->tee, TEE_RPC_ICMD_FREE, &rpc_free, sizeof(rpc_free));
+}
+
+static u32 handle_rpc(struct tee_tz *ptee, struct smc_param *param)
+{
+       struct tee_shm *shm;
+       int cookie;
+
+       switch (TEESMC_RETURN_GET_RPC_FUNC(param->a0)) {
+       case TEESMC_RPC_FUNC_ALLOC_ARG:
+               param->a1 = tee_shm_pool_alloc(DEV, ptee->shm_pool,
+                                       param->a1, 4);
+               break;
+       case TEESMC_RPC_FUNC_ALLOC_PAYLOAD:
+               /* Can't support payload shared memory with this interface */
+               param->a2 = 0;
+               break;
+       case TEESMC_RPC_FUNC_FREE_ARG:
+               tee_shm_pool_free(DEV, ptee->shm_pool, param->a1, 0);
+               break;
+       case TEESMC_RPC_FUNC_FREE_PAYLOAD:
+               /* Can't support payload shared memory with this interface */
+               break;
+       case TEESMC_ST_RPC_FUNC_ALLOC_PAYLOAD:
+               shm = handle_rpc_alloc(ptee, param->a1);
+               if (IS_ERR_OR_NULL(shm)) {
+                       param->a1 = 0;
+                       break;
+               }
+               cookie = handle_get(&shm_handle_db, shm);
+               if (cookie < 0) {
+                       handle_rpc_free(ptee, shm);
+                       param->a1 = 0;
+                       break;
+               }
+               param->a1 = shm->paddr;
+               param->a2 = cookie;
+               break;
+       case TEESMC_ST_RPC_FUNC_FREE_PAYLOAD:
+               shm = handle_put(&shm_handle_db, param->a1);
+               handle_rpc_free(ptee, shm);
+               break;
+       case TEESMC_RPC_FUNC_IRQ:
+               break;
+       case TEESMC_RPC_FUNC_CMD:
+               handle_rpc_func_cmd(ptee, param->a1);
+               break;
+       default:
+               dev_warn(DEV, "Unknown RPC func 0x%x\n",
+                        (u32)TEESMC_RETURN_GET_RPC_FUNC(param->a0));
+               break;
+       }
+
+       if (irqs_disabled())
+               return TEESMC32_FASTCALL_RETURN_FROM_RPC;
+       else
+               return TEESMC32_CALL_RETURN_FROM_RPC;
+}
+
+static void call_tee(struct tee_tz *ptee,
+                       uintptr_t parg32, struct teesmc32_arg *arg32)
+{
+       u32 ret;
+       u32 funcid;
+       struct smc_param param = { 0 };
+
+       if (irqs_disabled())
+               funcid = TEESMC32_FASTCALL_WITH_ARG;
+       else
+               funcid = TEESMC32_CALL_WITH_ARG;
+
+       /*
+        * Commented out elements used to visualize the layout dynamic part
+        * of the struct. Note that these fields are not available at all
+        * if num_params == 0.
+        *
+        * params is accessed through the macro TEESMC32_GET_PARAMS
+        */
+
+       /* struct teesmc32_param params[num_params]; */
+
+
+       param.a1 = parg32;
+       e_lock_teez(ptee);
+       while (true) {
+               param.a0 = funcid;
+
+               tee_smc_call(&param);
+               ret = param.a0;
+
+               if (ret == TEESMC_RETURN_EBUSY) {
+                       /*
+                        * Since secure world returned busy, release the
+                        * lock we had when entering this function and wait
+                        * for "something to happen" (something else to
+                        * exit from secure world and needed resources may
+                        * have become available).
+                        */
+                       e_lock_wait_completion_teez(ptee);
+               } else if (TEESMC_RETURN_IS_RPC(ret)) {
+                       /* Process the RPC. */
+                       e_unlock_teez(ptee);
+                       funcid = handle_rpc(ptee, &param);
+                       e_lock_teez(ptee);
+               } else {
+                       break;
+               }
+       }
+       e_unlock_teez(ptee);
+
+       switch (ret) {
+       case TEESMC_RETURN_UNKNOWN_FUNCTION:
+               break;
+       case TEESMC_RETURN_OK:
+               /* arg32->ret set by secure world */
+               break;
+       default:
+               /* Should not happen */
+               arg32->ret = TEEC_ERROR_COMMUNICATION;
+               arg32->ret_origin = TEEC_ORIGIN_COMMS;
+               break;
+       }
+}
+
+/*******************************************************************
+ * TEE service invoke formating
+ *******************************************************************/
+
+/* allocate tee service argument buffer and return virtual address */
+static void *alloc_tee_arg(struct tee_tz *ptee, unsigned long *p, size_t l)
+{
+       void *vaddr;
+       dev_dbg(DEV, ">\n");
+       BUG_ON(!CAPABLE(ptee->tee));
+
+       if ((p == NULL) || (l == 0))
+               return NULL;
+
+       /* assume a 4 bytes aligned is sufficient */
+       *p = tee_shm_pool_alloc(DEV, ptee->shm_pool, l, ALLOC_ALIGN);
+       if (*p == 0)
+               return NULL;
+
+       vaddr = tee_shm_pool_p2v(DEV, ptee->shm_pool, *p);
+
+       dev_dbg(DEV, "< %p\n", vaddr);
+
+       return vaddr;
+}
+
+/* free tee service argument buffer (from its physical address) */
+static void free_tee_arg(struct tee_tz *ptee, unsigned long p)
+{
+       dev_dbg(DEV, ">\n");
+       BUG_ON(!CAPABLE(ptee->tee));
+
+       if (p)
+               tee_shm_pool_free(DEV, ptee->shm_pool, p, 0);
+
+       dev_dbg(DEV, "<\n");
+}
+
+static uint32_t get_cache_attrs(struct tee_tz *ptee)
+{
+       if (tee_shm_pool_is_cached(ptee->shm_pool))
+               return TEESMC_ATTR_CACHE_DEFAULT << TEESMC_ATTR_CACHE_SHIFT;
+       else
+               return TEESMC_ATTR_CACHE_NONCACHE << TEESMC_ATTR_CACHE_SHIFT;
+}
+
+static uint32_t param_type_teec2teesmc(uint8_t type)
+{
+       switch (type) {
+       case TEEC_NONE:
+               return TEESMC_ATTR_TYPE_NONE;
+       case TEEC_VALUE_INPUT:
+               return TEESMC_ATTR_TYPE_VALUE_INPUT;
+       case TEEC_VALUE_OUTPUT:
+               return TEESMC_ATTR_TYPE_VALUE_OUTPUT;
+       case TEEC_VALUE_INOUT:
+               return TEESMC_ATTR_TYPE_VALUE_INOUT;
+       case TEEC_MEMREF_TEMP_INPUT:
+       case TEEC_MEMREF_PARTIAL_INPUT:
+               return TEESMC_ATTR_TYPE_MEMREF_INPUT;
+       case TEEC_MEMREF_TEMP_OUTPUT:
+       case TEEC_MEMREF_PARTIAL_OUTPUT:
+               return TEESMC_ATTR_TYPE_MEMREF_OUTPUT;
+       case TEEC_MEMREF_WHOLE:
+       case TEEC_MEMREF_TEMP_INOUT:
+       case TEEC_MEMREF_PARTIAL_INOUT:
+               return TEESMC_ATTR_TYPE_MEMREF_INOUT;
+       default:
+               WARN_ON(true);
+               return 0;
+       }
+}
+
+static void set_params(struct tee_tz *ptee,
+               struct teesmc32_param params32[TEEC_CONFIG_PAYLOAD_REF_COUNT],
+               uint32_t param_types,
+               struct tee_data *data)
+{
+       size_t n;
+       struct tee_shm *shm;
+       TEEC_Value *value;
+
+       for (n = 0; n < TEEC_CONFIG_PAYLOAD_REF_COUNT; n++) {
+               uint32_t type = TEEC_PARAM_TYPE_GET(param_types, n);
+
+               params32[n].attr = param_type_teec2teesmc(type);
+               if (params32[n].attr == TEESMC_ATTR_TYPE_NONE)
+                       continue;
+               if (params32[n].attr < TEESMC_ATTR_TYPE_MEMREF_INPUT) {
+                       value = (TEEC_Value *)&data->params[n];
+                       params32[n].u.value.a = value->a;
+                       params32[n].u.value.b = value->b;
+                       continue;
+               }
+               shm = data->params[n].shm;
+               params32[n].attr |= get_cache_attrs(ptee);
+               params32[n].u.memref.buf_ptr = shm->paddr;
+               params32[n].u.memref.size = shm->size_req;
+       }
+}
+
+static void get_params(struct tee_data *data,
+               struct teesmc32_param params32[TEEC_CONFIG_PAYLOAD_REF_COUNT])
+{
+       size_t n;
+       struct tee_shm *shm;
+       TEEC_Value *value;
+
+       for (n = 0; n < TEEC_CONFIG_PAYLOAD_REF_COUNT; n++) {
+               if (params32[n].attr == TEESMC_ATTR_TYPE_NONE)
+                       continue;
+               if (params32[n].attr < TEESMC_ATTR_TYPE_MEMREF_INPUT) {
+                       value = &data->params[n].value;
+                       value->a = params32[n].u.value.a;
+                       value->b = params32[n].u.value.b;
+                       continue;
+               }
+               shm = data->params[n].shm;
+               shm->size_req = params32[n].u.memref.size;
+       }
+}
+
+
+/*
+ * tee_open_session - invoke TEE to open a GP TEE session
+ */
+static int tz_open(struct tee_session *sess, struct tee_cmd *cmd)
+{
+       struct tee *tee;
+       struct tee_tz *ptee;
+       int ret = 0;
+
+       struct teesmc32_arg *arg32;
+       struct teesmc32_param *params32;
+       struct teesmc_meta_open_session *meta;
+       uintptr_t parg32;
+       uintptr_t pmeta;
+       size_t num_meta = 1;
+       uint8_t *ta;
+       TEEC_UUID *uuid;
+
+       BUG_ON(!sess->ctx->tee);
+       BUG_ON(!sess->ctx->tee->priv);
+       tee = sess->ctx->tee;
+       ptee = tee->priv;
+
+       if (cmd->uuid)
+               uuid = cmd->uuid->kaddr;
+       else
+               uuid = NULL;
+
+       dev_dbg(tee->dev, "> ta kaddr %p, uuid=%08x-%04x-%04x\n",
+               (cmd->ta) ? cmd->ta->kaddr : NULL,
+               ((uuid) ? uuid->timeLow : 0xDEAD),
+               ((uuid) ? uuid->timeMid : 0xDEAD),
+               ((uuid) ? uuid->timeHiAndVersion : 0xDEAD));
+
+       if (!CAPABLE(ptee->tee)) {
+               dev_dbg(tee->dev, "< not capable\n");
+               return -EBUSY;
+       }
+
+       /* case ta binary is inside the open request */
+       ta = NULL;
+       if (cmd->ta)
+               ta = cmd->ta->kaddr;
+       if (ta)
+               num_meta++;
+
+       arg32 = alloc_tee_arg(ptee, &parg32, TEESMC32_GET_ARG_SIZE(
+                               TEEC_CONFIG_PAYLOAD_REF_COUNT + num_meta));
+       meta = alloc_tee_arg(ptee, &pmeta, sizeof(*meta));
+
+       if ((arg32 == NULL) || (meta == NULL)) {
+               free_tee_arg(ptee, parg32);
+               free_tee_arg(ptee, pmeta);
+               return TEEC_ERROR_OUT_OF_MEMORY;
+       }
+
+       memset(arg32, 0, sizeof(*arg32));
+       memset(meta, 0, sizeof(*meta));
+       arg32->num_params = TEEC_CONFIG_PAYLOAD_REF_COUNT + num_meta;
+       params32 = TEESMC32_GET_PARAMS(arg32);
+
+       arg32->cmd = TEESMC_CMD_OPEN_SESSION;
+
+       params32[0].u.memref.buf_ptr = pmeta;
+       params32[0].u.memref.size = sizeof(*meta);
+       params32[0].attr = TEESMC_ATTR_TYPE_MEMREF_INPUT |
+                        TEESMC_ATTR_META | get_cache_attrs(ptee);
+
+       if (ta) {
+               params32[1].u.memref.buf_ptr =
+                       tee_shm_pool_v2p(DEV, ptee->shm_pool, cmd->ta->kaddr);
+               params32[1].u.memref.size = cmd->ta->size_req;
+               params32[1].attr = TEESMC_ATTR_TYPE_MEMREF_INPUT |
+                                TEESMC_ATTR_META | get_cache_attrs(ptee);
+       }
+
+       if (uuid != NULL)
+               memcpy(meta->uuid, uuid, TEESMC_UUID_LEN);
+       meta->clnt_login = 0; /* FIXME: is this reliable ? used ? */
+
+       params32 += num_meta;
+       set_params(ptee, params32, cmd->param.type, &cmd->param);
+
+       call_tee(ptee, parg32, arg32);
+
+       get_params(&cmd->param, params32);
+
+       if (arg32->ret != TEEC_ERROR_COMMUNICATION) {
+               sess->sessid = arg32->session;
+               cmd->err = arg32->ret;
+               cmd->origin = arg32->ret_origin;
+       } else
+               ret = -EBUSY;
+
+       free_tee_arg(ptee, parg32);
+       free_tee_arg(ptee, pmeta);
+
+       dev_dbg(DEV, "< %x:%d\n", arg32->ret, ret);
+       return ret;
+}
+
+/*
+ * tee_invoke_command - invoke TEE to invoke a GP TEE command
+ */
+static int tz_invoke(struct tee_session *sess, struct tee_cmd *cmd)
+{
+       struct tee *tee;
+       struct tee_tz *ptee;
+       int ret = 0;
+
+       struct teesmc32_arg *arg32;
+       uintptr_t parg32;
+       struct teesmc32_param *params32;
+
+       BUG_ON(!sess->ctx->tee);
+       BUG_ON(!sess->ctx->tee->priv);
+       tee = sess->ctx->tee;
+       ptee = tee->priv;
+
+       dev_dbg(DEV, "> sessid %x cmd %x type %x\n",
+               sess->sessid, cmd->cmd, cmd->param.type);
+
+       if (!CAPABLE(tee)) {
+               dev_dbg(tee->dev, "< not capable\n");
+               return -EBUSY;
+       }
+
+       arg32 = (typeof(arg32))alloc_tee_arg(ptee, &parg32,
+                       TEESMC32_GET_ARG_SIZE(TEEC_CONFIG_PAYLOAD_REF_COUNT));
+       if (!arg32) {
+               free_tee_arg(ptee, parg32);
+               return TEEC_ERROR_OUT_OF_MEMORY;
+       }
+
+       memset(arg32, 0, sizeof(*arg32));
+       arg32->num_params = TEEC_CONFIG_PAYLOAD_REF_COUNT;
+       params32 = TEESMC32_GET_PARAMS(arg32);
+
+       arg32->cmd = TEESMC_CMD_INVOKE_COMMAND;
+       arg32->session = sess->sessid;
+       arg32->ta_func = cmd->cmd;
+
+       set_params(ptee, params32, cmd->param.type, &cmd->param);
+
+       call_tee(ptee, parg32, arg32);
+
+       get_params(&cmd->param, params32);
+
+       if (arg32->ret != TEEC_ERROR_COMMUNICATION) {
+               cmd->err = arg32->ret;
+               cmd->origin = arg32->ret_origin;
+       } else
+               ret = -EBUSY;
+
+       free_tee_arg(ptee, parg32);
+
+       dev_dbg(DEV, "< %x:%d\n", arg32->ret, ret);
+       return ret;
+}
+
+/*
+ * tee_cancel_command - invoke TEE to cancel a GP TEE command
+ */
+static int tz_cancel(struct tee_session *sess, struct tee_cmd *cmd)
+{
+       struct tee *tee;
+       struct tee_tz *ptee;
+       int ret = 0;
+
+       struct teesmc32_arg *arg32;
+       uintptr_t parg32;
+
+       BUG_ON(!sess->ctx->tee);
+       BUG_ON(!sess->ctx->tee->priv);
+       tee = sess->ctx->tee;
+       ptee = tee->priv;
+
+       dev_dbg(DEV, "cancel on sessid=%08x\n", sess->sessid);
+
+       arg32 = alloc_tee_arg(ptee, &parg32, TEESMC32_GET_ARG_SIZE(0));
+       if (arg32 == NULL) {
+               free_tee_arg(ptee, parg32);
+               return TEEC_ERROR_OUT_OF_MEMORY;
+       }
+
+       memset(arg32, 0, sizeof(*arg32));
+       arg32->cmd = TEESMC_CMD_CANCEL;
+       arg32->session = sess->sessid;
+
+       call_tee(ptee, parg32, arg32);
+
+       if (arg32->ret == TEEC_ERROR_COMMUNICATION)
+               ret = -EBUSY;
+
+       free_tee_arg(ptee, parg32);
+
+       dev_dbg(DEV, "< %x:%d\n", arg32->ret, ret);
+       return ret;
+}
+
+/*
+ * tee_close_session - invoke TEE to close a GP TEE session
+ */
+static int tz_close(struct tee_session *sess)
+{
+       struct tee *tee;
+       struct tee_tz *ptee;
+       int ret = 0;
+
+       struct teesmc32_arg *arg32;
+       uintptr_t parg32;
+
+       BUG_ON(!sess->ctx->tee);
+       BUG_ON(!sess->ctx->tee->priv);
+       tee = sess->ctx->tee;
+       ptee = tee->priv;
+
+       dev_dbg(DEV, "close on sessid=%08x\n", sess->sessid);
+
+       if (!CAPABLE(tee)) {
+               dev_dbg(tee->dev, "< not capable\n");
+               return -EBUSY;
+       }
+
+       arg32 = alloc_tee_arg(ptee, &parg32, TEESMC32_GET_ARG_SIZE(0));
+       if (arg32 == NULL) {
+               free_tee_arg(ptee, parg32);
+               return TEEC_ERROR_OUT_OF_MEMORY;
+       }
+
+       dev_dbg(DEV, "> [%x]\n", sess->sessid);
+
+       memset(arg32, 0, sizeof(*arg32));
+       arg32->cmd = TEESMC_CMD_CLOSE_SESSION;
+       arg32->session = sess->sessid;
+
+       call_tee(ptee, parg32, arg32);
+
+       if (arg32->ret == TEEC_ERROR_COMMUNICATION)
+               ret = -EBUSY;
+
+       free_tee_arg(ptee, parg32);
+
+       dev_dbg(DEV, "< %x:%d\n", arg32->ret, ret);
+       return ret;
+}
+
+static struct tee_shm *tz_alloc(struct tee *tee, size_t size, uint32_t flags)
+{
+       struct tee_shm *shm = NULL;
+       struct tee_tz *ptee;
+       size_t size_aligned;
+       BUG_ON(!tee->priv);
+       ptee = tee->priv;
+
+       dev_dbg(DEV, "%s: s=%d,flags=0x%08x\n", __func__, (int)size, flags);
+
+/*     comment due to #6357
+ *     if ( (flags & ~(tee->shm_flags | TEE_SHM_MAPPED
+ *     | TEE_SHM_TEMP | TEE_SHM_FROM_RPC)) != 0 ) {
+               dev_err(tee->dev, "%s: flag parameter is invalid\n", __func__);
+               return ERR_PTR(-EINVAL);
+       }*/
+
+       size_aligned = ((size / SZ_4K) + 1) * SZ_4K;
+       if (unlikely(size_aligned == 0)) {
+               dev_err(DEV, "[%s] requested size too big\n", __func__);
+               return NULL;
+       }
+
+       shm = devm_kzalloc(tee->dev, sizeof(struct tee_shm), GFP_KERNEL);
+       if (!shm) {
+               dev_err(tee->dev, "%s: kzalloc failed\n", __func__);
+               return ERR_PTR(-ENOMEM);
+       }
+
+       shm->size_alloc = ((size / SZ_4K) + 1) * SZ_4K;
+       shm->size_req = size;
+       shm->paddr = tee_shm_pool_alloc(tee->dev, ptee->shm_pool,
+                                       shm->size_alloc, ALLOC_ALIGN);
+       if (!shm->paddr) {
+               dev_err(tee->dev, "%s: cannot alloc memory, size 0x%lx\n",
+                       __func__, (unsigned long)shm->size_alloc);
+               devm_kfree(tee->dev, shm);
+               return ERR_PTR(-ENOMEM);
+       }
+       shm->kaddr = tee_shm_pool_p2v(tee->dev, ptee->shm_pool, shm->paddr);
+       if (!shm->kaddr) {
+               dev_err(tee->dev, "%s: p2v(%p)=0\n", __func__,
+                       (void *)shm->paddr);
+               tee_shm_pool_free(tee->dev, ptee->shm_pool, shm->paddr, NULL);
+               devm_kfree(tee->dev, shm);
+               return ERR_PTR(-EFAULT);
+       }
+       shm->flags = flags;
+       if (ptee->shm_cached)
+               shm->flags |= TEE_SHM_CACHED;
+
+       dev_dbg(tee->dev, "%s: kaddr=%p, paddr=%p, shm=%p, size %x:%x\n",
+               __func__, shm->kaddr, (void *)shm->paddr, shm,
+               (unsigned int)shm->size_req, (unsigned int)shm->size_alloc);
+
+       return shm;
+}
+
+static void tz_free(struct tee_shm *shm)
+{
+       size_t size;
+       int ret;
+       struct tee *tee;
+       struct tee_tz *ptee;
+
+       BUG_ON(!shm->tee);
+       BUG_ON(!shm->tee->priv);
+       tee = shm->tee;
+       ptee = tee->priv;
+
+       dev_dbg(tee->dev, "%s: shm=%p\n", __func__, shm);
+
+       ret = tee_shm_pool_free(tee->dev, ptee->shm_pool, shm->paddr, &size);
+       if (!ret) {
+               devm_kfree(tee->dev, shm);
+               shm = NULL;
+       }
+}
+
+static int tz_shm_inc_ref(struct tee_shm *shm)
+{
+       struct tee *tee;
+       struct tee_tz *ptee;
+
+       BUG_ON(!shm->tee);
+       BUG_ON(!shm->tee->priv);
+       tee = shm->tee;
+       ptee = tee->priv;
+
+       return tee_shm_pool_incref(tee->dev, ptee->shm_pool, shm->paddr);
+}
+
+/******************************************************************************/
+/*
+static void tee_get_status(struct tee_tz *ptee)
+{
+       TEEC_Result ret;
+       struct tee_msg_send *arg;
+       struct tee_core_status_out *res;
+       unsigned long parg, pres;
+
+       if (!CAPABLE(ptee->tee))
+               return;
+
+       arg = (typeof(arg))alloc_tee_arg(ptee, &parg, sizeof(*arg));
+       res = (typeof(res))alloc_tee_arg(ptee, &pres, sizeof(*res));
+
+       if ((arg == NULL) || (res == NULL)) {
+               dev_err(DEV, "TZ outercache mutex error: alloc shm failed\n");
+               goto out;
+       }
+
+       memset(arg, 0, sizeof(*arg));
+       memset(res, 0, sizeof(*res));
+       arg->service = ISSWAPI_TEE_GET_CORE_STATUS;
+       ret = send_and_wait(ptee, ISSWAPI_TEE_GET_CORE_STATUS, SEC_ROM_DEFAULT,
+                               parg, pres);
+       if (ret != TEEC_SUCCESS) {
+               dev_warn(DEV, "get statuc failed\n");
+               goto out;
+       }
+
+       pr_info("TEETZ Firmware status:\n");
+       pr_info("%s", res->raw);
+
+out:
+       free_tee_arg(ptee, parg);
+       free_tee_arg(ptee, pres);
+}*/
+
+#ifdef CONFIG_OUTER_CACHE
+/*
+ * Synchronised outer cache maintenance support
+ */
+#ifndef CONFIG_ARM_TZ_SUPPORT
+/* weak outer_tz_mutex in case not supported by kernel */
+bool __weak outer_tz_mutex(unsigned long *p)
+{
+       pr_err("weak outer_tz_mutex");
+       if (p != NULL)
+               return false;
+       return true;
+}
+#endif
+
+/* register_outercache_mutex - Negotiate/Disable outer cache shared mutex */
+static int register_outercache_mutex(struct tee_tz *ptee, bool reg)
+{
+       unsigned long *vaddr = NULL;
+       int ret = 0;
+       struct smc_param param;
+       uintptr_t paddr = 0;
+
+       dev_dbg(ptee->tee->dev, ">\n");
+       BUG_ON(!CAPABLE(ptee->tee));
+
+       if ((reg == true) && (ptee->tz_outer_cache_mutex != NULL)) {
+               dev_err(DEV, "outer cache shared mutex already registered\n");
+               return -EINVAL;
+       }
+       if ((reg == false) && (ptee->tz_outer_cache_mutex == NULL))
+               return 0;
+
+       mutex_lock(&ptee->mutex);
+
+       if (reg == false) {
+               vaddr = ptee->tz_outer_cache_mutex;
+               ptee->tz_outer_cache_mutex = NULL;
+               goto out;
+       }
+
+       memset(&param, 0, sizeof(param));
+       param.a0 = TEESMC32_ST_FASTCALL_L2CC_MUTEX;
+       param.a1 = TEESMC_ST_L2CC_MUTEX_GET_ADDR;
+       tee_smc_call(&param);
+
+       if (param.a0 != TEESMC_RETURN_OK) {
+               dev_warn(DEV, "no TZ l2cc mutex service supported\n");
+               goto out;
+       }
+       paddr = param.a2;
+       dev_dbg(DEV, "outer cache shared mutex paddr 0x%lx\n", paddr);
+
+       vaddr = ioremap_cache(paddr, sizeof(u32));
+       if (vaddr == NULL) {
+               dev_warn(DEV, "TZ l2cc mutex disabled: ioremap failed\n");
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       dev_dbg(DEV, "outer cache shared mutex vaddr %p\n", vaddr);
+       if (outer_tz_mutex(vaddr) == false) {
+               dev_warn(DEV, "TZ l2cc mutex disabled: outer cache refused\n");
+               goto out;
+       }
+
+       memset(&param, 0, sizeof(param));
+       param.a0 = TEESMC32_ST_FASTCALL_L2CC_MUTEX;
+       param.a1 = TEESMC_ST_L2CC_MUTEX_ENABLE;
+       tee_smc_call(&param);
+
+       if (param.a0 != TEESMC_RETURN_OK) {
+
+               dev_warn(DEV, "TZ l2cc mutex disabled: TZ enable failed\n");
+               goto out;
+       }
+       ptee->tz_outer_cache_mutex = vaddr;
+
+out:
+       if (ptee->tz_outer_cache_mutex == NULL) {
+               memset(&param, 0, sizeof(param));
+               param.a0 = TEESMC32_ST_FASTCALL_L2CC_MUTEX;
+               param.a1 = TEESMC_ST_L2CC_MUTEX_DISABLE;
+               tee_smc_call(&param);
+               outer_tz_mutex(NULL);
+               if (vaddr)
+                       iounmap(vaddr);
+               dev_dbg(DEV, "outer cache shared mutex disabled\n");
+       }
+
+       mutex_unlock(&ptee->mutex);
+       dev_dbg(DEV, "< teetz outer mutex: ret=%d pa=0x%lX va=0x%p %sabled\n",
+               ret, paddr, vaddr, ptee->tz_outer_cache_mutex ? "en" : "dis");
+       return ret;
+}
+#endif
+
+/* configure_shm - Negotiate Shared Memory configuration with teetz. */
+static int configure_shm(struct tee_tz *ptee)
+{
+       struct smc_param param = { 0 };
+       size_t shm_size = -1;
+       int ret = 0;
+
+       dev_dbg(DEV, ">\n");
+       BUG_ON(!CAPABLE(ptee->tee));
+
+       mutex_lock(&ptee->mutex);
+       param.a0 = TEESMC32_ST_FASTCALL_GET_SHM_CONFIG;
+       tee_smc_call(&param);
+       mutex_unlock(&ptee->mutex);
+
+       if (param.a0 != TEESMC_RETURN_OK) {
+               dev_err(DEV, "shm service not available: %X", (uint)param.a0);
+               ret = -EINVAL;
+               goto out;
+       }
+
+       ptee->shm_paddr = param.a1;
+       shm_size = param.a2;
+       ptee->shm_cached = (bool)param.a3;
+
+       if (ptee->shm_cached)
+               ptee->shm_vaddr = ioremap_cache(ptee->shm_paddr, shm_size);
+       else
+               ptee->shm_vaddr = ioremap_nocache(ptee->shm_paddr, shm_size);
+
+       if (ptee->shm_vaddr == NULL) {
+               dev_err(DEV, "shm ioremap failed\n");
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       ptee->shm_pool = tee_shm_pool_create(DEV, shm_size,
+                                            ptee->shm_vaddr, ptee->shm_paddr);
+
+       if (!ptee->shm_pool) {
+               dev_err(DEV, "shm pool creation failed (%zu)", shm_size);
+               ret = -EINVAL;
+               goto out;
+       }
+
+       if (ptee->shm_cached)
+               tee_shm_pool_set_cached(ptee->shm_pool);
+out:
+       dev_dbg(DEV, "< ret=%d pa=0x%lX va=0x%p size=%zu, %scached",
+               ret, ptee->shm_paddr, ptee->shm_vaddr, shm_size,
+               (ptee->shm_cached == 1) ? "" : "un");
+       return ret;
+}
+
+
+/******************************************************************************/
+
+static int tz_start(struct tee *tee)
+{
+       struct tee_tz *ptee;
+       int ret;
+
+       BUG_ON(!tee || !tee->priv);
+       dev_dbg(tee->dev, ">\n");
+       if (!CAPABLE(tee)) {
+               dev_dbg(tee->dev, "< not capable\n");
+               return -EBUSY;
+       }
+
+       ptee = tee->priv;
+       BUG_ON(ptee->started);
+       ptee->started = true;
+
+       ret = configure_shm(ptee);
+       if (ret)
+               goto exit;
+
+
+#ifdef CONFIG_OUTER_CACHE
+       ret = register_outercache_mutex(ptee, true);
+       if (ret)
+               goto exit;
+#endif
+
+exit:
+       if (ret)
+               ptee->started = false;
+
+       dev_dbg(tee->dev, "< ret=%d dev=%s\n", ret, tee->name);
+       return ret;
+}
+
+static int tz_stop(struct tee *tee)
+{
+       struct tee_tz *ptee;
+
+       BUG_ON(!tee || !tee->priv);
+
+       ptee = tee->priv;
+
+       dev_dbg(tee->dev, "> dev=%s\n", tee->name);
+       if (!CAPABLE(tee)) {
+               dev_dbg(tee->dev, "< not capable\n");
+               return -EBUSY;
+       }
+
+#ifdef CONFIG_OUTER_CACHE
+       register_outercache_mutex(ptee, false);
+#endif
+       tee_shm_pool_destroy(tee->dev, ptee->shm_pool);
+       iounmap(ptee->shm_vaddr);
+       ptee->started = false;
+
+       dev_dbg(tee->dev, "< ret=0 dev=%s\n", tee->name);
+       return 0;
+}
+
+/******************************************************************************/
+
+const struct tee_ops tee_fops = {
+       .type = "tz",
+       .owner = THIS_MODULE,
+       .start = tz_start,
+       .stop = tz_stop,
+       .invoke = tz_invoke,
+       .cancel = tz_cancel,
+       .open = tz_open,
+       .close = tz_close,
+       .alloc = tz_alloc,
+       .free = tz_free,
+       .shm_inc_ref = tz_shm_inc_ref,
+};
+
+static int tz_tee_init(struct platform_device *pdev)
+{
+       int ret = 0;
+
+       struct tee *tee = platform_get_drvdata(pdev);
+       struct tee_tz *ptee = tee->priv;
+
+       tee_tz = ptee;
+
+#if 0
+       /* To replace by a syscall */
+#ifndef CONFIG_ARM_TZ_SUPPORT
+       dev_err(tee->dev,
+               "%s: dev=%s, TZ fw is not loaded: TEE TZ is not supported.\n",
+               __func__, tee->name);
+       tee->conf = TEE_CONF_FW_NOT_CAPABLE;
+       return 0;
+#endif
+#endif
+
+       ptee->started = false;
+       ptee->sess_id = 0xAB000000;
+       mutex_init(&ptee->mutex);
+       init_completion(&ptee->c);
+       ptee->c_waiters = 0;
+
+       tee_wait_queue_init(&ptee->wait_queue);
+       ret = tee_mutex_wait_init(&ptee->mutex_wait);
+
+       if (ret)
+               dev_err(tee->dev, "%s: dev=%s, Secure armv7 failed (%d)\n",
+                               __func__, tee->name, ret);
+       else
+               dev_dbg(tee->dev, "%s: dev=%s, Secure armv7\n",
+                               __func__, tee->name);
+       return ret;
+}
+
+static void tz_tee_deinit(struct platform_device *pdev)
+{
+       struct tee *tee = platform_get_drvdata(pdev);
+       struct tee_tz *ptee = tee->priv;
+
+       if (!CAPABLE(tee))
+               return;
+
+       tee_mutex_wait_exit(&ptee->mutex_wait);
+       tee_wait_queue_exit(&ptee->wait_queue);
+
+       dev_dbg(tee->dev, "%s: dev=%s, Secure armv7 started=%d\n", __func__,
+                tee->name, ptee->started);
+}
+
+static int tz_tee_probe(struct platform_device *pdev)
+{
+       int ret = 0;
+       struct device *dev = &pdev->dev;
+       struct tee *tee;
+       struct tee_tz *ptee;
+
+       pr_info("%s: name=\"%s\", id=%d, pdev_name=\"%s\"\n", __func__,
+               pdev->name, pdev->id, dev_name(dev));
+#ifdef _TEE_DEBUG
+       pr_debug("- dev=%p\n", dev);
+       pr_debug("- dev->parent=%p\n", dev->ctx);
+       pr_debug("- dev->driver=%p\n", dev->driver);
+#endif
+
+       tee = tee_core_alloc(dev, _TEE_TZ_NAME, pdev->id, &tee_fops,
+                            sizeof(struct tee_tz));
+       if (!tee)
+               return -ENOMEM;
+
+       ptee = tee->priv;
+       ptee->tee = tee;
+
+       platform_set_drvdata(pdev, tee);
+
+       ret = tz_tee_init(pdev);
+       if (ret)
+               goto bail0;
+
+       ret = tee_core_add(tee);
+       if (ret)
+               goto bail1;
+
+#ifdef _TEE_DEBUG
+       pr_debug("- tee=%p, id=%d, iminor=%d\n", tee, tee->id,
+                tee->miscdev.minor);
+#endif
+       return 0;
+
+bail1:
+       tz_tee_deinit(pdev);
+bail0:
+       tee_core_free(tee);
+       return ret;
+}
+
+static int tz_tee_remove(struct platform_device *pdev)
+{
+       struct tee *tee = platform_get_drvdata(pdev);
+       struct device *dev = &pdev->dev;
+       /*struct tee_tz *ptee;*/
+
+       pr_info("%s: name=\"%s\", id=%d, pdev_name=\"%s\"\n", __func__,
+               pdev->name, pdev->id, dev_name(dev));
+#ifdef _TEE_DEBUG
+       pr_debug("- tee=%p, id=%d, iminor=%d, name=%s\n",
+                tee, tee->id, tee->miscdev.minor, tee->name);
+#endif
+
+/*     ptee = tee->priv;
+       tee_get_status(ptee);*/
+
+       tz_tee_deinit(pdev);
+       tee_core_del(tee);
+       return 0;
+}
+
+static struct of_device_id tz_tee_match[] = {
+       {
+        .compatible = "stm,armv7sec",
+        },
+       {},
+};
+
+static struct platform_driver tz_tee_driver = {
+       .probe = tz_tee_probe,
+       .remove = tz_tee_remove,
+       .driver = {
+                  .name = "armv7sec",
+                  .owner = THIS_MODULE,
+                  .of_match_table = tz_tee_match,
+                  },
+};
+
+static struct platform_device tz_0_plt_device = {
+       .name = "armv7sec",
+       .id = 0,
+       .dev = {
+/*                              .platform_data = tz_0_tee_data,*/
+               },
+};
+
+static int __init tee_tz_init(void)
+{
+       int rc;
+
+       pr_info("TEE armv7 Driver initialization\n");
+
+#ifdef _TEE_DEBUG
+       pr_debug("- Register the platform_driver \"%s\" %p\n",
+                tz_tee_driver.driver.name, &tz_tee_driver.driver);
+#endif
+
+       rc = platform_driver_register(&tz_tee_driver);
+       if (rc != 0) {
+               pr_err("failed to register the platform driver (rc=%d)\n", rc);
+               goto bail0;
+       }
+
+       rc = platform_device_register(&tz_0_plt_device);
+       if (rc != 0) {
+               pr_err("failed to register the platform devices 0 (rc=%d)\n",
+                      rc);
+               goto bail1;
+       }
+
+       return rc;
+
+bail1:
+       platform_driver_unregister(&tz_tee_driver);
+bail0:
+       return rc;
+}
+
+static void __exit tee_tz_exit(void)
+{
+       pr_info("TEE ARMV7 Driver de-initialization\n");
+
+       platform_device_unregister(&tz_0_plt_device);
+       platform_driver_unregister(&tz_tee_driver);
+}
+
+module_init(tee_tz_init);
+module_exit(tee_tz_exit);
+
+MODULE_AUTHOR("STMicroelectronics");
+MODULE_DESCRIPTION("STM Secure TEE ARMV7 TZ driver");
+MODULE_SUPPORTED_DEVICE("");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("1.0");
diff --git a/security/optee_linuxdriver/armtz/tee_tz_op.h b/security/optee_linuxdriver/armtz/tee_tz_op.h
new file mode 100644 (file)
index 0000000..7f8f0af
--- /dev/null
@@ -0,0 +1,270 @@
+/*
+ * Copyright (c) 2014, STMicroelectronics International N.V.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License Version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ */
+#ifndef __TEE_ARMV7_OP_H__
+#define __TEE_ARMV7_OP_H__
+
+enum t_issw_service_id {
+       /*
+        * ("SSAPI_PRE_INIT_SERV")
+        */
+       SSAPI_PRE_INIT_SERV = 1,
+
+       /*
+        * ("SSAPI_POST_SPEEDUP_INIT_SERV")
+        * Reserved, Not used
+        */
+       SSAPI_POST_SPEEDUP_INIT_SERV = 2,
+
+       /*
+        * ("SSAPI_ISSW_IMPORT_SERV")
+        */
+       SSAPI_ISSW_IMPORT_SERV = 3,
+
+       /*
+        * ("SSAPI_RET_FROM_INT_SERV")
+        */
+       SSAPI_RET_FROM_INT_SERV = 4,
+
+       /*
+        * ("SSAPI_RET_FROM_RPC_SERV")
+        */
+       SSAPI_RET_FROM_RPC_SERV = 5,
+
+       /*
+        * "ISSWAPI_ISSW_EXECUTE_SERV" is linked to ROM code
+        * ("SSAPI_ISSW_EXECUTE_SERV")
+        */
+       ISSWAPI_ISSW_EXECUTE_SERV = 6,
+       ISSWAPI_PROT_APPL_MSG_SEND = 0x10000000,
+       ISSWAPI_EXTERNAL_CODE_CHECK = 0x10000001,
+       ISSWAPI_SECURE_LOAD = 0x10000002,
+       ISSWAPI_ISSW_REIMPORT_PUB_KEYS = 0x10000003,
+
+       /* Accessible only on request */
+       ISSWAPI_WRITE_L2CC = 0x10000004,
+       ISSWAPI_WRITE_CP15_SCTLR = 0x10000005,
+       ISSWAPI_READ_CP15_SCTLR = 0x10000006,
+       ISSWAPI_WRITE_CP15_ACTLR = 0x10000007,
+       ISSWAPI_READ_CP15_ACTLR = 0x10000008,
+       ISSWAPI_WRITE_CP15_DIAGR = 0x10000009,
+       ISSWAPI_READ_CP15_DIAGR = 0x1000000A,
+
+       ISSWAPI_EXECUTE_TA = 0x11000001,
+       ISSWAPI_CLOSE_TA = 0x11000002,
+       ISSWAPI_FLUSH_BOOT_CODE = 0x11000003,
+       /* Generic, restricted to be used by u-boot */
+       ISSWAPI_VERIFY_SIGNED_HEADER = 0x11000005,
+       ISSWAPI_VERIFY_HASH = 0x11000006,
+       /* 8500 only, restricted to be used by u-boot */
+       ISSWAPI_GET_RT_FLAGS = 0x11000007,
+
+       /* For TEE Client API 1.0 */
+       ISSWAPI_TEEC_OPEN_SESSION = 0x11000008,
+       ISSWAPI_TEEC_CLOSE_SESSION = 0x11000009,
+       ISSWAPI_TEEC_INVOKE_COMMAND = 0x1100000a,
+       ISSWAPI_REGISTER_RPC = 0x1100000b,      /* this is NOT a GP TEE API ! */
+       ISSWAPI_SET_SEC_DDR = 0x1100000c,       /* this is NOT a GP TEE API ! */
+       ISSWAPI_TEEC_CANCEL_COMMAND = 0x1100000d,
+       ISSWAPI_TEEC_REGISTER_MEMORY = 0x1100000e,
+       ISSWAPI_TEEC_UNREGISTER_MEMORY = 0x1100000f,
+
+       /* Internal command */
+       ISSWAPI_TEE_DEINIT_CPU = 0x11000010,
+       ISSWAPI_TEE_CRASH_CPU = 0x11000011,
+       ISSWAPI_TEE_SET_CORE_TRACE_LEVEL = 0x11000012,
+       ISSWAPI_TEE_GET_CORE_TRACE_LEVEL = 0x11000013,
+       ISSWAPI_TEE_SET_TA_TRACE_LEVEL = 0x11000014,
+       ISSWAPI_TEE_GET_TA_TRACE_LEVEL = 0x11000015,
+       ISSWAPI_TEE_GET_CORE_STATUS = 0x11000016,
+       ISSWAPI_TEE_FLUSH_CACHE = 0x11000017,
+
+       ISSWAPI_REGISTER_DEF_SHM = 0x11000020,
+       ISSWAPI_UNREGISTER_DEF_SHM = 0x11000021,
+       ISSWAPI_REGISTER_IRQFWD = 0x11000022,
+       ISSWAPI_UNREGISTER_IRQFWD = 0x11000023,
+       ISSWAPI_GET_SHM_START = 0x11000024,
+       ISSWAPI_GET_SHM_SIZE = 0x11000025,
+       ISSWAPI_GET_SHM_CACHED = 0x11000026,
+
+       ISSWAPI_ENABLE_L2CC_MUTEX = 0x20000000,
+       ISSWAPI_DISABLE_L2CC_MUTEX = 0x20000001,
+       ISSWAPI_GET_L2CC_MUTEX = 0x20000002,
+       ISSWAPI_SET_L2CC_MUTEX = 0x20000003,
+
+       ISSWAPI_LOAD_TEE = 0x20000004,
+
+};
+
+/*
+ * tee_msg_send - generic part of the msg sent to the TEE
+ */
+struct tee_msg_send {
+       unsigned int service;
+};
+
+/*
+ * tee_msg_recv - default strcutre of TEE service output message
+ */
+struct tee_msg_recv {
+       int duration;
+       uint32_t res;
+       uint32_t origin;
+};
+
+/*
+ * tee_register_irqfwd_xxx - (un)register callback for interrupt forwarding
+ */
+struct tee_register_irqfwd_send {
+       struct tee_msg_send header;
+       struct {
+               unsigned long cb;
+       } data;
+};
+struct tee_register_irqfwd_recv {
+       struct tee_msg_recv header;
+};
+
+/*
+ * tee_get_l2cc_mutex - input/output argument structures
+ */
+struct tee_get_l2cc_mutex_send {
+       struct tee_msg_send header;
+};
+struct tee_get_l2cc_mutex_recv {
+       struct tee_msg_recv header;
+       struct {
+               unsigned long paddr;
+       } data;
+};
+
+/**
+ * struct tee_identity - Represents the identity of the client
+ * @login: Login id
+ * @uuid: UUID as defined above
+ */
+struct tee_identity {
+       uint32_t login;
+       TEEC_UUID uuid;
+};
+
+/*
+ * tee_open_session_data - input arg structure for TEE open session service
+ */
+struct tee_open_session_data {
+       struct ta_signed_header_t *ta;
+       TEEC_UUID uuid;
+       uint32_t param_types;
+       TEEC_Value params[TEEC_CONFIG_PAYLOAD_REF_COUNT];
+       struct tee_identity client_id;
+       uint32_t params_flags[TEEC_CONFIG_PAYLOAD_REF_COUNT];
+};
+
+/*
+ * tee_open_session_send - input arg msg for TEE open session service
+ */
+struct tee_open_session_send {
+       struct tee_msg_send header;
+       struct tee_open_session_data data;
+};
+
+/*
+ * tee_open_session_recv - output arg structure for TEE open session service
+ */
+struct tee_open_session_recv {
+       struct tee_msg_recv header;
+       uint32_t sess;
+       TEEC_Value params[TEEC_CONFIG_PAYLOAD_REF_COUNT];
+};
+
+/*
+ * tee_invoke_command_data - input arg structure for TEE invoke cmd service
+ */
+struct tee_invoke_command_data {
+       uint32_t sess;
+       uint32_t cmd;
+       uint32_t param_types;
+       TEEC_Value params[TEEC_CONFIG_PAYLOAD_REF_COUNT];
+       uint32_t params_flags[TEEC_CONFIG_PAYLOAD_REF_COUNT];
+};
+
+struct tee_invoke_command_send {
+       struct tee_msg_send header;
+       struct tee_invoke_command_data data;
+};
+
+/*
+ * tee_invoke_command_recv - output arg structure for TEE invoke cmd service
+ */
+struct tee_invoke_command_recv {
+       struct tee_msg_recv header;
+       TEEC_Value params[TEEC_CONFIG_PAYLOAD_REF_COUNT];
+};
+
+/*
+ * tee_cancel_command_data - input arg structure for TEE cancel service
+ */
+struct tee_cancel_command_data {
+       uint32_t sess;
+};
+
+/*
+ * tee_cancel_command_send - input msg structure for TEE cancel service
+ */
+struct tee_cancel_command_send {
+       struct tee_msg_send header;
+       struct tee_cancel_command_data data;
+};
+
+/*
+ * tee_close_session_data - input arg structure for TEE close session service
+ */
+struct tee_close_session_data {
+       uint32_t sess;
+};
+
+/*
+ * tee_close_session_send - input arg msg for TEE close session service
+ */
+struct tee_close_session_send {
+       struct tee_msg_send header;
+       struct tee_close_session_data data;
+};
+
+/*
+ * tee_register_rpc_send_data - input arg structure for TEE register rpc service
+ */
+struct tee_register_rpc_send_data {
+       uint32_t fnk;
+       uint32_t bf;
+       uint32_t nbr_bf;
+};
+
+/*
+ * tee_register_rpc_send - input msg structure for TEE register rpc service
+ */
+struct tee_register_rpc_send {
+       struct tee_msg_send header;
+       struct tee_register_rpc_send_data data;
+};
+
+/*
+ * tee_core_status_out - output arg structure for TEE status service
+ */
+#define TEEC_STATUS_MSG_SIZE 80
+
+struct tee_core_status_out {
+       struct tee_msg_recv header;
+       char raw[TEEC_STATUS_MSG_SIZE];
+};
+
+#endif /* __TEE_ARMV7_OP_H__ */
diff --git a/security/optee_linuxdriver/armtz/tee_tz_priv.h b/security/optee_linuxdriver/armtz/tee_tz_priv.h
new file mode 100644 (file)
index 0000000..707c72e
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2014, STMicroelectronics International N.V.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License Version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ */
+#ifndef __TEE_TZ_PRIV__
+#define __TEE_TZ_PRIV__
+
+struct tee;
+struct shm_pool;
+struct tee_rpc_bf;
+
+#ifdef CONFIG_ARM
+struct smc_param {
+       uint32_t a0;
+       uint32_t a1;
+       uint32_t a2;
+       uint32_t a3;
+       uint32_t a4;
+       uint32_t a5;
+       uint32_t a6;
+       uint32_t a7;
+};
+#endif
+#ifdef CONFIG_ARM64
+struct smc_param {
+       uint64_t a0;
+       uint64_t a1;
+       uint64_t a2;
+       uint64_t a3;
+       uint64_t a4;
+       uint64_t a5;
+       uint64_t a6;
+       uint64_t a7;
+};
+#endif
+
+struct tee_tz {
+       uint32_t sess_id;
+       bool started;
+       struct tee *tee;
+       unsigned long shm_paddr;
+       void *shm_vaddr;
+       struct shm_pool *shm_pool;
+       struct mutex mutex;
+       struct completion c;
+       int c_waiters;
+       void *tz_outer_cache_mutex;
+       struct tee_rpc_bf *rpc_buffers;
+       bool shm_cached;
+       struct tee_mutex_wait_private mutex_wait;
+       struct tee_wait_queue_private wait_queue;
+};
+
+int tee_smc_call(struct smc_param *param);
+
+#endif /* __TEE_TZ_PRIV__ */
diff --git a/security/optee_linuxdriver/core/Makefile b/security/optee_linuxdriver/core/Makefile
new file mode 100644 (file)
index 0000000..86cde19
--- /dev/null
@@ -0,0 +1,33 @@
+CFG_TEE_CORE_CORE_TARGET := armv7
+
+#########################################################################
+# Set Internal Variables                                                #
+# May be modified to match your setup                                   #
+#########################################################################
+CFG_TEE_DRV_DEBUGFS?=0
+CFG_TEE_CORE_LOG_LEVEL?=2
+CFG_TEE_TA_LOG_LEVEL?=2
+
+ccflags-y+=-Werror
+ccflags-y+=-I$(M)/include/linux
+ccflags-y+=-I$(M)/include
+
+ccflags-y+=-DCFG_TEE_DRV_DEBUGFS=${CFG_TEE_DRV_DEBUGFS}
+ccflags-y+=-DCFG_TEE_CORE_LOG_LEVEL=${CFG_TEE_CORE_LOG_LEVEL}
+ccflags-y+=-DCFG_TEE_TA_LOG_LEVEL=${CFG_TEE_TA_LOG_LEVEL}
+
+obj-m += optee.o
+
+optee-objs:=   \
+               tee_core.o  \
+               tee_context.o  \
+               tee_session.o  \
+               tee_shm.o  \
+               tee_supp_com.o  \
+               tee_sysfs.o \
+               tee_debugfs.o \
+               tee_kernel_api.o \
+               tee_mutex_wait.o \
+               tee_wait_queue.o \
+
+
diff --git a/security/optee_linuxdriver/core/tee_context.c b/security/optee_linuxdriver/core/tee_context.c
new file mode 100644 (file)
index 0000000..b86c61a
--- /dev/null
@@ -0,0 +1,317 @@
+/*
+ * Copyright (c) 2014, STMicroelectronics International N.V.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License Version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ */
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/types.h>
+#include <linux/file.h>
+#include <linux/atomic.h>
+#include <linux/uaccess.h>
+#include <linux/sched.h>
+
+#include "tee_shm.h"
+#include "tee_core_priv.h"
+
+
+/**
+ * tee_context_dump -  Dump in a buffer the informations (ctx, sess & shm)
+ *                     associated to a tee.
+ */
+int tee_context_dump(struct tee *tee, char *buff, size_t len)
+{
+       struct list_head *ptr_ctx, *ptr_sess, *ptr_shm;
+       struct tee_context *ctx;
+       struct tee_session *sess;
+       struct tee_shm *shm;
+       int i = 0;
+       int j = 0;
+
+       int pos = 0;
+
+       BUG_ON(!tee);
+
+       if (len < 80 || list_empty(&tee->list_ctx))
+               return 0;
+
+       mutex_lock(&tee->lock);
+
+       list_for_each(ptr_ctx, &tee->list_ctx) {
+               ctx = list_entry(ptr_ctx, struct tee_context, entry);
+
+               pos += sprintf(buff + pos,
+                               "[%02d] ctx=%p (refcount=%d) (usr=%d)",
+                               i, ctx,
+                               (int)atomic_read(&ctx->refcount.
+                                       refcount),
+                               ctx->usr_client);
+               pos += sprintf(buff + pos, "name=\"%s\" (tgid=%d)\n",
+                               ctx->name,
+                               ctx->tgid);
+               if ((len - pos) < 80) {
+                       pos = 0;
+                       goto out;
+               }
+
+               if (list_empty(&ctx->list_sess))
+                       goto out;
+
+               j = 0;
+               list_for_each(ptr_sess, &ctx->list_sess) {
+                       sess = list_entry(ptr_sess,
+                                       struct tee_session,
+                                       entry);
+
+                       pos += sprintf(buff + pos,
+                                       "[%02d.%d] sess=%p sessid=%08x\n",
+                                       i, j, sess,
+                                       sess->sessid);
+
+                       if ((len - pos) < 80) {
+                               pos = 0;
+                               goto out;
+                       }
+
+                       j++;
+               }
+
+               if (list_empty(&ctx->list_shm))
+                       goto out;
+
+               j = 0;
+               list_for_each(ptr_shm, &ctx->list_shm) {
+                       shm = list_entry(ptr_shm, struct tee_shm, entry);
+
+                       pos += sprintf(buff + pos,
+                                       "[%02d.%d] shm=%p paddr=%p kaddr=%p",
+                                       i, j, shm,
+                                       &shm->paddr,
+                                       shm->kaddr);
+                       pos += sprintf(buff + pos,
+                                       " s=%zu(%zu)\n",
+                                       shm->size_req,
+                                       shm->size_alloc);
+                       if ((len - pos) < 80) {
+                               pos = 0;
+                               goto out;
+                       }
+
+                       j++;
+               }
+
+               i++;
+       }
+
+out:
+       mutex_unlock(&tee->lock);
+       return pos;
+}
+
+/**
+ * tee_context_create - Allocate and create a new context.
+ *                     Reference on the back-end is requested.
+ */
+struct tee_context *tee_context_create(struct tee *tee)
+{
+       int ret;
+       struct tee_context *ctx;
+
+       dev_dbg(_DEV(tee), "%s: >\n", __func__);
+
+       ctx = devm_kzalloc(_DEV(tee), sizeof(struct tee_context), GFP_KERNEL);
+       if (!ctx) {
+               dev_err(_DEV(tee), "%s: tee_context allocation failed\n",
+                       __func__);
+               return ERR_PTR(-ENOMEM);
+       }
+
+       kref_init(&ctx->refcount);
+       INIT_LIST_HEAD(&ctx->list_sess);
+       INIT_LIST_HEAD(&ctx->list_shm);
+
+       ctx->tee = tee;
+       snprintf(ctx->name, sizeof(ctx->name), "%s", current->comm);
+       ctx->tgid = current->tgid;
+
+       ret = tee_get(tee);
+       if (ret) {
+               devm_kfree(_DEV(tee), ctx);
+               return ERR_PTR(ret);
+       }
+
+       mutex_lock(&tee->lock);
+       tee_inc_stats(&tee->stats[TEE_STATS_CONTEXT_IDX]);
+       list_add_tail(&ctx->entry, &tee->list_ctx);
+       mutex_unlock(&tee->lock);
+
+       dev_dbg(_DEV(ctx->tee), "%s: < ctx=%p is created\n", __func__, ctx);
+       return ctx;
+}
+
+/**
+ * _tee_context_do_release - Final function to release
+ *                           and free a context.
+ */
+static void _tee_context_do_release(struct kref *kref)
+{
+       struct tee_context *ctx;
+       struct tee *tee;
+
+       ctx = container_of(kref, struct tee_context, refcount);
+
+       BUG_ON(!ctx || !ctx->tee);
+
+       tee = ctx->tee;
+
+       dev_dbg(_DEV(tee), "%s: > ctx=%p\n", __func__, ctx);
+
+       mutex_lock(&tee->lock);
+       tee_dec_stats(&tee->stats[TEE_STATS_CONTEXT_IDX]);
+       list_del(&ctx->entry);
+       mutex_unlock(&tee->lock);
+
+       devm_kfree(_DEV(tee), ctx);
+       tee_put(tee);
+
+       dev_dbg(_DEV(tee), "%s: < ctx=%p is destroyed\n", __func__, ctx);
+}
+
+/**
+ * tee_context_get - Increase the reference count of
+ *                   the context.
+ */
+void tee_context_get(struct tee_context *ctx)
+{
+       BUG_ON(!ctx || !ctx->tee);
+
+       kref_get(&ctx->refcount);
+
+       dev_dbg(_DEV(ctx->tee), "%s: ctx=%p, kref=%d\n", __func__,
+               ctx, (int)atomic_read(&ctx->refcount.refcount));
+}
+
+static int is_in_list(struct tee *tee, struct list_head *entry)
+{
+       int present = 1;
+
+       mutex_lock(&tee->lock);
+       if ((entry->next == LIST_POISON1) && (entry->prev == LIST_POISON2))
+               present = 0;
+       mutex_unlock(&tee->lock);
+       return present;
+}
+
+/**
+ * tee_context_put - Decreases the reference count of
+ *                   the context. If 0, the final
+ *                   release function is called.
+ */
+void tee_context_put(struct tee_context *ctx)
+{
+       struct tee_context *_ctx = ctx;
+       struct tee *tee;
+
+       BUG_ON(!ctx || !ctx->tee);
+       tee = ctx->tee;
+
+       if (!is_in_list(tee, &ctx->entry))
+               return;
+
+       kref_put(&ctx->refcount, _tee_context_do_release);
+
+       dev_dbg(_DEV(tee), "%s: ctx=%p, kref=%d\n", __func__,
+               _ctx, (int)atomic_read(&ctx->refcount.refcount));
+}
+
+/**
+ * tee_context_destroy - Request to destroy a context.
+ */
+void tee_context_destroy(struct tee_context *ctx)
+{
+       struct tee *tee;
+
+       if (!ctx || !ctx->tee)
+               return;
+
+       tee = ctx->tee;
+
+       dev_dbg(_DEV(tee), "%s: ctx=%p\n", __func__, ctx);
+
+       tee_context_put(ctx);
+}
+
+int tee_context_copy_from_client(const struct tee_context *ctx,
+                                void *dest, const void *src, size_t size)
+{
+       int res = 0;
+
+       if (dest && src && (size > 0)) {
+               if (ctx->usr_client)
+                       res = copy_from_user(dest, src, size);
+               else
+                       memcpy(dest, src, size);
+       }
+       return res;
+}
+
+struct tee_shm *tee_context_alloc_shm_tmp(struct tee_context *ctx,
+                                         size_t size, const void *src,
+                                         int type)
+{
+       struct tee_shm *shm;
+
+       type &= (TEEC_MEM_INPUT | TEEC_MEM_OUTPUT);
+
+       shm = tee_shm_alloc(ctx->tee, size,
+                       TEE_SHM_MAPPED | TEE_SHM_TEMP | type);
+       if (IS_ERR_OR_NULL(shm)) {
+               dev_err(_DEV(ctx->tee), "%s: buffer allocation failed (%ld)\n",
+                       __func__, PTR_ERR(shm));
+               return shm;
+       }
+
+       shm->ctx = ctx;
+
+       if (type & TEEC_MEM_INPUT) {
+               if (tee_context_copy_from_client(ctx, shm->kaddr, src, size)) {
+                       dev_err(_DEV(ctx->tee),
+                               "%s: tee_context_copy_from_client failed\n",
+                               __func__);
+                       tee_shm_free(shm);
+                       shm = NULL;
+               }
+       }
+       return shm;
+}
+
+struct tee_shm *tee_context_create_tmpref_buffer(struct tee_context *ctx,
+                                                size_t size,
+                                                const void *buffer, int type)
+{
+       struct tee_shm *shm = NULL;
+       int flags;
+
+       switch (type) {
+       case TEEC_MEMREF_TEMP_OUTPUT:
+               flags = TEEC_MEM_OUTPUT;
+               break;
+       case TEEC_MEMREF_TEMP_INPUT:
+               flags = TEEC_MEM_INPUT;
+               break;
+       case TEEC_MEMREF_TEMP_INOUT:
+               flags = TEEC_MEM_INPUT | TEEC_MEM_OUTPUT;
+               break;
+       default:
+               BUG_ON(1);
+       };
+       shm = tee_context_alloc_shm_tmp(ctx, size, buffer, flags);
+       return shm;
+}
diff --git a/security/optee_linuxdriver/core/tee_core.c b/security/optee_linuxdriver/core/tee_core.c
new file mode 100644 (file)
index 0000000..d337e39
--- /dev/null
@@ -0,0 +1,549 @@
+/*
+ * Copyright (c) 2014, STMicroelectronics International N.V.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License Version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ */
+/* #define DEBUG */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/cdev.h>
+#include <linux/idr.h>
+#include <linux/platform_device.h>
+#include <linux/device.h>
+#include <linux/fs.h>
+#include <linux/file.h>
+#include <linux/uaccess.h>
+#include <asm-generic/ioctl.h>
+#include <linux/sched.h>
+
+#include "linux/tee_core.h"
+#include "linux/tee_ioc.h"
+
+#include "tee_core_priv.h"
+
+#include "tee_sysfs.h"
+#include "tee_debugfs.h"
+#include "tee_shm.h"
+#include "tee_supp_com.h"
+
+#define _TEE_CORE_FW_VER "1:0.1"
+
+static char *_tee_supp_app_name = "tee-supplicant";
+
+/* Store the class misc reference */
+static struct class *misc_class;
+
+static int device_match(struct device *device, const void *devname)
+{
+       struct tee *tee = dev_get_drvdata(device);
+       int ret = strncmp(devname, tee->name, sizeof(tee->name));
+
+       BUG_ON(!tee);
+       if (ret == 0)
+               return 1;
+       else
+               return 0;
+}
+
+/*
+ * For the kernel api.
+ * Get a reference on a device tee from the device needed
+ */
+struct tee *tee_get_tee(const char *devname)
+{
+       struct device *device;
+
+       if (!devname)
+               return NULL;
+       device = class_find_device(misc_class, NULL, devname, device_match);
+       if (!device) {
+               pr_err("%s:%d - can't find device [%s]\n", __func__, __LINE__,
+                      devname);
+               return NULL;
+       }
+
+       return dev_get_drvdata(device);
+}
+
+void tee_inc_stats(struct tee_stats_entry *entry)
+{
+       entry->count++;
+       if (entry->count > entry->max)
+               entry->max = entry->count;
+}
+
+void tee_dec_stats(struct tee_stats_entry *entry)
+{
+       entry->count--;
+}
+
+/**
+ * tee_get - increases refcount of the tee
+ * @tee:       [in]    tee to increase refcount of
+ *
+ * @note: If tee.ops.start() callback function is available,
+ * it is called when refcount is equal at 1.
+ */
+int tee_get(struct tee *tee)
+{
+       int ret = 0;
+
+       BUG_ON(!tee);
+
+       if (atomic_inc_return(&tee->refcount) == 1) {
+               BUG_ON(!try_module_get(tee->ops->owner));
+               dev_dbg(_DEV(tee), "%s: refcount=1 call %s::start()...\n",
+                       __func__, tee->name);
+               get_device(tee->dev);
+               if (tee->ops->start)
+                       ret = tee->ops->start(tee);
+       }
+       if (ret) {
+               put_device(tee->dev);
+               module_put(tee->ops->owner);
+               dev_err(_DEV(tee), "%s: %s::start() failed, err=%d\n",
+                       __func__, tee->name, ret);
+               atomic_dec(&tee->refcount);
+       } else {
+               int count = (int)atomic_read(&tee->refcount);
+
+               dev_dbg(_DEV(tee), "%s: refcount=%d\n", __func__, count);
+               if (count > tee->max_refcount)
+                       tee->max_refcount = count;
+       }
+       return ret;
+}
+
+/**
+ * tee_put - decreases refcount of the tee
+ * @tee:       [in]    tee to reduce refcount of
+ *
+ * @note: If tee.ops.stop() callback function is available,
+ * it is called when refcount is equal at 0.
+ */
+int tee_put(struct tee *tee)
+{
+       int ret = 0;
+       int count;
+
+       BUG_ON(!tee);
+
+       if (atomic_dec_and_test(&tee->refcount)) {
+               dev_dbg(_DEV(tee), "%s: refcount=0 call %s::stop()...\n",
+                       __func__, tee->name);
+               if (tee->ops->stop)
+                       ret = tee->ops->stop(tee);
+               module_put(tee->ops->owner);
+               put_device(tee->dev);
+       }
+       if (ret) {
+               dev_err(_DEV(tee), "%s: %s::stop() has failed, ret=%d\n",
+                       __func__, tee->name, ret);
+       }
+
+       count = (int)atomic_read(&tee->refcount);
+       dev_dbg(_DEV(tee), "%s: refcount=%d\n", __func__, count);
+       return ret;
+}
+
+static int tee_supp_open(struct tee *tee)
+{
+       int ret = 0;
+
+       dev_dbg(_DEV(tee), "%s: appclient=\"%s\" pid=%d\n", __func__,
+               current->comm, current->pid);
+
+       BUG_ON(!tee->rpc);
+
+       if (strncmp(_tee_supp_app_name, current->comm,
+                       strlen(_tee_supp_app_name)) == 0) {
+               if (atomic_add_return(1, &tee->rpc->used) > 1) {
+                       ret = -EBUSY;
+                       dev_err(tee->dev, "%s: ERROR Only one Supplicant is allowed\n",
+                                       __func__);
+                       atomic_sub(1, &tee->rpc->used);
+               }
+       }
+
+       return ret;
+}
+
+static void tee_supp_release(struct tee *tee)
+{
+       dev_dbg(_DEV(tee), "%s: appclient=\"%s\" pid=%d\n", __func__,
+               current->comm, current->pid);
+
+       BUG_ON(!tee->rpc);
+
+       if ((atomic_read(&tee->rpc->used) == 1) &&
+                       (strncmp(_tee_supp_app_name, current->comm,
+                                       strlen(_tee_supp_app_name)) == 0))
+               atomic_sub(1, &tee->rpc->used);
+}
+
+static int tee_ctx_open(struct inode *inode, struct file *filp)
+{
+       struct tee_context *ctx;
+       struct tee *tee;
+       int ret;
+
+       tee = container_of(filp->private_data, struct tee, miscdev);
+
+       BUG_ON(!tee);
+       BUG_ON(tee->miscdev.minor != iminor(inode));
+
+       dev_dbg(_DEV(tee), "%s: > name=\"%s\"\n", __func__, tee->name);
+
+       ret = tee_supp_open(tee);
+       if (ret)
+               return ret;
+
+       ctx = tee_context_create(tee);
+       if (IS_ERR_OR_NULL(ctx))
+               return PTR_ERR(ctx);
+
+       ctx->usr_client = 1;
+       filp->private_data = ctx;
+
+       dev_dbg(_DEV(tee), "%s: < ctx=%p is created\n", __func__, (void *)ctx);
+
+       return 0;
+}
+
+static int tee_ctx_release(struct inode *inode, struct file *filp)
+{
+       struct tee_context *ctx = filp->private_data;
+       struct tee *tee;
+
+       if (!ctx)
+               return -EINVAL;
+
+       BUG_ON(!ctx->tee);
+       tee = ctx->tee;
+       BUG_ON(tee->miscdev.minor != iminor(inode));
+
+       dev_dbg(_DEV(tee), "%s: > ctx=%p\n", __func__, ctx);
+
+       tee_context_destroy(ctx);
+       tee_supp_release(tee);
+
+       dev_dbg(_DEV(tee), "%s: < ctx=%p is destroyed\n", __func__, ctx);
+       return 0;
+}
+
+static int tee_do_create_session(struct tee_context *ctx,
+                                struct tee_cmd_io __user *u_cmd)
+{
+       int ret = -EINVAL;
+       struct tee_cmd_io k_cmd;
+       struct tee *tee;
+
+       tee = ctx->tee;
+       BUG_ON(!ctx->usr_client);
+
+       dev_dbg(_DEV(tee), "%s: >\n", __func__);
+
+       if (copy_from_user(&k_cmd, (void *)u_cmd, sizeof(struct tee_cmd_io))) {
+               dev_err(_DEV(tee), "%s: copy_from_user failed\n", __func__);
+               goto exit;
+       }
+
+       if (k_cmd.fd_sess > 0) {
+               dev_err(_DEV(tee), "%s: invalid fd_sess %d\n", __func__,
+                       k_cmd.fd_sess);
+               goto exit;
+       }
+
+       if ((k_cmd.op == NULL) || (k_cmd.uuid == NULL) ||
+           ((k_cmd.data != NULL) && (k_cmd.data_size == 0)) ||
+           ((k_cmd.data == NULL) && (k_cmd.data_size != 0))) {
+               dev_err(_DEV(tee),
+                       "%s: op or/and data parameters are not valid\n",
+                       __func__);
+               goto exit;
+       }
+
+       ret = tee_session_create_fd(ctx, &k_cmd);
+       put_user(k_cmd.err, &u_cmd->err);
+       put_user(k_cmd.origin, &u_cmd->origin);
+       if (ret)
+               goto exit;
+
+       put_user(k_cmd.fd_sess, &u_cmd->fd_sess);
+
+exit:
+       dev_dbg(_DEV(tee), "%s: < ret=%d, sessfd=%d\n", __func__, ret,
+               k_cmd.fd_sess);
+       return ret;
+}
+
+static int tee_do_shm_alloc(struct tee_context *ctx,
+                           struct tee_shm_io __user *u_shm)
+{
+       int ret = -EINVAL;
+       struct tee_shm_io k_shm;
+       struct tee *tee = ctx->tee;
+
+       BUG_ON(!ctx->usr_client);
+
+       dev_dbg(_DEV(tee), "%s: >\n", __func__);
+
+       if (copy_from_user(&k_shm, (void *)u_shm, sizeof(struct tee_shm_io))) {
+               dev_err(_DEV(tee), "%s: copy_from_user failed\n", __func__);
+               goto exit;
+       }
+
+       if ((k_shm.buffer != NULL) || (k_shm.fd_shm != 0) ||
+           /*(k_shm.flags & ~(tee->shm_flags)) ||*/
+           ((k_shm.flags & tee->shm_flags) == 0) || (k_shm.registered != 0)) {
+               dev_err(_DEV(tee),
+                       "%s: shm parameters are not valid %p %d %08x %08x %d\n",
+                       __func__, (void *)k_shm.buffer, k_shm.fd_shm,
+                       (unsigned int)k_shm.flags, (unsigned int)tee->shm_flags,
+                       k_shm.registered);
+               goto exit;
+       }
+
+       ret = tee_shm_alloc_io(ctx, &k_shm);
+       if (ret)
+               goto exit;
+
+       put_user(k_shm.fd_shm, &u_shm->fd_shm);
+       put_user(k_shm.flags, &u_shm->flags);
+
+exit:
+       dev_dbg(_DEV(tee), "%s: < ret=%d, shmfd=%d\n", __func__, ret,
+               k_shm.fd_shm);
+       return ret;
+}
+
+static int tee_do_get_fd_for_rpc_shm(struct tee_context *ctx,
+                                    struct tee_shm_io __user *u_shm)
+{
+       int ret = -EINVAL;
+       struct tee_shm_io k_shm;
+       struct tee *tee = ctx->tee;
+
+       dev_dbg(_DEV(tee), "%s: >\n", __func__);
+       BUG_ON(!ctx->usr_client);
+
+       if (copy_from_user(&k_shm, (void *)u_shm, sizeof(struct tee_shm_io))) {
+               dev_err(_DEV(tee), "%s: copy_from_user failed\n", __func__);
+               goto exit;
+       }
+
+       if ((k_shm.buffer == NULL) || (k_shm.size == 0) || (k_shm.fd_shm != 0)
+           || (k_shm.flags & ~(tee->shm_flags))
+           || ((k_shm.flags & tee->shm_flags) == 0)
+           || (k_shm.registered != 0)) {
+               dev_err(_DEV(tee), "%s: shm parameters are not valid\n",
+                       __func__);
+               goto exit;
+       }
+
+       ret = tee_shm_fd_for_rpc(ctx, &k_shm);
+       if (ret)
+               goto exit;
+
+       put_user(k_shm.fd_shm, &u_shm->fd_shm);
+
+exit:
+       dev_dbg(_DEV(tee), "%s: < ret=%d, shmfd=%d\n", __func__, ret,
+               k_shm.fd_shm);
+       return ret;
+}
+
+static long tee_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
+{
+       int ret = -EINVAL;
+       struct tee_context *ctx = filp->private_data;
+
+       BUG_ON(!ctx);
+       BUG_ON(!ctx->tee);
+
+       dev_dbg(_DEV(ctx->tee), "%s: > cmd nr=%d\n", __func__, _IOC_NR(cmd));
+
+       switch (cmd) {
+       case TEE_OPEN_SESSION_IOC:
+               ret =
+                   tee_do_create_session(ctx, (struct tee_cmd_io __user *)arg);
+               break;
+       case TEE_ALLOC_SHM_IOC:
+               ret = tee_do_shm_alloc(ctx, (struct tee_shm_io __user *)arg);
+               break;
+       case TEE_GET_FD_FOR_RPC_SHM_IOC:
+               ret =
+                   tee_do_get_fd_for_rpc_shm(ctx,
+                                             (struct tee_shm_io __user *)arg);
+               break;
+       default:
+               ret = -ENOSYS;
+               break;
+       }
+
+       dev_dbg(_DEV(ctx->tee), "%s: < ret=%d\n", __func__, ret);
+
+       return ret;
+}
+
+const struct file_operations tee_fops = {
+       .owner = THIS_MODULE,
+       .read = tee_supp_read,
+       .write = tee_supp_write,
+       .open = tee_ctx_open,
+       .release = tee_ctx_release,
+       .unlocked_ioctl = tee_ioctl
+};
+
+static void tee_plt_device_release(struct device *dev)
+{
+       pr_debug("%s: (dev=%p)....\n", __func__, dev);
+}
+
+struct tee *tee_core_alloc(struct device *dev, char *name, int id,
+                          const struct tee_ops *ops, size_t len)
+{
+       struct tee *tee;
+
+       if (!dev || !name || !ops ||
+           !ops->open || !ops->close || !ops->alloc || !ops->free)
+               return NULL;
+
+       tee = devm_kzalloc(dev, sizeof(struct tee) + len, GFP_KERNEL);
+       if (!tee) {
+               dev_err(dev, "%s: kzalloc failed\n", __func__);
+               return NULL;
+       }
+
+       if (!dev->release)
+               dev->release = tee_plt_device_release;
+
+       tee->dev = dev;
+       tee->id = id;
+       tee->ops = ops;
+       tee->priv = &tee[1];
+
+       snprintf(tee->name, sizeof(tee->name), "optee%s%02d", name, tee->id);
+       pr_info("TEE core: Alloc the misc device \"%s\" (id=%d)\n", tee->name,
+               tee->id);
+
+       tee->miscdev.parent = dev;
+       tee->miscdev.minor = MISC_DYNAMIC_MINOR;
+       tee->miscdev.name = tee->name;
+       tee->miscdev.fops = &tee_fops;
+
+       mutex_init(&tee->lock);
+       atomic_set(&tee->refcount, 0);
+       INIT_LIST_HEAD(&tee->list_ctx);
+       INIT_LIST_HEAD(&tee->list_rpc_shm);
+
+       tee->state = TEE_OFFLINE;
+       tee->shm_flags = TEEC_MEM_INPUT | TEEC_MEM_OUTPUT;
+       tee->test = 0;
+
+       tee_supp_init(tee);
+
+       return tee;
+}
+EXPORT_SYMBOL(tee_core_alloc);
+
+int tee_core_free(struct tee *tee)
+{
+       if (tee) {
+               tee_supp_deinit(tee);
+               devm_kfree(tee->dev, tee);
+       }
+       return 0;
+}
+EXPORT_SYMBOL(tee_core_free);
+
+int tee_core_add(struct tee *tee)
+{
+       int rc = 0;
+
+       if (!tee)
+               return -EINVAL;
+
+       rc = misc_register(&tee->miscdev);
+       if (rc != 0) {
+               pr_err("TEE Core: misc_register() failed name=\"%s\"\n",
+                      tee->name);
+               return rc;
+       }
+
+       dev_set_drvdata(tee->miscdev.this_device, tee);
+
+       tee_init_sysfs(tee);
+       tee_create_debug_dir(tee);
+
+       /* Register a static reference on the class misc
+        * to allow finding device by class */
+       BUG_ON(!tee->miscdev.this_device->class);
+       if (misc_class)
+               BUG_ON(misc_class != tee->miscdev.this_device->class);
+       else
+               misc_class = tee->miscdev.this_device->class;
+
+       pr_info("TEE Core: Register the misc device \"%s\" (id=%d,minor=%d)\n",
+               dev_name(tee->miscdev.this_device), tee->id,
+               tee->miscdev.minor);
+       return rc;
+}
+EXPORT_SYMBOL(tee_core_add);
+
+int tee_core_del(struct tee *tee)
+{
+       if (tee) {
+               pr_info("TEE Core: Destroy the misc device \"%s\" (id=%d)\n",
+                       dev_name(tee->miscdev.this_device), tee->id);
+
+               tee_cleanup_sysfs(tee);
+               tee_delete_debug_dir(tee);
+
+               if (tee->miscdev.minor != MISC_DYNAMIC_MINOR) {
+                       pr_info("TEE Core: Deregister the misc device \"%s\" (id=%d)\n",
+                            dev_name(tee->miscdev.this_device), tee->id);
+                       misc_deregister(&tee->miscdev);
+               }
+       }
+
+       tee_core_free(tee);
+
+       return 0;
+}
+EXPORT_SYMBOL(tee_core_del);
+
+static int __init tee_core_init(void)
+{
+       pr_info("\nTEE Core Framework initialization (ver %s)\n",
+               _TEE_CORE_FW_VER);
+       tee_init_debugfs();
+
+       return 0;
+}
+
+static void __exit tee_core_exit(void)
+{
+       tee_exit_debugfs();
+       pr_info("TEE Core Framework unregistered\n");
+}
+
+module_init(tee_core_init);
+module_exit(tee_core_exit);
+
+MODULE_AUTHOR("STMicroelectronics");
+MODULE_DESCRIPTION("STM Secure TEE Framework/Core TEEC v1.0");
+MODULE_SUPPORTED_DEVICE("");
+MODULE_VERSION(_TEE_CORE_FW_VER);
+MODULE_LICENSE("GPL");
diff --git a/security/optee_linuxdriver/core/tee_core_priv.h b/security/optee_linuxdriver/core/tee_core_priv.h
new file mode 100644 (file)
index 0000000..933f55f
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2014, STMicroelectronics International N.V.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License Version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ */
+#ifndef __TEE_CORE_PRIV_H__
+#define __TEE_CORE_PRIV_H__
+
+#include "linux/tee_core.h"
+#include "linux/tee_ioc.h"
+
+/* from tee_core_module.c */
+int tee_get(struct tee *tee);
+int tee_put(struct tee *tee);
+
+void tee_inc_stats(struct tee_stats_entry *entry);
+void tee_dec_stats(struct tee_stats_entry *entry);
+
+/* from tee_context.c */
+int tee_context_dump(struct tee *tee, char *buff, size_t len);
+
+struct tee_context *tee_context_create(struct tee *tee);
+void tee_context_destroy(struct tee_context *ctx);
+
+void tee_context_get(struct tee_context *ctx);
+void tee_context_put(struct tee_context *ctx);
+
+struct tee_shm *tee_context_create_tmpref_buffer(struct tee_context *ctx,
+                                                size_t size,
+                                                const void *buffer, int type);
+struct tee_shm *tee_context_alloc_shm_tmp(struct tee_context *ctx, size_t size,
+                                         const void *data, int type);
+int tee_context_copy_from_client(const struct tee_context *ctx, void *dest,
+                                const void *src, size_t size);
+
+/* from tee_session.c */
+int tee_session_create_fd(struct tee_context *ctx, struct tee_cmd_io *cmd_io);
+struct tee_session *tee_session_create_and_open(struct tee_context *ctx,
+                                               struct tee_cmd_io *cmd_io);
+int tee_session_close_and_destroy(struct tee_session *sess);
+
+struct tee *tee_get_tee(const char *devname);
+int tee_session_invoke_be(struct tee_session *sess, struct tee_cmd_io *cmd_io);
+
+#endif
diff --git a/security/optee_linuxdriver/core/tee_debugfs.c b/security/optee_linuxdriver/core/tee_debugfs.c
new file mode 100644 (file)
index 0000000..95b44b8
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2014, STMicroelectronics International N.V.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License Version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ */
+#include <linux/kernel.h>
+#include <linux/debugfs.h>
+#include <linux/device.h>
+#include <linux/uaccess.h>
+
+#include "linux/tee_core.h"
+#include "tee_debugfs.h"
+
+static struct dentry *tee_debugfs_dir;
+
+static ssize_t tee_trace_read(struct file *filp, char __user *userbuf,
+                             size_t count, loff_t *ppos)
+{
+       struct tee *tee = filp->private_data;
+
+       char buff[258];
+       int len = sprintf(buff, "device=%s\n NO LOG AVAILABLE\n", tee->name);
+
+       return simple_read_from_buffer(userbuf, count, ppos, buff, len);
+}
+
+static const struct file_operations log_tee_ops = {
+       .read = tee_trace_read,
+       .open = simple_open,
+       .llseek = generic_file_llseek,
+};
+
+void tee_create_debug_dir(struct tee *tee)
+{
+       struct dentry *entry;
+       struct device *dev = tee->miscdev.this_device;
+
+       if (!tee_debugfs_dir)
+               return;
+
+       tee->dbg_dir = debugfs_create_dir(dev_name(dev), tee_debugfs_dir);
+       if (!tee->dbg_dir)
+               goto error_create_file;
+
+       entry = debugfs_create_file("log", S_IRUGO, tee->dbg_dir,
+                                   tee, &log_tee_ops);
+       if (!entry)
+               goto error_create_file;
+
+       return;
+
+error_create_file:
+       dev_err(dev, "can't create debugfs file\n");
+       tee_delete_debug_dir(tee);
+}
+
+void tee_delete_debug_dir(struct tee *tee)
+{
+       if (!tee || !tee->dbg_dir)
+               return;
+
+       debugfs_remove_recursive(tee->dbg_dir);
+}
+
+void __init tee_init_debugfs(void)
+{
+       if (debugfs_initialized()) {
+               tee_debugfs_dir = debugfs_create_dir("tee", NULL);
+               if (IS_ERR(tee_debugfs_dir))
+                       pr_err("can't create debugfs dir\n");
+       }
+}
+
+void __exit tee_exit_debugfs(void)
+{
+       if (tee_debugfs_dir)
+               debugfs_remove(tee_debugfs_dir);
+}
diff --git a/security/optee_linuxdriver/core/tee_debugfs.h b/security/optee_linuxdriver/core/tee_debugfs.h
new file mode 100644 (file)
index 0000000..36f1da7
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2014, STMicroelectronics International N.V.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License Version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ */
+#ifndef __TEE_DEBUGFS_H__
+#define __TEE_DEBUGFS_H__
+
+struct tee;
+
+void tee_create_debug_dir(struct tee *tee);
+void tee_delete_debug_dir(struct tee *tee);
+
+void __init tee_init_debugfs(void);
+void __exit tee_exit_debugfs(void);
+
+#endif /* __TEE_DEBUGFS_H__ */
diff --git a/security/optee_linuxdriver/core/tee_kernel_api.c b/security/optee_linuxdriver/core/tee_kernel_api.c
new file mode 100644 (file)
index 0000000..9da7bc8
--- /dev/null
@@ -0,0 +1,255 @@
+/*
+ * Copyright (c) 2014, STMicroelectronics International N.V.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License Version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ */
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/miscdevice.h>
+#include <linux/io.h>
+#include <linux/vmalloc.h>
+
+#include "linux/tee_kernel_api.h"
+#include "linux/tee_core.h"
+#include "linux/tee_ioc.h"
+
+#include "tee_core_priv.h"
+#include "tee_shm.h"
+#include "tee_supp_com.h"
+
+#define TEE_TZ_DEVICE_NAME     "opteearmtz00"
+
+static void reset_tee_cmd(struct tee_cmd_io *cmd)
+{
+       cmd->fd_sess = -1;
+       cmd->cmd = 0;
+       cmd->uuid = NULL;
+       cmd->origin = TEEC_ORIGIN_API;
+       cmd->err = TEEC_SUCCESS;
+       cmd->data = NULL;
+       cmd->data_size = 0;
+       cmd->op = NULL;
+}
+
+TEEC_Result TEEC_InitializeContext(const char *name, TEEC_Context *context)
+{
+       struct tee *tee;
+       struct tee_context *ctx;
+       pr_cont("%s: > name=\"%s\"\n", __func__, name);
+
+       if (!context)
+               return TEEC_ERROR_BAD_PARAMETERS;
+
+       context->fd = 0;
+
+       if (name == NULL)
+               strncpy(context->devname, TEE_TZ_DEVICE_NAME,
+                       sizeof(context->devname));
+       else
+               strncpy(context->devname, name, sizeof(context->devname));
+
+       tee = tee_get_tee(context->devname);
+       if (!tee) {
+               pr_err("%s - can't get device [%s]\n", __func__, name);
+               return TEEC_ERROR_BAD_PARAMETERS;
+       }
+
+       ctx = tee_context_create(tee);
+       if (IS_ERR_OR_NULL(ctx))
+               return TEEC_ERROR_BAD_PARAMETERS;
+
+       ctx->usr_client = 0;
+
+       /* TODO fixme will not work on 64-bit platform */
+       context->fd = (int)(uintptr_t)ctx;
+       BUG_ON(ctx != (struct tee_context *)(uintptr_t)context->fd);
+
+       pr_cont("%s: < ctx=%p is created\n", __func__, (void *)ctx);
+       return TEEC_SUCCESS;
+}
+EXPORT_SYMBOL(TEEC_InitializeContext);
+
+void TEEC_FinalizeContext(TEEC_Context *context)
+{
+       if (!context || !context->fd) {
+               pr_err("%s - can't release context %p:[%s]\n", __func__,
+                      context, (context
+                                && context->devname) ? context->devname : "");
+               return;
+       }
+       /* TODO fixme will not work on 64-bit platform */
+       tee_context_destroy((struct tee_context *)(uintptr_t)context->fd);
+       return;
+}
+EXPORT_SYMBOL(TEEC_FinalizeContext);
+
+TEEC_Result TEEC_OpenSession(TEEC_Context *context,
+                            TEEC_Session *session,
+                            const TEEC_UUID *destination,
+                            uint32_t connectionMethod,
+                            const void *connectionData,
+                            TEEC_Operation *operation,
+                            uint32_t *return_origin)
+{
+       TEEC_Operation dummy_op;
+       struct tee_cmd_io cmd;
+       struct tee_session *sess;
+       struct tee_context *ctx;
+
+       if (!operation) {
+               /*
+                * The code here exist because Global Platform API states that
+                * it is allowed to give operation as a NULL pointer.
+                * In kernel and secure world we in most cases don't want
+                * this to be NULL, hence we use this dummy operation when
+                * a client doesn't provide any operation.
+                */
+               memset(&dummy_op, 0, sizeof(TEEC_Operation));
+               operation = &dummy_op;
+       }
+
+       if (!context || !session || !destination || !operation
+           || !return_origin)
+               return TEEC_ERROR_BAD_PARAMETERS;
+
+       session->fd = 0;
+
+       /* TODO fixme will not work on 64-bit platform */
+       ctx = (struct tee_context *)(uintptr_t)context->fd;
+       reset_tee_cmd(&cmd);
+       cmd.op = operation;
+       cmd.uuid = (TEEC_UUID *) destination;
+
+       sess = tee_session_create_and_open(ctx, &cmd);
+       if (IS_ERR_OR_NULL(sess)) {
+               if (cmd.origin)
+                       *return_origin = cmd.origin;
+               else
+                       *return_origin = TEEC_ORIGIN_COMMS;
+               if (cmd.err)
+                       return cmd.err;
+               else
+                       return TEEC_ERROR_COMMUNICATION;
+       } else {
+               *return_origin = cmd.origin;
+               /* TODO fixme will not work on 64-bit platform */
+               session->fd = (int)(uintptr_t)sess;
+               BUG_ON(sess != (struct tee_session *)(uintptr_t)session->fd);
+               return cmd.err;
+       }
+}
+EXPORT_SYMBOL(TEEC_OpenSession);
+
+void TEEC_CloseSession(TEEC_Session *session)
+{
+       if (session && session->fd) {
+               /* TODO fixme will not work on 64-bit platform */
+               struct tee_session *sess =
+                       (struct tee_session *)(uintptr_t)session->fd;
+               tee_session_close_and_destroy(sess);
+       }
+}
+EXPORT_SYMBOL(TEEC_CloseSession);
+
+TEEC_Result TEEC_InvokeCommand(TEEC_Session *session,
+                              uint32_t commandID,
+                              TEEC_Operation *operation,
+                              uint32_t *return_origin)
+{
+       int ret = 0;
+       struct tee_cmd_io cmd;
+       struct tee_session *sess;
+
+       if (!session || !operation || !return_origin || !session->fd)
+               return TEEC_ERROR_BAD_PARAMETERS;
+
+       /* TODO fixme will not work on 64-bit platform */
+       sess = (struct tee_session *)(uintptr_t)session->fd;
+       reset_tee_cmd(&cmd);
+       cmd.cmd = commandID;
+       cmd.op = operation;
+
+       ret = tee_session_invoke_be(sess, &cmd);
+       if (ret) {
+               if (cmd.origin)
+                       *return_origin = cmd.origin;
+               else
+                       *return_origin = TEEC_ORIGIN_COMMS;
+               if (cmd.err)
+                       return cmd.err;
+               else
+                       return TEEC_ERROR_COMMUNICATION;
+       } else {
+               *return_origin = cmd.origin;
+               return cmd.err;
+       }
+}
+EXPORT_SYMBOL(TEEC_InvokeCommand);
+
+TEEC_Result TEEC_RegisterSharedMemory(TEEC_Context *context,
+                                     TEEC_SharedMemory *sharedMem)
+{
+       if (!sharedMem)
+               return TEEC_ERROR_BAD_PARAMETERS;
+
+       sharedMem->registered = 1;
+       return TEEC_SUCCESS;
+}
+EXPORT_SYMBOL(TEEC_RegisterSharedMemory);
+
+TEEC_Result TEEC_AllocateSharedMemory(TEEC_Context *context,
+                                     TEEC_SharedMemory *shared_memory)
+{
+       struct tee_shm_io shm_io;
+       int ret;
+       struct tee_shm *shm;
+
+       if (!context || !context->ctx || !shared_memory)
+               return TEEC_ERROR_BAD_PARAMETERS;
+
+       shm_io.size = shared_memory->size;
+       shm_io.flags = shared_memory->flags | TEEC_MEM_KAPI;
+       ret = tee_shm_alloc_io(context->ctx, &shm_io);
+       if (ret) {
+               pr_err("%s: tee_shm_alloc_io(%zd) failed\n", __func__,
+                      shared_memory->size);
+               return TEEC_ERROR_OUT_OF_MEMORY;
+       }
+
+       shared_memory->registered = 0;
+       shared_memory->flags = shm_io.flags;
+       shared_memory->d.fd = shm_io.fd_shm;
+
+       shm = (struct tee_shm *)(long)shm_io.fd_shm;
+       shared_memory->buffer = shm->kaddr;
+
+       pr_debug("%s(%zd) => fd=%d, kaddr=%p\n", __func__,
+                shm_io.size, shm_io.fd_shm, (void *)shared_memory->buffer);
+
+       return TEEC_SUCCESS;
+}
+EXPORT_SYMBOL(TEEC_AllocateSharedMemory);
+
+void TEEC_ReleaseSharedMemory(TEEC_SharedMemory *shared_memory)
+{
+       struct tee_shm *shm;
+
+       if (!shared_memory || shared_memory->registered)
+               return;
+
+       pr_debug("%s (vaddr = %p)\n", __func__, shared_memory->buffer);
+
+       shm = (struct tee_shm *)(long)shared_memory->d.fd;
+       tee_shm_free_io(shm);
+
+       shared_memory->buffer = NULL;
+       shared_memory->d.fd = 0;
+}
+EXPORT_SYMBOL(TEEC_ReleaseSharedMemory);
diff --git a/security/optee_linuxdriver/core/tee_mutex_wait.c b/security/optee_linuxdriver/core/tee_mutex_wait.c
new file mode 100644 (file)
index 0000000..17049f5
--- /dev/null
@@ -0,0 +1,144 @@
+/*
+ * Copyright (c) 2014, Linaro Limited
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License Version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ */
+#include <linux/slab.h>
+#include "tee_mutex_wait.h"
+
+struct tee_mutex_wait {
+       struct list_head link;
+       struct completion comp;
+       struct mutex mu;
+       u32 wait_after;
+       u32 key;
+};
+
+/*
+ * Compares two serial numbers using Serial Number Arithmetic
+ * (https://www.ietf.org/rfc/rfc1982.txt).
+ */
+#define TICK_GT(t1, t2) \
+       (((t1) < (t2) && (t2) - (t1) > 0xFFFFFFFFu) || \
+       ((t1) > (t2) && (t1) - (t2) < 0xFFFFFFFFu))
+
+static struct tee_mutex_wait *tee_mutex_wait_get(struct device *dev,
+                               struct tee_mutex_wait_private *priv, u32 key)
+{
+       struct tee_mutex_wait *w;
+
+       mutex_lock(&priv->mu);
+
+       list_for_each_entry(w, &priv->db, link)
+               if (w->key == key)
+                       goto out;
+
+       w = kmalloc(sizeof(struct tee_mutex_wait), GFP_KERNEL);
+       if (!w) {
+               dev_err(dev, "kmalloc <struct tee_mutex_wait> failed\n");
+               goto out;
+       }
+
+       init_completion(&w->comp);
+       mutex_init(&w->mu);
+       w->wait_after = 0;
+       w->key = key;
+       list_add_tail(&w->link, &priv->db);
+out:
+       mutex_unlock(&priv->mu);
+       return w;
+}
+
+static void tee_mutex_wait_delete_entry(struct tee_mutex_wait *w)
+{
+       list_del(&w->link);
+       mutex_destroy(&w->mu);
+       kfree(w);
+}
+
+void tee_mutex_wait_delete(struct device *dev,
+                       struct tee_mutex_wait_private *priv,
+                       u32 key)
+{
+       struct tee_mutex_wait *w;
+
+       mutex_lock(&priv->mu);
+
+       list_for_each_entry(w, &priv->db, link) {
+               if (w->key == key) {
+                       tee_mutex_wait_delete_entry(w);
+                       break;
+               }
+       }
+
+       mutex_unlock(&priv->mu);
+}
+EXPORT_SYMBOL(tee_mutex_wait_delete);
+
+void tee_mutex_wait_wakeup(struct device *dev,
+                       struct tee_mutex_wait_private *priv,
+                       u32 key, u32 wait_after)
+{
+       struct tee_mutex_wait *w = tee_mutex_wait_get(dev, priv, key);
+
+       if (!w)
+               return;
+
+       mutex_lock(&w->mu);
+       w->wait_after = wait_after;
+       mutex_unlock(&w->mu);
+       complete(&w->comp);
+}
+EXPORT_SYMBOL(tee_mutex_wait_wakeup);
+
+void tee_mutex_wait_sleep(struct device *dev,
+                       struct tee_mutex_wait_private *priv,
+                       u32 key, u32 wait_tick)
+{
+       struct tee_mutex_wait *w = tee_mutex_wait_get(dev, priv, key);
+       u32 wait_after;
+
+       if (!w)
+               return;
+
+       mutex_lock(&w->mu);
+       wait_after = w->wait_after;
+       mutex_unlock(&w->mu);
+
+       if (TICK_GT(wait_tick, wait_after))
+               wait_for_completion_timeout(&w->comp, HZ);
+}
+EXPORT_SYMBOL(tee_mutex_wait_sleep);
+
+int tee_mutex_wait_init(struct tee_mutex_wait_private *priv)
+{
+       mutex_init(&priv->mu);
+       INIT_LIST_HEAD(&priv->db);
+       return 0;
+}
+EXPORT_SYMBOL(tee_mutex_wait_init);
+
+void tee_mutex_wait_exit(struct tee_mutex_wait_private *priv)
+{
+       /*
+        * It's the callers responibility to ensure that no one is using
+        * anything inside priv.
+        */
+
+       mutex_destroy(&priv->mu);
+       while (!list_empty(&priv->db)) {
+               struct tee_mutex_wait *w =
+                               list_first_entry(&priv->db,
+                                                struct tee_mutex_wait,
+                                                link);
+               tee_mutex_wait_delete_entry(w);
+       }
+}
+EXPORT_SYMBOL(tee_mutex_wait_exit);
diff --git a/security/optee_linuxdriver/core/tee_mutex_wait.h b/security/optee_linuxdriver/core/tee_mutex_wait.h
new file mode 100644 (file)
index 0000000..19f9b0d
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2014, Linaro Limited
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License Version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ */
+#ifndef TEE_MUTEX_WAIT_H
+#define TEE_MUTEX_WAIT_H
+
+#include <linux/mutex.h>
+#include <linux/list.h>
+#include <linux/device.h>
+
+struct tee_mutex_wait_private {
+       struct mutex mu;
+       struct list_head db;
+};
+
+int tee_mutex_wait_init(struct tee_mutex_wait_private *priv);
+void tee_mutex_wait_exit(struct tee_mutex_wait_private *priv);
+
+void tee_mutex_wait_delete(struct device *dev,
+                       struct tee_mutex_wait_private *priv,
+                       u32 key);
+void tee_mutex_wait_wakeup(struct device *dev,
+                       struct tee_mutex_wait_private *priv,
+                       u32 key, u32 wait_after);
+void tee_mutex_wait_sleep(struct device *dev,
+                       struct tee_mutex_wait_private *priv,
+                       u32 key, u32 wait_tick);
+
+#endif /*TEE_MUTEX_WAIT_H*/
diff --git a/security/optee_linuxdriver/core/tee_session.c b/security/optee_linuxdriver/core/tee_session.c
new file mode 100644 (file)
index 0000000..32391af
--- /dev/null
@@ -0,0 +1,852 @@
+/*
+ * Copyright (c) 2014, STMicroelectronics International N.V.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License Version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ */
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/types.h>
+#include <linux/file.h>
+#include <linux/atomic.h>
+#include <linux/uaccess.h>
+#include <linux/anon_inodes.h>
+
+#include "tee_shm.h"
+#include "tee_core_priv.h"
+
+static int _init_tee_cmd(struct tee_session *sess, struct tee_cmd_io *cmd_io,
+                        struct tee_cmd *cmd);
+static void _update_client_tee_cmd(struct tee_session *sess,
+                                  struct tee_cmd_io *cmd_io,
+                                  struct tee_cmd *cmd);
+static void _release_tee_cmd(struct tee_session *sess, struct tee_cmd *cmd);
+
+#define _DEV_TEE _DEV(sess->ctx->tee)
+
+#define INMSG dev_dbg(_DEV_TEE, "%s: >\n", __func__)
+#define OUTMSG(val) dev_dbg(_DEV_TEE, "%s: < %d\n", __func__, (int)val)
+
+/******************************************************************************/
+
+static inline bool flag_set(int val, int flags)
+{
+       return (val & flags) == flags;
+}
+
+static inline bool is_mapped_temp(int flags)
+{
+       return flag_set(flags, TEE_SHM_MAPPED | TEE_SHM_TEMP);
+}
+
+
+/******************************************************************************/
+
+#define _UUID_STR_SIZE 35
+static char *_uuid_to_str(const TEEC_UUID *uuid)
+{
+       static char uuid_str[_UUID_STR_SIZE];
+
+       if (uuid) {
+               sprintf(uuid_str,
+                       "%08x-%04x-%04x-%02x%02x%02x%02x%02x%02x%02x%02x",
+                       uuid->timeLow, uuid->timeMid, uuid->timeHiAndVersion,
+                       uuid->clockSeqAndNode[0], uuid->clockSeqAndNode[1],
+                       uuid->clockSeqAndNode[2], uuid->clockSeqAndNode[3],
+                       uuid->clockSeqAndNode[4], uuid->clockSeqAndNode[5],
+                       uuid->clockSeqAndNode[6], uuid->clockSeqAndNode[7]);
+       } else {
+               sprintf(uuid_str, "NULL");
+       }
+
+       return uuid_str;
+}
+
+static int tee_copy_from_user(struct tee_context *ctx, void *to, void *from,
+                             size_t size)
+{
+       if ((!to) || (!from) || (!size))
+               return 0;
+       if (ctx->usr_client)
+               return copy_from_user(to, from, size);
+       else {
+               memcpy(to, from, size);
+               return 0;
+       }
+}
+
+static int tee_copy_to_user(struct tee_context *ctx, void *to, void *from,
+                           size_t size)
+{
+       if ((!to) || (!from) || (!size))
+               return 0;
+       if (ctx->usr_client)
+               return copy_to_user(to, from, size);
+       else {
+               memcpy(to, from, size);
+               return 0;
+       }
+}
+
+/* Defined as macro to let the put_user macro see the types */
+#define tee_put_user(ctx, from, to)                            \
+       do {                                                    \
+               if ((ctx)->usr_client)                          \
+                       put_user(from, to);                     \
+               else                                            \
+                       *to = from;                             \
+       } while (0)
+
+static inline int tee_session_is_opened(struct tee_session *sess)
+{
+       if (sess && sess->sessid)
+               return (sess->sessid != 0);
+       return 0;
+}
+
+static int tee_session_open_be(struct tee_session *sess,
+                              struct tee_cmd_io *cmd_io)
+{
+       int ret = -EINVAL;
+       struct tee *tee;
+       struct tee_cmd cmd;
+
+       BUG_ON(!sess || !sess->ctx || !sess->ctx->tee);
+
+       tee = sess->ctx->tee;
+
+       dev_dbg(_DEV(tee), "%s: > open a new session", __func__);
+
+       sess->sessid = 0;
+       ret = _init_tee_cmd(sess, cmd_io, &cmd);
+       if (ret)
+               goto out;
+
+       if (cmd.uuid) {
+               dev_dbg(_DEV(tee), "%s: UUID=%s\n", __func__,
+                       _uuid_to_str((TEEC_UUID *) cmd.uuid->kaddr));
+       }
+
+       ret = tee->ops->open(sess, &cmd);
+       if (ret == 0)
+               _update_client_tee_cmd(sess, cmd_io, &cmd);
+       else {
+               /* propagate the reason of the error */
+               cmd_io->origin = cmd.origin;
+               cmd_io->err = cmd.err;
+       }
+
+out:
+       _release_tee_cmd(sess, &cmd);
+       dev_dbg(_DEV(tee), "%s: < ret=%d, sessid=%08x", __func__, ret,
+               sess->sessid);
+       return ret;
+}
+
+int tee_session_invoke_be(struct tee_session *sess, struct tee_cmd_io *cmd_io)
+{
+       int ret = -EINVAL;
+       struct tee *tee;
+       struct tee_cmd cmd;
+
+       BUG_ON(!sess || !sess->ctx || !sess->ctx->tee);
+
+       tee = sess->ctx->tee;
+
+       dev_dbg(_DEV(tee), "%s: > sessid=%08x, cmd=0x%08x\n", __func__,
+               sess->sessid, cmd_io->cmd);
+
+       ret = _init_tee_cmd(sess, cmd_io, &cmd);
+       if (ret)
+               goto out;
+
+       ret = tee->ops->invoke(sess, &cmd);
+       if (!ret)
+               _update_client_tee_cmd(sess, cmd_io, &cmd);
+       else {
+               /* propagate the reason of the error */
+               cmd_io->origin = cmd.origin;
+               cmd_io->err = cmd.err;
+       }
+
+out:
+       _release_tee_cmd(sess, &cmd);
+       dev_dbg(_DEV(tee), "%s: < ret=%d", __func__, ret);
+       return ret;
+}
+
+static int tee_session_close_be(struct tee_session *sess)
+{
+       int ret = -EINVAL;
+       struct tee *tee;
+
+       BUG_ON(!sess || !sess->ctx || !sess->ctx->tee);
+
+       tee = sess->ctx->tee;
+
+       dev_dbg(_DEV(tee), "%s: > sessid=%08x", __func__, sess->sessid);
+
+       ret = tee->ops->close(sess);
+       sess->sessid = 0;
+
+       dev_dbg(_DEV(tee), "%s: < ret=%d", __func__, ret);
+       return ret;
+}
+
+static int tee_session_cancel_be(struct tee_session *sess,
+                                struct tee_cmd_io *cmd_io)
+{
+       int ret = -EINVAL;
+       struct tee *tee;
+       struct tee_cmd cmd;
+
+       BUG_ON(!sess || !sess->ctx || !sess->ctx->tee);
+
+       tee = sess->ctx->tee;
+
+       dev_dbg(_DEV(tee), "%s: > sessid=%08x, cmd=0x%08x\n", __func__,
+               sess->sessid, cmd_io->cmd);
+
+       ret = _init_tee_cmd(sess, cmd_io, &cmd);
+       if (ret)
+               goto out;
+
+       ret = tee->ops->cancel(sess, &cmd);
+
+out:
+       _release_tee_cmd(sess, &cmd);
+       dev_dbg(_DEV(tee), "%s: < ret=%d", __func__, ret);
+       return ret;
+}
+
+static int tee_do_invoke_command(struct tee_session *sess,
+                                struct tee_cmd_io __user *u_cmd)
+{
+       int ret = -EINVAL;
+       struct tee *tee;
+       struct tee_cmd_io k_cmd;
+       struct tee_context *ctx;
+
+       BUG_ON(!sess->ctx);
+       BUG_ON(!sess->ctx->tee);
+       ctx = sess->ctx;
+       tee = sess->ctx->tee;
+
+       dev_dbg(_DEV(tee), "%s: > sessid=%08x\n", __func__, sess->sessid);
+
+       BUG_ON(!sess->sessid);
+
+       if (tee_copy_from_user
+           (ctx, &k_cmd, (void *)u_cmd, sizeof(struct tee_cmd_io))) {
+               dev_err(_DEV(tee), "%s: tee_copy_from_user failed\n", __func__);
+               goto exit;
+       }
+
+       if ((k_cmd.op == NULL) || (k_cmd.uuid != NULL) ||
+           (k_cmd.data != NULL) || (k_cmd.data_size != 0)) {
+               dev_err(_DEV(tee),
+                       "%s: op or/and data parameters are not valid\n",
+                       __func__);
+               goto exit;
+       }
+
+       ret = tee_session_invoke_be(sess, &k_cmd);
+       if (ret)
+               dev_err(_DEV(tee), "%s: tee_invoke_command failed\n", __func__);
+
+       tee_put_user(ctx, k_cmd.err, &u_cmd->err);
+       tee_put_user(ctx, k_cmd.origin, &u_cmd->origin);
+
+exit:
+       dev_dbg(_DEV(tee), "%s: < ret=%d\n", __func__, ret);
+       return ret;
+}
+
+static int tee_do_cancel_cmd(struct tee_session *sess,
+                            struct tee_cmd_io __user *u_cmd)
+{
+       int ret = -EINVAL;
+       struct tee *tee;
+       struct tee_cmd_io k_cmd;
+       struct tee_context *ctx;
+
+       BUG_ON(!sess->ctx);
+       BUG_ON(!sess->ctx->tee);
+       ctx = sess->ctx;
+       tee = sess->ctx->tee;
+
+       dev_dbg(sess->ctx->tee->dev, "%s: > sessid=%08x\n", __func__,
+               sess->sessid);
+
+       BUG_ON(!sess->sessid);
+
+       if (tee_copy_from_user
+           (ctx, &k_cmd, (void *)u_cmd, sizeof(struct tee_cmd_io))) {
+               dev_err(_DEV(tee), "%s: tee_copy_from_user failed\n", __func__);
+               goto exit;
+       }
+
+       if ((k_cmd.op == NULL) || (k_cmd.uuid != NULL) ||
+           (k_cmd.data != NULL) || (k_cmd.data_size != 0)) {
+               dev_err(_DEV(tee),
+                       "%s: op or/and data parameters are not valid\n",
+                       __func__);
+               goto exit;
+       }
+
+       ret = tee_session_cancel_be(sess, &k_cmd);
+       if (ret)
+               dev_err(_DEV(tee), "%s: tee_invoke_command failed\n", __func__);
+
+       tee_put_user(ctx, k_cmd.err, &u_cmd->err);
+       tee_put_user(ctx, k_cmd.origin, &u_cmd->origin);
+
+exit:
+       dev_dbg(_DEV(tee), "%s: < ret=%d", __func__, ret);
+       return ret;
+}
+
+static long tee_session_ioctl(struct file *filp, unsigned int cmd,
+                             unsigned long arg)
+{
+       struct tee *tee;
+       struct tee_session *sess = filp->private_data;
+       int ret;
+
+       BUG_ON(!sess || !sess->ctx || !sess->ctx->tee);
+
+       tee = sess->ctx->tee;
+
+       dev_dbg(_DEV(tee), "%s: > cmd nr=%d\n", __func__, _IOC_NR(cmd));
+
+       switch (cmd) {
+       case TEE_INVOKE_COMMAND_IOC:
+               ret =
+                   tee_do_invoke_command(sess,
+                                         (struct tee_cmd_io __user *)arg);
+               break;
+       case TEE_REQUEST_CANCELLATION_IOC:
+               ret = tee_do_cancel_cmd(sess, (struct tee_cmd_io __user *)arg);
+               break;
+       default:
+               ret = -ENOSYS;
+               break;
+       }
+
+       dev_dbg(_DEV(tee), "%s: < ret=%d\n", __func__, ret);
+
+       return ret;
+}
+
+static int tee_session_release(struct inode *inode, struct file *filp)
+{
+       struct tee_session *sess = filp->private_data;
+       int ret = 0;
+       struct tee *tee;
+
+       BUG_ON(!sess || !sess->ctx || !sess->ctx->tee);
+       tee = sess->ctx->tee;
+
+       ret = tee_session_close_and_destroy(sess);
+       return ret;
+}
+
+const struct file_operations tee_session_fops = {
+       .owner = THIS_MODULE,
+       .unlocked_ioctl = tee_session_ioctl,
+       .release = tee_session_release,
+};
+
+int tee_session_close_and_destroy(struct tee_session *sess)
+{
+       int ret;
+       struct tee *tee;
+       struct tee_context *ctx;
+
+       if (!sess || !sess->ctx || !sess->ctx->tee)
+               return -EINVAL;
+
+       ctx = sess->ctx;
+       tee = ctx->tee;
+
+       dev_dbg(_DEV(tee), "%s: > sess=%p\n", __func__, sess);
+
+       if (!tee_session_is_opened(sess))
+               return -EINVAL;
+
+       ret = tee_session_close_be(sess);
+
+       mutex_lock(&sess->ctx->tee->lock);
+       tee_dec_stats(&tee->stats[TEE_STATS_SESSION_IDX]);
+       list_del(&sess->entry);
+       mutex_unlock(&sess->ctx->tee->lock);
+
+       devm_kfree(_DEV(tee), sess);
+       tee_context_put(ctx);
+       tee_put(tee);
+
+       dev_dbg(_DEV(tee), "%s: <\n", __func__);
+       return ret;
+}
+
+struct tee_session *tee_session_create_and_open(struct tee_context *ctx,
+                                               struct tee_cmd_io *cmd_io)
+{
+       int ret = 0;
+       struct tee_session *sess;
+       struct tee *tee;
+
+       BUG_ON(!ctx->tee);
+
+       tee = ctx->tee;
+
+       dev_dbg(_DEV(tee), "%s: >\n", __func__);
+       ret = tee_get(tee);
+       if (ret)
+               return ERR_PTR(-EBUSY);
+
+       sess = devm_kzalloc(_DEV(tee), sizeof(struct tee_session), GFP_KERNEL);
+       if (!sess) {
+               dev_err(_DEV(tee), "%s: tee_session allocation() failed\n",
+                       __func__);
+               tee_put(tee);
+               return ERR_PTR(-ENOMEM);
+       }
+
+       tee_context_get(ctx);
+       sess->ctx = ctx;
+
+       ret = tee_session_open_be(sess, cmd_io);
+       if (ret || !sess->sessid || cmd_io->err) {
+               dev_err(_DEV(tee), "%s: ERROR ret=%d (err=0x%08x, org=%d,  sessid=0x%08x)\n",
+                               __func__, ret, cmd_io->err,
+                               cmd_io->origin, sess->sessid);
+               tee_put(tee);
+               tee_context_put(ctx);
+               devm_kfree(_DEV(tee), sess);
+               if (ret)
+                       return ERR_PTR(ret);
+               else
+                       return NULL;
+       }
+
+       mutex_lock(&tee->lock);
+       tee_inc_stats(&tee->stats[TEE_STATS_SESSION_IDX]);
+       list_add_tail(&sess->entry, &ctx->list_sess);
+       mutex_unlock(&tee->lock);
+
+       dev_dbg(_DEV(tee), "%s: < sess=%p\n", __func__, sess);
+       return sess;
+}
+
+int tee_session_create_fd(struct tee_context *ctx, struct tee_cmd_io *cmd_io)
+{
+       int ret;
+       struct tee_session *sess;
+       struct tee *tee = ctx->tee;
+
+       BUG_ON(cmd_io->fd_sess > 0);
+
+       dev_dbg(_DEV(tee), "%s: >\n", __func__);
+
+       sess = tee_session_create_and_open(ctx, cmd_io);
+       if (IS_ERR_OR_NULL(sess)) {
+               ret = PTR_ERR(sess);
+               dev_dbg(_DEV(tee), "%s: ERROR can't create the session (ret=%d, err=0x%08x, org=%d)\n",
+                       __func__, ret, cmd_io->err, cmd_io->origin);
+               cmd_io->fd_sess = -1;
+               goto out;
+       }
+
+       /* Retrieve a fd */
+       cmd_io->fd_sess = -1;
+       ret =
+           anon_inode_getfd("tee_session", &tee_session_fops, sess, O_CLOEXEC);
+       if (ret < 0) {
+               dev_err(_DEV(tee), "%s: ERROR can't get a fd (ret=%d)\n",
+                       __func__, ret);
+               tee_session_close_and_destroy(sess);
+               goto out;
+       }
+       cmd_io->fd_sess = ret;
+       ret = 0;
+
+out:
+       dev_dbg(_DEV(tee), "%s: < ret=%d, sess=%p, fd=%d\n", __func__,
+               ret, sess, cmd_io->fd_sess);
+       return ret;
+}
+
+static bool tee_session_is_supported_type(struct tee_session *sess, int type)
+{
+       switch (type) {
+       case TEEC_NONE:
+       case TEEC_VALUE_INPUT:
+       case TEEC_VALUE_OUTPUT:
+       case TEEC_VALUE_INOUT:
+       case TEEC_MEMREF_TEMP_INPUT:
+       case TEEC_MEMREF_TEMP_OUTPUT:
+       case TEEC_MEMREF_TEMP_INOUT:
+       case TEEC_MEMREF_WHOLE:
+       case TEEC_MEMREF_PARTIAL_INPUT:
+       case TEEC_MEMREF_PARTIAL_OUTPUT:
+       case TEEC_MEMREF_PARTIAL_INOUT:
+               return true;
+       default:
+               dev_err(_DEV_TEE, "type is invalid (type %02x)\n", type);
+               return false;
+       }
+}
+
+static int to_memref_type(int flags)
+{
+       if (flag_set(flags, TEEC_MEM_INPUT | TEEC_MEM_OUTPUT))
+               return TEEC_MEMREF_TEMP_INOUT;
+
+       if (flag_set(flags, TEEC_MEM_INPUT))
+               return TEEC_MEMREF_TEMP_INPUT;
+
+       if (flag_set(flags, TEEC_MEM_OUTPUT))
+               return TEEC_MEMREF_TEMP_OUTPUT;
+
+       pr_err("%s: bad flags=%x\n", __func__, flags);
+       return 0;
+}
+
+static int _init_tee_cmd(struct tee_session *sess, struct tee_cmd_io *cmd_io,
+                        struct tee_cmd *cmd)
+{
+       int ret = -EINVAL;
+       int idx;
+       TEEC_Operation op;
+       struct tee_data *param = &cmd->param;
+       struct tee *tee;
+       struct tee_context *ctx;
+
+       BUG_ON(!sess->ctx);
+       BUG_ON(!sess->ctx->tee);
+       ctx = sess->ctx;
+       tee = sess->ctx->tee;
+
+       dev_dbg(_DEV(tee), "%s: > sessid=%08x\n", __func__, sess->sessid);
+
+       memset(cmd, 0, sizeof(struct tee_cmd));
+
+       cmd->cmd = cmd_io->cmd;
+       cmd->origin = TEEC_ORIGIN_TEE;
+       cmd->err = TEEC_ERROR_BAD_PARAMETERS;
+       cmd_io->origin = cmd->origin;
+       cmd_io->err = cmd->err;
+
+       if (tee_context_copy_from_client(ctx, &op, cmd_io->op, sizeof(op)))
+               goto out;
+
+       cmd->param.type_original = op.paramTypes;
+
+       for (idx = 0; idx < TEEC_CONFIG_PAYLOAD_REF_COUNT; ++idx) {
+               uint32_t offset = 0;
+               uint32_t size = 0;
+               int type = TEEC_PARAM_TYPE_GET(op.paramTypes, idx);
+
+               switch (type) {
+               case TEEC_NONE:
+                       break;
+
+               case TEEC_VALUE_INPUT:
+               case TEEC_VALUE_OUTPUT:
+               case TEEC_VALUE_INOUT:
+                       param->params[idx].value = op.params[idx].value;
+                       dev_dbg(_DEV_TEE,
+                               "%s: param[%d]:type=%d,a=%08x,b=%08x (VALUE)\n",
+                               __func__, idx, type, param->params[idx].value.a,
+                               param->params[idx].value.b);
+                       break;
+
+               case TEEC_MEMREF_TEMP_INPUT:
+               case TEEC_MEMREF_TEMP_OUTPUT:
+               case TEEC_MEMREF_TEMP_INOUT:
+                       dev_dbg(_DEV_TEE,
+                               "> param[%d]:type=%d,buffer=%p,s=%zu (TMPREF)\n",
+                               idx, type, op.params[idx].tmpref.buffer,
+                               op.params[idx].tmpref.size);
+
+                       param->params[idx].shm =
+                           tee_context_create_tmpref_buffer(ctx,
+                                            op.params[idx].tmpref.size,
+                                            op.params[idx].tmpref.buffer,
+                                            type);
+                       if (IS_ERR_OR_NULL(param->params[idx].shm))
+                               goto out;
+
+                       dev_dbg(_DEV_TEE, "< %d %p:%zd\n", idx,
+                                       (void *)param->params[idx].shm->paddr,
+                                       param->params[idx].shm->size_alloc);
+                       break;
+
+               case TEEC_MEMREF_PARTIAL_INPUT:
+               case TEEC_MEMREF_PARTIAL_OUTPUT:
+               case TEEC_MEMREF_PARTIAL_INOUT:
+               case TEEC_MEMREF_WHOLE:
+                       if (tee_copy_from_user(ctx, &param->c_shm[idx],
+                                               op.params[idx].memref.parent,
+                                               sizeof(param->c_shm[idx]))) {
+                               goto out;
+                       }
+
+                       if (type == TEEC_MEMREF_WHOLE) {
+                               offset = 0;
+                               size = param->c_shm[idx].size;
+                       } else { /* for PARTIAL, check the size */
+                               offset = op.params[idx].memref.offset;
+                               size = op.params[idx].memref.size;
+                               if (param->c_shm[idx].size < size + offset) {
+                                       dev_err(_DEV(tee), "A PARTIAL parameter is bigger than the parent %zd < %d + %d\n",
+                                               param->c_shm[idx].size, size,
+                                               offset);
+                                       goto out;
+                               }
+                       }
+
+                       dev_dbg(_DEV_TEE, "> param[%d]:type=%d,buffer=%p, offset=%d size=%d\n",
+                                       idx, type, param->c_shm[idx].buffer,
+                                       offset, size);
+
+                       type = to_memref_type(param->c_shm[idx].flags);
+                       if (type == 0)
+                               goto out;
+
+                       param->params[idx].shm = tee_shm_get(ctx,
+                                       &param->c_shm[idx], size, offset);
+
+                       if (IS_ERR_OR_NULL(param->params[idx].shm)) {
+                               param->params[idx].shm =
+                                   tee_context_create_tmpref_buffer(ctx, size,
+                                      param->c_shm[idx].buffer + offset, type);
+
+                               if (IS_ERR_OR_NULL(param->params[idx].shm))
+                                       goto out;
+                       }
+
+                       dev_dbg(_DEV_TEE, "< %d %p:%zd\n", idx,
+                               (void *)param->params[idx].shm->paddr,
+                               param->params[idx].shm->size_req);
+                       break;
+
+               default:
+                       BUG_ON(1);
+               }
+
+               param->type |= (type << (idx * 4));
+       }
+
+       if (cmd_io->uuid != NULL) {
+               dev_dbg(_DEV_TEE, "%s: copy UUID value...\n", __func__);
+               cmd->uuid = tee_context_alloc_shm_tmp(sess->ctx,
+                       sizeof(*cmd_io->uuid), cmd_io->uuid, TEEC_MEM_INPUT);
+               if (IS_ERR_OR_NULL(cmd->uuid)) {
+                       ret = -EINVAL;
+                       goto out;
+               }
+       }
+
+       ret = 0;
+
+out:
+       if (ret)
+               _release_tee_cmd(sess, cmd);
+
+       dev_dbg(_DEV_TEE, "%s: < ret=%d\n", __func__, ret);
+       return ret;
+}
+
+static void _update_client_tee_cmd(struct tee_session *sess,
+                                  struct tee_cmd_io *cmd_io,
+                                  struct tee_cmd *cmd)
+{
+       int idx;
+       struct tee_context *ctx;
+
+       BUG_ON(!cmd_io);
+       BUG_ON(!cmd_io->op);
+       BUG_ON(!cmd_io->op->params);
+       BUG_ON(!cmd);
+       BUG_ON(!sess->ctx);
+       ctx = sess->ctx;
+
+       dev_dbg(_DEV_TEE, "%s: returned err=0x%08x (origin=%d)\n", __func__,
+               cmd->err, cmd->origin);
+
+       cmd_io->origin = cmd->origin;
+       cmd_io->err = cmd->err;
+
+       if (cmd->param.type_original == TEEC_PARAM_TYPES(TEEC_NONE,
+                       TEEC_NONE, TEEC_NONE, TEEC_NONE))
+               return;
+
+       for (idx = 0; idx < TEEC_CONFIG_PAYLOAD_REF_COUNT; ++idx) {
+               int type = TEEC_PARAM_TYPE_GET(cmd->param.type_original, idx);
+               int offset = 0;
+               size_t size;
+               size_t size_new;
+               TEEC_SharedMemory *parent;
+
+               dev_dbg(_DEV_TEE, "%s: id %d type %d\n", __func__, idx, type);
+               BUG_ON(!tee_session_is_supported_type(sess, type));
+               switch (type) {
+               case TEEC_NONE:
+               case TEEC_VALUE_INPUT:
+               case TEEC_MEMREF_TEMP_INPUT:
+               case TEEC_MEMREF_PARTIAL_INPUT:
+                       break;
+               case TEEC_VALUE_OUTPUT:
+               case TEEC_VALUE_INOUT:
+                       dev_dbg(_DEV_TEE, "%s: a=%08x, b=%08x\n",
+                               __func__,
+                               cmd->param.params[idx].value.a,
+                               cmd->param.params[idx].value.b);
+                       if (tee_copy_to_user
+                           (ctx, &cmd_io->op->params[idx].value,
+                            &cmd->param.params[idx].value,
+                            sizeof(cmd_io->op->params[idx].value)))
+                               dev_err(_DEV_TEE,
+                                       "%s:%d: can't update %d result to user\n",
+                                       __func__, __LINE__, idx);
+                       break;
+               case TEEC_MEMREF_TEMP_OUTPUT:
+               case TEEC_MEMREF_TEMP_INOUT:
+                       /* Returned updated size */
+                       size_new = cmd->param.params[idx].shm->size_req;
+                       if (size_new !=
+                               cmd_io->op->params[idx].tmpref.size) {
+                               dev_dbg(_DEV_TEE,
+                                       "Size has been updated by the TA %zd != %zd\n",
+                                       size_new,
+                                       cmd_io->op->params[idx].tmpref.
+                                       size);
+                               tee_put_user(ctx, size_new,
+                                    &cmd_io->op->params[idx].tmpref.size);
+                       }
+
+                       dev_dbg(_DEV_TEE, "%s: tmpref %p\n", __func__,
+                               cmd->param.params[idx].shm->kaddr);
+
+                       /* ensure we do not exceed the shared buffer length */
+                       if (size_new > cmd_io->op->params[idx].tmpref.size)
+                               dev_err(_DEV_TEE,
+                                       "  *** Wrong returned size from %d:%zd > %zd\n",
+                                       idx, size_new,
+                                       cmd_io->op->params[idx].tmpref.
+                                       size);
+
+                       else if (tee_copy_to_user
+                                (ctx,
+                                 cmd_io->op->params[idx].tmpref.buffer,
+                                 cmd->param.params[idx].shm->kaddr,
+                                 size_new))
+                               dev_err(_DEV_TEE,
+                                       "%s:%d: can't update %d result to user\n",
+                                       __func__, __LINE__, idx);
+                       break;
+
+               case TEEC_MEMREF_PARTIAL_OUTPUT:
+               case TEEC_MEMREF_PARTIAL_INOUT:
+               case TEEC_MEMREF_WHOLE:
+                       if (type == TEEC_MEMREF_WHOLE) {
+                               offset = 0;
+                               size = parent->size;
+                       } else {
+                               offset = cmd_io->op->params[idx].memref.offset;
+                               size = cmd_io->op->params[idx].memref.size;
+                       }
+                       parent = &cmd->param.c_shm[idx];
+
+                       /* Returned updated size */
+                       size_new = cmd->param.params[idx].shm->size_req;
+                       tee_put_user(ctx, size_new,
+                                       &cmd_io->op->params[idx].memref.size);
+
+                       /*
+                        * If we allocated a tmpref buffer,
+                        * copy back data to the user buffer
+                        */
+                       if (is_mapped_temp(cmd->param.params[idx].shm->flags)) {
+                               if (parent->buffer &&
+                                       offset + size_new <= parent->size) {
+                                       if (tee_copy_to_user(ctx,
+                                          parent->buffer + offset,
+                                          cmd->param.params[idx].shm->kaddr,
+                                          size_new))
+                                                       dev_err(_DEV_TEE,
+                                                               "%s: can't update %d data to user\n",
+                                                               __func__, idx);
+                               }
+                       }
+                       break;
+               default:
+                       BUG_ON(1);
+               }
+       }
+
+}
+
+static void _release_tee_cmd(struct tee_session *sess, struct tee_cmd *cmd)
+{
+       int idx;
+       struct tee_context *ctx;
+
+       BUG_ON(!cmd);
+       BUG_ON(!sess);
+       BUG_ON(!sess->ctx);
+       BUG_ON(!sess->ctx->tee);
+
+       ctx = sess->ctx;
+
+       dev_dbg(_DEV_TEE, "%s: > free the temporary objects...\n", __func__);
+
+       tee_shm_free(cmd->uuid);
+
+       if (cmd->param.type_original == TEEC_PARAM_TYPES(TEEC_NONE,
+                       TEEC_NONE, TEEC_NONE, TEEC_NONE))
+               goto out;
+
+       for (idx = 0; idx < TEEC_CONFIG_PAYLOAD_REF_COUNT; ++idx) {
+               int type = TEEC_PARAM_TYPE_GET(cmd->param.type_original, idx);
+               struct tee_shm *shm;
+               switch (type) {
+               case TEEC_NONE:
+               case TEEC_VALUE_INPUT:
+               case TEEC_VALUE_OUTPUT:
+               case TEEC_VALUE_INOUT:
+                       break;
+               case TEEC_MEMREF_TEMP_INPUT:
+               case TEEC_MEMREF_TEMP_OUTPUT:
+               case TEEC_MEMREF_TEMP_INOUT:
+               case TEEC_MEMREF_WHOLE:
+               case TEEC_MEMREF_PARTIAL_INPUT:
+               case TEEC_MEMREF_PARTIAL_OUTPUT:
+               case TEEC_MEMREF_PARTIAL_INOUT:
+                       if (IS_ERR_OR_NULL(cmd->param.params[idx].shm))
+                               break;
+
+                       shm = cmd->param.params[idx].shm;
+
+                       if (is_mapped_temp(shm->flags))
+                               tee_shm_free(shm);
+                       else
+                               tee_shm_put(ctx, shm);
+                       break;
+               default:
+                       BUG_ON(1);
+               }
+       }
+
+out:
+       memset(cmd, 0, sizeof(struct tee_cmd));
+       dev_dbg(_DEV_TEE, "%s: <\n", __func__);
+}
diff --git a/security/optee_linuxdriver/core/tee_shm.c b/security/optee_linuxdriver/core/tee_shm.c
new file mode 100644 (file)
index 0000000..10d11af
--- /dev/null
@@ -0,0 +1,815 @@
+/*
+ * Copyright (c) 2014, STMicroelectronics International N.V.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License Version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ */
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/dma-buf.h>
+#include <linux/hugetlb.h>
+
+#include <linux/sched.h>
+#include <linux/mm.h>
+
+#include "tee_core_priv.h"
+#include "tee_shm.h"
+
+#define INMSG() dev_dbg(_DEV(tee), "%s: >\n", __func__)
+#define OUTMSG(val) dev_dbg(_DEV(tee), "%s: < %ld\n", __func__, (long)val)
+#define OUTMSGX(val) dev_dbg(_DEV(tee), "%s: < %08x\n",\
+               __func__, (unsigned int)(long)val)
+
+/* TODO
+#if (sizeof(TEEC_SharedMemory) != sizeof(tee_shm))
+#error "sizeof(TEEC_SharedMemory) != sizeof(tee_shm))"
+#endif
+*/
+struct tee_shm_attach {
+       struct sg_table sgt;
+       enum dma_data_direction dir;
+       bool is_mapped;
+};
+
+struct tee_shm *tee_shm_alloc_from_rpc(struct tee *tee, size_t size)
+{
+       struct tee_shm *shm;
+
+       INMSG();
+
+       shm = tee_shm_alloc(tee, size, TEE_SHM_TEMP | TEE_SHM_FROM_RPC);
+       if (IS_ERR_OR_NULL(shm)) {
+               dev_err(_DEV(tee), "%s: buffer allocation failed (%ld)\n",
+                       __func__, PTR_ERR(shm));
+               goto out;
+       }
+
+       mutex_lock(&tee->lock);
+       tee_inc_stats(&tee->stats[TEE_STATS_SHM_IDX]);
+       list_add_tail(&shm->entry, &tee->list_rpc_shm);
+       mutex_unlock(&tee->lock);
+       shm->ctx = NULL;
+
+out:
+       OUTMSGX(shm);
+       return shm;
+}
+
+void tee_shm_free_from_rpc(struct tee_shm *shm)
+{
+       if (shm == NULL)
+               return;
+
+       if (shm->ctx == NULL) {
+               mutex_lock(&shm->tee->lock);
+               tee_dec_stats(&shm->tee->stats[TEE_STATS_SHM_IDX]);
+               list_del(&shm->entry);
+               mutex_unlock(&shm->tee->lock);
+       }
+
+       tee_shm_free(shm);
+}
+
+struct tee_shm *tee_shm_alloc(struct tee *tee, size_t size, uint32_t flags)
+{
+       struct tee_shm *shm;
+       unsigned long pfn;
+       unsigned int nr_pages;
+       struct page *page;
+       int ret;
+
+       INMSG();
+
+       shm = tee->ops->alloc(tee, size, flags);
+       if (IS_ERR_OR_NULL(shm)) {
+               dev_err(_DEV(tee),
+                       "%s: allocation failed (s=%d,flags=0x%08x) err=%ld\n",
+                       __func__, (int)size, flags, PTR_ERR(shm));
+               goto exit;
+       }
+
+       shm->tee = tee;
+
+       dev_dbg(_DEV(tee), "%s: shm=%p, paddr=%p,s=%d/%d app=\"%s\" pid=%d\n",
+                __func__, shm, (void *)shm->paddr, (int)shm->size_req,
+                (int)shm->size_alloc, current->comm, current->pid);
+
+       pfn = shm->paddr >> PAGE_SHIFT;
+       page = pfn_to_page(pfn);
+       if (IS_ERR_OR_NULL(page)) {
+               dev_err(_DEV(tee), "%s: pfn_to_page(%lx) failed\n",
+                               __func__, pfn);
+               tee->ops->free(shm);
+               return (struct tee_shm *)page;
+       }
+
+       /* Only one page of contiguous physical memory */
+       nr_pages = 1;
+
+       ret = sg_alloc_table_from_pages(&shm->sgt, &page,
+                       nr_pages, 0, nr_pages * PAGE_SIZE, GFP_KERNEL);
+       if (IS_ERR_VALUE(ret)) {
+               dev_err(_DEV(tee), "%s: sg_alloc_table_from_pages() failed\n",
+                               __func__);
+               tee->ops->free(shm);
+               shm = ERR_PTR(ret);
+       }
+exit:
+       OUTMSGX(shm);
+       return shm;
+}
+
+void tee_shm_free(struct tee_shm *shm)
+{
+       struct tee *tee;
+
+       if (IS_ERR_OR_NULL(shm))
+               return;
+       tee = shm->tee;
+       if (tee == NULL)
+               pr_warn("invalid call to tee_shm_free(%p): NULL tee\n", shm);
+       else if (shm->tee == NULL)
+               dev_warn(_DEV(tee), "tee_shm_free(%p): NULL tee\n", shm);
+       else {
+               sg_free_table(&shm->sgt);
+               shm->tee->ops->free(shm);
+       }
+}
+
+static int _tee_shm_attach_dma_buf(struct dma_buf *dmabuf,
+                                       struct device *dev,
+                                       struct dma_buf_attachment *attach)
+{
+       struct tee_shm_attach *tee_shm_attach;
+       struct tee_shm *shm;
+       struct tee *tee;
+
+       shm = dmabuf->priv;
+       tee = shm->tee;
+
+       INMSG();
+
+       tee_shm_attach = devm_kzalloc(_DEV(tee),
+                       sizeof(*tee_shm_attach), GFP_KERNEL);
+       if (!tee_shm_attach) {
+               OUTMSG(-ENOMEM);
+               return -ENOMEM;
+       }
+
+       tee_shm_attach->dir = DMA_NONE;
+       attach->priv = tee_shm_attach;
+
+       OUTMSG(0);
+       return 0;
+}
+
+static void _tee_shm_detach_dma_buf(struct dma_buf *dmabuf,
+                                       struct dma_buf_attachment *attach)
+{
+       struct tee_shm_attach *tee_shm_attach = attach->priv;
+       struct sg_table *sgt;
+       struct tee_shm *shm;
+       struct tee *tee;
+
+       shm = dmabuf->priv;
+       tee = shm->tee;
+
+       INMSG();
+
+       if (!tee_shm_attach) {
+               OUTMSG(0);
+               return;
+       }
+
+       sgt = &tee_shm_attach->sgt;
+
+       if (tee_shm_attach->dir != DMA_NONE)
+               dma_unmap_sg(attach->dev, sgt->sgl, sgt->nents,
+                       tee_shm_attach->dir);
+
+       sg_free_table(sgt);
+       devm_kfree(_DEV(tee), tee_shm_attach);
+       attach->priv = NULL;
+       OUTMSG(0);
+}
+
+static struct sg_table *_tee_shm_dma_buf_map_dma_buf(
+               struct dma_buf_attachment *attach, enum dma_data_direction dir)
+{
+       struct tee_shm_attach *tee_shm_attach = attach->priv;
+       struct tee_shm *tee_shm = attach->dmabuf->priv;
+       struct sg_table *sgt = NULL;
+       struct scatterlist *rd, *wr;
+       unsigned int i;
+       int nents, ret;
+       struct tee *tee;
+
+       tee = tee_shm->tee;
+
+       INMSG();
+
+       /* just return current sgt if already requested. */
+       if (tee_shm_attach->dir == dir && tee_shm_attach->is_mapped) {
+               OUTMSGX(&tee_shm_attach->sgt);
+               return &tee_shm_attach->sgt;
+       }
+
+       sgt = &tee_shm_attach->sgt;
+
+       ret = sg_alloc_table(sgt, tee_shm->sgt.orig_nents, GFP_KERNEL);
+       if (ret) {
+               dev_err(_DEV(tee), "failed to alloc sgt.\n");
+               return ERR_PTR(-ENOMEM);
+       }
+
+       rd = tee_shm->sgt.sgl;
+       wr = sgt->sgl;
+       for (i = 0; i < sgt->orig_nents; ++i) {
+               sg_set_page(wr, sg_page(rd), rd->length, rd->offset);
+               rd = sg_next(rd);
+               wr = sg_next(wr);
+       }
+
+       if (dir != DMA_NONE) {
+               nents = dma_map_sg(attach->dev, sgt->sgl, sgt->orig_nents, dir);
+               if (!nents) {
+                       dev_err(_DEV(tee), "failed to map sgl with iommu.\n");
+                       sg_free_table(sgt);
+                       sgt = ERR_PTR(-EIO);
+                       goto err_unlock;
+               }
+       }
+
+       tee_shm_attach->is_mapped = true;
+       tee_shm_attach->dir = dir;
+       attach->priv = tee_shm_attach;
+
+err_unlock:
+       OUTMSGX(sgt);
+       return sgt;
+}
+
+static void _tee_shm_dma_buf_unmap_dma_buf(struct dma_buf_attachment *attach,
+                                         struct sg_table *table,
+                                         enum dma_data_direction dir)
+{
+       return;
+}
+
+static void _tee_shm_dma_buf_release(struct dma_buf *dmabuf)
+{
+       struct tee_shm *shm = dmabuf->priv;
+       struct tee_context *ctx;
+       struct tee *tee;
+
+       tee = shm->ctx->tee;
+
+       INMSG();
+
+       ctx = shm->ctx;
+       dev_dbg(_DEV(ctx->tee), "%s: shm=%p, paddr=%p,s=%d/%d app=\"%s\" pid=%d\n",
+                __func__, shm, (void *)shm->paddr, (int)shm->size_req,
+                (int)shm->size_alloc, current->comm, current->pid);
+
+       tee_shm_free_io(shm);
+
+       OUTMSG(0);
+}
+
+static int _tee_shm_dma_buf_mmap(struct dma_buf *dmabuf,
+                               struct vm_area_struct *vma)
+{
+       struct tee_shm *shm = dmabuf->priv;
+       size_t size = vma->vm_end - vma->vm_start;
+       struct tee *tee;
+       int ret;
+       pgprot_t prot;
+       unsigned long pfn;
+
+       tee = shm->ctx->tee;
+
+       pfn = shm->paddr >> PAGE_SHIFT;
+
+       INMSG();
+
+       if (shm->flags & TEE_SHM_CACHED)
+               prot = vma->vm_page_prot;
+       else
+               prot = pgprot_noncached(vma->vm_page_prot);
+
+       ret =
+           remap_pfn_range(vma, vma->vm_start, pfn, size, prot);
+       if (!ret)
+               vma->vm_private_data = (void *)shm;
+
+       dev_dbg(_DEV(shm->ctx->tee), "%s: map the shm (p@=%p,s=%dKiB) => %x\n",
+               __func__, (void *)shm->paddr, (int)size / 1024,
+               (unsigned int)vma->vm_start);
+
+       OUTMSG(ret);
+       return ret;
+}
+
+static void *_tee_shm_dma_buf_kmap_atomic(struct dma_buf *dmabuf,
+                                        unsigned long pgnum)
+{
+       return NULL;
+}
+
+static void *_tee_shm_dma_buf_kmap(struct dma_buf *db, unsigned long pgnum)
+{
+       struct tee_shm *shm = db->priv;
+
+       dev_dbg(_DEV(shm->ctx->tee), "%s: kmap the shm (p@=%p, v@=%p, s=%zdKiB)\n",
+               __func__, (void *)shm->paddr, (void *)shm->kaddr,
+               shm->size_alloc / 1024);
+       /*
+        * A this stage, a shm allocated by the tee
+        * must be have a kernel address
+        */
+       return shm->kaddr;
+}
+
+static void _tee_shm_dma_buf_kunmap(
+               struct dma_buf *db, unsigned long pfn, void *kaddr)
+{
+       /* unmap is done at the de init of the shm pool */
+}
+
+struct dma_buf_ops _tee_shm_dma_buf_ops = {
+       .attach = _tee_shm_attach_dma_buf,
+       .detach = _tee_shm_detach_dma_buf,
+       .map_dma_buf = _tee_shm_dma_buf_map_dma_buf,
+       .unmap_dma_buf = _tee_shm_dma_buf_unmap_dma_buf,
+       .release = _tee_shm_dma_buf_release,
+       .kmap_atomic = _tee_shm_dma_buf_kmap_atomic,
+       .kmap = _tee_shm_dma_buf_kmap,
+       .kunmap = _tee_shm_dma_buf_kunmap,
+       .mmap = _tee_shm_dma_buf_mmap,
+};
+
+/******************************************************************************/
+
+static int export_buf(struct tee *tee, struct tee_shm *shm, int *export)
+{
+       struct dma_buf *dmabuf;
+       int ret = 0;
+       /* Temporary fix to support both older and newer kernel versions. */
+#if defined(DEFINE_DMA_BUF_EXPORT_INFO)
+       DEFINE_DMA_BUF_EXPORT_INFO(exp_info);
+
+       exp_info.priv = shm;
+       exp_info.ops = &_tee_shm_dma_buf_ops;
+       exp_info.size = shm->size_alloc;
+       exp_info.flags = O_RDWR;
+
+       dmabuf = dma_buf_export(&exp_info);
+#else
+       dmabuf = dma_buf_export(shm, &_tee_shm_dma_buf_ops, shm->size_alloc,
+                               O_RDWR, 0);
+#endif
+       if (IS_ERR_OR_NULL(dmabuf)) {
+               dev_err(_DEV(tee), "%s: dmabuf: couldn't export buffer (%ld)\n",
+                       __func__, PTR_ERR(dmabuf));
+               ret = -EINVAL;
+               goto out;
+       }
+
+       *export = dma_buf_fd(dmabuf, O_CLOEXEC);
+out:
+       OUTMSG(ret);
+       return ret;
+}
+
+int tee_shm_alloc_io(struct tee_context *ctx, struct tee_shm_io *shm_io)
+{
+       struct tee_shm *shm;
+       struct tee *tee = ctx->tee;
+       int ret;
+
+       INMSG();
+
+       if (ctx->usr_client)
+               shm_io->fd_shm = 0;
+       else
+               shm_io->ptr = NULL;
+
+       shm = tee_shm_alloc(tee, shm_io->size, shm_io->flags);
+       if (IS_ERR_OR_NULL(shm)) {
+               dev_err(_DEV(tee), "%s: buffer allocation failed (%ld)\n",
+                       __func__, PTR_ERR(shm));
+               return PTR_ERR(shm);
+       }
+
+       if (ctx->usr_client) {
+               ret = export_buf(tee, shm, &shm_io->fd_shm);
+               if (ret) {
+                       tee_shm_free(shm);
+                       ret = -ENOMEM;
+                       goto out;
+               }
+
+               shm->flags |= TEEC_MEM_DMABUF;
+       } else
+               shm_io->ptr = shm;
+
+       shm->ctx = ctx;
+       shm->dev = get_device(_DEV(tee));
+       ret = tee_get(tee);
+       BUG_ON(ret);            /* tee_core_get must not issue */
+       tee_context_get(ctx);
+
+       mutex_lock(&tee->lock);
+       tee_inc_stats(&tee->stats[TEE_STATS_SHM_IDX]);
+       list_add_tail(&shm->entry, &ctx->list_shm);
+       mutex_unlock(&tee->lock);
+out:
+       OUTMSG(ret);
+       return ret;
+}
+
+void tee_shm_free_io(struct tee_shm *shm)
+{
+       struct tee_context *ctx = shm->ctx;
+       struct tee *tee = ctx->tee;
+       struct device *dev = shm->dev;
+
+       mutex_lock(&ctx->tee->lock);
+       tee_dec_stats(&tee->stats[TEE_STATS_SHM_IDX]);
+       list_del(&shm->entry);
+       mutex_unlock(&ctx->tee->lock);
+
+       tee_shm_free(shm);
+       tee_put(ctx->tee);
+       tee_context_put(ctx);
+       if (dev)
+               put_device(dev);
+}
+
+/* Buffer allocated by rpc from fw and to be accessed by the user
+ * Not need to be registered as it is not allocated by the user */
+int tee_shm_fd_for_rpc(struct tee_context *ctx, struct tee_shm_io *shm_io)
+{
+       struct tee_shm *shm = NULL;
+       struct tee *tee = ctx->tee;
+       int ret;
+       struct list_head *pshm;
+
+       INMSG();
+
+       shm_io->fd_shm = 0;
+
+       if (!list_empty(&tee->list_rpc_shm)) {
+               list_for_each(pshm, &tee->list_rpc_shm) {
+                       shm = list_entry(pshm, struct tee_shm, entry);
+                       if ((void *)shm->paddr == shm_io->buffer)
+                               goto found;
+               }
+       }
+
+       dev_err(_DEV(tee), "Can't find shm for %p\n", (void *)shm_io->buffer);
+       ret = -ENOMEM;
+       goto out;
+
+found:
+       ret = export_buf(tee, shm, &shm_io->fd_shm);
+       if (ret) {
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       shm->ctx = ctx;
+       mutex_lock(&tee->lock);
+       list_move(&shm->entry, &ctx->list_shm);
+       mutex_unlock(&tee->lock);
+
+       shm->dev = get_device(_DEV(tee));
+       ret = tee_get(tee);
+       BUG_ON(ret);
+       tee_context_get(ctx);
+
+       BUG_ON(!tee->ops->shm_inc_ref(shm));
+out:
+       OUTMSG(ret);
+       return ret;
+}
+
+/******************************************************************************/
+
+static int tee_shm_db_get(struct tee *tee, struct tee_shm *shm, int fd,
+               unsigned int flags, size_t size, int offset)
+{
+       struct tee_shm_dma_buf *sdb;
+       struct dma_buf *dma_buf;
+       int ret = 0;
+
+       dev_dbg(_DEV(tee), "%s: > fd=%d flags=%08x\n", __func__, fd, flags);
+
+       dma_buf = dma_buf_get(fd);
+       if (IS_ERR(dma_buf)) {
+               ret = PTR_ERR(dma_buf);
+               goto exit;
+       }
+
+       sdb = kzalloc(sizeof(*sdb), GFP_KERNEL);
+       if (IS_ERR_OR_NULL(sdb)) {
+               dev_err(_DEV(tee), "can't alloc tee_shm_dma_buf\n");
+               ret = PTR_ERR(sdb);
+               goto buf_put;
+       }
+       shm->sdb = sdb;
+
+       if (dma_buf->size < size + offset) {
+               dev_err(_DEV(tee), "dma_buf too small %zd < %zd + %d\n",
+                       dma_buf->size, size, offset);
+               ret = -EINVAL;
+               goto free_sdb;
+       }
+
+       sdb->attach = dma_buf_attach(dma_buf, _DEV(tee));
+       if (IS_ERR_OR_NULL(sdb->attach)) {
+               ret = PTR_ERR(sdb->attach);
+               goto free_sdb;
+       }
+
+       sdb->sgt = dma_buf_map_attachment(sdb->attach, DMA_NONE);
+       if (IS_ERR_OR_NULL(sdb->sgt)) {
+               ret = PTR_ERR(sdb->sgt);
+               goto buf_detach;
+       }
+
+       if (sg_nents(sdb->sgt->sgl) != 1) {
+               ret = -EINVAL;
+               goto buf_unmap;
+       }
+
+       shm->paddr = sg_phys(sdb->sgt->sgl) + offset;
+       if (dma_buf->ops->attach == _tee_shm_attach_dma_buf)
+               sdb->tee_allocated = true;
+       else
+               sdb->tee_allocated = false;
+
+       shm->flags |= TEEC_MEM_DMABUF;
+
+       dev_dbg(_DEV(tee), "fd=%d @p=%p is_tee=%d db=%p\n", fd,
+                       (void *)shm->paddr, sdb->tee_allocated, dma_buf);
+       goto exit;
+
+buf_unmap:
+       dma_buf_unmap_attachment(sdb->attach, sdb->sgt, DMA_NONE);
+buf_detach:
+       dma_buf_detach(dma_buf, sdb->attach);
+free_sdb:
+       kfree(sdb);
+buf_put:
+       dma_buf_put(dma_buf);
+exit:
+       OUTMSG(ret);
+       return ret;
+}
+
+#ifdef VA_GET_ENABLED
+static unsigned int tee_shm_get_phy_from_kla(
+               struct mm_struct *mm, unsigned int kla)
+{
+       pgd_t *pgd;
+       pud_t *pud;
+       pmd_t *pmd;
+       pte_t *ptep, pte;
+       unsigned int pa = 0;
+
+       /* stolen from kernel3.10:mm/memory.c:__follow_pte */
+
+       pgd = pgd_offset(mm, kla);
+       if (pgd_none(*pgd) || pgd_bad(*pgd))
+               return 0;
+
+       pud = pud_offset(pgd, kla);
+       if (pud_none(*pud) || pud_bad(*pud))
+               return 0;
+
+       pmd = pmd_offset(pud, kla);
+       VM_BUG_ON(pmd_trans_huge(*pmd));
+       if (pmd_none(*pmd) || pmd_bad(*pmd))
+               return 0;
+
+       /* We cannot handle huge page PFN maps.
+        * Luckily they don't exist. */
+       if (pmd_huge(*pmd))
+               return 0;
+
+       ptep = pte_offset_map(pmd, kla);
+
+       if (!ptep)
+               return 0;
+
+       pte = *ptep;
+
+       if (pte_present(pte))
+               pa = __pa(page_address(pte_page(pte)));
+
+       if (!pa)
+               return 0;
+
+       return pa;
+
+}
+
+static int tee_shm_va_get(struct tee_context *ctx, struct tee_shm *shm,
+               void *buffer, unsigned int flags, size_t size, int offset)
+{
+       int ret = 0;
+       struct mm_struct *mm = current->mm;
+       unsigned long va = (unsigned long)buffer;
+       unsigned int virt_base = (va / PAGE_SIZE) * PAGE_SIZE;
+       unsigned int offset_in_page = va - virt_base;
+       unsigned int offset_total = offset_in_page + offset;
+       struct vm_area_struct *vma;
+       struct tee *tee = ctx->tee;
+
+       dev_dbg(_DEV(tee), "%s: > %p\n", __func__, buffer);
+       /* if the caller is the kernel api, active_mm is mm */
+       if (!mm)
+               mm = current->active_mm;
+
+       BUG_ON(!mm);
+
+       vma = find_vma(mm, virt_base);
+
+       if (vma) {
+               unsigned long pfn;
+               /* It's a VMA => consider it a a user address */
+
+               if (follow_pfn(vma, virt_base, &pfn)) {
+                       dev_err(_DEV(tee), "%s can't get pfn for %p\n",
+                               __func__, buffer);
+                       ret = -EINVAL;
+                       goto out;
+               }
+
+               shm->paddr = PFN_PHYS(pfn) + offset_total;
+
+               if (vma->vm_end - vma->vm_start - offset_total < size) {
+                       dev_err(_DEV(tee), "%s %p:%x not big enough: %lx - %d < %x\n",
+                                       __func__, buffer, shm->paddr,
+                                       vma->vm_end - vma->vm_start,
+                                       offset_total, size);
+                       shm->paddr = 0;
+                       ret = -EINVAL;
+                       goto out;
+               }
+       } else if (!ctx->usr_client) {
+               /* It's not a VMA => consider it as a kernel address
+                * And look if it's an internal known phys addr
+                * Note: virt_to_phys is not usable since it can be a direct
+                * map or a vremap address
+               */
+               unsigned int phys_base;
+               int nb_page = (PAGE_SIZE - 1 + size + offset_total) / PAGE_SIZE;
+               int i;
+
+               spin_lock(&mm->page_table_lock);
+               phys_base = tee_shm_get_phy_from_kla(mm, virt_base);
+
+               if (!phys_base) {
+                       spin_unlock(&mm->page_table_lock);
+                       dev_err(_DEV(tee), "%s can't get physical address for %p\n",
+                                       __func__, buffer);
+                       goto err;
+               }
+
+               /* Check continuity on size */
+               for (i = 1; i < nb_page; i++) {
+                       unsigned int pa = tee_shm_get_phy_from_kla(mm,
+                                       virt_base + i*PAGE_SIZE);
+                       if (pa != phys_base + i*PAGE_SIZE) {
+                               spin_unlock(&mm->page_table_lock);
+                               dev_err(_DEV(tee), "%s %p:%x not big enough: %lx - %d < %x\n",
+                                               __func__, buffer, phys_base,
+                                               i*PAGE_SIZE,
+                                               offset_total, size);
+                               goto err;
+                       }
+               }
+               spin_unlock(&mm->page_table_lock);
+
+               shm->paddr = phys_base + offset_total;
+               goto out;
+err:
+               ret = -EINVAL;
+       }
+
+out:
+       dev_dbg(_DEV(tee), "%s: < %d shm=%p vaddr=%p paddr=%x\n",
+                       __func__, ret, (void *)shm, buffer, shm->paddr);
+       return ret;
+}
+#endif
+
+struct tee_shm *tee_shm_get(struct tee_context *ctx, TEEC_SharedMemory *c_shm,
+               size_t size, int offset)
+{
+       struct tee_shm *shm;
+       struct tee *tee = ctx->tee;
+       int ret;
+
+       dev_dbg(_DEV(tee), "%s: > fd=%d flags=%08x\n",
+                       __func__, c_shm->d.fd, c_shm->flags);
+
+       shm = kzalloc(sizeof(*shm), GFP_KERNEL);
+       if (IS_ERR_OR_NULL(shm)) {
+               dev_err(_DEV(tee), "can't alloc tee_shm\n");
+               return ERR_PTR(-ENOMEM);
+       }
+
+       shm->ctx = ctx;
+       shm->tee = tee;
+       shm->dev = _DEV(tee);
+       shm->flags = c_shm->flags | TEE_SHM_MEMREF;
+       shm->size_req = size;
+       shm->size_alloc = 0;
+
+       if (c_shm->flags & TEEC_MEM_KAPI) {
+               struct tee_shm *kc_shm = (struct tee_shm *)c_shm->d.ptr;
+
+               if (!kc_shm) {
+                       dev_err(_DEV(tee), "kapi fd null\n");
+                       ret = -EINVAL;
+                       goto err;
+               }
+               shm->paddr = kc_shm->paddr;
+
+               if (kc_shm->size_alloc < size + offset) {
+                       dev_err(_DEV(tee), "kapi buff too small %zd < %zd + %d\n",
+                               kc_shm->size_alloc, size, offset);
+                       ret = -EINVAL;
+                       goto err;
+               }
+
+               dev_dbg(_DEV(tee), "fd=%d @p=%p\n",
+                               c_shm->d.fd, (void *)shm->paddr);
+       } else if (c_shm->d.fd) {
+               ret = tee_shm_db_get(tee, shm,
+                               c_shm->d.fd, c_shm->flags, size, offset);
+               if (ret)
+                       goto err;
+       } else if (!c_shm->buffer) {
+               dev_dbg(_DEV(tee), "null buffer, pass 'as is'\n");
+       } else {
+#ifdef VA_GET_ENABLED
+               ret = tee_shm_va_get(ctx, shm,
+                               c_shm->buffer, c_shm->flags, size, offset);
+               if (ret)
+                       goto err;
+#else
+               ret = -EINVAL;
+               goto err;
+#endif
+       }
+
+       OUTMSGX(shm);
+       return shm;
+
+err:
+       kfree(shm);
+       OUTMSGX(ERR_PTR(ret));
+       return ERR_PTR(ret);
+}
+
+void tee_shm_put(struct tee_context *ctx, struct tee_shm *shm)
+{
+       struct tee *tee = ctx->tee;
+
+       dev_dbg(_DEV(tee), "%s: > shm=%p flags=%08x paddr=%p\n",
+                       __func__, (void *)shm, shm->flags, (void *)shm->paddr);
+
+       BUG_ON(!shm);
+       BUG_ON(!(shm->flags & TEE_SHM_MEMREF));
+
+       if (shm->flags & TEEC_MEM_DMABUF) {
+               struct tee_shm_dma_buf *sdb;
+               struct dma_buf *dma_buf;
+
+               sdb = shm->sdb;
+               dma_buf = sdb->attach->dmabuf;
+
+               dev_dbg(_DEV(tee), "%s: db=%p\n", __func__, (void *)dma_buf);
+
+               dma_buf_unmap_attachment(sdb->attach, sdb->sgt, DMA_NONE);
+               dma_buf_detach(dma_buf, sdb->attach);
+               dma_buf_put(dma_buf);
+
+               kfree(sdb);
+               sdb = 0;
+       }
+
+       kfree(shm);
+       OUTMSG(0);
+}
+
diff --git a/security/optee_linuxdriver/core/tee_shm.h b/security/optee_linuxdriver/core/tee_shm.h
new file mode 100644 (file)
index 0000000..8f76ed3
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2014, STMicroelectronics International N.V.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License Version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ */
+#ifndef __TEE_SHM_H__
+#define __TEE_SHM_H__
+
+#include <linux/tee_client_api.h>
+struct tee_context;
+struct tee_shm;
+struct tee_shm_io;
+struct tee;
+
+int tee_shm_alloc_io(struct tee_context *ctx, struct tee_shm_io *shm_io);
+void tee_shm_free_io(struct tee_shm *shm);
+
+int tee_shm_fd_for_rpc(struct tee_context *ctx, struct tee_shm_io *shm_io);
+
+struct tee_shm *tee_shm_alloc(struct tee *tee, size_t size, uint32_t flags);
+void tee_shm_free(struct tee_shm *shm);
+
+struct tee_shm *tee_shm_get(struct tee_context *ctx, TEEC_SharedMemory *c_shm,
+               size_t size, int offset);
+void tee_shm_put(struct tee_context *ctx, struct tee_shm *shm);
+
+#endif /* __TEE_SHM_H__ */
diff --git a/security/optee_linuxdriver/core/tee_supp_com.c b/security/optee_linuxdriver/core/tee_supp_com.c
new file mode 100644 (file)
index 0000000..3e60e91
--- /dev/null
@@ -0,0 +1,272 @@
+/*
+ * Copyright (c) 2014, STMicroelectronics International N.V.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License Version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ */
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/mutex.h>
+#include <linux/miscdevice.h>
+#include <linux/uaccess.h>
+#include <linux/anon_inodes.h>
+#include <linux/semaphore.h>
+#include <linux/mm.h>
+#include <linux/sched.h>
+#include <linux/device.h>
+
+#include "tee_shm.h"
+#include "tee_core.h"
+#include "tee_supp_com.h"
+
+#define TEE_RPC_BUFFER 0x00000001
+#define TEE_RPC_VALUE  0x00000002
+
+enum teec_rpc_result tee_supp_cmd(struct tee *tee,
+                                 uint32_t id, void *data, size_t datalen)
+{
+       struct tee_rpc *rpc = tee->rpc;
+       enum teec_rpc_result res = TEEC_RPC_FAIL;
+       size_t size;
+       struct task_struct *task = current;
+
+       dev_dbg(tee->dev, "> tgid:[%d] id:[0x%08x]\n", task->tgid, id);
+
+       if (atomic_read(&rpc->used) == 0) {
+               dev_err(tee->dev, "%s: ERROR Supplicant application NOT ready\n"
+                               , __func__);
+               goto out;
+       }
+
+       switch (id) {
+       case TEE_RPC_ICMD_ALLOCATE:
+               {
+                       struct tee_rpc_alloc *alloc;
+                       struct tee_shm *shmint;
+
+                       alloc = (struct tee_rpc_alloc *)data;
+                       size = alloc->size;
+                       memset(alloc, 0, sizeof(struct tee_rpc_alloc));
+                       shmint = tee_shm_alloc_from_rpc(tee, size);
+                       if (IS_ERR_OR_NULL(shmint))
+                               break;
+
+                       alloc->size = size;
+                       alloc->data = (void *)shmint->paddr;
+                       alloc->shm = shmint;
+                       res = TEEC_RPC_OK;
+
+                       break;
+               }
+       case TEE_RPC_ICMD_FREE:
+               {
+                       struct tee_rpc_free *free;
+
+                       free = (struct tee_rpc_free *)data;
+                       tee_shm_free_from_rpc(free->shm);
+                       res = TEEC_RPC_OK;
+                       break;
+               }
+       case TEE_RPC_ICMD_INVOKE:
+               {
+                       if (sizeof(rpc->commToUser) < datalen)
+                               break;
+
+                       mutex_lock(&rpc->outsync);
+
+                       memcpy(&rpc->commToUser, data, datalen);
+
+                       mutex_unlock(&rpc->outsync);
+
+                       dev_dbg(tee->dev,
+                               "Supplicant Cmd: %x. Give hand to supplicant\n",
+                               rpc->commToUser.cmd);
+
+                       up(&rpc->datatouser);
+
+                       down(&rpc->datafromuser);
+
+                       dev_dbg(tee->dev,
+                               "Supplicant Cmd: %x. Give hand to fw\n",
+                               rpc->commToUser.cmd);
+
+                       mutex_lock(&rpc->insync);
+
+                       memcpy(data, &rpc->commFromUser, datalen);
+
+                       mutex_unlock(&rpc->insync);
+
+                       res = TEEC_RPC_OK;
+
+                       break;
+               }
+       default:
+               /* not supported */
+               break;
+       }
+
+out:
+       dev_dbg(tee->dev, "< res: [%d]\n", res);
+
+       return res;
+}
+EXPORT_SYMBOL(tee_supp_cmd);
+
+ssize_t tee_supp_read(struct file *filp, char __user *buffer,
+                 size_t length, loff_t *offset)
+{
+       struct tee_context *ctx = (struct tee_context *)(filp->private_data);
+       struct tee *tee;
+       struct tee_rpc *rpc;
+       struct task_struct *task = current;
+       int ret;
+
+       BUG_ON(!ctx);
+       tee = ctx->tee;
+       BUG_ON(!tee);
+       BUG_ON(!tee->dev);
+       BUG_ON(!tee->rpc);
+
+       dev_dbg(tee->dev, "> ctx %p\n", ctx);
+
+       rpc = tee->rpc;
+
+       if (atomic_read(&rpc->used) == 0) {
+               dev_err(tee->dev, "%s: ERROR Supplicant application NOT ready\n"
+                               , __func__);
+               ret = -EPERM;
+               goto out;
+       }
+
+       if (down_interruptible(&rpc->datatouser))
+               return -ERESTARTSYS;
+
+       dev_dbg(tee->dev, "> tgid:[%d]\n", task->tgid);
+
+       mutex_lock(&rpc->outsync);
+
+       ret =
+           sizeof(rpc->commToUser) - sizeof(rpc->commToUser.cmds) +
+           sizeof(rpc->commToUser.cmds[0]) * rpc->commToUser.nbr_bf;
+       if (length < ret) {
+               ret = -EINVAL;
+       } else {
+               if (copy_to_user(buffer, &rpc->commToUser, ret)) {
+                       dev_err(tee->dev,
+                               "[%s] error, copy_to_user failed!\n", __func__);
+                       ret = -EINVAL;
+               }
+       }
+
+       mutex_unlock(&rpc->outsync);
+
+out:
+       dev_dbg(tee->dev, "< [%d]\n", ret);
+       return ret;
+}
+
+ssize_t tee_supp_write(struct file *filp, const char __user *buffer,
+                  size_t length, loff_t *offset)
+{
+       struct tee_context *ctx = (struct tee_context *)(filp->private_data);
+       struct tee *tee;
+       struct tee_rpc *rpc;
+       struct task_struct *task = current;
+       int ret = 0;
+
+       BUG_ON(!ctx);
+       BUG_ON(!ctx->tee);
+       BUG_ON(!ctx->tee->rpc);
+       tee = ctx->tee;
+       rpc = tee->rpc;
+       dev_dbg(tee->dev, "> tgid:[%d]\n", task->tgid);
+
+       if (atomic_read(&rpc->used) == 0) {
+               dev_err(tee->dev, "%s: ERROR Supplicant application NOT ready\n"
+                               , __func__);
+               goto out;
+       }
+
+       if (length > 0 && length < sizeof(rpc->commFromUser)) {
+               uint32_t i;
+
+               mutex_lock(&rpc->insync);
+
+               if (copy_from_user(&rpc->commFromUser, buffer, length)) {
+                       dev_err(tee->dev,
+                               "%s: ERROR, tee_session copy_from_user failed\n",
+                               __func__);
+                       mutex_unlock(&rpc->insync);
+                       ret = -EINVAL;
+                       goto out;
+               }
+
+               /* Translate virtual address of caller into physical address */
+               for (i = 0; i < rpc->commFromUser.nbr_bf; i++) {
+                       if (rpc->commFromUser.cmds[i].type == TEE_RPC_BUFFER
+                           && rpc->commFromUser.cmds[i].buffer) {
+                               struct vm_area_struct *vma =
+                                   find_vma(current->mm,
+                                            (unsigned long)rpc->
+                                            commFromUser.cmds[i].buffer);
+                               if (vma != NULL) {
+                                       struct tee_shm *shm =
+                                           vma->vm_private_data;
+                                       BUG_ON(!shm);
+                                       dev_dbg(tee->dev,
+                                               "%d gid2pa(0x%p => %x)\n", i,
+                                               rpc->commFromUser.cmds[i].
+                                               buffer,
+                                               (unsigned int)shm->paddr);
+                                       rpc->commFromUser.cmds[i].buffer =
+                                           (void *)shm->paddr;
+                               } else
+                                       dev_dbg(tee->dev,
+                                               " gid2pa(0x%p => NULL\n)",
+                                               rpc->commFromUser.cmds[i].
+                                               buffer);
+                       }
+               }
+
+               mutex_unlock(&rpc->insync);
+               up(&rpc->datafromuser);
+               ret = length;
+       }
+
+out:
+       dev_dbg(tee->dev, "< [%d]\n", ret);
+       return ret;
+}
+
+int tee_supp_init(struct tee *tee)
+{
+       struct tee_rpc *rpc =
+           devm_kzalloc(tee->dev, sizeof(struct tee_rpc), GFP_KERNEL);
+       if (!rpc) {
+               dev_err(tee->dev, "%s: can't allocate tee_rpc structure\n",
+                               __func__);
+               return -ENOMEM;
+       }
+
+       rpc->datafromuser = (struct semaphore)
+           __SEMAPHORE_INITIALIZER(rpc->datafromuser, 0);
+       rpc->datatouser = (struct semaphore)
+           __SEMAPHORE_INITIALIZER(rpc->datatouser, 0);
+       mutex_init(&rpc->outsync);
+       mutex_init(&rpc->insync);
+       atomic_set(&rpc->used, 0);
+       tee->rpc = rpc;
+       return 0;
+}
+
+void tee_supp_deinit(struct tee *tee)
+{
+       devm_kfree(tee->dev, tee->rpc);
+       tee->rpc = NULL;
+}
diff --git a/security/optee_linuxdriver/core/tee_supp_com.h b/security/optee_linuxdriver/core/tee_supp_com.h
new file mode 100644 (file)
index 0000000..841fe9f
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2014, STMicroelectronics International N.V.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License Version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ */
+#ifndef TEE_SUPP_COMM_H
+#define TEE_SUPP_COMM_H
+
+#define TEE_RPC_ICMD_ALLOCATE 0x1001
+#define TEE_RPC_ICMD_FREE     0x1002
+#define TEE_RPC_ICMD_INVOKE   0x1003
+
+#define TEE_RPC_NBR_BUFF 1
+#define TEE_RPC_DATA_SIZE 64
+#define TEE_RPC_BUFFER_NUMBER 5
+
+#define TEE_RPC_STATE_IDLE    0x00
+#define TEE_RPC_STATE_ACTIVE  0x01
+
+/* Keep aligned with optee_client (user space) */
+#define TEE_RPC_BUFFER         0x00000001
+#define TEE_RPC_VALUE          0x00000002
+#define TEE_RPC_LOAD_TA                0x10000001
+/*
+ * Handled within the driver only
+ * Keep aligned with optee_os (secure space)
+ */
+#define TEE_RPC_MUTEX_WAIT             0x20000000
+#define TEE_RPC_WAIT_QUEUE_SLEEP       0x20000001
+#define TEE_RPC_WAIT_QUEUE_WAKEUP      0x20000002
+#define TEE_RPC_WAIT                   0x30000000
+
+/* Parameters for TEE_RPC_WAIT_MUTEX above */
+#define TEE_MUTEX_WAIT_SLEEP   0
+#define TEE_MUTEX_WAIT_WAKEUP  1
+#define TEE_MUTEX_WAIT_DELETE  2
+
+#include <linux/semaphore.h>
+
+/**
+ * struct tee_rpc_bf - Contains definition of the tee com buffer
+ * @state: Buffer state
+ * @data: Command data
+ */
+struct tee_rpc_bf {
+       uint32_t state;
+       uint8_t data[TEE_RPC_DATA_SIZE];
+};
+
+struct tee_rpc_alloc {
+       uint32_t size;  /* size of block */
+       void *data;     /* pointer to data */
+       void *shm;      /* pointer to an opaque data, being shm structure */
+};
+
+struct tee_rpc_free {
+       void *shm;      /* pointer to an opaque data, being shm structure */
+};
+
+struct tee_rpc_cmd {
+       void *buffer;
+       uint32_t size;
+       uint32_t type;
+       int fd;
+};
+
+struct tee_rpc_invoke {
+       uint32_t cmd;
+       uint32_t res;
+       uint32_t nbr_bf;
+       struct tee_rpc_cmd cmds[TEE_RPC_BUFFER_NUMBER];
+};
+
+struct tee_rpc {
+       struct tee_rpc_invoke commToUser;
+       struct tee_rpc_invoke commFromUser;
+       struct semaphore datatouser;
+       struct semaphore datafromuser;
+       struct mutex outsync; /* Out sync mutex */
+       struct mutex insync; /* In sync mutex */
+       struct mutex reqsync; /* Request sync mutex */
+       atomic_t  used;
+};
+
+enum teec_rpc_result {
+       TEEC_RPC_OK,
+       TEEC_RPC_FAIL
+};
+
+struct tee;
+
+int tee_supp_init(struct tee *tee);
+void tee_supp_deinit(struct tee *tee);
+
+enum teec_rpc_result tee_supp_cmd(struct tee *tee,
+                                 uint32_t id, void *data, size_t datalen);
+
+ssize_t tee_supp_read(struct file *filp, char __user *buffer,
+                 size_t length, loff_t *offset);
+
+ssize_t tee_supp_write(struct file *filp, const char __user *buffer,
+                  size_t length, loff_t *offset);
+
+#endif
diff --git a/security/optee_linuxdriver/core/tee_sysfs.c b/security/optee_linuxdriver/core/tee_sysfs.c
new file mode 100644 (file)
index 0000000..c9512ad
--- /dev/null
@@ -0,0 +1,204 @@
+/*
+ * Copyright (c) 2014, STMicroelectronics International N.V.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License Version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ */
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/stat.h>
+#include <linux/atomic.h>
+#include <asm/page.h>
+
+#include "tee_core_priv.h"
+
+static ssize_t dump_show(struct device *device,
+                        struct device_attribute *attr, char *buf)
+{
+       struct tee *tee = dev_get_drvdata(device);
+       int len;
+       char *tmp_buf;
+
+       tmp_buf = kzalloc(PAGE_SIZE, GFP_KERNEL);
+       if (!tmp_buf) {
+               printk(KERN_ALERT "%s : Unable to get buf memory\n", __func__);
+               return -ENOMEM;
+       }
+
+       len = tee_context_dump(tee, tmp_buf, PAGE_SIZE - 128);
+
+       if (len > 0)
+               len = snprintf(buf, PAGE_SIZE, "%s", tmp_buf);
+       kfree(tmp_buf);
+
+       return len;
+}
+
+static ssize_t stat_show(struct device *device,
+                        struct device_attribute *attr, char *buf)
+{
+       struct tee *tee = dev_get_drvdata(device);
+
+       return snprintf(buf, PAGE_SIZE, "%d/%d %d/%d %d/%d %d/%d\n",
+                       atomic_read(&tee->refcount),
+                       tee->max_refcount,
+                       tee->stats[TEE_STATS_CONTEXT_IDX].count,
+                       tee->stats[TEE_STATS_CONTEXT_IDX].max,
+                       tee->stats[TEE_STATS_SESSION_IDX].count,
+                       tee->stats[TEE_STATS_SESSION_IDX].max,
+                       tee->stats[TEE_STATS_SHM_IDX].count,
+                       tee->stats[TEE_STATS_SHM_IDX].max);
+}
+
+static ssize_t info_show(struct device *device,
+                        struct device_attribute *attr, char *buf)
+{
+       struct tee *tee = dev_get_drvdata(device);
+
+       return snprintf(buf, PAGE_SIZE, "%s iminor=%d dev=\"%s\" state=%d\n",
+                       dev_name(tee->dev), tee->miscdev.minor,
+                       dev_name(tee->miscdev.this_device), tee->state);
+}
+
+static ssize_t name_show(struct device *device,
+                        struct device_attribute *attr, char *buf)
+{
+       struct tee *tee = dev_get_drvdata(device);
+
+       return snprintf(buf, PAGE_SIZE, "%s\n", tee->name);
+}
+
+static ssize_t type_show(struct device *device,
+                        struct device_attribute *attr, char *buf)
+{
+       struct tee *tee = dev_get_drvdata(device);
+
+       return snprintf(buf, PAGE_SIZE, "%s\n", tee->ops->type);
+}
+
+static ssize_t refcount_show(struct device *device,
+                            struct device_attribute *attr, char *buf)
+{
+       struct tee *tee = dev_get_drvdata(device);
+
+       return snprintf(buf, PAGE_SIZE, "%d\n", atomic_read(&tee->refcount));
+}
+
+static ssize_t conf_show(struct device *device,
+                        struct device_attribute *attr, char *buf)
+{
+       struct tee *tee = dev_get_drvdata(device);
+
+       return snprintf(buf, PAGE_SIZE, "0x%08x\n", tee->conf);
+}
+
+static ssize_t test_show(struct device *device,
+                        struct device_attribute *attr, char *buf)
+{
+       struct tee *tee = dev_get_drvdata(device);
+
+       return snprintf(buf, PAGE_SIZE, "%08X\n", tee->test);
+}
+
+static ssize_t test_store(struct device *device,
+                         struct device_attribute *attr, const char *buf,
+                         size_t count)
+{
+       struct tee *tee = dev_get_drvdata(device);
+       unsigned long val;
+       int status;
+
+       status = kstrtoul(buf, 0, &val);
+       if (status)
+               return status;
+
+       if ((tee->conf & TEE_CONF_TEST_MODE) == TEE_CONF_TEST_MODE)
+               tee->test = val;
+
+       return count;
+}
+
+/*
+ * A state-to-string lookup table, for exposing a human readable state
+ * via sysfs. Always keep in sync with enum tee_state
+ */
+static const char *const tee_state_string[] = {
+       "offline",
+       "online",
+       "suspended",
+       "running",
+       "crashed",
+       "invalid",
+};
+
+static ssize_t tee_show_state(struct device *device,
+                         struct device_attribute *attr, char *buf)
+{
+       struct tee *tee = dev_get_drvdata(device);
+
+       int state = tee->state > TEE_LAST ? TEE_LAST : tee->state;
+
+       return snprintf(buf, PAGE_SIZE, "%s (%d)\n", tee_state_string[state],
+                       tee->state);
+}
+
+/*
+ * In the following, 0660 is (S_IWUGO | S_IRUGO)
+ */
+static struct device_attribute device_attrs[] = {
+       __ATTR_RO(dump),
+       __ATTR_RO(stat),
+       __ATTR_RO(info),
+       __ATTR(test, (0660), test_show, test_store),
+       __ATTR(state, S_IRUGO, tee_show_state, NULL),
+       __ATTR(name, S_IRUGO, name_show, NULL),
+       __ATTR(refcount, S_IRUGO, refcount_show, NULL),
+       __ATTR(type, S_IRUGO, type_show, NULL),
+       __ATTR(conf, S_IRUGO, conf_show, NULL),
+};
+
+void tee_init_sysfs(struct tee *tee)
+{
+       int i, error = 0;
+
+       if (!tee)
+               return;
+
+       if (dev_get_drvdata(tee->miscdev.this_device) != tee) {
+               dev_err(_DEV(tee), "drvdata is not valid\n");
+               return;
+       }
+
+       for (i = 0; i < ARRAY_SIZE(device_attrs); i++) {
+               error =
+                   device_create_file(tee->miscdev.this_device,
+                                      &device_attrs[i]);
+               if (error)
+                       break;
+       }
+
+       if (error) {
+               while (--i >= 0)
+                       device_remove_file(tee->miscdev.this_device,
+                                          &device_attrs[i]);
+       }
+       /* location /sys/class/<class name>/<dev_name()>/<name> ->
+        * /sys/class/misc/teelx00/info */
+}
+
+void tee_cleanup_sysfs(struct tee *tee)
+{
+       int i;
+
+       if (!tee)
+               return;
+
+       for (i = 0; i < ARRAY_SIZE(device_attrs); i++)
+               device_remove_file(tee->miscdev.this_device, &device_attrs[i]);
+}
diff --git a/security/optee_linuxdriver/core/tee_sysfs.h b/security/optee_linuxdriver/core/tee_sysfs.h
new file mode 100644 (file)
index 0000000..fbd797b
--- /dev/null
@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 2014, STMicroelectronics International N.V.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License Version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ */
+#ifndef __TEE_SYSFS_H__
+#define __TEE_SYSFS_H__
+
+struct tee;
+
+void tee_init_sysfs(struct tee *tee);
+void tee_cleanup_sysfs(struct tee *tee);
+
+#endif
diff --git a/security/optee_linuxdriver/core/tee_wait_queue.c b/security/optee_linuxdriver/core/tee_wait_queue.c
new file mode 100644 (file)
index 0000000..1677040
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2015, Linaro Limited
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License Version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ */
+#include <linux/types.h>
+#include <linux/completion.h>
+#include <linux/slab.h>
+#include "tee_wait_queue.h"
+
+struct tee_wait_queue {
+       struct list_head link;
+       struct completion comp;
+       u32 key;
+};
+
+void tee_wait_queue_init(struct tee_wait_queue_private *priv)
+{
+       mutex_init(&priv->mu);
+       INIT_LIST_HEAD(&priv->db);
+}
+EXPORT_SYMBOL(tee_wait_queue_init);
+
+void tee_wait_queue_exit(struct tee_wait_queue_private *priv)
+{
+       mutex_destroy(&priv->mu);
+}
+EXPORT_SYMBOL(tee_wait_queue_exit);
+
+static struct tee_wait_queue *tee_wait_queue_get(struct device *dev,
+                               struct tee_wait_queue_private *priv, u32 key)
+{
+       struct tee_wait_queue *w;
+
+       mutex_lock(&priv->mu);
+
+       list_for_each_entry(w, &priv->db, link)
+               if (w->key == key)
+                       goto out;
+
+       w = kmalloc(sizeof(struct tee_wait_queue), GFP_KERNEL);
+       if (!w)
+               goto out;
+
+       init_completion(&w->comp);
+       w->key = key;
+       list_add_tail(&w->link, &priv->db);
+out:
+       mutex_unlock(&priv->mu);
+       return w;
+}
+
+void tee_wait_queue_sleep(struct device *dev,
+                       struct tee_wait_queue_private *priv, u32 key)
+{
+       struct tee_wait_queue *w = tee_wait_queue_get(dev, priv, key);
+
+       if (!w)
+               return;
+
+       wait_for_completion(&w->comp);
+       mutex_lock(&priv->mu);
+       list_del(&w->link);
+       mutex_unlock(&priv->mu);
+       kfree(w);
+}
+EXPORT_SYMBOL(tee_wait_queue_sleep);
+
+void tee_wait_queue_wakeup(struct device *dev,
+                       struct tee_wait_queue_private *priv, u32 key)
+{
+       struct tee_wait_queue *w = tee_wait_queue_get(dev, priv, key);
+
+       if (!w)
+               return;
+
+       complete(&w->comp);
+}
+EXPORT_SYMBOL(tee_wait_queue_wakeup);
diff --git a/security/optee_linuxdriver/core/tee_wait_queue.h b/security/optee_linuxdriver/core/tee_wait_queue.h
new file mode 100644 (file)
index 0000000..2ba619b
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2015, Linaro Limited
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License Version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ */
+#ifndef TEE_WAIT_QUEUE_H
+#define TEE_WAIT_QUEUE_H
+
+#include <linux/mutex.h>
+#include <linux/list.h>
+#include <linux/device.h>
+
+struct tee_wait_queue_private {
+       struct mutex mu;
+       struct list_head db;
+};
+
+void tee_wait_queue_init(struct tee_wait_queue_private *priv);
+void tee_wait_queue_exit(struct tee_wait_queue_private *priv);
+void tee_wait_queue_sleep(struct device *dev,
+                       struct tee_wait_queue_private *priv, u32 key);
+void tee_wait_queue_wakeup(struct device *dev,
+                       struct tee_wait_queue_private *priv, u32 key);
+
+#endif /*TEE_WAIT_QUEUE_H*/
diff --git a/security/optee_linuxdriver/fdts/fvp-foundation-gicv2-psci.dts b/security/optee_linuxdriver/fdts/fvp-foundation-gicv2-psci.dts
new file mode 100644 (file)
index 0000000..2237fbe
--- /dev/null
@@ -0,0 +1,202 @@
+/*
+ * Copyright (c) 2013-2014, ARM Limited and Contributors. All rights reserved.
+ *
+ * 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.
+ *
+ * Neither the name of the ARM 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.
+ */
+
+/dts-v1/;
+
+/memreserve/ 0x81000000 0x00100000;
+/memreserve/ 0x80000000 0x00010000;
+
+/ {
+};
+
+/ {
+       model = "FVP Foundation";
+       compatible = "arm,fvp-base", "arm,vexpress";
+       interrupt-parent = <&gic>;
+       #address-cells = <2>;
+       #size-cells = <2>;
+
+       chosen { };
+
+       aliases {
+               serial0 = &v2m_serial0;
+               serial1 = &v2m_serial1;
+               serial2 = &v2m_serial2;
+               serial3 = &v2m_serial3;
+       };
+
+       psci {
+               compatible = "arm,psci";
+               method = "smc";
+               cpu_suspend = <0xc4000001>;
+               cpu_off = <0x84000002>;
+               cpu_on = <0xc4000003>;
+       };
+
+       cpus {
+               #address-cells = <2>;
+               #size-cells = <0>;
+
+               cpu@0 {
+                       device_type = "cpu";
+                       compatible = "arm,armv8";
+                       reg = <0x0 0x0>;
+                       enable-method = "psci";
+               };
+               cpu@1 {
+                       device_type = "cpu";
+                       compatible = "arm,armv8";
+                       reg = <0x0 0x1>;
+                       enable-method = "psci";
+               };
+               cpu@2 {
+                       device_type = "cpu";
+                       compatible = "arm,armv8";
+                       reg = <0x0 0x2>;
+                       enable-method = "psci";
+               };
+               cpu@3 {
+                       device_type = "cpu";
+                       compatible = "arm,armv8";
+                       reg = <0x0 0x3>;
+                       enable-method = "psci";
+               };
+       };
+
+       memory@80000000 {
+               device_type = "memory";
+               reg = <0x00000000 0x80000000 0 0x7F000000>,
+                     <0x00000008 0x80000000 0 0x80000000>;
+       };
+
+       gic: interrupt-controller@2f000000 {
+               compatible = "arm,cortex-a15-gic", "arm,cortex-a9-gic";
+               #interrupt-cells = <3>;
+               #address-cells = <0>;
+               interrupt-controller;
+               reg = <0x0 0x2f000000 0 0x10000>,
+                     <0x0 0x2c000000 0 0x2000>,
+                     <0x0 0x2c010000 0 0x2000>,
+                     <0x0 0x2c02F000 0 0x2000>;
+               interrupts = <1 9 0xf04>;
+       };
+
+       timer {
+               compatible = "arm,armv8-timer";
+               interrupts = <1 13 0xff01>,
+                            <1 14 0xff01>,
+                            <1 11 0xff01>,
+                            <1 10 0xff01>;
+               clock-frequency = <100000000>;
+       };
+
+       timer@2a810000 {
+                       compatible = "arm,armv7-timer-mem";
+                       reg = <0x0 0x2a810000 0x0 0x10000>;
+                       clock-frequency = <100000000>;
+                       #address-cells = <2>;
+                       #size-cells = <2>;
+                       ranges;
+                       frame@2a830000 {
+                               frame-number = <1>;
+                               interrupts = <0 26 4>;
+                               reg = <0x0 0x2a830000 0x0 0x10000>;
+                       };
+       };
+
+       pmu {
+               compatible = "arm,armv8-pmuv3";
+               interrupts = <0 60 4>,
+                            <0 61 4>,
+                            <0 62 4>,
+                            <0 63 4>;
+       };
+
+       smb {
+               compatible = "simple-bus";
+
+               #address-cells = <2>;
+               #size-cells = <1>;
+               ranges = <0 0 0 0x08000000 0x04000000>,
+                        <1 0 0 0x14000000 0x04000000>,
+                        <2 0 0 0x18000000 0x04000000>,
+                        <3 0 0 0x1c000000 0x04000000>,
+                        <4 0 0 0x0c000000 0x04000000>,
+                        <5 0 0 0x10000000 0x04000000>;
+
+               #interrupt-cells = <1>;
+               interrupt-map-mask = <0 0 63>;
+               interrupt-map = <0 0  0 &gic 0  0 4>,
+                               <0 0  1 &gic 0  1 4>,
+                               <0 0  2 &gic 0  2 4>,
+                               <0 0  3 &gic 0  3 4>,
+                               <0 0  4 &gic 0  4 4>,
+                               <0 0  5 &gic 0  5 4>,
+                               <0 0  6 &gic 0  6 4>,
+                               <0 0  7 &gic 0  7 4>,
+                               <0 0  8 &gic 0  8 4>,
+                               <0 0  9 &gic 0  9 4>,
+                               <0 0 10 &gic 0 10 4>,
+                               <0 0 11 &gic 0 11 4>,
+                               <0 0 12 &gic 0 12 4>,
+                               <0 0 13 &gic 0 13 4>,
+                               <0 0 14 &gic 0 14 4>,
+                               <0 0 15 &gic 0 15 4>,
+                               <0 0 16 &gic 0 16 4>,
+                               <0 0 17 &gic 0 17 4>,
+                               <0 0 18 &gic 0 18 4>,
+                               <0 0 19 &gic 0 19 4>,
+                               <0 0 20 &gic 0 20 4>,
+                               <0 0 21 &gic 0 21 4>,
+                               <0 0 22 &gic 0 22 4>,
+                               <0 0 23 &gic 0 23 4>,
+                               <0 0 24 &gic 0 24 4>,
+                               <0 0 25 &gic 0 25 4>,
+                               <0 0 26 &gic 0 26 4>,
+                               <0 0 27 &gic 0 27 4>,
+                               <0 0 28 &gic 0 28 4>,
+                               <0 0 29 &gic 0 29 4>,
+                               <0 0 30 &gic 0 30 4>,
+                               <0 0 31 &gic 0 31 4>,
+                               <0 0 32 &gic 0 32 4>,
+                               <0 0 33 &gic 0 33 4>,
+                               <0 0 34 &gic 0 34 4>,
+                               <0 0 35 &gic 0 35 4>,
+                               <0 0 36 &gic 0 36 4>,
+                               <0 0 37 &gic 0 37 4>,
+                               <0 0 38 &gic 0 38 4>,
+                               <0 0 39 &gic 0 39 4>,
+                               <0 0 40 &gic 0 40 4>,
+                               <0 0 41 &gic 0 41 4>,
+                               <0 0 42 &gic 0 42 4>;
+
+               /include/ "fvp-foundation-motherboard.dtsi"
+       };
+};
diff --git a/security/optee_linuxdriver/fdts/fvp-foundation-motherboard.dtsi b/security/optee_linuxdriver/fdts/fvp-foundation-motherboard.dtsi
new file mode 100644 (file)
index 0000000..fd41c8a
--- /dev/null
@@ -0,0 +1,197 @@
+/*
+ * Copyright (c) 2013-2014, ARM Limited and Contributors. All rights reserved.
+ *
+ * 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.
+ *
+ * Neither the name of the ARM 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.
+ */
+
+       motherboard {
+               arm,v2m-memory-map = "rs1";
+               compatible = "arm,vexpress,v2m-p1", "simple-bus";
+               #address-cells = <2>; /* SMB chipselect number and offset */
+               #size-cells = <1>;
+               #interrupt-cells = <1>;
+               ranges;
+
+               ethernet@2,02000000 {
+                       compatible = "smsc,lan91c111";
+                       reg = <2 0x02000000 0x10000>;
+                       interrupts = <15>;
+               };
+
+               v2m_clk24mhz: clk24mhz {
+                       compatible = "fixed-clock";
+                       #clock-cells = <0>;
+                       clock-frequency = <24000000>;
+                       clock-output-names = "v2m:clk24mhz";
+               };
+
+               v2m_refclk1mhz: refclk1mhz {
+                       compatible = "fixed-clock";
+                       #clock-cells = <0>;
+                       clock-frequency = <1000000>;
+                       clock-output-names = "v2m:refclk1mhz";
+               };
+
+               v2m_refclk32khz: refclk32khz {
+                       compatible = "fixed-clock";
+                       #clock-cells = <0>;
+                       clock-frequency = <32768>;
+                       clock-output-names = "v2m:refclk32khz";
+               };
+
+               iofpga@3,00000000 {
+                       compatible = "arm,amba-bus", "simple-bus";
+                       #address-cells = <1>;
+                       #size-cells = <1>;
+                       ranges = <0 3 0 0x200000>;
+
+                       v2m_sysreg: sysreg@010000 {
+                               compatible = "arm,vexpress-sysreg";
+                               reg = <0x010000 0x1000>;
+                               gpio-controller;
+                               #gpio-cells = <2>;
+                       };
+
+                       v2m_sysctl: sysctl@020000 {
+                               compatible = "arm,sp810", "arm,primecell";
+                               reg = <0x020000 0x1000>;
+                               clocks = <&v2m_refclk32khz>, <&v2m_refclk1mhz>, <&v2m_clk24mhz>;
+                               clock-names = "refclk", "timclk", "apb_pclk";
+                               #clock-cells = <1>;
+                               clock-output-names = "timerclken0", "timerclken1", "timerclken2", "timerclken3";
+                       };
+
+                       v2m_serial0: uart@090000 {
+                               compatible = "arm,pl011", "arm,primecell";
+                               reg = <0x090000 0x1000>;
+                               interrupts = <5>;
+                               clocks = <&v2m_clk24mhz>, <&v2m_clk24mhz>;
+                               clock-names = "uartclk", "apb_pclk";
+                       };
+
+                       v2m_serial1: uart@0a0000 {
+                               compatible = "arm,pl011", "arm,primecell";
+                               reg = <0x0a0000 0x1000>;
+                               interrupts = <6>;
+                               clocks = <&v2m_clk24mhz>, <&v2m_clk24mhz>;
+                               clock-names = "uartclk", "apb_pclk";
+                       };
+
+                       v2m_serial2: uart@0b0000 {
+                               compatible = "arm,pl011", "arm,primecell";
+                               reg = <0x0b0000 0x1000>;
+                               interrupts = <7>;
+                               clocks = <&v2m_clk24mhz>, <&v2m_clk24mhz>;
+                               clock-names = "uartclk", "apb_pclk";
+                       };
+
+                       v2m_serial3: uart@0c0000 {
+                               compatible = "arm,pl011", "arm,primecell";
+                               reg = <0x0c0000 0x1000>;
+                               interrupts = <8>;
+                               clocks = <&v2m_clk24mhz>, <&v2m_clk24mhz>;
+                               clock-names = "uartclk", "apb_pclk";
+                       };
+
+                       wdt@0f0000 {
+                               compatible = "arm,sp805", "arm,primecell";
+                               reg = <0x0f0000 0x1000>;
+                               interrupts = <0>;
+                               clocks = <&v2m_refclk32khz>, <&v2m_clk24mhz>;
+                               clock-names = "wdogclk", "apb_pclk";
+                       };
+
+                       v2m_timer01: timer@110000 {
+                               compatible = "arm,sp804", "arm,primecell";
+                               reg = <0x110000 0x1000>;
+                               interrupts = <2>;
+                               clocks = <&v2m_sysctl 0>, <&v2m_sysctl 1>, <&v2m_clk24mhz>;
+                               clock-names = "timclken1", "timclken2", "apb_pclk";
+                       };
+
+                       v2m_timer23: timer@120000 {
+                               compatible = "arm,sp804", "arm,primecell";
+                               reg = <0x120000 0x1000>;
+                               interrupts = <3>;
+                               clocks = <&v2m_sysctl 2>, <&v2m_sysctl 3>, <&v2m_clk24mhz>;
+                               clock-names = "timclken1", "timclken2", "apb_pclk";
+                       };
+
+                       rtc@170000 {
+                               compatible = "arm,pl031", "arm,primecell";
+                               reg = <0x170000 0x1000>;
+                               interrupts = <4>;
+                               clocks = <&v2m_clk24mhz>;
+                               clock-names = "apb_pclk";
+                       };
+
+                       virtio_block@0130000 {
+                               compatible = "virtio,mmio";
+                               reg = <0x130000 0x1000>;
+                               interrupts = <0x2a>;
+                       };
+               };
+
+               v2m_fixed_3v3: fixedregulator@0 {
+                       compatible = "regulator-fixed";
+                       regulator-name = "3V3";
+                       regulator-min-microvolt = <3300000>;
+                       regulator-max-microvolt = <3300000>;
+                       regulator-always-on;
+               };
+
+
+               mcc {
+                       compatible = "arm,vexpress,config-bus", "simple-bus";
+                       arm,vexpress,config-bridge = <&v2m_sysreg>;
+
+                       reset@0 {
+                               compatible = "arm,vexpress-reset";
+                               arm,vexpress-sysreg,func = <5 0>;
+                       };
+
+                       muxfpga@0 {
+                               compatible = "arm,vexpress-muxfpga";
+                               arm,vexpress-sysreg,func = <7 0>;
+                       };
+
+                       shutdown@0 {
+                               compatible = "arm,vexpress-shutdown";
+                               arm,vexpress-sysreg,func = <8 0>;
+                       };
+
+                       reboot@0 {
+                               compatible = "arm,vexpress-reboot";
+                               arm,vexpress-sysreg,func = <9 0>;
+                       };
+
+                       dvimode@0 {
+                               compatible = "arm,vexpress-dvimode";
+                               arm,vexpress-sysreg,func = <11 0>;
+                       };
+               };
+       };
diff --git a/security/optee_linuxdriver/fdts/readme.txt b/security/optee_linuxdriver/fdts/readme.txt
new file mode 100644 (file)
index 0000000..e3fb474
--- /dev/null
@@ -0,0 +1,2 @@
+These files is a temporary workaround until the driver has switched
+to use CMA to allocate shared memory.
diff --git a/security/optee_linuxdriver/include/arm_common/teesmc.h b/security/optee_linuxdriver/include/arm_common/teesmc.h
new file mode 100644 (file)
index 0000000..6727db4
--- /dev/null
@@ -0,0 +1,700 @@
+/*
+ * Copyright (c) 2014, Linaro Limited
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License Version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ */
+#ifndef TEESMC_H
+#define TEESMC_H
+
+#ifndef ASM
+/*
+ * This section depends on uint64_t, uint32_t uint8_t already being
+ * defined. Since this file is used in several different environments
+ * (secure world OS and normal world Linux kernel to start with) where
+ * stdint.h may not be available it's the responsibility of the one
+ * including this file to provide those types.
+ */
+
+/*
+ * Trusted OS SMC interface.
+ *
+ * The SMC interface follows SMC Calling Convention
+ * (ARM_DEN0028A_SMC_Calling_Convention).
+ *
+ * The primary objective of this API is to provide a transport layer on
+ * which a Global Platform compliant TEE interfaces can be deployed. But the
+ * interface can also be used for other implementations.
+ *
+ * This file is divided in two parts.
+ * Part 1 deals with passing parameters to Trusted Applications running in
+ * a trusted OS in secure world.
+ * Part 2 deals with the lower level handling of the SMC.
+ */
+
+/*
+ *******************************************************************************
+ * Part 1 - passing parameters to Trusted Applications
+ *******************************************************************************
+ */
+
+/*
+ * Same values as TEE_PARAM_* from TEE Internal API
+ */
+#define TEESMC_ATTR_TYPE_NONE          0
+#define TEESMC_ATTR_TYPE_VALUE_INPUT   1
+#define TEESMC_ATTR_TYPE_VALUE_OUTPUT  2
+#define TEESMC_ATTR_TYPE_VALUE_INOUT   3
+#define TEESMC_ATTR_TYPE_MEMREF_INPUT  5
+#define TEESMC_ATTR_TYPE_MEMREF_OUTPUT 6
+#define TEESMC_ATTR_TYPE_MEMREF_INOUT  7
+
+#define TEESMC_ATTR_TYPE_MASK          0x7
+
+/*
+ * Meta parameter to be absorbed by the Secure OS and not passed
+ * to the Trusted Application.
+ *
+ * One example of this is a struct teesmc_meta_open_session which
+ * is added to TEESMC{32,64}_CMD_OPEN_SESSION.
+ */
+#define TEESMC_ATTR_META               0x8
+
+/*
+ * Used as an indication from normal world of compatible cache usage.
+ * 'I' stands for inner cache and 'O' for outer cache.
+ */
+#define TEESMC_ATTR_CACHE_I_NONCACHE   0x0
+#define TEESMC_ATTR_CACHE_I_WRITE_THR  0x1
+#define TEESMC_ATTR_CACHE_I_WRITE_BACK 0x2
+#define TEESMC_ATTR_CACHE_O_NONCACHE   0x0
+#define TEESMC_ATTR_CACHE_O_WRITE_THR  0x4
+#define TEESMC_ATTR_CACHE_O_WRITE_BACK 0x8
+
+#define TEESMC_ATTR_CACHE_NONCACHE     (TEESMC_ATTR_CACHE_I_NONCACHE | \
+                                        TEESMC_ATTR_CACHE_O_NONCACHE)
+#define TEESMC_ATTR_CACHE_DEFAULT      (TEESMC_ATTR_CACHE_I_WRITE_BACK | \
+                                        TEESMC_ATTR_CACHE_O_WRITE_BACK)
+
+#define TEESMC_ATTR_CACHE_SHIFT                4
+#define TEESMC_ATTR_CACHE_MASK         0xf
+
+#define TEESMC_CMD_OPEN_SESSION                0
+#define TEESMC_CMD_INVOKE_COMMAND      1
+#define TEESMC_CMD_CLOSE_SESSION       2
+#define TEESMC_CMD_CANCEL              3
+
+/**
+ * struct teesmc32_param_memref - memory reference
+ * @buf_ptr: Address of the buffer
+ * @size: Size of the buffer
+ *
+ * Secure and normal world communicates pointer via physical address instead of
+ * the virtual address with is usually used for pointers. This is because
+ * Secure and normal world has completely independant memory mapping. Normal
+ * world can even have a hypervisor which need to translate the guest
+ * physical address (AKA IPA in ARM lingo) to a real physical address
+ * before passing the structure to secure world.
+ */
+struct teesmc32_param_memref {
+       uint32_t buf_ptr;
+       uint32_t size;
+};
+
+/**
+ * struct teesmc32_param_memref - memory reference
+ * @buf_ptr: Address of the buffer
+ * @size: Size of the buffer
+ *
+ * See description of struct teesmc32_param_memref.
+ */
+struct teesmc64_param_memref {
+       uint64_t buf_ptr;
+       uint64_t size;
+};
+
+/**
+ * struct teesmc32_param_value - values
+ * @a: first value
+ * @b: second value
+ */
+struct teesmc32_param_value {
+       uint32_t a;
+       uint32_t b;
+};
+
+/**
+ * struct teesmc64_param_value - values
+ * @a: first value
+ * @b: second value
+ */
+struct teesmc64_param_value {
+       uint64_t a;
+       uint64_t b;
+};
+
+/**
+ * struct teesmc32_param - parameter
+ * @attr: attributes
+ * @memref: a memory reference
+ * @value: a value
+ *
+ * attr & TEESMC_ATTR_TYPE_MASK indicates if memref or value is used in the
+ * union. TEESMC_ATTR_TYPE_VALUE_* indicates value and
+ * TEESMC_ATTR_TYPE_MEMREF_* indicates memref. TEESMC_ATTR_TYPE_NONE
+ * indicates that none of the members are used.
+ */
+struct teesmc32_param {
+       uint32_t attr;
+       union {
+               struct teesmc32_param_memref memref;
+               struct teesmc32_param_value value;
+       } u;
+};
+
+/**
+ * struct teesmc64_param - parameter
+ * @attr: attributes
+ * @memref: a memory reference
+ * @value: a value
+ *
+ * See description of union teesmc32_param.
+ */
+struct teesmc64_param {
+       uint64_t attr;
+       union {
+               struct teesmc64_param_memref memref;
+               struct teesmc64_param_value value;
+       } u;
+};
+
+/**
+ * struct teesmc32_arg - SMC argument for Trusted OS
+ * @cmd: Command, one of TEESMC_CMD_*
+ * @ta_func: Trusted Application function, specific to the Trusted Application,
+ *          used if cmd == TEESMC_CMD_INVOKE_COMMAND
+ * @session: In parameter for all TEESMC_CMD_* except
+ *          TEESMC_CMD_OPEN_SESSION where it's an output paramter instead
+ * @ret: return value
+ * @ret_origin: origin of the return value
+ * @num_params: number of parameters supplied to the OS Command
+ * @params: the parameters supplied to the OS Command
+ *
+ * All normal SMC calls to Trusted OS uses this struct. If cmd requires
+ * further information than what these field holds it can be passed as a
+ * parameter tagged as meta (setting the TEESMC_ATTR_META bit in
+ * corresponding param_attrs). This is used for TEESMC_CMD_OPEN_SESSION
+ * to pass a struct teesmc32_meta_open_session which is needed find the
+ * Trusted Application and to indicate the credentials of the client.
+ */
+struct teesmc32_arg {
+       uint32_t cmd;
+       uint32_t ta_func;
+       uint32_t session;
+       uint32_t ret;
+       uint32_t ret_origin;
+       uint32_t num_params;
+       /*
+        * Commented out elements used to visualize the layout dynamic part
+        * of the struct. Note that these fields are not available at all
+        * if num_params == 0.
+        *
+        * params is accessed through the macro TEESMC32_GET_PARAMS
+        */
+
+       /* struct teesmc32_param params[num_params]; */
+};
+
+/**
+ * TEESMC32_GET_PARAMS - return pointer to union teesmc32_param *
+ *
+ * @x: Pointer to a struct teesmc32_arg
+ *
+ * Returns a pointer to the params[] inside a struct teesmc32_arg.
+ */
+#define TEESMC32_GET_PARAMS(x) \
+       (struct teesmc32_param *)(((struct teesmc32_arg *)(x)) + 1)
+
+/**
+ * TEESMC32_GET_ARG_SIZE - return size of struct teesmc32_arg
+ *
+ * @num_params: Number of parameters embedded in the struct teesmc32_arg
+ *
+ * Returns the size of the struct teesmc32_arg together with the number
+ * of embedded paramters.
+ */
+#define TEESMC32_GET_ARG_SIZE(num_params) \
+       (sizeof(struct teesmc32_arg) + \
+        sizeof(struct teesmc32_param) * (num_params))
+
+/**
+ * struct teesmc64_arg - SMC argument for Trusted OS
+ * @cmd: OS Command, one of TEESMC_CMD_*
+ * @ta_func: Trusted Application function, specific to the Trusted Application
+ * @session: In parameter for all TEESMC_CMD_* but
+ *          TEESMC_CMD_OPEN_SESSION
+ * @ret: return value
+ * @ret_origin: origin of the return value
+ * @num_params: number of parameters supplied to the OS Command
+ * @params: the parameters supplied to the OS Command
+ *
+ * See description of struct teesmc32_arg.
+ */
+struct teesmc64_arg {
+       uint64_t cmd;
+       uint64_t ta_func;
+       uint64_t session;
+       uint64_t ret;
+       uint64_t ret_origin;
+       uint64_t num_params;
+       /*
+        * Commented out elements used to visualize the layout dynamic part
+        * of the struct. Note that these fields are not available at all
+        * if num_params == 0.
+        *
+        * params is accessed through the macro TEESMC64_GET_PARAMS
+        */
+
+       /* struct teesmc64_param params[num_params]; */
+};
+
+/**
+ * TEESMC64_GET_PARAMS - return pointer to union teesmc64_param *
+ *
+ * @x: Pointer to a struct teesmc64_arg
+ *
+ * Returns a pointer to the params[] inside a struct teesmc64_arg.
+ */
+#define TEESMC64_GET_PARAMS(x) \
+       (struct teesmc64_param *)(((struct teesmc64_arg *)(x)) + 1)
+
+/**
+ * TEESMC64_GET_ARG_SIZE - return size of struct teesmc64_arg
+ *
+ * @num_params: Number of parameters embedded in the struct teesmc64_arg
+ *
+ * Returns the size of the struct teesmc64_arg together with the number
+ * of embedded paramters.
+ */
+#define TEESMC64_GET_ARG_SIZE(num_params) \
+       (sizeof(struct teesmc64_arg) + \
+        sizeof(union teesmc64_param) * (num_params))
+
+#define TEESMC_UUID_LEN        16
+
+/**
+ * struct teesmc_meta_open_session - additional parameters for
+ *                                  TEESMC32_CMD_OPEN_SESSION and
+ *                                  TEESMC64_CMD_OPEN_SESSION
+ * @uuid: UUID of the Trusted Application
+ * @clnt_uuid: UUID of client
+ * @clnt_login: Login class of client, TEE_LOGIN_* if being Global Platform
+ *             compliant
+ *
+ * This struct is passed in the first parameter as an input memref tagged
+ * as meta on an TEESMC{32,64}_CMD_OPEN_SESSION cmd. It's important
+ * that it really is the first parameter to make it easy for an eventual
+ * hypervisor to inspect and possibly update clnt_* values.
+ */
+struct teesmc_meta_open_session {
+       uint8_t uuid[TEESMC_UUID_LEN];
+       uint8_t clnt_uuid[TEESMC_UUID_LEN];
+       uint32_t clnt_login;
+};
+
+
+#endif /*!ASM*/
+
+/*
+ *******************************************************************************
+ * Part 2 - low level SMC interaction
+ *******************************************************************************
+ */
+
+#define TEESMC_32                      0
+#define TEESMC_64                      0x40000000
+#define TEESMC_FAST_CALL               0x80000000
+#define TEESMC_STD_CALL                        0
+
+#define TEESMC_OWNER_MASK              0x3F
+#define TEESMC_OWNER_SHIFT             24
+
+#define TEESMC_FUNC_MASK               0xFFFF
+
+#define TEESMC_IS_FAST_CALL(smc_val)   ((smc_val) & TEESMC_FAST_CALL)
+#define TEESMC_IS_64(smc_val)          ((smc_val) & TEESMC_64)
+#define TEESMC_FUNC_NUM(smc_val)       ((smc_val) & TEESMC_FUNC_MASK)
+#define TEESMC_OWNER_NUM(smc_val)      (((smc_val) >> TEESMC_OWNER_SHIFT) & \
+                                        TEESMC_OWNER_MASK)
+
+#define TEESMC_CALL_VAL(type, calling_convention, owner, func_num) \
+                       ((type) | (calling_convention) | \
+                       (((owner) & TEESMC_OWNER_MASK) << TEESMC_OWNER_SHIFT) |\
+                       ((func_num) & TEESMC_FUNC_MASK))
+
+#define TEESMC_OWNER_ARCH              0
+#define TEESMC_OWNER_CPU               1
+#define TEESMC_OWNER_SIP               2
+#define TEESMC_OWNER_OEM               3
+#define TEESMC_OWNER_STANDARD          4
+#define TEESMC_OWNER_TRUSTED_APP       48
+#define TEESMC_OWNER_TRUSTED_OS                50
+
+#define TEESMC_OWNER_TRUSTED_OS_API    63
+
+/*
+ * Function specified by SMC Calling convention.
+ */
+#define TEESMC32_FUNCID_CALLS_COUNT    0xFF00
+#define TEESMC32_CALLS_COUNT \
+       TEESMC_CALL_VAL(TEESMC_32, TEESMC_FAST_CALL, \
+                       TEESMC_OWNER_TRUSTED_OS_API, \
+                       TEESMC32_FUNCID_CALLS_COUNT)
+
+/*
+ * Function specified by SMC Calling convention
+ *
+ * Return one of the following UIDs if using API specified in this file
+ * without further extentions:
+ * 65cb6b93-af0c-4617-8ed6-644a8d1140f8 : Only 32 bit calls are supported
+ * 65cb6b93-af0c-4617-8ed6-644a8d1140f9 : Both 32 and 64 bit calls are supported
+ */
+#define TEESMC_UID_R0                  0x65cb6b93
+#define TEESMC_UID_R1                  0xaf0c4617
+#define TEESMC_UID_R2                  0x8ed6644a
+#define TEESMC_UID32_R3                        0x8d1140f8
+#define TEESMC_UID64_R3                        0x8d1140f9
+#define TEESMC32_FUNCID_CALLS_UID      0xFF01
+#define TEESMC32_CALLS_UID \
+       TEESMC_CALL_VAL(TEESMC_32, TEESMC_FAST_CALL, \
+                       TEESMC_OWNER_TRUSTED_OS_API, \
+                       TEESMC32_FUNCID_CALLS_UID)
+
+/*
+ * Function specified by SMC Calling convention
+ *
+ * Returns 1.0 if using API specified in this file without further extentions.
+ */
+#define TEESMC_REVISION_MAJOR  1
+#define TEESMC_REVISION_MINOR  0
+#define TEESMC32_FUNCID_CALLS_REVISION 0xFF03
+#define TEESMC32_CALLS_REVISION \
+       TEESMC_CALL_VAL(TEESMC_32, TEESMC_FAST_CALL, \
+                       TEESMC_OWNER_TRUSTED_OS_API, \
+                       TEESMC32_FUNCID_CALLS_REVISION)
+
+/*
+ * Get UUID of Trusted OS.
+ *
+ * Used by non-secure world to figure out which Trusted OS is installed.
+ * Note that returned UUID is the UUID of the Trusted OS, not of the API.
+ *
+ * Returns UUID in r0-4/w0-4 in the same way as TEESMC32_CALLS_UID
+ * described above.
+ */
+#define TEESMC_FUNCID_GET_OS_UUID      0
+#define TEESMC32_CALL_GET_OS_UUID \
+       TEESMC_CALL_VAL(TEESMC_32, TEESMC_FAST_CALL, TEESMC_OWNER_TRUSTED_OS, \
+                       TEESMC_FUNCID_GET_OS_UUID)
+
+/*
+ * Get revision of Trusted OS.
+ *
+ * Used by non-secure world to figure out which version of the Trusted OS
+ * is installed. Note that the returned revision is the revision of the
+ * Trusted OS, not of the API.
+ *
+ * Returns revision in r0-1/w0-1 in the same way as TEESMC32_CALLS_REVISION
+ * described above.
+ */
+#define TEESMC_FUNCID_GET_OS_REVISION  1
+#define TEESMC32_CALL_GET_OS_REVISION \
+       TEESMC_CALL_VAL(TEESMC_32, TEESMC_FAST_CALL, TEESMC_OWNER_TRUSTED_OS, \
+                       TEESMC_FUNCID_GET_OS_REVISION)
+
+
+
+/*
+ * Call with struct teesmc32_arg as argument
+ *
+ * Call register usage:
+ * r0/x0       SMC Function ID, TEESMC32_CALL_WITH_ARG
+ * r1/x1       Physical pointer to a struct teesmc32_arg
+ * r2-6/x2-6   Not used
+ * r7/x7       Hypervisor Client ID register
+ *
+ * Normal return register usage:
+ * r0/x0       Return value, TEESMC_RETURN_*
+ * r1-3/x1-3   Not used
+ * r4-7/x4-7   Preserved
+ *
+ * Ebusy return register usage:
+ * r0/x0       Return value, TEESMC_RETURN_EBUSY
+ * r1-3/x1-3   Preserved
+ * r4-7/x4-7   Preserved
+ *
+ * RPC return register usage:
+ * r0/x0       Return value, TEESMC_RETURN_IS_RPC(val)
+ * r1-2/x1-2   RPC parameters
+ * r3-7/x3-7   Resume information, must be preserved
+ *
+ * Possible return values:
+ * TEESMC_RETURN_UNKNOWN_FUNCTION      Trusted OS does not recognize this
+ *                                     function.
+ * TEESMC_RETURN_OK                    Call completed, result updated in
+ *                                     the previously supplied struct
+ *                                     teesmc32_arg.
+ * TEESMC_RETURN_EBUSY                 Trusted OS busy, try again later.
+ * TEESMC_RETURN_EBADADDR              Bad physcial pointer to struct
+ *                                     teesmc32_arg.
+ * TEESMC_RETURN_EBADCMD               Bad/unknown cmd in struct teesmc32_arg
+ * TEESMC_RETURN_IS_RPC()              Call suspended by RPC call to normal
+ *                                     world.
+ */
+#define TEESMC_FUNCID_CALL_WITH_ARG    2
+#define TEESMC32_CALL_WITH_ARG \
+       TEESMC_CALL_VAL(TEESMC_32, TEESMC_STD_CALL, TEESMC_OWNER_TRUSTED_OS, \
+       TEESMC_FUNCID_CALL_WITH_ARG)
+/* Same as TEESMC32_CALL_WITH_ARG but a "fast call". */
+#define TEESMC32_FASTCALL_WITH_ARG \
+       TEESMC_CALL_VAL(TEESMC_32, TEESMC_FAST_CALL, TEESMC_OWNER_TRUSTED_OS, \
+       TEESMC_FUNCID_CALL_WITH_ARG)
+
+/*
+ * Call with struct teesmc64_arg as argument
+ *
+ * See description of TEESMC32_CALL_WITH_ARG above, uses struct
+ * teesmc64_arg in x1 instead.
+ */
+#define TEESMC64_CALL_WITH_ARG \
+       TEESMC_CALL_VAL(TEESMC_64, TEESMC_STD_CALL, TEESMC_OWNER_TRUSTED_OS, \
+       TEESMC_FUNCID_CALL_WITH_ARG)
+/* Same as TEESMC64_CALL_WITH_ARG but a "fast call". */
+#define TEESMC64_FASTCALL_WITH_ARG \
+       TEESMC_CALL_VAL(TEESMC_64, TEESMC_FAST_CALL, TEESMC_OWNER_TRUSTED_OS, \
+       TEESMC_FUNCID_CALL_WITH_ARG)
+
+/*
+ * Resume from RPC (for example after processing an IRQ)
+ *
+ * Call register usage:
+ * r0/x0       SMC Function ID,
+ *             TEESMC32_CALL_RETURN_FROM_RPC or
+ *             TEESMC32_FASTCALL_RETURN_FROM_RPC
+ * r1-3/x1-3   Value of r1-3/x1-3 when TEESMC32_CALL_WITH_ARG returned
+ *             TEESMC_RETURN_RPC in r0/x0
+ *
+ * Return register usage is the same as for TEESMC32_CALL_WITH_ARG above.
+ *
+ * Possible return values
+ * TEESMC_RETURN_UNKNOWN_FUNCTION      Trusted OS does not recognize this
+ *                                     function.
+ * TEESMC_RETURN_OK                    Original call completed, result
+ *                                     updated in the previously supplied.
+ *                                     struct teesmc32_arg
+ * TEESMC_RETURN_RPC                   Call suspended by RPC call to normal
+ *                                     world.
+ * TEESMC_RETURN_EBUSY                 Trusted OS busy, try again later.
+ * TEESMC_RETURN_ERESUME               Resume failed, the opaque resume
+ *                                     information was corrupt.
+ */
+#define TEESMC_FUNCID_RETURN_FROM_RPC  3
+#define TEESMC32_CALL_RETURN_FROM_RPC \
+       TEESMC_CALL_VAL(TEESMC_32, TEESMC_STD_CALL, TEESMC_OWNER_TRUSTED_OS, \
+                       TEESMC_FUNCID_RETURN_FROM_RPC)
+/* Same as TEESMC32_CALL_RETURN_FROM_RPC but a "fast call". */
+#define TEESMC32_FASTCALL_RETURN_FROM_RPC \
+       TEESMC_CALL_VAL(TEESMC_32, TEESMC_FAST_CALL, TEESMC_OWNER_TRUSTED_OS, \
+                       TEESMC_FUNCID_RETURN_FROM_RPC)
+
+/*
+ * Resume from RPC (for example after processing an IRQ)
+ *
+ * See description of TEESMC32_CALL_RETURN_FROM_RPC above, used when
+ * it's a 64bit call that has returned.
+ */
+#define TEESMC64_CALL_RETURN_FROM_RPC \
+       TEESMC_CALL_VAL(TEESMC_64, TEESMC_STD_CALL, TEESMC_OWNER_TRUSTED_OS, \
+                       TEESMC_FUNCID_RETURN_FROM_RPC)
+/* Same as TEESMC64_CALL_RETURN_FROM_RPC but a "fast call". */
+#define TEESMC64_FASTCALL_RETURN_FROM_RPC \
+       TEESMC_CALL_VAL(TEESMC_64, TEESMC_FAST_CALL, TEESMC_OWNER_TRUSTED_OS, \
+                       TEESMC_FUNCID_RETURN_FROM_RPC)
+
+/*
+ * From secure monitor to Trusted OS, handle FIQ
+ *
+ * A virtual call which is injected by the Secure Monitor when an FIQ is
+ * raised while in normal world (SCR_NS is set). The monitor restores
+ * secure architecture registers and secure EL_SP1 and jumps to previous
+ * secure EL3_ELR. Trusted OS should preserve all general purpose
+ * registers.
+ *
+ * Call register usage:
+ * r0/x0       SMC Function ID, TEESMC32_CALL_HANDLE_FIQ
+ * r1-7/x1-7   Not used, but must be preserved
+ *
+ * Return register usage:
+ * Note used
+ */
+#define TEESMC_FUNCID_CALL_HANDLE_FIQ  0xf000
+#define TEESMC32_CALL_HANDLE_FIQ \
+       TEESMC_CALL_VAL(TEESMC_32, TEESMC_FAST_CALL, TEESMC_OWNER_TRUSTED_OS, \
+                       TEESMC_FUNCID_CALL_HANDLE_FIQ)
+
+#define TEESMC_RETURN_RPC_PREFIX_MASK  0xFFFF0000
+#define TEESMC_RETURN_RPC_PREFIX       0xFFFF0000
+#define TEESMC_RETURN_RPC_FUNC_MASK    0x0000FFFF
+
+#define TEESMC_RETURN_GET_RPC_FUNC(ret)        ((ret) & TEESMC_RETURN_RPC_FUNC_MASK)
+
+#define TEESMC_RPC_VAL(func)           ((func) | TEESMC_RETURN_RPC_PREFIX)
+
+/*
+ * Allocate argument memory for RPC parameter passing.
+ * Argument memory is used to hold a struct teesmc32_arg.
+ *
+ * "Call" register usage:
+ * r0/x0       This value, TEESMC_RETURN_RPC_ALLOC
+ * r1/x1       Size in bytes of required argument memory
+ * r2-7/x2-7   Resume information, must be preserved
+ *
+ * "Return" register usage:
+ * r0/x0       SMC Function ID, TEESMC32_CALL_RETURN_FROM_RPC if it was an
+ *             AArch32 SMC return or TEESMC64_CALL_RETURN_FROM_RPC for
+ *             AArch64 SMC return
+ * r1/x1       Physical pointer to allocated argument memory, 0 if size
+ *             was 0 or if memory can't be allocated
+ * r2-7/x2-7   Preserved
+ */
+#define TEESMC_RPC_FUNC_ALLOC_ARG      0
+#define TEESMC_RETURN_RPC_ALLOC_ARG    \
+       TEESMC_RPC_VAL(TEESMC_RPC_FUNC_ALLOC_ARG)
+
+/*
+ * Allocate payload memory for RPC parameter passing.
+ * Payload memory is used to hold the memory referred to by struct
+ * teesmc32_param_memref.
+ *
+ * "Call" register usage:
+ * r0/x0       This value, TEESMC_RETURN_RPC_ALLOC
+ * r1/x1       Size in bytes of required payload memory
+ * r2-7/x2-7   Resume information, must be preserved
+ *
+ * "Return" register usage:
+ * r0/x0       SMC Function ID, TEESMC32_CALL_RETURN_FROM_RPC if it was an
+ *             AArch32 SMC return or TEESMC64_CALL_RETURN_FROM_RPC for
+ *             AArch64 SMC return
+ * r1/x1       Physical pointer to allocated payload memory, 0 if size
+ *             was 0 or if memory can't be allocated
+ * r2-7/x2-7   Preserved
+ */
+#define TEESMC_RPC_FUNC_ALLOC_PAYLOAD  1
+#define TEESMC_RETURN_RPC_ALLOC_PAYLOAD        \
+       TEESMC_RPC_VAL(TEESMC_RPC_FUNC_ALLOC_PAYLOAD)
+
+/*
+ * Free memory previously allocated by TEESMC_RETURN_RPC_ALLOC_ARG.
+ *
+ * "Call" register usage:
+ * r0/x0       This value, TEESMC_RETURN_RPC_FREE
+ * r1/x1       Physical pointer to previously allocated argument memory
+ * r2-7/x2-7   Resume information, must be preserved
+ *
+ * "Return" register usage:
+ * r0/x0       SMC Function ID, TEESMC32_CALL_RETURN_FROM_RPC if it was an
+ *             AArch32 SMC return or TEESMC64_CALL_RETURN_FROM_RPC for
+ *             AArch64 SMC return
+ * r1/x1       Not used
+ * r2-7/x2-7   Preserved
+ */
+#define TEESMC_RPC_FUNC_FREE_ARG       2
+#define TEESMC_RETURN_RPC_FREE_ARG     TEESMC_RPC_VAL(TEESMC_RPC_FUNC_FREE_ARG)
+
+/*
+ * Free memory previously allocated by TEESMC_RETURN_RPC_ALLOC_PAYLOAD.
+ *
+ * "Call" register usage:
+ * r0/x0       This value, TEESMC_RETURN_RPC_FREE
+ * r1/x1       Physical pointer to previously allocated payload memory
+ * r3-7/x3-7   Resume information, must be preserved
+ *
+ * "Return" register usage:
+ * r0/x0       SMC Function ID, TEESMC32_CALL_RETURN_FROM_RPC if it was an
+ *             AArch32 SMC return or TEESMC64_CALL_RETURN_FROM_RPC for
+ *             AArch64 SMC return
+ * r1-2/x1-2   Not used
+ * r3-7/x3-7   Preserved
+ */
+#define TEESMC_RPC_FUNC_FREE_PAYLOAD   3
+#define TEESMC_RETURN_RPC_FREE_PAYLOAD \
+       TEESMC_RPC_VAL(TEESMC_RPC_FUNC_FREE_PAYLOAD)
+
+/*
+ * Deliver an IRQ in normal world.
+ *
+ * "Call" register usage:
+ * r0/x0       TEESMC_RETURN_RPC_IRQ
+ * r1-7/x1-7   Resume information, must be preserved
+ *
+ * "Return" register usage:
+ * r0/x0       SMC Function ID, TEESMC32_CALL_RETURN_FROM_RPC if it was an
+ *             AArch32 SMC return or TEESMC64_CALL_RETURN_FROM_RPC for
+ *             AArch64 SMC return
+ * r1-7/x1-7   Preserved
+ */
+#define TEESMC_RPC_FUNC_IRQ            4
+#define TEESMC_RETURN_RPC_IRQ          TEESMC_RPC_VAL(TEESMC_RPC_FUNC_IRQ)
+
+/*
+ * Do an RPC request. The supplied struct teesmc{32,64}_arg tells which
+ * request to do and the paramters for the request. The following fields
+ * are used (the rest are unused):
+ * - cmd               the Request ID
+ * - ret               return value of the request, filled in by normal world
+ * - num_params                number of parameters for the request
+ * - params            the parameters
+ * - param_attrs       attributes of the parameters
+ *
+ * "Call" register usage:
+ * r0/x0       TEESMC_RETURN_RPC_CMD
+ * r1/x1       Physical pointer to a struct teesmc32_arg if returning from
+ *             a AArch32 SMC or a struct teesmc64_arg if returning from a
+ *             AArch64 SMC, must be preserved, only the data should
+ *             be updated
+ * r2-7/x2-7   Resume information, must be preserved
+ *
+ * "Return" register usage:
+ * r0/x0       SMC Function ID, TEESMC32_CALL_RETURN_FROM_RPC if it was an
+ *             AArch32 SMC return or TEESMC64_CALL_RETURN_FROM_RPC for
+ *             AArch64 SMC return
+ * r1-7/x1-7   Preserved
+ */
+#define TEESMC_RPC_FUNC_CMD            5
+#define TEESMC_RETURN_RPC_CMD          TEESMC_RPC_VAL(TEESMC_RPC_FUNC_CMD)
+
+
+/* Returned in r0 */
+#define TEESMC_RETURN_UNKNOWN_FUNCTION 0xFFFFFFFF
+
+/* Returned in r0 only from Trusted OS functions */
+#define TEESMC_RETURN_OK               0x0
+#define TEESMC_RETURN_EBUSY            0x1
+#define TEESMC_RETURN_ERESUME          0x2
+#define TEESMC_RETURN_EBADADDR         0x3
+#define TEESMC_RETURN_EBADCMD          0x4
+#define TEESMC_RETURN_IS_RPC(ret) \
+       (((ret) & TEESMC_RETURN_RPC_PREFIX_MASK) == TEESMC_RETURN_RPC_PREFIX)
+
+/*
+ * Returned in r1 by Trusted OS functions if r0 = TEESMC_RETURN_RPC
+ */
+#define TEESMC_RPC_REQUEST_IRQ         0x0
+
+#endif /* TEESMC_H */
diff --git a/security/optee_linuxdriver/include/arm_common/teesmc_st.h b/security/optee_linuxdriver/include/arm_common/teesmc_st.h
new file mode 100644 (file)
index 0000000..0517391
--- /dev/null
@@ -0,0 +1,147 @@
+/*
+ * Copyright (c) 2014, STMicroelectronics International N.V.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License Version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ */
+#ifndef TEESMC_ST_H
+#define TEESMC_ST_H
+
+#define TEESMC_ST_RETURN_NOTAVAIL      0x5700
+
+/*
+ * Get Shared Memory Config
+ *
+ * Returns the Secure/Non-secure shared memory config.
+ *
+ * Call register usage:
+ * r0  SMC Function ID, TEESMC32_ST_FASTCALL_GET_SHM_CONFIG
+ * r1-6        Not used
+ * r7  Hypervisor Client ID register
+ *
+ * Have config return register usage:
+ * r0  TEESMC_RETURN_OK
+ * r1  Physical address of start of SHM
+ * r2  Size of of SHM
+ * r3  1 if SHM is cached, 0 if uncached.
+ * r4-7        Preserved
+ *
+ * Not available register usage:
+ * r0  TEESMC_ST_RETURN_NOTAVAIL
+ * r1-3 Not used
+ * r4-7        Preserved
+ */
+#define TEESMC_ST_FUNCID_GET_SHM_CONFIG        0x5700
+#define TEESMC32_ST_FASTCALL_GET_SHM_CONFIG \
+       TEESMC_CALL_VAL(TEESMC_32, TEESMC_FAST_CALL, TEESMC_OWNER_TRUSTED_OS, \
+                       TEESMC_ST_FUNCID_GET_SHM_CONFIG)
+
+/*
+ * Configures TZ/NS shared mutex for outer cache maintenance
+ *
+ * Disables, enables usage of outercache mutex.
+ * Returns or sets physical address of outercache mutex.
+ *
+ * Call register usage:
+ * r0  SMC Function ID, TEESMC32_ST_FASTCALL_L2CC_MUTEX
+ * r1  TEESMC_ST_L2CC_MUTEX_GET_ADDR   Get physical address of mutex
+ *     TEESMC_ST_L2CC_MUTEX_SET_ADDR   Set physical address of mutex
+ *     TEESMC_ST_L2CC_MUTEX_ENABLE     Enable usage of mutex
+ *     TEESMC_ST_L2CC_MUTEX_DISABLE    Disable usage of mutex
+ * r2  if r1 == TEESMC_ST_L2CC_MUTEX_SET_ADDR, physical address of mutex
+ * r3-6        Not used
+ * r7  Hypervisor Client ID register
+ *
+ * Have config return register usage:
+ * r0  TEESMC_RETURN_OK
+ * r1  Preserved
+ * r2  if r1 == 0, physical address of L2CC mutex
+ * r3-7        Preserved
+ *
+ * Error return register usage:
+ * r0  TEESMC_ST_RETURN_NOTAVAIL       Physical address not available
+ *     TEESMC_RETURN_EBADADDR          Bad supplied physical address
+ *     TEESMC_RETURN_EBADCMD           Unsupported value in r1
+ * r1-7        Preserved
+ */
+#define TEESMC_ST_L2CC_MUTEX_GET_ADDR  0
+#define TEESMC_ST_L2CC_MUTEX_SET_ADDR  1
+#define TEESMC_ST_L2CC_MUTEX_ENABLE    2
+#define TEESMC_ST_L2CC_MUTEX_DISABLE   3
+#define TEESMC_ST_FUNCID_L2CC_MUTEX    0x5701
+#define TEESMC32_ST_FASTCALL_L2CC_MUTEX \
+       TEESMC_CALL_VAL(TEESMC_32, TEESMC_FAST_CALL, TEESMC_OWNER_TRUSTED_OS, \
+                       TEESMC_ST_FUNCID_L2CC_MUTEX)
+
+/*
+ * Allocate payload memory for RPC parameter passing.
+ *
+ * "Call" register usage:
+ * r0/x0       This value, TEESMC_RETURN_ST_RPC_ALLOC_PAYLOAD
+ * r1/x1       Size in bytes of required payload memory
+ * r2/x2       Not used
+ * r3-7/x3-7   Resume information, must be preserved
+ *
+ * "Return" register usage:
+ * r0/x0       SMC Function ID, TEESMC32_CALL_RETURN_FROM_RPC if it was an
+ *             AArch32 SMC return or TEESMC64_CALL_RETURN_FROM_RPC for
+ *             AArch64 SMC return
+ * r1/x1       Physical pointer to allocated payload memory, 0 if size
+ *             was 0 or if memory can't be allocated
+ * r2/x2       Shared memory cookie used when freeing the memory
+ * r3-7/x3-7   Preserved
+ */
+#define TEESMC_ST_RPC_FUNC_ALLOC_PAYLOAD       0x5700
+#define TEESMC_RETURN_ST_RPC_ALLOC_PAYLOAD     \
+               TEESMC_RPC_VAL(TEESMC_ST_RPC_FUNC_ALLOC_PAYLOAD)
+
+
+/*
+ * Free memory previously allocated by TEESMC_RETURN_ST_RPC_ALLOC_PAYLOAD
+ *
+ * "Call" register usage:
+ * r0/x0       This value, TEESMC_RETURN_ST_RPC_FREE_PAYLOAD
+ * r1/x1       Shared memory cookie belonging to this payload memory
+ * r2-7/x2-7   Resume information, must be preserved
+ *
+ * "Return" register usage:
+ * r0/x0       SMC Function ID, TEESMC32_CALL_RETURN_FROM_RPC if it was an
+ *             AArch32 SMC return or TEESMC64_CALL_RETURN_FROM_RPC for
+ *             AArch64 SMC return
+ * r2-7/x2-7   Preserved
+ */
+#define TEESMC_ST_RPC_FUNC_FREE_PAYLOAD                0x5701
+#define TEESMC_RETURN_ST_RPC_FREE_PAYLOAD      \
+               TEESMC_RPC_VAL(TEESMC_ST_RPC_FUNC_FREE_PAYLOAD)
+
+/* Overriding default UID since the interface is extended
+ * 384fb3e0-e7f8-11e3-af63-0002a5d5c51b
+ */
+#define TEESMC_ST_UID_R0               0x384fb3e0
+#define TEESMC_ST_UID_R1               0xe7f811e3
+#define TEESMC_ST_UID_R2               0xaf630002
+#define TEESMC_ST_UID32_R3             0xa5d5c51b
+#define TEESMC_ST_UID64_R3             0xa5d5c51c
+
+#define TEESMC_ST_REVISION_MAJOR       1
+#define TEESMC_ST_REVISION_MINOR       0
+
+/*
+ * UUID for OP-TEE
+ * 486178e0-e7f8-11e3-bc5e-0002a5d5c51b
+ */
+#define TEESMC_OS_OPTEE_UUID_R0                0x486178e0
+#define TEESMC_OS_OPTEE_UUID_R1                0xe7f811e3
+#define TEESMC_OS_OPTEE_UUID_R2                0xbc5e0002
+#define TEESMC_OS_OPTEE_UUID_R3                0xa5d5c51b
+
+#define TEESMC_OS_OPTEE_REVISION_MAJOR 1
+#define TEESMC_OS_OPTEE_REVISION_MINOR 0
+
+#endif /*TEESMC_ST_H*/
diff --git a/security/optee_linuxdriver/include/linux/tee_client_api.h b/security/optee_linuxdriver/include/linux/tee_client_api.h
new file mode 100644 (file)
index 0000000..729a408
--- /dev/null
@@ -0,0 +1,566 @@
+/*
+ * Copyright (c) 2014, STMicroelectronics International N.V.
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * 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.
+ */
+#ifndef TEE_CLIENT_API_H
+#define TEE_CLIENT_API_H
+
+#ifndef __KERNEL__
+#include <stdint.h>
+#include <stddef.h>
+#endif /* __KERNEL__ */
+
+/*
+ * Defines the number of available memory references in an open session or
+ * invoke command operation payload.
+ */
+#define TEEC_CONFIG_PAYLOAD_REF_COUNT 4
+
+/**
+ * Defines the maximum size of a single shared memory block, in bytes, of both
+ * API allocated and API registered memory. The size is currently set to
+ * 512 * kB (512 * 1024).
+ */
+#define TEEC_CONFIG_SHAREDMEM_MAX_SIZE 0x8000
+
+/**
+ * Flag constants indicating the type of parameters encoded inside the
+ * operation payload (TEEC_Operation), Type is uint32_t.
+ *
+ * TEEC_NONE                   The Parameter is not used
+ *
+ * TEEC_VALUE_INPUT            The Parameter is a TEEC_Value tagged as input.
+ *
+ * TEEC_VALUE_OUTPUT           The Parameter is a TEEC_Value tagged as output.
+ *
+ * TEEC_VALUE_INOUT            The Parameter is a TEEC_Value tagged as both as
+ *                             input and output, i.e., for which both the
+ *                             behaviors of TEEC_VALUE_INPUT and
+ *                             TEEC_VALUE_OUTPUT apply.
+ *
+ * TEEC_MEMREF_TEMP_INPUT      The Parameter is a TEEC_TempMemoryReference
+ *                             describing a region of memory which needs to be
+ *                             temporarily registered for the duration of the
+ *                             Operation and is tagged as input.
+ *
+ * TEEC_MEMREF_TEMP_OUTPUT     Same as TEEC_MEMREF_TEMP_INPUT, but the Memory
+ *                             Reference is tagged as output. The
+ *                             Implementation may update the size field to
+ *                             reflect the required output size in some use
+ *                             cases.
+ *
+ * TEEC_MEMREF_TEMP_INOUT      A Temporary Memory Reference tagged as both
+ *                             input and output, i.e., for which both the
+ *                             behaviors of TEEC_MEMREF_TEMP_INPUT and
+ *                             TEEC_MEMREF_TEMP_OUTPUT apply.
+ *
+ * TEEC_MEMREF_WHOLE           The Parameter is a Registered Memory Reference
+ *                             that refers to the entirety of its parent Shared
+ *                             Memory block. The parameter structure is a
+ *                             TEEC_MemoryReference. In this structure, the
+ *                             Implementation MUST read only the parent field
+ *                             and MAY update the size field when the operation
+ *                             completes.
+ *
+ * TEEC_MEMREF_PARTIAL_INPUT   A Registered Memory Reference structure that
+ *                             refers to a partial region of its parent Shared
+ *                             Memory block and is tagged as input.
+ *
+ * TEEC_MEMREF_PARTIAL_OUTPUT  Registered Memory Reference structure that
+ *                             refers to a partial region of its parent Shared
+ *                             Memory block and is tagged as output.
+ *
+ * TEEC_MEMREF_PARTIAL_INOUT   The Registered Memory Reference structure that
+ *                             refers to a partial region of its parent Shared
+ *                             Memory block and is tagged as both input and
+ *                             output, i.e., for which both the behaviors of
+ *                             TEEC_MEMREF_PARTIAL_INPUT and
+ *                             TEEC_MEMREF_PARTIAL_OUTPUT apply.
+ */
+#define TEEC_NONE                   0x00000000
+#define TEEC_VALUE_INPUT            0x00000001
+#define TEEC_VALUE_OUTPUT           0x00000002
+#define TEEC_VALUE_INOUT            0x00000003
+#define TEEC_MEMREF_TEMP_INPUT      0x00000005
+#define TEEC_MEMREF_TEMP_OUTPUT     0x00000006
+#define TEEC_MEMREF_TEMP_INOUT      0x00000007
+#define TEEC_MEMREF_WHOLE           0x0000000C
+#define TEEC_MEMREF_PARTIAL_INPUT   0x0000000D
+#define TEEC_MEMREF_PARTIAL_OUTPUT  0x0000000E
+#define TEEC_MEMREF_PARTIAL_INOUT   0x0000000F
+
+/**
+ * Flag constants indicating the data transfer direction of memory in
+ * TEEC_Parameter. TEEC_MEM_INPUT signifies data transfer direction from the
+ * client application to the TEE. TEEC_MEM_OUTPUT signifies data transfer
+ * direction from the TEE to the client application. Type is uint32_t.
+ *
+ * TEEC_MEM_INPUT   The Shared Memory can carry data from the client
+ *                  application to the Trusted Application.
+ * TEEC_MEM_OUTPUT  The Shared Memory can carry data from the Trusted
+ *                  Application to the client application.
+ * TEEC_MEM_DMABUF  The Shared Memory is allocated with the dma buf api and
+ *                  not necessarly user mapped.
+ *                  Handle of the memory pass to drivers is the implementation
+ *                  fd field instead of the buffer field.
+ * TEEC_MEM_KAPI    Shared memory is required from another linux module.
+ *                  Dma buf file descriptor is not created.
+ */
+#define TEEC_MEM_INPUT   0x00000001
+#define TEEC_MEM_OUTPUT  0x00000002
+#define TEEC_MEM_DMABUF  0x00010000
+#define TEEC_MEM_KAPI    0x00020000
+
+/**
+ * Return values. Type is TEEC_Result
+ *
+ * TEEC_SUCCESS                 The operation was successful.
+ * TEEC_ERROR_GENERIC           Non-specific cause.
+ * TEEC_ERROR_ACCESS_DENIED     Access privileges are not sufficient.
+ * TEEC_ERROR_CANCEL            The operation was canceled.
+ * TEEC_ERROR_ACCESS_CONFLICT   Concurrent accesses caused conflict.
+ * TEEC_ERROR_EXCESS_DATA       Too much data for the requested operation was
+ *                              passed.
+ * TEEC_ERROR_BAD_FORMAT        Input data was of invalid format.
+ * TEEC_ERROR_BAD_PARAMETERS    Input parameters were invalid.
+ * TEEC_ERROR_BAD_STATE         Operation is not valid in the current state.
+ * TEEC_ERROR_ITEM_NOT_FOUND    The requested data item is not found.
+ * TEEC_ERROR_NOT_IMPLEMENTED   The requested operation should exist but is not
+ *                              yet implemented.
+ * TEEC_ERROR_NOT_SUPPORTED     The requested operation is valid but is not
+ *                              supported in this implementation.
+ * TEEC_ERROR_NO_DATA           Expected data was missing.
+ * TEEC_ERROR_OUT_OF_MEMORY     System ran out of resources.
+ * TEEC_ERROR_BUSY              The system is busy working on something else.
+ * TEEC_ERROR_COMMUNICATION     Communication with a remote party failed.
+ * TEEC_ERROR_SECURITY          A security fault was detected.
+ * TEEC_ERROR_SHORT_BUFFER      The supplied buffer is too short for the
+ *                              generated output.
+ * TEEC_ERROR_TARGET_DEAD       Trusted Application has panicked
+ *                              during the operation.
+ */
+
+/**
+ *  Standard defined error codes.
+ */
+#define TEEC_SUCCESS                0x00000000
+#define TEEC_ERROR_GENERIC          0xFFFF0000
+#define TEEC_ERROR_ACCESS_DENIED    0xFFFF0001
+#define TEEC_ERROR_CANCEL           0xFFFF0002
+#define TEEC_ERROR_ACCESS_CONFLICT  0xFFFF0003
+#define TEEC_ERROR_EXCESS_DATA      0xFFFF0004
+#define TEEC_ERROR_BAD_FORMAT       0xFFFF0005
+#define TEEC_ERROR_BAD_PARAMETERS   0xFFFF0006
+#define TEEC_ERROR_BAD_STATE        0xFFFF0007
+#define TEEC_ERROR_ITEM_NOT_FOUND   0xFFFF0008
+#define TEEC_ERROR_NOT_IMPLEMENTED  0xFFFF0009
+#define TEEC_ERROR_NOT_SUPPORTED    0xFFFF000A
+#define TEEC_ERROR_NO_DATA          0xFFFF000B
+#define TEEC_ERROR_OUT_OF_MEMORY    0xFFFF000C
+#define TEEC_ERROR_BUSY             0xFFFF000D
+#define TEEC_ERROR_COMMUNICATION    0xFFFF000E
+#define TEEC_ERROR_SECURITY         0xFFFF000F
+#define TEEC_ERROR_SHORT_BUFFER     0xFFFF0010
+#define TEEC_ERROR_TARGET_DEAD      0xFFFF3024
+
+/**
+ * Function error origins, of type TEEC_ErrorOrigin. These indicate where in
+ * the software stack a particular return value originates from.
+ *
+ * TEEC_ORIGIN_API          The error originated within the TEE Client API
+ *                          implementation.
+ * TEEC_ORIGIN_COMMS        The error originated within the underlying
+ *                          communications stack linking the rich OS with
+ *                          the TEE.
+ * TEEC_ORIGIN_TEE          The error originated within the common TEE code.
+ * TEEC_ORIGIN_TRUSTED_APP  The error originated within the Trusted Application
+ *                          code.
+ */
+#define TEEC_ORIGIN_API          0x00000001
+#define TEEC_ORIGIN_COMMS        0x00000002
+#define TEEC_ORIGIN_TEE          0x00000003
+#define TEEC_ORIGIN_TRUSTED_APP  0x00000004
+
+/**
+ * Session login methods, for use in TEEC_OpenSession() as parameter
+ * connectionMethod. Type is uint32_t.
+ *
+ * TEEC_LOGIN_PUBLIC       No login data is provided.
+ * TEEC_LOGIN_USER         Login data about the user running the Client
+ *                         Application process is provided.
+ * TEEC_LOGIN_GROUP        Login data about the group running the Client
+ *                         Application process is provided.
+ * TEEC_LOGIN_APPLICATION  Login data about the running Client Application
+ *                         itself is provided.
+ */
+#define TEEC_LOGIN_PUBLIC       0x00000000
+#define TEEC_LOGIN_USER         0x00000001
+#define TEEC_LOGIN_GROUP        0x00000002
+#define TEEC_LOGIN_APPLICATION  0x00000004
+
+/**
+ * Encode the paramTypes according to the supplied types.
+ *
+ * @param p0 The first param type.
+ * @param p1 The second param type.
+ * @param p2 The third param type.
+ * @param p3 The fourth param type.
+ */
+#define TEEC_PARAM_TYPES(p0, p1, p2, p3) \
+       ((p0) | ((p1) << 4) | ((p2) << 8) | ((p3) << 12))
+
+/**
+ * Get the i_th param type from the paramType.
+ *
+ * @param p The paramType.
+ * @param i The i-th parameter to get the type for.
+ */
+#define TEEC_PARAM_TYPE_GET(p, i) (((p) >> (i * 4)) & 0xF)
+
+typedef uint32_t TEEC_Result;
+
+/**
+ * struct TEEC_Context - Represents a connection between a client application
+ * and a TEE.
+ *
+ * Context identifier can be a handle (when opened from user land)
+ * or a structure pointer (when opened from kernel land).
+ * Identifier is defined as an union to match type sizes on all architectures.
+ */
+typedef struct {
+       char devname[256];
+       union {
+               struct tee_context *ctx;
+               int fd;
+       };
+} TEEC_Context;
+
+/**
+ * This type contains a Universally Unique Resource Identifier (UUID) type as
+ * defined in RFC4122. These UUID values are used to identify Trusted
+ * Applications.
+ */
+typedef struct {
+       uint32_t timeLow;
+       uint16_t timeMid;
+       uint16_t timeHiAndVersion;
+       uint8_t clockSeqAndNode[8];
+} TEEC_UUID;
+
+/**
+ * struct TEEC_SharedMemory - Memory to transfer data between a client
+ * application and trusted code.
+ *
+ * @param buffer      The memory buffer which is to be, or has been, shared
+ *                    with the TEE.
+ * @param size        The size, in bytes, of the memory buffer.
+ * @param flags       Bit-vector which holds properties of buffer.
+ *                    The bit-vector can contain either or both of the
+ *                    TEEC_MEM_INPUT and TEEC_MEM_OUTPUT flags.
+ *
+ * A shared memory block is a region of memory allocated in the context of the
+ * client application memory space that can be used to transfer data between
+ * that client application and a trusted application. The user of this struct
+ * is responsible to populate the buffer pointer.
+ */
+typedef struct {
+       void *buffer;
+       size_t size;
+       uint32_t flags;
+       /*
+        * identifier can store a handle (int) or a structure pointer (void *).
+        * define this union to match case where sizeof(int)!=sizeof(void *).
+        */
+       union {
+               int fd;
+               void *ptr;
+       } d;
+       uint8_t registered;
+} TEEC_SharedMemory;
+
+/**
+ * struct TEEC_TempMemoryReference - Temporary memory to transfer data between
+ * a client application and trusted code, only used for the duration of the
+ * operation.
+ *
+ * @param buffer  The memory buffer which is to be, or has been shared with
+ *                the TEE.
+ * @param size    The size, in bytes, of the memory buffer.
+ *
+ * A memory buffer that is registered temporarily for the duration of the
+ * operation to be called.
+ */
+typedef struct {
+       void *buffer;
+       size_t size;
+} TEEC_TempMemoryReference;
+
+/**
+ * struct TEEC_RegisteredMemoryReference - use a pre-registered or
+ * pre-allocated shared memory block of memory to transfer data between
+ * a client application and trusted code.
+ *
+ * @param parent  Points to a shared memory structure. The memory reference
+ *                may utilize the whole shared memory or only a part of it.
+ *                Must not be NULL
+ *
+ * @param size    The size, in bytes, of the memory buffer.
+ *
+ * @param offset  The offset, in bytes, of the referenced memory region from
+ *                the start of the shared memory block.
+ *
+ */
+typedef struct {
+       TEEC_SharedMemory *parent;
+       size_t size;
+       size_t offset;
+} TEEC_RegisteredMemoryReference;
+
+/**
+ * struct TEEC_Value - Small raw data container
+ *
+ * Instead of allocating a shared memory buffer this structure can be used
+ * to pass small raw data between a client application and trusted code.
+ *
+ * @param a  The first integer value.
+ *
+ * @param b  The second second value.
+ */
+typedef struct {
+       uint32_t a;
+       uint32_t b;
+} TEEC_Value;
+
+/**
+ * union TEEC_Parameter - Memory container to be used when passing data between
+ *                        client application and trusted code.
+ *
+ * Either the client uses a shared memory reference, parts of it or a small raw
+ * data container.
+ *
+ * @param tmpref  A temporary memory reference only valid for the duration
+ *                of the operation.
+ *
+ * @param memref  The entire shared memory or parts of it.
+ *
+ * @param value   The small raw data container to use
+ */
+typedef union {
+       TEEC_TempMemoryReference tmpref;
+       TEEC_RegisteredMemoryReference memref;
+       TEEC_Value value;
+} TEEC_Parameter;
+
+/**
+ * struct TEEC_Session - Represents a connection between a client application
+ * and a trusted application.
+ */
+typedef struct {
+       int fd;
+} TEEC_Session;
+
+/**
+ * struct TEEC_Operation - Holds information and memory references used in
+ * TEEC_InvokeCommand().
+ *
+ * @param   started     Client must initialize to zero if it needs to cancel
+ *                      an operation about to be performed.
+ * @param   paramTypes  Type of data passed. Use TEEC_PARAMS_TYPE macro to
+ *                      create the correct flags.
+ *                      0 means TEEC_NONE is passed for all params.
+ * @param   params      Array of parameters of type TEEC_Parameter.
+ * @param   session     Internal pointer to the last session used by
+ *                      TEEC_InvokeCommand with this operation.
+ *
+ */
+typedef struct {
+       uint32_t started;
+       uint32_t paramTypes;
+       TEEC_Parameter params[TEEC_CONFIG_PAYLOAD_REF_COUNT];
+       /* Implementation-Defined */
+       TEEC_Session *session;
+       TEEC_SharedMemory memRefs[TEEC_CONFIG_PAYLOAD_REF_COUNT];
+       uint32_t flags;
+} TEEC_Operation;
+
+/**
+ * TEEC_InitializeContext() - Initializes a context holding connection
+ * information on the specific TEE, designated by the name string.
+
+ * @param name    A zero-terminated string identifying the TEE to connect to.
+ *                If name is set to NULL, the default TEE is connected to. NULL
+ *                is the only supported value in this version of the API
+ *                implementation.
+ *
+ * @param context The context structure which is to be initialized.
+ *
+ * @return TEEC_SUCCESS  The initialization was successful.
+ * @return TEEC_Result   Something failed.
+ */
+TEEC_Result TEEC_InitializeContext(const char *name, TEEC_Context *context);
+
+/**
+ * TEEC_FinalizeContext() - Destroys a context holding connection information
+ * on the specific TEE.
+ *
+ * This function destroys an initialized TEE context, closing the connection
+ * between the client application and the TEE. This function must only be
+ * called when all sessions related to this TEE context have been closed and
+ * all shared memory blocks have been released.
+ *
+ * @param context       The context to be destroyed.
+ */
+void TEEC_FinalizeContext(TEEC_Context *context);
+
+/**
+ * TEEC_OpenSession() - Opens a new session with the specified trusted
+ *                      application.
+ *
+ * @param context            The initialized TEE context structure in which
+ *                           scope to open the session.
+ * @param session            The session to initialize.
+ * @param destination        A structure identifying the trusted application
+ *                           with which to open a session.
+ *
+ * @param connectionMethod   The connection method to use.
+ * @param connectionData     Any data necessary to connect with the chosen
+ *                           connection method. Not supported, should be set to
+ *                           NULL.
+ * @param operation          An operation structure to use in the session. May
+ *                           be set to NULL to signify no operation structure
+ *                           needed.
+ *
+ * @param returnOrigin       A parameter which will hold the error origin if
+ *                           this function returns any value other than
+ *                           TEEC_SUCCESS.
+ *
+ * @return TEEC_SUCCESS      OpenSession successfully opened a new session.
+ * @return TEEC_Result       Something failed.
+ *
+ */
+TEEC_Result TEEC_OpenSession(TEEC_Context *context,
+                            TEEC_Session *session,
+                            const TEEC_UUID *destination,
+                            uint32_t connectionMethod,
+                            const void *connectionData,
+                            TEEC_Operation *operation,
+                            uint32_t *returnOrigin);
+
+/**
+ * TEEC_CloseSession() - Closes the session which has been opened with the
+ * specific trusted application.
+ *
+ * @param session The opened session to close.
+ */
+void TEEC_CloseSession(TEEC_Session *session);
+
+/**
+ * TEEC_InvokeCommand() - Executes a command in the specified trusted
+ * application.
+ *
+ * @param session        A handle to an open connection to the trusted
+ *                       application.
+ * @param commandID      Identifier of the command in the trusted application
+ *                       to invoke.
+ * @param operation      An operation structure to use in the invoke command.
+ *                       May be set to NULL to signify no operation structure
+ *                       needed.
+ * @param returnOrigin   A parameter which will hold the error origin if this
+ *                       function returns any value other than TEEC_SUCCESS.
+ *
+ * @return TEEC_SUCCESS  OpenSession successfully opened a new session.
+ * @return TEEC_Result   Something failed.
+ */
+TEEC_Result TEEC_InvokeCommand(TEEC_Session *session,
+                              uint32_t commandID,
+                              TEEC_Operation *operation,
+                              uint32_t *returnOrigin);
+
+/**
+ * TEEC_RegisterSharedMemory() - Register a block of existing memory as a
+ * shared block within the scope of the specified context.
+ *
+ * @param context    The initialized TEE context structure in which scope to
+ *                   open the session.
+ * @param sharedMem  pointer to the shared memory structure to register.
+ *
+ * @return TEEC_SUCCESS              The registration was successful.
+ * @return TEEC_ERROR_OUT_OF_MEMORY  Memory exhaustion.
+ * @return TEEC_Result               Something failed.
+ */
+TEEC_Result TEEC_RegisterSharedMemory(TEEC_Context *context,
+                                     TEEC_SharedMemory *sharedMem);
+
+/**
+ * TEEC_AllocateSharedMemory() - Allocate shared memory for TEE.
+ *
+ * @param context     The initialized TEE context structure in which scope to
+ *                    open the session.
+ * @param sharedMem   Pointer to the allocated shared memory.
+ *
+ * @return TEEC_SUCCESS              The registration was successful.
+ * @return TEEC_ERROR_OUT_OF_MEMORY  Memory exhaustion.
+ * @return TEEC_Result               Something failed.
+ */
+TEEC_Result TEEC_AllocateSharedMemory(TEEC_Context *context,
+                                     TEEC_SharedMemory *sharedMem);
+
+/**
+ * TEEC_ReleaseSharedMemory() - Free or deregister the shared memory.
+ *
+ * @param sharedMem  Pointer to the shared memory to be freed.
+ */
+void TEEC_ReleaseSharedMemory(TEEC_SharedMemory *sharedMemory);
+
+/**
+ * TEEC_RequestCancellation() - Request the cancellation of a pending open
+ *                              session or command invocation.
+ *
+ * @param operation Pointer to an operation previously passed to open session
+ *                  or invoke.
+ */
+void TEEC_RequestCancellation(TEEC_Operation *operation);
+
+/**
+ * Register a pre-allocated Trusted Application This is mainly intended for
+ * OS-FREE contexts or when a filesystem is not available.
+ *
+ * @param ta   Pointer to the trusted application binary
+ * @param size The size of the TA binary
+ *
+ * @return TEEC_SUCCESS if successful.
+ * @return TEEC_Result something failed.
+ */
+TEEC_Result TEEC_RegisterTA(const void *ta, const size_t size);
+
+/**
+ * Unregister a pre-allocated Trusted Application This is mainly intended for
+ * OS-FREE contexts or when a filesystem is not available.
+ *
+ * @param ta Pointer to the trusted application binary
+ */
+void TEEC_UnregisterTA(const void *ta);
+
+#endif
diff --git a/security/optee_linuxdriver/include/linux/tee_core.h b/security/optee_linuxdriver/include/linux/tee_core.h
new file mode 100644 (file)
index 0000000..672b212
--- /dev/null
@@ -0,0 +1,205 @@
+/*
+ * Copyright (c) 2014, STMicroelectronics International N.V.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License Version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ */
+#ifndef __TEE_CORE_DRV_H__
+#define __TEE_CORE_DRV_H__
+
+#include <linux/klist.h>
+#include <linux/device.h>
+#include <linux/file.h>
+#include <linux/cdev.h>
+#include <linux/debugfs.h>
+#include <linux/miscdevice.h>
+#include <linux/types.h>
+#include <linux/atomic.h>
+#include <linux/scatterlist.h>
+
+#include <linux/types.h>
+
+#include <linux/tee_client_api.h>
+
+struct tee_cmd_io;
+struct tee_shm_io;
+struct tee_rpc;
+
+enum tee_state {
+       TEE_OFFLINE = 0,
+       TEE_ONLINE = 1,
+       TEE_SUSPENDED = 2,
+       TEE_RUNNING = 3,
+       TEE_CRASHED = 4,
+       TEE_LAST = 5,
+};
+
+#define TEE_CONF_TEST_MODE             0x01000000
+#define TEE_CONF_FW_NOT_CAPABLE                0x00000001
+
+struct tee_stats_entry {
+       int count;
+       int max;
+};
+
+#define TEE_STATS_CONTEXT_IDX   0
+#define TEE_STATS_SESSION_IDX   1
+#define TEE_STATS_SHM_IDX       2
+
+#define TEE_MAX_TEE_DEV_NAME (64)
+struct tee {
+       struct klist_node node;
+       char name[TEE_MAX_TEE_DEV_NAME];
+       int id;
+       void *priv;
+       const struct tee_ops *ops;
+       struct device *dev;
+       struct miscdevice miscdev;
+       struct tee_rpc *rpc;
+       struct dentry *dbg_dir;
+       atomic_t refcount;
+       int max_refcount;
+       struct tee_stats_entry stats[3];
+       struct list_head list_ctx;
+       struct list_head list_rpc_shm;
+       struct mutex lock;
+       unsigned int state;
+       uint32_t shm_flags;     /* supported flags for shm allocation */
+       uint32_t conf;
+       uint32_t test;
+};
+
+#define _DEV(tee) (tee->miscdev.this_device)
+
+#define TEE_MAX_CLIENT_NAME (128)
+
+/**
+ * struct tee_context - internal structure to store a TEE context.
+ *
+ * @tee: tee attached to the tee_context
+ * @usr_client: flag to known if the client is user side client
+ * @entry: list of tee_context
+ * @list_sess: list of tee_session that denotes all tee_session attached
+ * @list_shm: list of tee_shm that denotes all tee_shm attached
+ * @refcount: number of objects which reference it (including itself)
+ */
+struct tee_context {
+       struct tee *tee;
+       char name[TEE_MAX_CLIENT_NAME];
+       int tgid;
+       int usr_client;
+       struct list_head entry;
+       struct list_head list_sess;
+       struct list_head list_shm;
+       struct kref refcount;
+};
+
+/**
+ * struct tee_session - internal structure to store a TEE session.
+ *
+ * @entry: list of tee_context
+ * @ctx: tee_context attached to the tee_session
+ * @sessid: session ID returned by the secure world
+ * @priv: exporter specific private data for this buffer object
+ */
+struct tee_session {
+       struct list_head entry;
+       struct tee_context *ctx;
+       uint32_t sessid;
+       void *priv;
+};
+
+struct tee_shm_dma_buf {
+       struct dma_buf_attachment *attach;
+       struct sg_table *sgt;
+       bool tee_allocated;
+};
+
+/**
+ * struct tee_shm - internal structure to store a shm object.
+ *
+ * @ctx: tee_context attached to the buffer.
+ * @tee: tee attached to the buffer.
+ * @dev: device attached to the buffer.
+ * @size_req: requested size for the buffer
+ * @size_alloc: effective size of the buffer
+ * @kaddr: kernel address if mapped kernel side
+ * @paddr: physical address
+ * @flags: flags which denote the type of the buffer
+ * @entry: list of tee_shm
+ */
+struct tee_shm {
+       struct list_head entry;
+       struct tee_context *ctx;
+       struct tee *tee;
+       struct device *dev;
+       size_t size_req;
+       size_t size_alloc;
+       uint32_t flags;
+       void *kaddr;
+       dma_addr_t paddr;
+       struct sg_table sgt;
+       struct tee_shm_dma_buf *sdb;
+};
+
+#define TEE_SHM_MAPPED                 0x01000000
+#define TEE_SHM_TEMP                   0x02000000
+#define TEE_SHM_FROM_RPC               0x04000000
+#define TEE_SHM_REGISTERED             0x08000000
+#define TEE_SHM_MEMREF                 0x10000000
+#define TEE_SHM_CACHED                 0x20000000
+
+#define TEE_SHM_DRV_PRIV_MASK          0xFF000000
+
+struct tee_data {
+       uint32_t type;
+       uint32_t type_original;
+       TEEC_SharedMemory c_shm[TEEC_CONFIG_PAYLOAD_REF_COUNT];
+       union {
+               struct tee_shm *shm;
+               TEEC_Value value;
+       } params[TEEC_CONFIG_PAYLOAD_REF_COUNT];
+};
+
+struct tee_cmd {
+       TEEC_Result err;
+       uint32_t origin;
+       uint32_t cmd;
+       struct tee_shm *uuid;
+       struct tee_shm *ta;
+       struct tee_data param;
+};
+
+struct tee_shm *tee_shm_alloc_from_rpc(struct tee *tee, size_t size);
+void tee_shm_free_from_rpc(struct tee_shm *);
+
+int tee_core_add(struct tee *tee);
+int tee_core_del(struct tee *tee);
+
+struct tee *tee_core_alloc(struct device *dev, char *name, int id,
+                          const struct tee_ops *ops, size_t len);
+int tee_core_free(struct tee *tee);
+
+struct tee_ops {
+       struct module *owner;
+       const char *type;
+
+       int (*start)(struct tee *tee);
+       int (*stop)(struct tee *tee);
+       int (*open)(struct tee_session *sess, struct tee_cmd *cmd);
+       int (*close)(struct tee_session *sess);
+       int (*invoke)(struct tee_session *sess, struct tee_cmd *cmd);
+       int (*cancel)(struct tee_session *sess, struct tee_cmd *cmd);
+       struct tee_shm *(*alloc)(struct tee *tee, size_t size,
+                                 uint32_t flags);
+       void (*free)(struct tee_shm *shm);
+       int (*shm_inc_ref)(struct tee_shm *shm);
+};
+
+#endif /* __TEE_CORE_DRV_H__ */
diff --git a/security/optee_linuxdriver/include/linux/tee_ioc.h b/security/optee_linuxdriver/include/linux/tee_ioc.h
new file mode 100644 (file)
index 0000000..fb6ed96
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2014, STMicroelectronics International N.V.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License Version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ */
+#ifndef _TEE_IOC_H
+#define _TEE_IOC_H
+
+#include <linux/tee_client_api.h>
+
+#ifndef __KERNEL__
+#define __user
+#endif
+
+/**
+ * struct tee_cmd_io - The command sent to an open tee device.
+ * @err: Error code (as in Global Platform TEE Client API spec)
+ * @origin: Origin for the error code (also from spec).
+ * @cmd: The command to be executed in the trusted application.
+ * @uuid: The uuid for the trusted application.
+ * @data: The trusted application or memory block.
+ * @data_size: The size of the trusted application or memory block.
+ * @op: The cmd payload operation for the trusted application.
+ *
+ * This structure is mainly used in the Linux kernel for communication
+ * with the user space.
+ */
+struct tee_cmd_io {
+       TEEC_Result err;
+       uint32_t origin;
+       uint32_t cmd;
+       TEEC_UUID __user *uuid;
+       void __user *data;
+       uint32_t data_size;
+       TEEC_Operation __user *op;
+       int fd_sess;
+};
+
+struct tee_shm_io {
+       void __user *buffer;
+       size_t size;
+       uint32_t flags;
+       union {
+               int fd_shm;
+               void *ptr;
+       };
+       uint8_t registered;
+};
+
+#define TEE_OPEN_SESSION_IOC           _IOWR('t', 161, struct tee_cmd_io)
+#define TEE_INVOKE_COMMAND_IOC         _IOWR('t', 163, struct tee_cmd_io)
+#define TEE_REQUEST_CANCELLATION_IOC   _IOWR('t', 164, struct tee_cmd_io)
+#define TEE_ALLOC_SHM_IOC              _IOWR('t', 165, struct tee_shm_io)
+#define TEE_GET_FD_FOR_RPC_SHM_IOC     _IOWR('t', 167, struct tee_shm_io)
+
+#endif /* _TEE_IOC_H */
diff --git a/security/optee_linuxdriver/include/linux/tee_kernel_api.h b/security/optee_linuxdriver/include/linux/tee_kernel_api.h
new file mode 100644 (file)
index 0000000..3447daf
--- /dev/null
@@ -0,0 +1,174 @@
+/*
+ * Copyright (c) 2014, STMicroelectronics International N.V.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License Version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ */
+#ifndef _TEE_KERNEL_API_H
+#define _TEE_KERNEL_API_H
+
+#include <linux/tee_client_api.h>
+
+/**
+ * struct TEEC_Context - Represents a connection between a client application
+ * and a TEE.
+ */
+/*typedef struct {
+       char devname[256];
+} TEEC_Context;*/
+
+/**
+ * struct TEEC_Session - Represents a connection between a client application
+ * and a trusted application.
+ */
+/*typedef struct {
+       void *session;
+} TEEC_Session;*/
+
+/**
+ * TEEC_InitializeContext() - Initializes a context holding connection
+ * information on the specific TEE, designated by the name string.
+
+ * @param name    A zero-terminated string identifying the TEE to connect to.
+ *                If name is set to NULL, the default TEE is connected to. NULL
+ *                is the only supported value in this version of the API
+ *                implementation.
+ *
+ * @param context The context structure which is to be initialized.
+ *
+ * @return TEEC_SUCCESS  The initialization was successful.
+ * @return TEEC_Result   Something failed.
+ */
+TEEC_Result TEEC_InitializeContext(const char *name, TEEC_Context *context);
+
+/**
+ * TEEC_FinalizeContext() - Destroys a context holding connection information
+ * on the specific TEE.
+ *
+ * This function destroys an initialized TEE context, closing the connection
+ * between the client application and the TEE. This function must only be
+ * called when all sessions related to this TEE context have been closed and
+ * all shared memory blocks have been released.
+ *
+ * @param context       The context to be destroyed.
+ */
+void TEEC_FinalizeContext(TEEC_Context *context);
+
+/**
+ * TEEC_OpenSession() - Opens a new session with the specified trusted
+ *                      application.
+ *
+ * @param context            The initialized TEE context structure in which
+ *                           scope to open the session.
+ * @param session            The session to initialize.
+ * @param destination        A structure identifying the trusted application
+ *                           with which to open a session.
+ *
+ * @param connectionMethod   The connection method to use.
+ * @param connectionData     Any data necessary to connect with the chosen
+ *                           connection method. Not supported, should be set to
+ *                           NULL.
+ * @param operation          An operation structure to use in the session. May
+ *                           be set to NULL to signify no operation structure
+ *                           needed.
+ *
+ * @param returnOrigin       A parameter which will hold the error origin if
+ *                           this function returns any value other than
+ *                           TEEC_SUCCESS.
+ *
+ * @return TEEC_SUCCESS      OpenSession successfully opened a new session.
+ * @return TEEC_Result       Something failed.
+ *
+ */
+TEEC_Result TEEC_OpenSession(TEEC_Context *context,
+                            TEEC_Session *session,
+                            const TEEC_UUID *destination,
+                            uint32_t connectionMethod,
+                            const void *connectionData,
+                            TEEC_Operation *operation,
+                            uint32_t *returnOrigin);
+
+/**
+ * TEEC_CloseSession() - Closes the session which has been opened with the
+ * specific trusted application.
+ *
+ * @param session The opened session to close.
+ */
+void TEEC_CloseSession(TEEC_Session *session);
+
+/**
+ * TEEC_InvokeCommand() - Executes a command in the specified trusted
+ * application.
+ *
+ * @param session        A handle to an open connection to the trusted
+ *                       application.
+ * @param commandID      Identifier of the command in the trusted application
+ *                       to invoke.
+ * @param operation      An operation structure to use in the invoke command.
+ *                       May be set to NULL to signify no operation structure
+ *                       needed.
+ * @param returnOrigin   A parameter which will hold the error origin if this
+ *                       function returns any value other than TEEC_SUCCESS.
+ *
+ * @return TEEC_SUCCESS  OpenSession successfully opened a new session.
+ * @return TEEC_Result   Something failed.
+ */
+TEEC_Result TEEC_InvokeCommand(TEEC_Session *session,
+                              uint32_t commandID,
+                              TEEC_Operation *operation,
+                              uint32_t *returnOrigin);
+
+/**
+ * TEEC_RegisterSharedMemory() - Register a block of existing memory as a
+ * shared block within the scope of the specified context.
+ *
+ * @param context    The initialized TEE context structure in which scope to
+ *                   open the session.
+ * @param sharedMem  pointer to the shared memory structure to register.
+ *
+ * @return TEEC_SUCCESS              The registration was successful.
+ * @return TEEC_ERROR_OUT_OF_MEMORY  Memory exhaustion.
+ * @return TEEC_Result               Something failed.
+ */
+TEEC_Result TEEC_RegisterSharedMemory(TEEC_Context *context,
+                                     TEEC_SharedMemory *sharedMem);
+
+/**
+ * TEEC_AllocateSharedMemory() - Allocate shared memory for TEE.
+ *
+ * @param context     The initialized TEE context structure in which scope to
+ *                    open the session.
+ * @param sharedMem   Pointer to the allocated shared memory.
+ *
+ * @return TEEC_SUCCESS              The registration was successful.
+ * @return TEEC_ERROR_OUT_OF_MEMORY  Memory exhaustion.
+ * @return TEEC_Result               Something failed.
+ */
+TEEC_Result TEEC_AllocateSharedMemory(TEEC_Context *context,
+                                     TEEC_SharedMemory *sharedMem);
+
+/**
+ * TEEC_ReleaseSharedMemory() - Free or deregister the shared memory.
+ *
+ * @param sharedMem  Pointer to the shared memory to be freed.
+ */
+void TEEC_ReleaseSharedMemory(TEEC_SharedMemory *sharedMemory);
+
+#if 0
+/**
+ * TEEC_RequestCancellation() - Request the cancellation of a pending open
+ *                              session or command invocation.
+ *
+ * @param operation Pointer to an operation previously passed to open session
+ *                  or invoke.
+ */
+void TEEC_RequestCancellation(TEEC_Operation *operation);
+#endif
+
+#endif