OP-TEE: add optee driver from GitHub: optee_linuxdriver

Commit 4136b9d5a139(Fix TEESMC{32,64}_FASTCALL_RETURN_FROM_RPC)

Change-Id: I389e4f79270e3bc6e8844ec81758f8b5546192a1
Signed-off-by: Zhang Zhijie <zhangzj@rock-chips.com>
This commit is contained in:
Zhang Zhijie
2015-10-20 10:47:31 +08:00
parent a1fd352edd
commit 8e4b2eda3f
42 changed files with 9308 additions and 0 deletions

View File

@@ -0,0 +1,20 @@
#
# Copyright (c) 2014, STMicroelectronics International N.V.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License Version 2 as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# Trursted Execution Environment Configuration
config TEE_SUPPORT
bool "Trusted Execution Environment Support"
default y
---help---
This implements the Trusted Execution Environment (TEE) Client
API Specification from GlobalPlatform Device Technology.

View File

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

View File

@@ -0,0 +1,2 @@
obj-y += core/
obj-y += armtz/

View File

@@ -0,0 +1,4 @@
To avoid duplicating information we link to the Notice.md file in optee_os which
states the contributor agreement rules etc, i.e, optee_linuxdriver follows what
is written in the Notice.md on the URL below
https://github.com/OP-TEE/optee_os/blob/master/Notice.md

View File

