From 8e4b2eda3fcee91ba7aa3c9b444d2c47413ee900 Mon Sep 17 00:00:00 2001 From: Zhang Zhijie Date: Tue, 20 Oct 2015 10:47:31 +0800 Subject: [PATCH] OP-TEE: add optee driver from GitHub: optee_linuxdriver Commit 4136b9d5a139(Fix TEESMC{32,64}_FASTCALL_RETURN_FROM_RPC) Change-Id: I389e4f79270e3bc6e8844ec81758f8b5546192a1 Signed-off-by: Zhang Zhijie --- security/optee_linuxdriver/Kconfig | 20 + security/optee_linuxdriver/LICENSE | 339 ++++ security/optee_linuxdriver/Makefile | 2 + security/optee_linuxdriver/Notice.md | 4 + security/optee_linuxdriver/README.md | 91 ++ security/optee_linuxdriver/armtz/Makefile | 40 + security/optee_linuxdriver/armtz/handle.c | 87 ++ security/optee_linuxdriver/armtz/handle.h | 52 + security/optee_linuxdriver/armtz/tee_mem.c | 675 ++++++++ security/optee_linuxdriver/armtz/tee_mem.h | 50 + .../optee_linuxdriver/armtz/tee_smc-arm.S | 29 + .../optee_linuxdriver/armtz/tee_smc-arm64.S | 36 + security/optee_linuxdriver/armtz/tee_tz_drv.c | 1369 +++++++++++++++++ security/optee_linuxdriver/armtz/tee_tz_op.h | 270 ++++ .../optee_linuxdriver/armtz/tee_tz_priv.h | 64 + security/optee_linuxdriver/core/Makefile | 33 + security/optee_linuxdriver/core/tee_context.c | 317 ++++ security/optee_linuxdriver/core/tee_core.c | 549 +++++++ .../optee_linuxdriver/core/tee_core_priv.h | 52 + security/optee_linuxdriver/core/tee_debugfs.c | 85 + security/optee_linuxdriver/core/tee_debugfs.h | 24 + .../optee_linuxdriver/core/tee_kernel_api.c | 255 +++ .../optee_linuxdriver/core/tee_mutex_wait.c | 144 ++ .../optee_linuxdriver/core/tee_mutex_wait.h | 38 + security/optee_linuxdriver/core/tee_session.c | 852 ++++++++++ security/optee_linuxdriver/core/tee_shm.c | 815 ++++++++++ security/optee_linuxdriver/core/tee_shm.h | 34 + .../optee_linuxdriver/core/tee_supp_com.c | 272 ++++ .../optee_linuxdriver/core/tee_supp_com.h | 111 ++ security/optee_linuxdriver/core/tee_sysfs.c | 204 +++ security/optee_linuxdriver/core/tee_sysfs.h | 21 + .../optee_linuxdriver/core/tee_wait_queue.c | 86 ++ .../optee_linuxdriver/core/tee_wait_queue.h | 32 + .../fdts/fvp-foundation-gicv2-psci.dts | 202 +++ .../fdts/fvp-foundation-motherboard.dtsi | 197 +++ security/optee_linuxdriver/fdts/readme.txt | 2 + .../include/arm_common/teesmc.h | 700 +++++++++ .../include/arm_common/teesmc_st.h | 147 ++ .../include/linux/tee_client_api.h | 566 +++++++ .../include/linux/tee_core.h | 205 +++ .../optee_linuxdriver/include/linux/tee_ioc.h | 63 + .../include/linux/tee_kernel_api.h | 174 +++ 42 files changed, 9308 insertions(+) create mode 100644 security/optee_linuxdriver/Kconfig create mode 100644 security/optee_linuxdriver/LICENSE create mode 100644 security/optee_linuxdriver/Makefile create mode 100644 security/optee_linuxdriver/Notice.md create mode 100644 security/optee_linuxdriver/README.md create mode 100644 security/optee_linuxdriver/armtz/Makefile create mode 100644 security/optee_linuxdriver/armtz/handle.c create mode 100644 security/optee_linuxdriver/armtz/handle.h create mode 100644 security/optee_linuxdriver/armtz/tee_mem.c create mode 100644 security/optee_linuxdriver/armtz/tee_mem.h create mode 100644 security/optee_linuxdriver/armtz/tee_smc-arm.S create mode 100644 security/optee_linuxdriver/armtz/tee_smc-arm64.S create mode 100644 security/optee_linuxdriver/armtz/tee_tz_drv.c create mode 100644 security/optee_linuxdriver/armtz/tee_tz_op.h create mode 100644 security/optee_linuxdriver/armtz/tee_tz_priv.h create mode 100644 security/optee_linuxdriver/core/Makefile create mode 100644 security/optee_linuxdriver/core/tee_context.c create mode 100644 security/optee_linuxdriver/core/tee_core.c create mode 100644 security/optee_linuxdriver/core/tee_core_priv.h create mode 100644 security/optee_linuxdriver/core/tee_debugfs.c create mode 100644 security/optee_linuxdriver/core/tee_debugfs.h create mode 100644 security/optee_linuxdriver/core/tee_kernel_api.c create mode 100644 security/optee_linuxdriver/core/tee_mutex_wait.c create mode 100644 security/optee_linuxdriver/core/tee_mutex_wait.h create mode 100644 security/optee_linuxdriver/core/tee_session.c create mode 100644 security/optee_linuxdriver/core/tee_shm.c create mode 100644 security/optee_linuxdriver/core/tee_shm.h create mode 100644 security/optee_linuxdriver/core/tee_supp_com.c create mode 100644 security/optee_linuxdriver/core/tee_supp_com.h create mode 100644 security/optee_linuxdriver/core/tee_sysfs.c create mode 100644 security/optee_linuxdriver/core/tee_sysfs.h create mode 100644 security/optee_linuxdriver/core/tee_wait_queue.c create mode 100644 security/optee_linuxdriver/core/tee_wait_queue.h create mode 100644 security/optee_linuxdriver/fdts/fvp-foundation-gicv2-psci.dts create mode 100644 security/optee_linuxdriver/fdts/fvp-foundation-motherboard.dtsi create mode 100644 security/optee_linuxdriver/fdts/readme.txt create mode 100644 security/optee_linuxdriver/include/arm_common/teesmc.h create mode 100644 security/optee_linuxdriver/include/arm_common/teesmc_st.h create mode 100644 security/optee_linuxdriver/include/linux/tee_client_api.h create mode 100644 security/optee_linuxdriver/include/linux/tee_core.h create mode 100644 security/optee_linuxdriver/include/linux/tee_ioc.h create mode 100644 security/optee_linuxdriver/include/linux/tee_kernel_api.h diff --git a/security/optee_linuxdriver/Kconfig b/security/optee_linuxdriver/Kconfig new file mode 100644 index 000000000000..a3f0714e4675 --- /dev/null +++ b/security/optee_linuxdriver/Kconfig @@ -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 index 000000000000..d159169d1050 --- /dev/null +++ b/security/optee_linuxdriver/LICENSE @@ -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. + + + Copyright (C) + + 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. + + , 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 index 000000000000..02a32aa23925 --- /dev/null +++ b/security/optee_linuxdriver/Makefile @@ -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 index 000000000000..88c6cfa003ac --- /dev/null +++ b/security/optee_linuxdriver/Notice.md @@ -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 index 000000000000..d2a0cb4ad64f --- /dev/null +++ b/security/optee_linuxdriver/README.md @@ -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 index 000000000000..af2b10cfd8eb --- /dev/null +++ b/security/optee_linuxdriver/armtz/Makefile @@ -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 index 000000000000..f40479f466b6 --- /dev/null +++ b/security/optee_linuxdriver/armtz/handle.c @@ -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 +#include +#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 index 000000000000..0d205349e3da --- /dev/null +++ b/security/optee_linuxdriver/armtz/handle.h @@ -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 index 000000000000..bbad023912a2 --- /dev/null +++ b/security/optee_linuxdriver/armtz/tee_mem.c @@ -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 + +#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 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 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 index 000000000000..01d08a8bd9d7 --- /dev/null +++ b/security/optee_linuxdriver/armtz/tee_mem.h @@ -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 +#include + +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 index 000000000000..5d1d2ea7a0df --- /dev/null +++ b/security/optee_linuxdriver/armtz/tee_smc-arm.S @@ -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 + +.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 index 000000000000..27224026cc96 --- /dev/null +++ b/security/optee_linuxdriver/armtz/tee_smc-arm64.S @@ -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 + + .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 index 000000000000..6541b02e646f --- /dev/null +++ b/security/optee_linuxdriver/armtz/tee_tz_drv.c @@ -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 +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include +#include + +#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(¶m); + 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, ¶m); + 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(¶m, 0, sizeof(param)); + param.a0 = TEESMC32_ST_FASTCALL_L2CC_MUTEX; + param.a1 = TEESMC_ST_L2CC_MUTEX_GET_ADDR; + tee_smc_call(¶m); + + 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(¶m, 0, sizeof(param)); + param.a0 = TEESMC32_ST_FASTCALL_L2CC_MUTEX; + param.a1 = TEESMC_ST_L2CC_MUTEX_ENABLE; + tee_smc_call(¶m); + + 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(¶m, 0, sizeof(param)); + param.a0 = TEESMC32_ST_FASTCALL_L2CC_MUTEX; + param.a1 = TEESMC_ST_L2CC_MUTEX_DISABLE; + tee_smc_call(¶m); + 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(¶m); + 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 index 000000000000..7f8f0aff500c --- /dev/null +++ b/security/optee_linuxdriver/armtz/tee_tz_op.h @@ -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 index 000000000000..707c72e49c4a --- /dev/null +++ b/security/optee_linuxdriver/armtz/tee_tz_priv.h @@ -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 index 000000000000..86cde19c607f --- /dev/null +++ b/security/optee_linuxdriver/core/Makefile @@ -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 index 000000000000..b86c61ac4789 --- /dev/null +++ b/security/optee_linuxdriver/core/tee_context.c @@ -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 +#include +#include +#include +#include +#include +#include + +#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 index 000000000000..d337e39f56c9 --- /dev/null +++ b/security/optee_linuxdriver/core/tee_core.c @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 index 000000000000..933f55fc0c68 --- /dev/null +++ b/security/optee_linuxdriver/core/tee_core_priv.h @@ -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 index 000000000000..95b44b8a2272 --- /dev/null +++ b/security/optee_linuxdriver/core/tee_debugfs.c @@ -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 +#include +#include +#include + +#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 index 000000000000..36f1da76d2cd --- /dev/null +++ b/security/optee_linuxdriver/core/tee_debugfs.h @@ -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 index 000000000000..9da7bc80ad67 --- /dev/null +++ b/security/optee_linuxdriver/core/tee_kernel_api.c @@ -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 +#include +#include +#include +#include + +#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 index 000000000000..17049f5eff08 --- /dev/null +++ b/security/optee_linuxdriver/core/tee_mutex_wait.c @@ -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 +#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 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 index 000000000000..19f9b0d43e70 --- /dev/null +++ b/security/optee_linuxdriver/core/tee_mutex_wait.h @@ -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 +#include +#include + +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 index 000000000000..32391afef073 --- /dev/null +++ b/security/optee_linuxdriver/core/tee_session.c @@ -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 +#include +#include +#include +#include +#include +#include + +#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, ¶m->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, + ¶m->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 index 000000000000..10d11af06a15 --- /dev/null +++ b/security/optee_linuxdriver/core/tee_shm.c @@ -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 +#include +#include +#include + +#include +#include + +#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 index 000000000000..8f76ed388a8e --- /dev/null +++ b/security/optee_linuxdriver/core/tee_shm.h @@ -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 +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 index 000000000000..3e60e9155c26 --- /dev/null +++ b/security/optee_linuxdriver/core/tee_supp_com.c @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 index 000000000000..841fe9f1fbd3 --- /dev/null +++ b/security/optee_linuxdriver/core/tee_supp_com.h @@ -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 + +/** + * 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 index 000000000000..c9512ad67db2 --- /dev/null +++ b/security/optee_linuxdriver/core/tee_sysfs.c @@ -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 +#include +#include +#include +#include + +#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/// -> + * /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 index 000000000000..fbd797b5c870 --- /dev/null +++ b/security/optee_linuxdriver/core/tee_sysfs.h @@ -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 index 000000000000..16770401392b --- /dev/null +++ b/security/optee_linuxdriver/core/tee_wait_queue.c @@ -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 +#include +#include +#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 index 000000000000..2ba619bc5310 --- /dev/null +++ b/security/optee_linuxdriver/core/tee_wait_queue.h @@ -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 +#include +#include + +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 index 000000000000..2237fbec0d2b --- /dev/null +++ b/security/optee_linuxdriver/fdts/fvp-foundation-gicv2-psci.dts @@ -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 index 000000000000..fd41c8ae25c0 --- /dev/null +++ b/security/optee_linuxdriver/fdts/fvp-foundation-motherboard.dtsi @@ -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 index 000000000000..e3fb4740b159 --- /dev/null +++ b/security/optee_linuxdriver/fdts/readme.txt @@ -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 index 000000000000..6727db4713d2 --- /dev/null +++ b/security/optee_linuxdriver/include/arm_common/teesmc.h @@ -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 index 000000000000..05173919c6c9 --- /dev/null +++ b/security/optee_linuxdriver/include/arm_common/teesmc_st.h @@ -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 index 000000000000..729a408ac209 --- /dev/null +++ b/security/optee_linuxdriver/include/linux/tee_client_api.h @@ -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 +#include +#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 index 000000000000..672b21253052 --- /dev/null +++ b/security/optee_linuxdriver/include/linux/tee_core.h @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +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 index 000000000000..fb6ed964396a --- /dev/null +++ b/security/optee_linuxdriver/include/linux/tee_ioc.h @@ -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 + +#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 index 000000000000..3447daf203dd --- /dev/null +++ b/security/optee_linuxdriver/include/linux/tee_kernel_api.h @@ -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 + +/** + * 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 -- 2.34.1