--- /dev/null
+#
+# 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.
--- /dev/null
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
--- /dev/null
+obj-y += core/
+obj-y += armtz/
--- /dev/null
+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
--- /dev/null
+# 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).
--- /dev/null
+
+#########################################################################
+# 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
+
--- /dev/null
+/*
+ * Copyright (c) 2014, Linaro Limited
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License Version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include <linux/slab.h>
+#include <linux/string.h>
+#include "handle.h"
+
+/*
+ * Define the initial capacity of the database. It should be a low number
+ * multiple of 2 since some databases a likely to only use a few handles.
+ * Since the algorithm is to doubles up when growing it shouldn't cause a
+ * noticable overhead on large databases.
+ */
+#define HANDLE_DB_INITIAL_MAX_PTRS 4
+
+void handle_db_destroy(struct handle_db *db)
+{
+ if (db) {
+ kfree(db->ptrs);
+ db->ptrs = NULL;
+ db->max_ptrs = 0;
+ }
+}
+
+int handle_get(struct handle_db *db, void *ptr)
+{
+ unsigned n;
+ void *p;
+ unsigned new_max_ptrs;
+
+ if (!db || !ptr)
+ return -1;
+
+ /* Try to find an empty location */
+ for (n = 0; n < db->max_ptrs; n++) {
+ if (!db->ptrs[n]) {
+ db->ptrs[n] = ptr;
+ return n;
+ }
+ }
+
+ /* No location available, grow the ptrs array */
+ if (db->max_ptrs)
+ new_max_ptrs = db->max_ptrs * 2;
+ else
+ new_max_ptrs = HANDLE_DB_INITIAL_MAX_PTRS;
+ p = krealloc(db->ptrs, new_max_ptrs * sizeof(void *), GFP_KERNEL);
+ if (!p)
+ return -1;
+ db->ptrs = p;
+ memset(db->ptrs + db->max_ptrs, 0,
+ (new_max_ptrs - db->max_ptrs) * sizeof(void *));
+ db->max_ptrs = new_max_ptrs;
+
+ /* Since n stopped at db->max_ptrs there is an empty location there */
+ db->ptrs[n] = ptr;
+ return n;
+}
+
+void *handle_put(struct handle_db *db, int handle)
+{
+ void *p;
+
+ if (!db || handle < 0 || (unsigned)handle >= db->max_ptrs)
+ return NULL;
+
+ p = db->ptrs[handle];
+ db->ptrs[handle] = NULL;
+ return p;
+}
+
+void *handle_lookup(struct handle_db *db, int handle)
+{
+ if (!db || handle < 0 || (unsigned)handle >= db->max_ptrs)
+ return NULL;
+
+ return db->ptrs[handle];
+}
--- /dev/null
+/*
+ * 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*/
--- /dev/null
+/*
+ * Copyright (c) 2014, STMicroelectronics International N.V.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License Version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+/**
+ * \file tee_mem.c
+ * \brief Functions to manage a pool of memory chunks.
+ *
+ * This module provides basic functions to manage dynamically a fixed amount
+ * of memory (memory region). For this current implementation the provided
+ * memory region used for the allocations should be physically AND logically
+ * contiguous (only one region is supported for a given allocator).
+ *
+ * Principle of the allocator algorithm: "best fit"
+ *
+ */
+
+#include <linux/slab.h>
+
+#include "tee_mem.h"
+
+#define _DUMP_INFO_ALLOCATOR 0
+#define USE_DEVM_ALLOC 1
+
+#ifndef USE_DEVM_ALLOC
+#define _KMALLOC(s, f) kmalloc(s, f)
+#define _KFREE(a) kfree(a)
+#else
+#define _KMALLOC(s, f) devm_kzalloc(dev, s, f)
+#define _KFREE(a) devm_kfree(dev, a)
+#endif
+
+/**
+ * \struct mem_chunk
+ * \brief Elementary descriptor of an allocated memory block
+ *
+ * \param node Node for linked list
+ * \param counter Reference counter
+ * (0 indicates that the block is not used/freed)
+ * \param size Total size in bytes
+ * \param paddr Physical base address
+ *
+ * Elementary memory block definition
+ */
+struct mem_chunk {
+ struct list_head node;
+ uint32_t counter;
+ size_t size;
+ unsigned long paddr;
+};
+
+/**
+ * \struct shm_pool
+ * \brief Main structure to describe a shared memory pool
+ *
+ * \param size Total size in bytes of the associated memory region
+ * \param vaddr Logical base address
+ * \param paddr Physical base address
+ * \param used Total size in bytes of the used memory
+ * \param mchunks List head for handle the elementary memory blocks
+ *
+ * Shared memory pool structure definition
+ */
+struct shm_pool {
+ struct mutex lock;
+ size_t size; /* Size of pool/heap memory segment */
+ size_t used; /* Number of bytes allocated */
+ void *vaddr; /* Associated Virtual address */
+ unsigned long paddr; /* Associated Physical address */
+ bool cached; /* true if pool is cacheable */
+ struct list_head mchunks; /* Head of memory chunk/block list */
+};
+
+#define __CALCULATE_RATIO_MEM_USED(a) (((a->used)*100)/(a->size))
+
+/**
+ * \brief Dumps the information of the shared memory pool
+ *
+ * \param pool Pointer on the pool
+ * \param detailforced Flag to force the log for the detailed informations
+ *
+ * Dump/log the meta data of the shared memory pool on the standard output.
+ *
+ */
+void tee_shm_pool_dump(struct device *dev, struct shm_pool *pool, bool forced)
+{
+ struct mem_chunk *chunk;
+
+ if (WARN_ON(!dev || !pool))
+ return;
+
+ dev_info(dev,
+ "tee_shm_pool_dump() poolH(0x%p) pAddr=0x%p vAddr=0x%p size=%zu used=%zu(%zu%%)\n",
+ (void *)pool,
+ (void *)pool->paddr,
+ (void *)pool->vaddr,
+ pool->size, pool->used, __CALCULATE_RATIO_MEM_USED(pool));
+
+ if ((pool->used != 0) || (forced == true)) {
+ dev_info(dev, " \\ HEAD next:[0x%p] prev:[0x%p]\n",
+ (void *)pool->mchunks.next,
+ (void *)pool->mchunks.prev);
+
+ dev_info(dev,
+ " |-[@] next prev pAddr size refCount\n");
+
+ list_for_each_entry(chunk, &pool->mchunks, node) {
+ dev_info(dev, " | [0x%p] 0x%p 0x%p 0x%p %08zu %d\n",
+ (void *)chunk,
+ (void *)chunk->node.next,
+ (void *)chunk->node.prev,
+ (void *)chunk->paddr,
+ chunk->size, chunk->counter);
+ }
+ }
+}
+
+bool tee_shm_pool_is_cached(struct shm_pool *pool)
+{
+ return pool->cached;
+}
+
+void tee_shm_pool_set_cached(struct shm_pool *pool)
+{
+ pool->cached = true;
+}
+
+/**
+ * \brief Creates and returns a new shared memory pool manager structure
+ *
+ * \param shm_size Size of the associated memory chunk
+ * \param shm_vaddr Virtual/logical base address
+ * \param shm_paddr Physical base address
+ *
+ * \return Reference of the created shared memory pool manager
+ *
+ * Create and initialize a shared memory pool manager.
+ * The description of the memory region (shm_size, shm_vaddr, shm_paddr)
+ * which is passed should be a physically AND virtually contiguous
+ * (no check is performed by the function).
+ * If a error is detected returned pool is NULL.
+ */
+struct shm_pool *tee_shm_pool_create(struct device *dev, size_t shm_size,
+ void *shm_vaddr, unsigned long shm_paddr)
+{
+ struct mem_chunk *chunk = NULL;
+ struct shm_pool *pool = NULL;
+
+ if (WARN_ON(!dev))
+ goto alloc_failed;
+
+ dev_dbg(dev, "> vaddr=0x%p, paddr=0x%p, size=%zuKiB\n",
+ shm_vaddr, (void *)shm_paddr, shm_size / 1024);
+
+ /* Alloc and initialize the shm_pool structure */
+ pool = _KMALLOC(sizeof(struct shm_pool), GFP_KERNEL);
+ if (!pool) {
+ dev_err(dev, "kmalloc <struct shm_pool> failed\n");
+ goto alloc_failed;
+ }
+ memset(pool, 0, sizeof(*pool));
+ mutex_init(&pool->lock);
+ mutex_lock(&pool->lock);
+
+ INIT_LIST_HEAD(&(pool->mchunks));
+ pool->size = shm_size;
+ pool->vaddr = shm_vaddr;
+ pool->paddr = shm_paddr;
+
+ /* Create the initial elementary memory chunk */
+ /* which handles the whole memory region */
+ chunk = _KMALLOC(sizeof(struct mem_chunk), GFP_KERNEL);
+ if (!chunk) {
+ dev_err(dev, "kmalloc <struct MemChunk> failed\n");
+ goto alloc_failed;
+ }
+ memset(chunk, 0, sizeof(*chunk));
+ chunk->paddr = shm_paddr;
+ chunk->size = shm_size;
+
+ /* Adds the new entry immediately after the list head */
+ list_add(&(chunk->node), &(pool->mchunks));
+
+#if defined(_DUMP_INFO_ALLOCATOR) && (_DUMP_INFO_ALLOCATOR > 0)
+ tee_shm_pool_dump(dev, pool, true);
+#endif
+ dev_dbg(dev, "< poolH(0x%p) chunkH=0x%p\n",
+ (void *)(pool), (void *)chunk);
+
+ mutex_unlock(&pool->lock);
+ return pool;
+
+alloc_failed:
+ if (chunk)
+ _KFREE(chunk);
+
+ if (pool)
+ _KFREE(pool);
+
+ return NULL;
+}
+
+/**
+ * Local helper function to check that the physical address is valid
+ */
+static inline int is_valid_paddr(struct shm_pool *pool, unsigned long paddr)
+{
+ return (paddr >= pool->paddr && paddr < (pool->paddr + pool->size));
+}
+
+/**
+ * Local helper function to check that the virtual address is valid
+ */
+static inline int is_valid_vaddr(struct shm_pool *pool, void *vaddr)
+{
+ return (vaddr >= pool->vaddr && vaddr < (pool->vaddr + pool->size));
+}
+
+/**
+ * \brief Destroy the shared memory pool manager
+ *
+ * \param pool Pointer on the pool
+ *
+ * Destroy a memory pool manager
+ *
+ */
+void tee_shm_pool_destroy(struct device *dev, struct shm_pool *pool)
+{
+ struct mem_chunk *chunk;
+
+ if (WARN_ON(!dev || !pool))
+ return;
+
+ dev_dbg(dev, "> poolH(0x%p)\n", (void *)pool);
+
+#if defined(_DUMP_INFO_ALLOCATOR) && (_DUMP_INFO_ALLOCATOR > 0)
+ tee_shm_pool_dump(dev, pool, true);
+#endif
+
+ tee_shm_pool_reset(dev, pool);
+
+ chunk = list_first_entry(&pool->mchunks, struct mem_chunk, node);
+ dev_dbg(dev, "free chunkH=0x%p\n", (void *)chunk);
+ _KFREE(chunk);
+ _KFREE(pool);
+
+ dev_dbg(dev, "<\n");
+}
+
+/**
+ * \brief Free all reserved chunk if any, and set pool at it initial state
+ *
+ * \param pool Pointer on the pool
+ *
+ */
+void tee_shm_pool_reset(struct device *dev, struct shm_pool *pool)
+{
+ struct mem_chunk *chunk;
+ struct mem_chunk *tmp;
+ struct mem_chunk *first = NULL;
+
+ if (WARN_ON(!dev || !pool))
+ return;
+
+ mutex_lock(&pool->lock);
+
+ list_for_each_entry_safe(chunk, tmp, &pool->mchunks, node) {
+ if (first != NULL) {
+ dev_err(dev, "Free lost chunkH=0x%p\n", (void *)chunk);
+ list_del(&chunk->node);
+ _KFREE(chunk);
+ } else {
+ first = chunk;
+ }
+ }
+
+ first->counter = 0;
+ first->paddr = pool->paddr;
+ first->size = pool->size;
+ pool->used = 0;
+
+ mutex_unlock(&pool->lock);
+}
+
+/**
+ * \brief Return the logical address
+ *
+ * \param pool Pointer on the pool
+ * \param paddr Physical address
+ *
+ * \return Virtual/logical address
+ *
+ * Return the associated virtual/logical address. The address should be inside
+ * the range of addresses managed by the shm pool.
+ *
+ */
+void *tee_shm_pool_p2v(struct device *dev, struct shm_pool *pool,
+ unsigned long paddr)
+{
+ if (WARN_ON(!dev || !pool))
+ return NULL;
+
+ mutex_lock(&pool->lock);
+ if (!is_valid_paddr(pool, paddr)) {
+ mutex_unlock(&pool->lock);
+ dev_err(dev,
+ "tee_shm_pool_p2v() paddr=0x%p not in the shm pool\n",
+ (void *)paddr);
+ return NULL;
+ } else {
+ unsigned long offset = paddr - pool->paddr;
+ void *p = (void *)((unsigned long)pool->vaddr + offset);
+
+ mutex_unlock(&pool->lock);
+ return p;
+ }
+}
+
+/**
+ * \brief Return the physical address
+ *
+ * \param pool Pointer on the pool
+ * \param vaddr Logical/Virtual address
+ *
+ * \return Physical address
+ *
+ * Return the associated physical address. The address should be inside
+ * the range of addresses managed by the pool.
+ *
+ */
+unsigned long tee_shm_pool_v2p(struct device *dev, struct shm_pool *pool,
+ void *vaddr)
+{
+ if (WARN_ON(!dev || !pool))
+ return 0UL;
+
+ mutex_lock(&pool->lock);
+ if (!is_valid_vaddr(pool, vaddr)) {
+ dev_err(dev,
+ "tee_shm_pool_v2p() vaddr=0x%p not in shm pool\n",
+ (void *)vaddr);
+ mutex_unlock(&pool->lock);
+ return 0UL;
+ } else {
+ unsigned long offset = vaddr - pool->vaddr;
+ unsigned long p = pool->paddr + offset;
+
+ mutex_unlock(&pool->lock);
+ return p;
+ }
+}
+
+/**
+ * \brief Allocate a new block of memory
+ *
+ * \param pool Pointer on the pool
+ * \param size Expected size (in byte)
+ * \param alignment Alignment constraint (in byte)
+ *
+ * \return Physical base address of the allocated block
+ *
+ * Allocate a memory chunk inside the memory region managed by the pool.
+ *
+ */
+unsigned long tee_shm_pool_alloc(struct device *dev,
+ struct shm_pool *pool,
+ size_t size, size_t alignment)
+{
+ struct mem_chunk *chunk;
+ struct mem_chunk *betterchunk = NULL;
+ struct mem_chunk *prev_chunk = NULL;
+ struct mem_chunk *next_chunk = NULL;
+ unsigned long begAddr;
+ unsigned long endAddr;
+
+ if (WARN_ON(!dev || !pool))
+ return 0UL;
+
+ dev_dbg(dev, "> poolH(%p:%p:%x) size=0x%zx align=0x%zx\n",
+ pool, (void *)pool->paddr, (unsigned int)pool->size, size,
+ alignment);
+
+ /* Align on cache line of the target */
+ /* \todo(jmd) Should be defined by a global target specific parameter */
+ /* size = (size + (32-1)) & ~(32-1) */
+
+ if (ALIGN(size, 0x20) < size)
+ goto failed_out;
+
+ if (alignment == 0)
+ alignment = 1;
+
+ size = ALIGN(size, 0x20);
+
+ alignment = ALIGN(alignment, 0x20);
+
+ if (size > (pool->size - pool->used))
+ goto failed_out;
+
+ mutex_lock(&pool->lock);
+
+ /**
+ * Algorithm: Smallest waste (best fit): We choose the block that has the
+ * smallest waste. In other words we choose the block so that
+ * size(b) - size is as small as possible.
+ */
+ list_for_each_entry(chunk, &pool->mchunks, node) {
+ if (chunk->counter == 0) { /* Free chunk */
+ begAddr = ALIGN(chunk->paddr, alignment);
+ endAddr = begAddr + size;
+
+ if (begAddr >= chunk->paddr
+ && endAddr <= (chunk->paddr + chunk->size)
+ && (betterchunk == NULL
+ /* Always split smaller block */
+ || chunk->size < betterchunk->size))
+ betterchunk = chunk;
+ }
+ }
+
+ /**
+ * Update the linked list
+ */
+ if (betterchunk != NULL) {
+ prev_chunk = _KMALLOC(sizeof(struct mem_chunk), GFP_KERNEL);
+ next_chunk = _KMALLOC(sizeof(struct mem_chunk), GFP_KERNEL);
+
+ if ((!prev_chunk) || (!next_chunk))
+ goto failed_out_unlock;
+
+ begAddr = ALIGN(betterchunk->paddr, alignment);
+ endAddr = begAddr + size;
+
+ if (betterchunk->paddr < begAddr) {
+ /* memory between begin of chunk and begin
+ * of created memory => create a free chunk */
+ prev_chunk->counter = 0;
+ prev_chunk->paddr = betterchunk->paddr;
+ prev_chunk->size = begAddr - betterchunk->paddr;
+
+ betterchunk->paddr = begAddr;
+ betterchunk->size -= prev_chunk->size;
+
+ dev_dbg(dev,
+ "create p_chunkH=0x%p paddr=0x%p (s=%zu)\n",
+ (void *)prev_chunk,
+ (void *)prev_chunk->paddr, prev_chunk->size);
+
+ list_add_tail(&(prev_chunk->node),
+ &(betterchunk->node));
+ prev_chunk = NULL;
+ } else {
+ _KFREE(prev_chunk);
+ }
+
+ if (betterchunk->paddr + betterchunk->size > endAddr) {
+ /* memory between end of chunk and end of
+ * created memory => create a free chunk */
+ next_chunk->counter = 0;
+ next_chunk->paddr = endAddr;
+ next_chunk->size = betterchunk->size - size;
+
+ dev_dbg(dev,
+ "create n_chunkH=0x%p paddr=0x%p (s=%zu)\n",
+ (void *)next_chunk,
+ (void *)next_chunk->paddr, next_chunk->size);
+
+ betterchunk->size = size;
+
+ list_add(&(next_chunk->node), &(betterchunk->node));
+ next_chunk = NULL;
+ } else {
+ _KFREE(next_chunk);
+ }
+
+ betterchunk->counter = 1;
+ pool->used += size;
+
+ mutex_unlock(&pool->lock);
+
+#if defined(_DUMP_INFO_ALLOCATOR) && (_DUMP_INFO_ALLOCATOR > 1)
+ tee_shm_pool_dump(dev, pool, false);
+#endif
+
+ dev_dbg(dev,
+ "< chunkH=0x%p paddr=%p (s=%zu) align=0x%zx\n",
+ (void *)betterchunk,
+ (void *)betterchunk->paddr,
+ betterchunk->size, alignment);
+
+ return betterchunk->paddr;
+ }
+
+failed_out_unlock:
+ mutex_unlock(&pool->lock);
+failed_out:
+ if (prev_chunk)
+ _KFREE(prev_chunk);
+ if (next_chunk)
+ _KFREE(next_chunk);
+
+ dev_err(dev,
+ "tee_shm_pool_alloc() FAILED, size=0x%zx, align=0x%zx free=%zu\n",
+ size, alignment, pool->size - pool->used);
+
+#if defined(_DUMP_INFO_ALLOCATOR) && (_DUMP_INFO_ALLOCATOR > 1)
+ tee_shm_pool_dump(dev, pool, true);
+#endif
+
+ return 0UL;
+}
+
+/**
+ * \brief Release a allocated block of memory
+ *
+ * \param pool Pointer on the pool
+ * \param paddr Physical @ of the block which must be released
+ * \param size Reference to return the size of the block
+ *
+ * Free a allocated memory block inside
+ * the memory region managed by the pool.
+ *
+ */
+int tee_shm_pool_free(struct device *dev, struct shm_pool *pool,
+ unsigned long paddr, size_t *size)
+{
+ struct mem_chunk *chunk;
+
+ if (WARN_ON(!dev || !pool))
+ return -EINVAL;
+
+ dev_dbg(dev, "> Try to free ... poolH(0x%p) paddr=0x%p\n",
+ (void *)pool, (void *)paddr);
+
+#if defined(_DUMP_INFO_ALLOCATOR) && (_DUMP_INFO_ALLOCATOR > 1)
+ tee_shm_pool_dump(dev, pool, false);
+#endif
+
+ mutex_lock(&pool->lock);
+
+ if (!is_valid_paddr(pool, paddr))
+ goto out_failed;
+
+ list_for_each_entry(chunk, &pool->mchunks, node) {
+ if (chunk->paddr == paddr) {
+ if (size != NULL)
+ *size = chunk->size;
+
+ if (chunk->counter == 0) {
+ dev_warn(dev,
+ "< tee_shm_pool_free() WARNING, paddr=0x%p already released\n",
+ (void *)paddr);
+ return -EINVAL;
+ } else if (--chunk->counter == 0) {
+ dev_dbg(dev, "paddr=%p\n", (void *)paddr);
+
+ pool->used -= chunk->size;
+
+ /* Merge with previous */
+ if (chunk->node.prev != &pool->mchunks) {
+ struct mem_chunk *prev =
+ list_entry(chunk->node.prev,
+ struct mem_chunk, node);
+ if (prev->counter == 0) {
+ dev_dbg(dev,
+ "chunkH=0x%p paddr=0x%p free ok\n",
+ (void *)chunk,
+ (void *)paddr);
+ prev->size += chunk->size;
+ list_del(&chunk->node);
+ _KFREE(chunk);
+ chunk = prev;
+ }
+ }
+ /* Merge with next */
+ if (chunk->node.next != &pool->mchunks) {
+ struct mem_chunk *next =
+ list_entry(chunk->node.next,
+ struct mem_chunk, node);
+ if (next->counter == 0) {
+ dev_dbg(dev,
+ "chunkH=0x%p paddr=0x%p free ok\n",
+ (void *)chunk,
+ (void *)paddr);
+ chunk->size += next->size;
+ list_del(&next->node);
+ _KFREE(next);
+ }
+ }
+ mutex_unlock(&pool->lock);
+
+#if defined(_DUMP_INFO_ALLOCATOR) && (_DUMP_INFO_ALLOCATOR > 1)
+ tee_shm_pool_dump(dev, pool, false);
+#endif
+ dev_dbg(dev, "< freed\n");
+ return 0;
+
+ } else {
+ mutex_unlock(&pool->lock);
+ dev_dbg(dev,
+ "< paddr=0x%p (--) refcounter is decremented ret=1\n",
+ (void *)paddr);
+ return 1;
+ }
+ }
+ }
+
+out_failed:
+ mutex_unlock(&pool->lock);
+#if defined(_DUMP_INFO_ALLOCATOR) && (_DUMP_INFO_ALLOCATOR > 1)
+ tee_shm_pool_dump(dev, pool, false);
+#endif
+ dev_err(dev,
+ "< tee_shm_pool_free() FAILED, pAddr=0x%p not found\n",
+ (void *)paddr);
+ return -EINVAL;
+}
+
+/**
+ * \brief Increase the reference count of the memory chunk
+ *
+ * \param pool Pointer on the pool
+ * \param paddr Physical address
+ *
+ * \return true if successful (false otherwise)
+ *
+ * Increment the reference count of the allocated block of memory.
+ * paddr should a valid address returned by the tee_shm_pool_alloc().
+ *
+ */
+bool tee_shm_pool_incref(struct device *dev, struct shm_pool *pool,
+ unsigned long paddr)
+{
+ struct mem_chunk *chunk;
+
+ if (WARN_ON(!dev || !pool))
+ return false;
+
+ mutex_lock(&pool->lock);
+
+ if (!is_valid_paddr(pool, paddr))
+ goto out_failed;
+
+ list_for_each_entry(chunk, &pool->mchunks, node) {
+ if (chunk->paddr == paddr) {
+ dev_dbg(dev,
+ "pAddr=%p (++) refcounter is incremented\n",
+ (void *)paddr);
+ chunk->counter++;
+
+#if defined(_DUMP_INFO_ALLOCATOR) && (_DUMP_INFO_ALLOCATOR > 0)
+ tee_shm_pool_dump(dev, pool, false);
+#endif
+ mutex_unlock(&pool->lock);
+ return true;
+ }
+ }
+
+out_failed:
+ mutex_unlock(&pool->lock);
+
+ dev_err(dev,
+ "tee_shm_pool_incref() FAILED, pAddr=%p is not a valid @\n",
+ (void *)paddr);
+
+ return false;
+}
--- /dev/null
+/*
+ * Copyright (c) 2014, STMicroelectronics International N.V.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License Version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#ifndef TEE_MEM_H
+#define TEE_MEM_H
+
+#include <linux/types.h>
+#include <linux/device.h>
+
+struct shm_pool;
+
+struct shm_pool *tee_shm_pool_create(struct device *dev, size_t shm_size,
+ void *shm_vaddr, unsigned long shm_paddr);
+
+void tee_shm_pool_destroy(struct device *dev, struct shm_pool *pool);
+
+void *tee_shm_pool_p2v(struct device *dev, struct shm_pool *pool,
+ unsigned long paddr);
+
+unsigned long tee_shm_pool_v2p(struct device *dev, struct shm_pool *pool,
+ void *vaddr);
+
+unsigned long tee_shm_pool_alloc(struct device *dev,
+ struct shm_pool *pool,
+ size_t size, size_t alignment);
+
+int tee_shm_pool_free(struct device *dev, struct shm_pool *pool,
+ unsigned long paddr, size_t *size);
+
+bool tee_shm_pool_incref(struct device *dev, struct shm_pool *pool,
+ unsigned long paddr);
+
+void tee_shm_pool_dump(struct device *dev, struct shm_pool *pool, bool forced);
+
+void tee_shm_pool_reset(struct device *dev, struct shm_pool *pool);
+
+bool tee_shm_pool_is_cached(struct shm_pool *pool);
+
+void tee_shm_pool_set_cached(struct shm_pool *pool);
+
+#endif
--- /dev/null
+/*
+ * Copyright (c) 2014, STMicroelectronics International N.V.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License Version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include <linux/linkage.h>
+
+.text
+.balign 4
+.code 32
+
+ /* void tee_smc_call(struct smc_param *param); */
+ .globl tee_smc_call
+ENTRY(tee_smc_call)
+ push {r4-r8, lr}
+ mov r8, r0
+ ldm r8, {r0-r7}
+.arch_extension sec
+ smc #0
+ stm r8, {r0-r7}
+ pop {r4-r8, pc}
+ENDPROC(tee_smc_call)
--- /dev/null
+/*
+ * Copyright (c) 2014, Linaro Limited
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License Version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include <linux/linkage.h>
+
+ .text
+
+#define SMC_PARAM_X0_OFFS 0
+#define SMC_PARAM_X2_OFFS 16
+#define SMC_PARAM_X4_OFFS 32
+#define SMC_PARAM_X6_OFFS 48
+
+ /* void tee_smc_call(struct smc_param *param); */
+ .globl tee_smc_call
+ENTRY(tee_smc_call)
+ stp x28, x30, [sp, #-16]!
+ mov x28, x0
+ ldp x0, x1, [x28, #SMC_PARAM_X0_OFFS]
+ ldp x2, x3, [x28, #SMC_PARAM_X2_OFFS]
+ ldp x4, x5, [x28, #SMC_PARAM_X4_OFFS]
+ ldp x6, x7, [x28, #SMC_PARAM_X6_OFFS]
+ smc #0
+ stp x0, x1, [x28, #SMC_PARAM_X0_OFFS]
+ stp x2, x3, [x28, #SMC_PARAM_X2_OFFS]
+ ldp x28, x30, [sp], #16
+ ret
+ENDPROC(tee_smc_call)
--- /dev/null
+/*
+ * Copyright (c) 2014, STMicroelectronics International N.V.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License Version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+/* #define DEBUG */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/sched.h>
+#include <linux/jiffies.h>
+
+#include <linux/tee_core.h>
+#include <linux/tee_ioc.h>
+
+#include <tee_shm.h>
+#include <tee_supp_com.h>
+#include <tee_mutex_wait.h>
+#include <tee_wait_queue.h>
+
+#include <arm_common/teesmc.h>
+#include <arm_common/teesmc_st.h>
+
+#include "tee_mem.h"
+#include "tee_tz_op.h"
+#include "tee_tz_priv.h"
+#include "handle.h"
+
+#define _TEE_TZ_NAME "armtz"
+#define DEV (ptee->tee->dev)
+
+/* #define TEE_STRESS_OUTERCACHE_FLUSH */
+
+/* magic config: bit 1 is set, Secure TEE shall handler NSec IRQs */
+#define SEC_ROM_NO_FLAG_MASK 0x0000
+#define SEC_ROM_IRQ_ENABLE_MASK 0x0001
+#define SEC_ROM_DEFAULT SEC_ROM_IRQ_ENABLE_MASK
+#define TEE_RETURN_BUSY 0x3
+#define ALLOC_ALIGN SZ_4K
+
+#define CAPABLE(tee) !(tee->conf & TEE_CONF_FW_NOT_CAPABLE)
+
+static struct tee_tz *tee_tz;
+
+static struct handle_db shm_handle_db = HANDLE_DB_INITIALIZER;
+
+
+/* Temporary workaround until we're only using post 3.13 kernels */
+#ifdef ioremap_cached
+#define ioremap_cache ioremap_cached
+#endif
+
+
+/*******************************************************************
+ * Calling TEE
+ *******************************************************************/
+
+static void e_lock_teez(struct tee_tz *ptee)
+{
+ mutex_lock(&ptee->mutex);
+}
+
+static void e_lock_wait_completion_teez(struct tee_tz *ptee)
+{
+ /*
+ * Release the lock until "something happens" and then reacquire it
+ * again.
+ *
+ * This is needed when TEE returns "busy" and we need to try again
+ * later.
+ */
+ ptee->c_waiters++;
+ mutex_unlock(&ptee->mutex);
+ /*
+ * Wait at most one second. Secure world is normally never busy
+ * more than that so we should normally never timeout.
+ */
+ wait_for_completion_timeout(&ptee->c, HZ);
+ mutex_lock(&ptee->mutex);
+ ptee->c_waiters--;
+}
+
+static void e_unlock_teez(struct tee_tz *ptee)
+{
+ /*
+ * If at least one thread is waiting for "something to happen" let
+ * one thread know that "something has happened".
+ */
+ if (ptee->c_waiters)
+ complete(&ptee->c);
+ mutex_unlock(&ptee->mutex);
+}
+
+static void handle_rpc_func_cmd_mutex_wait(struct tee_tz *ptee,
+ struct teesmc32_arg *arg32)
+{
+ struct teesmc32_param *params;
+
+ if (arg32->num_params != 2)
+ goto bad;
+
+ params = TEESMC32_GET_PARAMS(arg32);
+
+ if ((params[0].attr & TEESMC_ATTR_TYPE_MASK) !=
+ TEESMC_ATTR_TYPE_VALUE_INPUT)
+ goto bad;
+ if ((params[1].attr & TEESMC_ATTR_TYPE_MASK) !=
+ TEESMC_ATTR_TYPE_VALUE_INPUT)
+ goto bad;
+
+ switch (params[0].u.value.a) {
+ case TEE_MUTEX_WAIT_SLEEP:
+ tee_mutex_wait_sleep(DEV, &ptee->mutex_wait,
+ params[1].u.value.a,
+ params[1].u.value.b);
+ break;
+ case TEE_MUTEX_WAIT_WAKEUP:
+ tee_mutex_wait_wakeup(DEV, &ptee->mutex_wait,
+ params[1].u.value.a,
+ params[1].u.value.b);
+ break;
+ case TEE_MUTEX_WAIT_DELETE:
+ tee_mutex_wait_delete(DEV, &ptee->mutex_wait,
+ params[1].u.value.a);
+ break;
+ default:
+ goto bad;
+ }
+
+ arg32->ret = TEEC_SUCCESS;
+ return;
+bad:
+ arg32->ret = TEEC_ERROR_BAD_PARAMETERS;
+}
+
+static void handle_rpc_func_cmd_wait_queue(struct tee_tz *ptee,
+ struct teesmc32_arg *arg32)
+{
+ struct teesmc32_param *params;
+
+ if (arg32->num_params != 2)
+ goto bad;
+
+ params = TEESMC32_GET_PARAMS(arg32);
+
+ if ((params[0].attr & TEESMC_ATTR_TYPE_MASK) !=
+ TEESMC_ATTR_TYPE_VALUE_INPUT)
+ goto bad;
+ if ((params[1].attr & TEESMC_ATTR_TYPE_MASK) !=
+ TEESMC_ATTR_TYPE_NONE)
+ goto bad;
+
+ switch (arg32->cmd) {
+ case TEE_RPC_WAIT_QUEUE_SLEEP:
+ tee_wait_queue_sleep(DEV, &ptee->wait_queue,
+ params[0].u.value.a);
+ break;
+ case TEE_RPC_WAIT_QUEUE_WAKEUP:
+ tee_wait_queue_wakeup(DEV, &ptee->wait_queue,
+ params[0].u.value.a);
+ break;
+ default:
+ goto bad;
+ }
+
+ arg32->ret = TEEC_SUCCESS;
+ return;
+bad:
+ arg32->ret = TEEC_ERROR_BAD_PARAMETERS;
+}
+
+
+
+static void handle_rpc_func_cmd_wait(struct teesmc32_arg *arg32)
+{
+ struct teesmc32_param *params;
+ u32 msec_to_wait;
+
+ if (arg32->num_params != 1)
+ goto bad;
+
+ params = TEESMC32_GET_PARAMS(arg32);
+ msec_to_wait = params[0].u.value.a;
+
+ /* set task's state to interruptible sleep */
+ set_current_state(TASK_INTERRUPTIBLE);
+
+ /* take a nap */
+ schedule_timeout(msecs_to_jiffies(msec_to_wait));
+
+ arg32->ret = TEEC_SUCCESS;
+ return;
+bad:
+ arg32->ret = TEEC_ERROR_BAD_PARAMETERS;
+}
+
+static void handle_rpc_func_cmd_to_supplicant(struct tee_tz *ptee,
+ struct teesmc32_arg *arg32)
+{
+ struct teesmc32_param *params;
+ struct tee_rpc_invoke inv;
+ size_t n;
+ uint32_t ret;
+
+ if (arg32->num_params > TEE_RPC_BUFFER_NUMBER) {
+ arg32->ret = TEEC_ERROR_GENERIC;
+ return;
+ }
+
+ params = TEESMC32_GET_PARAMS(arg32);
+
+ memset(&inv, 0, sizeof(inv));
+ inv.cmd = arg32->cmd;
+ /*
+ * Set a suitable error code in case tee-supplicant
+ * ignores the request.
+ */
+ inv.res = TEEC_ERROR_NOT_IMPLEMENTED;
+ inv.nbr_bf = arg32->num_params;
+ for (n = 0; n < arg32->num_params; n++) {
+ switch (params[n].attr & TEESMC_ATTR_TYPE_MASK) {
+ case TEESMC_ATTR_TYPE_VALUE_INPUT:
+ case TEESMC_ATTR_TYPE_VALUE_INOUT:
+ inv.cmds[n].fd = (int)params[n].u.value.a;
+ /* Fall through */
+ case TEESMC_ATTR_TYPE_VALUE_OUTPUT:
+ inv.cmds[n].type = TEE_RPC_VALUE;
+ break;
+ case TEESMC_ATTR_TYPE_MEMREF_INPUT:
+ case TEESMC_ATTR_TYPE_MEMREF_OUTPUT:
+ case TEESMC_ATTR_TYPE_MEMREF_INOUT:
+ inv.cmds[n].buffer =
+ (void *)(uintptr_t)params[n].u.memref.buf_ptr;
+ inv.cmds[n].size = params[n].u.memref.size;
+ inv.cmds[n].type = TEE_RPC_BUFFER;
+ break;
+ default:
+ arg32->ret = TEEC_ERROR_GENERIC;
+ return;
+ }
+ }
+
+ ret = tee_supp_cmd(ptee->tee, TEE_RPC_ICMD_INVOKE,
+ &inv, sizeof(inv));
+ if (ret == TEEC_RPC_OK)
+ arg32->ret = inv.res;
+
+ for (n = 0; n < arg32->num_params; n++) {
+ switch (params[n].attr & TEESMC_ATTR_TYPE_MASK) {
+ case TEESMC_ATTR_TYPE_MEMREF_OUTPUT:
+ case TEESMC_ATTR_TYPE_MEMREF_INOUT:
+ /*
+ * Allow supplicant to assign a new pointer
+ * to an out-buffer. Needed when the
+ * supplicant allocates a new buffer, for
+ * instance when loading a TA.
+ */
+ params[n].u.memref.buf_ptr =
+ (uint32_t)(uintptr_t)inv.cmds[n].buffer;
+ params[n].u.memref.size = inv.cmds[n].size;
+ break;
+ case TEESMC_ATTR_TYPE_VALUE_OUTPUT:
+ case TEESMC_ATTR_TYPE_VALUE_INOUT:
+ params[n].u.value.a = inv.cmds[n].fd;
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+static void handle_rpc_func_cmd(struct tee_tz *ptee, u32 parg32)
+{
+ struct teesmc32_arg *arg32;
+
+ arg32 = tee_shm_pool_p2v(DEV, ptee->shm_pool, parg32);
+ if (!arg32)
+ return;
+
+ switch (arg32->cmd) {
+ case TEE_RPC_MUTEX_WAIT:
+ handle_rpc_func_cmd_mutex_wait(ptee, arg32);
+ break;
+ case TEE_RPC_WAIT_QUEUE_SLEEP:
+ case TEE_RPC_WAIT_QUEUE_WAKEUP:
+ handle_rpc_func_cmd_wait_queue(ptee, arg32);
+ break;
+ case TEE_RPC_WAIT:
+ handle_rpc_func_cmd_wait(arg32);
+ break;
+ default:
+ handle_rpc_func_cmd_to_supplicant(ptee, arg32);
+ }
+}
+
+static struct tee_shm *handle_rpc_alloc(struct tee_tz *ptee, size_t size)
+{
+ struct tee_rpc_alloc rpc_alloc;
+
+ rpc_alloc.size = size;
+ tee_supp_cmd(ptee->tee, TEE_RPC_ICMD_ALLOCATE,
+ &rpc_alloc, sizeof(rpc_alloc));
+ return rpc_alloc.shm;
+}
+
+static void handle_rpc_free(struct tee_tz *ptee, struct tee_shm *shm)
+{
+ struct tee_rpc_free rpc_free;
+
+ if (!shm)
+ return;
+ rpc_free.shm = shm;
+ tee_supp_cmd(ptee->tee, TEE_RPC_ICMD_FREE, &rpc_free, sizeof(rpc_free));
+}
+
+static u32 handle_rpc(struct tee_tz *ptee, struct smc_param *param)
+{
+ struct tee_shm *shm;
+ int cookie;
+
+ switch (TEESMC_RETURN_GET_RPC_FUNC(param->a0)) {
+ case TEESMC_RPC_FUNC_ALLOC_ARG:
+ param->a1 = tee_shm_pool_alloc(DEV, ptee->shm_pool,
+ param->a1, 4);
+ break;
+ case TEESMC_RPC_FUNC_ALLOC_PAYLOAD:
+ /* Can't support payload shared memory with this interface */
+ param->a2 = 0;
+ break;
+ case TEESMC_RPC_FUNC_FREE_ARG:
+ tee_shm_pool_free(DEV, ptee->shm_pool, param->a1, 0);
+ break;
+ case TEESMC_RPC_FUNC_FREE_PAYLOAD:
+ /* Can't support payload shared memory with this interface */
+ break;
+ case TEESMC_ST_RPC_FUNC_ALLOC_PAYLOAD:
+ shm = handle_rpc_alloc(ptee, param->a1);
+ if (IS_ERR_OR_NULL(shm)) {
+ param->a1 = 0;
+ break;
+ }
+ cookie = handle_get(&shm_handle_db, shm);
+ if (cookie < 0) {
+ handle_rpc_free(ptee, shm);
+ param->a1 = 0;
+ break;
+ }
+ param->a1 = shm->paddr;
+ param->a2 = cookie;
+ break;
+ case TEESMC_ST_RPC_FUNC_FREE_PAYLOAD:
+ shm = handle_put(&shm_handle_db, param->a1);
+ handle_rpc_free(ptee, shm);
+ break;
+ case TEESMC_RPC_FUNC_IRQ:
+ break;
+ case TEESMC_RPC_FUNC_CMD:
+ handle_rpc_func_cmd(ptee, param->a1);
+ break;
+ default:
+ dev_warn(DEV, "Unknown RPC func 0x%x\n",
+ (u32)TEESMC_RETURN_GET_RPC_FUNC(param->a0));
+ break;
+ }
+
+ if (irqs_disabled())
+ return TEESMC32_FASTCALL_RETURN_FROM_RPC;
+ else
+ return TEESMC32_CALL_RETURN_FROM_RPC;
+}
+
+static void call_tee(struct tee_tz *ptee,
+ uintptr_t parg32, struct teesmc32_arg *arg32)
+{
+ u32 ret;
+ u32 funcid;
+ struct smc_param param = { 0 };
+
+ if (irqs_disabled())
+ funcid = TEESMC32_FASTCALL_WITH_ARG;
+ else
+ funcid = TEESMC32_CALL_WITH_ARG;
+
+ /*
+ * Commented out elements used to visualize the layout dynamic part
+ * of the struct. Note that these fields are not available at all
+ * if num_params == 0.
+ *
+ * params is accessed through the macro TEESMC32_GET_PARAMS
+ */
+
+ /* struct teesmc32_param params[num_params]; */
+
+
+ param.a1 = parg32;
+ e_lock_teez(ptee);
+ while (true) {
+ param.a0 = funcid;
+
+ tee_smc_call(¶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");
--- /dev/null
+/*
+ * 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__ */
--- /dev/null
+/*
+ * 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__ */
--- /dev/null
+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 \
+
+
--- /dev/null
+/*
+ * Copyright (c) 2014, STMicroelectronics International N.V.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License Version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/types.h>
+#include <linux/file.h>
+#include <linux/atomic.h>
+#include <linux/uaccess.h>
+#include <linux/sched.h>
+
+#include "tee_shm.h"
+#include "tee_core_priv.h"
+
+
+/**
+ * tee_context_dump - Dump in a buffer the informations (ctx, sess & shm)
+ * associated to a tee.
+ */
+int tee_context_dump(struct tee *tee, char *buff, size_t len)
+{
+ struct list_head *ptr_ctx, *ptr_sess, *ptr_shm;
+ struct tee_context *ctx;
+ struct tee_session *sess;
+ struct tee_shm *shm;
+ int i = 0;
+ int j = 0;
+
+ int pos = 0;
+
+ BUG_ON(!tee);
+
+ if (len < 80 || list_empty(&tee->list_ctx))
+ return 0;
+
+ mutex_lock(&tee->lock);
+
+ list_for_each(ptr_ctx, &tee->list_ctx) {
+ ctx = list_entry(ptr_ctx, struct tee_context, entry);
+
+ pos += sprintf(buff + pos,
+ "[%02d] ctx=%p (refcount=%d) (usr=%d)",
+ i, ctx,
+ (int)atomic_read(&ctx->refcount.
+ refcount),
+ ctx->usr_client);
+ pos += sprintf(buff + pos, "name=\"%s\" (tgid=%d)\n",
+ ctx->name,
+ ctx->tgid);
+ if ((len - pos) < 80) {
+ pos = 0;
+ goto out;
+ }
+
+ if (list_empty(&ctx->list_sess))
+ goto out;
+
+ j = 0;
+ list_for_each(ptr_sess, &ctx->list_sess) {
+ sess = list_entry(ptr_sess,
+ struct tee_session,
+ entry);
+
+ pos += sprintf(buff + pos,
+ "[%02d.%d] sess=%p sessid=%08x\n",
+ i, j, sess,
+ sess->sessid);
+
+ if ((len - pos) < 80) {
+ pos = 0;
+ goto out;
+ }
+
+ j++;
+ }
+
+ if (list_empty(&ctx->list_shm))
+ goto out;
+
+ j = 0;
+ list_for_each(ptr_shm, &ctx->list_shm) {
+ shm = list_entry(ptr_shm, struct tee_shm, entry);
+
+ pos += sprintf(buff + pos,
+ "[%02d.%d] shm=%p paddr=%p kaddr=%p",
+ i, j, shm,
+ &shm->paddr,
+ shm->kaddr);
+ pos += sprintf(buff + pos,
+ " s=%zu(%zu)\n",
+ shm->size_req,
+ shm->size_alloc);
+ if ((len - pos) < 80) {
+ pos = 0;
+ goto out;
+ }
+
+ j++;
+ }
+
+ i++;
+ }
+
+out:
+ mutex_unlock(&tee->lock);
+ return pos;
+}
+
+/**
+ * tee_context_create - Allocate and create a new context.
+ * Reference on the back-end is requested.
+ */
+struct tee_context *tee_context_create(struct tee *tee)
+{
+ int ret;
+ struct tee_context *ctx;
+
+ dev_dbg(_DEV(tee), "%s: >\n", __func__);
+
+ ctx = devm_kzalloc(_DEV(tee), sizeof(struct tee_context), GFP_KERNEL);
+ if (!ctx) {
+ dev_err(_DEV(tee), "%s: tee_context allocation failed\n",
+ __func__);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ kref_init(&ctx->refcount);
+ INIT_LIST_HEAD(&ctx->list_sess);
+ INIT_LIST_HEAD(&ctx->list_shm);
+
+ ctx->tee = tee;
+ snprintf(ctx->name, sizeof(ctx->name), "%s", current->comm);
+ ctx->tgid = current->tgid;
+
+ ret = tee_get(tee);
+ if (ret) {
+ devm_kfree(_DEV(tee), ctx);
+ return ERR_PTR(ret);
+ }
+
+ mutex_lock(&tee->lock);
+ tee_inc_stats(&tee->stats[TEE_STATS_CONTEXT_IDX]);
+ list_add_tail(&ctx->entry, &tee->list_ctx);
+ mutex_unlock(&tee->lock);
+
+ dev_dbg(_DEV(ctx->tee), "%s: < ctx=%p is created\n", __func__, ctx);
+ return ctx;
+}
+
+/**
+ * _tee_context_do_release - Final function to release
+ * and free a context.
+ */
+static void _tee_context_do_release(struct kref *kref)
+{
+ struct tee_context *ctx;
+ struct tee *tee;
+
+ ctx = container_of(kref, struct tee_context, refcount);
+
+ BUG_ON(!ctx || !ctx->tee);
+
+ tee = ctx->tee;
+
+ dev_dbg(_DEV(tee), "%s: > ctx=%p\n", __func__, ctx);
+
+ mutex_lock(&tee->lock);
+ tee_dec_stats(&tee->stats[TEE_STATS_CONTEXT_IDX]);
+ list_del(&ctx->entry);
+ mutex_unlock(&tee->lock);
+
+ devm_kfree(_DEV(tee), ctx);
+ tee_put(tee);
+
+ dev_dbg(_DEV(tee), "%s: < ctx=%p is destroyed\n", __func__, ctx);
+}
+
+/**
+ * tee_context_get - Increase the reference count of
+ * the context.
+ */
+void tee_context_get(struct tee_context *ctx)
+{
+ BUG_ON(!ctx || !ctx->tee);
+
+ kref_get(&ctx->refcount);
+
+ dev_dbg(_DEV(ctx->tee), "%s: ctx=%p, kref=%d\n", __func__,
+ ctx, (int)atomic_read(&ctx->refcount.refcount));
+}
+
+static int is_in_list(struct tee *tee, struct list_head *entry)
+{
+ int present = 1;
+
+ mutex_lock(&tee->lock);
+ if ((entry->next == LIST_POISON1) && (entry->prev == LIST_POISON2))
+ present = 0;
+ mutex_unlock(&tee->lock);
+ return present;
+}
+
+/**
+ * tee_context_put - Decreases the reference count of
+ * the context. If 0, the final
+ * release function is called.
+ */
+void tee_context_put(struct tee_context *ctx)
+{
+ struct tee_context *_ctx = ctx;
+ struct tee *tee;
+
+ BUG_ON(!ctx || !ctx->tee);
+ tee = ctx->tee;
+
+ if (!is_in_list(tee, &ctx->entry))
+ return;
+
+ kref_put(&ctx->refcount, _tee_context_do_release);
+
+ dev_dbg(_DEV(tee), "%s: ctx=%p, kref=%d\n", __func__,
+ _ctx, (int)atomic_read(&ctx->refcount.refcount));
+}
+
+/**
+ * tee_context_destroy - Request to destroy a context.
+ */
+void tee_context_destroy(struct tee_context *ctx)
+{
+ struct tee *tee;
+
+ if (!ctx || !ctx->tee)
+ return;
+
+ tee = ctx->tee;
+
+ dev_dbg(_DEV(tee), "%s: ctx=%p\n", __func__, ctx);
+
+ tee_context_put(ctx);
+}
+
+int tee_context_copy_from_client(const struct tee_context *ctx,
+ void *dest, const void *src, size_t size)
+{
+ int res = 0;
+
+ if (dest && src && (size > 0)) {
+ if (ctx->usr_client)
+ res = copy_from_user(dest, src, size);
+ else
+ memcpy(dest, src, size);
+ }
+ return res;
+}
+
+struct tee_shm *tee_context_alloc_shm_tmp(struct tee_context *ctx,
+ size_t size, const void *src,
+ int type)
+{
+ struct tee_shm *shm;
+
+ type &= (TEEC_MEM_INPUT | TEEC_MEM_OUTPUT);
+
+ shm = tee_shm_alloc(ctx->tee, size,
+ TEE_SHM_MAPPED | TEE_SHM_TEMP | type);
+ if (IS_ERR_OR_NULL(shm)) {
+ dev_err(_DEV(ctx->tee), "%s: buffer allocation failed (%ld)\n",
+ __func__, PTR_ERR(shm));
+ return shm;
+ }
+
+ shm->ctx = ctx;
+
+ if (type & TEEC_MEM_INPUT) {
+ if (tee_context_copy_from_client(ctx, shm->kaddr, src, size)) {
+ dev_err(_DEV(ctx->tee),
+ "%s: tee_context_copy_from_client failed\n",
+ __func__);
+ tee_shm_free(shm);
+ shm = NULL;
+ }
+ }
+ return shm;
+}
+
+struct tee_shm *tee_context_create_tmpref_buffer(struct tee_context *ctx,
+ size_t size,
+ const void *buffer, int type)
+{
+ struct tee_shm *shm = NULL;
+ int flags;
+
+ switch (type) {
+ case TEEC_MEMREF_TEMP_OUTPUT:
+ flags = TEEC_MEM_OUTPUT;
+ break;
+ case TEEC_MEMREF_TEMP_INPUT:
+ flags = TEEC_MEM_INPUT;
+ break;
+ case TEEC_MEMREF_TEMP_INOUT:
+ flags = TEEC_MEM_INPUT | TEEC_MEM_OUTPUT;
+ break;
+ default:
+ BUG_ON(1);
+ };
+ shm = tee_context_alloc_shm_tmp(ctx, size, buffer, flags);
+ return shm;
+}
--- /dev/null
+/*
+ * Copyright (c) 2014, STMicroelectronics International N.V.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License Version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+/* #define DEBUG */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/cdev.h>
+#include <linux/idr.h>
+#include <linux/platform_device.h>
+#include <linux/device.h>
+#include <linux/fs.h>
+#include <linux/file.h>
+#include <linux/uaccess.h>
+#include <asm-generic/ioctl.h>
+#include <linux/sched.h>
+
+#include "linux/tee_core.h"
+#include "linux/tee_ioc.h"
+
+#include "tee_core_priv.h"
+
+#include "tee_sysfs.h"
+#include "tee_debugfs.h"
+#include "tee_shm.h"
+#include "tee_supp_com.h"
+
+#define _TEE_CORE_FW_VER "1:0.1"
+
+static char *_tee_supp_app_name = "tee-supplicant";
+
+/* Store the class misc reference */
+static struct class *misc_class;
+
+static int device_match(struct device *device, const void *devname)
+{
+ struct tee *tee = dev_get_drvdata(device);
+ int ret = strncmp(devname, tee->name, sizeof(tee->name));
+
+ BUG_ON(!tee);
+ if (ret == 0)
+ return 1;
+ else
+ return 0;
+}
+
+/*
+ * For the kernel api.
+ * Get a reference on a device tee from the device needed
+ */
+struct tee *tee_get_tee(const char *devname)
+{
+ struct device *device;
+
+ if (!devname)
+ return NULL;
+ device = class_find_device(misc_class, NULL, devname, device_match);
+ if (!device) {
+ pr_err("%s:%d - can't find device [%s]\n", __func__, __LINE__,
+ devname);
+ return NULL;
+ }
+
+ return dev_get_drvdata(device);
+}
+
+void tee_inc_stats(struct tee_stats_entry *entry)
+{
+ entry->count++;
+ if (entry->count > entry->max)
+ entry->max = entry->count;
+}
+
+void tee_dec_stats(struct tee_stats_entry *entry)
+{
+ entry->count--;
+}
+
+/**
+ * tee_get - increases refcount of the tee
+ * @tee: [in] tee to increase refcount of
+ *
+ * @note: If tee.ops.start() callback function is available,
+ * it is called when refcount is equal at 1.
+ */
+int tee_get(struct tee *tee)
+{
+ int ret = 0;
+
+ BUG_ON(!tee);
+
+ if (atomic_inc_return(&tee->refcount) == 1) {
+ BUG_ON(!try_module_get(tee->ops->owner));
+ dev_dbg(_DEV(tee), "%s: refcount=1 call %s::start()...\n",
+ __func__, tee->name);
+ get_device(tee->dev);
+ if (tee->ops->start)
+ ret = tee->ops->start(tee);
+ }
+ if (ret) {
+ put_device(tee->dev);
+ module_put(tee->ops->owner);
+ dev_err(_DEV(tee), "%s: %s::start() failed, err=%d\n",
+ __func__, tee->name, ret);
+ atomic_dec(&tee->refcount);
+ } else {
+ int count = (int)atomic_read(&tee->refcount);
+
+ dev_dbg(_DEV(tee), "%s: refcount=%d\n", __func__, count);
+ if (count > tee->max_refcount)
+ tee->max_refcount = count;
+ }
+ return ret;
+}
+
+/**
+ * tee_put - decreases refcount of the tee
+ * @tee: [in] tee to reduce refcount of
+ *
+ * @note: If tee.ops.stop() callback function is available,
+ * it is called when refcount is equal at 0.
+ */
+int tee_put(struct tee *tee)
+{
+ int ret = 0;
+ int count;
+
+ BUG_ON(!tee);
+
+ if (atomic_dec_and_test(&tee->refcount)) {
+ dev_dbg(_DEV(tee), "%s: refcount=0 call %s::stop()...\n",
+ __func__, tee->name);
+ if (tee->ops->stop)
+ ret = tee->ops->stop(tee);
+ module_put(tee->ops->owner);
+ put_device(tee->dev);
+ }
+ if (ret) {
+ dev_err(_DEV(tee), "%s: %s::stop() has failed, ret=%d\n",
+ __func__, tee->name, ret);
+ }
+
+ count = (int)atomic_read(&tee->refcount);
+ dev_dbg(_DEV(tee), "%s: refcount=%d\n", __func__, count);
+ return ret;
+}
+
+static int tee_supp_open(struct tee *tee)
+{
+ int ret = 0;
+
+ dev_dbg(_DEV(tee), "%s: appclient=\"%s\" pid=%d\n", __func__,
+ current->comm, current->pid);
+
+ BUG_ON(!tee->rpc);
+
+ if (strncmp(_tee_supp_app_name, current->comm,
+ strlen(_tee_supp_app_name)) == 0) {
+ if (atomic_add_return(1, &tee->rpc->used) > 1) {
+ ret = -EBUSY;
+ dev_err(tee->dev, "%s: ERROR Only one Supplicant is allowed\n",
+ __func__);
+ atomic_sub(1, &tee->rpc->used);
+ }
+ }
+
+ return ret;
+}
+
+static void tee_supp_release(struct tee *tee)
+{
+ dev_dbg(_DEV(tee), "%s: appclient=\"%s\" pid=%d\n", __func__,
+ current->comm, current->pid);
+
+ BUG_ON(!tee->rpc);
+
+ if ((atomic_read(&tee->rpc->used) == 1) &&
+ (strncmp(_tee_supp_app_name, current->comm,
+ strlen(_tee_supp_app_name)) == 0))
+ atomic_sub(1, &tee->rpc->used);
+}
+
+static int tee_ctx_open(struct inode *inode, struct file *filp)
+{
+ struct tee_context *ctx;
+ struct tee *tee;
+ int ret;
+
+ tee = container_of(filp->private_data, struct tee, miscdev);
+
+ BUG_ON(!tee);
+ BUG_ON(tee->miscdev.minor != iminor(inode));
+
+ dev_dbg(_DEV(tee), "%s: > name=\"%s\"\n", __func__, tee->name);
+
+ ret = tee_supp_open(tee);
+ if (ret)
+ return ret;
+
+ ctx = tee_context_create(tee);
+ if (IS_ERR_OR_NULL(ctx))
+ return PTR_ERR(ctx);
+
+ ctx->usr_client = 1;
+ filp->private_data = ctx;
+
+ dev_dbg(_DEV(tee), "%s: < ctx=%p is created\n", __func__, (void *)ctx);
+
+ return 0;
+}
+
+static int tee_ctx_release(struct inode *inode, struct file *filp)
+{
+ struct tee_context *ctx = filp->private_data;
+ struct tee *tee;
+
+ if (!ctx)
+ return -EINVAL;
+
+ BUG_ON(!ctx->tee);
+ tee = ctx->tee;
+ BUG_ON(tee->miscdev.minor != iminor(inode));
+
+ dev_dbg(_DEV(tee), "%s: > ctx=%p\n", __func__, ctx);
+
+ tee_context_destroy(ctx);
+ tee_supp_release(tee);
+
+ dev_dbg(_DEV(tee), "%s: < ctx=%p is destroyed\n", __func__, ctx);
+ return 0;
+}
+
+static int tee_do_create_session(struct tee_context *ctx,
+ struct tee_cmd_io __user *u_cmd)
+{
+ int ret = -EINVAL;
+ struct tee_cmd_io k_cmd;
+ struct tee *tee;
+
+ tee = ctx->tee;
+ BUG_ON(!ctx->usr_client);
+
+ dev_dbg(_DEV(tee), "%s: >\n", __func__);
+
+ if (copy_from_user(&k_cmd, (void *)u_cmd, sizeof(struct tee_cmd_io))) {
+ dev_err(_DEV(tee), "%s: copy_from_user failed\n", __func__);
+ goto exit;
+ }
+
+ if (k_cmd.fd_sess > 0) {
+ dev_err(_DEV(tee), "%s: invalid fd_sess %d\n", __func__,
+ k_cmd.fd_sess);
+ goto exit;
+ }
+
+ if ((k_cmd.op == NULL) || (k_cmd.uuid == NULL) ||
+ ((k_cmd.data != NULL) && (k_cmd.data_size == 0)) ||
+ ((k_cmd.data == NULL) && (k_cmd.data_size != 0))) {
+ dev_err(_DEV(tee),
+ "%s: op or/and data parameters are not valid\n",
+ __func__);
+ goto exit;
+ }
+
+ ret = tee_session_create_fd(ctx, &k_cmd);
+ put_user(k_cmd.err, &u_cmd->err);
+ put_user(k_cmd.origin, &u_cmd->origin);
+ if (ret)
+ goto exit;
+
+ put_user(k_cmd.fd_sess, &u_cmd->fd_sess);
+
+exit:
+ dev_dbg(_DEV(tee), "%s: < ret=%d, sessfd=%d\n", __func__, ret,
+ k_cmd.fd_sess);
+ return ret;
+}
+
+static int tee_do_shm_alloc(struct tee_context *ctx,
+ struct tee_shm_io __user *u_shm)
+{
+ int ret = -EINVAL;
+ struct tee_shm_io k_shm;
+ struct tee *tee = ctx->tee;
+
+ BUG_ON(!ctx->usr_client);
+
+ dev_dbg(_DEV(tee), "%s: >\n", __func__);
+
+ if (copy_from_user(&k_shm, (void *)u_shm, sizeof(struct tee_shm_io))) {
+ dev_err(_DEV(tee), "%s: copy_from_user failed\n", __func__);
+ goto exit;
+ }
+
+ if ((k_shm.buffer != NULL) || (k_shm.fd_shm != 0) ||
+ /*(k_shm.flags & ~(tee->shm_flags)) ||*/
+ ((k_shm.flags & tee->shm_flags) == 0) || (k_shm.registered != 0)) {
+ dev_err(_DEV(tee),
+ "%s: shm parameters are not valid %p %d %08x %08x %d\n",
+ __func__, (void *)k_shm.buffer, k_shm.fd_shm,
+ (unsigned int)k_shm.flags, (unsigned int)tee->shm_flags,
+ k_shm.registered);
+ goto exit;
+ }
+
+ ret = tee_shm_alloc_io(ctx, &k_shm);
+ if (ret)
+ goto exit;
+
+ put_user(k_shm.fd_shm, &u_shm->fd_shm);
+ put_user(k_shm.flags, &u_shm->flags);
+
+exit:
+ dev_dbg(_DEV(tee), "%s: < ret=%d, shmfd=%d\n", __func__, ret,
+ k_shm.fd_shm);
+ return ret;
+}
+
+static int tee_do_get_fd_for_rpc_shm(struct tee_context *ctx,
+ struct tee_shm_io __user *u_shm)
+{
+ int ret = -EINVAL;
+ struct tee_shm_io k_shm;
+ struct tee *tee = ctx->tee;
+
+ dev_dbg(_DEV(tee), "%s: >\n", __func__);
+ BUG_ON(!ctx->usr_client);
+
+ if (copy_from_user(&k_shm, (void *)u_shm, sizeof(struct tee_shm_io))) {
+ dev_err(_DEV(tee), "%s: copy_from_user failed\n", __func__);
+ goto exit;
+ }
+
+ if ((k_shm.buffer == NULL) || (k_shm.size == 0) || (k_shm.fd_shm != 0)
+ || (k_shm.flags & ~(tee->shm_flags))
+ || ((k_shm.flags & tee->shm_flags) == 0)
+ || (k_shm.registered != 0)) {
+ dev_err(_DEV(tee), "%s: shm parameters are not valid\n",
+ __func__);
+ goto exit;
+ }
+
+ ret = tee_shm_fd_for_rpc(ctx, &k_shm);
+ if (ret)
+ goto exit;
+
+ put_user(k_shm.fd_shm, &u_shm->fd_shm);
+
+exit:
+ dev_dbg(_DEV(tee), "%s: < ret=%d, shmfd=%d\n", __func__, ret,
+ k_shm.fd_shm);
+ return ret;
+}
+
+static long tee_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
+{
+ int ret = -EINVAL;
+ struct tee_context *ctx = filp->private_data;
+
+ BUG_ON(!ctx);
+ BUG_ON(!ctx->tee);
+
+ dev_dbg(_DEV(ctx->tee), "%s: > cmd nr=%d\n", __func__, _IOC_NR(cmd));
+
+ switch (cmd) {
+ case TEE_OPEN_SESSION_IOC:
+ ret =
+ tee_do_create_session(ctx, (struct tee_cmd_io __user *)arg);
+ break;
+ case TEE_ALLOC_SHM_IOC:
+ ret = tee_do_shm_alloc(ctx, (struct tee_shm_io __user *)arg);
+ break;
+ case TEE_GET_FD_FOR_RPC_SHM_IOC:
+ ret =
+ tee_do_get_fd_for_rpc_shm(ctx,
+ (struct tee_shm_io __user *)arg);
+ break;
+ default:
+ ret = -ENOSYS;
+ break;
+ }
+
+ dev_dbg(_DEV(ctx->tee), "%s: < ret=%d\n", __func__, ret);
+
+ return ret;
+}
+
+const struct file_operations tee_fops = {
+ .owner = THIS_MODULE,
+ .read = tee_supp_read,
+ .write = tee_supp_write,
+ .open = tee_ctx_open,
+ .release = tee_ctx_release,
+ .unlocked_ioctl = tee_ioctl
+};
+
+static void tee_plt_device_release(struct device *dev)
+{
+ pr_debug("%s: (dev=%p)....\n", __func__, dev);
+}
+
+struct tee *tee_core_alloc(struct device *dev, char *name, int id,
+ const struct tee_ops *ops, size_t len)
+{
+ struct tee *tee;
+
+ if (!dev || !name || !ops ||
+ !ops->open || !ops->close || !ops->alloc || !ops->free)
+ return NULL;
+
+ tee = devm_kzalloc(dev, sizeof(struct tee) + len, GFP_KERNEL);
+ if (!tee) {
+ dev_err(dev, "%s: kzalloc failed\n", __func__);
+ return NULL;
+ }
+
+ if (!dev->release)
+ dev->release = tee_plt_device_release;
+
+ tee->dev = dev;
+ tee->id = id;
+ tee->ops = ops;
+ tee->priv = &tee[1];
+
+ snprintf(tee->name, sizeof(tee->name), "optee%s%02d", name, tee->id);
+ pr_info("TEE core: Alloc the misc device \"%s\" (id=%d)\n", tee->name,
+ tee->id);
+
+ tee->miscdev.parent = dev;
+ tee->miscdev.minor = MISC_DYNAMIC_MINOR;
+ tee->miscdev.name = tee->name;
+ tee->miscdev.fops = &tee_fops;
+
+ mutex_init(&tee->lock);
+ atomic_set(&tee->refcount, 0);
+ INIT_LIST_HEAD(&tee->list_ctx);
+ INIT_LIST_HEAD(&tee->list_rpc_shm);
+
+ tee->state = TEE_OFFLINE;
+ tee->shm_flags = TEEC_MEM_INPUT | TEEC_MEM_OUTPUT;
+ tee->test = 0;
+
+ tee_supp_init(tee);
+
+ return tee;
+}
+EXPORT_SYMBOL(tee_core_alloc);
+
+int tee_core_free(struct tee *tee)
+{
+ if (tee) {
+ tee_supp_deinit(tee);
+ devm_kfree(tee->dev, tee);
+ }
+ return 0;
+}
+EXPORT_SYMBOL(tee_core_free);
+
+int tee_core_add(struct tee *tee)
+{
+ int rc = 0;
+
+ if (!tee)
+ return -EINVAL;
+
+ rc = misc_register(&tee->miscdev);
+ if (rc != 0) {
+ pr_err("TEE Core: misc_register() failed name=\"%s\"\n",
+ tee->name);
+ return rc;
+ }
+
+ dev_set_drvdata(tee->miscdev.this_device, tee);
+
+ tee_init_sysfs(tee);
+ tee_create_debug_dir(tee);
+
+ /* Register a static reference on the class misc
+ * to allow finding device by class */
+ BUG_ON(!tee->miscdev.this_device->class);
+ if (misc_class)
+ BUG_ON(misc_class != tee->miscdev.this_device->class);
+ else
+ misc_class = tee->miscdev.this_device->class;
+
+ pr_info("TEE Core: Register the misc device \"%s\" (id=%d,minor=%d)\n",
+ dev_name(tee->miscdev.this_device), tee->id,
+ tee->miscdev.minor);
+ return rc;
+}
+EXPORT_SYMBOL(tee_core_add);
+
+int tee_core_del(struct tee *tee)
+{
+ if (tee) {
+ pr_info("TEE Core: Destroy the misc device \"%s\" (id=%d)\n",
+ dev_name(tee->miscdev.this_device), tee->id);
+
+ tee_cleanup_sysfs(tee);
+ tee_delete_debug_dir(tee);
+
+ if (tee->miscdev.minor != MISC_DYNAMIC_MINOR) {
+ pr_info("TEE Core: Deregister the misc device \"%s\" (id=%d)\n",
+ dev_name(tee->miscdev.this_device), tee->id);
+ misc_deregister(&tee->miscdev);
+ }
+ }
+
+ tee_core_free(tee);
+
+ return 0;
+}
+EXPORT_SYMBOL(tee_core_del);
+
+static int __init tee_core_init(void)
+{
+ pr_info("\nTEE Core Framework initialization (ver %s)\n",
+ _TEE_CORE_FW_VER);
+ tee_init_debugfs();
+
+ return 0;
+}
+
+static void __exit tee_core_exit(void)
+{
+ tee_exit_debugfs();
+ pr_info("TEE Core Framework unregistered\n");
+}
+
+module_init(tee_core_init);
+module_exit(tee_core_exit);
+
+MODULE_AUTHOR("STMicroelectronics");
+MODULE_DESCRIPTION("STM Secure TEE Framework/Core TEEC v1.0");
+MODULE_SUPPORTED_DEVICE("");
+MODULE_VERSION(_TEE_CORE_FW_VER);
+MODULE_LICENSE("GPL");
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * Copyright (c) 2014, STMicroelectronics International N.V.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License Version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include <linux/kernel.h>
+#include <linux/debugfs.h>
+#include <linux/device.h>
+#include <linux/uaccess.h>
+
+#include "linux/tee_core.h"
+#include "tee_debugfs.h"
+
+static struct dentry *tee_debugfs_dir;
+
+static ssize_t tee_trace_read(struct file *filp, char __user *userbuf,
+ size_t count, loff_t *ppos)
+{
+ struct tee *tee = filp->private_data;
+
+ char buff[258];
+ int len = sprintf(buff, "device=%s\n NO LOG AVAILABLE\n", tee->name);
+
+ return simple_read_from_buffer(userbuf, count, ppos, buff, len);
+}
+
+static const struct file_operations log_tee_ops = {
+ .read = tee_trace_read,
+ .open = simple_open,
+ .llseek = generic_file_llseek,
+};
+
+void tee_create_debug_dir(struct tee *tee)
+{
+ struct dentry *entry;
+ struct device *dev = tee->miscdev.this_device;
+
+ if (!tee_debugfs_dir)
+ return;
+
+ tee->dbg_dir = debugfs_create_dir(dev_name(dev), tee_debugfs_dir);
+ if (!tee->dbg_dir)
+ goto error_create_file;
+
+ entry = debugfs_create_file("log", S_IRUGO, tee->dbg_dir,
+ tee, &log_tee_ops);
+ if (!entry)
+ goto error_create_file;
+
+ return;
+
+error_create_file:
+ dev_err(dev, "can't create debugfs file\n");
+ tee_delete_debug_dir(tee);
+}
+
+void tee_delete_debug_dir(struct tee *tee)
+{
+ if (!tee || !tee->dbg_dir)
+ return;
+
+ debugfs_remove_recursive(tee->dbg_dir);
+}
+
+void __init tee_init_debugfs(void)
+{
+ if (debugfs_initialized()) {
+ tee_debugfs_dir = debugfs_create_dir("tee", NULL);
+ if (IS_ERR(tee_debugfs_dir))
+ pr_err("can't create debugfs dir\n");
+ }
+}
+
+void __exit tee_exit_debugfs(void)
+{
+ if (tee_debugfs_dir)
+ debugfs_remove(tee_debugfs_dir);
+}
--- /dev/null
+/*
+ * 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__ */
--- /dev/null
+/*
+ * Copyright (c) 2014, STMicroelectronics International N.V.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License Version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/miscdevice.h>
+#include <linux/io.h>
+#include <linux/vmalloc.h>
+
+#include "linux/tee_kernel_api.h"
+#include "linux/tee_core.h"
+#include "linux/tee_ioc.h"
+
+#include "tee_core_priv.h"
+#include "tee_shm.h"
+#include "tee_supp_com.h"
+
+#define TEE_TZ_DEVICE_NAME "opteearmtz00"
+
+static void reset_tee_cmd(struct tee_cmd_io *cmd)
+{
+ cmd->fd_sess = -1;
+ cmd->cmd = 0;
+ cmd->uuid = NULL;
+ cmd->origin = TEEC_ORIGIN_API;
+ cmd->err = TEEC_SUCCESS;
+ cmd->data = NULL;
+ cmd->data_size = 0;
+ cmd->op = NULL;
+}
+
+TEEC_Result TEEC_InitializeContext(const char *name, TEEC_Context *context)
+{
+ struct tee *tee;
+ struct tee_context *ctx;
+ pr_cont("%s: > name=\"%s\"\n", __func__, name);
+
+ if (!context)
+ return TEEC_ERROR_BAD_PARAMETERS;
+
+ context->fd = 0;
+
+ if (name == NULL)
+ strncpy(context->devname, TEE_TZ_DEVICE_NAME,
+ sizeof(context->devname));
+ else
+ strncpy(context->devname, name, sizeof(context->devname));
+
+ tee = tee_get_tee(context->devname);
+ if (!tee) {
+ pr_err("%s - can't get device [%s]\n", __func__, name);
+ return TEEC_ERROR_BAD_PARAMETERS;
+ }
+
+ ctx = tee_context_create(tee);
+ if (IS_ERR_OR_NULL(ctx))
+ return TEEC_ERROR_BAD_PARAMETERS;
+
+ ctx->usr_client = 0;
+
+ /* TODO fixme will not work on 64-bit platform */
+ context->fd = (int)(uintptr_t)ctx;
+ BUG_ON(ctx != (struct tee_context *)(uintptr_t)context->fd);
+
+ pr_cont("%s: < ctx=%p is created\n", __func__, (void *)ctx);
+ return TEEC_SUCCESS;
+}
+EXPORT_SYMBOL(TEEC_InitializeContext);
+
+void TEEC_FinalizeContext(TEEC_Context *context)
+{
+ if (!context || !context->fd) {
+ pr_err("%s - can't release context %p:[%s]\n", __func__,
+ context, (context
+ && context->devname) ? context->devname : "");
+ return;
+ }
+ /* TODO fixme will not work on 64-bit platform */
+ tee_context_destroy((struct tee_context *)(uintptr_t)context->fd);
+ return;
+}
+EXPORT_SYMBOL(TEEC_FinalizeContext);
+
+TEEC_Result TEEC_OpenSession(TEEC_Context *context,
+ TEEC_Session *session,
+ const TEEC_UUID *destination,
+ uint32_t connectionMethod,
+ const void *connectionData,
+ TEEC_Operation *operation,
+ uint32_t *return_origin)
+{
+ TEEC_Operation dummy_op;
+ struct tee_cmd_io cmd;
+ struct tee_session *sess;
+ struct tee_context *ctx;
+
+ if (!operation) {
+ /*
+ * The code here exist because Global Platform API states that
+ * it is allowed to give operation as a NULL pointer.
+ * In kernel and secure world we in most cases don't want
+ * this to be NULL, hence we use this dummy operation when
+ * a client doesn't provide any operation.
+ */
+ memset(&dummy_op, 0, sizeof(TEEC_Operation));
+ operation = &dummy_op;
+ }
+
+ if (!context || !session || !destination || !operation
+ || !return_origin)
+ return TEEC_ERROR_BAD_PARAMETERS;
+
+ session->fd = 0;
+
+ /* TODO fixme will not work on 64-bit platform */
+ ctx = (struct tee_context *)(uintptr_t)context->fd;
+ reset_tee_cmd(&cmd);
+ cmd.op = operation;
+ cmd.uuid = (TEEC_UUID *) destination;
+
+ sess = tee_session_create_and_open(ctx, &cmd);
+ if (IS_ERR_OR_NULL(sess)) {
+ if (cmd.origin)
+ *return_origin = cmd.origin;
+ else
+ *return_origin = TEEC_ORIGIN_COMMS;
+ if (cmd.err)
+ return cmd.err;
+ else
+ return TEEC_ERROR_COMMUNICATION;
+ } else {
+ *return_origin = cmd.origin;
+ /* TODO fixme will not work on 64-bit platform */
+ session->fd = (int)(uintptr_t)sess;
+ BUG_ON(sess != (struct tee_session *)(uintptr_t)session->fd);
+ return cmd.err;
+ }
+}
+EXPORT_SYMBOL(TEEC_OpenSession);
+
+void TEEC_CloseSession(TEEC_Session *session)
+{
+ if (session && session->fd) {
+ /* TODO fixme will not work on 64-bit platform */
+ struct tee_session *sess =
+ (struct tee_session *)(uintptr_t)session->fd;
+ tee_session_close_and_destroy(sess);
+ }
+}
+EXPORT_SYMBOL(TEEC_CloseSession);
+
+TEEC_Result TEEC_InvokeCommand(TEEC_Session *session,
+ uint32_t commandID,
+ TEEC_Operation *operation,
+ uint32_t *return_origin)
+{
+ int ret = 0;
+ struct tee_cmd_io cmd;
+ struct tee_session *sess;
+
+ if (!session || !operation || !return_origin || !session->fd)
+ return TEEC_ERROR_BAD_PARAMETERS;
+
+ /* TODO fixme will not work on 64-bit platform */
+ sess = (struct tee_session *)(uintptr_t)session->fd;
+ reset_tee_cmd(&cmd);
+ cmd.cmd = commandID;
+ cmd.op = operation;
+
+ ret = tee_session_invoke_be(sess, &cmd);
+ if (ret) {
+ if (cmd.origin)
+ *return_origin = cmd.origin;
+ else
+ *return_origin = TEEC_ORIGIN_COMMS;
+ if (cmd.err)
+ return cmd.err;
+ else
+ return TEEC_ERROR_COMMUNICATION;
+ } else {
+ *return_origin = cmd.origin;
+ return cmd.err;
+ }
+}
+EXPORT_SYMBOL(TEEC_InvokeCommand);
+
+TEEC_Result TEEC_RegisterSharedMemory(TEEC_Context *context,
+ TEEC_SharedMemory *sharedMem)
+{
+ if (!sharedMem)
+ return TEEC_ERROR_BAD_PARAMETERS;
+
+ sharedMem->registered = 1;
+ return TEEC_SUCCESS;
+}
+EXPORT_SYMBOL(TEEC_RegisterSharedMemory);
+
+TEEC_Result TEEC_AllocateSharedMemory(TEEC_Context *context,
+ TEEC_SharedMemory *shared_memory)
+{
+ struct tee_shm_io shm_io;
+ int ret;
+ struct tee_shm *shm;
+
+ if (!context || !context->ctx || !shared_memory)
+ return TEEC_ERROR_BAD_PARAMETERS;
+
+ shm_io.size = shared_memory->size;
+ shm_io.flags = shared_memory->flags | TEEC_MEM_KAPI;
+ ret = tee_shm_alloc_io(context->ctx, &shm_io);
+ if (ret) {
+ pr_err("%s: tee_shm_alloc_io(%zd) failed\n", __func__,
+ shared_memory->size);
+ return TEEC_ERROR_OUT_OF_MEMORY;
+ }
+
+ shared_memory->registered = 0;
+ shared_memory->flags = shm_io.flags;
+ shared_memory->d.fd = shm_io.fd_shm;
+
+ shm = (struct tee_shm *)(long)shm_io.fd_shm;
+ shared_memory->buffer = shm->kaddr;
+
+ pr_debug("%s(%zd) => fd=%d, kaddr=%p\n", __func__,
+ shm_io.size, shm_io.fd_shm, (void *)shared_memory->buffer);
+
+ return TEEC_SUCCESS;
+}
+EXPORT_SYMBOL(TEEC_AllocateSharedMemory);
+
+void TEEC_ReleaseSharedMemory(TEEC_SharedMemory *shared_memory)
+{
+ struct tee_shm *shm;
+
+ if (!shared_memory || shared_memory->registered)
+ return;
+
+ pr_debug("%s (vaddr = %p)\n", __func__, shared_memory->buffer);
+
+ shm = (struct tee_shm *)(long)shared_memory->d.fd;
+ tee_shm_free_io(shm);
+
+ shared_memory->buffer = NULL;
+ shared_memory->d.fd = 0;
+}
+EXPORT_SYMBOL(TEEC_ReleaseSharedMemory);
--- /dev/null
+/*
+ * Copyright (c) 2014, Linaro Limited
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License Version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include <linux/slab.h>
+#include "tee_mutex_wait.h"
+
+struct tee_mutex_wait {
+ struct list_head link;
+ struct completion comp;
+ struct mutex mu;
+ u32 wait_after;
+ u32 key;
+};
+
+/*
+ * Compares two serial numbers using Serial Number Arithmetic
+ * (https://www.ietf.org/rfc/rfc1982.txt).
+ */
+#define TICK_GT(t1, t2) \
+ (((t1) < (t2) && (t2) - (t1) > 0xFFFFFFFFu) || \
+ ((t1) > (t2) && (t1) - (t2) < 0xFFFFFFFFu))
+
+static struct tee_mutex_wait *tee_mutex_wait_get(struct device *dev,
+ struct tee_mutex_wait_private *priv, u32 key)
+{
+ struct tee_mutex_wait *w;
+
+ mutex_lock(&priv->mu);
+
+ list_for_each_entry(w, &priv->db, link)
+ if (w->key == key)
+ goto out;
+
+ w = kmalloc(sizeof(struct tee_mutex_wait), GFP_KERNEL);
+ if (!w) {
+ dev_err(dev, "kmalloc <struct tee_mutex_wait> failed\n");
+ goto out;
+ }
+
+ init_completion(&w->comp);
+ mutex_init(&w->mu);
+ w->wait_after = 0;
+ w->key = key;
+ list_add_tail(&w->link, &priv->db);
+out:
+ mutex_unlock(&priv->mu);
+ return w;
+}
+
+static void tee_mutex_wait_delete_entry(struct tee_mutex_wait *w)
+{
+ list_del(&w->link);
+ mutex_destroy(&w->mu);
+ kfree(w);
+}
+
+void tee_mutex_wait_delete(struct device *dev,
+ struct tee_mutex_wait_private *priv,
+ u32 key)
+{
+ struct tee_mutex_wait *w;
+
+ mutex_lock(&priv->mu);
+
+ list_for_each_entry(w, &priv->db, link) {
+ if (w->key == key) {
+ tee_mutex_wait_delete_entry(w);
+ break;
+ }
+ }
+
+ mutex_unlock(&priv->mu);
+}
+EXPORT_SYMBOL(tee_mutex_wait_delete);
+
+void tee_mutex_wait_wakeup(struct device *dev,
+ struct tee_mutex_wait_private *priv,
+ u32 key, u32 wait_after)
+{
+ struct tee_mutex_wait *w = tee_mutex_wait_get(dev, priv, key);
+
+ if (!w)
+ return;
+
+ mutex_lock(&w->mu);
+ w->wait_after = wait_after;
+ mutex_unlock(&w->mu);
+ complete(&w->comp);
+}
+EXPORT_SYMBOL(tee_mutex_wait_wakeup);
+
+void tee_mutex_wait_sleep(struct device *dev,
+ struct tee_mutex_wait_private *priv,
+ u32 key, u32 wait_tick)
+{
+ struct tee_mutex_wait *w = tee_mutex_wait_get(dev, priv, key);
+ u32 wait_after;
+
+ if (!w)
+ return;
+
+ mutex_lock(&w->mu);
+ wait_after = w->wait_after;
+ mutex_unlock(&w->mu);
+
+ if (TICK_GT(wait_tick, wait_after))
+ wait_for_completion_timeout(&w->comp, HZ);
+}
+EXPORT_SYMBOL(tee_mutex_wait_sleep);
+
+int tee_mutex_wait_init(struct tee_mutex_wait_private *priv)
+{
+ mutex_init(&priv->mu);
+ INIT_LIST_HEAD(&priv->db);
+ return 0;
+}
+EXPORT_SYMBOL(tee_mutex_wait_init);
+
+void tee_mutex_wait_exit(struct tee_mutex_wait_private *priv)
+{
+ /*
+ * It's the callers responibility to ensure that no one is using
+ * anything inside priv.
+ */
+
+ mutex_destroy(&priv->mu);
+ while (!list_empty(&priv->db)) {
+ struct tee_mutex_wait *w =
+ list_first_entry(&priv->db,
+ struct tee_mutex_wait,
+ link);
+ tee_mutex_wait_delete_entry(w);
+ }
+}
+EXPORT_SYMBOL(tee_mutex_wait_exit);
--- /dev/null
+/*
+ * Copyright (c) 2014, Linaro Limited
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License Version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#ifndef TEE_MUTEX_WAIT_H
+#define TEE_MUTEX_WAIT_H
+
+#include <linux/mutex.h>
+#include <linux/list.h>
+#include <linux/device.h>
+
+struct tee_mutex_wait_private {
+ struct mutex mu;
+ struct list_head db;
+};
+
+int tee_mutex_wait_init(struct tee_mutex_wait_private *priv);
+void tee_mutex_wait_exit(struct tee_mutex_wait_private *priv);
+
+void tee_mutex_wait_delete(struct device *dev,
+ struct tee_mutex_wait_private *priv,
+ u32 key);
+void tee_mutex_wait_wakeup(struct device *dev,
+ struct tee_mutex_wait_private *priv,
+ u32 key, u32 wait_after);
+void tee_mutex_wait_sleep(struct device *dev,
+ struct tee_mutex_wait_private *priv,
+ u32 key, u32 wait_tick);
+
+#endif /*TEE_MUTEX_WAIT_H*/
--- /dev/null
+/*
+ * Copyright (c) 2014, STMicroelectronics International N.V.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License Version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/types.h>
+#include <linux/file.h>
+#include <linux/atomic.h>
+#include <linux/uaccess.h>
+#include <linux/anon_inodes.h>
+
+#include "tee_shm.h"
+#include "tee_core_priv.h"
+
+static int _init_tee_cmd(struct tee_session *sess, struct tee_cmd_io *cmd_io,
+ struct tee_cmd *cmd);
+static void _update_client_tee_cmd(struct tee_session *sess,
+ struct tee_cmd_io *cmd_io,
+ struct tee_cmd *cmd);
+static void _release_tee_cmd(struct tee_session *sess, struct tee_cmd *cmd);
+
+#define _DEV_TEE _DEV(sess->ctx->tee)
+
+#define INMSG dev_dbg(_DEV_TEE, "%s: >\n", __func__)
+#define OUTMSG(val) dev_dbg(_DEV_TEE, "%s: < %d\n", __func__, (int)val)
+
+/******************************************************************************/
+
+static inline bool flag_set(int val, int flags)
+{
+ return (val & flags) == flags;
+}
+
+static inline bool is_mapped_temp(int flags)
+{
+ return flag_set(flags, TEE_SHM_MAPPED | TEE_SHM_TEMP);
+}
+
+
+/******************************************************************************/
+
+#define _UUID_STR_SIZE 35
+static char *_uuid_to_str(const TEEC_UUID *uuid)
+{
+ static char uuid_str[_UUID_STR_SIZE];
+
+ if (uuid) {
+ sprintf(uuid_str,
+ "%08x-%04x-%04x-%02x%02x%02x%02x%02x%02x%02x%02x",
+ uuid->timeLow, uuid->timeMid, uuid->timeHiAndVersion,
+ uuid->clockSeqAndNode[0], uuid->clockSeqAndNode[1],
+ uuid->clockSeqAndNode[2], uuid->clockSeqAndNode[3],
+ uuid->clockSeqAndNode[4], uuid->clockSeqAndNode[5],
+ uuid->clockSeqAndNode[6], uuid->clockSeqAndNode[7]);
+ } else {
+ sprintf(uuid_str, "NULL");
+ }
+
+ return uuid_str;
+}
+
+static int tee_copy_from_user(struct tee_context *ctx, void *to, void *from,
+ size_t size)
+{
+ if ((!to) || (!from) || (!size))
+ return 0;
+ if (ctx->usr_client)
+ return copy_from_user(to, from, size);
+ else {
+ memcpy(to, from, size);
+ return 0;
+ }
+}
+
+static int tee_copy_to_user(struct tee_context *ctx, void *to, void *from,
+ size_t size)
+{
+ if ((!to) || (!from) || (!size))
+ return 0;
+ if (ctx->usr_client)
+ return copy_to_user(to, from, size);
+ else {
+ memcpy(to, from, size);
+ return 0;
+ }
+}
+
+/* Defined as macro to let the put_user macro see the types */
+#define tee_put_user(ctx, from, to) \
+ do { \
+ if ((ctx)->usr_client) \
+ put_user(from, to); \
+ else \
+ *to = from; \
+ } while (0)
+
+static inline int tee_session_is_opened(struct tee_session *sess)
+{
+ if (sess && sess->sessid)
+ return (sess->sessid != 0);
+ return 0;
+}
+
+static int tee_session_open_be(struct tee_session *sess,
+ struct tee_cmd_io *cmd_io)
+{
+ int ret = -EINVAL;
+ struct tee *tee;
+ struct tee_cmd cmd;
+
+ BUG_ON(!sess || !sess->ctx || !sess->ctx->tee);
+
+ tee = sess->ctx->tee;
+
+ dev_dbg(_DEV(tee), "%s: > open a new session", __func__);
+
+ sess->sessid = 0;
+ ret = _init_tee_cmd(sess, cmd_io, &cmd);
+ if (ret)
+ goto out;
+
+ if (cmd.uuid) {
+ dev_dbg(_DEV(tee), "%s: UUID=%s\n", __func__,
+ _uuid_to_str((TEEC_UUID *) cmd.uuid->kaddr));
+ }
+
+ ret = tee->ops->open(sess, &cmd);
+ if (ret == 0)
+ _update_client_tee_cmd(sess, cmd_io, &cmd);
+ else {
+ /* propagate the reason of the error */
+ cmd_io->origin = cmd.origin;
+ cmd_io->err = cmd.err;
+ }
+
+out:
+ _release_tee_cmd(sess, &cmd);
+ dev_dbg(_DEV(tee), "%s: < ret=%d, sessid=%08x", __func__, ret,
+ sess->sessid);
+ return ret;
+}
+
+int tee_session_invoke_be(struct tee_session *sess, struct tee_cmd_io *cmd_io)
+{
+ int ret = -EINVAL;
+ struct tee *tee;
+ struct tee_cmd cmd;
+
+ BUG_ON(!sess || !sess->ctx || !sess->ctx->tee);
+
+ tee = sess->ctx->tee;
+
+ dev_dbg(_DEV(tee), "%s: > sessid=%08x, cmd=0x%08x\n", __func__,
+ sess->sessid, cmd_io->cmd);
+
+ ret = _init_tee_cmd(sess, cmd_io, &cmd);
+ if (ret)
+ goto out;
+
+ ret = tee->ops->invoke(sess, &cmd);
+ if (!ret)
+ _update_client_tee_cmd(sess, cmd_io, &cmd);
+ else {
+ /* propagate the reason of the error */
+ cmd_io->origin = cmd.origin;
+ cmd_io->err = cmd.err;
+ }
+
+out:
+ _release_tee_cmd(sess, &cmd);
+ dev_dbg(_DEV(tee), "%s: < ret=%d", __func__, ret);
+ return ret;
+}
+
+static int tee_session_close_be(struct tee_session *sess)
+{
+ int ret = -EINVAL;
+ struct tee *tee;
+
+ BUG_ON(!sess || !sess->ctx || !sess->ctx->tee);
+
+ tee = sess->ctx->tee;
+
+ dev_dbg(_DEV(tee), "%s: > sessid=%08x", __func__, sess->sessid);
+
+ ret = tee->ops->close(sess);
+ sess->sessid = 0;
+
+ dev_dbg(_DEV(tee), "%s: < ret=%d", __func__, ret);
+ return ret;
+}
+
+static int tee_session_cancel_be(struct tee_session *sess,
+ struct tee_cmd_io *cmd_io)
+{
+ int ret = -EINVAL;
+ struct tee *tee;
+ struct tee_cmd cmd;
+
+ BUG_ON(!sess || !sess->ctx || !sess->ctx->tee);
+
+ tee = sess->ctx->tee;
+
+ dev_dbg(_DEV(tee), "%s: > sessid=%08x, cmd=0x%08x\n", __func__,
+ sess->sessid, cmd_io->cmd);
+
+ ret = _init_tee_cmd(sess, cmd_io, &cmd);
+ if (ret)
+ goto out;
+
+ ret = tee->ops->cancel(sess, &cmd);
+
+out:
+ _release_tee_cmd(sess, &cmd);
+ dev_dbg(_DEV(tee), "%s: < ret=%d", __func__, ret);
+ return ret;
+}
+
+static int tee_do_invoke_command(struct tee_session *sess,
+ struct tee_cmd_io __user *u_cmd)
+{
+ int ret = -EINVAL;
+ struct tee *tee;
+ struct tee_cmd_io k_cmd;
+ struct tee_context *ctx;
+
+ BUG_ON(!sess->ctx);
+ BUG_ON(!sess->ctx->tee);
+ ctx = sess->ctx;
+ tee = sess->ctx->tee;
+
+ dev_dbg(_DEV(tee), "%s: > sessid=%08x\n", __func__, sess->sessid);
+
+ BUG_ON(!sess->sessid);
+
+ if (tee_copy_from_user
+ (ctx, &k_cmd, (void *)u_cmd, sizeof(struct tee_cmd_io))) {
+ dev_err(_DEV(tee), "%s: tee_copy_from_user failed\n", __func__);
+ goto exit;
+ }
+
+ if ((k_cmd.op == NULL) || (k_cmd.uuid != NULL) ||
+ (k_cmd.data != NULL) || (k_cmd.data_size != 0)) {
+ dev_err(_DEV(tee),
+ "%s: op or/and data parameters are not valid\n",
+ __func__);
+ goto exit;
+ }
+
+ ret = tee_session_invoke_be(sess, &k_cmd);
+ if (ret)
+ dev_err(_DEV(tee), "%s: tee_invoke_command failed\n", __func__);
+
+ tee_put_user(ctx, k_cmd.err, &u_cmd->err);
+ tee_put_user(ctx, k_cmd.origin, &u_cmd->origin);
+
+exit:
+ dev_dbg(_DEV(tee), "%s: < ret=%d\n", __func__, ret);
+ return ret;
+}
+
+static int tee_do_cancel_cmd(struct tee_session *sess,
+ struct tee_cmd_io __user *u_cmd)
+{
+ int ret = -EINVAL;
+ struct tee *tee;
+ struct tee_cmd_io k_cmd;
+ struct tee_context *ctx;
+
+ BUG_ON(!sess->ctx);
+ BUG_ON(!sess->ctx->tee);
+ ctx = sess->ctx;
+ tee = sess->ctx->tee;
+
+ dev_dbg(sess->ctx->tee->dev, "%s: > sessid=%08x\n", __func__,
+ sess->sessid);
+
+ BUG_ON(!sess->sessid);
+
+ if (tee_copy_from_user
+ (ctx, &k_cmd, (void *)u_cmd, sizeof(struct tee_cmd_io))) {
+ dev_err(_DEV(tee), "%s: tee_copy_from_user failed\n", __func__);
+ goto exit;
+ }
+
+ if ((k_cmd.op == NULL) || (k_cmd.uuid != NULL) ||
+ (k_cmd.data != NULL) || (k_cmd.data_size != 0)) {
+ dev_err(_DEV(tee),
+ "%s: op or/and data parameters are not valid\n",
+ __func__);
+ goto exit;
+ }
+
+ ret = tee_session_cancel_be(sess, &k_cmd);
+ if (ret)
+ dev_err(_DEV(tee), "%s: tee_invoke_command failed\n", __func__);
+
+ tee_put_user(ctx, k_cmd.err, &u_cmd->err);
+ tee_put_user(ctx, k_cmd.origin, &u_cmd->origin);
+
+exit:
+ dev_dbg(_DEV(tee), "%s: < ret=%d", __func__, ret);
+ return ret;
+}
+
+static long tee_session_ioctl(struct file *filp, unsigned int cmd,
+ unsigned long arg)
+{
+ struct tee *tee;
+ struct tee_session *sess = filp->private_data;
+ int ret;
+
+ BUG_ON(!sess || !sess->ctx || !sess->ctx->tee);
+
+ tee = sess->ctx->tee;
+
+ dev_dbg(_DEV(tee), "%s: > cmd nr=%d\n", __func__, _IOC_NR(cmd));
+
+ switch (cmd) {
+ case TEE_INVOKE_COMMAND_IOC:
+ ret =
+ tee_do_invoke_command(sess,
+ (struct tee_cmd_io __user *)arg);
+ break;
+ case TEE_REQUEST_CANCELLATION_IOC:
+ ret = tee_do_cancel_cmd(sess, (struct tee_cmd_io __user *)arg);
+ break;
+ default:
+ ret = -ENOSYS;
+ break;
+ }
+
+ dev_dbg(_DEV(tee), "%s: < ret=%d\n", __func__, ret);
+
+ return ret;
+}
+
+static int tee_session_release(struct inode *inode, struct file *filp)
+{
+ struct tee_session *sess = filp->private_data;
+ int ret = 0;
+ struct tee *tee;
+
+ BUG_ON(!sess || !sess->ctx || !sess->ctx->tee);
+ tee = sess->ctx->tee;
+
+ ret = tee_session_close_and_destroy(sess);
+ return ret;
+}
+
+const struct file_operations tee_session_fops = {
+ .owner = THIS_MODULE,
+ .unlocked_ioctl = tee_session_ioctl,
+ .release = tee_session_release,
+};
+
+int tee_session_close_and_destroy(struct tee_session *sess)
+{
+ int ret;
+ struct tee *tee;
+ struct tee_context *ctx;
+
+ if (!sess || !sess->ctx || !sess->ctx->tee)
+ return -EINVAL;
+
+ ctx = sess->ctx;
+ tee = ctx->tee;
+
+ dev_dbg(_DEV(tee), "%s: > sess=%p\n", __func__, sess);
+
+ if (!tee_session_is_opened(sess))
+ return -EINVAL;
+
+ ret = tee_session_close_be(sess);
+
+ mutex_lock(&sess->ctx->tee->lock);
+ tee_dec_stats(&tee->stats[TEE_STATS_SESSION_IDX]);
+ list_del(&sess->entry);
+ mutex_unlock(&sess->ctx->tee->lock);
+
+ devm_kfree(_DEV(tee), sess);
+ tee_context_put(ctx);
+ tee_put(tee);
+
+ dev_dbg(_DEV(tee), "%s: <\n", __func__);
+ return ret;
+}
+
+struct tee_session *tee_session_create_and_open(struct tee_context *ctx,
+ struct tee_cmd_io *cmd_io)
+{
+ int ret = 0;
+ struct tee_session *sess;
+ struct tee *tee;
+
+ BUG_ON(!ctx->tee);
+
+ tee = ctx->tee;
+
+ dev_dbg(_DEV(tee), "%s: >\n", __func__);
+ ret = tee_get(tee);
+ if (ret)
+ return ERR_PTR(-EBUSY);
+
+ sess = devm_kzalloc(_DEV(tee), sizeof(struct tee_session), GFP_KERNEL);
+ if (!sess) {
+ dev_err(_DEV(tee), "%s: tee_session allocation() failed\n",
+ __func__);
+ tee_put(tee);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ tee_context_get(ctx);
+ sess->ctx = ctx;
+
+ ret = tee_session_open_be(sess, cmd_io);
+ if (ret || !sess->sessid || cmd_io->err) {
+ dev_err(_DEV(tee), "%s: ERROR ret=%d (err=0x%08x, org=%d, sessid=0x%08x)\n",
+ __func__, ret, cmd_io->err,
+ cmd_io->origin, sess->sessid);
+ tee_put(tee);
+ tee_context_put(ctx);
+ devm_kfree(_DEV(tee), sess);
+ if (ret)
+ return ERR_PTR(ret);
+ else
+ return NULL;
+ }
+
+ mutex_lock(&tee->lock);
+ tee_inc_stats(&tee->stats[TEE_STATS_SESSION_IDX]);
+ list_add_tail(&sess->entry, &ctx->list_sess);
+ mutex_unlock(&tee->lock);
+
+ dev_dbg(_DEV(tee), "%s: < sess=%p\n", __func__, sess);
+ return sess;
+}
+
+int tee_session_create_fd(struct tee_context *ctx, struct tee_cmd_io *cmd_io)
+{
+ int ret;
+ struct tee_session *sess;
+ struct tee *tee = ctx->tee;
+
+ BUG_ON(cmd_io->fd_sess > 0);
+
+ dev_dbg(_DEV(tee), "%s: >\n", __func__);
+
+ sess = tee_session_create_and_open(ctx, cmd_io);
+ if (IS_ERR_OR_NULL(sess)) {
+ ret = PTR_ERR(sess);
+ dev_dbg(_DEV(tee), "%s: ERROR can't create the session (ret=%d, err=0x%08x, org=%d)\n",
+ __func__, ret, cmd_io->err, cmd_io->origin);
+ cmd_io->fd_sess = -1;
+ goto out;
+ }
+
+ /* Retrieve a fd */
+ cmd_io->fd_sess = -1;
+ ret =
+ anon_inode_getfd("tee_session", &tee_session_fops, sess, O_CLOEXEC);
+ if (ret < 0) {
+ dev_err(_DEV(tee), "%s: ERROR can't get a fd (ret=%d)\n",
+ __func__, ret);
+ tee_session_close_and_destroy(sess);
+ goto out;
+ }
+ cmd_io->fd_sess = ret;
+ ret = 0;
+
+out:
+ dev_dbg(_DEV(tee), "%s: < ret=%d, sess=%p, fd=%d\n", __func__,
+ ret, sess, cmd_io->fd_sess);
+ return ret;
+}
+
+static bool tee_session_is_supported_type(struct tee_session *sess, int type)
+{
+ switch (type) {
+ case TEEC_NONE:
+ case TEEC_VALUE_INPUT:
+ case TEEC_VALUE_OUTPUT:
+ case TEEC_VALUE_INOUT:
+ case TEEC_MEMREF_TEMP_INPUT:
+ case TEEC_MEMREF_TEMP_OUTPUT:
+ case TEEC_MEMREF_TEMP_INOUT:
+ case TEEC_MEMREF_WHOLE:
+ case TEEC_MEMREF_PARTIAL_INPUT:
+ case TEEC_MEMREF_PARTIAL_OUTPUT:
+ case TEEC_MEMREF_PARTIAL_INOUT:
+ return true;
+ default:
+ dev_err(_DEV_TEE, "type is invalid (type %02x)\n", type);
+ return false;
+ }
+}
+
+static int to_memref_type(int flags)
+{
+ if (flag_set(flags, TEEC_MEM_INPUT | TEEC_MEM_OUTPUT))
+ return TEEC_MEMREF_TEMP_INOUT;
+
+ if (flag_set(flags, TEEC_MEM_INPUT))
+ return TEEC_MEMREF_TEMP_INPUT;
+
+ if (flag_set(flags, TEEC_MEM_OUTPUT))
+ return TEEC_MEMREF_TEMP_OUTPUT;
+
+ pr_err("%s: bad flags=%x\n", __func__, flags);
+ return 0;
+}
+
+static int _init_tee_cmd(struct tee_session *sess, struct tee_cmd_io *cmd_io,
+ struct tee_cmd *cmd)
+{
+ int ret = -EINVAL;
+ int idx;
+ TEEC_Operation op;
+ struct tee_data *param = &cmd->param;
+ struct tee *tee;
+ struct tee_context *ctx;
+
+ BUG_ON(!sess->ctx);
+ BUG_ON(!sess->ctx->tee);
+ ctx = sess->ctx;
+ tee = sess->ctx->tee;
+
+ dev_dbg(_DEV(tee), "%s: > sessid=%08x\n", __func__, sess->sessid);
+
+ memset(cmd, 0, sizeof(struct tee_cmd));
+
+ cmd->cmd = cmd_io->cmd;
+ cmd->origin = TEEC_ORIGIN_TEE;
+ cmd->err = TEEC_ERROR_BAD_PARAMETERS;
+ cmd_io->origin = cmd->origin;
+ cmd_io->err = cmd->err;
+
+ if (tee_context_copy_from_client(ctx, &op, cmd_io->op, sizeof(op)))
+ goto out;
+
+ cmd->param.type_original = op.paramTypes;
+
+ for (idx = 0; idx < TEEC_CONFIG_PAYLOAD_REF_COUNT; ++idx) {
+ uint32_t offset = 0;
+ uint32_t size = 0;
+ int type = TEEC_PARAM_TYPE_GET(op.paramTypes, idx);
+
+ switch (type) {
+ case TEEC_NONE:
+ break;
+
+ case TEEC_VALUE_INPUT:
+ case TEEC_VALUE_OUTPUT:
+ case TEEC_VALUE_INOUT:
+ param->params[idx].value = op.params[idx].value;
+ dev_dbg(_DEV_TEE,
+ "%s: param[%d]:type=%d,a=%08x,b=%08x (VALUE)\n",
+ __func__, idx, type, param->params[idx].value.a,
+ param->params[idx].value.b);
+ break;
+
+ case TEEC_MEMREF_TEMP_INPUT:
+ case TEEC_MEMREF_TEMP_OUTPUT:
+ case TEEC_MEMREF_TEMP_INOUT:
+ dev_dbg(_DEV_TEE,
+ "> param[%d]:type=%d,buffer=%p,s=%zu (TMPREF)\n",
+ idx, type, op.params[idx].tmpref.buffer,
+ op.params[idx].tmpref.size);
+
+ param->params[idx].shm =
+ tee_context_create_tmpref_buffer(ctx,
+ op.params[idx].tmpref.size,
+ op.params[idx].tmpref.buffer,
+ type);
+ if (IS_ERR_OR_NULL(param->params[idx].shm))
+ goto out;
+
+ dev_dbg(_DEV_TEE, "< %d %p:%zd\n", idx,
+ (void *)param->params[idx].shm->paddr,
+ param->params[idx].shm->size_alloc);
+ break;
+
+ case TEEC_MEMREF_PARTIAL_INPUT:
+ case TEEC_MEMREF_PARTIAL_OUTPUT:
+ case TEEC_MEMREF_PARTIAL_INOUT:
+ case TEEC_MEMREF_WHOLE:
+ if (tee_copy_from_user(ctx, ¶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__);
+}
--- /dev/null
+/*
+ * Copyright (c) 2014, STMicroelectronics International N.V.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License Version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/dma-buf.h>
+#include <linux/hugetlb.h>
+
+#include <linux/sched.h>
+#include <linux/mm.h>
+
+#include "tee_core_priv.h"
+#include "tee_shm.h"
+
+#define INMSG() dev_dbg(_DEV(tee), "%s: >\n", __func__)
+#define OUTMSG(val) dev_dbg(_DEV(tee), "%s: < %ld\n", __func__, (long)val)
+#define OUTMSGX(val) dev_dbg(_DEV(tee), "%s: < %08x\n",\
+ __func__, (unsigned int)(long)val)
+
+/* TODO
+#if (sizeof(TEEC_SharedMemory) != sizeof(tee_shm))
+#error "sizeof(TEEC_SharedMemory) != sizeof(tee_shm))"
+#endif
+*/
+struct tee_shm_attach {
+ struct sg_table sgt;
+ enum dma_data_direction dir;
+ bool is_mapped;
+};
+
+struct tee_shm *tee_shm_alloc_from_rpc(struct tee *tee, size_t size)
+{
+ struct tee_shm *shm;
+
+ INMSG();
+
+ shm = tee_shm_alloc(tee, size, TEE_SHM_TEMP | TEE_SHM_FROM_RPC);
+ if (IS_ERR_OR_NULL(shm)) {
+ dev_err(_DEV(tee), "%s: buffer allocation failed (%ld)\n",
+ __func__, PTR_ERR(shm));
+ goto out;
+ }
+
+ mutex_lock(&tee->lock);
+ tee_inc_stats(&tee->stats[TEE_STATS_SHM_IDX]);
+ list_add_tail(&shm->entry, &tee->list_rpc_shm);
+ mutex_unlock(&tee->lock);
+ shm->ctx = NULL;
+
+out:
+ OUTMSGX(shm);
+ return shm;
+}
+
+void tee_shm_free_from_rpc(struct tee_shm *shm)
+{
+ if (shm == NULL)
+ return;
+
+ if (shm->ctx == NULL) {
+ mutex_lock(&shm->tee->lock);
+ tee_dec_stats(&shm->tee->stats[TEE_STATS_SHM_IDX]);
+ list_del(&shm->entry);
+ mutex_unlock(&shm->tee->lock);
+ }
+
+ tee_shm_free(shm);
+}
+
+struct tee_shm *tee_shm_alloc(struct tee *tee, size_t size, uint32_t flags)
+{
+ struct tee_shm *shm;
+ unsigned long pfn;
+ unsigned int nr_pages;
+ struct page *page;
+ int ret;
+
+ INMSG();
+
+ shm = tee->ops->alloc(tee, size, flags);
+ if (IS_ERR_OR_NULL(shm)) {
+ dev_err(_DEV(tee),
+ "%s: allocation failed (s=%d,flags=0x%08x) err=%ld\n",
+ __func__, (int)size, flags, PTR_ERR(shm));
+ goto exit;
+ }
+
+ shm->tee = tee;
+
+ dev_dbg(_DEV(tee), "%s: shm=%p, paddr=%p,s=%d/%d app=\"%s\" pid=%d\n",
+ __func__, shm, (void *)shm->paddr, (int)shm->size_req,
+ (int)shm->size_alloc, current->comm, current->pid);
+
+ pfn = shm->paddr >> PAGE_SHIFT;
+ page = pfn_to_page(pfn);
+ if (IS_ERR_OR_NULL(page)) {
+ dev_err(_DEV(tee), "%s: pfn_to_page(%lx) failed\n",
+ __func__, pfn);
+ tee->ops->free(shm);
+ return (struct tee_shm *)page;
+ }
+
+ /* Only one page of contiguous physical memory */
+ nr_pages = 1;
+
+ ret = sg_alloc_table_from_pages(&shm->sgt, &page,
+ nr_pages, 0, nr_pages * PAGE_SIZE, GFP_KERNEL);
+ if (IS_ERR_VALUE(ret)) {
+ dev_err(_DEV(tee), "%s: sg_alloc_table_from_pages() failed\n",
+ __func__);
+ tee->ops->free(shm);
+ shm = ERR_PTR(ret);
+ }
+exit:
+ OUTMSGX(shm);
+ return shm;
+}
+
+void tee_shm_free(struct tee_shm *shm)
+{
+ struct tee *tee;
+
+ if (IS_ERR_OR_NULL(shm))
+ return;
+ tee = shm->tee;
+ if (tee == NULL)
+ pr_warn("invalid call to tee_shm_free(%p): NULL tee\n", shm);
+ else if (shm->tee == NULL)
+ dev_warn(_DEV(tee), "tee_shm_free(%p): NULL tee\n", shm);
+ else {
+ sg_free_table(&shm->sgt);
+ shm->tee->ops->free(shm);
+ }
+}
+
+static int _tee_shm_attach_dma_buf(struct dma_buf *dmabuf,
+ struct device *dev,
+ struct dma_buf_attachment *attach)
+{
+ struct tee_shm_attach *tee_shm_attach;
+ struct tee_shm *shm;
+ struct tee *tee;
+
+ shm = dmabuf->priv;
+ tee = shm->tee;
+
+ INMSG();
+
+ tee_shm_attach = devm_kzalloc(_DEV(tee),
+ sizeof(*tee_shm_attach), GFP_KERNEL);
+ if (!tee_shm_attach) {
+ OUTMSG(-ENOMEM);
+ return -ENOMEM;
+ }
+
+ tee_shm_attach->dir = DMA_NONE;
+ attach->priv = tee_shm_attach;
+
+ OUTMSG(0);
+ return 0;
+}
+
+static void _tee_shm_detach_dma_buf(struct dma_buf *dmabuf,
+ struct dma_buf_attachment *attach)
+{
+ struct tee_shm_attach *tee_shm_attach = attach->priv;
+ struct sg_table *sgt;
+ struct tee_shm *shm;
+ struct tee *tee;
+
+ shm = dmabuf->priv;
+ tee = shm->tee;
+
+ INMSG();
+
+ if (!tee_shm_attach) {
+ OUTMSG(0);
+ return;
+ }
+
+ sgt = &tee_shm_attach->sgt;
+
+ if (tee_shm_attach->dir != DMA_NONE)
+ dma_unmap_sg(attach->dev, sgt->sgl, sgt->nents,
+ tee_shm_attach->dir);
+
+ sg_free_table(sgt);
+ devm_kfree(_DEV(tee), tee_shm_attach);
+ attach->priv = NULL;
+ OUTMSG(0);
+}
+
+static struct sg_table *_tee_shm_dma_buf_map_dma_buf(
+ struct dma_buf_attachment *attach, enum dma_data_direction dir)
+{
+ struct tee_shm_attach *tee_shm_attach = attach->priv;
+ struct tee_shm *tee_shm = attach->dmabuf->priv;
+ struct sg_table *sgt = NULL;
+ struct scatterlist *rd, *wr;
+ unsigned int i;
+ int nents, ret;
+ struct tee *tee;
+
+ tee = tee_shm->tee;
+
+ INMSG();
+
+ /* just return current sgt if already requested. */
+ if (tee_shm_attach->dir == dir && tee_shm_attach->is_mapped) {
+ OUTMSGX(&tee_shm_attach->sgt);
+ return &tee_shm_attach->sgt;
+ }
+
+ sgt = &tee_shm_attach->sgt;
+
+ ret = sg_alloc_table(sgt, tee_shm->sgt.orig_nents, GFP_KERNEL);
+ if (ret) {
+ dev_err(_DEV(tee), "failed to alloc sgt.\n");
+ return ERR_PTR(-ENOMEM);
+ }
+
+ rd = tee_shm->sgt.sgl;
+ wr = sgt->sgl;
+ for (i = 0; i < sgt->orig_nents; ++i) {
+ sg_set_page(wr, sg_page(rd), rd->length, rd->offset);
+ rd = sg_next(rd);
+ wr = sg_next(wr);
+ }
+
+ if (dir != DMA_NONE) {
+ nents = dma_map_sg(attach->dev, sgt->sgl, sgt->orig_nents, dir);
+ if (!nents) {
+ dev_err(_DEV(tee), "failed to map sgl with iommu.\n");
+ sg_free_table(sgt);
+ sgt = ERR_PTR(-EIO);
+ goto err_unlock;
+ }
+ }
+
+ tee_shm_attach->is_mapped = true;
+ tee_shm_attach->dir = dir;
+ attach->priv = tee_shm_attach;
+
+err_unlock:
+ OUTMSGX(sgt);
+ return sgt;
+}
+
+static void _tee_shm_dma_buf_unmap_dma_buf(struct dma_buf_attachment *attach,
+ struct sg_table *table,
+ enum dma_data_direction dir)
+{
+ return;
+}
+
+static void _tee_shm_dma_buf_release(struct dma_buf *dmabuf)
+{
+ struct tee_shm *shm = dmabuf->priv;
+ struct tee_context *ctx;
+ struct tee *tee;
+
+ tee = shm->ctx->tee;
+
+ INMSG();
+
+ ctx = shm->ctx;
+ dev_dbg(_DEV(ctx->tee), "%s: shm=%p, paddr=%p,s=%d/%d app=\"%s\" pid=%d\n",
+ __func__, shm, (void *)shm->paddr, (int)shm->size_req,
+ (int)shm->size_alloc, current->comm, current->pid);
+
+ tee_shm_free_io(shm);
+
+ OUTMSG(0);
+}
+
+static int _tee_shm_dma_buf_mmap(struct dma_buf *dmabuf,
+ struct vm_area_struct *vma)
+{
+ struct tee_shm *shm = dmabuf->priv;
+ size_t size = vma->vm_end - vma->vm_start;
+ struct tee *tee;
+ int ret;
+ pgprot_t prot;
+ unsigned long pfn;
+
+ tee = shm->ctx->tee;
+
+ pfn = shm->paddr >> PAGE_SHIFT;
+
+ INMSG();
+
+ if (shm->flags & TEE_SHM_CACHED)
+ prot = vma->vm_page_prot;
+ else
+ prot = pgprot_noncached(vma->vm_page_prot);
+
+ ret =
+ remap_pfn_range(vma, vma->vm_start, pfn, size, prot);
+ if (!ret)
+ vma->vm_private_data = (void *)shm;
+
+ dev_dbg(_DEV(shm->ctx->tee), "%s: map the shm (p@=%p,s=%dKiB) => %x\n",
+ __func__, (void *)shm->paddr, (int)size / 1024,
+ (unsigned int)vma->vm_start);
+
+ OUTMSG(ret);
+ return ret;
+}
+
+static void *_tee_shm_dma_buf_kmap_atomic(struct dma_buf *dmabuf,
+ unsigned long pgnum)
+{
+ return NULL;
+}
+
+static void *_tee_shm_dma_buf_kmap(struct dma_buf *db, unsigned long pgnum)
+{
+ struct tee_shm *shm = db->priv;
+
+ dev_dbg(_DEV(shm->ctx->tee), "%s: kmap the shm (p@=%p, v@=%p, s=%zdKiB)\n",
+ __func__, (void *)shm->paddr, (void *)shm->kaddr,
+ shm->size_alloc / 1024);
+ /*
+ * A this stage, a shm allocated by the tee
+ * must be have a kernel address
+ */
+ return shm->kaddr;
+}
+
+static void _tee_shm_dma_buf_kunmap(
+ struct dma_buf *db, unsigned long pfn, void *kaddr)
+{
+ /* unmap is done at the de init of the shm pool */
+}
+
+struct dma_buf_ops _tee_shm_dma_buf_ops = {
+ .attach = _tee_shm_attach_dma_buf,
+ .detach = _tee_shm_detach_dma_buf,
+ .map_dma_buf = _tee_shm_dma_buf_map_dma_buf,
+ .unmap_dma_buf = _tee_shm_dma_buf_unmap_dma_buf,
+ .release = _tee_shm_dma_buf_release,
+ .kmap_atomic = _tee_shm_dma_buf_kmap_atomic,
+ .kmap = _tee_shm_dma_buf_kmap,
+ .kunmap = _tee_shm_dma_buf_kunmap,
+ .mmap = _tee_shm_dma_buf_mmap,
+};
+
+/******************************************************************************/
+
+static int export_buf(struct tee *tee, struct tee_shm *shm, int *export)
+{
+ struct dma_buf *dmabuf;
+ int ret = 0;
+ /* Temporary fix to support both older and newer kernel versions. */
+#if defined(DEFINE_DMA_BUF_EXPORT_INFO)
+ DEFINE_DMA_BUF_EXPORT_INFO(exp_info);
+
+ exp_info.priv = shm;
+ exp_info.ops = &_tee_shm_dma_buf_ops;
+ exp_info.size = shm->size_alloc;
+ exp_info.flags = O_RDWR;
+
+ dmabuf = dma_buf_export(&exp_info);
+#else
+ dmabuf = dma_buf_export(shm, &_tee_shm_dma_buf_ops, shm->size_alloc,
+ O_RDWR, 0);
+#endif
+ if (IS_ERR_OR_NULL(dmabuf)) {
+ dev_err(_DEV(tee), "%s: dmabuf: couldn't export buffer (%ld)\n",
+ __func__, PTR_ERR(dmabuf));
+ ret = -EINVAL;
+ goto out;
+ }
+
+ *export = dma_buf_fd(dmabuf, O_CLOEXEC);
+out:
+ OUTMSG(ret);
+ return ret;
+}
+
+int tee_shm_alloc_io(struct tee_context *ctx, struct tee_shm_io *shm_io)
+{
+ struct tee_shm *shm;
+ struct tee *tee = ctx->tee;
+ int ret;
+
+ INMSG();
+
+ if (ctx->usr_client)
+ shm_io->fd_shm = 0;
+ else
+ shm_io->ptr = NULL;
+
+ shm = tee_shm_alloc(tee, shm_io->size, shm_io->flags);
+ if (IS_ERR_OR_NULL(shm)) {
+ dev_err(_DEV(tee), "%s: buffer allocation failed (%ld)\n",
+ __func__, PTR_ERR(shm));
+ return PTR_ERR(shm);
+ }
+
+ if (ctx->usr_client) {
+ ret = export_buf(tee, shm, &shm_io->fd_shm);
+ if (ret) {
+ tee_shm_free(shm);
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ shm->flags |= TEEC_MEM_DMABUF;
+ } else
+ shm_io->ptr = shm;
+
+ shm->ctx = ctx;
+ shm->dev = get_device(_DEV(tee));
+ ret = tee_get(tee);
+ BUG_ON(ret); /* tee_core_get must not issue */
+ tee_context_get(ctx);
+
+ mutex_lock(&tee->lock);
+ tee_inc_stats(&tee->stats[TEE_STATS_SHM_IDX]);
+ list_add_tail(&shm->entry, &ctx->list_shm);
+ mutex_unlock(&tee->lock);
+out:
+ OUTMSG(ret);
+ return ret;
+}
+
+void tee_shm_free_io(struct tee_shm *shm)
+{
+ struct tee_context *ctx = shm->ctx;
+ struct tee *tee = ctx->tee;
+ struct device *dev = shm->dev;
+
+ mutex_lock(&ctx->tee->lock);
+ tee_dec_stats(&tee->stats[TEE_STATS_SHM_IDX]);
+ list_del(&shm->entry);
+ mutex_unlock(&ctx->tee->lock);
+
+ tee_shm_free(shm);
+ tee_put(ctx->tee);
+ tee_context_put(ctx);
+ if (dev)
+ put_device(dev);
+}
+
+/* Buffer allocated by rpc from fw and to be accessed by the user
+ * Not need to be registered as it is not allocated by the user */
+int tee_shm_fd_for_rpc(struct tee_context *ctx, struct tee_shm_io *shm_io)
+{
+ struct tee_shm *shm = NULL;
+ struct tee *tee = ctx->tee;
+ int ret;
+ struct list_head *pshm;
+
+ INMSG();
+
+ shm_io->fd_shm = 0;
+
+ if (!list_empty(&tee->list_rpc_shm)) {
+ list_for_each(pshm, &tee->list_rpc_shm) {
+ shm = list_entry(pshm, struct tee_shm, entry);
+ if ((void *)shm->paddr == shm_io->buffer)
+ goto found;
+ }
+ }
+
+ dev_err(_DEV(tee), "Can't find shm for %p\n", (void *)shm_io->buffer);
+ ret = -ENOMEM;
+ goto out;
+
+found:
+ ret = export_buf(tee, shm, &shm_io->fd_shm);
+ if (ret) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ shm->ctx = ctx;
+ mutex_lock(&tee->lock);
+ list_move(&shm->entry, &ctx->list_shm);
+ mutex_unlock(&tee->lock);
+
+ shm->dev = get_device(_DEV(tee));
+ ret = tee_get(tee);
+ BUG_ON(ret);
+ tee_context_get(ctx);
+
+ BUG_ON(!tee->ops->shm_inc_ref(shm));
+out:
+ OUTMSG(ret);
+ return ret;
+}
+
+/******************************************************************************/
+
+static int tee_shm_db_get(struct tee *tee, struct tee_shm *shm, int fd,
+ unsigned int flags, size_t size, int offset)
+{
+ struct tee_shm_dma_buf *sdb;
+ struct dma_buf *dma_buf;
+ int ret = 0;
+
+ dev_dbg(_DEV(tee), "%s: > fd=%d flags=%08x\n", __func__, fd, flags);
+
+ dma_buf = dma_buf_get(fd);
+ if (IS_ERR(dma_buf)) {
+ ret = PTR_ERR(dma_buf);
+ goto exit;
+ }
+
+ sdb = kzalloc(sizeof(*sdb), GFP_KERNEL);
+ if (IS_ERR_OR_NULL(sdb)) {
+ dev_err(_DEV(tee), "can't alloc tee_shm_dma_buf\n");
+ ret = PTR_ERR(sdb);
+ goto buf_put;
+ }
+ shm->sdb = sdb;
+
+ if (dma_buf->size < size + offset) {
+ dev_err(_DEV(tee), "dma_buf too small %zd < %zd + %d\n",
+ dma_buf->size, size, offset);
+ ret = -EINVAL;
+ goto free_sdb;
+ }
+
+ sdb->attach = dma_buf_attach(dma_buf, _DEV(tee));
+ if (IS_ERR_OR_NULL(sdb->attach)) {
+ ret = PTR_ERR(sdb->attach);
+ goto free_sdb;
+ }
+
+ sdb->sgt = dma_buf_map_attachment(sdb->attach, DMA_NONE);
+ if (IS_ERR_OR_NULL(sdb->sgt)) {
+ ret = PTR_ERR(sdb->sgt);
+ goto buf_detach;
+ }
+
+ if (sg_nents(sdb->sgt->sgl) != 1) {
+ ret = -EINVAL;
+ goto buf_unmap;
+ }
+
+ shm->paddr = sg_phys(sdb->sgt->sgl) + offset;
+ if (dma_buf->ops->attach == _tee_shm_attach_dma_buf)
+ sdb->tee_allocated = true;
+ else
+ sdb->tee_allocated = false;
+
+ shm->flags |= TEEC_MEM_DMABUF;
+
+ dev_dbg(_DEV(tee), "fd=%d @p=%p is_tee=%d db=%p\n", fd,
+ (void *)shm->paddr, sdb->tee_allocated, dma_buf);
+ goto exit;
+
+buf_unmap:
+ dma_buf_unmap_attachment(sdb->attach, sdb->sgt, DMA_NONE);
+buf_detach:
+ dma_buf_detach(dma_buf, sdb->attach);
+free_sdb:
+ kfree(sdb);
+buf_put:
+ dma_buf_put(dma_buf);
+exit:
+ OUTMSG(ret);
+ return ret;
+}
+
+#ifdef VA_GET_ENABLED
+static unsigned int tee_shm_get_phy_from_kla(
+ struct mm_struct *mm, unsigned int kla)
+{
+ pgd_t *pgd;
+ pud_t *pud;
+ pmd_t *pmd;
+ pte_t *ptep, pte;
+ unsigned int pa = 0;
+
+ /* stolen from kernel3.10:mm/memory.c:__follow_pte */
+
+ pgd = pgd_offset(mm, kla);
+ if (pgd_none(*pgd) || pgd_bad(*pgd))
+ return 0;
+
+ pud = pud_offset(pgd, kla);
+ if (pud_none(*pud) || pud_bad(*pud))
+ return 0;
+
+ pmd = pmd_offset(pud, kla);
+ VM_BUG_ON(pmd_trans_huge(*pmd));
+ if (pmd_none(*pmd) || pmd_bad(*pmd))
+ return 0;
+
+ /* We cannot handle huge page PFN maps.
+ * Luckily they don't exist. */
+ if (pmd_huge(*pmd))
+ return 0;
+
+ ptep = pte_offset_map(pmd, kla);
+
+ if (!ptep)
+ return 0;
+
+ pte = *ptep;
+
+ if (pte_present(pte))
+ pa = __pa(page_address(pte_page(pte)));
+
+ if (!pa)
+ return 0;
+
+ return pa;
+
+}
+
+static int tee_shm_va_get(struct tee_context *ctx, struct tee_shm *shm,
+ void *buffer, unsigned int flags, size_t size, int offset)
+{
+ int ret = 0;
+ struct mm_struct *mm = current->mm;
+ unsigned long va = (unsigned long)buffer;
+ unsigned int virt_base = (va / PAGE_SIZE) * PAGE_SIZE;
+ unsigned int offset_in_page = va - virt_base;
+ unsigned int offset_total = offset_in_page + offset;
+ struct vm_area_struct *vma;
+ struct tee *tee = ctx->tee;
+
+ dev_dbg(_DEV(tee), "%s: > %p\n", __func__, buffer);
+ /* if the caller is the kernel api, active_mm is mm */
+ if (!mm)
+ mm = current->active_mm;
+
+ BUG_ON(!mm);
+
+ vma = find_vma(mm, virt_base);
+
+ if (vma) {
+ unsigned long pfn;
+ /* It's a VMA => consider it a a user address */
+
+ if (follow_pfn(vma, virt_base, &pfn)) {
+ dev_err(_DEV(tee), "%s can't get pfn for %p\n",
+ __func__, buffer);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ shm->paddr = PFN_PHYS(pfn) + offset_total;
+
+ if (vma->vm_end - vma->vm_start - offset_total < size) {
+ dev_err(_DEV(tee), "%s %p:%x not big enough: %lx - %d < %x\n",
+ __func__, buffer, shm->paddr,
+ vma->vm_end - vma->vm_start,
+ offset_total, size);
+ shm->paddr = 0;
+ ret = -EINVAL;
+ goto out;
+ }
+ } else if (!ctx->usr_client) {
+ /* It's not a VMA => consider it as a kernel address
+ * And look if it's an internal known phys addr
+ * Note: virt_to_phys is not usable since it can be a direct
+ * map or a vremap address
+ */
+ unsigned int phys_base;
+ int nb_page = (PAGE_SIZE - 1 + size + offset_total) / PAGE_SIZE;
+ int i;
+
+ spin_lock(&mm->page_table_lock);
+ phys_base = tee_shm_get_phy_from_kla(mm, virt_base);
+
+ if (!phys_base) {
+ spin_unlock(&mm->page_table_lock);
+ dev_err(_DEV(tee), "%s can't get physical address for %p\n",
+ __func__, buffer);
+ goto err;
+ }
+
+ /* Check continuity on size */
+ for (i = 1; i < nb_page; i++) {
+ unsigned int pa = tee_shm_get_phy_from_kla(mm,
+ virt_base + i*PAGE_SIZE);
+ if (pa != phys_base + i*PAGE_SIZE) {
+ spin_unlock(&mm->page_table_lock);
+ dev_err(_DEV(tee), "%s %p:%x not big enough: %lx - %d < %x\n",
+ __func__, buffer, phys_base,
+ i*PAGE_SIZE,
+ offset_total, size);
+ goto err;
+ }
+ }
+ spin_unlock(&mm->page_table_lock);
+
+ shm->paddr = phys_base + offset_total;
+ goto out;
+err:
+ ret = -EINVAL;
+ }
+
+out:
+ dev_dbg(_DEV(tee), "%s: < %d shm=%p vaddr=%p paddr=%x\n",
+ __func__, ret, (void *)shm, buffer, shm->paddr);
+ return ret;
+}
+#endif
+
+struct tee_shm *tee_shm_get(struct tee_context *ctx, TEEC_SharedMemory *c_shm,
+ size_t size, int offset)
+{
+ struct tee_shm *shm;
+ struct tee *tee = ctx->tee;
+ int ret;
+
+ dev_dbg(_DEV(tee), "%s: > fd=%d flags=%08x\n",
+ __func__, c_shm->d.fd, c_shm->flags);
+
+ shm = kzalloc(sizeof(*shm), GFP_KERNEL);
+ if (IS_ERR_OR_NULL(shm)) {
+ dev_err(_DEV(tee), "can't alloc tee_shm\n");
+ return ERR_PTR(-ENOMEM);
+ }
+
+ shm->ctx = ctx;
+ shm->tee = tee;
+ shm->dev = _DEV(tee);
+ shm->flags = c_shm->flags | TEE_SHM_MEMREF;
+ shm->size_req = size;
+ shm->size_alloc = 0;
+
+ if (c_shm->flags & TEEC_MEM_KAPI) {
+ struct tee_shm *kc_shm = (struct tee_shm *)c_shm->d.ptr;
+
+ if (!kc_shm) {
+ dev_err(_DEV(tee), "kapi fd null\n");
+ ret = -EINVAL;
+ goto err;
+ }
+ shm->paddr = kc_shm->paddr;
+
+ if (kc_shm->size_alloc < size + offset) {
+ dev_err(_DEV(tee), "kapi buff too small %zd < %zd + %d\n",
+ kc_shm->size_alloc, size, offset);
+ ret = -EINVAL;
+ goto err;
+ }
+
+ dev_dbg(_DEV(tee), "fd=%d @p=%p\n",
+ c_shm->d.fd, (void *)shm->paddr);
+ } else if (c_shm->d.fd) {
+ ret = tee_shm_db_get(tee, shm,
+ c_shm->d.fd, c_shm->flags, size, offset);
+ if (ret)
+ goto err;
+ } else if (!c_shm->buffer) {
+ dev_dbg(_DEV(tee), "null buffer, pass 'as is'\n");
+ } else {
+#ifdef VA_GET_ENABLED
+ ret = tee_shm_va_get(ctx, shm,
+ c_shm->buffer, c_shm->flags, size, offset);
+ if (ret)
+ goto err;
+#else
+ ret = -EINVAL;
+ goto err;
+#endif
+ }
+
+ OUTMSGX(shm);
+ return shm;
+
+err:
+ kfree(shm);
+ OUTMSGX(ERR_PTR(ret));
+ return ERR_PTR(ret);
+}
+
+void tee_shm_put(struct tee_context *ctx, struct tee_shm *shm)
+{
+ struct tee *tee = ctx->tee;
+
+ dev_dbg(_DEV(tee), "%s: > shm=%p flags=%08x paddr=%p\n",
+ __func__, (void *)shm, shm->flags, (void *)shm->paddr);
+
+ BUG_ON(!shm);
+ BUG_ON(!(shm->flags & TEE_SHM_MEMREF));
+
+ if (shm->flags & TEEC_MEM_DMABUF) {
+ struct tee_shm_dma_buf *sdb;
+ struct dma_buf *dma_buf;
+
+ sdb = shm->sdb;
+ dma_buf = sdb->attach->dmabuf;
+
+ dev_dbg(_DEV(tee), "%s: db=%p\n", __func__, (void *)dma_buf);
+
+ dma_buf_unmap_attachment(sdb->attach, sdb->sgt, DMA_NONE);
+ dma_buf_detach(dma_buf, sdb->attach);
+ dma_buf_put(dma_buf);
+
+ kfree(sdb);
+ sdb = 0;
+ }
+
+ kfree(shm);
+ OUTMSG(0);
+}
+
--- /dev/null
+/*
+ * Copyright (c) 2014, STMicroelectronics International N.V.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License Version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#ifndef __TEE_SHM_H__
+#define __TEE_SHM_H__
+
+#include <linux/tee_client_api.h>
+struct tee_context;
+struct tee_shm;
+struct tee_shm_io;
+struct tee;
+
+int tee_shm_alloc_io(struct tee_context *ctx, struct tee_shm_io *shm_io);
+void tee_shm_free_io(struct tee_shm *shm);
+
+int tee_shm_fd_for_rpc(struct tee_context *ctx, struct tee_shm_io *shm_io);
+
+struct tee_shm *tee_shm_alloc(struct tee *tee, size_t size, uint32_t flags);
+void tee_shm_free(struct tee_shm *shm);
+
+struct tee_shm *tee_shm_get(struct tee_context *ctx, TEEC_SharedMemory *c_shm,
+ size_t size, int offset);
+void tee_shm_put(struct tee_context *ctx, struct tee_shm *shm);
+
+#endif /* __TEE_SHM_H__ */
--- /dev/null
+/*
+ * Copyright (c) 2014, STMicroelectronics International N.V.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License Version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/mutex.h>
+#include <linux/miscdevice.h>
+#include <linux/uaccess.h>
+#include <linux/anon_inodes.h>
+#include <linux/semaphore.h>
+#include <linux/mm.h>
+#include <linux/sched.h>
+#include <linux/device.h>
+
+#include "tee_shm.h"
+#include "tee_core.h"
+#include "tee_supp_com.h"
+
+#define TEE_RPC_BUFFER 0x00000001
+#define TEE_RPC_VALUE 0x00000002
+
+enum teec_rpc_result tee_supp_cmd(struct tee *tee,
+ uint32_t id, void *data, size_t datalen)
+{
+ struct tee_rpc *rpc = tee->rpc;
+ enum teec_rpc_result res = TEEC_RPC_FAIL;
+ size_t size;
+ struct task_struct *task = current;
+
+ dev_dbg(tee->dev, "> tgid:[%d] id:[0x%08x]\n", task->tgid, id);
+
+ if (atomic_read(&rpc->used) == 0) {
+ dev_err(tee->dev, "%s: ERROR Supplicant application NOT ready\n"
+ , __func__);
+ goto out;
+ }
+
+ switch (id) {
+ case TEE_RPC_ICMD_ALLOCATE:
+ {
+ struct tee_rpc_alloc *alloc;
+ struct tee_shm *shmint;
+
+ alloc = (struct tee_rpc_alloc *)data;
+ size = alloc->size;
+ memset(alloc, 0, sizeof(struct tee_rpc_alloc));
+ shmint = tee_shm_alloc_from_rpc(tee, size);
+ if (IS_ERR_OR_NULL(shmint))
+ break;
+
+ alloc->size = size;
+ alloc->data = (void *)shmint->paddr;
+ alloc->shm = shmint;
+ res = TEEC_RPC_OK;
+
+ break;
+ }
+ case TEE_RPC_ICMD_FREE:
+ {
+ struct tee_rpc_free *free;
+
+ free = (struct tee_rpc_free *)data;
+ tee_shm_free_from_rpc(free->shm);
+ res = TEEC_RPC_OK;
+ break;
+ }
+ case TEE_RPC_ICMD_INVOKE:
+ {
+ if (sizeof(rpc->commToUser) < datalen)
+ break;
+
+ mutex_lock(&rpc->outsync);
+
+ memcpy(&rpc->commToUser, data, datalen);
+
+ mutex_unlock(&rpc->outsync);
+
+ dev_dbg(tee->dev,
+ "Supplicant Cmd: %x. Give hand to supplicant\n",
+ rpc->commToUser.cmd);
+
+ up(&rpc->datatouser);
+
+ down(&rpc->datafromuser);
+
+ dev_dbg(tee->dev,
+ "Supplicant Cmd: %x. Give hand to fw\n",
+ rpc->commToUser.cmd);
+
+ mutex_lock(&rpc->insync);
+
+ memcpy(data, &rpc->commFromUser, datalen);
+
+ mutex_unlock(&rpc->insync);
+
+ res = TEEC_RPC_OK;
+
+ break;
+ }
+ default:
+ /* not supported */
+ break;
+ }
+
+out:
+ dev_dbg(tee->dev, "< res: [%d]\n", res);
+
+ return res;
+}
+EXPORT_SYMBOL(tee_supp_cmd);
+
+ssize_t tee_supp_read(struct file *filp, char __user *buffer,
+ size_t length, loff_t *offset)
+{
+ struct tee_context *ctx = (struct tee_context *)(filp->private_data);
+ struct tee *tee;
+ struct tee_rpc *rpc;
+ struct task_struct *task = current;
+ int ret;
+
+ BUG_ON(!ctx);
+ tee = ctx->tee;
+ BUG_ON(!tee);
+ BUG_ON(!tee->dev);
+ BUG_ON(!tee->rpc);
+
+ dev_dbg(tee->dev, "> ctx %p\n", ctx);
+
+ rpc = tee->rpc;
+
+ if (atomic_read(&rpc->used) == 0) {
+ dev_err(tee->dev, "%s: ERROR Supplicant application NOT ready\n"
+ , __func__);
+ ret = -EPERM;
+ goto out;
+ }
+
+ if (down_interruptible(&rpc->datatouser))
+ return -ERESTARTSYS;
+
+ dev_dbg(tee->dev, "> tgid:[%d]\n", task->tgid);
+
+ mutex_lock(&rpc->outsync);
+
+ ret =
+ sizeof(rpc->commToUser) - sizeof(rpc->commToUser.cmds) +
+ sizeof(rpc->commToUser.cmds[0]) * rpc->commToUser.nbr_bf;
+ if (length < ret) {
+ ret = -EINVAL;
+ } else {
+ if (copy_to_user(buffer, &rpc->commToUser, ret)) {
+ dev_err(tee->dev,
+ "[%s] error, copy_to_user failed!\n", __func__);
+ ret = -EINVAL;
+ }
+ }
+
+ mutex_unlock(&rpc->outsync);
+
+out:
+ dev_dbg(tee->dev, "< [%d]\n", ret);
+ return ret;
+}
+
+ssize_t tee_supp_write(struct file *filp, const char __user *buffer,
+ size_t length, loff_t *offset)
+{
+ struct tee_context *ctx = (struct tee_context *)(filp->private_data);
+ struct tee *tee;
+ struct tee_rpc *rpc;
+ struct task_struct *task = current;
+ int ret = 0;
+
+ BUG_ON(!ctx);
+ BUG_ON(!ctx->tee);
+ BUG_ON(!ctx->tee->rpc);
+ tee = ctx->tee;
+ rpc = tee->rpc;
+ dev_dbg(tee->dev, "> tgid:[%d]\n", task->tgid);
+
+ if (atomic_read(&rpc->used) == 0) {
+ dev_err(tee->dev, "%s: ERROR Supplicant application NOT ready\n"
+ , __func__);
+ goto out;
+ }
+
+ if (length > 0 && length < sizeof(rpc->commFromUser)) {
+ uint32_t i;
+
+ mutex_lock(&rpc->insync);
+
+ if (copy_from_user(&rpc->commFromUser, buffer, length)) {
+ dev_err(tee->dev,
+ "%s: ERROR, tee_session copy_from_user failed\n",
+ __func__);
+ mutex_unlock(&rpc->insync);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ /* Translate virtual address of caller into physical address */
+ for (i = 0; i < rpc->commFromUser.nbr_bf; i++) {
+ if (rpc->commFromUser.cmds[i].type == TEE_RPC_BUFFER
+ && rpc->commFromUser.cmds[i].buffer) {
+ struct vm_area_struct *vma =
+ find_vma(current->mm,
+ (unsigned long)rpc->
+ commFromUser.cmds[i].buffer);
+ if (vma != NULL) {
+ struct tee_shm *shm =
+ vma->vm_private_data;
+ BUG_ON(!shm);
+ dev_dbg(tee->dev,
+ "%d gid2pa(0x%p => %x)\n", i,
+ rpc->commFromUser.cmds[i].
+ buffer,
+ (unsigned int)shm->paddr);
+ rpc->commFromUser.cmds[i].buffer =
+ (void *)shm->paddr;
+ } else
+ dev_dbg(tee->dev,
+ " gid2pa(0x%p => NULL\n)",
+ rpc->commFromUser.cmds[i].
+ buffer);
+ }
+ }
+
+ mutex_unlock(&rpc->insync);
+ up(&rpc->datafromuser);
+ ret = length;
+ }
+
+out:
+ dev_dbg(tee->dev, "< [%d]\n", ret);
+ return ret;
+}
+
+int tee_supp_init(struct tee *tee)
+{
+ struct tee_rpc *rpc =
+ devm_kzalloc(tee->dev, sizeof(struct tee_rpc), GFP_KERNEL);
+ if (!rpc) {
+ dev_err(tee->dev, "%s: can't allocate tee_rpc structure\n",
+ __func__);
+ return -ENOMEM;
+ }
+
+ rpc->datafromuser = (struct semaphore)
+ __SEMAPHORE_INITIALIZER(rpc->datafromuser, 0);
+ rpc->datatouser = (struct semaphore)
+ __SEMAPHORE_INITIALIZER(rpc->datatouser, 0);
+ mutex_init(&rpc->outsync);
+ mutex_init(&rpc->insync);
+ atomic_set(&rpc->used, 0);
+ tee->rpc = rpc;
+ return 0;
+}
+
+void tee_supp_deinit(struct tee *tee)
+{
+ devm_kfree(tee->dev, tee->rpc);
+ tee->rpc = NULL;
+}
--- /dev/null
+/*
+ * Copyright (c) 2014, STMicroelectronics International N.V.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License Version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#ifndef TEE_SUPP_COMM_H
+#define TEE_SUPP_COMM_H
+
+#define TEE_RPC_ICMD_ALLOCATE 0x1001
+#define TEE_RPC_ICMD_FREE 0x1002
+#define TEE_RPC_ICMD_INVOKE 0x1003
+
+#define TEE_RPC_NBR_BUFF 1
+#define TEE_RPC_DATA_SIZE 64
+#define TEE_RPC_BUFFER_NUMBER 5
+
+#define TEE_RPC_STATE_IDLE 0x00
+#define TEE_RPC_STATE_ACTIVE 0x01
+
+/* Keep aligned with optee_client (user space) */
+#define TEE_RPC_BUFFER 0x00000001
+#define TEE_RPC_VALUE 0x00000002
+#define TEE_RPC_LOAD_TA 0x10000001
+/*
+ * Handled within the driver only
+ * Keep aligned with optee_os (secure space)
+ */
+#define TEE_RPC_MUTEX_WAIT 0x20000000
+#define TEE_RPC_WAIT_QUEUE_SLEEP 0x20000001
+#define TEE_RPC_WAIT_QUEUE_WAKEUP 0x20000002
+#define TEE_RPC_WAIT 0x30000000
+
+/* Parameters for TEE_RPC_WAIT_MUTEX above */
+#define TEE_MUTEX_WAIT_SLEEP 0
+#define TEE_MUTEX_WAIT_WAKEUP 1
+#define TEE_MUTEX_WAIT_DELETE 2
+
+#include <linux/semaphore.h>
+
+/**
+ * struct tee_rpc_bf - Contains definition of the tee com buffer
+ * @state: Buffer state
+ * @data: Command data
+ */
+struct tee_rpc_bf {
+ uint32_t state;
+ uint8_t data[TEE_RPC_DATA_SIZE];
+};
+
+struct tee_rpc_alloc {
+ uint32_t size; /* size of block */
+ void *data; /* pointer to data */
+ void *shm; /* pointer to an opaque data, being shm structure */
+};
+
+struct tee_rpc_free {
+ void *shm; /* pointer to an opaque data, being shm structure */
+};
+
+struct tee_rpc_cmd {
+ void *buffer;
+ uint32_t size;
+ uint32_t type;
+ int fd;
+};
+
+struct tee_rpc_invoke {
+ uint32_t cmd;
+ uint32_t res;
+ uint32_t nbr_bf;
+ struct tee_rpc_cmd cmds[TEE_RPC_BUFFER_NUMBER];
+};
+
+struct tee_rpc {
+ struct tee_rpc_invoke commToUser;
+ struct tee_rpc_invoke commFromUser;
+ struct semaphore datatouser;
+ struct semaphore datafromuser;
+ struct mutex outsync; /* Out sync mutex */
+ struct mutex insync; /* In sync mutex */
+ struct mutex reqsync; /* Request sync mutex */
+ atomic_t used;
+};
+
+enum teec_rpc_result {
+ TEEC_RPC_OK,
+ TEEC_RPC_FAIL
+};
+
+struct tee;
+
+int tee_supp_init(struct tee *tee);
+void tee_supp_deinit(struct tee *tee);
+
+enum teec_rpc_result tee_supp_cmd(struct tee *tee,
+ uint32_t id, void *data, size_t datalen);
+
+ssize_t tee_supp_read(struct file *filp, char __user *buffer,
+ size_t length, loff_t *offset);
+
+ssize_t tee_supp_write(struct file *filp, const char __user *buffer,
+ size_t length, loff_t *offset);
+
+#endif
--- /dev/null
+/*
+ * Copyright (c) 2014, STMicroelectronics International N.V.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License Version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/stat.h>
+#include <linux/atomic.h>
+#include <asm/page.h>
+
+#include "tee_core_priv.h"
+
+static ssize_t dump_show(struct device *device,
+ struct device_attribute *attr, char *buf)
+{
+ struct tee *tee = dev_get_drvdata(device);
+ int len;
+ char *tmp_buf;
+
+ tmp_buf = kzalloc(PAGE_SIZE, GFP_KERNEL);
+ if (!tmp_buf) {
+ printk(KERN_ALERT "%s : Unable to get buf memory\n", __func__);
+ return -ENOMEM;
+ }
+
+ len = tee_context_dump(tee, tmp_buf, PAGE_SIZE - 128);
+
+ if (len > 0)
+ len = snprintf(buf, PAGE_SIZE, "%s", tmp_buf);
+ kfree(tmp_buf);
+
+ return len;
+}
+
+static ssize_t stat_show(struct device *device,
+ struct device_attribute *attr, char *buf)
+{
+ struct tee *tee = dev_get_drvdata(device);
+
+ return snprintf(buf, PAGE_SIZE, "%d/%d %d/%d %d/%d %d/%d\n",
+ atomic_read(&tee->refcount),
+ tee->max_refcount,
+ tee->stats[TEE_STATS_CONTEXT_IDX].count,
+ tee->stats[TEE_STATS_CONTEXT_IDX].max,
+ tee->stats[TEE_STATS_SESSION_IDX].count,
+ tee->stats[TEE_STATS_SESSION_IDX].max,
+ tee->stats[TEE_STATS_SHM_IDX].count,
+ tee->stats[TEE_STATS_SHM_IDX].max);
+}
+
+static ssize_t info_show(struct device *device,
+ struct device_attribute *attr, char *buf)
+{
+ struct tee *tee = dev_get_drvdata(device);
+
+ return snprintf(buf, PAGE_SIZE, "%s iminor=%d dev=\"%s\" state=%d\n",
+ dev_name(tee->dev), tee->miscdev.minor,
+ dev_name(tee->miscdev.this_device), tee->state);
+}
+
+static ssize_t name_show(struct device *device,
+ struct device_attribute *attr, char *buf)
+{
+ struct tee *tee = dev_get_drvdata(device);
+
+ return snprintf(buf, PAGE_SIZE, "%s\n", tee->name);
+}
+
+static ssize_t type_show(struct device *device,
+ struct device_attribute *attr, char *buf)
+{
+ struct tee *tee = dev_get_drvdata(device);
+
+ return snprintf(buf, PAGE_SIZE, "%s\n", tee->ops->type);
+}
+
+static ssize_t refcount_show(struct device *device,
+ struct device_attribute *attr, char *buf)
+{
+ struct tee *tee = dev_get_drvdata(device);
+
+ return snprintf(buf, PAGE_SIZE, "%d\n", atomic_read(&tee->refcount));
+}
+
+static ssize_t conf_show(struct device *device,
+ struct device_attribute *attr, char *buf)
+{
+ struct tee *tee = dev_get_drvdata(device);
+
+ return snprintf(buf, PAGE_SIZE, "0x%08x\n", tee->conf);
+}
+
+static ssize_t test_show(struct device *device,
+ struct device_attribute *attr, char *buf)
+{
+ struct tee *tee = dev_get_drvdata(device);
+
+ return snprintf(buf, PAGE_SIZE, "%08X\n", tee->test);
+}
+
+static ssize_t test_store(struct device *device,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ struct tee *tee = dev_get_drvdata(device);
+ unsigned long val;
+ int status;
+
+ status = kstrtoul(buf, 0, &val);
+ if (status)
+ return status;
+
+ if ((tee->conf & TEE_CONF_TEST_MODE) == TEE_CONF_TEST_MODE)
+ tee->test = val;
+
+ return count;
+}
+
+/*
+ * A state-to-string lookup table, for exposing a human readable state
+ * via sysfs. Always keep in sync with enum tee_state
+ */
+static const char *const tee_state_string[] = {
+ "offline",
+ "online",
+ "suspended",
+ "running",
+ "crashed",
+ "invalid",
+};
+
+static ssize_t tee_show_state(struct device *device,
+ struct device_attribute *attr, char *buf)
+{
+ struct tee *tee = dev_get_drvdata(device);
+
+ int state = tee->state > TEE_LAST ? TEE_LAST : tee->state;
+
+ return snprintf(buf, PAGE_SIZE, "%s (%d)\n", tee_state_string[state],
+ tee->state);
+}
+
+/*
+ * In the following, 0660 is (S_IWUGO | S_IRUGO)
+ */
+static struct device_attribute device_attrs[] = {
+ __ATTR_RO(dump),
+ __ATTR_RO(stat),
+ __ATTR_RO(info),
+ __ATTR(test, (0660), test_show, test_store),
+ __ATTR(state, S_IRUGO, tee_show_state, NULL),
+ __ATTR(name, S_IRUGO, name_show, NULL),
+ __ATTR(refcount, S_IRUGO, refcount_show, NULL),
+ __ATTR(type, S_IRUGO, type_show, NULL),
+ __ATTR(conf, S_IRUGO, conf_show, NULL),
+};
+
+void tee_init_sysfs(struct tee *tee)
+{
+ int i, error = 0;
+
+ if (!tee)
+ return;
+
+ if (dev_get_drvdata(tee->miscdev.this_device) != tee) {
+ dev_err(_DEV(tee), "drvdata is not valid\n");
+ return;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(device_attrs); i++) {
+ error =
+ device_create_file(tee->miscdev.this_device,
+ &device_attrs[i]);
+ if (error)
+ break;
+ }
+
+ if (error) {
+ while (--i >= 0)
+ device_remove_file(tee->miscdev.this_device,
+ &device_attrs[i]);
+ }
+ /* location /sys/class/<class name>/<dev_name()>/<name> ->
+ * /sys/class/misc/teelx00/info */
+}
+
+void tee_cleanup_sysfs(struct tee *tee)
+{
+ int i;
+
+ if (!tee)
+ return;
+
+ for (i = 0; i < ARRAY_SIZE(device_attrs); i++)
+ device_remove_file(tee->miscdev.this_device, &device_attrs[i]);
+}
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * Copyright (c) 2015, Linaro Limited
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License Version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include <linux/types.h>
+#include <linux/completion.h>
+#include <linux/slab.h>
+#include "tee_wait_queue.h"
+
+struct tee_wait_queue {
+ struct list_head link;
+ struct completion comp;
+ u32 key;
+};
+
+void tee_wait_queue_init(struct tee_wait_queue_private *priv)
+{
+ mutex_init(&priv->mu);
+ INIT_LIST_HEAD(&priv->db);
+}
+EXPORT_SYMBOL(tee_wait_queue_init);
+
+void tee_wait_queue_exit(struct tee_wait_queue_private *priv)
+{
+ mutex_destroy(&priv->mu);
+}
+EXPORT_SYMBOL(tee_wait_queue_exit);
+
+static struct tee_wait_queue *tee_wait_queue_get(struct device *dev,
+ struct tee_wait_queue_private *priv, u32 key)
+{
+ struct tee_wait_queue *w;
+
+ mutex_lock(&priv->mu);
+
+ list_for_each_entry(w, &priv->db, link)
+ if (w->key == key)
+ goto out;
+
+ w = kmalloc(sizeof(struct tee_wait_queue), GFP_KERNEL);
+ if (!w)
+ goto out;
+
+ init_completion(&w->comp);
+ w->key = key;
+ list_add_tail(&w->link, &priv->db);
+out:
+ mutex_unlock(&priv->mu);
+ return w;
+}
+
+void tee_wait_queue_sleep(struct device *dev,
+ struct tee_wait_queue_private *priv, u32 key)
+{
+ struct tee_wait_queue *w = tee_wait_queue_get(dev, priv, key);
+
+ if (!w)
+ return;
+
+ wait_for_completion(&w->comp);
+ mutex_lock(&priv->mu);
+ list_del(&w->link);
+ mutex_unlock(&priv->mu);
+ kfree(w);
+}
+EXPORT_SYMBOL(tee_wait_queue_sleep);
+
+void tee_wait_queue_wakeup(struct device *dev,
+ struct tee_wait_queue_private *priv, u32 key)
+{
+ struct tee_wait_queue *w = tee_wait_queue_get(dev, priv, key);
+
+ if (!w)
+ return;
+
+ complete(&w->comp);
+}
+EXPORT_SYMBOL(tee_wait_queue_wakeup);
--- /dev/null
+/*
+ * Copyright (c) 2015, Linaro Limited
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License Version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#ifndef TEE_WAIT_QUEUE_H
+#define TEE_WAIT_QUEUE_H
+
+#include <linux/mutex.h>
+#include <linux/list.h>
+#include <linux/device.h>
+
+struct tee_wait_queue_private {
+ struct mutex mu;
+ struct list_head db;
+};
+
+void tee_wait_queue_init(struct tee_wait_queue_private *priv);
+void tee_wait_queue_exit(struct tee_wait_queue_private *priv);
+void tee_wait_queue_sleep(struct device *dev,
+ struct tee_wait_queue_private *priv, u32 key);
+void tee_wait_queue_wakeup(struct device *dev,
+ struct tee_wait_queue_private *priv, u32 key);
+
+#endif /*TEE_WAIT_QUEUE_H*/
--- /dev/null
+/*
+ * 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"
+ };
+};
--- /dev/null
+/*
+ * 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>;
+ };
+ };
+ };
--- /dev/null
+These files is a temporary workaround until the driver has switched
+to use CMA to allocate shared memory.
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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*/
--- /dev/null
+/*
+ * Copyright (c) 2014, STMicroelectronics International N.V.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+#ifndef TEE_CLIENT_API_H
+#define TEE_CLIENT_API_H
+
+#ifndef __KERNEL__
+#include <stdint.h>
+#include <stddef.h>
+#endif /* __KERNEL__ */
+
+/*
+ * Defines the number of available memory references in an open session or
+ * invoke command operation payload.
+ */
+#define TEEC_CONFIG_PAYLOAD_REF_COUNT 4
+
+/**
+ * Defines the maximum size of a single shared memory block, in bytes, of both
+ * API allocated and API registered memory. The size is currently set to
+ * 512 * kB (512 * 1024).
+ */
+#define TEEC_CONFIG_SHAREDMEM_MAX_SIZE 0x8000
+
+/**
+ * Flag constants indicating the type of parameters encoded inside the
+ * operation payload (TEEC_Operation), Type is uint32_t.
+ *
+ * TEEC_NONE The Parameter is not used
+ *
+ * TEEC_VALUE_INPUT The Parameter is a TEEC_Value tagged as input.
+ *
+ * TEEC_VALUE_OUTPUT The Parameter is a TEEC_Value tagged as output.
+ *
+ * TEEC_VALUE_INOUT The Parameter is a TEEC_Value tagged as both as
+ * input and output, i.e., for which both the
+ * behaviors of TEEC_VALUE_INPUT and
+ * TEEC_VALUE_OUTPUT apply.
+ *
+ * TEEC_MEMREF_TEMP_INPUT The Parameter is a TEEC_TempMemoryReference
+ * describing a region of memory which needs to be
+ * temporarily registered for the duration of the
+ * Operation and is tagged as input.
+ *
+ * TEEC_MEMREF_TEMP_OUTPUT Same as TEEC_MEMREF_TEMP_INPUT, but the Memory
+ * Reference is tagged as output. The
+ * Implementation may update the size field to
+ * reflect the required output size in some use
+ * cases.
+ *
+ * TEEC_MEMREF_TEMP_INOUT A Temporary Memory Reference tagged as both
+ * input and output, i.e., for which both the
+ * behaviors of TEEC_MEMREF_TEMP_INPUT and
+ * TEEC_MEMREF_TEMP_OUTPUT apply.
+ *
+ * TEEC_MEMREF_WHOLE The Parameter is a Registered Memory Reference
+ * that refers to the entirety of its parent Shared
+ * Memory block. The parameter structure is a
+ * TEEC_MemoryReference. In this structure, the
+ * Implementation MUST read only the parent field
+ * and MAY update the size field when the operation
+ * completes.
+ *
+ * TEEC_MEMREF_PARTIAL_INPUT A Registered Memory Reference structure that
+ * refers to a partial region of its parent Shared
+ * Memory block and is tagged as input.
+ *
+ * TEEC_MEMREF_PARTIAL_OUTPUT Registered Memory Reference structure that
+ * refers to a partial region of its parent Shared
+ * Memory block and is tagged as output.
+ *
+ * TEEC_MEMREF_PARTIAL_INOUT The Registered Memory Reference structure that
+ * refers to a partial region of its parent Shared
+ * Memory block and is tagged as both input and
+ * output, i.e., for which both the behaviors of
+ * TEEC_MEMREF_PARTIAL_INPUT and
+ * TEEC_MEMREF_PARTIAL_OUTPUT apply.
+ */
+#define TEEC_NONE 0x00000000
+#define TEEC_VALUE_INPUT 0x00000001
+#define TEEC_VALUE_OUTPUT 0x00000002
+#define TEEC_VALUE_INOUT 0x00000003
+#define TEEC_MEMREF_TEMP_INPUT 0x00000005
+#define TEEC_MEMREF_TEMP_OUTPUT 0x00000006
+#define TEEC_MEMREF_TEMP_INOUT 0x00000007
+#define TEEC_MEMREF_WHOLE 0x0000000C
+#define TEEC_MEMREF_PARTIAL_INPUT 0x0000000D
+#define TEEC_MEMREF_PARTIAL_OUTPUT 0x0000000E
+#define TEEC_MEMREF_PARTIAL_INOUT 0x0000000F
+
+/**
+ * Flag constants indicating the data transfer direction of memory in
+ * TEEC_Parameter. TEEC_MEM_INPUT signifies data transfer direction from the
+ * client application to the TEE. TEEC_MEM_OUTPUT signifies data transfer
+ * direction from the TEE to the client application. Type is uint32_t.
+ *
+ * TEEC_MEM_INPUT The Shared Memory can carry data from the client
+ * application to the Trusted Application.
+ * TEEC_MEM_OUTPUT The Shared Memory can carry data from the Trusted
+ * Application to the client application.
+ * TEEC_MEM_DMABUF The Shared Memory is allocated with the dma buf api and
+ * not necessarly user mapped.
+ * Handle of the memory pass to drivers is the implementation
+ * fd field instead of the buffer field.
+ * TEEC_MEM_KAPI Shared memory is required from another linux module.
+ * Dma buf file descriptor is not created.
+ */
+#define TEEC_MEM_INPUT 0x00000001
+#define TEEC_MEM_OUTPUT 0x00000002
+#define TEEC_MEM_DMABUF 0x00010000
+#define TEEC_MEM_KAPI 0x00020000
+
+/**
+ * Return values. Type is TEEC_Result
+ *
+ * TEEC_SUCCESS The operation was successful.
+ * TEEC_ERROR_GENERIC Non-specific cause.
+ * TEEC_ERROR_ACCESS_DENIED Access privileges are not sufficient.
+ * TEEC_ERROR_CANCEL The operation was canceled.
+ * TEEC_ERROR_ACCESS_CONFLICT Concurrent accesses caused conflict.
+ * TEEC_ERROR_EXCESS_DATA Too much data for the requested operation was
+ * passed.
+ * TEEC_ERROR_BAD_FORMAT Input data was of invalid format.
+ * TEEC_ERROR_BAD_PARAMETERS Input parameters were invalid.
+ * TEEC_ERROR_BAD_STATE Operation is not valid in the current state.
+ * TEEC_ERROR_ITEM_NOT_FOUND The requested data item is not found.
+ * TEEC_ERROR_NOT_IMPLEMENTED The requested operation should exist but is not
+ * yet implemented.
+ * TEEC_ERROR_NOT_SUPPORTED The requested operation is valid but is not
+ * supported in this implementation.
+ * TEEC_ERROR_NO_DATA Expected data was missing.
+ * TEEC_ERROR_OUT_OF_MEMORY System ran out of resources.
+ * TEEC_ERROR_BUSY The system is busy working on something else.
+ * TEEC_ERROR_COMMUNICATION Communication with a remote party failed.
+ * TEEC_ERROR_SECURITY A security fault was detected.
+ * TEEC_ERROR_SHORT_BUFFER The supplied buffer is too short for the
+ * generated output.
+ * TEEC_ERROR_TARGET_DEAD Trusted Application has panicked
+ * during the operation.
+ */
+
+/**
+ * Standard defined error codes.
+ */
+#define TEEC_SUCCESS 0x00000000
+#define TEEC_ERROR_GENERIC 0xFFFF0000
+#define TEEC_ERROR_ACCESS_DENIED 0xFFFF0001
+#define TEEC_ERROR_CANCEL 0xFFFF0002
+#define TEEC_ERROR_ACCESS_CONFLICT 0xFFFF0003
+#define TEEC_ERROR_EXCESS_DATA 0xFFFF0004
+#define TEEC_ERROR_BAD_FORMAT 0xFFFF0005
+#define TEEC_ERROR_BAD_PARAMETERS 0xFFFF0006
+#define TEEC_ERROR_BAD_STATE 0xFFFF0007
+#define TEEC_ERROR_ITEM_NOT_FOUND 0xFFFF0008
+#define TEEC_ERROR_NOT_IMPLEMENTED 0xFFFF0009
+#define TEEC_ERROR_NOT_SUPPORTED 0xFFFF000A
+#define TEEC_ERROR_NO_DATA 0xFFFF000B
+#define TEEC_ERROR_OUT_OF_MEMORY 0xFFFF000C
+#define TEEC_ERROR_BUSY 0xFFFF000D
+#define TEEC_ERROR_COMMUNICATION 0xFFFF000E
+#define TEEC_ERROR_SECURITY 0xFFFF000F
+#define TEEC_ERROR_SHORT_BUFFER 0xFFFF0010
+#define TEEC_ERROR_TARGET_DEAD 0xFFFF3024
+
+/**
+ * Function error origins, of type TEEC_ErrorOrigin. These indicate where in
+ * the software stack a particular return value originates from.
+ *
+ * TEEC_ORIGIN_API The error originated within the TEE Client API
+ * implementation.
+ * TEEC_ORIGIN_COMMS The error originated within the underlying
+ * communications stack linking the rich OS with
+ * the TEE.
+ * TEEC_ORIGIN_TEE The error originated within the common TEE code.
+ * TEEC_ORIGIN_TRUSTED_APP The error originated within the Trusted Application
+ * code.
+ */
+#define TEEC_ORIGIN_API 0x00000001
+#define TEEC_ORIGIN_COMMS 0x00000002
+#define TEEC_ORIGIN_TEE 0x00000003
+#define TEEC_ORIGIN_TRUSTED_APP 0x00000004
+
+/**
+ * Session login methods, for use in TEEC_OpenSession() as parameter
+ * connectionMethod. Type is uint32_t.
+ *
+ * TEEC_LOGIN_PUBLIC No login data is provided.
+ * TEEC_LOGIN_USER Login data about the user running the Client
+ * Application process is provided.
+ * TEEC_LOGIN_GROUP Login data about the group running the Client
+ * Application process is provided.
+ * TEEC_LOGIN_APPLICATION Login data about the running Client Application
+ * itself is provided.
+ */
+#define TEEC_LOGIN_PUBLIC 0x00000000
+#define TEEC_LOGIN_USER 0x00000001
+#define TEEC_LOGIN_GROUP 0x00000002
+#define TEEC_LOGIN_APPLICATION 0x00000004
+
+/**
+ * Encode the paramTypes according to the supplied types.
+ *
+ * @param p0 The first param type.
+ * @param p1 The second param type.
+ * @param p2 The third param type.
+ * @param p3 The fourth param type.
+ */
+#define TEEC_PARAM_TYPES(p0, p1, p2, p3) \
+ ((p0) | ((p1) << 4) | ((p2) << 8) | ((p3) << 12))
+
+/**
+ * Get the i_th param type from the paramType.
+ *
+ * @param p The paramType.
+ * @param i The i-th parameter to get the type for.
+ */
+#define TEEC_PARAM_TYPE_GET(p, i) (((p) >> (i * 4)) & 0xF)
+
+typedef uint32_t TEEC_Result;
+
+/**
+ * struct TEEC_Context - Represents a connection between a client application
+ * and a TEE.
+ *
+ * Context identifier can be a handle (when opened from user land)
+ * or a structure pointer (when opened from kernel land).
+ * Identifier is defined as an union to match type sizes on all architectures.
+ */
+typedef struct {
+ char devname[256];
+ union {
+ struct tee_context *ctx;
+ int fd;
+ };
+} TEEC_Context;
+
+/**
+ * This type contains a Universally Unique Resource Identifier (UUID) type as
+ * defined in RFC4122. These UUID values are used to identify Trusted
+ * Applications.
+ */
+typedef struct {
+ uint32_t timeLow;
+ uint16_t timeMid;
+ uint16_t timeHiAndVersion;
+ uint8_t clockSeqAndNode[8];
+} TEEC_UUID;
+
+/**
+ * struct TEEC_SharedMemory - Memory to transfer data between a client
+ * application and trusted code.
+ *
+ * @param buffer The memory buffer which is to be, or has been, shared
+ * with the TEE.
+ * @param size The size, in bytes, of the memory buffer.
+ * @param flags Bit-vector which holds properties of buffer.
+ * The bit-vector can contain either or both of the
+ * TEEC_MEM_INPUT and TEEC_MEM_OUTPUT flags.
+ *
+ * A shared memory block is a region of memory allocated in the context of the
+ * client application memory space that can be used to transfer data between
+ * that client application and a trusted application. The user of this struct
+ * is responsible to populate the buffer pointer.
+ */
+typedef struct {
+ void *buffer;
+ size_t size;
+ uint32_t flags;
+ /*
+ * identifier can store a handle (int) or a structure pointer (void *).
+ * define this union to match case where sizeof(int)!=sizeof(void *).
+ */
+ union {
+ int fd;
+ void *ptr;
+ } d;
+ uint8_t registered;
+} TEEC_SharedMemory;
+
+/**
+ * struct TEEC_TempMemoryReference - Temporary memory to transfer data between
+ * a client application and trusted code, only used for the duration of the
+ * operation.
+ *
+ * @param buffer The memory buffer which is to be, or has been shared with
+ * the TEE.
+ * @param size The size, in bytes, of the memory buffer.
+ *
+ * A memory buffer that is registered temporarily for the duration of the
+ * operation to be called.
+ */
+typedef struct {
+ void *buffer;
+ size_t size;
+} TEEC_TempMemoryReference;
+
+/**
+ * struct TEEC_RegisteredMemoryReference - use a pre-registered or
+ * pre-allocated shared memory block of memory to transfer data between
+ * a client application and trusted code.
+ *
+ * @param parent Points to a shared memory structure. The memory reference
+ * may utilize the whole shared memory or only a part of it.
+ * Must not be NULL
+ *
+ * @param size The size, in bytes, of the memory buffer.
+ *
+ * @param offset The offset, in bytes, of the referenced memory region from
+ * the start of the shared memory block.
+ *
+ */
+typedef struct {
+ TEEC_SharedMemory *parent;
+ size_t size;
+ size_t offset;
+} TEEC_RegisteredMemoryReference;
+
+/**
+ * struct TEEC_Value - Small raw data container
+ *
+ * Instead of allocating a shared memory buffer this structure can be used
+ * to pass small raw data between a client application and trusted code.
+ *
+ * @param a The first integer value.
+ *
+ * @param b The second second value.
+ */
+typedef struct {
+ uint32_t a;
+ uint32_t b;
+} TEEC_Value;
+
+/**
+ * union TEEC_Parameter - Memory container to be used when passing data between
+ * client application and trusted code.
+ *
+ * Either the client uses a shared memory reference, parts of it or a small raw
+ * data container.
+ *
+ * @param tmpref A temporary memory reference only valid for the duration
+ * of the operation.
+ *
+ * @param memref The entire shared memory or parts of it.
+ *
+ * @param value The small raw data container to use
+ */
+typedef union {
+ TEEC_TempMemoryReference tmpref;
+ TEEC_RegisteredMemoryReference memref;
+ TEEC_Value value;
+} TEEC_Parameter;
+
+/**
+ * struct TEEC_Session - Represents a connection between a client application
+ * and a trusted application.
+ */
+typedef struct {
+ int fd;
+} TEEC_Session;
+
+/**
+ * struct TEEC_Operation - Holds information and memory references used in
+ * TEEC_InvokeCommand().
+ *
+ * @param started Client must initialize to zero if it needs to cancel
+ * an operation about to be performed.
+ * @param paramTypes Type of data passed. Use TEEC_PARAMS_TYPE macro to
+ * create the correct flags.
+ * 0 means TEEC_NONE is passed for all params.
+ * @param params Array of parameters of type TEEC_Parameter.
+ * @param session Internal pointer to the last session used by
+ * TEEC_InvokeCommand with this operation.
+ *
+ */
+typedef struct {
+ uint32_t started;
+ uint32_t paramTypes;
+ TEEC_Parameter params[TEEC_CONFIG_PAYLOAD_REF_COUNT];
+ /* Implementation-Defined */
+ TEEC_Session *session;
+ TEEC_SharedMemory memRefs[TEEC_CONFIG_PAYLOAD_REF_COUNT];
+ uint32_t flags;
+} TEEC_Operation;
+
+/**
+ * TEEC_InitializeContext() - Initializes a context holding connection
+ * information on the specific TEE, designated by the name string.
+
+ * @param name A zero-terminated string identifying the TEE to connect to.
+ * If name is set to NULL, the default TEE is connected to. NULL
+ * is the only supported value in this version of the API
+ * implementation.
+ *
+ * @param context The context structure which is to be initialized.
+ *
+ * @return TEEC_SUCCESS The initialization was successful.
+ * @return TEEC_Result Something failed.
+ */
+TEEC_Result TEEC_InitializeContext(const char *name, TEEC_Context *context);
+
+/**
+ * TEEC_FinalizeContext() - Destroys a context holding connection information
+ * on the specific TEE.
+ *
+ * This function destroys an initialized TEE context, closing the connection
+ * between the client application and the TEE. This function must only be
+ * called when all sessions related to this TEE context have been closed and
+ * all shared memory blocks have been released.
+ *
+ * @param context The context to be destroyed.
+ */
+void TEEC_FinalizeContext(TEEC_Context *context);
+
+/**
+ * TEEC_OpenSession() - Opens a new session with the specified trusted
+ * application.
+ *
+ * @param context The initialized TEE context structure in which
+ * scope to open the session.
+ * @param session The session to initialize.
+ * @param destination A structure identifying the trusted application
+ * with which to open a session.
+ *
+ * @param connectionMethod The connection method to use.
+ * @param connectionData Any data necessary to connect with the chosen
+ * connection method. Not supported, should be set to
+ * NULL.
+ * @param operation An operation structure to use in the session. May
+ * be set to NULL to signify no operation structure
+ * needed.
+ *
+ * @param returnOrigin A parameter which will hold the error origin if
+ * this function returns any value other than
+ * TEEC_SUCCESS.
+ *
+ * @return TEEC_SUCCESS OpenSession successfully opened a new session.
+ * @return TEEC_Result Something failed.
+ *
+ */
+TEEC_Result TEEC_OpenSession(TEEC_Context *context,
+ TEEC_Session *session,
+ const TEEC_UUID *destination,
+ uint32_t connectionMethod,
+ const void *connectionData,
+ TEEC_Operation *operation,
+ uint32_t *returnOrigin);
+
+/**
+ * TEEC_CloseSession() - Closes the session which has been opened with the
+ * specific trusted application.
+ *
+ * @param session The opened session to close.
+ */
+void TEEC_CloseSession(TEEC_Session *session);
+
+/**
+ * TEEC_InvokeCommand() - Executes a command in the specified trusted
+ * application.
+ *
+ * @param session A handle to an open connection to the trusted
+ * application.
+ * @param commandID Identifier of the command in the trusted application
+ * to invoke.
+ * @param operation An operation structure to use in the invoke command.
+ * May be set to NULL to signify no operation structure
+ * needed.
+ * @param returnOrigin A parameter which will hold the error origin if this
+ * function returns any value other than TEEC_SUCCESS.
+ *
+ * @return TEEC_SUCCESS OpenSession successfully opened a new session.
+ * @return TEEC_Result Something failed.
+ */
+TEEC_Result TEEC_InvokeCommand(TEEC_Session *session,
+ uint32_t commandID,
+ TEEC_Operation *operation,
+ uint32_t *returnOrigin);
+
+/**
+ * TEEC_RegisterSharedMemory() - Register a block of existing memory as a
+ * shared block within the scope of the specified context.
+ *
+ * @param context The initialized TEE context structure in which scope to
+ * open the session.
+ * @param sharedMem pointer to the shared memory structure to register.
+ *
+ * @return TEEC_SUCCESS The registration was successful.
+ * @return TEEC_ERROR_OUT_OF_MEMORY Memory exhaustion.
+ * @return TEEC_Result Something failed.
+ */
+TEEC_Result TEEC_RegisterSharedMemory(TEEC_Context *context,
+ TEEC_SharedMemory *sharedMem);
+
+/**
+ * TEEC_AllocateSharedMemory() - Allocate shared memory for TEE.
+ *
+ * @param context The initialized TEE context structure in which scope to
+ * open the session.
+ * @param sharedMem Pointer to the allocated shared memory.
+ *
+ * @return TEEC_SUCCESS The registration was successful.
+ * @return TEEC_ERROR_OUT_OF_MEMORY Memory exhaustion.
+ * @return TEEC_Result Something failed.
+ */
+TEEC_Result TEEC_AllocateSharedMemory(TEEC_Context *context,
+ TEEC_SharedMemory *sharedMem);
+
+/**
+ * TEEC_ReleaseSharedMemory() - Free or deregister the shared memory.
+ *
+ * @param sharedMem Pointer to the shared memory to be freed.
+ */
+void TEEC_ReleaseSharedMemory(TEEC_SharedMemory *sharedMemory);
+
+/**
+ * TEEC_RequestCancellation() - Request the cancellation of a pending open
+ * session or command invocation.
+ *
+ * @param operation Pointer to an operation previously passed to open session
+ * or invoke.
+ */
+void TEEC_RequestCancellation(TEEC_Operation *operation);
+
+/**
+ * Register a pre-allocated Trusted Application This is mainly intended for
+ * OS-FREE contexts or when a filesystem is not available.
+ *
+ * @param ta Pointer to the trusted application binary
+ * @param size The size of the TA binary
+ *
+ * @return TEEC_SUCCESS if successful.
+ * @return TEEC_Result something failed.
+ */
+TEEC_Result TEEC_RegisterTA(const void *ta, const size_t size);
+
+/**
+ * Unregister a pre-allocated Trusted Application This is mainly intended for
+ * OS-FREE contexts or when a filesystem is not available.
+ *
+ * @param ta Pointer to the trusted application binary
+ */
+void TEEC_UnregisterTA(const void *ta);
+
+#endif
--- /dev/null
+/*
+ * Copyright (c) 2014, STMicroelectronics International N.V.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License Version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#ifndef __TEE_CORE_DRV_H__
+#define __TEE_CORE_DRV_H__
+
+#include <linux/klist.h>
+#include <linux/device.h>
+#include <linux/file.h>
+#include <linux/cdev.h>
+#include <linux/debugfs.h>
+#include <linux/miscdevice.h>
+#include <linux/types.h>
+#include <linux/atomic.h>
+#include <linux/scatterlist.h>
+
+#include <linux/types.h>
+
+#include <linux/tee_client_api.h>
+
+struct tee_cmd_io;
+struct tee_shm_io;
+struct tee_rpc;
+
+enum tee_state {
+ TEE_OFFLINE = 0,
+ TEE_ONLINE = 1,
+ TEE_SUSPENDED = 2,
+ TEE_RUNNING = 3,
+ TEE_CRASHED = 4,
+ TEE_LAST = 5,
+};
+
+#define TEE_CONF_TEST_MODE 0x01000000
+#define TEE_CONF_FW_NOT_CAPABLE 0x00000001
+
+struct tee_stats_entry {
+ int count;
+ int max;
+};
+
+#define TEE_STATS_CONTEXT_IDX 0
+#define TEE_STATS_SESSION_IDX 1
+#define TEE_STATS_SHM_IDX 2
+
+#define TEE_MAX_TEE_DEV_NAME (64)
+struct tee {
+ struct klist_node node;
+ char name[TEE_MAX_TEE_DEV_NAME];
+ int id;
+ void *priv;
+ const struct tee_ops *ops;
+ struct device *dev;
+ struct miscdevice miscdev;
+ struct tee_rpc *rpc;
+ struct dentry *dbg_dir;
+ atomic_t refcount;
+ int max_refcount;
+ struct tee_stats_entry stats[3];
+ struct list_head list_ctx;
+ struct list_head list_rpc_shm;
+ struct mutex lock;
+ unsigned int state;
+ uint32_t shm_flags; /* supported flags for shm allocation */
+ uint32_t conf;
+ uint32_t test;
+};
+
+#define _DEV(tee) (tee->miscdev.this_device)
+
+#define TEE_MAX_CLIENT_NAME (128)
+
+/**
+ * struct tee_context - internal structure to store a TEE context.
+ *
+ * @tee: tee attached to the tee_context
+ * @usr_client: flag to known if the client is user side client
+ * @entry: list of tee_context
+ * @list_sess: list of tee_session that denotes all tee_session attached
+ * @list_shm: list of tee_shm that denotes all tee_shm attached
+ * @refcount: number of objects which reference it (including itself)
+ */
+struct tee_context {
+ struct tee *tee;
+ char name[TEE_MAX_CLIENT_NAME];
+ int tgid;
+ int usr_client;
+ struct list_head entry;
+ struct list_head list_sess;
+ struct list_head list_shm;
+ struct kref refcount;
+};
+
+/**
+ * struct tee_session - internal structure to store a TEE session.
+ *
+ * @entry: list of tee_context
+ * @ctx: tee_context attached to the tee_session
+ * @sessid: session ID returned by the secure world
+ * @priv: exporter specific private data for this buffer object
+ */
+struct tee_session {
+ struct list_head entry;
+ struct tee_context *ctx;
+ uint32_t sessid;
+ void *priv;
+};
+
+struct tee_shm_dma_buf {
+ struct dma_buf_attachment *attach;
+ struct sg_table *sgt;
+ bool tee_allocated;
+};
+
+/**
+ * struct tee_shm - internal structure to store a shm object.
+ *
+ * @ctx: tee_context attached to the buffer.
+ * @tee: tee attached to the buffer.
+ * @dev: device attached to the buffer.
+ * @size_req: requested size for the buffer
+ * @size_alloc: effective size of the buffer
+ * @kaddr: kernel address if mapped kernel side
+ * @paddr: physical address
+ * @flags: flags which denote the type of the buffer
+ * @entry: list of tee_shm
+ */
+struct tee_shm {
+ struct list_head entry;
+ struct tee_context *ctx;
+ struct tee *tee;
+ struct device *dev;
+ size_t size_req;
+ size_t size_alloc;
+ uint32_t flags;
+ void *kaddr;
+ dma_addr_t paddr;
+ struct sg_table sgt;
+ struct tee_shm_dma_buf *sdb;
+};
+
+#define TEE_SHM_MAPPED 0x01000000
+#define TEE_SHM_TEMP 0x02000000
+#define TEE_SHM_FROM_RPC 0x04000000
+#define TEE_SHM_REGISTERED 0x08000000
+#define TEE_SHM_MEMREF 0x10000000
+#define TEE_SHM_CACHED 0x20000000
+
+#define TEE_SHM_DRV_PRIV_MASK 0xFF000000
+
+struct tee_data {
+ uint32_t type;
+ uint32_t type_original;
+ TEEC_SharedMemory c_shm[TEEC_CONFIG_PAYLOAD_REF_COUNT];
+ union {
+ struct tee_shm *shm;
+ TEEC_Value value;
+ } params[TEEC_CONFIG_PAYLOAD_REF_COUNT];
+};
+
+struct tee_cmd {
+ TEEC_Result err;
+ uint32_t origin;
+ uint32_t cmd;
+ struct tee_shm *uuid;
+ struct tee_shm *ta;
+ struct tee_data param;
+};
+
+struct tee_shm *tee_shm_alloc_from_rpc(struct tee *tee, size_t size);
+void tee_shm_free_from_rpc(struct tee_shm *);
+
+int tee_core_add(struct tee *tee);
+int tee_core_del(struct tee *tee);
+
+struct tee *tee_core_alloc(struct device *dev, char *name, int id,
+ const struct tee_ops *ops, size_t len);
+int tee_core_free(struct tee *tee);
+
+struct tee_ops {
+ struct module *owner;
+ const char *type;
+
+ int (*start)(struct tee *tee);
+ int (*stop)(struct tee *tee);
+ int (*open)(struct tee_session *sess, struct tee_cmd *cmd);
+ int (*close)(struct tee_session *sess);
+ int (*invoke)(struct tee_session *sess, struct tee_cmd *cmd);
+ int (*cancel)(struct tee_session *sess, struct tee_cmd *cmd);
+ struct tee_shm *(*alloc)(struct tee *tee, size_t size,
+ uint32_t flags);
+ void (*free)(struct tee_shm *shm);
+ int (*shm_inc_ref)(struct tee_shm *shm);
+};
+
+#endif /* __TEE_CORE_DRV_H__ */
--- /dev/null
+/*
+ * Copyright (c) 2014, STMicroelectronics International N.V.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License Version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#ifndef _TEE_IOC_H
+#define _TEE_IOC_H
+
+#include <linux/tee_client_api.h>
+
+#ifndef __KERNEL__
+#define __user
+#endif
+
+/**
+ * struct tee_cmd_io - The command sent to an open tee device.
+ * @err: Error code (as in Global Platform TEE Client API spec)
+ * @origin: Origin for the error code (also from spec).
+ * @cmd: The command to be executed in the trusted application.
+ * @uuid: The uuid for the trusted application.
+ * @data: The trusted application or memory block.
+ * @data_size: The size of the trusted application or memory block.
+ * @op: The cmd payload operation for the trusted application.
+ *
+ * This structure is mainly used in the Linux kernel for communication
+ * with the user space.
+ */
+struct tee_cmd_io {
+ TEEC_Result err;
+ uint32_t origin;
+ uint32_t cmd;
+ TEEC_UUID __user *uuid;
+ void __user *data;
+ uint32_t data_size;
+ TEEC_Operation __user *op;
+ int fd_sess;
+};
+
+struct tee_shm_io {
+ void __user *buffer;
+ size_t size;
+ uint32_t flags;
+ union {
+ int fd_shm;
+ void *ptr;
+ };
+ uint8_t registered;
+};
+
+#define TEE_OPEN_SESSION_IOC _IOWR('t', 161, struct tee_cmd_io)
+#define TEE_INVOKE_COMMAND_IOC _IOWR('t', 163, struct tee_cmd_io)
+#define TEE_REQUEST_CANCELLATION_IOC _IOWR('t', 164, struct tee_cmd_io)
+#define TEE_ALLOC_SHM_IOC _IOWR('t', 165, struct tee_shm_io)
+#define TEE_GET_FD_FOR_RPC_SHM_IOC _IOWR('t', 167, struct tee_shm_io)
+
+#endif /* _TEE_IOC_H */
--- /dev/null
+/*
+ * Copyright (c) 2014, STMicroelectronics International N.V.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License Version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#ifndef _TEE_KERNEL_API_H
+#define _TEE_KERNEL_API_H
+
+#include <linux/tee_client_api.h>
+
+/**
+ * struct TEEC_Context - Represents a connection between a client application
+ * and a TEE.
+ */
+/*typedef struct {
+ char devname[256];
+} TEEC_Context;*/
+
+/**
+ * struct TEEC_Session - Represents a connection between a client application
+ * and a trusted application.
+ */
+/*typedef struct {
+ void *session;
+} TEEC_Session;*/
+
+/**
+ * TEEC_InitializeContext() - Initializes a context holding connection
+ * information on the specific TEE, designated by the name string.
+
+ * @param name A zero-terminated string identifying the TEE to connect to.
+ * If name is set to NULL, the default TEE is connected to. NULL
+ * is the only supported value in this version of the API
+ * implementation.
+ *
+ * @param context The context structure which is to be initialized.
+ *
+ * @return TEEC_SUCCESS The initialization was successful.
+ * @return TEEC_Result Something failed.
+ */
+TEEC_Result TEEC_InitializeContext(const char *name, TEEC_Context *context);
+
+/**
+ * TEEC_FinalizeContext() - Destroys a context holding connection information
+ * on the specific TEE.
+ *
+ * This function destroys an initialized TEE context, closing the connection
+ * between the client application and the TEE. This function must only be
+ * called when all sessions related to this TEE context have been closed and
+ * all shared memory blocks have been released.
+ *
+ * @param context The context to be destroyed.
+ */
+void TEEC_FinalizeContext(TEEC_Context *context);
+
+/**
+ * TEEC_OpenSession() - Opens a new session with the specified trusted
+ * application.
+ *
+ * @param context The initialized TEE context structure in which
+ * scope to open the session.
+ * @param session The session to initialize.
+ * @param destination A structure identifying the trusted application
+ * with which to open a session.
+ *
+ * @param connectionMethod The connection method to use.
+ * @param connectionData Any data necessary to connect with the chosen
+ * connection method. Not supported, should be set to
+ * NULL.
+ * @param operation An operation structure to use in the session. May
+ * be set to NULL to signify no operation structure
+ * needed.
+ *
+ * @param returnOrigin A parameter which will hold the error origin if
+ * this function returns any value other than
+ * TEEC_SUCCESS.
+ *
+ * @return TEEC_SUCCESS OpenSession successfully opened a new session.
+ * @return TEEC_Result Something failed.
+ *
+ */
+TEEC_Result TEEC_OpenSession(TEEC_Context *context,
+ TEEC_Session *session,
+ const TEEC_UUID *destination,
+ uint32_t connectionMethod,
+ const void *connectionData,
+ TEEC_Operation *operation,
+ uint32_t *returnOrigin);
+
+/**
+ * TEEC_CloseSession() - Closes the session which has been opened with the
+ * specific trusted application.
+ *
+ * @param session The opened session to close.
+ */
+void TEEC_CloseSession(TEEC_Session *session);
+
+/**
+ * TEEC_InvokeCommand() - Executes a command in the specified trusted
+ * application.
+ *
+ * @param session A handle to an open connection to the trusted
+ * application.
+ * @param commandID Identifier of the command in the trusted application
+ * to invoke.
+ * @param operation An operation structure to use in the invoke command.
+ * May be set to NULL to signify no operation structure
+ * needed.
+ * @param returnOrigin A parameter which will hold the error origin if this
+ * function returns any value other than TEEC_SUCCESS.
+ *
+ * @return TEEC_SUCCESS OpenSession successfully opened a new session.
+ * @return TEEC_Result Something failed.
+ */
+TEEC_Result TEEC_InvokeCommand(TEEC_Session *session,
+ uint32_t commandID,
+ TEEC_Operation *operation,
+ uint32_t *returnOrigin);
+
+/**
+ * TEEC_RegisterSharedMemory() - Register a block of existing memory as a
+ * shared block within the scope of the specified context.
+ *
+ * @param context The initialized TEE context structure in which scope to
+ * open the session.
+ * @param sharedMem pointer to the shared memory structure to register.
+ *
+ * @return TEEC_SUCCESS The registration was successful.
+ * @return TEEC_ERROR_OUT_OF_MEMORY Memory exhaustion.
+ * @return TEEC_Result Something failed.
+ */
+TEEC_Result TEEC_RegisterSharedMemory(TEEC_Context *context,
+ TEEC_SharedMemory *sharedMem);
+
+/**
+ * TEEC_AllocateSharedMemory() - Allocate shared memory for TEE.
+ *
+ * @param context The initialized TEE context structure in which scope to
+ * open the session.
+ * @param sharedMem Pointer to the allocated shared memory.
+ *
+ * @return TEEC_SUCCESS The registration was successful.
+ * @return TEEC_ERROR_OUT_OF_MEMORY Memory exhaustion.
+ * @return TEEC_Result Something failed.
+ */
+TEEC_Result TEEC_AllocateSharedMemory(TEEC_Context *context,
+ TEEC_SharedMemory *sharedMem);
+
+/**
+ * TEEC_ReleaseSharedMemory() - Free or deregister the shared memory.
+ *
+ * @param sharedMem Pointer to the shared memory to be freed.
+ */
+void TEEC_ReleaseSharedMemory(TEEC_SharedMemory *sharedMemory);
+
+#if 0
+/**
+ * TEEC_RequestCancellation() - Request the cancellation of a pending open
+ * session or command invocation.
+ *
+ * @param operation Pointer to an operation previously passed to open session
+ * or invoke.
+ */
+void TEEC_RequestCancellation(TEEC_Operation *operation);
+#endif
+
+#endif