@@ -0,0 +1,91 @@
# OP-TEE Linux Driver
The optee_linuxdriver git, containing the source code for the TEE driver
module in Linux.
It is distributed under the GPLv2 open-source license. For a general
overview of OP-TEE, please see the [Notice.md](Notice.md) file.
In this git, the module to build is optee.ko.
It allows communication between the Rich OS Client Application (unsecure
world), the Trusted OS (secure world) and the tee-supplicant (unsecure
world) which is a daemon serving the Trusted OS in secure world with
miscellaneous features, such as file system access.
## License
The software is provided under the
[GPL-2.0](http://opensource.org/licenses/GPL-2.0) license.
## Platforms supported
This software has hardware dependencies.
The software has been tested using:
- STMicroelectronics b2020-h416 (orly-2) hardware (32-bits)
- Some initial testing has been done using
[Foundation FVP](http://www.arm.com/fvp), which can be downloaded free of
charge.
## Get and build the software
### Get the compiler
We will strive to use the latest available compiler from Linaro. Start by
downloading and unpacking the compiler. Then export the PATH to the bin folder.
$ cd $HOME
$ mkdir toolchains
$ cd toolchains
$ wget http://releases.linaro.org/14.05/components/toolchain/binaries/gcc-linaro-arm-linux-gnueabihf-4.9-2014.05_linux.tar.xz
$ tar xvf gcc-linaro-arm-linux-gnueabihf-4.9-2014.05_linux.tar.xz
$ export PATH=$HOME/toolchains/gcc-linaro-arm-linux-gnueabihf-4.9-2014.05_linux/bin:$PATH
### Get the Linux kernel (from www.kernel.org)
$ cd $HOME
$ mkdir devel
$ cd devel
$ tar xf linux-3.10.32.tar.xz
$ mv linux-3.10.32 linux
### Download the source code
$ cd $HOME
$ cd devel
$ git clone https://github.com/OP-TEE/optee_linuxdriver.git
### Build
$ cd $HOME/devel/linux
$ make -j3 ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- mrproper
$ make -j3 ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- defconfig
$ make -j3 ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- all
$ make -j3 ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- M=$HOME/devel/optee_linuxdriver modules
#### Compiler flags
To be able to see the full command when building you could build using following
flag:
`$ make V=1`
## Coding standards
In this project we are trying to adhere to the same coding convention as used in
the Linux kernel (see
[CodingStyle](https://www.kernel.org/doc/Documentation/CodingStyle)). We achieve this by running
[checkpatch](http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/scripts/checkpatch.pl) from Linux kernel.
However there are a few exceptions that we had to make since the code also
follows GlobalPlatform standards. The exceptions are as follows:
- CamelCase for GlobalPlatform types are allowed.
- And we also exclude checking third party code that we might use in this
project, such as LibTomCrypt, MPA, newlib (not in this particular git, but
those are also part of the complete TEE solution). The reason for excluding
and not fixing third party code is because we would probably deviate too much
from upstream and therefore it would be hard to rebase against those projects
later on (and we don't expect that it is easy to convince other software
projects to change coding style).
### checkpatch
Since checkpatch is licensed under the terms of GNU GPL License Version 2, we
cannot include this script directly into this project. Therefore we have
written the Makefile so you need to explicitly point to the script by exporting
an environment variable, namely CHECKPATCH. So, suppose that the source code for
the Linux kernel is at `$HOME/devel/linux`, then you have to export like follows:
$ export CHECKPATCH=$HOME/devel/linux/scripts/checkpatch.pl
thereafter it should be possible to use one of the different checkpatch targets
in the [Makefile](Makefile). There are targets for checking all files, checking
against latest commit, against a certain base-commit etc. For the details, read
the [Makefile](Makefile).

View File

@@ -0,0 +1,40 @@
#########################################################################
# Set Internal Variables #
# May be modified to match your setup #
#########################################################################
CFG_TEE_DRV_DEBUGFS?=0
CFG_TEE_CORE_LOG_LEVEL?=2
CFG_TEE_TA_LOG_LEVEL?=2
ccflags-y+=-Werror
ccflags-y+=-I$(M)/include/arm_common
ccflags-y+=-I$(M)/include/linux
ccflags-y+=-I$(M)/include
ccflags-y+=-I$(M)/core
ccflags-y+=-DCFG_TEE_DRV_DEBUGFS=${CFG_TEE_DRV_DEBUGFS}
ccflags-y+=-DCFG_TEE_CORE_LOG_LEVEL=${CFG_TEE_CORE_LOG_LEVEL}
ccflags-y+=-DCFG_TEE_TA_LOG_LEVEL=${CFG_TEE_TA_LOG_LEVEL}
obj-m += optee_armtz.o
optee_armtz-objs:= \
tee_tz_drv.o \
tee_mem.o \
handle.o
ifeq ($(CONFIG_ARM),y)
# "smc" assembly intruction requires dedicated "armv7 secure extension"
secext := $(call as-instr,.arch_extension sec,+sec)
AFLAGS_tee_smc-arm.o := -Wa,-march=armv7-a$(secext)
optee_armtz-objs += \
tee_smc-arm.o
endif
ifeq ($(CONFIG_ARM64),y)
optee_armtz-objs += \
tee_smc-arm64.o
endif

View File

@@ -0,0 +1,87 @@
/*
* Copyright (c) 2014, Linaro Limited
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License Version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/slab.h>
#include <linux/string.h>
#include "handle.h"
/*
* Define the initial capacity of the database. It should be a low number
* multiple of 2 since some databases a likely to only use a few handles.
* Since the algorithm is to doubles up when growing it shouldn't cause a
* noticable overhead on large databases.
*/
#define HANDLE_DB_INITIAL_MAX_PTRS 4
void handle_db_destroy(struct handle_db *db)
{
if (db) {
kfree(db->ptrs);
db->ptrs = NULL;
db->max_ptrs = 0;
}
}
int handle_get(struct handle_db *db, void *ptr)
{
unsigned n;
void *p;
unsigned new_max_ptrs;
if (!db || !ptr)
return -1;
/* Try to find an empty location */
for (n = 0; n < db->max_ptrs; n++) {
if (!db->ptrs[n]) {
db->ptrs[n] = ptr;
return n;
}
}
/* No location available, grow the ptrs array */
if (db->max_ptrs)
new_max_ptrs = db->max_ptrs * 2;
else
new_max_ptrs = HANDLE_DB_INITIAL_MAX_PTRS;
p = krealloc(db->ptrs, new_max_ptrs * sizeof(void *), GFP_KERNEL);
if (!p)
return -1;
db->ptrs = p;
memset(db->ptrs + db->max_ptrs, 0,
(new_max_ptrs - db->max_ptrs) * sizeof(void *));
db->max_ptrs = new_max_ptrs;
/* Since n stopped at db->max_ptrs there is an empty location there */
db->ptrs[n] = ptr;
return n;
}
void *handle_put(struct handle_db *db, int handle)
{
void *p;
if (!db || handle < 0 || (unsigned)handle >= db->max_ptrs)
return NULL;
p = db->ptrs[handle];
db->ptrs[handle] = NULL;
return p;
}
void *handle_lookup(struct handle_db *db, int handle)
{
if (!db || handle < 0 || (unsigned)handle >= db->max_ptrs)
return NULL;
return db->ptrs[handle];
}

View File

@@ -0,0 +1,52 @@
/*
* Copyright (c) 2014, Linaro Limited
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License Version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef HANDLE_H
#define HANDLE_H
struct handle_db {
void **ptrs;
unsigned max_ptrs;
};
#define HANDLE_DB_INITIALIZER { NULL, 0 }
/*
* Frees all internal data structures of the database, but does not free
* the db pointer. The database is safe to reuse after it's destroyed, it
* just be empty again.
*/
void handle_db_destroy(struct handle_db *db);
/*
* Allocates a new handle and assigns the supplied pointer to it,
* ptr must not be NULL.
* The function returns
* >= 0 on success and
* -1 on failure
*/
int handle_get(struct handle_db *db, void *ptr);
/*
* Deallocates a handle. Returns the associated pointer of the handle
* the the handle was valid or NULL if it's invalid.
*/
void *handle_put(struct handle_db *db, int handle);
/*
* Returns the associated pointer of the handle if the handle is a valid
* handle.
* Returns NULL on failure.
*/
void *handle_lookup(struct handle_db *db, int handle);
#endif /*HANDLE_H*/

View File

@@ -0,0 +1,675 @@
/*
* Copyright (c) 2014, STMicroelectronics International N.V.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License Version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
/**
* \file tee_mem.c
* \brief Functions to manage a pool of memory chunks.
*
* This module provides basic functions to manage dynamically a fixed amount
* of memory (memory region). For this current implementation the provided
* memory region used for the allocations should be physically AND logically
* contiguous (only one region is supported for a given allocator).
*
* Principle of the allocator algorithm: "best fit"
*
*/
#include <linux/slab.h>
#include "tee_mem.h"
#define _DUMP_INFO_ALLOCATOR 0
#define USE_DEVM_ALLOC 1
#ifndef USE_DEVM_ALLOC
#define _KMALLOC(s, f) kmalloc(s, f)
#define _KFREE(a) kfree(a)
#else
#define _KMALLOC(s, f) devm_kzalloc(dev, s, f)
#define _KFREE(a) devm_kfree(dev, a)
#endif
/**
* \struct mem_chunk
* \brief Elementary descriptor of an allocated memory block
*
* \param node Node for linked list
* \param counter Reference counter
* (0 indicates that the block is not used/freed)
* \param size Total size in bytes
* \param paddr Physical base address
*
* Elementary memory block definition
*/
struct mem_chunk {
struct list_head node;
uint32_t counter;
size_t size;
unsigned long paddr;
};
/**
* \struct shm_pool
* \brief Main structure to describe a shared memory pool
*
* \param size Total size in bytes of the associated memory region
* \param vaddr Logical base address
* \param paddr Physical base address
* \param used Total size in bytes of the used memory
* \param mchunks List head for handle the elementary memory blocks
*
* Shared memory pool structure definition
*/
struct shm_pool {
struct mutex lock;
size_t size; /* Size of pool/heap memory segment */
size_t used; /* Number of bytes allocated */
void *vaddr; /* Associated Virtual address */
unsigned long paddr; /* Associated Physical address */
bool cached; /* true if pool is cacheable */
struct list_head mchunks; /* Head of memory chunk/block list */
};
#define __CALCULATE_RATIO_MEM_USED(a) (((a->used)*100)/(a->size))
/**
* \brief Dumps the information of the shared memory pool
*
* \param pool Pointer on the pool
* \param detailforced Flag to force the log for the detailed informations
*
* Dump/log the meta data of the shared memory pool on the standard output.
*
*/
void tee_shm_pool_dump(struct device *dev, struct shm_pool *pool, bool forced)
{
struct mem_chunk *chunk;
if (WARN_ON(!dev || !pool))
return;
dev_info(dev,
"tee_shm_pool_dump() poolH(0x%p) pAddr=0x%p vAddr=0x%p size=%zu used=%zu(%zu%%)\n",
(void *)pool,
(void *)pool->paddr,
(void *)pool->vaddr,
pool->size, pool->used, __CALCULATE_RATIO_MEM_USED(pool));
if ((pool->used != 0) || (forced == true)) {
dev_info(dev, " \\ HEAD next:[0x%p] prev:[0x%p]\n",
(void *)pool->mchunks.next,
(void *)pool->mchunks.prev);
dev_info(dev,
" |-[@] next prev pAddr size refCount\n");
list_for_each_entry(chunk, &pool->mchunks, node) {
dev_info(dev, " | [0x%p] 0x%p 0x%p 0x%p %08zu %d\n",
(void *)chunk,
(void *)chunk->node.next,
(void *)chunk->node.prev,
(void *)chunk->paddr,
chunk->size, chunk->counter);
}
}
}
bool tee_shm_pool_is_cached(struct shm_pool *pool)
{
return pool->cached;
}
void tee_shm_pool_set_cached(struct shm_pool *pool)
{
pool->cached = true;
}
/**
* \brief Creates and returns a new shared memory pool manager structure
*
* \param shm_size Size of the associated memory chunk
* \param shm_vaddr Virtual/logical base address
* \param shm_paddr Physical base address
*
* \return Reference of the created shared memory pool manager
*
* Create and initialize a shared memory pool manager.
* The description of the memory region (shm_size, shm_vaddr, shm_paddr)
* which is passed should be a physically AND virtually contiguous
* (no check is performed by the function).
* If a error is detected returned pool is NULL.
*/
struct shm_pool *tee_shm_pool_create(struct device *dev, size_t shm_size,
void *shm_vaddr, unsigned long shm_paddr)
{
struct mem_chunk *chunk = NULL;
struct shm_pool *pool = NULL;
if (WARN_ON(!dev))
goto alloc_failed;
dev_dbg(dev, "> vaddr=0x%p, paddr=0x%p, size=%zuKiB\n",
shm_vaddr, (void *)shm_paddr, shm_size / 1024);
/* Alloc and initialize the shm_pool structure */
pool = _KMALLOC(sizeof(struct shm_pool), GFP_KERNEL);
if (!pool) {
dev_err(dev, "kmalloc <struct shm_pool> failed\n");
goto alloc_failed;
}
memset(pool, 0, sizeof(*pool));
mutex_init(&pool->lock);
mutex_lock(&pool->lock);
INIT_LIST_HEAD(&(pool->mchunks));
pool->size = shm_size;
pool->vaddr = shm_vaddr;
pool->paddr = shm_paddr;
/* Create the initial elementary memory chunk */
/* which handles the whole memory region */
chunk = _KMALLOC(sizeof(struct mem_chunk), GFP_KERNEL);
if (!chunk) {
dev_err(dev, "kmalloc <struct MemChunk> failed\n");
goto alloc_failed;
}
memset(chunk, 0, sizeof(*chunk));
chunk->paddr = shm_paddr;
chunk->size = shm_size;
/* Adds the new entry immediately after the list head */
list_add(&(chunk->node), &(pool->mchunks));
#if defined(_DUMP_INFO_ALLOCATOR) && (_DUMP_INFO_ALLOCATOR > 0)
tee_shm_pool_dump(dev, pool, true);
#endif
dev_dbg(dev, "< poolH(0x%p) chunkH=0x%p\n",
(void *)(pool), (void *)chunk);
mutex_unlock(&pool->lock);
return pool;
alloc_failed:
if (chunk)
_KFREE(chunk);
if (pool)
_KFREE(pool);
return NULL;
}
/**
* Local helper function to check that the physical address is valid
*/
static inline int is_valid_paddr(struct shm_pool *pool, unsigned long paddr)
{
return (paddr >= pool->paddr && paddr < (pool->paddr + pool->size));
}
/**
* Local helper function to check that the virtual address is valid
*/
static inline int is_valid_vaddr(struct shm_pool *pool, void *vaddr)
{
return (vaddr >= pool->vaddr && vaddr < (pool->vaddr + pool->size));
}
/**
* \brief Destroy the shared memory pool manager
*
* \param pool Pointer on the pool
*
* Destroy a memory pool manager
*
*/
void tee_shm_pool_destroy(struct device *dev, struct shm_pool *pool)
{
struct mem_chunk *chunk;
if (WARN_ON(!dev || !pool))
return;
dev_dbg(dev, "> poolH(0x%p)\n", (void *)pool);
#if defined(_DUMP_INFO_ALLOCATOR) && (_DUMP_INFO_ALLOCATOR > 0)
tee_shm_pool_dump(dev, pool, true);
#endif
tee_shm_pool_reset(dev, pool);
chunk = list_first_entry(&pool->mchunks, struct mem_chunk, node);
dev_dbg(dev, "free chunkH=0x%p\n", (void *)chunk);
_KFREE(chunk);
_KFREE(pool);
dev_dbg(dev, "<\n");
}
/**
* \brief Free all reserved chunk if any, and set pool at it initial state
*
* \param pool Pointer on the pool
*
*/
void tee_shm_pool_reset(struct device *dev, struct shm_pool *pool)
{
struct mem_chunk *chunk;
struct mem_chunk *tmp;
struct mem_chunk *first = NULL;
if (WARN_ON(!dev || !pool))
return;
mutex_lock(&pool->lock);
list_for_each_entry_safe(chunk, tmp, &pool->mchunks, node) {
if (first != NULL) {
dev_err(dev, "Free lost chunkH=0x%p\n", (void *)chunk);
list_del(&chunk->node);
_KFREE(chunk);
} else {
first = chunk;
}
}
first->counter = 0;
first->paddr = pool->paddr;
first->size = pool->size;
pool->used = 0;
mutex_unlock(&pool->lock);
}
/**
* \brief Return the logical address
*
* \param pool Pointer on the pool
* \param paddr Physical address
*
* \return Virtual/logical address
*
* Return the associated virtual/logical address. The address should be inside
* the range of addresses managed by the shm pool.
*
*/
void *tee_shm_pool_p2v(struct device *dev, struct shm_pool *pool,
unsigned long paddr)
{
if (WARN_ON(!dev || !pool))
return NULL;
mutex_lock(&pool->lock);
if (!is_valid_paddr(pool, paddr)) {
mutex_unlock(&pool->lock);
dev_err(dev,
"tee_shm_pool_p2v() paddr=0x%p not in the shm pool\n",
(void *)paddr);
return NULL;
} else {
unsigned long offset = paddr - pool->paddr;
void *p = (void *)((unsigned long)pool->vaddr + offset);
mutex_unlock(&pool->lock);
return p;
}
}
/**
* \brief Return the physical address
*
* \param pool Pointer on the pool
* \param vaddr Logical/Virtual address
*
* \return Physical address
*
* Return the associated physical address. The address should be inside
* the range of addresses managed by the pool.
*
*/
unsigned long tee_shm_pool_v2p(struct device *dev, struct shm_pool *pool,
void *vaddr)
{
if (WARN_ON(!dev || !pool))
return 0UL;
mutex_lock(&pool->lock);
if (!is_valid_vaddr(pool, vaddr)) {
dev_err(dev,
"tee_shm_pool_v2p() vaddr=0x%p not in shm pool\n",
(void *)vaddr);
mutex_unlock(&pool->lock);
return 0UL;
} else {
unsigned long offset = vaddr - pool->vaddr;
unsigned long p = pool->paddr + offset;
mutex_unlock(&pool->lock);
return p;
}
}
/**
* \brief Allocate a new block of memory
*
* \param pool Pointer on the pool
* \param size Expected size (in byte)
* \param alignment Alignment constraint (in byte)
*
* \return Physical base address of the allocated block
*
* Allocate a memory chunk inside the memory region managed by the pool.
*
*/
unsigned long tee_shm_pool_alloc(struct device *dev,
struct shm_pool *pool,
size_t size, size_t alignment)
{
struct mem_chunk *chunk;
struct mem_chunk *betterchunk = NULL;
struct mem_chunk *prev_chunk = NULL;
struct mem_chunk *next_chunk = NULL;
unsigned long begAddr;
unsigned long endAddr;
if (WARN_ON(!dev || !pool))
return 0UL;
dev_dbg(dev, "> poolH(%p:%p:%x) size=0x%zx align=0x%zx\n",
pool, (void *)pool->paddr, (unsigned int)pool->size, size,
alignment);
/* Align on cache line of the target */
/* \todo(jmd) Should be defined by a global target specific parameter */
/* size = (size + (32-1)) & ~(32-1) */
if (ALIGN(size, 0x20) < size)
goto failed_out;
if (alignment == 0)
alignment = 1;
size = ALIGN(size, 0x20);
alignment = ALIGN(alignment, 0x20);
if (size > (pool->size - pool->used))
goto failed_out;
mutex_lock(&pool->lock);
/**
* Algorithm: Smallest waste (best fit): We choose the block that has the
* smallest waste. In other words we choose the block so that
* size(b) - size is as small as possible.
*/
list_for_each_entry(chunk, &pool->mchunks, node) {
if (chunk->counter == 0) { /* Free chunk */
begAddr = ALIGN(chunk->paddr, alignment);
endAddr = begAddr + size;
if (begAddr >= chunk->paddr
&& endAddr <= (chunk->paddr + chunk->size)
&& (betterchunk == NULL
/* Always split smaller block */
|| chunk->size < betterchunk->size))
betterchunk = chunk;
}
}
/**
* Update the linked list
*/
if (betterchunk != NULL) {
prev_chunk = _KMALLOC(sizeof(struct mem_chunk), GFP_KERNEL);
next_chunk = _KMALLOC(sizeof(struct mem_chunk), GFP_KERNEL);
if ((!prev_chunk) || (!next_chunk))
goto failed_out_unlock;
begAddr = ALIGN(betterchunk->paddr, alignment);
endAddr = begAddr + size;
if (betterchunk->paddr < begAddr) {
/* memory between begin of chunk and begin
* of created memory => create a free chunk */
prev_chunk->counter = 0;
prev_chunk->paddr = betterchunk->paddr;
prev_chunk->size = begAddr - betterchunk->paddr;
betterchunk->paddr = begAddr;
betterchunk->size -= prev_chunk->size;
dev_dbg(dev,
"create p_chunkH=0x%p paddr=0x%p (s=%zu)\n",
(void *)prev_chunk,
(void *)prev_chunk->paddr, prev_chunk->size);
list_add_tail(&(prev_chunk->node),
&(betterchunk->node));
prev_chunk = NULL;
} else {
_KFREE(prev_chunk);
}
if (betterchunk->paddr + betterchunk->size > endAddr) {
/* memory between end of chunk and end of
* created memory => create a free chunk */
next_chunk->counter = 0;
next_chunk->paddr = endAddr;
next_chunk->size = betterchunk->size - size;
dev_dbg(dev,
"create n_chunkH=0x%p paddr=0x%p (s=%zu)\n",
(void *)next_chunk,
(void *)next_chunk->paddr, next_chunk->size);
betterchunk->size = size;
list_add(&(next_chunk->node), &(betterchunk->node));
next_chunk = NULL;
} else {
_KFREE(next_chunk);
}
betterchunk->counter = 1;
pool->used += size;
mutex_unlock(&pool->lock);
#if defined(_DUMP_INFO_ALLOCATOR) && (_DUMP_INFO_ALLOCATOR > 1)
tee_shm_pool_dump(dev, pool, false);
#endif
dev_dbg(dev,
"< chunkH=0x%p paddr=%p (s=%zu) align=0x%zx\n",
(void *)betterchunk,
(void *)betterchunk->paddr,
betterchunk->size, alignment);
return betterchunk->paddr;
}
failed_out_unlock:
mutex_unlock(&pool->lock);
failed_out:
if (prev_chunk)
_KFREE(prev_chunk);
if (next_chunk)
_KFREE(next_chunk);
dev_err(dev,
"tee_shm_pool_alloc() FAILED, size=0x%zx, align=0x%zx free=%zu\n",
size, alignment, pool->size - pool->used);
#if defined(_DUMP_INFO_ALLOCATOR) && (_DUMP_INFO_ALLOCATOR > 1)
tee_shm_pool_dump(dev, pool, true);
#endif
return 0UL;
}
/**
* \brief Release a allocated block of memory
*
* \param pool Pointer on the pool
* \param paddr Physical @ of the block which must be released
* \param size Reference to return the size of the block
*
* Free a allocated memory block inside
* the memory region managed by the pool.
*
*/
int tee_shm_pool_free(struct device *dev, struct shm_pool *pool,
unsigned long paddr, size_t *size)
{
struct mem_chunk *chunk;
if (WARN_ON(!dev || !pool))
return -EINVAL;
dev_dbg(dev, "> Try to free ... poolH(0x%p) paddr=0x%p\n",
(void *)pool, (void *)paddr);
#if defined(_DUMP_INFO_ALLOCATOR) && (_DUMP_INFO_ALLOCATOR > 1)
tee_shm_pool_dump(dev, pool, false);
#endif
mutex_lock(&pool->lock);
if (!is_valid_paddr(pool, paddr))
goto out_failed;
list_for_each_entry(chunk, &pool->mchunks, node) {
if (chunk->paddr == paddr) {
if (size != NULL)
*size = chunk->size;
if (chunk->counter == 0) {
dev_warn(dev,
"< tee_shm_pool_free() WARNING, paddr=0x%p already released\n",
(void *)paddr);
return -EINVAL;
} else if (--chunk->counter == 0) {
dev_dbg(dev, "paddr=%p\n", (void *)paddr);
pool->used -= chunk->size;
/* Merge with previous */
if (chunk->node.prev != &pool->mchunks) {
struct mem_chunk *prev =
list_entry(chunk->node.prev,
struct mem_chunk, node);
if (prev->counter == 0) {
dev_dbg(dev,
"chunkH=0x%p paddr=0x%p free ok\n",
(void *)chunk,
(void *)paddr);
prev->size += chunk->size;
list_del(&chunk->node);
_KFREE(chunk);
chunk = prev;
}
}
/* Merge with next */
if (chunk->node.next != &pool->mchunks) {
struct mem_chunk *next =
list_entry(chunk->node.next,
struct mem_chunk, node);
if (next->counter == 0) {
dev_dbg(dev,
"chunkH=0x%p paddr=0x%p free ok\n",
(void *)chunk,
(void *)paddr);
chunk->size += next->size;
list_del(&next->node);
_KFREE(next);
}
}
mutex_unlock(&pool->lock);
#if defined(_DUMP_INFO_ALLOCATOR) && (_DUMP_INFO_ALLOCATOR > 1)
tee_shm_pool_dump(dev, pool, false);
#endif
dev_dbg(dev, "< freed\n");
return 0;
} else {
mutex_unlock(&pool->lock);
dev_dbg(dev,
"< paddr=0x%p (--) refcounter is decremented ret=1\n",
(void *)paddr);
return 1;
}
}
}
out_failed:
mutex_unlock(&pool->lock);
#if defined(_DUMP_INFO_ALLOCATOR) && (_DUMP_INFO_ALLOCATOR > 1)
tee_shm_pool_dump(dev, pool, false);
#endif
dev_err(dev,
"< tee_shm_pool_free() FAILED, pAddr=0x%p not found\n",
(void *)paddr);
return -EINVAL;
}
/**
* \brief Increase the reference count of the memory chunk
*
* \param pool Pointer on the pool
* \param paddr Physical address
*
* \return true if successful (false otherwise)
*
* Increment the reference count of the allocated block of memory.
* paddr should a valid address returned by the tee_shm_pool_alloc().
*
*/
bool tee_shm_pool_incref(struct device *dev, struct shm_pool *pool,
unsigned long paddr)
{
struct mem_chunk *chunk;
if (WARN_ON(!dev || !pool))
return false;
mutex_lock(&pool->lock);
if (!is_valid_paddr(pool, paddr))
goto out_failed;
list_for_each_entry(chunk, &pool->mchunks, node) {
if (chunk->paddr == paddr) {
dev_dbg(dev,
"pAddr=%p (++) refcounter is incremented\n",
(void *)paddr);
chunk->counter++;
#if defined(_DUMP_INFO_ALLOCATOR) && (_DUMP_INFO_ALLOCATOR > 0)
tee_shm_pool_dump(dev, pool, false);
#endif
mutex_unlock(&pool->lock);
return true;
}
}
out_failed:
mutex_unlock(&pool->lock);
dev_err(dev,
"tee_shm_pool_incref() FAILED, pAddr=%p is not a valid @\n",
(void *)paddr);
return false;
}

View File

@@ -0,0 +1,50 @@
/*
* Copyright (c) 2014, STMicroelectronics International N.V.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License Version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef TEE_MEM_H
#define TEE_MEM_H
#include <linux/types.h>
#include <linux/device.h>
struct shm_pool;
struct shm_pool *tee_shm_pool_create(struct device *dev, size_t shm_size,
void *shm_vaddr, unsigned long shm_paddr);
void tee_shm_pool_destroy(struct device *dev, struct shm_pool *pool);
void *tee_shm_pool_p2v(struct device *dev, struct shm_pool *pool,
unsigned long paddr);
unsigned long tee_shm_pool_v2p(struct device *dev, struct shm_pool *pool,
void *vaddr);
unsigned long tee_shm_pool_alloc(struct device *dev,
struct shm_pool *pool,
size_t size, size_t alignment);
int tee_shm_pool_free(struct device *dev, struct shm_pool *pool,
unsigned long paddr, size_t *size);
bool tee_shm_pool_incref(struct device *dev, struct shm_pool *pool,
unsigned long paddr);
void tee_shm_pool_dump(struct device *dev, struct shm_pool *pool, bool forced);
void tee_shm_pool_reset(struct device *dev, struct shm_pool *pool);
bool tee_shm_pool_is_cached(struct shm_pool *pool);
void tee_shm_pool_set_cached(struct shm_pool *pool);
#endif

View File

@@ -0,0 +1,29 @@
/*
* Copyright (c) 2014, STMicroelectronics International N.V.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License Version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/linkage.h>
.text
.balign 4
.code 32
/* void tee_smc_call(struct smc_param *param); */
.globl tee_smc_call
ENTRY(tee_smc_call)
push {r4-r8, lr}
mov r8, r0
ldm r8, {r0-r7}
.arch_extension sec
smc #0
stm r8, {r0-r7}
pop {r4-r8, pc}
ENDPROC(tee_smc_call)

View File

@@ -0,0 +1,36 @@
/*
* Copyright (c) 2014, Linaro Limited
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License Version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/linkage.h>
.text
#define SMC_PARAM_X0_OFFS 0
#define SMC_PARAM_X2_OFFS 16
#define SMC_PARAM_X4_OFFS 32
#define SMC_PARAM_X6_OFFS 48
/* void tee_smc_call(struct smc_param *param); */
.globl tee_smc_call
ENTRY(tee_smc_call)
stp x28, x30, [sp, #-16]!
mov x28, x0
ldp x0, x1, [x28, #SMC_PARAM_X0_OFFS]
ldp x2, x3, [x28, #SMC_PARAM_X2_OFFS]
ldp x4, x5, [x28, #SMC_PARAM_X4_OFFS]
ldp x6, x7, [x28, #SMC_PARAM_X6_OFFS]
smc #0
stp x0, x1, [x28, #SMC_PARAM_X0_OFFS]
stp x2, x3, [x28, #SMC_PARAM_X2_OFFS]
ldp x28, x30, [sp], #16
ret
ENDPROC(tee_smc_call)

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,270 @@
/*
* Copyright (c) 2014, STMicroelectronics International N.V.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License Version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef __TEE_ARMV7_OP_H__
#define __TEE_ARMV7_OP_H__
enum t_issw_service_id {
/*
* ("SSAPI_PRE_INIT_SERV")
*/
SSAPI_PRE_INIT_SERV = 1,
/*
* ("SSAPI_POST_SPEEDUP_INIT_SERV")
* Reserved, Not used
*/
SSAPI_POST_SPEEDUP_INIT_SERV = 2,
/*
* ("SSAPI_ISSW_IMPORT_SERV")
*/
SSAPI_ISSW_IMPORT_SERV = 3,
/*
* ("SSAPI_RET_FROM_INT_SERV")
*/
SSAPI_RET_FROM_INT_SERV = 4,
/*
* ("SSAPI_RET_FROM_RPC_SERV")
*/
SSAPI_RET_FROM_RPC_SERV = 5,
/*
* "ISSWAPI_ISSW_EXECUTE_SERV" is linked to ROM code
* ("SSAPI_ISSW_EXECUTE_SERV")
*/
ISSWAPI_ISSW_EXECUTE_SERV = 6,
ISSWAPI_PROT_APPL_MSG_SEND = 0x10000000,
ISSWAPI_EXTERNAL_CODE_CHECK = 0x10000001,
ISSWAPI_SECURE_LOAD = 0x10000002,
ISSWAPI_ISSW_REIMPORT_PUB_KEYS = 0x10000003,
/* Accessible only on request */
ISSWAPI_WRITE_L2CC = 0x10000004,
ISSWAPI_WRITE_CP15_SCTLR = 0x10000005,
ISSWAPI_READ_CP15_SCTLR = 0x10000006,
ISSWAPI_WRITE_CP15_ACTLR = 0x10000007,
ISSWAPI_READ_CP15_ACTLR = 0x10000008,
ISSWAPI_WRITE_CP15_DIAGR = 0x10000009,
ISSWAPI_READ_CP15_DIAGR = 0x1000000A,
ISSWAPI_EXECUTE_TA = 0x11000001,
ISSWAPI_CLOSE_TA = 0x11000002,
ISSWAPI_FLUSH_BOOT_CODE = 0x11000003,
/* Generic, restricted to be used by u-boot */
ISSWAPI_VERIFY_SIGNED_HEADER = 0x11000005,
ISSWAPI_VERIFY_HASH = 0x11000006,
/* 8500 only, restricted to be used by u-boot */
ISSWAPI_GET_RT_FLAGS = 0x11000007,
/* For TEE Client API 1.0 */
ISSWAPI_TEEC_OPEN_SESSION = 0x11000008,
ISSWAPI_TEEC_CLOSE_SESSION = 0x11000009,
ISSWAPI_TEEC_INVOKE_COMMAND = 0x1100000a,
ISSWAPI_REGISTER_RPC = 0x1100000b, /* this is NOT a GP TEE API ! */
ISSWAPI_SET_SEC_DDR = 0x1100000c, /* this is NOT a GP TEE API ! */
ISSWAPI_TEEC_CANCEL_COMMAND = 0x1100000d,
ISSWAPI_TEEC_REGISTER_MEMORY = 0x1100000e,
ISSWAPI_TEEC_UNREGISTER_MEMORY = 0x1100000f,
/* Internal command */
ISSWAPI_TEE_DEINIT_CPU = 0x11000010,
ISSWAPI_TEE_CRASH_CPU = 0x11000011,
ISSWAPI_TEE_SET_CORE_TRACE_LEVEL = 0x11000012,
ISSWAPI_TEE_GET_CORE_TRACE_LEVEL = 0x11000013,
ISSWAPI_TEE_SET_TA_TRACE_LEVEL = 0x11000014,
ISSWAPI_TEE_GET_TA_TRACE_LEVEL = 0x11000015,
ISSWAPI_TEE_GET_CORE_STATUS = 0x11000016,
ISSWAPI_TEE_FLUSH_CACHE = 0x11000017,
ISSWAPI_REGISTER_DEF_SHM = 0x11000020,
ISSWAPI_UNREGISTER_DEF_SHM = 0x11000021,
ISSWAPI_REGISTER_IRQFWD = 0x11000022,
ISSWAPI_UNREGISTER_IRQFWD = 0x11000023,
ISSWAPI_GET_SHM_START = 0x11000024,
ISSWAPI_GET_SHM_SIZE = 0x11000025,
ISSWAPI_GET_SHM_CACHED = 0x11000026,
ISSWAPI_ENABLE_L2CC_MUTEX = 0x20000000,
ISSWAPI_DISABLE_L2CC_MUTEX = 0x20000001,
ISSWAPI_GET_L2CC_MUTEX = 0x20000002,
ISSWAPI_SET_L2CC_MUTEX = 0x20000003,
ISSWAPI_LOAD_TEE = 0x20000004,
};
/*
* tee_msg_send - generic part of the msg sent to the TEE
*/
struct tee_msg_send {
unsigned int service;
};
/*
* tee_msg_recv - default strcutre of TEE service output message
*/
struct tee_msg_recv {
int duration;
uint32_t res;
uint32_t origin;
};
/*
* tee_register_irqfwd_xxx - (un)register callback for interrupt forwarding
*/
struct tee_register_irqfwd_send {
struct tee_msg_send header;
struct {
unsigned long cb;
} data;
};
struct tee_register_irqfwd_recv {
struct tee_msg_recv header;
};
/*
* tee_get_l2cc_mutex - input/output argument structures
*/
struct tee_get_l2cc_mutex_send {
struct tee_msg_send header;
};
struct tee_get_l2cc_mutex_recv {
struct tee_msg_recv header;
struct {
unsigned long paddr;
} data;
};
/**
* struct tee_identity - Represents the identity of the client
* @login: Login id
* @uuid: UUID as defined above
*/
struct tee_identity {
uint32_t login;
TEEC_UUID uuid;
};
/*
* tee_open_session_data - input arg structure for TEE open session service
*/
struct tee_open_session_data {
struct ta_signed_header_t *ta;
TEEC_UUID uuid;
uint32_t param_types;
TEEC_Value params[TEEC_CONFIG_PAYLOAD_REF_COUNT];
struct tee_identity client_id;
uint32_t params_flags[TEEC_CONFIG_PAYLOAD_REF_COUNT];
};
/*
* tee_open_session_send - input arg msg for TEE open session service
*/
struct tee_open_session_send {
struct tee_msg_send header;
struct tee_open_session_data data;
};
/*
* tee_open_session_recv - output arg structure for TEE open session service
*/
struct tee_open_session_recv {
struct tee_msg_recv header;
uint32_t sess;
TEEC_Value params[TEEC_CONFIG_PAYLOAD_REF_COUNT];
};
/*
* tee_invoke_command_data - input arg structure for TEE invoke cmd service
*/
struct tee_invoke_command_data {
uint32_t sess;
uint32_t cmd;
uint32_t param_types;
TEEC_Value params[TEEC_CONFIG_PAYLOAD_REF_COUNT];
uint32_t params_flags[TEEC_CONFIG_PAYLOAD_REF_COUNT];
};
struct tee_invoke_command_send {
struct tee_msg_send header;
struct tee_invoke_command_data data;
};
/*
* tee_invoke_command_recv - output arg structure for TEE invoke cmd service
*/
struct tee_invoke_command_recv {
struct tee_msg_recv header;
TEEC_Value params[TEEC_CONFIG_PAYLOAD_REF_COUNT];
};
/*
* tee_cancel_command_data - input arg structure for TEE cancel service
*/
struct tee_cancel_command_data {
uint32_t sess;
};
/*
* tee_cancel_command_send - input msg structure for TEE cancel service
*/
struct tee_cancel_command_send {
struct tee_msg_send header;
struct tee_cancel_command_data data;
};
/*
* tee_close_session_data - input arg structure for TEE close session service
*/
struct tee_close_session_data {
uint32_t sess;
};
/*
* tee_close_session_send - input arg msg for TEE close session service
*/
struct tee_close_session_send {
struct tee_msg_send header;
struct tee_close_session_data data;
};
/*
* tee_register_rpc_send_data - input arg structure for TEE register rpc service
*/
struct tee_register_rpc_send_data {
uint32_t fnk;
uint32_t bf;
uint32_t nbr_bf;
};
/*
* tee_register_rpc_send - input msg structure for TEE register rpc service
*/
struct tee_register_rpc_send {
struct tee_msg_send header;
struct tee_register_rpc_send_data data;
};
/*
* tee_core_status_out - output arg structure for TEE status service
*/
#define TEEC_STATUS_MSG_SIZE 80
struct tee_core_status_out {
struct tee_msg_recv header;
char raw[TEEC_STATUS_MSG_SIZE];
};
#endif /* __TEE_ARMV7_OP_H__ */

View File

@@ -0,0 +1,64 @@
/*
* Copyright (c) 2014, STMicroelectronics International N.V.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License Version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef __TEE_TZ_PRIV__
#define __TEE_TZ_PRIV__
struct tee;
struct shm_pool;
struct tee_rpc_bf;
#ifdef CONFIG_ARM
struct smc_param {
uint32_t a0;
uint32_t a1;
uint32_t a2;
uint32_t a3;
uint32_t a4;
uint32_t a5;
uint32_t a6;
uint32_t a7;
};
#endif
#ifdef CONFIG_ARM64
struct smc_param {
uint64_t a0;
uint64_t a1;
uint64_t a2;
uint64_t a3;
uint64_t a4;
uint64_t a5;
uint64_t a6;
uint64_t a7;
};
#endif
struct tee_tz {
uint32_t sess_id;
bool started;
struct tee *tee;
unsigned long shm_paddr;
void *shm_vaddr;
struct shm_pool *shm_pool;
struct mutex mutex;
struct completion c;
int c_waiters;
void *tz_outer_cache_mutex;
struct tee_rpc_bf *rpc_buffers;
bool shm_cached;
struct tee_mutex_wait_private mutex_wait;
struct tee_wait_queue_private wait_queue;
};
int tee_smc_call(struct smc_param *param);
#endif /* __TEE_TZ_PRIV__ */

View File

@@ -0,0 +1,33 @@
CFG_TEE_CORE_CORE_TARGET := armv7
#########################################################################
# Set Internal Variables #
# May be modified to match your setup #
#########################################################################
CFG_TEE_DRV_DEBUGFS?=0
CFG_TEE_CORE_LOG_LEVEL?=2
CFG_TEE_TA_LOG_LEVEL?=2
ccflags-y+=-Werror
ccflags-y+=-I$(M)/include/linux
ccflags-y+=-I$(M)/include
ccflags-y+=-DCFG_TEE_DRV_DEBUGFS=${CFG_TEE_DRV_DEBUGFS}
ccflags-y+=-DCFG_TEE_CORE_LOG_LEVEL=${CFG_TEE_CORE_LOG_LEVEL}
ccflags-y+=-DCFG_TEE_TA_LOG_LEVEL=${CFG_TEE_TA_LOG_LEVEL}
obj-m += optee.o
optee-objs:= \
tee_core.o \
tee_context.o \
tee_session.o \
tee_shm.o \
tee_supp_com.o \
tee_sysfs.o \
tee_debugfs.o \
tee_kernel_api.o \
tee_mutex_wait.o \
tee_wait_queue.o \

View File

@@ -0,0 +1,317 @@
/*
* Copyright (c) 2014, STMicroelectronics International N.V.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License Version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/slab.h>
#include <linux/device.h>
#include <linux/types.h>
#include <linux/file.h>
#include <linux/atomic.h>
#include <linux/uaccess.h>
#include <linux/sched.h>
#include "tee_shm.h"
#include "tee_core_priv.h"
/**
* tee_context_dump - Dump in a buffer the informations (ctx, sess & shm)
* associated to a tee.
*/
int tee_context_dump(struct tee *tee, char *buff, size_t len)
{
struct list_head *ptr_ctx, *ptr_sess, *ptr_shm;
struct tee_context *ctx;
struct tee_session *sess;
struct tee_shm *shm;
int i = 0;
int j = 0;
int pos = 0;
BUG_ON(!tee);
if (len < 80 || list_empty(&tee->list_ctx))
return 0;
mutex_lock(&tee->lock);
list_for_each(ptr_ctx, &tee->list_ctx) {
ctx = list_entry(ptr_ctx, struct tee_context, entry);
pos += sprintf(buff + pos,
"[%02d] ctx=%p (refcount=%d) (usr=%d)",
i, ctx,
(int)atomic_read(&ctx->refcount.
refcount),
ctx->usr_client);
pos += sprintf(buff + pos, "name=\"%s\" (tgid=%d)\n",
ctx->name,
ctx->tgid);
if ((len - pos) < 80) {
pos = 0;
goto out;
}
if (list_empty(&ctx->list_sess))
goto out;
j = 0;
list_for_each(ptr_sess, &ctx->list_sess) {
sess = list_entry(ptr_sess,
struct tee_session,
entry);
pos += sprintf(buff + pos,
"[%02d.%d] sess=%p sessid=%08x\n",
i, j, sess,
sess->sessid);
if ((len - pos) < 80) {
pos = 0;
goto out;
}
j++;
}
if (list_empty(&ctx->list_shm))
goto out;
j = 0;
list_for_each(ptr_shm, &ctx->list_shm) {
shm = list_entry(ptr_shm, struct tee_shm, entry);
pos += sprintf(buff + pos,
"[%02d.%d] shm=%p paddr=%p kaddr=%p",
i, j, shm,
&shm->paddr,
shm->kaddr);
pos += sprintf(buff + pos,
" s=%zu(%zu)\n",
shm->size_req,
shm->size_alloc);
if ((len - pos) < 80) {
pos = 0;
goto out;
}
j++;
}
i++;
}
out:
mutex_unlock(&tee->lock);
return pos;
}
/**
* tee_context_create - Allocate and create a new context.
* Reference on the back-end is requested.
*/
struct tee_context *tee_context_create(struct tee *tee)
{
int ret;
struct tee_context *ctx;
dev_dbg(_DEV(tee), "%s: >\n", __func__);
ctx = devm_kzalloc(_DEV(tee), sizeof(struct tee_context), GFP_KERNEL);
if (!ctx) {
dev_err(_DEV(tee), "%s: tee_context allocation failed\n",
__func__);
return ERR_PTR(-ENOMEM);
}
kref_init(&ctx->refcount);
INIT_LIST_HEAD(&ctx->list_sess);
INIT_LIST_HEAD(&ctx->list_shm);
ctx->tee = tee;
snprintf(ctx->name, sizeof(ctx->name), "%s", current->comm);
ctx->tgid = current->tgid;
ret = tee_get(tee);
if (ret) {
devm_kfree(_DEV(tee), ctx);
return ERR_PTR(ret);
}
mutex_lock(&tee->lock);
tee_inc_stats(&tee->stats[TEE_STATS_CONTEXT_IDX]);
list_add_tail(&ctx->entry, &tee->list_ctx);
mutex_unlock(&tee->lock);
dev_dbg(_DEV(ctx->tee), "%s: < ctx=%p is created\n", __func__, ctx);
return ctx;
}
/**
* _tee_context_do_release - Final function to release
* and free a context.
*/
static void _tee_context_do_release(struct kref *kref)
{
struct tee_context *ctx;
struct tee *tee;
ctx = container_of(kref, struct tee_context, refcount);
BUG_ON(!ctx || !ctx->tee);
tee = ctx->tee;
dev_dbg(_DEV(tee), "%s: > ctx=%p\n", __func__, ctx);
mutex_lock(&tee->lock);
tee_dec_stats(&tee->stats[TEE_STATS_CONTEXT_IDX]);
list_del(&ctx->entry);
mutex_unlock(&tee->lock);
devm_kfree(_DEV(tee), ctx);
tee_put(tee);
dev_dbg(_DEV(tee), "%s: < ctx=%p is destroyed\n", __func__, ctx);
}
/**
* tee_context_get - Increase the reference count of
* the context.
*/
void tee_context_get(struct tee_context *ctx)
{
BUG_ON(!ctx || !ctx->tee);
kref_get(&ctx->refcount);
dev_dbg(_DEV(ctx->tee), "%s: ctx=%p, kref=%d\n", __func__,
ctx, (int)atomic_read(&ctx->refcount.refcount));
}
static int is_in_list(struct tee *tee, struct list_head *entry)
{
int present = 1;
mutex_lock(&tee->lock);
if ((entry->next == LIST_POISON1) && (entry->prev == LIST_POISON2))
present = 0;
mutex_unlock(&tee->lock);
return present;
}
/**
* tee_context_put - Decreases the reference count of
* the context. If 0, the final
* release function is called.
*/
void tee_context_put(struct tee_context *ctx)
{
struct tee_context *_ctx = ctx;
struct tee *tee;
BUG_ON(!ctx || !ctx->tee);
tee = ctx->tee;
if (!is_in_list(tee, &ctx->entry))
return;
kref_put(&ctx->refcount, _tee_context_do_release);
dev_dbg(_DEV(tee), "%s: ctx=%p, kref=%d\n", __func__,
_ctx, (int)atomic_read(&ctx->refcount.refcount));
}
/**
* tee_context_destroy - Request to destroy a context.
*/
void tee_context_destroy(struct tee_context *ctx)
{
struct tee *tee;
if (!ctx || !ctx->tee)
return;
tee = ctx->tee;
dev_dbg(_DEV(tee), "%s: ctx=%p\n", __func__, ctx);
tee_context_put(ctx);
}
int tee_context_copy_from_client(const struct tee_context *ctx,
void *dest, const void *src, size_t size)
{
int res = 0;
if (dest && src && (size > 0)) {
if (ctx->usr_client)
res = copy_from_user(dest, src, size);
else
memcpy(dest, src, size);
}
return res;
}
struct tee_shm *tee_context_alloc_shm_tmp(struct tee_context *ctx,
size_t size, const void *src,
int type)
{
struct tee_shm *shm;
type &= (TEEC_MEM_INPUT | TEEC_MEM_OUTPUT);
shm = tee_shm_alloc(ctx->tee, size,
TEE_SHM_MAPPED | TEE_SHM_TEMP | type);
if (IS_ERR_OR_NULL(shm)) {
dev_err(_DEV(ctx->tee), "%s: buffer allocation failed (%ld)\n",
__func__, PTR_ERR(shm));
return shm;
}
shm->ctx = ctx;
if (type & TEEC_MEM_INPUT) {
if (tee_context_copy_from_client(ctx, shm->kaddr, src, size)) {
dev_err(_DEV(ctx->tee),
"%s: tee_context_copy_from_client failed\n",
__func__);
tee_shm_free(shm);
shm = NULL;
}
}
return shm;
}
struct tee_shm *tee_context_create_tmpref_buffer(struct tee_context *ctx,
size_t size,
const void *buffer, int type)
{
struct tee_shm *shm = NULL;
int flags;
switch (type) {
case TEEC_MEMREF_TEMP_OUTPUT:
flags = TEEC_MEM_OUTPUT;
break;
case TEEC_MEMREF_TEMP_INPUT:
flags = TEEC_MEM_INPUT;
break;
case TEEC_MEMREF_TEMP_INOUT:
flags = TEEC_MEM_INPUT | TEEC_MEM_OUTPUT;
break;
default:
BUG_ON(1);
};
shm = tee_context_alloc_shm_tmp(ctx, size, buffer, flags);
return shm;
}

View File

@@ -0,0 +1,549 @@
/*
* Copyright (c) 2014, STMicroelectronics International N.V.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License Version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
/* #define DEBUG */
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/cdev.h>
#include <linux/idr.h>
#include <linux/platform_device.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/file.h>
#include <linux/uaccess.h>
#include <asm-generic/ioctl.h>
#include <linux/sched.h>
#include "linux/tee_core.h"
#include "linux/tee_ioc.h"
#include "tee_core_priv.h"
#include "tee_sysfs.h"
#include "tee_debugfs.h"
#include "tee_shm.h"
#include "tee_supp_com.h"
#define _TEE_CORE_FW_VER "1:0.1"
static char *_tee_supp_app_name = "tee-supplicant";
/* Store the class misc reference */
static struct class *misc_class;
static int device_match(struct device *device, const void *devname)
{
struct tee *tee = dev_get_drvdata(device);
int ret = strncmp(devname, tee->name, sizeof(tee->name));
BUG_ON(!tee);
if (ret == 0)
return 1;
else
return 0;
}
/*
* For the kernel api.
* Get a reference on a device tee from the device needed
*/
struct tee *tee_get_tee(const char *devname)
{
struct device *device;
if (!devname)
return NULL;
device = class_find_device(misc_class, NULL, devname, device_match);
if (!device) {
pr_err("%s:%d - can't find device [%s]\n", __func__, __LINE__,
devname);
return NULL;
}
return dev_get_drvdata(device);
}
void tee_inc_stats(struct tee_stats_entry *entry)
{
entry->count++;
if (entry->count > entry->max)
entry->max = entry->count;
}
void tee_dec_stats(struct tee_stats_entry *entry)
{
entry->count--;
}
/**
* tee_get - increases refcount of the tee
* @tee: [in] tee to increase refcount of
*
* @note: If tee.ops.start() callback function is available,
* it is called when refcount is equal at 1.
*/
int tee_get(struct tee *tee)
{
int ret = 0;
BUG_ON(!tee);
if (atomic_inc_return(&tee->refcount) == 1) {
BUG_ON(!try_module_get(tee->ops->owner));
dev_dbg(_DEV(tee), "%s: refcount=1 call %s::start()...\n",
__func__, tee->name);
get_device(tee->dev);
if (tee->ops->start)
ret = tee->ops->start(tee);
}
if (ret) {
put_device(tee->dev);
module_put(tee->ops->owner);
dev_err(_DEV(tee), "%s: %s::start() failed, err=%d\n",
__func__, tee->name, ret);
atomic_dec(&tee->refcount);
} else {
int count = (int)atomic_read(&tee->refcount);
dev_dbg(_DEV(tee), "%s: refcount=%d\n", __func__, count);
if (count > tee->max_refcount)
tee->max_refcount = count;
}
return ret;
}
/**
* tee_put - decreases refcount of the tee
* @tee: [in] tee to reduce refcount of
*
* @note: If tee.ops.stop() callback function is available,
* it is called when refcount is equal at 0.
*/
int tee_put(struct tee *tee)
{
int ret = 0;
int count;
BUG_ON(!tee);
if (atomic_dec_and_test(&tee->refcount)) {
dev_dbg(_DEV(tee), "%s: refcount=0 call %s::stop()...\n",
__func__, tee->name);
if (tee->ops->stop)
ret = tee->ops->stop(tee);
module_put(tee->ops->owner);
put_device(tee->dev);
}
if (ret) {
dev_err(_DEV(tee), "%s: %s::stop() has failed, ret=%d\n",
__func__, tee->name, ret);
}
count = (int)atomic_read(&tee->refcount);
dev_dbg(_DEV(tee), "%s: refcount=%d\n", __func__, count);
return ret;
}
static int tee_supp_open(struct tee *tee)
{
int ret = 0;
dev_dbg(_DEV(tee), "%s: appclient=\"%s\" pid=%d\n", __func__,
current->comm, current->pid);
BUG_ON(!tee->rpc);
if (strncmp(_tee_supp_app_name, current->comm,
strlen(_tee_supp_app_name)) == 0) {
if (atomic_add_return(1, &tee->rpc->used) > 1) {
ret = -EBUSY;
dev_err(tee->dev, "%s: ERROR Only one Supplicant is allowed\n",
__func__);
atomic_sub(1, &tee->rpc->used);
}
}
return ret;
}
static void tee_supp_release(struct tee *tee)
{
dev_dbg(_DEV(tee), "%s: appclient=\"%s\" pid=%d\n", __func__,
current->comm, current->pid);
BUG_ON(!tee->rpc);
if ((atomic_read(&tee->rpc->used) == 1) &&
(strncmp(_tee_supp_app_name, current->comm,
strlen(_tee_supp_app_name)) == 0))
atomic_sub(1, &tee->rpc->used);
}
static int tee_ctx_open(struct inode *inode, struct file *filp)
{
struct tee_context *ctx;
struct tee *tee;
int ret;
tee = container_of(filp->private_data, struct tee, miscdev);
BUG_ON(!tee);
BUG_ON(tee->miscdev.minor != iminor(inode));
dev_dbg(_DEV(tee), "%s: > name=\"%s\"\n", __func__, tee->name);
ret = tee_supp_open(tee);
if (ret)
return ret;
ctx = tee_context_create(tee);
if (IS_ERR_OR_NULL(ctx))
return PTR_ERR(ctx);
ctx->usr_client = 1;
filp->private_data = ctx;
dev_dbg(_DEV(tee), "%s: < ctx=%p is created\n", __func__, (void *)ctx);
return 0;
}
static int tee_ctx_release(struct inode *inode, struct file *filp)
{
struct tee_context *ctx = filp->private_data;
struct tee *tee;
if (!ctx)
return -EINVAL;
BUG_ON(!ctx->tee);
tee = ctx->tee;
BUG_ON(tee->miscdev.minor != iminor(inode));
dev_dbg(_DEV(tee), "%s: > ctx=%p\n", __func__, ctx);
tee_context_destroy(ctx);
tee_supp_release(tee);
dev_dbg(_DEV(tee), "%s: < ctx=%p is destroyed\n", __func__, ctx);
return 0;
}
static int tee_do_create_session(struct tee_context *ctx,
struct tee_cmd_io __user *u_cmd)
{
int ret = -EINVAL;
struct tee_cmd_io k_cmd;
struct tee *tee;
tee = ctx->tee;
BUG_ON(!ctx->usr_client);
dev_dbg(_DEV(tee), "%s: >\n", __func__);
if (copy_from_user(&k_cmd, (void *)u_cmd, sizeof(struct tee_cmd_io))) {
dev_err(_DEV(tee), "%s: copy_from_user failed\n", __func__);
goto exit;
}
if (k_cmd.fd_sess > 0) {
dev_err(_DEV(tee), "%s: invalid fd_sess %d\n", __func__,
k_cmd.fd_sess);
goto exit;
}
if ((k_cmd.op == NULL) || (k_cmd.uuid == NULL) ||
((k_cmd.data != NULL) && (k_cmd.data_size == 0)) ||
((k_cmd.data == NULL) && (k_cmd.data_size != 0))) {
dev_err(_DEV(tee),
"%s: op or/and data parameters are not valid\n",
__func__);
goto exit;
}
ret = tee_session_create_fd(ctx, &k_cmd);
put_user(k_cmd.err, &u_cmd->err);
put_user(k_cmd.origin, &u_cmd->origin);
if (ret)
goto exit;
put_user(k_cmd.fd_sess, &u_cmd->fd_sess);
exit:
dev_dbg(_DEV(tee), "%s: < ret=%d, sessfd=%d\n", __func__, ret,
k_cmd.fd_sess);
return ret;
}
static int tee_do_shm_alloc(struct tee_context *ctx,
struct tee_shm_io __user *u_shm)
{
int ret = -EINVAL;
struct tee_shm_io k_shm;
struct tee *tee = ctx->tee;
BUG_ON(!ctx->usr_client);
dev_dbg(_DEV(tee), "%s: >\n", __func__);
if (copy_from_user(&k_shm, (void *)u_shm, sizeof(struct tee_shm_io))) {
dev_err(_DEV(tee), "%s: copy_from_user failed\n", __func__);
goto exit;
}
if ((k_shm.buffer != NULL) || (k_shm.fd_shm != 0) ||
/*(k_shm.flags & ~(tee->shm_flags)) ||*/
((k_shm.flags & tee->shm_flags) == 0) || (k_shm.registered != 0)) {
dev_err(_DEV(tee),
"%s: shm parameters are not valid %p %d %08x %08x %d\n",
__func__, (void *)k_shm.buffer, k_shm.fd_shm,
(unsigned int)k_shm.flags, (unsigned int)tee->shm_flags,
k_shm.registered);
goto exit;
}
ret = tee_shm_alloc_io(ctx, &k_shm);
if (ret)
goto exit;
put_user(k_shm.fd_shm, &u_shm->fd_shm);
put_user(k_shm.flags, &u_shm->flags);
exit:
dev_dbg(_DEV(tee), "%s: < ret=%d, shmfd=%d\n", __func__, ret,
k_shm.fd_shm);
return ret;
}
static int tee_do_get_fd_for_rpc_shm(struct tee_context *ctx,
struct tee_shm_io __user *u_shm)
{
int ret = -EINVAL;
struct tee_shm_io k_shm;
struct tee *tee = ctx->tee;
dev_dbg(_DEV(tee), "%s: >\n", __func__);
BUG_ON(!ctx->usr_client);
if (copy_from_user(&k_shm, (void *)u_shm, sizeof(struct tee_shm_io))) {
dev_err(_DEV(tee), "%s: copy_from_user failed\n", __func__);
goto exit;
}
if ((k_shm.buffer == NULL) || (k_shm.size == 0) || (k_shm.fd_shm != 0)
|| (k_shm.flags & ~(tee->shm_flags))
|| ((k_shm.flags & tee->shm_flags) == 0)
|| (k_shm.registered != 0)) {
dev_err(_DEV(tee), "%s: shm parameters are not valid\n",
__func__);
goto exit;
}
ret = tee_shm_fd_for_rpc(ctx, &k_shm);
if (ret)
goto exit;
put_user(k_shm.fd_shm, &u_shm->fd_shm);
exit:
dev_dbg(_DEV(tee), "%s: < ret=%d, shmfd=%d\n", __func__, ret,
k_shm.fd_shm);
return ret;
}
static long tee_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
int ret = -EINVAL;
struct tee_context *ctx = filp->private_data;
BUG_ON(!ctx);
BUG_ON(!ctx->tee);
dev_dbg(_DEV(ctx->tee), "%s: > cmd nr=%d\n", __func__, _IOC_NR(cmd));
switch (cmd) {
case TEE_OPEN_SESSION_IOC:
ret =
tee_do_create_session(ctx, (struct tee_cmd_io __user *)arg);
break;
case TEE_ALLOC_SHM_IOC:
ret = tee_do_shm_alloc(ctx, (struct tee_shm_io __user *)arg);
break;
case TEE_GET_FD_FOR_RPC_SHM_IOC:
ret =
tee_do_get_fd_for_rpc_shm(ctx,
(struct tee_shm_io __user *)arg);
break;
default:
ret = -ENOSYS;
break;
}
dev_dbg(_DEV(ctx->tee), "%s: < ret=%d\n", __func__, ret);
return ret;
}
const struct file_operations tee_fops = {
.owner = THIS_MODULE,
.read = tee_supp_read,
.write = tee_supp_write,
.open = tee_ctx_open,
.release = tee_ctx_release,
.unlocked_ioctl = tee_ioctl
};
static void tee_plt_device_release(struct device *dev)
{
pr_debug("%s: (dev=%p)....\n", __func__, dev);
}
struct tee *tee_core_alloc(struct device *dev, char *name, int id,
const struct tee_ops *ops, size_t len)
{
struct tee *tee;
if (!dev || !name || !ops ||
!ops->open || !ops->close || !ops->alloc || !ops->free)
return NULL;
tee = devm_kzalloc(dev, sizeof(struct tee) + len, GFP_KERNEL);
if (!tee) {
dev_err(dev, "%s: kzalloc failed\n", __func__);
return NULL;
}
if (!dev->release)
dev->release = tee_plt_device_release;
tee->dev = dev;
tee->id = id;
tee->ops = ops;
tee->priv = &tee[1];
snprintf(tee->name, sizeof(tee->name), "optee%s%02d", name, tee->id);
pr_info("TEE core: Alloc the misc device \"%s\" (id=%d)\n", tee->name,
tee->id);
tee->miscdev.parent = dev;
tee->miscdev.minor = MISC_DYNAMIC_MINOR;
tee->miscdev.name = tee->name;
tee->miscdev.fops = &tee_fops;
mutex_init(&tee->lock);
atomic_set(&tee->refcount, 0);
INIT_LIST_HEAD(&tee->list_ctx);
INIT_LIST_HEAD(&tee->list_rpc_shm);
tee->state = TEE_OFFLINE;
tee->shm_flags = TEEC_MEM_INPUT | TEEC_MEM_OUTPUT;
tee->test = 0;
tee_supp_init(tee);
return tee;
}
EXPORT_SYMBOL(tee_core_alloc);
int tee_core_free(struct tee *tee)
{
if (tee) {
tee_supp_deinit(tee);
devm_kfree(tee->dev, tee);
}
return 0;
}
EXPORT_SYMBOL(tee_core_free);
int tee_core_add(struct tee *tee)
{
int rc = 0;
if (!tee)
return -EINVAL;
rc = misc_register(&tee->miscdev);
if (rc != 0) {
pr_err("TEE Core: misc_register() failed name=\"%s\"\n",
tee->name);
return rc;
}
dev_set_drvdata(tee->miscdev.this_device, tee);
tee_init_sysfs(tee);
tee_create_debug_dir(tee);
/* Register a static reference on the class misc
* to allow finding device by class */
BUG_ON(!tee->miscdev.this_device->class);
if (misc_class)
BUG_ON(misc_class != tee->miscdev.this_device->class);
else
misc_class = tee->miscdev.this_device->class;
pr_info("TEE Core: Register the misc device \"%s\" (id=%d,minor=%d)\n",
dev_name(tee->miscdev.this_device), tee->id,
tee->miscdev.minor);
return rc;
}
EXPORT_SYMBOL(tee_core_add);
int tee_core_del(struct tee *tee)
{
if (tee) {
pr_info("TEE Core: Destroy the misc device \"%s\" (id=%d)\n",
dev_name(tee->miscdev.this_device), tee->id);
tee_cleanup_sysfs(tee);
tee_delete_debug_dir(tee);
if (tee->miscdev.minor != MISC_DYNAMIC_MINOR) {
pr_info("TEE Core: Deregister the misc device \"%s\" (id=%d)\n",
dev_name(tee->miscdev.this_device), tee->id);
misc_deregister(&tee->miscdev);
}
}
tee_core_free(tee);
return 0;
}
EXPORT_SYMBOL(tee_core_del);
static int __init tee_core_init(void)
{
pr_info("\nTEE Core Framework initialization (ver %s)\n",
_TEE_CORE_FW_VER);
tee_init_debugfs();
return 0;
}
static void __exit tee_core_exit(void)
{
tee_exit_debugfs();
pr_info("TEE Core Framework unregistered\n");
}
module_init(tee_core_init);
module_exit(tee_core_exit);
MODULE_AUTHOR("STMicroelectronics");
MODULE_DESCRIPTION("STM Secure TEE Framework/Core TEEC v1.0");
MODULE_SUPPORTED_DEVICE("");
MODULE_VERSION(_TEE_CORE_FW_VER);
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,52 @@
/*
* Copyright (c) 2014, STMicroelectronics International N.V.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License Version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef __TEE_CORE_PRIV_H__
#define __TEE_CORE_PRIV_H__
#include "linux/tee_core.h"
#include "linux/tee_ioc.h"
/* from tee_core_module.c */
int tee_get(struct tee *tee);
int tee_put(struct tee *tee);
void tee_inc_stats(struct tee_stats_entry *entry);
void tee_dec_stats(struct tee_stats_entry *entry);
/* from tee_context.c */
int tee_context_dump(struct tee *tee, char *buff, size_t len);
struct tee_context *tee_context_create(struct tee *tee);
void tee_context_destroy(struct tee_context *ctx);
void tee_context_get(struct tee_context *ctx);
void tee_context_put(struct tee_context *ctx);
struct tee_shm *tee_context_create_tmpref_buffer(struct tee_context *ctx,
size_t size,
const void *buffer, int type);
struct tee_shm *tee_context_alloc_shm_tmp(struct tee_context *ctx, size_t size,
const void *data, int type);
int tee_context_copy_from_client(const struct tee_context *ctx, void *dest,
const void *src, size_t size);
/* from tee_session.c */
int tee_session_create_fd(struct tee_context *ctx, struct tee_cmd_io *cmd_io);
struct tee_session *tee_session_create_and_open(struct tee_context *ctx,
struct tee_cmd_io *cmd_io);
int tee_session_close_and_destroy(struct tee_session *sess);
struct tee *tee_get_tee(const char *devname);
int tee_session_invoke_be(struct tee_session *sess, struct tee_cmd_io *cmd_io);
#endif

View File

@@ -0,0 +1,85 @@
/*
* Copyright (c) 2014, STMicroelectronics International N.V.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License Version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/kernel.h>
#include <linux/debugfs.h>
#include <linux/device.h>
#include <linux/uaccess.h>
#include "linux/tee_core.h"
#include "tee_debugfs.h"
static struct dentry *tee_debugfs_dir;
static ssize_t tee_trace_read(struct file *filp, char __user *userbuf,
size_t count, loff_t *ppos)
{
struct tee *tee = filp->private_data;
char buff[258];
int len = sprintf(buff, "device=%s\n NO LOG AVAILABLE\n", tee->name);
return simple_read_from_buffer(userbuf, count, ppos, buff, len);
}
static const struct file_operations log_tee_ops = {
.read = tee_trace_read,
.open = simple_open,
.llseek = generic_file_llseek,
};
void tee_create_debug_dir(struct tee *tee)
{
struct dentry *entry;
struct device *dev = tee->miscdev.this_device;
if (!tee_debugfs_dir)
return;
tee->dbg_dir = debugfs_create_dir(dev_name(dev), tee_debugfs_dir);
if (!tee->dbg_dir)
goto error_create_file;
entry = debugfs_create_file("log", S_IRUGO, tee->dbg_dir,
tee, &log_tee_ops);
if (!entry)
goto error_create_file;
return;
error_create_file:
dev_err(dev, "can't create debugfs file\n");
tee_delete_debug_dir(tee);
}
void tee_delete_debug_dir(struct tee *tee)
{
if (!tee || !tee->dbg_dir)
return;
debugfs_remove_recursive(tee->dbg_dir);
}
void __init tee_init_debugfs(void)
{
if (debugfs_initialized()) {
tee_debugfs_dir = debugfs_create_dir("tee", NULL);
if (IS_ERR(tee_debugfs_dir))
pr_err("can't create debugfs dir\n");
}
}
void __exit tee_exit_debugfs(void)
{
if (tee_debugfs_dir)
debugfs_remove(tee_debugfs_dir);
}

View File

@@ -0,0 +1,24 @@
/*
* Copyright (c) 2014, STMicroelectronics International N.V.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License Version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef __TEE_DEBUGFS_H__
#define __TEE_DEBUGFS_H__
struct tee;
void tee_create_debug_dir(struct tee *tee);
void tee_delete_debug_dir(struct tee *tee);
void __init tee_init_debugfs(void);
void __exit tee_exit_debugfs(void);
#endif /* __TEE_DEBUGFS_H__ */

View File

@@ -0,0 +1,255 @@
/*
* Copyright (c) 2014, STMicroelectronics International N.V.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License Version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/module.h>
#include <linux/device.h>
#include <linux/miscdevice.h>
#include <linux/io.h>
#include <linux/vmalloc.h>
#include "linux/tee_kernel_api.h"
#include "linux/tee_core.h"
#include "linux/tee_ioc.h"
#include "tee_core_priv.h"
#include "tee_shm.h"
#include "tee_supp_com.h"
#define TEE_TZ_DEVICE_NAME "opteearmtz00"
static void reset_tee_cmd(struct tee_cmd_io *cmd)
{
cmd->fd_sess = -1;
cmd->cmd = 0;
cmd->uuid = NULL;
cmd->origin = TEEC_ORIGIN_API;
cmd->err = TEEC_SUCCESS;
cmd->data = NULL;
cmd->data_size = 0;
cmd->op = NULL;
}
TEEC_Result TEEC_InitializeContext(const char *name, TEEC_Context *context)
{
struct tee *tee;
struct tee_context *ctx;
pr_cont("%s: > name=\"%s\"\n", __func__, name);
if (!context)
return TEEC_ERROR_BAD_PARAMETERS;
context->fd = 0;
if (name == NULL)
strncpy(context->devname, TEE_TZ_DEVICE_NAME,
sizeof(context->devname));
else
strncpy(context->devname, name, sizeof(context->devname));
tee = tee_get_tee(context->devname);
if (!tee) {
pr_err("%s - can't get device [%s]\n", __func__, name);
return TEEC_ERROR_BAD_PARAMETERS;
}
ctx = tee_context_create(tee);
if (IS_ERR_OR_NULL(ctx))
return TEEC_ERROR_BAD_PARAMETERS;
ctx->usr_client = 0;
/* TODO fixme will not work on 64-bit platform */
context->fd = (int)(uintptr_t)ctx;
BUG_ON(ctx != (struct tee_context *)(uintptr_t)context->fd);
pr_cont("%s: < ctx=%p is created\n", __func__, (void *)ctx);
return TEEC_SUCCESS;
}
EXPORT_SYMBOL(TEEC_InitializeContext);
void TEEC_FinalizeContext(TEEC_Context *context)
{
if (!context || !context->fd) {
pr_err("%s - can't release context %p:[%s]\n", __func__,
context, (context
&& context->devname) ? context->devname : "");
return;
}
/* TODO fixme will not work on 64-bit platform */
tee_context_destroy((struct tee_context *)(uintptr_t)context->fd);
return;
}
EXPORT_SYMBOL(TEEC_FinalizeContext);
TEEC_Result TEEC_OpenSession(TEEC_Context *context,
TEEC_Session *session,
const TEEC_UUID *destination,
uint32_t connectionMethod,
const void *connectionData,
TEEC_Operation *operation,
uint32_t *return_origin)
{
TEEC_Operation dummy_op;
struct tee_cmd_io cmd;
struct tee_session *sess;
struct tee_context *ctx;
if (!operation) {
/*
* The code here exist because Global Platform API states that
* it is allowed to give operation as a NULL pointer.
* In kernel and secure world we in most cases don't want
* this to be NULL, hence we use this dummy operation when
* a client doesn't provide any operation.
*/
memset(&dummy_op, 0, sizeof(TEEC_Operation));
operation = &dummy_op;
}
if (!context || !session || !destination || !operation
|| !return_origin)
return TEEC_ERROR_BAD_PARAMETERS;
session->fd = 0;
/* TODO fixme will not work on 64-bit platform */
ctx = (struct tee_context *)(uintptr_t)context->fd;
reset_tee_cmd(&cmd);
cmd.op = operation;
cmd.uuid = (TEEC_UUID *) destination;
sess = tee_session_create_and_open(ctx, &cmd);
if (IS_ERR_OR_NULL(sess)) {
if (cmd.origin)
*return_origin = cmd.origin;
else
*return_origin = TEEC_ORIGIN_COMMS;
if (cmd.err)
return cmd.err;
else
return TEEC_ERROR_COMMUNICATION;
} else {
*return_origin = cmd.origin;
/* TODO fixme will not work on 64-bit platform */
session->fd = (int)(uintptr_t)sess;
BUG_ON(sess != (struct tee_session *)(uintptr_t)session->fd);
return cmd.err;
}
}
EXPORT_SYMBOL(TEEC_OpenSession);
void TEEC_CloseSession(TEEC_Session *session)
{
if (session && session->fd) {
/* TODO fixme will not work on 64-bit platform */
struct tee_session *sess =
(struct tee_session *)(uintptr_t)session->fd;
tee_session_close_and_destroy(sess);
}
}
EXPORT_SYMBOL(TEEC_CloseSession);
TEEC_Result TEEC_InvokeCommand(TEEC_Session *session,
uint32_t commandID,
TEEC_Operation *operation,
uint32_t *return_origin)
{
int ret = 0;
struct tee_cmd_io cmd;
struct tee_session *sess;
if (!session || !operation || !return_origin || !session->fd)
return TEEC_ERROR_BAD_PARAMETERS;
/* TODO fixme will not work on 64-bit platform */
sess = (struct tee_session *)(uintptr_t)session->fd;
reset_tee_cmd(&cmd);
cmd.cmd = commandID;
cmd.op = operation;
ret = tee_session_invoke_be(sess, &cmd);
if (ret) {
if (cmd.origin)
*return_origin = cmd.origin;
else
*return_origin = TEEC_ORIGIN_COMMS;
if (cmd.err)
return cmd.err;
else
return TEEC_ERROR_COMMUNICATION;
} else {
*return_origin = cmd.origin;
return cmd.err;
}
}
EXPORT_SYMBOL(TEEC_InvokeCommand);
TEEC_Result TEEC_RegisterSharedMemory(TEEC_Context *context,
TEEC_SharedMemory *sharedMem)
{
if (!sharedMem)
return TEEC_ERROR_BAD_PARAMETERS;
sharedMem->registered = 1;
return TEEC_SUCCESS;
}
EXPORT_SYMBOL(TEEC_RegisterSharedMemory);
TEEC_Result TEEC_AllocateSharedMemory(TEEC_Context *context,
TEEC_SharedMemory *shared_memory)
{
struct tee_shm_io shm_io;
int ret;
struct tee_shm *shm;
if (!context || !context->ctx || !shared_memory)
return TEEC_ERROR_BAD_PARAMETERS;
shm_io.size = shared_memory->size;
shm_io.flags = shared_memory->flags | TEEC_MEM_KAPI;
ret = tee_shm_alloc_io(context->ctx, &shm_io);
if (ret) {
pr_err("%s: tee_shm_alloc_io(%zd) failed\n", __func__,
shared_memory->size);
return TEEC_ERROR_OUT_OF_MEMORY;
}
shared_memory->registered = 0;
shared_memory->flags = shm_io.flags;
shared_memory->d.fd = shm_io.fd_shm;
shm = (struct tee_shm *)(long)shm_io.fd_shm;
shared_memory->buffer = shm->kaddr;
pr_debug("%s(%zd) => fd=%d, kaddr=%p\n", __func__,
shm_io.size, shm_io.fd_shm, (void *)shared_memory->buffer);
return TEEC_SUCCESS;
}
EXPORT_SYMBOL(TEEC_AllocateSharedMemory);
void TEEC_ReleaseSharedMemory(TEEC_SharedMemory *shared_memory)
{
struct tee_shm *shm;
if (!shared_memory || shared_memory->registered)
return;
pr_debug("%s (vaddr = %p)\n", __func__, shared_memory->buffer);
shm = (struct tee_shm *)(long)shared_memory->d.fd;
tee_shm_free_io(shm);
shared_memory->buffer = NULL;
shared_memory->d.fd = 0;
}
EXPORT_SYMBOL(TEEC_ReleaseSharedMemory);

View File

@@ -0,0 +1,144 @@
/*
* Copyright (c) 2014, Linaro Limited
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License Version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/slab.h>
#include "tee_mutex_wait.h"
struct tee_mutex_wait {
struct list_head link;
struct completion comp;
struct mutex mu;
u32 wait_after;
u32 key;
};
/*
* Compares two serial numbers using Serial Number Arithmetic
* (https://www.ietf.org/rfc/rfc1982.txt).
*/
#define TICK_GT(t1, t2) \
(((t1) < (t2) && (t2) - (t1) > 0xFFFFFFFFu) || \
((t1) > (t2) && (t1) - (t2) < 0xFFFFFFFFu))
static struct tee_mutex_wait *tee_mutex_wait_get(struct device *dev,
struct tee_mutex_wait_private *priv, u32 key)
{
struct tee_mutex_wait *w;
mutex_lock(&priv->mu);
list_for_each_entry(w, &priv->db, link)
if (w->key == key)
goto out;
w = kmalloc(sizeof(struct tee_mutex_wait), GFP_KERNEL);
if (!w) {
dev_err(dev, "kmalloc <struct tee_mutex_wait> failed\n");
goto out;
}
init_completion(&w->comp);
mutex_init(&w->mu);
w->wait_after = 0;
w->key = key;
list_add_tail(&w->link, &priv->db);
out:
mutex_unlock(&priv->mu);
return w;
}
static void tee_mutex_wait_delete_entry(struct tee_mutex_wait *w)
{
list_del(&w->link);
mutex_destroy(&w->mu);
kfree(w);
}
void tee_mutex_wait_delete(struct device *dev,
struct tee_mutex_wait_private *priv,
u32 key)
{
struct tee_mutex_wait *w;
mutex_lock(&priv->mu);
list_for_each_entry(w, &priv->db, link) {
if (w->key == key) {
tee_mutex_wait_delete_entry(w);
break;
}
}
mutex_unlock(&priv->mu);
}
EXPORT_SYMBOL(tee_mutex_wait_delete);
void tee_mutex_wait_wakeup(struct device *dev,
struct tee_mutex_wait_private *priv,
u32 key, u32 wait_after)
{
struct tee_mutex_wait *w = tee_mutex_wait_get(dev, priv, key);
if (!w)
return;
mutex_lock(&w->mu);
w->wait_after = wait_after;
mutex_unlock(&w->mu);
complete(&w->comp);
}
EXPORT_SYMBOL(tee_mutex_wait_wakeup);
void tee_mutex_wait_sleep(struct device *dev,
struct tee_mutex_wait_private *priv,
u32 key, u32 wait_tick)
{
struct tee_mutex_wait *w = tee_mutex_wait_get(dev, priv, key);
u32 wait_after;
if (!w)
return;
mutex_lock(&w->mu);
wait_after = w->wait_after;
mutex_unlock(&w->mu);
if (TICK_GT(wait_tick, wait_after))
wait_for_completion_timeout(&w->comp, HZ);
}
EXPORT_SYMBOL(tee_mutex_wait_sleep);
int tee_mutex_wait_init(struct tee_mutex_wait_private *priv)
{
mutex_init(&priv->mu);
INIT_LIST_HEAD(&priv->db);
return 0;
}
EXPORT_SYMBOL(tee_mutex_wait_init);
void tee_mutex_wait_exit(struct tee_mutex_wait_private *priv)
{
/*
* It's the callers responibility to ensure that no one is using
* anything inside priv.
*/
mutex_destroy(&priv->mu);
while (!list_empty(&priv->db)) {
struct tee_mutex_wait *w =
list_first_entry(&priv->db,
struct tee_mutex_wait,
link);
tee_mutex_wait_delete_entry(w);
}
}
EXPORT_SYMBOL(tee_mutex_wait_exit);

View File

@@ -0,0 +1,38 @@
/*
* Copyright (c) 2014, Linaro Limited
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License Version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef TEE_MUTEX_WAIT_H
#define TEE_MUTEX_WAIT_H
#include <linux/mutex.h>
#include <linux/list.h>
#include <linux/device.h>
struct tee_mutex_wait_private {
struct mutex mu;
struct list_head db;
};
int tee_mutex_wait_init(struct tee_mutex_wait_private *priv);
void tee_mutex_wait_exit(struct tee_mutex_wait_private *priv);
void tee_mutex_wait_delete(struct device *dev,
struct tee_mutex_wait_private *priv,
u32 key);
void tee_mutex_wait_wakeup(struct device *dev,
struct tee_mutex_wait_private *priv,
u32 key, u32 wait_after);
void tee_mutex_wait_sleep(struct device *dev,
struct tee_mutex_wait_private *priv,
u32 key, u32 wait_tick);
#endif /*TEE_MUTEX_WAIT_H*/

View File

@@ -0,0 +1,852 @@
/*
* Copyright (c) 2014, STMicroelectronics International N.V.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License Version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/slab.h>
#include <linux/device.h>
#include <linux/types.h>
#include <linux/file.h>
#include <linux/atomic.h>
#include <linux/uaccess.h>
#include <linux/anon_inodes.h>
#include "tee_shm.h"
#include "tee_core_priv.h"
static int _init_tee_cmd(struct tee_session *sess, struct tee_cmd_io *cmd_io,
struct tee_cmd *cmd);
static void _update_client_tee_cmd(struct tee_session *sess,
struct tee_cmd_io *cmd_io,
struct tee_cmd *cmd);
static void _release_tee_cmd(struct tee_session *sess, struct tee_cmd *cmd);
#define _DEV_TEE _DEV(sess->ctx->tee)
#define INMSG dev_dbg(_DEV_TEE, "%s: >\n", __func__)
#define OUTMSG(val) dev_dbg(_DEV_TEE, "%s: < %d\n", __func__, (int)val)
/******************************************************************************/
static inline bool flag_set(int val, int flags)
{
return (val & flags) == flags;
}
static inline bool is_mapped_temp(int flags)
{
return flag_set(flags, TEE_SHM_MAPPED | TEE_SHM_TEMP);
}
/******************************************************************************/
#define _UUID_STR_SIZE 35
static char *_uuid_to_str(const TEEC_UUID *uuid)
{
static char uuid_str[_UUID_STR_SIZE];
if (uuid) {
sprintf(uuid_str,
"%08x-%04x-%04x-%02x%02x%02x%02x%02x%02x%02x%02x",
uuid->timeLow, uuid->timeMid, uuid->timeHiAndVersion,
uuid->clockSeqAndNode[0], uuid->clockSeqAndNode[1],
uuid->clockSeqAndNode[2], uuid->clockSeqAndNode[3],
uuid->clockSeqAndNode[4], uuid->clockSeqAndNode[5],
uuid->clockSeqAndNode[6], uuid->clockSeqAndNode[7]);
} else {
sprintf(uuid_str, "NULL");
}
return uuid_str;
}
static int tee_copy_from_user(struct tee_context *ctx, void *to, void *from,
size_t size)
{
if ((!to) || (!from) || (!size))
return 0;
if (ctx->usr_client)
return copy_from_user(to, from, size);
else {
memcpy(to, from, size);
return 0;
}
}
static int tee_copy_to_user(struct tee_context *ctx, void *to, void *from,
size_t size)
{
if ((!to) || (!from) || (!size))
return 0;
if (ctx->usr_client)
return copy_to_user(to, from, size);
else {
memcpy(to, from, size);
return 0;
}
}
/* Defined as macro to let the put_user macro see the types */
#define tee_put_user(ctx, from, to) \
do { \
if ((ctx)->usr_client) \
put_user(from, to); \
else \
*to = from; \
} while (0)
static inline int tee_session_is_opened(struct tee_session *sess)
{
if (sess && sess->sessid)
return (sess->sessid != 0);
return 0;
}
static int tee_session_open_be(struct tee_session *sess,
struct tee_cmd_io *cmd_io)
{
int ret = -EINVAL;
struct tee *tee;
struct tee_cmd cmd;
BUG_ON(!sess || !sess->ctx || !sess->ctx->tee);
tee = sess->ctx->tee;
dev_dbg(_DEV(tee), "%s: > open a new session", __func__);
sess->sessid = 0;
ret = _init_tee_cmd(sess, cmd_io, &cmd);
if (ret)
goto out;
if (cmd.uuid) {
dev_dbg(_DEV(tee), "%s: UUID=%s\n", __func__,
_uuid_to_str((TEEC_UUID *) cmd.uuid->kaddr));
}
ret = tee->ops->open(sess, &cmd);
if (ret == 0)
_update_client_tee_cmd(sess, cmd_io, &cmd);
else {
/* propagate the reason of the error */
cmd_io->origin = cmd.origin;
cmd_io->err = cmd.err;
}
out:
_release_tee_cmd(sess, &cmd);
dev_dbg(_DEV(tee), "%s: < ret=%d, sessid=%08x", __func__, ret,
sess->sessid);
return ret;
}
int tee_session_invoke_be(struct tee_session *sess, struct tee_cmd_io *cmd_io)
{
int ret = -EINVAL;
struct tee *tee;
struct tee_cmd cmd;
BUG_ON(!sess || !sess->ctx || !sess->ctx->tee);
tee = sess->ctx->tee;
dev_dbg(_DEV(tee), "%s: > sessid=%08x, cmd=0x%08x\n", __func__,
sess->sessid, cmd_io->cmd);
ret = _init_tee_cmd(sess, cmd_io, &cmd);
if (ret)
goto out;
ret = tee->ops->invoke(sess, &cmd);
if (!ret)
_update_client_tee_cmd(sess, cmd_io, &cmd);
else {
/* propagate the reason of the error */
cmd_io->origin = cmd.origin;
cmd_io->err = cmd.err;
}
out:
_release_tee_cmd(sess, &cmd);
dev_dbg(_DEV(tee), "%s: < ret=%d", __func__, ret);
return ret;
}
static int tee_session_close_be(struct tee_session *sess)
{
int ret = -EINVAL;
struct tee *tee;
BUG_ON(!sess || !sess->ctx || !sess->ctx->tee);
tee = sess->ctx->tee;
dev_dbg(_DEV(tee), "%s: > sessid=%08x", __func__, sess->sessid);
ret = tee->ops->close(sess);
sess->sessid = 0;
dev_dbg(_DEV(tee), "%s: < ret=%d", __func__, ret);
return ret;
}
static int tee_session_cancel_be(struct tee_session *sess,
struct tee_cmd_io *cmd_io)
{
int ret = -EINVAL;
struct tee *tee;
struct tee_cmd cmd;
BUG_ON(!sess || !sess->ctx || !sess->ctx->tee);
tee = sess->ctx->tee;
dev_dbg(_DEV(tee), "%s: > sessid=%08x, cmd=0x%08x\n", __func__,
sess->sessid, cmd_io->cmd);
ret = _init_tee_cmd(sess, cmd_io, &cmd);
if (ret)
goto out;
ret = tee->ops->cancel(sess, &cmd);
out:
_release_tee_cmd(sess, &cmd);
dev_dbg(_DEV(tee), "%s: < ret=%d", __func__, ret);
return ret;
}
static int tee_do_invoke_command(struct tee_session *sess,
struct tee_cmd_io __user *u_cmd)
{
int ret = -EINVAL;
struct tee *tee;
struct tee_cmd_io k_cmd;
struct tee_context *ctx;
BUG_ON(!sess->ctx);
BUG_ON(!sess->ctx->tee);
ctx = sess->ctx;
tee = sess->ctx->tee;
dev_dbg(_DEV(tee), "%s: > sessid=%08x\n", __func__, sess->sessid);
BUG_ON(!sess->sessid);
if (tee_copy_from_user
(ctx, &k_cmd, (void *)u_cmd, sizeof(struct tee_cmd_io))) {
dev_err(_DEV(tee), "%s: tee_copy_from_user failed\n", __func__);
goto exit;
}
if ((k_cmd.op == NULL) || (k_cmd.uuid != NULL) ||
(k_cmd.data != NULL) || (k_cmd.data_size != 0)) {
dev_err(_DEV(tee),
"%s: op or/and data parameters are not valid\n",
__func__);
goto exit;
}
ret = tee_session_invoke_be(sess, &k_cmd);
if (ret)
dev_err(_DEV(tee), "%s: tee_invoke_command failed\n", __func__);
tee_put_user(ctx, k_cmd.err, &u_cmd->err);
tee_put_user(ctx, k_cmd.origin, &u_cmd->origin);
exit:
dev_dbg(_DEV(tee), "%s: < ret=%d\n", __func__, ret);
return ret;
}
static int tee_do_cancel_cmd(struct tee_session *sess,
struct tee_cmd_io __user *u_cmd)
{
int ret = -EINVAL;
struct tee *tee;
struct tee_cmd_io k_cmd;
struct tee_context *ctx;
BUG_ON(!sess->ctx);
BUG_ON(!sess->ctx->tee);
ctx = sess->ctx;
tee = sess->ctx->tee;
dev_dbg(sess->ctx->tee->dev, "%s: > sessid=%08x\n", __func__,
sess->sessid);
BUG_ON(!sess->sessid);
if (tee_copy_from_user
(ctx, &k_cmd, (void *)u_cmd, sizeof(struct tee_cmd_io))) {
dev_err(_DEV(tee), "%s: tee_copy_from_user failed\n", __func__);
goto exit;
}
if ((k_cmd.op == NULL) || (k_cmd.uuid != NULL) ||
(k_cmd.data != NULL) || (k_cmd.data_size != 0)) {
dev_err(_DEV(tee),
"%s: op or/and data parameters are not valid\n",
__func__);
goto exit;
}
ret = tee_session_cancel_be(sess, &k_cmd);
if (ret)
dev_err(_DEV(tee), "%s: tee_invoke_command failed\n", __func__);
tee_put_user(ctx, k_cmd.err, &u_cmd->err);
tee_put_user(ctx, k_cmd.origin, &u_cmd->origin);
exit:
dev_dbg(_DEV(tee), "%s: < ret=%d", __func__, ret);
return ret;
}
static long tee_session_ioctl(struct file *filp, unsigned int cmd,
unsigned long arg)
{
struct tee *tee;
struct tee_session *sess = filp->private_data;
int ret;
BUG_ON(!sess || !sess->ctx || !sess->ctx->tee);
tee = sess->ctx->tee;
dev_dbg(_DEV(tee), "%s: > cmd nr=%d\n", __func__, _IOC_NR(cmd));
switch (cmd) {
case TEE_INVOKE_COMMAND_IOC:
ret =
tee_do_invoke_command(sess,
(struct tee_cmd_io __user *)arg);
break;
case TEE_REQUEST_CANCELLATION_IOC:
ret = tee_do_cancel_cmd(sess, (struct tee_cmd_io __user *)arg);
break;
default:
ret = -ENOSYS;
break;
}
dev_dbg(_DEV(tee), "%s: < ret=%d\n", __func__, ret);
return ret;
}
static int tee_session_release(struct inode *inode, struct file *filp)
{
struct tee_session *sess = filp->private_data;
int ret = 0;
struct tee *tee;
BUG_ON(!sess || !sess->ctx || !sess->ctx->tee);
tee = sess->ctx->tee;
ret = tee_session_close_and_destroy(sess);
return ret;
}
const struct file_operations tee_session_fops = {
.owner = THIS_MODULE,
.unlocked_ioctl = tee_session_ioctl,
.release = tee_session_release,
};
int tee_session_close_and_destroy(struct tee_session *sess)
{
int ret;
struct tee *tee;
struct tee_context *ctx;
if (!sess || !sess->ctx || !sess->ctx->tee)
return -EINVAL;
ctx = sess->ctx;
tee = ctx->tee;
dev_dbg(_DEV(tee), "%s: > sess=%p\n", __func__, sess);
if (!tee_session_is_opened(sess))
return -EINVAL;
ret = tee_session_close_be(sess);
mutex_lock(&sess->ctx->tee->lock);
tee_dec_stats(&tee->stats[TEE_STATS_SESSION_IDX]);
list_del(&sess->entry);
mutex_unlock(&sess->ctx->tee->lock);
devm_kfree(_DEV(tee), sess);
tee_context_put(ctx);
tee_put(tee);
dev_dbg(_DEV(tee), "%s: <\n", __func__);
return ret;
}
struct tee_session *tee_session_create_and_open(struct tee_context *ctx,
struct tee_cmd_io *cmd_io)
{
int ret = 0;
struct tee_session *sess;
struct tee *tee;
BUG_ON(!ctx->tee);
tee = ctx->tee;
dev_dbg(_DEV(tee), "%s: >\n", __func__);
ret = tee_get(tee);
if (ret)
return ERR_PTR(-EBUSY);
sess = devm_kzalloc(_DEV(tee), sizeof(struct tee_session), GFP_KERNEL);
if (!sess) {
dev_err(_DEV(tee), "%s: tee_session allocation() failed\n",
__func__);
tee_put(tee);
return ERR_PTR(-ENOMEM);
}
tee_context_get(ctx);
sess->ctx = ctx;
ret = tee_session_open_be(sess, cmd_io);
if (ret || !sess->sessid || cmd_io->err) {
dev_err(_DEV(tee), "%s: ERROR ret=%d (err=0x%08x, org=%d, sessid=0x%08x)\n",
__func__, ret, cmd_io->err,
cmd_io->origin, sess->sessid);
tee_put(tee);
tee_context_put(ctx);
devm_kfree(_DEV(tee), sess);
if (ret)
return ERR_PTR(ret);
else
return NULL;
}
mutex_lock(&tee->lock);
tee_inc_stats(&tee->stats[TEE_STATS_SESSION_IDX]);
list_add_tail(&sess->entry, &ctx->list_sess);
mutex_unlock(&tee->lock);
dev_dbg(_DEV(tee), "%s: < sess=%p\n", __func__, sess);
return sess;
}
int tee_session_create_fd(struct tee_context *ctx, struct tee_cmd_io *cmd_io)
{
int ret;
struct tee_session *sess;
struct tee *tee = ctx->tee;
BUG_ON(cmd_io->fd_sess > 0);
dev_dbg(_DEV(tee), "%s: >\n", __func__);
sess = tee_session_create_and_open(ctx, cmd_io);
if (IS_ERR_OR_NULL(sess)) {
ret = PTR_ERR(sess);
dev_dbg(_DEV(tee), "%s: ERROR can't create the session (ret=%d, err=0x%08x, org=%d)\n",
__func__, ret, cmd_io->err, cmd_io->origin);
cmd_io->fd_sess = -1;
goto out;
}
/* Retrieve a fd */
cmd_io->fd_sess = -1;
ret =
anon_inode_getfd("tee_session", &tee_session_fops, sess, O_CLOEXEC);
if (ret < 0) {
dev_err(_DEV(tee), "%s: ERROR can't get a fd (ret=%d)\n",
__func__, ret);
tee_session_close_and_destroy(sess);
goto out;
}
cmd_io->fd_sess = ret;
ret = 0;
out:
dev_dbg(_DEV(tee), "%s: < ret=%d, sess=%p, fd=%d\n", __func__,
ret, sess, cmd_io->fd_sess);
return ret;
}
static bool tee_session_is_supported_type(struct tee_session *sess, int type)
{
switch (type) {
case TEEC_NONE:
case TEEC_VALUE_INPUT:
case TEEC_VALUE_OUTPUT:
case TEEC_VALUE_INOUT:
case TEEC_MEMREF_TEMP_INPUT:
case TEEC_MEMREF_TEMP_OUTPUT:
case TEEC_MEMREF_TEMP_INOUT:
case TEEC_MEMREF_WHOLE:
case TEEC_MEMREF_PARTIAL_INPUT:
case TEEC_MEMREF_PARTIAL_OUTPUT:
case TEEC_MEMREF_PARTIAL_INOUT:
return true;
default:
dev_err(_DEV_TEE, "type is invalid (type %02x)\n", type);
return false;
}
}
static int to_memref_type(int flags)
{
if (flag_set(flags, TEEC_MEM_INPUT | TEEC_MEM_OUTPUT))
return TEEC_MEMREF_TEMP_INOUT;
if (flag_set(flags, TEEC_MEM_INPUT))
return TEEC_MEMREF_TEMP_INPUT;
if (flag_set(flags, TEEC_MEM_OUTPUT))
return TEEC_MEMREF_TEMP_OUTPUT;
pr_err("%s: bad flags=%x\n", __func__, flags);
return 0;
}
static int _init_tee_cmd(struct tee_session *sess, struct tee_cmd_io *cmd_io,
struct tee_cmd *cmd)
{
int ret = -EINVAL;
int idx;
TEEC_Operation op;
struct tee_data *param = &cmd->param;
struct tee *tee;
struct tee_context *ctx;
BUG_ON(!sess->ctx);
BUG_ON(!sess->ctx->tee);
ctx = sess->ctx;
tee = sess->ctx->tee;
dev_dbg(_DEV(tee), "%s: > sessid=%08x\n", __func__, sess->sessid);
memset(cmd, 0, sizeof(struct tee_cmd));
cmd->cmd = cmd_io->cmd;
cmd->origin = TEEC_ORIGIN_TEE;
cmd->err = TEEC_ERROR_BAD_PARAMETERS;
cmd_io->origin = cmd->origin;
cmd_io->err = cmd->err;
if (tee_context_copy_from_client(ctx, &op, cmd_io->op, sizeof(op)))
goto out;
cmd->param.type_original = op.paramTypes;
for (idx = 0; idx < TEEC_CONFIG_PAYLOAD_REF_COUNT; ++idx) {
uint32_t offset = 0;
uint32_t size = 0;
int type = TEEC_PARAM_TYPE_GET(op.paramTypes, idx);
switch (type) {
case TEEC_NONE:
break;
case TEEC_VALUE_INPUT:
case TEEC_VALUE_OUTPUT:
case TEEC_VALUE_INOUT:
param->params[idx].value = op.params[idx].value;
dev_dbg(_DEV_TEE,
"%s: param[%d]:type=%d,a=%08x,b=%08x (VALUE)\n",
__func__, idx, type, param->params[idx].value.a,
param->params[idx].value.b);
break;
case TEEC_MEMREF_TEMP_INPUT:
case TEEC_MEMREF_TEMP_OUTPUT:
case TEEC_MEMREF_TEMP_INOUT:
dev_dbg(_DEV_TEE,
"> param[%d]:type=%d,buffer=%p,s=%zu (TMPREF)\n",
idx, type, op.params[idx].tmpref.buffer,
op.params[idx].tmpref.size);
param->params[idx].shm =
tee_context_create_tmpref_buffer(ctx,
op.params[idx].tmpref.size,
op.params[idx].tmpref.buffer,
type);
if (IS_ERR_OR_NULL(param->params[idx].shm))
goto out;
dev_dbg(_DEV_TEE, "< %d %p:%zd\n", idx,
(void *)param->params[idx].shm->paddr,
param->params[idx].shm->size_alloc);
break;
case TEEC_MEMREF_PARTIAL_INPUT:
case TEEC_MEMREF_PARTIAL_OUTPUT:
case TEEC_MEMREF_PARTIAL_INOUT:
case TEEC_MEMREF_WHOLE:
if (tee_copy_from_user(ctx, &param->c_shm[idx],
op.params[idx].memref.parent,
sizeof(param->c_shm[idx]))) {
goto out;
}
if (type == TEEC_MEMREF_WHOLE) {
offset = 0;
size = param->c_shm[idx].size;
} else { /* for PARTIAL, check the size */
offset = op.params[idx].memref.offset;
size = op.params[idx].memref.size;
if (param->c_shm[idx].size < size + offset) {
dev_err(_DEV(tee), "A PARTIAL parameter is bigger than the parent %zd < %d + %d\n",
param->c_shm[idx].size, size,
offset);
goto out;
}
}
dev_dbg(_DEV_TEE, "> param[%d]:type=%d,buffer=%p, offset=%d size=%d\n",
idx, type, param->c_shm[idx].buffer,
offset, size);
type = to_memref_type(param->c_shm[idx].flags);
if (type == 0)
goto out;
param->params[idx].shm = tee_shm_get(ctx,
&param->c_shm[idx], size, offset);
if (IS_ERR_OR_NULL(param->params[idx].shm)) {
param->params[idx].shm =
tee_context_create_tmpref_buffer(ctx, size,
param->c_shm[idx].buffer + offset, type);
if (IS_ERR_OR_NULL(param->params[idx].shm))
goto out;
}
dev_dbg(_DEV_TEE, "< %d %p:%zd\n", idx,
(void *)param->params[idx].shm->paddr,
param->params[idx].shm->size_req);
break;
default:
BUG_ON(1);
}
param->type |= (type << (idx * 4));
}
if (cmd_io->uuid != NULL) {
dev_dbg(_DEV_TEE, "%s: copy UUID value...\n", __func__);
cmd->uuid = tee_context_alloc_shm_tmp(sess->ctx,
sizeof(*cmd_io->uuid), cmd_io->uuid, TEEC_MEM_INPUT);
if (IS_ERR_OR_NULL(cmd->uuid)) {
ret = -EINVAL;
goto out;
}
}
ret = 0;
out:
if (ret)
_release_tee_cmd(sess, cmd);
dev_dbg(_DEV_TEE, "%s: < ret=%d\n", __func__, ret);
return ret;
}
static void _update_client_tee_cmd(struct tee_session *sess,
struct tee_cmd_io *cmd_io,
struct tee_cmd *cmd)
{
int idx;
struct tee_context *ctx;
BUG_ON(!cmd_io);
BUG_ON(!cmd_io->op);
BUG_ON(!cmd_io->op->params);
BUG_ON(!cmd);
BUG_ON(!sess->ctx);
ctx = sess->ctx;
dev_dbg(_DEV_TEE, "%s: returned err=0x%08x (origin=%d)\n", __func__,
cmd->err, cmd->origin);
cmd_io->origin = cmd->origin;
cmd_io->err = cmd->err;
if (cmd->param.type_original == TEEC_PARAM_TYPES(TEEC_NONE,
TEEC_NONE, TEEC_NONE, TEEC_NONE))
return;
for (idx = 0; idx < TEEC_CONFIG_PAYLOAD_REF_COUNT; ++idx) {
int type = TEEC_PARAM_TYPE_GET(cmd->param.type_original, idx);
int offset = 0;
size_t size;
size_t size_new;
TEEC_SharedMemory *parent;
dev_dbg(_DEV_TEE, "%s: id %d type %d\n", __func__, idx, type);
BUG_ON(!tee_session_is_supported_type(sess, type));
switch (type) {
case TEEC_NONE:
case TEEC_VALUE_INPUT:
case TEEC_MEMREF_TEMP_INPUT:
case TEEC_MEMREF_PARTIAL_INPUT:
break;
case TEEC_VALUE_OUTPUT:
case TEEC_VALUE_INOUT:
dev_dbg(_DEV_TEE, "%s: a=%08x, b=%08x\n",
__func__,
cmd->param.params[idx].value.a,
cmd->param.params[idx].value.b);
if (tee_copy_to_user
(ctx, &cmd_io->op->params[idx].value,
&cmd->param.params[idx].value,
sizeof(cmd_io->op->params[idx].value)))
dev_err(_DEV_TEE,
"%s:%d: can't update %d result to user\n",
__func__, __LINE__, idx);
break;
case TEEC_MEMREF_TEMP_OUTPUT:
case TEEC_MEMREF_TEMP_INOUT:
/* Returned updated size */
size_new = cmd->param.params[idx].shm->size_req;
if (size_new !=
cmd_io->op->params[idx].tmpref.size) {
dev_dbg(_DEV_TEE,
"Size has been updated by the TA %zd != %zd\n",
size_new,
cmd_io->op->params[idx].tmpref.
size);
tee_put_user(ctx, size_new,
&cmd_io->op->params[idx].tmpref.size);
}
dev_dbg(_DEV_TEE, "%s: tmpref %p\n", __func__,
cmd->param.params[idx].shm->kaddr);
/* ensure we do not exceed the shared buffer length */
if (size_new > cmd_io->op->params[idx].tmpref.size)
dev_err(_DEV_TEE,
" *** Wrong returned size from %d:%zd > %zd\n",
idx, size_new,
cmd_io->op->params[idx].tmpref.
size);
else if (tee_copy_to_user
(ctx,
cmd_io->op->params[idx].tmpref.buffer,
cmd->param.params[idx].shm->kaddr,
size_new))
dev_err(_DEV_TEE,
"%s:%d: can't update %d result to user\n",
__func__, __LINE__, idx);
break;
case TEEC_MEMREF_PARTIAL_OUTPUT:
case TEEC_MEMREF_PARTIAL_INOUT:
case TEEC_MEMREF_WHOLE:
if (type == TEEC_MEMREF_WHOLE) {
offset = 0;
size = parent->size;
} else {
offset = cmd_io->op->params[idx].memref.offset;
size = cmd_io->op->params[idx].memref.size;
}
parent = &cmd->param.c_shm[idx];
/* Returned updated size */
size_new = cmd->param.params[idx].shm->size_req;
tee_put_user(ctx, size_new,
&cmd_io->op->params[idx].memref.size);
/*
* If we allocated a tmpref buffer,
* copy back data to the user buffer
*/
if (is_mapped_temp(cmd->param.params[idx].shm->flags)) {
if (parent->buffer &&
offset + size_new <= parent->size) {
if (tee_copy_to_user(ctx,
parent->buffer + offset,
cmd->param.params[idx].shm->kaddr,
size_new))
dev_err(_DEV_TEE,
"%s: can't update %d data to user\n",
__func__, idx);
}
}
break;
default:
BUG_ON(1);
}
}
}
static void _release_tee_cmd(struct tee_session *sess, struct tee_cmd *cmd)
{
int idx;
struct tee_context *ctx;
BUG_ON(!cmd);
BUG_ON(!sess);
BUG_ON(!sess->ctx);
BUG_ON(!sess->ctx->tee);
ctx = sess->ctx;
dev_dbg(_DEV_TEE, "%s: > free the temporary objects...\n", __func__);
tee_shm_free(cmd->uuid);
if (cmd->param.type_original == TEEC_PARAM_TYPES(TEEC_NONE,
TEEC_NONE, TEEC_NONE, TEEC_NONE))
goto out;
for (idx = 0; idx < TEEC_CONFIG_PAYLOAD_REF_COUNT; ++idx) {
int type = TEEC_PARAM_TYPE_GET(cmd->param.type_original, idx);
struct tee_shm *shm;
switch (type) {
case TEEC_NONE:
case TEEC_VALUE_INPUT:
case TEEC_VALUE_OUTPUT:
case TEEC_VALUE_INOUT:
break;
case TEEC_MEMREF_TEMP_INPUT:
case TEEC_MEMREF_TEMP_OUTPUT:
case TEEC_MEMREF_TEMP_INOUT:
case TEEC_MEMREF_WHOLE:
case TEEC_MEMREF_PARTIAL_INPUT:
case TEEC_MEMREF_PARTIAL_OUTPUT:
case TEEC_MEMREF_PARTIAL_INOUT:
if (IS_ERR_OR_NULL(cmd->param.params[idx].shm))
break;
shm = cmd->param.params[idx].shm;
if (is_mapped_temp(shm->flags))
tee_shm_free(shm);
else
tee_shm_put(ctx, shm);
break;
default:
BUG_ON(1);
}
}
out:
memset(cmd, 0, sizeof(struct tee_cmd));
dev_dbg(_DEV_TEE, "%s: <\n", __func__);
}

View File

@@ -0,0 +1,815 @@
/*
* Copyright (c) 2014, STMicroelectronics International N.V.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License Version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/dma-buf.h>
#include <linux/hugetlb.h>
#include <linux/sched.h>
#include <linux/mm.h>
#include "tee_core_priv.h"
#include "tee_shm.h"
#define INMSG() dev_dbg(_DEV(tee), "%s: >\n", __func__)
#define OUTMSG(val) dev_dbg(_DEV(tee), "%s: < %ld\n", __func__, (long)val)
#define OUTMSGX(val) dev_dbg(_DEV(tee), "%s: < %08x\n",\
__func__, (unsigned int)(long)val)
/* TODO
#if (sizeof(TEEC_SharedMemory) != sizeof(tee_shm))
#error "sizeof(TEEC_SharedMemory) != sizeof(tee_shm))"
#endif
*/
struct tee_shm_attach {
struct sg_table sgt;
enum dma_data_direction dir;
bool is_mapped;
};
struct tee_shm *tee_shm_alloc_from_rpc(struct tee *tee, size_t size)
{
struct tee_shm *shm;
INMSG();
shm = tee_shm_alloc(tee, size, TEE_SHM_TEMP | TEE_SHM_FROM_RPC);
if (IS_ERR_OR_NULL(shm)) {
dev_err(_DEV(tee), "%s: buffer allocation failed (%ld)\n",
__func__, PTR_ERR(shm));
goto out;
}
mutex_lock(&tee->lock);
tee_inc_stats(&tee->stats[TEE_STATS_SHM_IDX]);
list_add_tail(&shm->entry, &tee->list_rpc_shm);
mutex_unlock(&tee->lock);
shm->ctx = NULL;
out:
OUTMSGX(shm);
return shm;
}
void tee_shm_free_from_rpc(struct tee_shm *shm)
{
if (shm == NULL)
return;
if (shm->ctx == NULL) {
mutex_lock(&shm->tee->lock);
tee_dec_stats(&shm->tee->stats[TEE_STATS_SHM_IDX]);
list_del(&shm->entry);
mutex_unlock(&shm->tee->lock);
}
tee_shm_free(shm);
}
struct tee_shm *tee_shm_alloc(struct tee *tee, size_t size, uint32_t flags)
{
struct tee_shm *shm;
unsigned long pfn;
unsigned int nr_pages;
struct page *page;
int ret;
INMSG();
shm = tee->ops->alloc(tee, size, flags);
if (IS_ERR_OR_NULL(shm)) {
dev_err(_DEV(tee),
"%s: allocation failed (s=%d,flags=0x%08x) err=%ld\n",
__func__, (int)size, flags, PTR_ERR(shm));
goto exit;
}
shm->tee = tee;
dev_dbg(_DEV(tee), "%s: shm=%p, paddr=%p,s=%d/%d app=\"%s\" pid=%d\n",
__func__, shm, (void *)shm->paddr, (int)shm->size_req,
(int)shm->size_alloc, current->comm, current->pid);
pfn = shm->paddr >> PAGE_SHIFT;
page = pfn_to_page(pfn);
if (IS_ERR_OR_NULL(page)) {
dev_err(_DEV(tee), "%s: pfn_to_page(%lx) failed\n",
__func__, pfn);
tee->ops->free(shm);
return (struct tee_shm *)page;
}
/* Only one page of contiguous physical memory */
nr_pages = 1;
ret = sg_alloc_table_from_pages(&shm->sgt, &page,
nr_pages, 0, nr_pages * PAGE_SIZE, GFP_KERNEL);
if (IS_ERR_VALUE(ret)) {
dev_err(_DEV(tee), "%s: sg_alloc_table_from_pages() failed\n",
__func__);
tee->ops->free(shm);
shm = ERR_PTR(ret);
}
exit:
OUTMSGX(shm);
return shm;
}
void tee_shm_free(struct tee_shm *shm)
{
struct tee *tee;
if (IS_ERR_OR_NULL(shm))
return;
tee = shm->tee;
if (tee == NULL)
pr_warn("invalid call to tee_shm_free(%p): NULL tee\n", shm);
else if (shm->tee == NULL)
dev_warn(_DEV(tee), "tee_shm_free(%p): NULL tee\n", shm);
else {
sg_free_table(&shm->sgt);
shm->tee->ops->free(shm);
}
}
static int _tee_shm_attach_dma_buf(struct dma_buf *dmabuf,
struct device *dev,
struct dma_buf_attachment *attach)
{
struct tee_shm_attach *tee_shm_attach;
struct tee_shm *shm;
struct tee *tee;
shm = dmabuf->priv;
tee = shm->tee;
INMSG();
tee_shm_attach = devm_kzalloc(_DEV(tee),
sizeof(*tee_shm_attach), GFP_KERNEL);
if (!tee_shm_attach) {
OUTMSG(-ENOMEM);
return -ENOMEM;
}
tee_shm_attach->dir = DMA_NONE;
attach->priv = tee_shm_attach;
OUTMSG(0);
return 0;
}
static void _tee_shm_detach_dma_buf(struct dma_buf *dmabuf,
struct dma_buf_attachment *attach)
{
struct tee_shm_attach *tee_shm_attach = attach->priv;
struct sg_table *sgt;
struct tee_shm *shm;
struct tee *tee;
shm = dmabuf->priv;
tee = shm->tee;
INMSG();
if (!tee_shm_attach) {
OUTMSG(0);
return;
}
sgt = &tee_shm_attach->sgt;
if (tee_shm_attach->dir != DMA_NONE)
dma_unmap_sg(attach->dev, sgt->sgl, sgt->nents,
tee_shm_attach->dir);
sg_free_table(sgt);
devm_kfree(_DEV(tee), tee_shm_attach);
attach->priv = NULL;
OUTMSG(0);
}
static struct sg_table *_tee_shm_dma_buf_map_dma_buf(
struct dma_buf_attachment *attach, enum dma_data_direction dir)
{
struct tee_shm_attach *tee_shm_attach = attach->priv;
struct tee_shm *tee_shm = attach->dmabuf->priv;
struct sg_table *sgt = NULL;
struct scatterlist *rd, *wr;
unsigned int i;
int nents, ret;
struct tee *tee;
tee = tee_shm->tee;
INMSG();
/* just return current sgt if already requested. */
if (tee_shm_attach->dir == dir && tee_shm_attach->is_mapped) {
OUTMSGX(&tee_shm_attach->sgt);
return &tee_shm_attach->sgt;
}
sgt = &tee_shm_attach->sgt;
ret = sg_alloc_table(sgt, tee_shm->sgt.orig_nents, GFP_KERNEL);
if (ret) {
dev_err(_DEV(tee), "failed to alloc sgt.\n");
return ERR_PTR(-ENOMEM);
}
rd = tee_shm->sgt.sgl;
wr = sgt->sgl;
for (i = 0; i < sgt->orig_nents; ++i) {
sg_set_page(wr, sg_page(rd), rd->length, rd->offset);
rd = sg_next(rd);
wr = sg_next(wr);
}
if (dir != DMA_NONE) {
nents = dma_map_sg(attach->dev, sgt->sgl, sgt->orig_nents, dir);
if (!nents) {
dev_err(_DEV(tee), "failed to map sgl with iommu.\n");
sg_free_table(sgt);
sgt = ERR_PTR(-EIO);
goto err_unlock;
}
}
tee_shm_attach->is_mapped = true;
tee_shm_attach->dir = dir;
attach->priv = tee_shm_attach;
err_unlock:
OUTMSGX(sgt);
return sgt;
}
static void _tee_shm_dma_buf_unmap_dma_buf(struct dma_buf_attachment *attach,
struct sg_table *table,
enum dma_data_direction dir)
{
return;
}
static void _tee_shm_dma_buf_release(struct dma_buf *dmabuf)
{
struct tee_shm *shm = dmabuf->priv;
struct tee_context *ctx;
struct tee *tee;
tee = shm->ctx->tee;
INMSG();
ctx = shm->ctx;
dev_dbg(_DEV(ctx->tee), "%s: shm=%p, paddr=%p,s=%d/%d app=\"%s\" pid=%d\n",
__func__, shm, (void *)shm->paddr, (int)shm->size_req,
(int)shm->size_alloc, current->comm, current->pid);
tee_shm_free_io(shm);
OUTMSG(0);
}
static int _tee_shm_dma_buf_mmap(struct dma_buf *dmabuf,
struct vm_area_struct *vma)
{
struct tee_shm *shm = dmabuf->priv;
size_t size = vma->vm_end - vma->vm_start;
struct tee *tee;
int ret;
pgprot_t prot;
unsigned long pfn;
tee = shm->ctx->tee;
pfn = shm->paddr >> PAGE_SHIFT;
INMSG();
if (shm->flags & TEE_SHM_CACHED)
prot = vma->vm_page_prot;
else
prot = pgprot_noncached(vma->vm_page_prot);
ret =
remap_pfn_range(vma, vma->vm_start, pfn, size, prot);
if (!ret)
vma->vm_private_data = (void *)shm;
dev_dbg(_DEV(shm->ctx->tee), "%s: map the shm (p@=%p,s=%dKiB) => %x\n",
__func__, (void *)shm->paddr, (int)size / 1024,
(unsigned int)vma->vm_start);
OUTMSG(ret);
return ret;
}
static void *_tee_shm_dma_buf_kmap_atomic(struct dma_buf *dmabuf,
unsigned long pgnum)
{
return NULL;
}
static void *_tee_shm_dma_buf_kmap(struct dma_buf *db, unsigned long pgnum)
{
struct tee_shm *shm = db->priv;
dev_dbg(_DEV(shm->ctx->tee), "%s: kmap the shm (p@=%p, v@=%p, s=%zdKiB)\n",
__func__, (void *)shm->paddr, (void *)shm->kaddr,
shm->size_alloc / 1024);
/*
* A this stage, a shm allocated by the tee
* must be have a kernel address
*/
return shm->kaddr;
}
static void _tee_shm_dma_buf_kunmap(
struct dma_buf *db, unsigned long pfn, void *kaddr)
{
/* unmap is done at the de init of the shm pool */
}
struct dma_buf_ops _tee_shm_dma_buf_ops = {
.attach = _tee_shm_attach_dma_buf,
.detach = _tee_shm_detach_dma_buf,
.map_dma_buf = _tee_shm_dma_buf_map_dma_buf,
.unmap_dma_buf = _tee_shm_dma_buf_unmap_dma_buf,
.release = _tee_shm_dma_buf_release,
.kmap_atomic = _tee_shm_dma_buf_kmap_atomic,
.kmap = _tee_shm_dma_buf_kmap,
.kunmap = _tee_shm_dma_buf_kunmap,
.mmap = _tee_shm_dma_buf_mmap,
};
/******************************************************************************/
static int export_buf(struct tee *tee, struct tee_shm *shm, int *export)
{
struct dma_buf *dmabuf;
int ret = 0;
/* Temporary fix to support both older and newer kernel versions. */
#if defined(DEFINE_DMA_BUF_EXPORT_INFO)
DEFINE_DMA_BUF_EXPORT_INFO(exp_info);
exp_info.priv = shm;
exp_info.ops = &_tee_shm_dma_buf_ops;
exp_info.size = shm->size_alloc;
exp_info.flags = O_RDWR;
dmabuf = dma_buf_export(&exp_info);
#else
dmabuf = dma_buf_export(shm, &_tee_shm_dma_buf_ops, shm->size_alloc,
O_RDWR, 0);
#endif
if (IS_ERR_OR_NULL(dmabuf)) {
dev_err(_DEV(tee), "%s: dmabuf: couldn't export buffer (%ld)\n",
__func__, PTR_ERR(dmabuf));
ret = -EINVAL;
goto out;
}
*export = dma_buf_fd(dmabuf, O_CLOEXEC);
out:
OUTMSG(ret);
return ret;
}
int tee_shm_alloc_io(struct tee_context *ctx, struct tee_shm_io *shm_io)
{
struct tee_shm *shm;
struct tee *tee = ctx->tee;
int ret;
INMSG();
if (ctx->usr_client)
shm_io->fd_shm = 0;
else
shm_io->ptr = NULL;
shm = tee_shm_alloc(tee, shm_io->size, shm_io->flags);
if (IS_ERR_OR_NULL(shm)) {
dev_err(_DEV(tee), "%s: buffer allocation failed (%ld)\n",
__func__, PTR_ERR(shm));
return PTR_ERR(shm);
}
if (ctx->usr_client) {
ret = export_buf(tee, shm, &shm_io->fd_shm);
if (ret) {
tee_shm_free(shm);
ret = -ENOMEM;
goto out;
}
shm->flags |= TEEC_MEM_DMABUF;
} else
shm_io->ptr = shm;
shm->ctx = ctx;
shm->dev = get_device(_DEV(tee));
ret = tee_get(tee);
BUG_ON(ret); /* tee_core_get must not issue */
tee_context_get(ctx);
mutex_lock(&tee->lock);
tee_inc_stats(&tee->stats[TEE_STATS_SHM_IDX]);
list_add_tail(&shm->entry, &ctx->list_shm);
mutex_unlock(&tee->lock);
out:
OUTMSG(ret);
return ret;
}
void tee_shm_free_io(struct tee_shm *shm)
{
struct tee_context *ctx = shm->ctx;
struct tee *tee = ctx->tee;
struct device *dev = shm->dev;
mutex_lock(&ctx->tee->lock);
tee_dec_stats(&tee->stats[TEE_STATS_SHM_IDX]);
list_del(&shm->entry);
mutex_unlock(&ctx->tee->lock);
tee_shm_free(shm);
tee_put(ctx->tee);
tee_context_put(ctx);
if (dev)
put_device(dev);
}
/* Buffer allocated by rpc from fw and to be accessed by the user
* Not need to be registered as it is not allocated by the user */
int tee_shm_fd_for_rpc(struct tee_context *ctx, struct tee_shm_io *shm_io)
{
struct tee_shm *shm = NULL;
struct tee *tee = ctx->tee;
int ret;
struct list_head *pshm;
INMSG();
shm_io->fd_shm = 0;
if (!list_empty(&tee->list_rpc_shm)) {
list_for_each(pshm, &tee->list_rpc_shm) {
shm = list_entry(pshm, struct tee_shm, entry);
if ((void *)shm->paddr == shm_io->buffer)
goto found;
}
}
dev_err(_DEV(tee), "Can't find shm for %p\n", (void *)shm_io->buffer);
ret = -ENOMEM;
goto out;
found:
ret = export_buf(tee, shm, &shm_io->fd_shm);
if (ret) {
ret = -ENOMEM;
goto out;
}
shm->ctx = ctx;
mutex_lock(&tee->lock);
list_move(&shm->entry, &ctx->list_shm);
mutex_unlock(&tee->lock);
shm->dev = get_device(_DEV(tee));
ret = tee_get(tee);
BUG_ON(ret);
tee_context_get(ctx);
BUG_ON(!tee->ops->shm_inc_ref(shm));
out:
OUTMSG(ret);
return ret;
}
/******************************************************************************/
static int tee_shm_db_get(struct tee *tee, struct tee_shm *shm, int fd,
unsigned int flags, size_t size, int offset)
{
struct tee_shm_dma_buf *sdb;
struct dma_buf *dma_buf;
int ret = 0;
dev_dbg(_DEV(tee), "%s: > fd=%d flags=%08x\n", __func__, fd, flags);
dma_buf = dma_buf_get(fd);
if (IS_ERR(dma_buf)) {
ret = PTR_ERR(dma_buf);
goto exit;
}
sdb = kzalloc(sizeof(*sdb), GFP_KERNEL);
if (IS_ERR_OR_NULL(sdb)) {
dev_err(_DEV(tee), "can't alloc tee_shm_dma_buf\n");
ret = PTR_ERR(sdb);
goto buf_put;
}
shm->sdb = sdb;
if (dma_buf->size < size + offset) {
dev_err(_DEV(tee), "dma_buf too small %zd < %zd + %d\n",
dma_buf->size, size, offset);
ret = -EINVAL;
goto free_sdb;
}
sdb->attach = dma_buf_attach(dma_buf, _DEV(tee));
if (IS_ERR_OR_NULL(sdb->attach)) {
ret = PTR_ERR(sdb->attach);
goto free_sdb;
}
sdb->sgt = dma_buf_map_attachment(sdb->attach, DMA_NONE);
if (IS_ERR_OR_NULL(sdb->sgt)) {
ret = PTR_ERR(sdb->sgt);
goto buf_detach;
}
if (sg_nents(sdb->sgt->sgl) != 1) {
ret = -EINVAL;
goto buf_unmap;
}
shm->paddr = sg_phys(sdb->sgt->sgl) + offset;
if (dma_buf->ops->attach == _tee_shm_attach_dma_buf)
sdb->tee_allocated = true;
else
sdb->tee_allocated = false;
shm->flags |= TEEC_MEM_DMABUF;
dev_dbg(_DEV(tee), "fd=%d @p=%p is_tee=%d db=%p\n", fd,
(void *)shm->paddr, sdb->tee_allocated, dma_buf);
goto exit;
buf_unmap:
dma_buf_unmap_attachment(sdb->attach, sdb->sgt, DMA_NONE);
buf_detach:
dma_buf_detach(dma_buf, sdb->attach);
free_sdb:
kfree(sdb);
buf_put:
dma_buf_put(dma_buf);
exit:
OUTMSG(ret);
return ret;
}
#ifdef VA_GET_ENABLED
static unsigned int tee_shm_get_phy_from_kla(
struct mm_struct *mm, unsigned int kla)
{
pgd_t *pgd;
pud_t *pud;
pmd_t *pmd;
pte_t *ptep, pte;
unsigned int pa = 0;
/* stolen from kernel3.10:mm/memory.c:__follow_pte */
pgd = pgd_offset(mm, kla);
if (pgd_none(*pgd) || pgd_bad(*pgd))
return 0;
pud = pud_offset(pgd, kla);
if (pud_none(*pud) || pud_bad(*pud))
return 0;
pmd = pmd_offset(pud, kla);
VM_BUG_ON(pmd_trans_huge(*pmd));
if (pmd_none(*pmd) || pmd_bad(*pmd))
return 0;
/* We cannot handle huge page PFN maps.
* Luckily they don't exist. */
if (pmd_huge(*pmd))
return 0;
ptep = pte_offset_map(pmd, kla);
if (!ptep)
return 0;
pte = *ptep;
if (pte_present(pte))
pa = __pa(page_address(pte_page(pte)));
if (!pa)
return 0;
return pa;
}
static int tee_shm_va_get(struct tee_context *ctx, struct tee_shm *shm,
void *buffer, unsigned int flags, size_t size, int offset)
{
int ret = 0;
struct mm_struct *mm = current->mm;
unsigned long va = (unsigned long)buffer;
unsigned int virt_base = (va / PAGE_SIZE) * PAGE_SIZE;
unsigned int offset_in_page = va - virt_base;
unsigned int offset_total = offset_in_page + offset;
struct vm_area_struct *vma;
struct tee *tee = ctx->tee;
dev_dbg(_DEV(tee), "%s: > %p\n", __func__, buffer);
/* if the caller is the kernel api, active_mm is mm */
if (!mm)
mm = current->active_mm;
BUG_ON(!mm);
vma = find_vma(mm, virt_base);
if (vma) {
unsigned long pfn;
/* It's a VMA => consider it a a user address */
if (follow_pfn(vma, virt_base, &pfn)) {
dev_err(_DEV(tee), "%s can't get pfn for %p\n",
__func__, buffer);
ret = -EINVAL;
goto out;
}
shm->paddr = PFN_PHYS(pfn) + offset_total;
if (vma->vm_end - vma->vm_start - offset_total < size) {
dev_err(_DEV(tee), "%s %p:%x not big enough: %lx - %d < %x\n",
__func__, buffer, shm->paddr,
vma->vm_end - vma->vm_start,
offset_total, size);
shm->paddr = 0;
ret = -EINVAL;
goto out;
}
} else if (!ctx->usr_client) {
/* It's not a VMA => consider it as a kernel address
* And look if it's an internal known phys addr
* Note: virt_to_phys is not usable since it can be a direct
* map or a vremap address
*/
unsigned int phys_base;
int nb_page = (PAGE_SIZE - 1 + size + offset_total) / PAGE_SIZE;
int i;
spin_lock(&mm->page_table_lock);
phys_base = tee_shm_get_phy_from_kla(mm, virt_base);
if (!phys_base) {
spin_unlock(&mm->page_table_lock);
dev_err(_DEV(tee), "%s can't get physical address for %p\n",
__func__, buffer);
goto err;
}
/* Check continuity on size */
for (i = 1; i < nb_page; i++) {
unsigned int pa = tee_shm_get_phy_from_kla(mm,
virt_base + i*PAGE_SIZE);
if (pa != phys_base + i*PAGE_SIZE) {
spin_unlock(&mm->page_table_lock);
dev_err(_DEV(tee), "%s %p:%x not big enough: %lx - %d < %x\n",
__func__, buffer, phys_base,
i*PAGE_SIZE,
offset_total, size);
goto err;
}
}
spin_unlock(&mm->page_table_lock);
shm->paddr = phys_base + offset_total;
goto out;
err:
ret = -EINVAL;
}
out:
dev_dbg(_DEV(tee), "%s: < %d shm=%p vaddr=%p paddr=%x\n",
__func__, ret, (void *)shm, buffer, shm->paddr);
return ret;
}
#endif
struct tee_shm *tee_shm_get(struct tee_context *ctx, TEEC_SharedMemory *c_shm,
size_t size, int offset)
{
struct tee_shm *shm;
struct tee *tee = ctx->tee;
int ret;
dev_dbg(_DEV(tee), "%s: > fd=%d flags=%08x\n",
__func__, c_shm->d.fd, c_shm->flags);
shm = kzalloc(sizeof(*shm), GFP_KERNEL);
if (IS_ERR_OR_NULL(shm)) {
dev_err(_DEV(tee), "can't alloc tee_shm\n");
return ERR_PTR(-ENOMEM);
}
shm->ctx = ctx;
shm->tee = tee;
shm->dev = _DEV(tee);
shm->flags = c_shm->flags | TEE_SHM_MEMREF;
shm->size_req = size;
shm->size_alloc = 0;
if (c_shm->flags & TEEC_MEM_KAPI) {
struct tee_shm *kc_shm = (struct tee_shm *)c_shm->d.ptr;
if (!kc_shm) {
dev_err(_DEV(tee), "kapi fd null\n");
ret = -EINVAL;
goto err;
}
shm->paddr = kc_shm->paddr;
if (kc_shm->size_alloc < size + offset) {
dev_err(_DEV(tee), "kapi buff too small %zd < %zd + %d\n",
kc_shm->size_alloc, size, offset);
ret = -EINVAL;
goto err;
}
dev_dbg(_DEV(tee), "fd=%d @p=%p\n",
c_shm->d.fd, (void *)shm->paddr);
} else if (c_shm->d.fd) {
ret = tee_shm_db_get(tee, shm,
c_shm->d.fd, c_shm->flags, size, offset);
if (ret)
goto err;
} else if (!c_shm->buffer) {
dev_dbg(_DEV(tee), "null buffer, pass 'as is'\n");
} else {
#ifdef VA_GET_ENABLED
ret = tee_shm_va_get(ctx, shm,
c_shm->buffer, c_shm->flags, size, offset);
if (ret)
goto err;
#else
ret = -EINVAL;
goto err;
#endif
}
OUTMSGX(shm);
return shm;
err:
kfree(shm);
OUTMSGX(ERR_PTR(ret));
return ERR_PTR(ret);
}
void tee_shm_put(struct tee_context *ctx, struct tee_shm *shm)
{
struct tee *tee = ctx->tee;
dev_dbg(_DEV(tee), "%s: > shm=%p flags=%08x paddr=%p\n",
__func__, (void *)shm, shm->flags, (void *)shm->paddr);
BUG_ON(!shm);
BUG_ON(!(shm->flags & TEE_SHM_MEMREF));
if (shm->flags & TEEC_MEM_DMABUF) {
struct tee_shm_dma_buf *sdb;
struct dma_buf *dma_buf;
sdb = shm->sdb;
dma_buf = sdb->attach->dmabuf;
dev_dbg(_DEV(tee), "%s: db=%p\n", __func__, (void *)dma_buf);
dma_buf_unmap_attachment(sdb->attach, sdb->sgt, DMA_NONE);
dma_buf_detach(dma_buf, sdb->attach);
dma_buf_put(dma_buf);
kfree(sdb);
sdb = 0;
}
kfree(shm);
OUTMSG(0);
}

View File

@@ -0,0 +1,34 @@
/*
* Copyright (c) 2014, STMicroelectronics International N.V.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License Version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef __TEE_SHM_H__
#define __TEE_SHM_H__
#include <linux/tee_client_api.h>
struct tee_context;
struct tee_shm;
struct tee_shm_io;
struct tee;
int tee_shm_alloc_io(struct tee_context *ctx, struct tee_shm_io *shm_io);
void tee_shm_free_io(struct tee_shm *shm);
int tee_shm_fd_for_rpc(struct tee_context *ctx, struct tee_shm_io *shm_io);
struct tee_shm *tee_shm_alloc(struct tee *tee, size_t size, uint32_t flags);
void tee_shm_free(struct tee_shm *shm);
struct tee_shm *tee_shm_get(struct tee_context *ctx, TEEC_SharedMemory *c_shm,
size_t size, int offset);
void tee_shm_put(struct tee_context *ctx, struct tee_shm *shm);
#endif /* __TEE_SHM_H__ */

View File

@@ -0,0 +1,272 @@
/*
* Copyright (c) 2014, STMicroelectronics International N.V.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License Version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/mutex.h>
#include <linux/miscdevice.h>
#include <linux/uaccess.h>
#include <linux/anon_inodes.h>
#include <linux/semaphore.h>
#include <linux/mm.h>
#include <linux/sched.h>
#include <linux/device.h>
#include "tee_shm.h"
#include "tee_core.h"
#include "tee_supp_com.h"
#define TEE_RPC_BUFFER 0x00000001
#define TEE_RPC_VALUE 0x00000002
enum teec_rpc_result tee_supp_cmd(struct tee *tee,
uint32_t id, void *data, size_t datalen)
{
struct tee_rpc *rpc = tee->rpc;
enum teec_rpc_result res = TEEC_RPC_FAIL;
size_t size;
struct task_struct *task = current;
dev_dbg(tee->dev, "> tgid:[%d] id:[0x%08x]\n", task->tgid, id);
if (atomic_read(&rpc->used) == 0) {
dev_err(tee->dev, "%s: ERROR Supplicant application NOT ready\n"
, __func__);
goto out;
}
switch (id) {
case TEE_RPC_ICMD_ALLOCATE:
{
struct tee_rpc_alloc *alloc;
struct tee_shm *shmint;
alloc = (struct tee_rpc_alloc *)data;
size = alloc->size;
memset(alloc, 0, sizeof(struct tee_rpc_alloc));
shmint = tee_shm_alloc_from_rpc(tee, size);
if (IS_ERR_OR_NULL(shmint))
break;
alloc->size = size;
alloc->data = (void *)shmint->paddr;
alloc->shm = shmint;
res = TEEC_RPC_OK;
break;
}
case TEE_RPC_ICMD_FREE:
{
struct tee_rpc_free *free;
free = (struct tee_rpc_free *)data;
tee_shm_free_from_rpc(free->shm);
res = TEEC_RPC_OK;
break;
}
case TEE_RPC_ICMD_INVOKE:
{
if (sizeof(rpc->commToUser) < datalen)
break;
mutex_lock(&rpc->outsync);
memcpy(&rpc->commToUser, data, datalen);
mutex_unlock(&rpc->outsync);
dev_dbg(tee->dev,
"Supplicant Cmd: %x. Give hand to supplicant\n",
rpc->commToUser.cmd);
up(&rpc->datatouser);
down(&rpc->datafromuser);
dev_dbg(tee->dev,
"Supplicant Cmd: %x. Give hand to fw\n",
rpc->commToUser.cmd);
mutex_lock(&rpc->insync);
memcpy(data, &rpc->commFromUser, datalen);
mutex_unlock(&rpc->insync);
res = TEEC_RPC_OK;
break;
}
default:
/* not supported */
break;
}
out:
dev_dbg(tee->dev, "< res: [%d]\n", res);
return res;
}
EXPORT_SYMBOL(tee_supp_cmd);
ssize_t tee_supp_read(struct file *filp, char __user *buffer,
size_t length, loff_t *offset)
{
struct tee_context *ctx = (struct tee_context *)(filp->private_data);
struct tee *tee;
struct tee_rpc *rpc;
struct task_struct *task = current;
int ret;
BUG_ON(!ctx);
tee = ctx->tee;
BUG_ON(!tee);
BUG_ON(!tee->dev);
BUG_ON(!tee->rpc);
dev_dbg(tee->dev, "> ctx %p\n", ctx);
rpc = tee->rpc;
if (atomic_read(&rpc->used) == 0) {
dev_err(tee->dev, "%s: ERROR Supplicant application NOT ready\n"
, __func__);
ret = -EPERM;
goto out;
}
if (down_interruptible(&rpc->datatouser))
return -ERESTARTSYS;
dev_dbg(tee->dev, "> tgid:[%d]\n", task->tgid);
mutex_lock(&rpc->outsync);
ret =
sizeof(rpc->commToUser) - sizeof(rpc->commToUser.cmds) +
sizeof(rpc->commToUser.cmds[0]) * rpc->commToUser.nbr_bf;
if (length < ret) {
ret = -EINVAL;
} else {
if (copy_to_user(buffer, &rpc->commToUser, ret)) {
dev_err(tee->dev,
"[%s] error, copy_to_user failed!\n", __func__);
ret = -EINVAL;
}
}
mutex_unlock(&rpc->outsync);
out:
dev_dbg(tee->dev, "< [%d]\n", ret);
return ret;
}
ssize_t tee_supp_write(struct file *filp, const char __user *buffer,
size_t length, loff_t *offset)
{
struct tee_context *ctx = (struct tee_context *)(filp->private_data);
struct tee *tee;
struct tee_rpc *rpc;
struct task_struct *task = current;
int ret = 0;
BUG_ON(!ctx);
BUG_ON(!ctx->tee);
BUG_ON(!ctx->tee->rpc);
tee = ctx->tee;
rpc = tee->rpc;
dev_dbg(tee->dev, "> tgid:[%d]\n", task->tgid);
if (atomic_read(&rpc->used) == 0) {
dev_err(tee->dev, "%s: ERROR Supplicant application NOT ready\n"
, __func__);
goto out;
}
if (length > 0 && length < sizeof(rpc->commFromUser)) {
uint32_t i;
mutex_lock(&rpc->insync);
if (copy_from_user(&rpc->commFromUser, buffer, length)) {
dev_err(tee->dev,
"%s: ERROR, tee_session copy_from_user failed\n",
__func__);
mutex_unlock(&rpc->insync);
ret = -EINVAL;
goto out;
}
/* Translate virtual address of caller into physical address */
for (i = 0; i < rpc->commFromUser.nbr_bf; i++) {
if (rpc->commFromUser.cmds[i].type == TEE_RPC_BUFFER
&& rpc->commFromUser.cmds[i].buffer) {
struct vm_area_struct *vma =
find_vma(current->mm,
(unsigned long)rpc->
commFromUser.cmds[i].buffer);
if (vma != NULL) {
struct tee_shm *shm =
vma->vm_private_data;
BUG_ON(!shm);
dev_dbg(tee->dev,
"%d gid2pa(0x%p => %x)\n", i,
rpc->commFromUser.cmds[i].
buffer,
(unsigned int)shm->paddr);
rpc->commFromUser.cmds[i].buffer =
(void *)shm->paddr;
} else
dev_dbg(tee->dev,
" gid2pa(0x%p => NULL\n)",
rpc->commFromUser.cmds[i].
buffer);
}
}
mutex_unlock(&rpc->insync);
up(&rpc->datafromuser);
ret = length;
}
out:
dev_dbg(tee->dev, "< [%d]\n", ret);
return ret;
}
int tee_supp_init(struct tee *tee)
{
struct tee_rpc *rpc =
devm_kzalloc(tee->dev, sizeof(struct tee_rpc), GFP_KERNEL);
if (!rpc) {
dev_err(tee->dev, "%s: can't allocate tee_rpc structure\n",
__func__);
return -ENOMEM;
}
rpc->datafromuser = (struct semaphore)
__SEMAPHORE_INITIALIZER(rpc->datafromuser, 0);
rpc->datatouser = (struct semaphore)
__SEMAPHORE_INITIALIZER(rpc->datatouser, 0);
mutex_init(&rpc->outsync);
mutex_init(&rpc->insync);
atomic_set(&rpc->used, 0);
tee->rpc = rpc;
return 0;
}
void tee_supp_deinit(struct tee *tee)
{
devm_kfree(tee->dev, tee->rpc);
tee->rpc = NULL;
}

View File

@@ -0,0 +1,111 @@
/*
* Copyright (c) 2014, STMicroelectronics International N.V.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License Version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef TEE_SUPP_COMM_H
#define TEE_SUPP_COMM_H
#define TEE_RPC_ICMD_ALLOCATE 0x1001
#define TEE_RPC_ICMD_FREE 0x1002
#define TEE_RPC_ICMD_INVOKE 0x1003
#define TEE_RPC_NBR_BUFF 1
#define TEE_RPC_DATA_SIZE 64
#define TEE_RPC_BUFFER_NUMBER 5
#define TEE_RPC_STATE_IDLE 0x00
#define TEE_RPC_STATE_ACTIVE 0x01
/* Keep aligned with optee_client (user space) */
#define TEE_RPC_BUFFER 0x00000001
#define TEE_RPC_VALUE 0x00000002
#define TEE_RPC_LOAD_TA 0x10000001
/*
* Handled within the driver only
* Keep aligned with optee_os (secure space)
*/
#define TEE_RPC_MUTEX_WAIT 0x20000000
#define TEE_RPC_WAIT_QUEUE_SLEEP 0x20000001
#define TEE_RPC_WAIT_QUEUE_WAKEUP 0x20000002
#define TEE_RPC_WAIT 0x30000000
/* Parameters for TEE_RPC_WAIT_MUTEX above */
#define TEE_MUTEX_WAIT_SLEEP 0
#define TEE_MUTEX_WAIT_WAKEUP 1
#define TEE_MUTEX_WAIT_DELETE 2
#include <linux/semaphore.h>
/**
* struct tee_rpc_bf - Contains definition of the tee com buffer
* @state: Buffer state
* @data: Command data
*/
struct tee_rpc_bf {
uint32_t state;
uint8_t data[TEE_RPC_DATA_SIZE];
};
struct tee_rpc_alloc {
uint32_t size; /* size of block */
void *data; /* pointer to data */
void *shm; /* pointer to an opaque data, being shm structure */
};
struct tee_rpc_free {
void *shm; /* pointer to an opaque data, being shm structure */
};
struct tee_rpc_cmd {
void *buffer;
uint32_t size;
uint32_t type;
int fd;
};
struct tee_rpc_invoke {
uint32_t cmd;
uint32_t res;
uint32_t nbr_bf;
struct tee_rpc_cmd cmds[TEE_RPC_BUFFER_NUMBER];
};
struct tee_rpc {
struct tee_rpc_invoke commToUser;
struct tee_rpc_invoke commFromUser;
struct semaphore datatouser;
struct semaphore datafromuser;
struct mutex outsync; /* Out sync mutex */
struct mutex insync; /* In sync mutex */
struct mutex reqsync; /* Request sync mutex */
atomic_t used;
};
enum teec_rpc_result {
TEEC_RPC_OK,
TEEC_RPC_FAIL
};
struct tee;
int tee_supp_init(struct tee *tee);
void tee_supp_deinit(struct tee *tee);
enum teec_rpc_result tee_supp_cmd(struct tee *tee,
uint32_t id, void *data, size_t datalen);
ssize_t tee_supp_read(struct file *filp, char __user *buffer,
size_t length, loff_t *offset);
ssize_t tee_supp_write(struct file *filp, const char __user *buffer,
size_t length, loff_t *offset);
#endif

View File

@@ -0,0 +1,204 @@
/*
* Copyright (c) 2014, STMicroelectronics International N.V.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License Version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/stat.h>
#include <linux/atomic.h>
#include <asm/page.h>
#include "tee_core_priv.h"
static ssize_t dump_show(struct device *device,
struct device_attribute *attr, char *buf)
{
struct tee *tee = dev_get_drvdata(device);
int len;
char *tmp_buf;
tmp_buf = kzalloc(PAGE_SIZE, GFP_KERNEL);
if (!tmp_buf) {
printk(KERN_ALERT "%s : Unable to get buf memory\n", __func__);
return -ENOMEM;
}
len = tee_context_dump(tee, tmp_buf, PAGE_SIZE - 128);
if (len > 0)
len = snprintf(buf, PAGE_SIZE, "%s", tmp_buf);
kfree(tmp_buf);
return len;
}
static ssize_t stat_show(struct device *device,
struct device_attribute *attr, char *buf)
{
struct tee *tee = dev_get_drvdata(device);
return snprintf(buf, PAGE_SIZE, "%d/%d %d/%d %d/%d %d/%d\n",
atomic_read(&tee->refcount),
tee->max_refcount,
tee->stats[TEE_STATS_CONTEXT_IDX].count,
tee->stats[TEE_STATS_CONTEXT_IDX].max,
tee->stats[TEE_STATS_SESSION_IDX].count,
tee->stats[TEE_STATS_SESSION_IDX].max,
tee->stats[TEE_STATS_SHM_IDX].count,
tee->stats[TEE_STATS_SHM_IDX].max);
}
static ssize_t info_show(struct device *device,
struct device_attribute *attr, char *buf)
{
struct tee *tee = dev_get_drvdata(device);
return snprintf(buf, PAGE_SIZE, "%s iminor=%d dev=\"%s\" state=%d\n",
dev_name(tee->dev), tee->miscdev.minor,
dev_name(tee->miscdev.this_device), tee->state);
}
static ssize_t name_show(struct device *device,
struct device_attribute *attr, char *buf)
{
struct tee *tee = dev_get_drvdata(device);
return snprintf(buf, PAGE_SIZE, "%s\n", tee->name);
}
static ssize_t type_show(struct device *device,
struct device_attribute *attr, char *buf)
{
struct tee *tee = dev_get_drvdata(device);
return snprintf(buf, PAGE_SIZE, "%s\n", tee->ops->type);
}
static ssize_t refcount_show(struct device *device,
struct device_attribute *attr, char *buf)
{
struct tee *tee = dev_get_drvdata(device);
return snprintf(buf, PAGE_SIZE, "%d\n", atomic_read(&tee->refcount));
}
static ssize_t conf_show(struct device *device,
struct device_attribute *attr, char *buf)
{
struct tee *tee = dev_get_drvdata(device);
return snprintf(buf, PAGE_SIZE, "0x%08x\n", tee->conf);
}
static ssize_t test_show(struct device *device,
struct device_attribute *attr, char *buf)
{
struct tee *tee = dev_get_drvdata(device);
return snprintf(buf, PAGE_SIZE, "%08X\n", tee->test);
}
static ssize_t test_store(struct device *device,
struct device_attribute *attr, const char *buf,
size_t count)
{
struct tee *tee = dev_get_drvdata(device);
unsigned long val;
int status;
status = kstrtoul(buf, 0, &val);
if (status)
return status;
if ((tee->conf & TEE_CONF_TEST_MODE) == TEE_CONF_TEST_MODE)
tee->test = val;
return count;
}
/*
* A state-to-string lookup table, for exposing a human readable state
* via sysfs. Always keep in sync with enum tee_state
*/
static const char *const tee_state_string[] = {
"offline",
"online",
"suspended",
"running",
"crashed",
"invalid",
};
static ssize_t tee_show_state(struct device *device,
struct device_attribute *attr, char *buf)
{
struct tee *tee = dev_get_drvdata(device);
int state = tee->state > TEE_LAST ? TEE_LAST : tee->state;
return snprintf(buf, PAGE_SIZE, "%s (%d)\n", tee_state_string[state],
tee->state);
}
/*
* In the following, 0660 is (S_IWUGO | S_IRUGO)
*/
static struct device_attribute device_attrs[] = {
__ATTR_RO(dump),
__ATTR_RO(stat),
__ATTR_RO(info),
__ATTR(test, (0660), test_show, test_store),
__ATTR(state, S_IRUGO, tee_show_state, NULL),
__ATTR(name, S_IRUGO, name_show, NULL),
__ATTR(refcount, S_IRUGO, refcount_show, NULL),
__ATTR(type, S_IRUGO, type_show, NULL),
__ATTR(conf, S_IRUGO, conf_show, NULL),
};
void tee_init_sysfs(struct tee *tee)
{
int i, error = 0;
if (!tee)
return;
if (dev_get_drvdata(tee->miscdev.this_device) != tee) {
dev_err(_DEV(tee), "drvdata is not valid\n");
return;
}
for (i = 0; i < ARRAY_SIZE(device_attrs); i++) {
error =
device_create_file(tee->miscdev.this_device,
&device_attrs[i]);
if (error)
break;
}
if (error) {
while (--i >= 0)
device_remove_file(tee->miscdev.this_device,
&device_attrs[i]);
}
/* location /sys/class/<class name>/<dev_name()>/<name> ->
* /sys/class/misc/teelx00/info */
}
void tee_cleanup_sysfs(struct tee *tee)
{
int i;
if (!tee)
return;
for (i = 0; i < ARRAY_SIZE(device_attrs); i++)
device_remove_file(tee->miscdev.this_device, &device_attrs[i]);
}

View File

@@ -0,0 +1,21 @@
/*
* Copyright (c) 2014, STMicroelectronics International N.V.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License Version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef __TEE_SYSFS_H__
#define __TEE_SYSFS_H__
struct tee;
void tee_init_sysfs(struct tee *tee);
void tee_cleanup_sysfs(struct tee *tee);
#endif

View File

@@ -0,0 +1,86 @@
/*
* Copyright (c) 2015, Linaro Limited
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License Version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/types.h>
#include <linux/completion.h>
#include <linux/slab.h>
#include "tee_wait_queue.h"
struct tee_wait_queue {
struct list_head link;
struct completion comp;
u32 key;
};
void tee_wait_queue_init(struct tee_wait_queue_private *priv)
{
mutex_init(&priv->mu);
INIT_LIST_HEAD(&priv->db);
}
EXPORT_SYMBOL(tee_wait_queue_init);
void tee_wait_queue_exit(struct tee_wait_queue_private *priv)
{
mutex_destroy(&priv->mu);
}
EXPORT_SYMBOL(tee_wait_queue_exit);
static struct tee_wait_queue *tee_wait_queue_get(struct device *dev,
struct tee_wait_queue_private *priv, u32 key)
{
struct tee_wait_queue *w;
mutex_lock(&priv->mu);
list_for_each_entry(w, &priv->db, link)
if (w->key == key)
goto out;
w = kmalloc(sizeof(struct tee_wait_queue), GFP_KERNEL);
if (!w)
goto out;
init_completion(&w->comp);
w->key = key;
list_add_tail(&w->link, &priv->db);
out:
mutex_unlock(&priv->mu);
return w;
}
void tee_wait_queue_sleep(struct device *dev,
struct tee_wait_queue_private *priv, u32 key)
{
struct tee_wait_queue *w = tee_wait_queue_get(dev, priv, key);
if (!w)
return;
wait_for_completion(&w->comp);
mutex_lock(&priv->mu);
list_del(&w->link);
mutex_unlock(&priv->mu);
kfree(w);
}
EXPORT_SYMBOL(tee_wait_queue_sleep);
void tee_wait_queue_wakeup(struct device *dev,
struct tee_wait_queue_private *priv, u32 key)
{
struct tee_wait_queue *w = tee_wait_queue_get(dev, priv, key);
if (!w)
return;
complete(&w->comp);
}
EXPORT_SYMBOL(tee_wait_queue_wakeup);

View File

@@ -0,0 +1,32 @@
/*
* Copyright (c) 2015, Linaro Limited
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License Version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef TEE_WAIT_QUEUE_H
#define TEE_WAIT_QUEUE_H
#include <linux/mutex.h>
#include <linux/list.h>
#include <linux/device.h>
struct tee_wait_queue_private {
struct mutex mu;
struct list_head db;
};
void tee_wait_queue_init(struct tee_wait_queue_private *priv);
void tee_wait_queue_exit(struct tee_wait_queue_private *priv);
void tee_wait_queue_sleep(struct device *dev,
struct tee_wait_queue_private *priv, u32 key);
void tee_wait_queue_wakeup(struct device *dev,
struct tee_wait_queue_private *priv, u32 key);
#endif /*TEE_WAIT_QUEUE_H*/

View File

@@ -0,0 +1,202 @@
/*
* Copyright (c) 2013-2014, ARM Limited and Contributors. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of the ARM nor the names of its contributors may be used
* to endorse or promote products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/dts-v1/;
/memreserve/ 0x81000000 0x00100000;
/memreserve/ 0x80000000 0x00010000;
/ {
};
/ {
model = "FVP Foundation";
compatible = "arm,fvp-base", "arm,vexpress";
interrupt-parent = <&gic>;
#address-cells = <2>;
#size-cells = <2>;
chosen { };
aliases {
serial0 = &v2m_serial0;
serial1 = &v2m_serial1;
serial2 = &v2m_serial2;
serial3 = &v2m_serial3;
};
psci {
compatible = "arm,psci";
method = "smc";
cpu_suspend = <0xc4000001>;
cpu_off = <0x84000002>;
cpu_on = <0xc4000003>;
};
cpus {
#address-cells = <2>;
#size-cells = <0>;
cpu@0 {
device_type = "cpu";
compatible = "arm,armv8";
reg = <0x0 0x0>;
enable-method = "psci";
};
cpu@1 {
device_type = "cpu";
compatible = "arm,armv8";
reg = <0x0 0x1>;
enable-method = "psci";
};
cpu@2 {
device_type = "cpu";
compatible = "arm,armv8";
reg = <0x0 0x2>;
enable-method = "psci";
};
cpu@3 {
device_type = "cpu";
compatible = "arm,armv8";
reg = <0x0 0x3>;
enable-method = "psci";
};
};
memory@80000000 {
device_type = "memory";
reg = <0x00000000 0x80000000 0 0x7F000000>,
<0x00000008 0x80000000 0 0x80000000>;
};
gic: interrupt-controller@2f000000 {
compatible = "arm,cortex-a15-gic", "arm,cortex-a9-gic";
#interrupt-cells = <3>;
#address-cells = <0>;
interrupt-controller;
reg = <0x0 0x2f000000 0 0x10000>,
<0x0 0x2c000000 0 0x2000>,
<0x0 0x2c010000 0 0x2000>,
<0x0 0x2c02F000 0 0x2000>;
interrupts = <1 9 0xf04>;
};
timer {
compatible = "arm,armv8-timer";
interrupts = <1 13 0xff01>,
<1 14 0xff01>,
<1 11 0xff01>,
<1 10 0xff01>;
clock-frequency = <100000000>;
};
timer@2a810000 {
compatible = "arm,armv7-timer-mem";
reg = <0x0 0x2a810000 0x0 0x10000>;
clock-frequency = <100000000>;
#address-cells = <2>;
#size-cells = <2>;
ranges;
frame@2a830000 {
frame-number = <1>;
interrupts = <0 26 4>;
reg = <0x0 0x2a830000 0x0 0x10000>;
};
};
pmu {
compatible = "arm,armv8-pmuv3";
interrupts = <0 60 4>,
<0 61 4>,
<0 62 4>,
<0 63 4>;
};
smb {
compatible = "simple-bus";
#address-cells = <2>;
#size-cells = <1>;
ranges = <0 0 0 0x08000000 0x04000000>,
<1 0 0 0x14000000 0x04000000>,
<2 0 0 0x18000000 0x04000000>,
<3 0 0 0x1c000000 0x04000000>,
<4 0 0 0x0c000000 0x04000000>,
<5 0 0 0x10000000 0x04000000>;
#interrupt-cells = <1>;
interrupt-map-mask = <0 0 63>;
interrupt-map = <0 0 0 &gic 0 0 4>,
<0 0 1 &gic 0 1 4>,
<0 0 2 &gic 0 2 4>,
<0 0 3 &gic 0 3 4>,
<0 0 4 &gic 0 4 4>,
<0 0 5 &gic 0 5 4>,
<0 0 6 &gic 0 6 4>,
<0 0 7 &gic 0 7 4>,
<0 0 8 &gic 0 8 4>,
<0 0 9 &gic 0 9 4>,
<0 0 10 &gic 0 10 4>,
<0 0 11 &gic 0 11 4>,
<0 0 12 &gic 0 12 4>,
<0 0 13 &gic 0 13 4>,
<0 0 14 &gic 0 14 4>,
<0 0 15 &gic 0 15 4>,
<0 0 16 &gic 0 16 4>,
<0 0 17 &gic 0 17 4>,
<0 0 18 &gic 0 18 4>,
<0 0 19 &gic 0 19 4>,
<0 0 20 &gic 0 20 4>,
<0 0 21 &gic 0 21 4>,
<0 0 22 &gic 0 22 4>,
<0 0 23 &gic 0 23 4>,
<0 0 24 &gic 0 24 4>,
<0 0 25 &gic 0 25 4>,
<0 0 26 &gic 0 26 4>,
<0 0 27 &gic 0 27 4>,
<0 0 28 &gic 0 28 4>,
<0 0 29 &gic 0 29 4>,
<0 0 30 &gic 0 30 4>,
<0 0 31 &gic 0 31 4>,
<0 0 32 &gic 0 32 4>,
<0 0 33 &gic 0 33 4>,
<0 0 34 &gic 0 34 4>,
<0 0 35 &gic 0 35 4>,
<0 0 36 &gic 0 36 4>,
<0 0 37 &gic 0 37 4>,
<0 0 38 &gic 0 38 4>,
<0 0 39 &gic 0 39 4>,
<0 0 40 &gic 0 40 4>,
<0 0 41 &gic 0 41 4>,
<0 0 42 &gic 0 42 4>;
/include/ "fvp-foundation-motherboard.dtsi"
};
};

View File

@@ -0,0 +1,197 @@
/*
* Copyright (c) 2013-2014, ARM Limited and Contributors. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of the ARM nor the names of its contributors may be used
* to endorse or promote products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
motherboard {
arm,v2m-memory-map = "rs1";
compatible = "arm,vexpress,v2m-p1", "simple-bus";
#address-cells = <2>; /* SMB chipselect number and offset */
#size-cells = <1>;
#interrupt-cells = <1>;
ranges;
ethernet@2,02000000 {
compatible = "smsc,lan91c111";
reg = <2 0x02000000 0x10000>;
interrupts = <15>;
};
v2m_clk24mhz: clk24mhz {
compatible = "fixed-clock";
#clock-cells = <0>;
clock-frequency = <24000000>;
clock-output-names = "v2m:clk24mhz";
};
v2m_refclk1mhz: refclk1mhz {
compatible = "fixed-clock";
#clock-cells = <0>;
clock-frequency = <1000000>;
clock-output-names = "v2m:refclk1mhz";
};
v2m_refclk32khz: refclk32khz {
compatible = "fixed-clock";
#clock-cells = <0>;
clock-frequency = <32768>;
clock-output-names = "v2m:refclk32khz";
};
iofpga@3,00000000 {
compatible = "arm,amba-bus", "simple-bus";
#address-cells = <1>;
#size-cells = <1>;
ranges = <0 3 0 0x200000>;
v2m_sysreg: sysreg@010000 {
compatible = "arm,vexpress-sysreg";
reg = <0x010000 0x1000>;
gpio-controller;
#gpio-cells = <2>;
};
v2m_sysctl: sysctl@020000 {
compatible = "arm,sp810", "arm,primecell";
reg = <0x020000 0x1000>;
clocks = <&v2m_refclk32khz>, <&v2m_refclk1mhz>, <&v2m_clk24mhz>;
clock-names = "refclk", "timclk", "apb_pclk";
#clock-cells = <1>;
clock-output-names = "timerclken0", "timerclken1", "timerclken2", "timerclken3";
};
v2m_serial0: uart@090000 {
compatible = "arm,pl011", "arm,primecell";
reg = <0x090000 0x1000>;
interrupts = <5>;
clocks = <&v2m_clk24mhz>, <&v2m_clk24mhz>;
clock-names = "uartclk", "apb_pclk";
};
v2m_serial1: uart@0a0000 {
compatible = "arm,pl011", "arm,primecell";
reg = <0x0a0000 0x1000>;
interrupts = <6>;
clocks = <&v2m_clk24mhz>, <&v2m_clk24mhz>;
clock-names = "uartclk", "apb_pclk";
};
v2m_serial2: uart@0b0000 {
compatible = "arm,pl011", "arm,primecell";
reg = <0x0b0000 0x1000>;
interrupts = <7>;
clocks = <&v2m_clk24mhz>, <&v2m_clk24mhz>;
clock-names = "uartclk", "apb_pclk";
};
v2m_serial3: uart@0c0000 {
compatible = "arm,pl011", "arm,primecell";
reg = <0x0c0000 0x1000>;
interrupts = <8>;
clocks = <&v2m_clk24mhz>, <&v2m_clk24mhz>;
clock-names = "uartclk", "apb_pclk";
};
wdt@0f0000 {
compatible = "arm,sp805", "arm,primecell";
reg = <0x0f0000 0x1000>;
interrupts = <0>;
clocks = <&v2m_refclk32khz>, <&v2m_clk24mhz>;
clock-names = "wdogclk", "apb_pclk";
};
v2m_timer01: timer@110000 {
compatible = "arm,sp804", "arm,primecell";
reg = <0x110000 0x1000>;
interrupts = <2>;
clocks = <&v2m_sysctl 0>, <&v2m_sysctl 1>, <&v2m_clk24mhz>;
clock-names = "timclken1", "timclken2", "apb_pclk";
};
v2m_timer23: timer@120000 {
compatible = "arm,sp804", "arm,primecell";
reg = <0x120000 0x1000>;
interrupts = <3>;
clocks = <&v2m_sysctl 2>, <&v2m_sysctl 3>, <&v2m_clk24mhz>;
clock-names = "timclken1", "timclken2", "apb_pclk";
};
rtc@170000 {
compatible = "arm,pl031", "arm,primecell";
reg = <0x170000 0x1000>;
interrupts = <4>;
clocks = <&v2m_clk24mhz>;
clock-names = "apb_pclk";
};
virtio_block@0130000 {
compatible = "virtio,mmio";
reg = <0x130000 0x1000>;
interrupts = <0x2a>;
};
};
v2m_fixed_3v3: fixedregulator@0 {
compatible = "regulator-fixed";
regulator-name = "3V3";
regulator-min-microvolt = <3300000>;
regulator-max-microvolt = <3300000>;
regulator-always-on;
};
mcc {
compatible = "arm,vexpress,config-bus", "simple-bus";
arm,vexpress,config-bridge = <&v2m_sysreg>;
reset@0 {
compatible = "arm,vexpress-reset";
arm,vexpress-sysreg,func = <5 0>;
};
muxfpga@0 {
compatible = "arm,vexpress-muxfpga";
arm,vexpress-sysreg,func = <7 0>;
};
shutdown@0 {
compatible = "arm,vexpress-shutdown";
arm,vexpress-sysreg,func = <8 0>;
};
reboot@0 {
compatible = "arm,vexpress-reboot";
arm,vexpress-sysreg,func = <9 0>;
};
dvimode@0 {
compatible = "arm,vexpress-dvimode";
arm,vexpress-sysreg,func = <11 0>;
};
};
};

View File

@@ -0,0 +1,2 @@
These files is a temporary workaround until the driver has switched
to use CMA to allocate shared memory.

View File

@@ -0,0 +1,700 @@
/*
* Copyright (c) 2014, Linaro Limited
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License Version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef TEESMC_H
#define TEESMC_H
#ifndef ASM
/*
* This section depends on uint64_t, uint32_t uint8_t already being
* defined. Since this file is used in several different environments
* (secure world OS and normal world Linux kernel to start with) where
* stdint.h may not be available it's the responsibility of the one
* including this file to provide those types.
*/
/*
* Trusted OS SMC interface.
*
* The SMC interface follows SMC Calling Convention
* (ARM_DEN0028A_SMC_Calling_Convention).
*
* The primary objective of this API is to provide a transport layer on
* which a Global Platform compliant TEE interfaces can be deployed. But the
* interface can also be used for other implementations.
*
* This file is divided in two parts.
* Part 1 deals with passing parameters to Trusted Applications running in
* a trusted OS in secure world.
* Part 2 deals with the lower level handling of the SMC.
*/
/*
*******************************************************************************
* Part 1 - passing parameters to Trusted Applications
*******************************************************************************
*/
/*
* Same values as TEE_PARAM_* from TEE Internal API
*/
#define TEESMC_ATTR_TYPE_NONE 0
#define TEESMC_ATTR_TYPE_VALUE_INPUT 1
#define TEESMC_ATTR_TYPE_VALUE_OUTPUT 2
#define TEESMC_ATTR_TYPE_VALUE_INOUT 3
#define TEESMC_ATTR_TYPE_MEMREF_INPUT 5
#define TEESMC_ATTR_TYPE_MEMREF_OUTPUT 6
#define TEESMC_ATTR_TYPE_MEMREF_INOUT 7
#define TEESMC_ATTR_TYPE_MASK 0x7
/*
* Meta parameter to be absorbed by the Secure OS and not passed
* to the Trusted Application.
*
* One example of this is a struct teesmc_meta_open_session which
* is added to TEESMC{32,64}_CMD_OPEN_SESSION.
*/
#define TEESMC_ATTR_META 0x8
/*
* Used as an indication from normal world of compatible cache usage.
* 'I' stands for inner cache and 'O' for outer cache.
*/
#define TEESMC_ATTR_CACHE_I_NONCACHE 0x0
#define TEESMC_ATTR_CACHE_I_WRITE_THR 0x1
#define TEESMC_ATTR_CACHE_I_WRITE_BACK 0x2
#define TEESMC_ATTR_CACHE_O_NONCACHE 0x0
#define TEESMC_ATTR_CACHE_O_WRITE_THR 0x4
#define TEESMC_ATTR_CACHE_O_WRITE_BACK 0x8
#define TEESMC_ATTR_CACHE_NONCACHE (TEESMC_ATTR_CACHE_I_NONCACHE | \
TEESMC_ATTR_CACHE_O_NONCACHE)
#define TEESMC_ATTR_CACHE_DEFAULT (TEESMC_ATTR_CACHE_I_WRITE_BACK | \
TEESMC_ATTR_CACHE_O_WRITE_BACK)
#define TEESMC_ATTR_CACHE_SHIFT 4
#define TEESMC_ATTR_CACHE_MASK 0xf
#define TEESMC_CMD_OPEN_SESSION 0
#define TEESMC_CMD_INVOKE_COMMAND 1
#define TEESMC_CMD_CLOSE_SESSION 2
#define TEESMC_CMD_CANCEL 3
/**
* struct teesmc32_param_memref - memory reference
* @buf_ptr: Address of the buffer
* @size: Size of the buffer
*
* Secure and normal world communicates pointer via physical address instead of
* the virtual address with is usually used for pointers. This is because
* Secure and normal world has completely independant memory mapping. Normal
* world can even have a hypervisor which need to translate the guest
* physical address (AKA IPA in ARM lingo) to a real physical address
* before passing the structure to secure world.
*/
struct teesmc32_param_memref {
uint32_t buf_ptr;
uint32_t size;
};
/**
* struct teesmc32_param_memref - memory reference
* @buf_ptr: Address of the buffer
* @size: Size of the buffer
*
* See description of struct teesmc32_param_memref.
*/
struct teesmc64_param_memref {
uint64_t buf_ptr;
uint64_t size;
};
/**
* struct teesmc32_param_value - values
* @a: first value
* @b: second value
*/
struct teesmc32_param_value {
uint32_t a;
uint32_t b;
};
/**
* struct teesmc64_param_value - values
* @a: first value
* @b: second value
*/
struct teesmc64_param_value {
uint64_t a;
uint64_t b;
};
/**
* struct teesmc32_param - parameter
* @attr: attributes
* @memref: a memory reference
* @value: a value
*
* attr & TEESMC_ATTR_TYPE_MASK indicates if memref or value is used in the
* union. TEESMC_ATTR_TYPE_VALUE_* indicates value and
* TEESMC_ATTR_TYPE_MEMREF_* indicates memref. TEESMC_ATTR_TYPE_NONE
* indicates that none of the members are used.
*/
struct teesmc32_param {
uint32_t attr;
union {
struct teesmc32_param_memref memref;
struct teesmc32_param_value value;
} u;
};
/**
* struct teesmc64_param - parameter
* @attr: attributes
* @memref: a memory reference
* @value: a value
*
* See description of union teesmc32_param.
*/
struct teesmc64_param {
uint64_t attr;
union {
struct teesmc64_param_memref memref;
struct teesmc64_param_value value;
} u;
};
/**
* struct teesmc32_arg - SMC argument for Trusted OS
* @cmd: Command, one of TEESMC_CMD_*
* @ta_func: Trusted Application function, specific to the Trusted Application,
* used if cmd == TEESMC_CMD_INVOKE_COMMAND
* @session: In parameter for all TEESMC_CMD_* except
* TEESMC_CMD_OPEN_SESSION where it's an output paramter instead
* @ret: return value
* @ret_origin: origin of the return value
* @num_params: number of parameters supplied to the OS Command
* @params: the parameters supplied to the OS Command
*
* All normal SMC calls to Trusted OS uses this struct. If cmd requires
* further information than what these field holds it can be passed as a
* parameter tagged as meta (setting the TEESMC_ATTR_META bit in
* corresponding param_attrs). This is used for TEESMC_CMD_OPEN_SESSION
* to pass a struct teesmc32_meta_open_session which is needed find the
* Trusted Application and to indicate the credentials of the client.
*/
struct teesmc32_arg {
uint32_t cmd;
uint32_t ta_func;
uint32_t session;
uint32_t ret;
uint32_t ret_origin;
uint32_t num_params;
/*
* Commented out elements used to visualize the layout dynamic part
* of the struct. Note that these fields are not available at all
* if num_params == 0.
*
* params is accessed through the macro TEESMC32_GET_PARAMS
*/
/* struct teesmc32_param params[num_params]; */
};
/**
* TEESMC32_GET_PARAMS - return pointer to union teesmc32_param *
*
* @x: Pointer to a struct teesmc32_arg
*
* Returns a pointer to the params[] inside a struct teesmc32_arg.
*/
#define TEESMC32_GET_PARAMS(x) \
(struct teesmc32_param *)(((struct teesmc32_arg *)(x)) + 1)
/**
* TEESMC32_GET_ARG_SIZE - return size of struct teesmc32_arg
*
* @num_params: Number of parameters embedded in the struct teesmc32_arg
*
* Returns the size of the struct teesmc32_arg together with the number
* of embedded paramters.
*/
#define TEESMC32_GET_ARG_SIZE(num_params) \
(sizeof(struct teesmc32_arg) + \
sizeof(struct teesmc32_param) * (num_params))
/**
* struct teesmc64_arg - SMC argument for Trusted OS
* @cmd: OS Command, one of TEESMC_CMD_*
* @ta_func: Trusted Application function, specific to the Trusted Application
* @session: In parameter for all TEESMC_CMD_* but
* TEESMC_CMD_OPEN_SESSION
* @ret: return value
* @ret_origin: origin of the return value
* @num_params: number of parameters supplied to the OS Command
* @params: the parameters supplied to the OS Command
*
* See description of struct teesmc32_arg.
*/
struct teesmc64_arg {
uint64_t cmd;
uint64_t ta_func;
uint64_t session;
uint64_t ret;
uint64_t ret_origin;
uint64_t num_params;
/*
* Commented out elements used to visualize the layout dynamic part
* of the struct. Note that these fields are not available at all
* if num_params == 0.
*
* params is accessed through the macro TEESMC64_GET_PARAMS
*/
/* struct teesmc64_param params[num_params]; */
};
/**
* TEESMC64_GET_PARAMS - return pointer to union teesmc64_param *
*
* @x: Pointer to a struct teesmc64_arg
*
* Returns a pointer to the params[] inside a struct teesmc64_arg.
*/
#define TEESMC64_GET_PARAMS(x) \
(struct teesmc64_param *)(((struct teesmc64_arg *)(x)) + 1)
/**
* TEESMC64_GET_ARG_SIZE - return size of struct teesmc64_arg
*
* @num_params: Number of parameters embedded in the struct teesmc64_arg
*
* Returns the size of the struct teesmc64_arg together with the number
* of embedded paramters.
*/
#define TEESMC64_GET_ARG_SIZE(num_params) \
(sizeof(struct teesmc64_arg) + \
sizeof(union teesmc64_param) * (num_params))
#define TEESMC_UUID_LEN 16
/**
* struct teesmc_meta_open_session - additional parameters for
* TEESMC32_CMD_OPEN_SESSION and
* TEESMC64_CMD_OPEN_SESSION
* @uuid: UUID of the Trusted Application
* @clnt_uuid: UUID of client
* @clnt_login: Login class of client, TEE_LOGIN_* if being Global Platform
* compliant
*
* This struct is passed in the first parameter as an input memref tagged
* as meta on an TEESMC{32,64}_CMD_OPEN_SESSION cmd. It's important
* that it really is the first parameter to make it easy for an eventual
* hypervisor to inspect and possibly update clnt_* values.
*/
struct teesmc_meta_open_session {
uint8_t uuid[TEESMC_UUID_LEN];
uint8_t clnt_uuid[TEESMC_UUID_LEN];
uint32_t clnt_login;
};
#endif /*!ASM*/
/*
*******************************************************************************
* Part 2 - low level SMC interaction
*******************************************************************************
*/
#define TEESMC_32 0
#define TEESMC_64 0x40000000
#define TEESMC_FAST_CALL 0x80000000
#define TEESMC_STD_CALL 0
#define TEESMC_OWNER_MASK 0x3F
#define TEESMC_OWNER_SHIFT 24
#define TEESMC_FUNC_MASK 0xFFFF
#define TEESMC_IS_FAST_CALL(smc_val) ((smc_val) & TEESMC_FAST_CALL)
#define TEESMC_IS_64(smc_val) ((smc_val) & TEESMC_64)
#define TEESMC_FUNC_NUM(smc_val) ((smc_val) & TEESMC_FUNC_MASK)
#define TEESMC_OWNER_NUM(smc_val) (((smc_val) >> TEESMC_OWNER_SHIFT) & \
TEESMC_OWNER_MASK)
#define TEESMC_CALL_VAL(type, calling_convention, owner, func_num) \
((type) | (calling_convention) | \
(((owner) & TEESMC_OWNER_MASK) << TEESMC_OWNER_SHIFT) |\
((func_num) & TEESMC_FUNC_MASK))
#define TEESMC_OWNER_ARCH 0
#define TEESMC_OWNER_CPU 1
#define TEESMC_OWNER_SIP 2
#define TEESMC_OWNER_OEM 3
#define TEESMC_OWNER_STANDARD 4
#define TEESMC_OWNER_TRUSTED_APP 48
#define TEESMC_OWNER_TRUSTED_OS 50
#define TEESMC_OWNER_TRUSTED_OS_API 63
/*
* Function specified by SMC Calling convention.
*/
#define TEESMC32_FUNCID_CALLS_COUNT 0xFF00
#define TEESMC32_CALLS_COUNT \
TEESMC_CALL_VAL(TEESMC_32, TEESMC_FAST_CALL, \
TEESMC_OWNER_TRUSTED_OS_API, \
TEESMC32_FUNCID_CALLS_COUNT)
/*
* Function specified by SMC Calling convention
*
* Return one of the following UIDs if using API specified in this file
* without further extentions:
* 65cb6b93-af0c-4617-8ed6-644a8d1140f8 : Only 32 bit calls are supported
* 65cb6b93-af0c-4617-8ed6-644a8d1140f9 : Both 32 and 64 bit calls are supported
*/
#define TEESMC_UID_R0 0x65cb6b93
#define TEESMC_UID_R1 0xaf0c4617
#define TEESMC_UID_R2 0x8ed6644a
#define TEESMC_UID32_R3 0x8d1140f8
#define TEESMC_UID64_R3 0x8d1140f9
#define TEESMC32_FUNCID_CALLS_UID 0xFF01
#define TEESMC32_CALLS_UID \
TEESMC_CALL_VAL(TEESMC_32, TEESMC_FAST_CALL, \
TEESMC_OWNER_TRUSTED_OS_API, \
TEESMC32_FUNCID_CALLS_UID)
/*
* Function specified by SMC Calling convention
*
* Returns 1.0 if using API specified in this file without further extentions.
*/
#define TEESMC_REVISION_MAJOR 1
#define TEESMC_REVISION_MINOR 0
#define TEESMC32_FUNCID_CALLS_REVISION 0xFF03
#define TEESMC32_CALLS_REVISION \
TEESMC_CALL_VAL(TEESMC_32, TEESMC_FAST_CALL, \
TEESMC_OWNER_TRUSTED_OS_API, \
TEESMC32_FUNCID_CALLS_REVISION)
/*
* Get UUID of Trusted OS.
*
* Used by non-secure world to figure out which Trusted OS is installed.
* Note that returned UUID is the UUID of the Trusted OS, not of the API.
*
* Returns UUID in r0-4/w0-4 in the same way as TEESMC32_CALLS_UID
* described above.
*/
#define TEESMC_FUNCID_GET_OS_UUID 0
#define TEESMC32_CALL_GET_OS_UUID \
TEESMC_CALL_VAL(TEESMC_32, TEESMC_FAST_CALL, TEESMC_OWNER_TRUSTED_OS, \
TEESMC_FUNCID_GET_OS_UUID)
/*
* Get revision of Trusted OS.
*
* Used by non-secure world to figure out which version of the Trusted OS
* is installed. Note that the returned revision is the revision of the
* Trusted OS, not of the API.
*
* Returns revision in r0-1/w0-1 in the same way as TEESMC32_CALLS_REVISION
* described above.
*/
#define TEESMC_FUNCID_GET_OS_REVISION 1
#define TEESMC32_CALL_GET_OS_REVISION \
TEESMC_CALL_VAL(TEESMC_32, TEESMC_FAST_CALL, TEESMC_OWNER_TRUSTED_OS, \
TEESMC_FUNCID_GET_OS_REVISION)
/*
* Call with struct teesmc32_arg as argument
*
* Call register usage:
* r0/x0 SMC Function ID, TEESMC32_CALL_WITH_ARG
* r1/x1 Physical pointer to a struct teesmc32_arg
* r2-6/x2-6 Not used
* r7/x7 Hypervisor Client ID register
*
* Normal return register usage:
* r0/x0 Return value, TEESMC_RETURN_*
* r1-3/x1-3 Not used
* r4-7/x4-7 Preserved
*
* Ebusy return register usage:
* r0/x0 Return value, TEESMC_RETURN_EBUSY
* r1-3/x1-3 Preserved
* r4-7/x4-7 Preserved
*
* RPC return register usage:
* r0/x0 Return value, TEESMC_RETURN_IS_RPC(val)
* r1-2/x1-2 RPC parameters
* r3-7/x3-7 Resume information, must be preserved
*
* Possible return values:
* TEESMC_RETURN_UNKNOWN_FUNCTION Trusted OS does not recognize this
* function.
* TEESMC_RETURN_OK Call completed, result updated in
* the previously supplied struct
* teesmc32_arg.
* TEESMC_RETURN_EBUSY Trusted OS busy, try again later.
* TEESMC_RETURN_EBADADDR Bad physcial pointer to struct
* teesmc32_arg.
* TEESMC_RETURN_EBADCMD Bad/unknown cmd in struct teesmc32_arg
* TEESMC_RETURN_IS_RPC() Call suspended by RPC call to normal
* world.
*/
#define TEESMC_FUNCID_CALL_WITH_ARG 2
#define TEESMC32_CALL_WITH_ARG \
TEESMC_CALL_VAL(TEESMC_32, TEESMC_STD_CALL, TEESMC_OWNER_TRUSTED_OS, \
TEESMC_FUNCID_CALL_WITH_ARG)
/* Same as TEESMC32_CALL_WITH_ARG but a "fast call". */
#define TEESMC32_FASTCALL_WITH_ARG \
TEESMC_CALL_VAL(TEESMC_32, TEESMC_FAST_CALL, TEESMC_OWNER_TRUSTED_OS, \
TEESMC_FUNCID_CALL_WITH_ARG)
/*
* Call with struct teesmc64_arg as argument
*
* See description of TEESMC32_CALL_WITH_ARG above, uses struct
* teesmc64_arg in x1 instead.
*/
#define TEESMC64_CALL_WITH_ARG \
TEESMC_CALL_VAL(TEESMC_64, TEESMC_STD_CALL, TEESMC_OWNER_TRUSTED_OS, \
TEESMC_FUNCID_CALL_WITH_ARG)
/* Same as TEESMC64_CALL_WITH_ARG but a "fast call". */
#define TEESMC64_FASTCALL_WITH_ARG \
TEESMC_CALL_VAL(TEESMC_64, TEESMC_FAST_CALL, TEESMC_OWNER_TRUSTED_OS, \
TEESMC_FUNCID_CALL_WITH_ARG)
/*
* Resume from RPC (for example after processing an IRQ)
*
* Call register usage:
* r0/x0 SMC Function ID,
* TEESMC32_CALL_RETURN_FROM_RPC or
* TEESMC32_FASTCALL_RETURN_FROM_RPC
* r1-3/x1-3 Value of r1-3/x1-3 when TEESMC32_CALL_WITH_ARG returned
* TEESMC_RETURN_RPC in r0/x0
*
* Return register usage is the same as for TEESMC32_CALL_WITH_ARG above.
*
* Possible return values
* TEESMC_RETURN_UNKNOWN_FUNCTION Trusted OS does not recognize this
* function.
* TEESMC_RETURN_OK Original call completed, result
* updated in the previously supplied.
* struct teesmc32_arg
* TEESMC_RETURN_RPC Call suspended by RPC call to normal
* world.
* TEESMC_RETURN_EBUSY Trusted OS busy, try again later.
* TEESMC_RETURN_ERESUME Resume failed, the opaque resume
* information was corrupt.
*/
#define TEESMC_FUNCID_RETURN_FROM_RPC 3
#define TEESMC32_CALL_RETURN_FROM_RPC \
TEESMC_CALL_VAL(TEESMC_32, TEESMC_STD_CALL, TEESMC_OWNER_TRUSTED_OS, \
TEESMC_FUNCID_RETURN_FROM_RPC)
/* Same as TEESMC32_CALL_RETURN_FROM_RPC but a "fast call". */
#define TEESMC32_FASTCALL_RETURN_FROM_RPC \
TEESMC_CALL_VAL(TEESMC_32, TEESMC_FAST_CALL, TEESMC_OWNER_TRUSTED_OS, \
TEESMC_FUNCID_RETURN_FROM_RPC)
/*
* Resume from RPC (for example after processing an IRQ)
*
* See description of TEESMC32_CALL_RETURN_FROM_RPC above, used when
* it's a 64bit call that has returned.
*/
#define TEESMC64_CALL_RETURN_FROM_RPC \
TEESMC_CALL_VAL(TEESMC_64, TEESMC_STD_CALL, TEESMC_OWNER_TRUSTED_OS, \
TEESMC_FUNCID_RETURN_FROM_RPC)
/* Same as TEESMC64_CALL_RETURN_FROM_RPC but a "fast call". */
#define TEESMC64_FASTCALL_RETURN_FROM_RPC \
TEESMC_CALL_VAL(TEESMC_64, TEESMC_FAST_CALL, TEESMC_OWNER_TRUSTED_OS, \
TEESMC_FUNCID_RETURN_FROM_RPC)
/*
* From secure monitor to Trusted OS, handle FIQ
*
* A virtual call which is injected by the Secure Monitor when an FIQ is
* raised while in normal world (SCR_NS is set). The monitor restores
* secure architecture registers and secure EL_SP1 and jumps to previous
* secure EL3_ELR. Trusted OS should preserve all general purpose
* registers.
*
* Call register usage:
* r0/x0 SMC Function ID, TEESMC32_CALL_HANDLE_FIQ
* r1-7/x1-7 Not used, but must be preserved
*
* Return register usage:
* Note used
*/
#define TEESMC_FUNCID_CALL_HANDLE_FIQ 0xf000
#define TEESMC32_CALL_HANDLE_FIQ \
TEESMC_CALL_VAL(TEESMC_32, TEESMC_FAST_CALL, TEESMC_OWNER_TRUSTED_OS, \
TEESMC_FUNCID_CALL_HANDLE_FIQ)
#define TEESMC_RETURN_RPC_PREFIX_MASK 0xFFFF0000
#define TEESMC_RETURN_RPC_PREFIX 0xFFFF0000
#define TEESMC_RETURN_RPC_FUNC_MASK 0x0000FFFF
#define TEESMC_RETURN_GET_RPC_FUNC(ret) ((ret) & TEESMC_RETURN_RPC_FUNC_MASK)
#define TEESMC_RPC_VAL(func) ((func) | TEESMC_RETURN_RPC_PREFIX)
/*
* Allocate argument memory for RPC parameter passing.
* Argument memory is used to hold a struct teesmc32_arg.
*
* "Call" register usage:
* r0/x0 This value, TEESMC_RETURN_RPC_ALLOC
* r1/x1 Size in bytes of required argument memory
* r2-7/x2-7 Resume information, must be preserved
*
* "Return" register usage:
* r0/x0 SMC Function ID, TEESMC32_CALL_RETURN_FROM_RPC if it was an
* AArch32 SMC return or TEESMC64_CALL_RETURN_FROM_RPC for
* AArch64 SMC return
* r1/x1 Physical pointer to allocated argument memory, 0 if size
* was 0 or if memory can't be allocated
* r2-7/x2-7 Preserved
*/
#define TEESMC_RPC_FUNC_ALLOC_ARG 0
#define TEESMC_RETURN_RPC_ALLOC_ARG \
TEESMC_RPC_VAL(TEESMC_RPC_FUNC_ALLOC_ARG)
/*
* Allocate payload memory for RPC parameter passing.
* Payload memory is used to hold the memory referred to by struct
* teesmc32_param_memref.
*
* "Call" register usage:
* r0/x0 This value, TEESMC_RETURN_RPC_ALLOC
* r1/x1 Size in bytes of required payload memory
* r2-7/x2-7 Resume information, must be preserved
*
* "Return" register usage:
* r0/x0 SMC Function ID, TEESMC32_CALL_RETURN_FROM_RPC if it was an
* AArch32 SMC return or TEESMC64_CALL_RETURN_FROM_RPC for
* AArch64 SMC return
* r1/x1 Physical pointer to allocated payload memory, 0 if size
* was 0 or if memory can't be allocated
* r2-7/x2-7 Preserved
*/
#define TEESMC_RPC_FUNC_ALLOC_PAYLOAD 1
#define TEESMC_RETURN_RPC_ALLOC_PAYLOAD \
TEESMC_RPC_VAL(TEESMC_RPC_FUNC_ALLOC_PAYLOAD)
/*
* Free memory previously allocated by TEESMC_RETURN_RPC_ALLOC_ARG.
*
* "Call" register usage:
* r0/x0 This value, TEESMC_RETURN_RPC_FREE
* r1/x1 Physical pointer to previously allocated argument memory
* r2-7/x2-7 Resume information, must be preserved
*
* "Return" register usage:
* r0/x0 SMC Function ID, TEESMC32_CALL_RETURN_FROM_RPC if it was an
* AArch32 SMC return or TEESMC64_CALL_RETURN_FROM_RPC for
* AArch64 SMC return
* r1/x1 Not used
* r2-7/x2-7 Preserved
*/
#define TEESMC_RPC_FUNC_FREE_ARG 2
#define TEESMC_RETURN_RPC_FREE_ARG TEESMC_RPC_VAL(TEESMC_RPC_FUNC_FREE_ARG)
/*
* Free memory previously allocated by TEESMC_RETURN_RPC_ALLOC_PAYLOAD.
*
* "Call" register usage:
* r0/x0 This value, TEESMC_RETURN_RPC_FREE
* r1/x1 Physical pointer to previously allocated payload memory
* r3-7/x3-7 Resume information, must be preserved
*
* "Return" register usage:
* r0/x0 SMC Function ID, TEESMC32_CALL_RETURN_FROM_RPC if it was an
* AArch32 SMC return or TEESMC64_CALL_RETURN_FROM_RPC for
* AArch64 SMC return
* r1-2/x1-2 Not used
* r3-7/x3-7 Preserved
*/
#define TEESMC_RPC_FUNC_FREE_PAYLOAD 3
#define TEESMC_RETURN_RPC_FREE_PAYLOAD \
TEESMC_RPC_VAL(TEESMC_RPC_FUNC_FREE_PAYLOAD)
/*
* Deliver an IRQ in normal world.
*
* "Call" register usage:
* r0/x0 TEESMC_RETURN_RPC_IRQ
* r1-7/x1-7 Resume information, must be preserved
*
* "Return" register usage:
* r0/x0 SMC Function ID, TEESMC32_CALL_RETURN_FROM_RPC if it was an
* AArch32 SMC return or TEESMC64_CALL_RETURN_FROM_RPC for
* AArch64 SMC return
* r1-7/x1-7 Preserved
*/
#define TEESMC_RPC_FUNC_IRQ 4
#define TEESMC_RETURN_RPC_IRQ TEESMC_RPC_VAL(TEESMC_RPC_FUNC_IRQ)
/*
* Do an RPC request. The supplied struct teesmc{32,64}_arg tells which
* request to do and the paramters for the request. The following fields
* are used (the rest are unused):
* - cmd the Request ID
* - ret return value of the request, filled in by normal world
* - num_params number of parameters for the request
* - params the parameters
* - param_attrs attributes of the parameters
*
* "Call" register usage:
* r0/x0 TEESMC_RETURN_RPC_CMD
* r1/x1 Physical pointer to a struct teesmc32_arg if returning from
* a AArch32 SMC or a struct teesmc64_arg if returning from a
* AArch64 SMC, must be preserved, only the data should
* be updated
* r2-7/x2-7 Resume information, must be preserved
*
* "Return" register usage:
* r0/x0 SMC Function ID, TEESMC32_CALL_RETURN_FROM_RPC if it was an
* AArch32 SMC return or TEESMC64_CALL_RETURN_FROM_RPC for
* AArch64 SMC return
* r1-7/x1-7 Preserved
*/
#define TEESMC_RPC_FUNC_CMD 5
#define TEESMC_RETURN_RPC_CMD TEESMC_RPC_VAL(TEESMC_RPC_FUNC_CMD)
/* Returned in r0 */
#define TEESMC_RETURN_UNKNOWN_FUNCTION 0xFFFFFFFF
/* Returned in r0 only from Trusted OS functions */
#define TEESMC_RETURN_OK 0x0
#define TEESMC_RETURN_EBUSY 0x1
#define TEESMC_RETURN_ERESUME 0x2
#define TEESMC_RETURN_EBADADDR 0x3
#define TEESMC_RETURN_EBADCMD 0x4
#define TEESMC_RETURN_IS_RPC(ret) \
(((ret) & TEESMC_RETURN_RPC_PREFIX_MASK) == TEESMC_RETURN_RPC_PREFIX)
/*
* Returned in r1 by Trusted OS functions if r0 = TEESMC_RETURN_RPC
*/
#define TEESMC_RPC_REQUEST_IRQ 0x0
#endif /* TEESMC_H */

View File

@@ -0,0 +1,147 @@
/*
* Copyright (c) 2014, STMicroelectronics International N.V.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License Version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef TEESMC_ST_H
#define TEESMC_ST_H
#define TEESMC_ST_RETURN_NOTAVAIL 0x5700
/*
* Get Shared Memory Config
*
* Returns the Secure/Non-secure shared memory config.
*
* Call register usage:
* r0 SMC Function ID, TEESMC32_ST_FASTCALL_GET_SHM_CONFIG
* r1-6 Not used
* r7 Hypervisor Client ID register
*
* Have config return register usage:
* r0 TEESMC_RETURN_OK
* r1 Physical address of start of SHM
* r2 Size of of SHM
* r3 1 if SHM is cached, 0 if uncached.
* r4-7 Preserved
*
* Not available register usage:
* r0 TEESMC_ST_RETURN_NOTAVAIL
* r1-3 Not used
* r4-7 Preserved
*/
#define TEESMC_ST_FUNCID_GET_SHM_CONFIG 0x5700
#define TEESMC32_ST_FASTCALL_GET_SHM_CONFIG \
TEESMC_CALL_VAL(TEESMC_32, TEESMC_FAST_CALL, TEESMC_OWNER_TRUSTED_OS, \
TEESMC_ST_FUNCID_GET_SHM_CONFIG)
/*
* Configures TZ/NS shared mutex for outer cache maintenance
*
* Disables, enables usage of outercache mutex.
* Returns or sets physical address of outercache mutex.
*
* Call register usage:
* r0 SMC Function ID, TEESMC32_ST_FASTCALL_L2CC_MUTEX
* r1 TEESMC_ST_L2CC_MUTEX_GET_ADDR Get physical address of mutex
* TEESMC_ST_L2CC_MUTEX_SET_ADDR Set physical address of mutex
* TEESMC_ST_L2CC_MUTEX_ENABLE Enable usage of mutex
* TEESMC_ST_L2CC_MUTEX_DISABLE Disable usage of mutex
* r2 if r1 == TEESMC_ST_L2CC_MUTEX_SET_ADDR, physical address of mutex
* r3-6 Not used
* r7 Hypervisor Client ID register
*
* Have config return register usage:
* r0 TEESMC_RETURN_OK
* r1 Preserved
* r2 if r1 == 0, physical address of L2CC mutex
* r3-7 Preserved
*
* Error return register usage:
* r0 TEESMC_ST_RETURN_NOTAVAIL Physical address not available
* TEESMC_RETURN_EBADADDR Bad supplied physical address
* TEESMC_RETURN_EBADCMD Unsupported value in r1
* r1-7 Preserved
*/
#define TEESMC_ST_L2CC_MUTEX_GET_ADDR 0
#define TEESMC_ST_L2CC_MUTEX_SET_ADDR 1
#define TEESMC_ST_L2CC_MUTEX_ENABLE 2
#define TEESMC_ST_L2CC_MUTEX_DISABLE 3
#define TEESMC_ST_FUNCID_L2CC_MUTEX 0x5701
#define TEESMC32_ST_FASTCALL_L2CC_MUTEX \
TEESMC_CALL_VAL(TEESMC_32, TEESMC_FAST_CALL, TEESMC_OWNER_TRUSTED_OS, \
TEESMC_ST_FUNCID_L2CC_MUTEX)
/*
* Allocate payload memory for RPC parameter passing.
*
* "Call" register usage:
* r0/x0 This value, TEESMC_RETURN_ST_RPC_ALLOC_PAYLOAD
* r1/x1 Size in bytes of required payload memory
* r2/x2 Not used
* r3-7/x3-7 Resume information, must be preserved
*
* "Return" register usage:
* r0/x0 SMC Function ID, TEESMC32_CALL_RETURN_FROM_RPC if it was an
* AArch32 SMC return or TEESMC64_CALL_RETURN_FROM_RPC for
* AArch64 SMC return
* r1/x1 Physical pointer to allocated payload memory, 0 if size
* was 0 or if memory can't be allocated
* r2/x2 Shared memory cookie used when freeing the memory
* r3-7/x3-7 Preserved
*/
#define TEESMC_ST_RPC_FUNC_ALLOC_PAYLOAD 0x5700
#define TEESMC_RETURN_ST_RPC_ALLOC_PAYLOAD \
TEESMC_RPC_VAL(TEESMC_ST_RPC_FUNC_ALLOC_PAYLOAD)
/*
* Free memory previously allocated by TEESMC_RETURN_ST_RPC_ALLOC_PAYLOAD
*
* "Call" register usage:
* r0/x0 This value, TEESMC_RETURN_ST_RPC_FREE_PAYLOAD
* r1/x1 Shared memory cookie belonging to this payload memory
* r2-7/x2-7 Resume information, must be preserved
*
* "Return" register usage:
* r0/x0 SMC Function ID, TEESMC32_CALL_RETURN_FROM_RPC if it was an
* AArch32 SMC return or TEESMC64_CALL_RETURN_FROM_RPC for
* AArch64 SMC return
* r2-7/x2-7 Preserved
*/
#define TEESMC_ST_RPC_FUNC_FREE_PAYLOAD 0x5701
#define TEESMC_RETURN_ST_RPC_FREE_PAYLOAD \
TEESMC_RPC_VAL(TEESMC_ST_RPC_FUNC_FREE_PAYLOAD)
/* Overriding default UID since the interface is extended
* 384fb3e0-e7f8-11e3-af63-0002a5d5c51b
*/
#define TEESMC_ST_UID_R0 0x384fb3e0
#define TEESMC_ST_UID_R1 0xe7f811e3
#define TEESMC_ST_UID_R2 0xaf630002
#define TEESMC_ST_UID32_R3 0xa5d5c51b
#define TEESMC_ST_UID64_R3 0xa5d5c51c
#define TEESMC_ST_REVISION_MAJOR 1
#define TEESMC_ST_REVISION_MINOR 0
/*
* UUID for OP-TEE
* 486178e0-e7f8-11e3-bc5e-0002a5d5c51b
*/
#define TEESMC_OS_OPTEE_UUID_R0 0x486178e0
#define TEESMC_OS_OPTEE_UUID_R1 0xe7f811e3
#define TEESMC_OS_OPTEE_UUID_R2 0xbc5e0002
#define TEESMC_OS_OPTEE_UUID_R3 0xa5d5c51b
#define TEESMC_OS_OPTEE_REVISION_MAJOR 1
#define TEESMC_OS_OPTEE_REVISION_MINOR 0
#endif /*TEESMC_ST_H*/

View File

@@ -0,0 +1,566 @@
/*
* Copyright (c) 2014, STMicroelectronics International N.V.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef TEE_CLIENT_API_H
#define TEE_CLIENT_API_H
#ifndef __KERNEL__
#include <stdint.h>
#include <stddef.h>
#endif /* __KERNEL__ */
/*
* Defines the number of available memory references in an open session or
* invoke command operation payload.
*/
#define TEEC_CONFIG_PAYLOAD_REF_COUNT 4
/**
* Defines the maximum size of a single shared memory block, in bytes, of both
* API allocated and API registered memory. The size is currently set to
* 512 * kB (512 * 1024).
*/
#define TEEC_CONFIG_SHAREDMEM_MAX_SIZE 0x8000
/**
* Flag constants indicating the type of parameters encoded inside the
* operation payload (TEEC_Operation), Type is uint32_t.
*
* TEEC_NONE The Parameter is not used
*
* TEEC_VALUE_INPUT The Parameter is a TEEC_Value tagged as input.
*
* TEEC_VALUE_OUTPUT The Parameter is a TEEC_Value tagged as output.
*
* TEEC_VALUE_INOUT The Parameter is a TEEC_Value tagged as both as
* input and output, i.e., for which both the
* behaviors of TEEC_VALUE_INPUT and
* TEEC_VALUE_OUTPUT apply.
*
* TEEC_MEMREF_TEMP_INPUT The Parameter is a TEEC_TempMemoryReference
* describing a region of memory which needs to be
* temporarily registered for the duration of the
* Operation and is tagged as input.
*
* TEEC_MEMREF_TEMP_OUTPUT Same as TEEC_MEMREF_TEMP_INPUT, but the Memory
* Reference is tagged as output. The
* Implementation may update the size field to
* reflect the required output size in some use
* cases.
*
* TEEC_MEMREF_TEMP_INOUT A Temporary Memory Reference tagged as both
* input and output, i.e., for which both the
* behaviors of TEEC_MEMREF_TEMP_INPUT and
* TEEC_MEMREF_TEMP_OUTPUT apply.
*
* TEEC_MEMREF_WHOLE The Parameter is a Registered Memory Reference
* that refers to the entirety of its parent Shared
* Memory block. The parameter structure is a
* TEEC_MemoryReference. In this structure, the
* Implementation MUST read only the parent field
* and MAY update the size field when the operation
* completes.
*
* TEEC_MEMREF_PARTIAL_INPUT A Registered Memory Reference structure that
* refers to a partial region of its parent Shared
* Memory block and is tagged as input.
*
* TEEC_MEMREF_PARTIAL_OUTPUT Registered Memory Reference structure that
* refers to a partial region of its parent Shared
* Memory block and is tagged as output.
*
* TEEC_MEMREF_PARTIAL_INOUT The Registered Memory Reference structure that
* refers to a partial region of its parent Shared
* Memory block and is tagged as both input and
* output, i.e., for which both the behaviors of
* TEEC_MEMREF_PARTIAL_INPUT and
* TEEC_MEMREF_PARTIAL_OUTPUT apply.
*/
#define TEEC_NONE 0x00000000
#define TEEC_VALUE_INPUT 0x00000001
#define TEEC_VALUE_OUTPUT 0x00000002
#define TEEC_VALUE_INOUT 0x00000003
#define TEEC_MEMREF_TEMP_INPUT 0x00000005
#define TEEC_MEMREF_TEMP_OUTPUT 0x00000006
#define TEEC_MEMREF_TEMP_INOUT 0x00000007
#define TEEC_MEMREF_WHOLE 0x0000000C
#define TEEC_MEMREF_PARTIAL_INPUT 0x0000000D
#define TEEC_MEMREF_PARTIAL_OUTPUT 0x0000000E
#define TEEC_MEMREF_PARTIAL_INOUT 0x0000000F
/**
* Flag constants indicating the data transfer direction of memory in
* TEEC_Parameter. TEEC_MEM_INPUT signifies data transfer direction from the
* client application to the TEE. TEEC_MEM_OUTPUT signifies data transfer
* direction from the TEE to the client application. Type is uint32_t.
*
* TEEC_MEM_INPUT The Shared Memory can carry data from the client
* application to the Trusted Application.
* TEEC_MEM_OUTPUT The Shared Memory can carry data from the Trusted
* Application to the client application.
* TEEC_MEM_DMABUF The Shared Memory is allocated with the dma buf api and
* not necessarly user mapped.
* Handle of the memory pass to drivers is the implementation
* fd field instead of the buffer field.
* TEEC_MEM_KAPI Shared memory is required from another linux module.
* Dma buf file descriptor is not created.
*/
#define TEEC_MEM_INPUT 0x00000001
#define TEEC_MEM_OUTPUT 0x00000002
#define TEEC_MEM_DMABUF 0x00010000
#define TEEC_MEM_KAPI 0x00020000
/**
* Return values. Type is TEEC_Result
*
* TEEC_SUCCESS The operation was successful.
* TEEC_ERROR_GENERIC Non-specific cause.
* TEEC_ERROR_ACCESS_DENIED Access privileges are not sufficient.
* TEEC_ERROR_CANCEL The operation was canceled.
* TEEC_ERROR_ACCESS_CONFLICT Concurrent accesses caused conflict.
* TEEC_ERROR_EXCESS_DATA Too much data for the requested operation was
* passed.
* TEEC_ERROR_BAD_FORMAT Input data was of invalid format.
* TEEC_ERROR_BAD_PARAMETERS Input parameters were invalid.
* TEEC_ERROR_BAD_STATE Operation is not valid in the current state.
* TEEC_ERROR_ITEM_NOT_FOUND The requested data item is not found.
* TEEC_ERROR_NOT_IMPLEMENTED The requested operation should exist but is not
* yet implemented.
* TEEC_ERROR_NOT_SUPPORTED The requested operation is valid but is not
* supported in this implementation.
* TEEC_ERROR_NO_DATA Expected data was missing.
* TEEC_ERROR_OUT_OF_MEMORY System ran out of resources.
* TEEC_ERROR_BUSY The system is busy working on something else.
* TEEC_ERROR_COMMUNICATION Communication with a remote party failed.
* TEEC_ERROR_SECURITY A security fault was detected.
* TEEC_ERROR_SHORT_BUFFER The supplied buffer is too short for the
* generated output.
* TEEC_ERROR_TARGET_DEAD Trusted Application has panicked
* during the operation.
*/
/**
* Standard defined error codes.
*/
#define TEEC_SUCCESS 0x00000000
#define TEEC_ERROR_GENERIC 0xFFFF0000
#define TEEC_ERROR_ACCESS_DENIED 0xFFFF0001
#define TEEC_ERROR_CANCEL 0xFFFF0002
#define TEEC_ERROR_ACCESS_CONFLICT 0xFFFF0003
#define TEEC_ERROR_EXCESS_DATA 0xFFFF0004
#define TEEC_ERROR_BAD_FORMAT 0xFFFF0005
#define TEEC_ERROR_BAD_PARAMETERS 0xFFFF0006
#define TEEC_ERROR_BAD_STATE 0xFFFF0007
#define TEEC_ERROR_ITEM_NOT_FOUND 0xFFFF0008
#define TEEC_ERROR_NOT_IMPLEMENTED 0xFFFF0009
#define TEEC_ERROR_NOT_SUPPORTED 0xFFFF000A
#define TEEC_ERROR_NO_DATA 0xFFFF000B
#define TEEC_ERROR_OUT_OF_MEMORY 0xFFFF000C
#define TEEC_ERROR_BUSY 0xFFFF000D
#define TEEC_ERROR_COMMUNICATION 0xFFFF000E
#define TEEC_ERROR_SECURITY 0xFFFF000F
#define TEEC_ERROR_SHORT_BUFFER 0xFFFF0010
#define TEEC_ERROR_TARGET_DEAD 0xFFFF3024
/**
* Function error origins, of type TEEC_ErrorOrigin. These indicate where in
* the software stack a particular return value originates from.
*
* TEEC_ORIGIN_API The error originated within the TEE Client API
* implementation.
* TEEC_ORIGIN_COMMS The error originated within the underlying
* communications stack linking the rich OS with
* the TEE.
* TEEC_ORIGIN_TEE The error originated within the common TEE code.
* TEEC_ORIGIN_TRUSTED_APP The error originated within the Trusted Application
* code.
*/
#define TEEC_ORIGIN_API 0x00000001
#define TEEC_ORIGIN_COMMS 0x00000002
#define TEEC_ORIGIN_TEE 0x00000003
#define TEEC_ORIGIN_TRUSTED_APP 0x00000004
/**
* Session login methods, for use in TEEC_OpenSession() as parameter
* connectionMethod. Type is uint32_t.
*
* TEEC_LOGIN_PUBLIC No login data is provided.
* TEEC_LOGIN_USER Login data about the user running the Client
* Application process is provided.
* TEEC_LOGIN_GROUP Login data about the group running the Client
* Application process is provided.
* TEEC_LOGIN_APPLICATION Login data about the running Client Application
* itself is provided.
*/
#define TEEC_LOGIN_PUBLIC 0x00000000
#define TEEC_LOGIN_USER 0x00000001
#define TEEC_LOGIN_GROUP 0x00000002
#define TEEC_LOGIN_APPLICATION 0x00000004
/**
* Encode the paramTypes according to the supplied types.
*
* @param p0 The first param type.
* @param p1 The second param type.
* @param p2 The third param type.
* @param p3 The fourth param type.
*/
#define TEEC_PARAM_TYPES(p0, p1, p2, p3) \
((p0) | ((p1) << 4) | ((p2) << 8) | ((p3) << 12))
/**
* Get the i_th param type from the paramType.
*
* @param p The paramType.
* @param i The i-th parameter to get the type for.
*/
#define TEEC_PARAM_TYPE_GET(p, i) (((p) >> (i * 4)) & 0xF)
typedef uint32_t TEEC_Result;
/**
* struct TEEC_Context - Represents a connection between a client application
* and a TEE.
*
* Context identifier can be a handle (when opened from user land)
* or a structure pointer (when opened from kernel land).
* Identifier is defined as an union to match type sizes on all architectures.
*/
typedef struct {
char devname[256];
union {
struct tee_context *ctx;
int fd;
};
} TEEC_Context;
/**
* This type contains a Universally Unique Resource Identifier (UUID) type as
* defined in RFC4122. These UUID values are used to identify Trusted
* Applications.
*/
typedef struct {
uint32_t timeLow;
uint16_t timeMid;
uint16_t timeHiAndVersion;
uint8_t clockSeqAndNode[8];
} TEEC_UUID;
/**
* struct TEEC_SharedMemory - Memory to transfer data between a client
* application and trusted code.
*
* @param buffer The memory buffer which is to be, or has been, shared
* with the TEE.
* @param size The size, in bytes, of the memory buffer.
* @param flags Bit-vector which holds properties of buffer.
* The bit-vector can contain either or both of the
* TEEC_MEM_INPUT and TEEC_MEM_OUTPUT flags.
*
* A shared memory block is a region of memory allocated in the context of the
* client application memory space that can be used to transfer data between
* that client application and a trusted application. The user of this struct
* is responsible to populate the buffer pointer.
*/
typedef struct {
void *buffer;
size_t size;
uint32_t flags;
/*
* identifier can store a handle (int) or a structure pointer (void *).
* define this union to match case where sizeof(int)!=sizeof(void *).
*/
union {
int fd;
void *ptr;
} d;
uint8_t registered;
} TEEC_SharedMemory;
/**
* struct TEEC_TempMemoryReference - Temporary memory to transfer data between
* a client application and trusted code, only used for the duration of the
* operation.
*
* @param buffer The memory buffer which is to be, or has been shared with
* the TEE.
* @param size The size, in bytes, of the memory buffer.
*
* A memory buffer that is registered temporarily for the duration of the
* operation to be called.
*/
typedef struct {
void *buffer;
size_t size;
} TEEC_TempMemoryReference;
/**
* struct TEEC_RegisteredMemoryReference - use a pre-registered or
* pre-allocated shared memory block of memory to transfer data between
* a client application and trusted code.
*
* @param parent Points to a shared memory structure. The memory reference
* may utilize the whole shared memory or only a part of it.
* Must not be NULL
*
* @param size The size, in bytes, of the memory buffer.
*
* @param offset The offset, in bytes, of the referenced memory region from
* the start of the shared memory block.
*
*/
typedef struct {
TEEC_SharedMemory *parent;
size_t size;
size_t offset;
} TEEC_RegisteredMemoryReference;
/**
* struct TEEC_Value - Small raw data container
*
* Instead of allocating a shared memory buffer this structure can be used
* to pass small raw data between a client application and trusted code.
*
* @param a The first integer value.
*
* @param b The second second value.
*/
typedef struct {
uint32_t a;
uint32_t b;
} TEEC_Value;
/**
* union TEEC_Parameter - Memory container to be used when passing data between
* client application and trusted code.
*
* Either the client uses a shared memory reference, parts of it or a small raw
* data container.
*
* @param tmpref A temporary memory reference only valid for the duration
* of the operation.
*
* @param memref The entire shared memory or parts of it.
*
* @param value The small raw data container to use
*/
typedef union {
TEEC_TempMemoryReference tmpref;
TEEC_RegisteredMemoryReference memref;
TEEC_Value value;
} TEEC_Parameter;
/**
* struct TEEC_Session - Represents a connection between a client application
* and a trusted application.
*/
typedef struct {
int fd;
} TEEC_Session;
/**
* struct TEEC_Operation - Holds information and memory references used in
* TEEC_InvokeCommand().
*
* @param started Client must initialize to zero if it needs to cancel
* an operation about to be performed.
* @param paramTypes Type of data passed. Use TEEC_PARAMS_TYPE macro to
* create the correct flags.
* 0 means TEEC_NONE is passed for all params.
* @param params Array of parameters of type TEEC_Parameter.
* @param session Internal pointer to the last session used by
* TEEC_InvokeCommand with this operation.
*
*/
typedef struct {
uint32_t started;
uint32_t paramTypes;
TEEC_Parameter params[TEEC_CONFIG_PAYLOAD_REF_COUNT];
/* Implementation-Defined */
TEEC_Session *session;
TEEC_SharedMemory memRefs[TEEC_CONFIG_PAYLOAD_REF_COUNT];
uint32_t flags;
} TEEC_Operation;
/**
* TEEC_InitializeContext() - Initializes a context holding connection
* information on the specific TEE, designated by the name string.
* @param name A zero-terminated string identifying the TEE to connect to.
* If name is set to NULL, the default TEE is connected to. NULL
* is the only supported value in this version of the API
* implementation.
*
* @param context The context structure which is to be initialized.
*
* @return TEEC_SUCCESS The initialization was successful.
* @return TEEC_Result Something failed.
*/
TEEC_Result TEEC_InitializeContext(const char *name, TEEC_Context *context);
/**
* TEEC_FinalizeContext() - Destroys a context holding connection information
* on the specific TEE.
*
* This function destroys an initialized TEE context, closing the connection
* between the client application and the TEE. This function must only be
* called when all sessions related to this TEE context have been closed and
* all shared memory blocks have been released.
*
* @param context The context to be destroyed.
*/
void TEEC_FinalizeContext(TEEC_Context *context);
/**
* TEEC_OpenSession() - Opens a new session with the specified trusted
* application.
*
* @param context The initialized TEE context structure in which
* scope to open the session.
* @param session The session to initialize.
* @param destination A structure identifying the trusted application
* with which to open a session.
*
* @param connectionMethod The connection method to use.
* @param connectionData Any data necessary to connect with the chosen
* connection method. Not supported, should be set to
* NULL.
* @param operation An operation structure to use in the session. May
* be set to NULL to signify no operation structure
* needed.
*
* @param returnOrigin A parameter which will hold the error origin if
* this function returns any value other than
* TEEC_SUCCESS.
*
* @return TEEC_SUCCESS OpenSession successfully opened a new session.
* @return TEEC_Result Something failed.
*
*/
TEEC_Result TEEC_OpenSession(TEEC_Context *context,
TEEC_Session *session,
const TEEC_UUID *destination,
uint32_t connectionMethod,
const void *connectionData,
TEEC_Operation *operation,
uint32_t *returnOrigin);
/**
* TEEC_CloseSession() - Closes the session which has been opened with the
* specific trusted application.
*
* @param session The opened session to close.
*/
void TEEC_CloseSession(TEEC_Session *session);
/**
* TEEC_InvokeCommand() - Executes a command in the specified trusted
* application.
*
* @param session A handle to an open connection to the trusted
* application.
* @param commandID Identifier of the command in the trusted application
* to invoke.
* @param operation An operation structure to use in the invoke command.
* May be set to NULL to signify no operation structure
* needed.
* @param returnOrigin A parameter which will hold the error origin if this
* function returns any value other than TEEC_SUCCESS.
*
* @return TEEC_SUCCESS OpenSession successfully opened a new session.
* @return TEEC_Result Something failed.
*/
TEEC_Result TEEC_InvokeCommand(TEEC_Session *session,
uint32_t commandID,
TEEC_Operation *operation,
uint32_t *returnOrigin);
/**
* TEEC_RegisterSharedMemory() - Register a block of existing memory as a
* shared block within the scope of the specified context.
*
* @param context The initialized TEE context structure in which scope to
* open the session.
* @param sharedMem pointer to the shared memory structure to register.
*
* @return TEEC_SUCCESS The registration was successful.
* @return TEEC_ERROR_OUT_OF_MEMORY Memory exhaustion.
* @return TEEC_Result Something failed.
*/
TEEC_Result TEEC_RegisterSharedMemory(TEEC_Context *context,
TEEC_SharedMemory *sharedMem);
/**
* TEEC_AllocateSharedMemory() - Allocate shared memory for TEE.
*
* @param context The initialized TEE context structure in which scope to
* open the session.
* @param sharedMem Pointer to the allocated shared memory.
*
* @return TEEC_SUCCESS The registration was successful.
* @return TEEC_ERROR_OUT_OF_MEMORY Memory exhaustion.
* @return TEEC_Result Something failed.
*/
TEEC_Result TEEC_AllocateSharedMemory(TEEC_Context *context,
TEEC_SharedMemory *sharedMem);
/**
* TEEC_ReleaseSharedMemory() - Free or deregister the shared memory.
*
* @param sharedMem Pointer to the shared memory to be freed.
*/
void TEEC_ReleaseSharedMemory(TEEC_SharedMemory *sharedMemory);
/**
* TEEC_RequestCancellation() - Request the cancellation of a pending open
* session or command invocation.
*
* @param operation Pointer to an operation previously passed to open session
* or invoke.
*/
void TEEC_RequestCancellation(TEEC_Operation *operation);
/**
* Register a pre-allocated Trusted Application This is mainly intended for
* OS-FREE contexts or when a filesystem is not available.
*
* @param ta Pointer to the trusted application binary
* @param size The size of the TA binary
*
* @return TEEC_SUCCESS if successful.
* @return TEEC_Result something failed.
*/
TEEC_Result TEEC_RegisterTA(const void *ta, const size_t size);
/**
* Unregister a pre-allocated Trusted Application This is mainly intended for
* OS-FREE contexts or when a filesystem is not available.
*
* @param ta Pointer to the trusted application binary
*/
void TEEC_UnregisterTA(const void *ta);
#endif

View File

@@ -0,0 +1,205 @@
/*
* Copyright (c) 2014, STMicroelectronics International N.V.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License Version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef __TEE_CORE_DRV_H__
#define __TEE_CORE_DRV_H__
#include <linux/klist.h>
#include <linux/device.h>
#include <linux/file.h>
#include <linux/cdev.h>
#include <linux/debugfs.h>
#include <linux/miscdevice.h>
#include <linux/types.h>
#include <linux/atomic.h>
#include <linux/scatterlist.h>
#include <linux/types.h>
#include <linux/tee_client_api.h>
struct tee_cmd_io;
struct tee_shm_io;
struct tee_rpc;
enum tee_state {
TEE_OFFLINE = 0,
TEE_ONLINE = 1,
TEE_SUSPENDED = 2,
TEE_RUNNING = 3,
TEE_CRASHED = 4,
TEE_LAST = 5,
};
#define TEE_CONF_TEST_MODE 0x01000000
#define TEE_CONF_FW_NOT_CAPABLE 0x00000001
struct tee_stats_entry {
int count;
int max;
};
#define TEE_STATS_CONTEXT_IDX 0
#define TEE_STATS_SESSION_IDX 1
#define TEE_STATS_SHM_IDX 2
#define TEE_MAX_TEE_DEV_NAME (64)
struct tee {
struct klist_node node;
char name[TEE_MAX_TEE_DEV_NAME];
int id;
void *priv;
const struct tee_ops *ops;
struct device *dev;
struct miscdevice miscdev;
struct tee_rpc *rpc;
struct dentry *dbg_dir;
atomic_t refcount;
int max_refcount;
struct tee_stats_entry stats[3];
struct list_head list_ctx;
struct list_head list_rpc_shm;
struct mutex lock;
unsigned int state;
uint32_t shm_flags; /* supported flags for shm allocation */
uint32_t conf;
uint32_t test;
};
#define _DEV(tee) (tee->miscdev.this_device)
#define TEE_MAX_CLIENT_NAME (128)
/**
* struct tee_context - internal structure to store a TEE context.
*
* @tee: tee attached to the tee_context
* @usr_client: flag to known if the client is user side client
* @entry: list of tee_context
* @list_sess: list of tee_session that denotes all tee_session attached
* @list_shm: list of tee_shm that denotes all tee_shm attached
* @refcount: number of objects which reference it (including itself)
*/
struct tee_context {
struct tee *tee;
char name[TEE_MAX_CLIENT_NAME];
int tgid;
int usr_client;
struct list_head entry;
struct list_head list_sess;
struct list_head list_shm;
struct kref refcount;
};
/**
* struct tee_session - internal structure to store a TEE session.
*
* @entry: list of tee_context
* @ctx: tee_context attached to the tee_session
* @sessid: session ID returned by the secure world
* @priv: exporter specific private data for this buffer object
*/
struct tee_session {
struct list_head entry;
struct tee_context *ctx;
uint32_t sessid;
void *priv;
};
struct tee_shm_dma_buf {
struct dma_buf_attachment *attach;
struct sg_table *sgt;
bool tee_allocated;
};
/**
* struct tee_shm - internal structure to store a shm object.
*
* @ctx: tee_context attached to the buffer.
* @tee: tee attached to the buffer.
* @dev: device attached to the buffer.
* @size_req: requested size for the buffer
* @size_alloc: effective size of the buffer
* @kaddr: kernel address if mapped kernel side
* @paddr: physical address
* @flags: flags which denote the type of the buffer
* @entry: list of tee_shm
*/
struct tee_shm {
struct list_head entry;
struct tee_context *ctx;
struct tee *tee;
struct device *dev;
size_t size_req;
size_t size_alloc;
uint32_t flags;
void *kaddr;
dma_addr_t paddr;
struct sg_table sgt;
struct tee_shm_dma_buf *sdb;
};
#define TEE_SHM_MAPPED 0x01000000
#define TEE_SHM_TEMP 0x02000000
#define TEE_SHM_FROM_RPC 0x04000000
#define TEE_SHM_REGISTERED 0x08000000
#define TEE_SHM_MEMREF 0x10000000
#define TEE_SHM_CACHED 0x20000000
#define TEE_SHM_DRV_PRIV_MASK 0xFF000000
struct tee_data {
uint32_t type;
uint32_t type_original;
TEEC_SharedMemory c_shm[TEEC_CONFIG_PAYLOAD_REF_COUNT];
union {
struct tee_shm *shm;
TEEC_Value value;
} params[TEEC_CONFIG_PAYLOAD_REF_COUNT];
};
struct tee_cmd {
TEEC_Result err;
uint32_t origin;
uint32_t cmd;
struct tee_shm *uuid;
struct tee_shm *ta;
struct tee_data param;
};
struct tee_shm *tee_shm_alloc_from_rpc(struct tee *tee, size_t size);
void tee_shm_free_from_rpc(struct tee_shm *);
int tee_core_add(struct tee *tee);
int tee_core_del(struct tee *tee);
struct tee *tee_core_alloc(struct device *dev, char *name, int id,
const struct tee_ops *ops, size_t len);
int tee_core_free(struct tee *tee);
struct tee_ops {
struct module *owner;
const char *type;
int (*start)(struct tee *tee);
int (*stop)(struct tee *tee);
int (*open)(struct tee_session *sess, struct tee_cmd *cmd);
int (*close)(struct tee_session *sess);
int (*invoke)(struct tee_session *sess, struct tee_cmd *cmd);
int (*cancel)(struct tee_session *sess, struct tee_cmd *cmd);
struct tee_shm *(*alloc)(struct tee *tee, size_t size,
uint32_t flags);
void (*free)(struct tee_shm *shm);
int (*shm_inc_ref)(struct tee_shm *shm);
};
#endif /* __TEE_CORE_DRV_H__ */

View File

@@ -0,0 +1,63 @@
/*
* Copyright (c) 2014, STMicroelectronics International N.V.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License Version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef _TEE_IOC_H
#define _TEE_IOC_H
#include <linux/tee_client_api.h>
#ifndef __KERNEL__
#define __user
#endif
/**
* struct tee_cmd_io - The command sent to an open tee device.
* @err: Error code (as in Global Platform TEE Client API spec)
* @origin: Origin for the error code (also from spec).
* @cmd: The command to be executed in the trusted application.
* @uuid: The uuid for the trusted application.
* @data: The trusted application or memory block.
* @data_size: The size of the trusted application or memory block.
* @op: The cmd payload operation for the trusted application.
*
* This structure is mainly used in the Linux kernel for communication
* with the user space.
*/
struct tee_cmd_io {
TEEC_Result err;
uint32_t origin;
uint32_t cmd;
TEEC_UUID __user *uuid;
void __user *data;
uint32_t data_size;
TEEC_Operation __user *op;
int fd_sess;
};
struct tee_shm_io {
void __user *buffer;
size_t size;
uint32_t flags;
union {
int fd_shm;
void *ptr;
};
uint8_t registered;
};
#define TEE_OPEN_SESSION_IOC _IOWR('t', 161, struct tee_cmd_io)
#define TEE_INVOKE_COMMAND_IOC _IOWR('t', 163, struct tee_cmd_io)
#define TEE_REQUEST_CANCELLATION_IOC _IOWR('t', 164, struct tee_cmd_io)
#define TEE_ALLOC_SHM_IOC _IOWR('t', 165, struct tee_shm_io)
#define TEE_GET_FD_FOR_RPC_SHM_IOC _IOWR('t', 167, struct tee_shm_io)
#endif /* _TEE_IOC_H */

View File

@@ -0,0 +1,174 @@
/*
* Copyright (c) 2014, STMicroelectronics International N.V.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License Version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef _TEE_KERNEL_API_H
#define _TEE_KERNEL_API_H
#include <linux/tee_client_api.h>
/**
* struct TEEC_Context - Represents a connection between a client application
* and a TEE.
*/
/*typedef struct {
char devname[256];
} TEEC_Context;*/
/**
* struct TEEC_Session - Represents a connection between a client application
* and a trusted application.
*/
/*typedef struct {
void *session;
} TEEC_Session;*/
/**
* TEEC_InitializeContext() - Initializes a context holding connection
* information on the specific TEE, designated by the name string.
* @param name A zero-terminated string identifying the TEE to connect to.
* If name is set to NULL, the default TEE is connected to. NULL
* is the only supported value in this version of the API
* implementation.
*
* @param context The context structure which is to be initialized.
*
* @return TEEC_SUCCESS The initialization was successful.
* @return TEEC_Result Something failed.
*/
TEEC_Result TEEC_InitializeContext(const char *name, TEEC_Context *context);
/**
* TEEC_FinalizeContext() - Destroys a context holding connection information
* on the specific TEE.
*
* This function destroys an initialized TEE context, closing the connection
* between the client application and the TEE. This function must only be
* called when all sessions related to this TEE context have been closed and
* all shared memory blocks have been released.
*
* @param context The context to be destroyed.
*/
void TEEC_FinalizeContext(TEEC_Context *context);
/**
* TEEC_OpenSession() - Opens a new session with the specified trusted
* application.
*
* @param context The initialized TEE context structure in which
* scope to open the session.
* @param session The session to initialize.
* @param destination A structure identifying the trusted application
* with which to open a session.
*
* @param connectionMethod The connection method to use.
* @param connectionData Any data necessary to connect with the chosen
* connection method. Not supported, should be set to
* NULL.
* @param operation An operation structure to use in the session. May
* be set to NULL to signify no operation structure
* needed.
*
* @param returnOrigin A parameter which will hold the error origin if
* this function returns any value other than
* TEEC_SUCCESS.
*
* @return TEEC_SUCCESS OpenSession successfully opened a new session.
* @return TEEC_Result Something failed.
*
*/
TEEC_Result TEEC_OpenSession(TEEC_Context *context,
TEEC_Session *session,
const TEEC_UUID *destination,
uint32_t connectionMethod,
const void *connectionData,
TEEC_Operation *operation,
uint32_t *returnOrigin);
/**
* TEEC_CloseSession() - Closes the session which has been opened with the
* specific trusted application.
*
* @param session The opened session to close.
*/
void TEEC_CloseSession(TEEC_Session *session);
/**
* TEEC_InvokeCommand() - Executes a command in the specified trusted
* application.
*
* @param session A handle to an open connection to the trusted
* application.
* @param commandID Identifier of the command in the trusted application
* to invoke.
* @param operation An operation structure to use in the invoke command.
* May be set to NULL to signify no operation structure
* needed.
* @param returnOrigin A parameter which will hold the error origin if this
* function returns any value other than TEEC_SUCCESS.
*
* @return TEEC_SUCCESS OpenSession successfully opened a new session.
* @return TEEC_Result Something failed.
*/
TEEC_Result TEEC_InvokeCommand(TEEC_Session *session,
uint32_t commandID,
TEEC_Operation *operation,
uint32_t *returnOrigin);
/**
* TEEC_RegisterSharedMemory() - Register a block of existing memory as a
* shared block within the scope of the specified context.
*
* @param context The initialized TEE context structure in which scope to
* open the session.
* @param sharedMem pointer to the shared memory structure to register.
*
* @return TEEC_SUCCESS The registration was successful.
* @return TEEC_ERROR_OUT_OF_MEMORY Memory exhaustion.
* @return TEEC_Result Something failed.
*/
TEEC_Result TEEC_RegisterSharedMemory(TEEC_Context *context,
TEEC_SharedMemory *sharedMem);
/**
* TEEC_AllocateSharedMemory() - Allocate shared memory for TEE.
*
* @param context The initialized TEE context structure in which scope to
* open the session.
* @param sharedMem Pointer to the allocated shared memory.
*
* @return TEEC_SUCCESS The registration was successful.
* @return TEEC_ERROR_OUT_OF_MEMORY Memory exhaustion.
* @return TEEC_Result Something failed.
*/
TEEC_Result TEEC_AllocateSharedMemory(TEEC_Context *context,
TEEC_SharedMemory *sharedMem);
/**
* TEEC_ReleaseSharedMemory() - Free or deregister the shared memory.
*
* @param sharedMem Pointer to the shared memory to be freed.
*/
void TEEC_ReleaseSharedMemory(TEEC_SharedMemory *sharedMemory);
#if 0
/**
* TEEC_RequestCancellation() - Request the cancellation of a pending open
* session or command invocation.
*
* @param operation Pointer to an operation previously passed to open session
* or invoke.
*/
void TEEC_RequestCancellation(TEEC_Operation *operation);
#endif
#endif