diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..4a0b21c
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,83 @@
+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.
+
+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
diff --git a/README.en.md b/README.en.md
deleted file mode 100644
index 9ef7d5e..0000000
--- a/README.en.md
+++ /dev/null
@@ -1,36 +0,0 @@
-# tee_tee_tzdriver
-
-#### Description
-{**When you're done, you can delete the content in this README and update the file with details for others getting started with your repository**}
-
-#### Software Architecture
-Software architecture description
-
-#### Installation
-
-1. xxxx
-2. xxxx
-3. xxxx
-
-#### Instructions
-
-1. xxxx
-2. xxxx
-3. xxxx
-
-#### Contribution
-
-1. Fork the repository
-2. Create Feat_xxx branch
-3. Commit your code
-4. Create Pull Request
-
-
-#### Gitee Feature
-
-1. You can use Readme\_XXX.md to support different languages, such as Readme\_en.md, Readme\_zh.md
-2. Gitee blog [blog.gitee.com](https://blog.gitee.com)
-3. Explore open source project [https://gitee.com/explore](https://gitee.com/explore)
-4. The most valuable open source project [GVP](https://gitee.com/gvp)
-5. The manual of Gitee [https://gitee.com/help](https://gitee.com/help)
-6. The most popular members [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/)
diff --git a/README.md b/README.md
index 1fcde11..b6c3308 100644
--- a/README.md
+++ b/README.md
@@ -1,39 +1,52 @@
-# tee_tee_tzdriver
-
-#### 介绍
-{**以下是 Gitee 平台说明,您可以替换此简介**
-Gitee 是 OSCHINA 推出的基于 Git 的代码托管平台(同时支持 SVN)。专为开发者提供稳定、高效、安全的云端软件开发协作平台
-无论是个人、团队、或是企业,都能够用 Gitee 实现代码托管、项目管理、协作开发。企业项目请看 [https://gitee.com/enterprises](https://gitee.com/enterprises)}
-
-#### 软件架构
-软件架构说明
-
-
-#### 安装教程
-
-1. xxxx
-2. xxxx
-3. xxxx
-
-#### 使用说明
-
-1. xxxx
-2. xxxx
-3. xxxx
-
-#### 参与贡献
-
-1. Fork 本仓库
-2. 新建 Feat_xxx 分支
-3. 提交代码
-4. 新建 Pull Request
-
-
-#### 特技
-
-1. 使用 Readme\_XXX.md 来支持不同的语言,例如 Readme\_en.md, Readme\_zh.md
-2. Gitee 官方博客 [blog.gitee.com](https://blog.gitee.com)
-3. 你可以 [https://gitee.com/explore](https://gitee.com/explore) 这个地址来了解 Gitee 上的优秀开源项目
-4. [GVP](https://gitee.com/gvp) 全称是 Gitee 最有价值开源项目,是综合评定出的优秀开源项目
-5. Gitee 官方提供的使用手册 [https://gitee.com/help](https://gitee.com/help)
-6. Gitee 封面人物是一档用来展示 Gitee 会员风采的栏目 [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/)
+# Tzdriver Module Introduction
+
+- [Introduction](#section469617221261)
+- [tzdriver project Structure](#section15884114210197)
+- [secondary Directories structure](#section1464106163817)
+
+## Introduction
+
+tzdriver is a part of REE. The REE module provides a set of rich execution environment \(REE\) API components for interacting with TEEOS, including tzdriver \(driver\), libteec \(API library\), and teecd \(agent service\). This module tzdriver normally is a part of kernel, sometimes could be ko(kernel module).
+
+## Tzdriver project Structure
+
+tee_tzdriver:project directory
+- README.md&README_zh.md:Introduction file
+- LICENSE:GPL v2 LICENSE
+- linux:tzdriver for linux kernel
+- liteos:tzdriver for liteos_a kernel
+
+## Secondary Directories structure
+
+**Table 1** tzdriver main secondary source code structure
+
+
+
main secondary directory
+ |
+description
+ |
+
+
+core
+ |
+core function code, include:smc, agent...
+ |
+
+tlogger
+ |
+for tee log
+ |
+
+auth
+ |
+for authentication
+ |
+
+include
+ |
+export header files
+ |
+
+
+
+
diff --git a/README_zh.md b/README_zh.md
new file mode 100644
index 0000000..5c787e0
--- /dev/null
+++ b/README_zh.md
@@ -0,0 +1,52 @@
+# Tzdriver模块介绍
+
+- [简介](#section469617221261)
+- [tzdriver工程框架](#section15884114210197)
+- [二级目录结构](#section1464106163817)
+
+## 简介
+
+tzdriver(Trustzone driver)是REE的一部分,REE组件提供了一套用于和TEEOS交互的富运行环境(REE)接口组件,包括驱动(tzdriver)、libteec(应用接口库)、teecd(agent服务)。本组件tzdriver一般情况下为内核的一部分,也可以编译为ko模块。
+
+## Tzdriver工程框架
+
+tee_tzdriver:工程目录
+- README.md&README_zh.md:指导文件
+- LICENSE:许可证(GPL v2)
+- linux:为linux kernel提供的tzdriver
+- liteos:为liteos_a kernel提供的tzdriver
+
+## 二级目录结构
+
+**表 1** tzdriver二级源代码主要目录结构
+
+
+主要二级目录
+ |
+描述
+ |
+
+
+core
+ |
+核心功能代码,smc,agent等都在这里面
+ |
+
+tlogger
+ |
+日志组件相关代码
+ |
+
+auth
+ |
+鉴权相关代码
+ |
+
+include
+ |
+头文件导出
+ |
+
+
+
+
diff --git a/bundle.json b/bundle.json
new file mode 100755
index 0000000..ffc6c32
--- /dev/null
+++ b/bundle.json
@@ -0,0 +1,34 @@
+{
+ "name": "@openharmony/tee_tzdriver",
+ "version": "1.0.0",
+ "description": "TrustZone driver",
+ "private": false,
+ "scripts": {},
+ "author": {},
+ "repository": "",
+ "license": "GNU Public License v2",
+ "component": {
+ "name": "tee_tzdriver",
+ "subsystem": "tee",
+ "syscap": [],
+ "features": [],
+ "adapted_system_type": [ "small", "standard" ],
+ "rom": "400KB",
+ "ram": "400KB",
+ "deps": {
+ "components": [
+ "kernel",
+ "libmbedtls"
+ ],
+ "third_party": [
+ "bounds_checking_function",
+ "mbedtls"
+ ]
+ },
+ "build": {
+ "sub_component": [],
+ "inner_kits": [],
+ "test": []
+ }
+ }
+}
diff --git a/linux/Kconfig b/linux/Kconfig
new file mode 100644
index 0000000..c86fec2
--- /dev/null
+++ b/linux/Kconfig
@@ -0,0 +1,20 @@
+menu "TEE OS"
+
+config TZDRIVER
+ tristate "Secure Execution Communicator driver"
+ default n
+ help
+ Provides a communication interface between userspace and
+ TrustZone Operating Environment.
+
+config ASAN_DEBUG
+ bool "ASAN debug version"
+ default n
+ help
+ Macro defined for ASAN debug version
+
+source "../../../../../base/tee/tee_tzdriver/linux/auth/Kconfig"
+source "../../../../../base/tee/tee_tzdriver/linux/core/Kconfig"
+source "../../../../../base/tee/tee_tzdriver/linux/tlogger/Kconfig"
+endmenu
+
diff --git a/linux/Makefile b/linux/Makefile
new file mode 100644
index 0000000..6eeabd7
--- /dev/null
+++ b/linux/Makefile
@@ -0,0 +1,11 @@
+ifeq ($(CONFIG_TZDRIVER),y)
+KERNEL_DIR := $(srctree)
+
+EXTRA_CFLAGS += -I$(KERNEL_DIR)/../../../../../third_party/bounds_checking_function/include/
+EXTRA_CFLAGS += -I$(KERNEL_DIR)/../../../../../base/tee/tee_tzdriver/linux/include/
+
+obj-$(CONFIG_TZDRIVER) += auth/
+obj-$(CONFIG_TZDRIVER) += core/
+obj-$(CONFIG_TZDRIVER) += tlogger/
+
+endif
diff --git a/linux/auth/Kconfig b/linux/auth/Kconfig
new file mode 100644
index 0000000..b8e31c5
--- /dev/null
+++ b/linux/auth/Kconfig
@@ -0,0 +1,8 @@
+# Auth Configuration
+config CLIENT_AUTH
+ bool "Client Application Hash Auth"
+ default n
+ depends on TZDRIVER
+ help
+ TEEOS CA code hash auth
+
diff --git a/linux/auth/Makefile b/linux/auth/Makefile
new file mode 100644
index 0000000..0f24776
--- /dev/null
+++ b/linux/auth/Makefile
@@ -0,0 +1,12 @@
+KERNEL_DIR :=$(srctree)
+
+EXTRA_CFLAGS += -I$(KERNEL_DIR)/../../../../../third_party/bounds_checking_function/include/
+EXTRA_CFLAGS += -I$(KERNEL_DIR)/../../../../../base/tee/tee_tzdriver/linux/include
+EXTRA_CFLAGS += -I$(KERNEL_DIR)/../../../../../base/tee/tee_tzdriver/linux/core
+EXTRA_CFLAGS += -I$(KERNEL_DIR)/../../../../../base/tee/tee_tzdriver/linux/tlogger
+
+obj-$(CONFIG_CLIENT_AUTH) += client_hash_auth.o
+
+ifeq ($(CONFIG_CLIENT_AUTH), y)
+ obj-y += auth_base_impl.o
+endif
diff --git a/linux/auth/auth_base_impl.c b/linux/auth/auth_base_impl.c
new file mode 100644
index 0000000..9dcddf5
--- /dev/null
+++ b/linux/auth/auth_base_impl.c
@@ -0,0 +1,252 @@
+/*
+ * auth_base_impl.c
+ *
+ * function for base hash operation
+ *
+ * Copyright (C) 2022 Huawei Technologies Co., Ltd.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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 "auth_base_impl.h"
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#if (KERNEL_VERSION(4, 14, 0) <= LINUX_VERSION_CODE)
+#include
+#endif
+
+#include
+#include "tc_ns_log.h"
+#include "tc_ns_client.h"
+#include "agent.h" /* for get_proc_dpath */
+#include "ko_adapt.h"
+
+/* for crypto */
+struct crypto_shash *g_shash_handle;
+bool g_shash_handle_state;
+struct mutex g_shash_handle_lock;
+
+void init_crypto_hash_lock(void)
+{
+ mutex_init(&g_shash_handle_lock);
+}
+
+void mutex_crypto_hash_lock(void)
+{
+ mutex_lock(&g_shash_handle_lock);
+}
+
+void mutex_crypto_hash_unlock(void)
+{
+ mutex_unlock(&g_shash_handle_lock);
+}
+
+/* begin: prepare crypto context */
+struct crypto_shash *get_shash_handle(void)
+{
+ return g_shash_handle;
+}
+
+void tee_exit_shash_handle(void)
+{
+ if (g_shash_handle) {
+ crypto_free_shash(g_shash_handle);
+ g_shash_handle_state = false;
+ g_shash_handle = NULL;
+ }
+}
+
+int tee_init_shash_handle(char *hash_type)
+{
+ long rc;
+
+ if (!hash_type) {
+ tloge("tee init crypto: error input parameter\n");
+ return -EFAULT;
+ }
+
+ mutex_crypto_hash_lock();
+ if (g_shash_handle_state) {
+ mutex_crypto_hash_unlock();
+ return 0;
+ }
+
+ g_shash_handle = crypto_alloc_shash(hash_type, 0, 0);
+ if (IS_ERR_OR_NULL(g_shash_handle)) {
+ rc = PTR_ERR(g_shash_handle);
+ tloge("Can not allocate %s reason: %ld\n", hash_type, rc);
+ mutex_crypto_hash_unlock();
+ return rc;
+ }
+ g_shash_handle_state = true;
+
+ mutex_crypto_hash_unlock();
+ return 0;
+}
+/* end: prepare crypto context */
+
+/* begin: Calculate the SHA256 file digest */
+static int prepare_desc(struct sdesc **desc)
+{
+ size_t size;
+ size_t shash_size;
+
+ shash_size = crypto_shash_descsize(g_shash_handle);
+ size = sizeof((*desc)->shash) + shash_size;
+ if (size < sizeof((*desc)->shash) || size < shash_size) {
+ tloge("size flow\n");
+ return -ENOMEM;
+ }
+
+ *desc = kzalloc(size, GFP_KERNEL);
+ if (ZERO_OR_NULL_PTR((unsigned long)(uintptr_t)(*desc))) {
+ tloge("alloc desc failed\n");
+ return -ENOMEM;
+ }
+
+ return EOK;
+}
+
+#define PINED_PAGE_NUMBER 1
+static int get_proc_user_pages(struct mm_struct *mm, unsigned long start_code,
+ struct page **ptr_page, struct task_struct *cur_struct)
+{
+#if (KERNEL_VERSION(5, 10, 0) <= LINUX_VERSION_CODE)
+ (void)cur_struct;
+ return get_user_pages_remote(mm, start_code,
+ (unsigned long)PINED_PAGE_NUMBER, FOLL_FORCE, ptr_page, NULL, NULL);
+#elif (KERNEL_VERSION(4, 9, 0) <= LINUX_VERSION_CODE)
+ return get_user_pages_remote(cur_struct, mm, start_code,
+ (unsigned long)PINED_PAGE_NUMBER, FOLL_FORCE, ptr_page, NULL, NULL);
+#elif (KERNEL_VERSION(4, 4, 197) <= LINUX_VERSION_CODE)
+ return get_user_pages_locked(cur_struct, mm, start_code,
+ (unsigned long)PINED_PAGE_NUMBER, FOLL_FORCE, ptr_page, NULL);
+#else
+ return get_user_pages_locked(cur_struct, mm, start_code,
+ (unsigned long)PINED_PAGE_NUMBER, 0, 1, ptr_page, NULL);
+#endif
+}
+
+static int update_task_hash(struct mm_struct *mm,
+ struct task_struct *cur_struct, struct shash_desc *shash)
+{
+ int rc = -1;
+ unsigned long in_size;
+ struct page *ptr_page = NULL;
+ void *ptr_base = NULL;
+
+#if (KERNEL_VERSION(5, 10, 0) <= LINUX_VERSION_CODE)
+ if (!mm->mmap) {
+ tloge("mm sturct error! no start_code\n");
+ return -EFAULT;
+ }
+ unsigned long start_code = mm->mmap->vm_start;
+#else
+ unsigned long start_code = mm->start_code;
+#endif
+ unsigned long end_code = mm->end_code;
+ unsigned long code_size = end_code - start_code;
+ if (code_size == 0) {
+ tloge("bad code size\n");
+ return -EINVAL;
+ }
+
+ while (start_code < end_code) {
+ /* Get a handle of the page we want to read */
+ rc = get_proc_user_pages(mm, start_code, &ptr_page, cur_struct);
+ if (rc != PINED_PAGE_NUMBER) {
+ tloge("get user pages error[0x%x]\n", rc);
+ rc = -EFAULT;
+ break;
+ }
+
+ ptr_base = kmap_atomic(ptr_page);
+ if (!ptr_base) {
+ rc = -EFAULT;
+ put_page(ptr_page);
+ break;
+ }
+
+ in_size = (code_size > PAGE_SIZE) ? PAGE_SIZE : code_size;
+ rc = crypto_shash_update(shash, ptr_base, in_size);
+ if (rc) {
+ kunmap_atomic(ptr_base);
+ put_page(ptr_page);
+ break;
+ }
+
+ kunmap_atomic(ptr_base);
+ put_page(ptr_page);
+ start_code += in_size;
+ code_size = end_code - start_code;
+ }
+ return rc;
+}
+
+int calc_task_hash(unsigned char *digest, uint32_t dig_len,
+ struct task_struct *cur_struct)
+{
+ struct mm_struct *mm = NULL;
+ struct sdesc *desc = NULL;
+ bool check_value = false;
+ int rc;
+
+ check_value = (!cur_struct || !digest ||
+ dig_len != SHA256_DIGEST_LENGTH);
+ if (check_value) {
+ tloge("tee hash: input param is error\n");
+ return -EFAULT;
+ }
+
+ mm = get_task_mm(cur_struct);
+ if (!mm) {
+ if (memset_s(digest, dig_len, 0, MAX_SHA_256_SZ))
+ return -EFAULT;
+ tloge("kernel proc need not check\n");
+ return EOK;
+ }
+
+ if (prepare_desc(&desc) != EOK) {
+ mmput(mm);
+ return -ENOMEM;
+ }
+
+ desc->shash.tfm = g_shash_handle;
+ if (crypto_shash_init(&desc->shash)) {
+ tloge("shash init failed\n");
+ rc = -ENOMEM;
+ goto free_res;
+ }
+
+ down_read(&mm_sem_lock(mm));
+ if (update_task_hash(mm, cur_struct, &desc->shash)) {
+ up_read(&mm_sem_lock(mm));
+ rc = -ENOMEM;
+ goto free_res;
+ }
+ up_read(&mm_sem_lock(mm));
+
+ rc = crypto_shash_final(&desc->shash, digest);
+free_res:
+ mmput(mm);
+ kfree(desc);
+ return rc;
+}
+/* end: Calculate the SHA256 file digest */
diff --git a/linux/auth/auth_base_impl.h b/linux/auth/auth_base_impl.h
new file mode 100644
index 0000000..dca16f7
--- /dev/null
+++ b/linux/auth/auth_base_impl.h
@@ -0,0 +1,69 @@
+/*
+ * auth_base_impl.h
+ *
+ * function definition for base hash operation
+ *
+ * Copyright (C) 2022 Huawei Technologies Co., Ltd.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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 AUTH_BASE_IMPL_H
+#define AUTH_BASE_IMPL_H
+
+#ifdef CONFIG_CLIENT_AUTH
+#include
+#if (KERNEL_VERSION(4, 14, 0) <= LINUX_VERSION_CODE)
+#include
+#endif
+#include
+#include
+
+#define CHECK_ACCESS_SUCC 0
+#define CHECK_ACCESS_FAIL 0xffff
+#define CHECK_PATH_HASH_FAIL 0xff01
+#define CHECK_SECLABEL_FAIL 0xff02
+#define CHECK_CODE_HASH_FAIL 0xff03
+#define ENTER_BYPASS_CHANNEL 0xff04
+
+#define BUF_MAX_SIZE 1024
+#define MAX_PATH_SIZE 512
+#define SHA256_DIGEST_LENGTH 32
+
+struct sdesc {
+ struct shash_desc shash;
+ char ctx[];
+};
+
+int calc_path_hash(bool is_hidl_srvc, unsigned char *digest, unsigned int dig_len);
+int calc_task_hash(unsigned char *digest, uint32_t dig_len,
+ struct task_struct *cur_struct);
+
+int tee_init_shash_handle(char *hash_type);
+void tee_exit_shash_handle(void);
+struct crypto_shash *get_shash_handle(void);
+
+void init_crypto_hash_lock(void);
+void mutex_crypto_hash_lock(void);
+void mutex_crypto_hash_unlock(void);
+#else
+
+static inline void tee_exit_shash_handle(void)
+{
+ return;
+}
+
+static void init_crypto_hash_lock(void)
+{
+ return;
+}
+
+#endif /* CLIENT_AUTH */
+
+#endif
diff --git a/linux/auth/client_hash_auth.c b/linux/auth/client_hash_auth.c
new file mode 100644
index 0000000..c499064
--- /dev/null
+++ b/linux/auth/client_hash_auth.c
@@ -0,0 +1,247 @@
+/*
+ * client_hash_auth.c
+ *
+ * function for CA code hash auth
+ *
+ * Copyright (C) 2022 Huawei Technologies Co., Ltd.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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 "client_hash_auth.h"
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "tc_ns_log.h"
+#include "auth_base_impl.h"
+
+#define LIBTEEC_CODE_PAGE_SIZE 8
+#define DEFAULT_TEXT_OFF 0
+#define LIBTEEC_NAME_MAX_LEN 50
+const char g_libso[KIND_OF_SO][LIBTEEC_NAME_MAX_LEN] = {
+ "libteec_vendor.so",
+};
+
+static int find_lib_code_area(struct mm_struct *mm,
+ struct vm_area_struct **lib_code_area, int so_index)
+{
+ struct vm_area_struct *vma = NULL;
+ bool is_valid_vma = false;
+ bool is_so_exists = false;
+ bool param_check = (!mm || !mm->mmap ||
+ !lib_code_area || so_index >= KIND_OF_SO);
+
+ if (param_check) {
+ tloge("illegal input params\n");
+ return -EFAULT;
+ }
+ for (vma = mm->mmap; vma; vma = vma->vm_next) {
+ is_valid_vma = (vma->vm_file &&
+ vma->vm_file->f_path.dentry &&
+ vma->vm_file->f_path.dentry->d_name.name);
+ if (is_valid_vma) {
+ is_so_exists = !strcmp(g_libso[so_index],
+ vma->vm_file->f_path.dentry->d_name.name);
+ if (is_so_exists && (vma->vm_flags & VM_EXEC)) {
+ *lib_code_area = vma;
+ tlogd("so name is %s\n",
+ vma->vm_file->f_path.dentry->d_name.name);
+ return EOK;
+ }
+ }
+ }
+ return -EFAULT;
+}
+
+struct get_code_info {
+ unsigned long code_start;
+ unsigned long code_end;
+ unsigned long code_size;
+};
+static int update_so_hash(struct mm_struct *mm,
+ struct task_struct *cur_struct, struct shash_desc *shash, int so_index)
+{
+ struct vm_area_struct *vma = NULL;
+ int rc = -EFAULT;
+ struct get_code_info code_info;
+ unsigned long in_size;
+ struct page *ptr_page = NULL;
+ void *ptr_base = NULL;
+
+ if (find_lib_code_area(mm, &vma, so_index)) {
+ tlogd("get lib code vma area failed\n");
+ return -EFAULT;
+ }
+
+ code_info.code_start = vma->vm_start;
+ code_info.code_end = vma->vm_end;
+ code_info.code_size = code_info.code_end - code_info.code_start;
+
+ while (code_info.code_start < code_info.code_end) {
+ // Get a handle of the page we want to read
+#if (KERNEL_VERSION(5, 10, 0) <= LINUX_VERSION_CODE)
+ rc = get_user_pages_remote(mm, code_info.code_start, 1, FOLL_FORCE, &ptr_page, NULL, NULL);
+#else
+ rc = get_user_pages_remote(cur_struct, mm, code_info.code_start,
+ 1, FOLL_FORCE, &ptr_page, NULL, NULL);
+#endif
+ if (rc != 1) {
+ tloge("get user pages locked error[0x%x]\n", rc);
+ rc = -EFAULT;
+ break;
+ }
+
+ ptr_base = kmap_atomic(ptr_page);
+ if (!ptr_base) {
+ rc = -EFAULT;
+ put_page(ptr_page);
+ break;
+ }
+ in_size = (code_info.code_size > PAGE_SIZE) ? PAGE_SIZE : code_info.code_size;
+
+ rc = crypto_shash_update(shash, ptr_base, in_size);
+ if (rc) {
+ kunmap_atomic(ptr_base);
+ put_page(ptr_page);
+ break;
+ }
+ kunmap_atomic(ptr_base);
+ put_page(ptr_page);
+ code_info.code_start += in_size;
+ code_info.code_size = code_info.code_end - code_info.code_start;
+ }
+ return rc;
+}
+
+/* Calculate the SHA256 library digest */
+static int calc_task_so_hash(unsigned char *digest, uint32_t dig_len,
+ struct task_struct *cur_struct, int so_index)
+{
+ struct mm_struct *mm = NULL;
+ int rc;
+ size_t size;
+ size_t shash_size;
+ struct sdesc *desc = NULL;
+
+ if (!digest || dig_len != SHA256_DIGEST_LENGTH) {
+ tloge("tee hash: digest is NULL\n");
+ return -EFAULT;
+ }
+
+ shash_size = crypto_shash_descsize(get_shash_handle());
+ size = sizeof(desc->shash) + shash_size;
+ if (size < sizeof(desc->shash) || size < shash_size) {
+ tloge("size overflow\n");
+ return -ENOMEM;
+ }
+
+ desc = kzalloc(size, GFP_KERNEL);
+ if (ZERO_OR_NULL_PTR((unsigned long)(uintptr_t)desc)) {
+ tloge("alloc desc failed\n");
+ return -ENOMEM;
+ }
+
+ desc->shash.tfm = get_shash_handle();
+ if (crypto_shash_init(&desc->shash)) {
+ kfree(desc);
+ return -EFAULT;
+ }
+
+ mm = get_task_mm(cur_struct);
+ if (!mm) {
+ tloge("so does not have mm struct\n");
+ if (memset_s(digest, MAX_SHA_256_SZ, 0, dig_len))
+ tloge("memset digest failed\n");
+ kfree(desc);
+ return -EFAULT;
+ }
+
+ down_read(&mm_sem_lock(mm));
+ rc = update_so_hash(mm, cur_struct, &desc->shash, so_index);
+ up_read(&mm_sem_lock(mm));
+ mmput(mm);
+ if (!rc)
+ rc = crypto_shash_final(&desc->shash, digest);
+
+ kfree(desc);
+ return rc;
+}
+
+static int proc_calc_hash(uint8_t kernel_api, struct tc_ns_session *session,
+ struct task_struct *cur_struct)
+{
+ int rc, i;
+ int so_found = 0;
+
+ mutex_crypto_hash_lock();
+ if (kernel_api == TEE_REQ_FROM_USER_MODE) {
+ for (i = 0; so_found < NUM_OF_SO && i < KIND_OF_SO; i++) {
+ rc = calc_task_so_hash(session->auth_hash_buf + MAX_SHA_256_SZ * so_found,
+ (uint32_t)SHA256_DIGEST_LENGTH, cur_struct, i);
+ if (!rc)
+ so_found++;
+ }
+ if (so_found != NUM_OF_SO)
+ tlogd("so library found: %d\n", so_found);
+ } else {
+ tlogd("request from kernel\n");
+ }
+
+#ifdef CONFIG_ASAN_DEBUG
+ tloge("so auth disabled for ASAN debug\n");
+ uint32_t so_hash_len = MAX_SHA_256_SZ * NUM_OF_SO;
+ errno_t sret = memset_s(session->auth_hash_buf, so_hash_len, 0, so_hash_len);
+ if (sret) {
+ mutex_crypto_hash_unlock();
+ tloge("memset so hash failed\n");
+ return -EFAULT;
+ }
+#endif
+
+ rc = calc_task_hash(session->auth_hash_buf + MAX_SHA_256_SZ * NUM_OF_SO,
+ (uint32_t)SHA256_DIGEST_LENGTH, cur_struct);
+ if (rc) {
+ mutex_crypto_hash_unlock();
+ tloge("tee calc ca hash failed\n");
+ return -EFAULT;
+ }
+ mutex_crypto_hash_unlock();
+ return EOK;
+}
+
+int calc_client_auth_hash(struct tc_ns_dev_file *dev_file,
+ struct tc_ns_client_context *context, struct tc_ns_session *session)
+{
+ int ret;
+ struct task_struct *cur_struct = NULL;
+ bool check = false;
+ check = (!dev_file || !context || !session);
+ if (check) {
+ tloge("bad params\n");
+ return -EFAULT;
+ }
+
+ if (tee_init_shash_handle("sha256")) {
+ tloge("init code hash error\n");
+ return -EFAULT;
+ }
+
+ cur_struct = current;
+ ret = proc_calc_hash(dev_file->kernel_api, session, cur_struct);
+ return ret;
+}
diff --git a/linux/auth/client_hash_auth.h b/linux/auth/client_hash_auth.h
new file mode 100644
index 0000000..7a3da5d
--- /dev/null
+++ b/linux/auth/client_hash_auth.h
@@ -0,0 +1,39 @@
+/*
+ * client_hash_auth.h
+ *
+ * function definition for CA code hash auth
+ *
+ * Copyright (C) 2022 Huawei Technologies Co., Ltd.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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 CLIENT_HASH_CALC_H
+#define CLIENT_HASH_CALC_H
+
+#include "tc_ns_client.h"
+#include "teek_ns_client.h"
+
+#ifdef CONFIG_CLIENT_AUTH
+#include "auth_base_impl.h"
+
+int calc_client_auth_hash(struct tc_ns_dev_file *dev_file,
+ struct tc_ns_client_context *context, struct tc_ns_session *session);
+#else
+
+static inline int calc_client_auth_hash(struct tc_ns_dev_file *dev_file,
+ struct tc_ns_client_context *context, struct tc_ns_session *session)
+{
+ return 0;
+}
+
+#endif
+
+#endif
diff --git a/linux/core/Kconfig b/linux/core/Kconfig
new file mode 100644
index 0000000..6759c34
--- /dev/null
+++ b/linux/core/Kconfig
@@ -0,0 +1,49 @@
+# Framework Configuration
+config CPU_AFF_NR
+ int "Default Cpu Affinity"
+ default 0
+ depends on TZDRIVER
+ help
+ Default Cpu Affinity
+
+config DRM_ADAPT
+ bool "Drm Feature Adapt"
+ default n
+ depends on TZDRIVER
+ help
+ Drm Feature Adapt
+
+config TA_AFFINITY
+ bool "TA affinity"
+ default n
+ depends on TZDRIVER
+ help
+ TA Cpu Affinity bind range, consistent with CONFIG_MAX_NUM_NODES in TEE
+
+config TA_AFFINITY_CPU_NUMS
+ int "TA affinity max support cpus"
+ default 8
+ depends on TA_AFFINITY
+ help
+ consistent with CONFIG_MAX_NUM_NODES in TEE
+
+config TEE_AUDIT
+ bool "Audit TA"
+ default n
+ depends on AUTH_ENHANCE
+ help
+ Audit TA in case of evil TA
+
+config KERNEL_CLIENT
+ bool "Kernel Client Interface"
+ default n
+ depends on TZDRIVER
+ help
+ Kernel Client Interface
+
+config BIG_SESSION
+ bool "open more sessions"
+ default n
+ depends on TZDRIVER
+ help
+ TEEOS open more sessions
diff --git a/linux/core/Makefile b/linux/core/Makefile
new file mode 100644
index 0000000..da6d6a2
--- /dev/null
+++ b/linux/core/Makefile
@@ -0,0 +1,17 @@
+KERNEL_DIR :=$(srctree)
+
+ifneq ($(TARGET_BUILD_VARIANT),user)
+ ccflags-y += -DDEF_ENG
+endif
+
+EXTRA_CFLAGS += -I$(KERNEL_DIR)/../../../../../third_party/bounds_checking_function/include/
+EXTRA_CFLAGS += -I$(KERNEL_DIR)/../../../../../base/tee/tee_tzdriver/linux/include
+EXTRA_CFLAGS += -I$(KERNEL_DIR)/../../../../../base/tee/tee_tzdriver/linux/auth
+EXTRA_CFLAGS += -I$(KERNEL_DIR)/../../../../../base/tee/tee_tzdriver/linux/tlogger
+EXTRA_CFLAGS += -I$(KERNEL_DIR)/../../../../../base/tee/tee_tzdriver/linux/core
+EXTRA_CFLAGS += -I$(KERNEL_DIR)/../../../../../base/tee/tee_tzdriver/linux/kthread_affinity
+
+obj-$(CONFIG_KERNEL_CLIENT) += teek_client_api.o
+obj-y += smc_smp.o tc_client_driver.o session_manager.o mailbox_mempool.o teek_app_load.o
+obj-y += agent.o gp_ops.o mem.o cmdmonitor.o tzdebug.o tz_spi_notify.o tz_pm.o tee_compat_check.o
+obj-y += reserved_mempool.o
diff --git a/linux/core/agent.c b/linux/core/agent.c
new file mode 100644
index 0000000..8677122
--- /dev/null
+++ b/linux/core/agent.c
@@ -0,0 +1,1315 @@
+/*
+ * agent.c
+ *
+ * agent manager function, such as register and send cmd
+ *
+ * Copyright (C) 2022 Huawei Technologies Co., Ltd.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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 "agent.h"
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#if (KERNEL_VERSION(4, 14, 0) <= LINUX_VERSION_CODE)
+#include
+#include
+#endif
+#if (KERNEL_VERSION(5, 4, 0) <= LINUX_VERSION_CODE)
+#include
+#else
+#include
+#endif
+#include
+#include
+#include "teek_client_constants.h"
+#include "teek_ns_client.h"
+#include "smc_smp.h"
+#include "mem.h"
+#include "tc_ns_log.h"
+#include "mailbox_mempool.h"
+#include "tc_client_driver.h"
+#include "cmdmonitor.h"
+#include "ko_adapt.h"
+#include "tz_kthread_affinity.h"
+
+#ifdef CONFIG_CMS_CAHASH_AUTH
+#define HASH_FILE_MAX_SIZE CONFIG_HASH_FILE_SIZE
+#else
+#define HASH_FILE_MAX_SIZE (16 * 1024)
+#endif
+#define AGENT_BUFF_SIZE (4 * 1024)
+#define AGENT_MAX 32
+#define PAGE_ORDER_RATIO 2
+
+static struct list_head g_tee_agent_list;
+
+struct agent_control {
+ spinlock_t lock;
+ struct list_head agent_list;
+};
+
+static struct agent_control g_agent_control;
+
+int __attribute__((weak)) is_allowed_agent_ca(const struct ca_info *ca,
+ bool check_agent_id)
+{
+ (void)ca;
+ (void)check_agent_id;
+
+ return -EFAULT;
+}
+
+static int check_mm_struct(struct mm_struct *mm)
+{
+ if (!mm)
+ return -EINVAL;
+
+ if (!mm->exe_file) {
+ mmput(mm);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+char *get_proc_dpath(char *path, int path_len)
+{
+ char *dpath = NULL;
+ struct path base_path = {
+ .mnt = NULL,
+ .dentry = NULL
+ };
+ struct mm_struct *mm = NULL;
+ struct file *exe_file = NULL;
+
+ if (!path || path_len != MAX_PATH_SIZE) {
+ tloge("bad params\n");
+ return NULL;
+ }
+
+ if (memset_s(path, path_len, '\0', MAX_PATH_SIZE)) {
+ tloge("memset error\n");
+ return NULL;
+ }
+
+ mm = get_task_mm(current);
+ if (check_mm_struct(mm)) {
+ tloge("check mm_struct failed\n");
+ return NULL;
+ }
+
+ exe_file = get_mm_exe_file(mm);
+ if (!exe_file) {
+ mmput(mm);
+ return NULL;
+ }
+
+ base_path = exe_file->f_path;
+ path_get(&base_path);
+ dpath = d_path(&base_path, path, MAX_PATH_SIZE);
+ path_put(&base_path);
+ fput(exe_file);
+ mmput(mm);
+
+ return dpath;
+}
+
+static int get_ca_path_and_uid(struct ca_info *ca)
+{
+ char *path = NULL;
+ const struct cred *cred = NULL;
+ int message_size;
+ char *tpath = NULL;
+
+ tpath = kmalloc(MAX_PATH_SIZE, GFP_KERNEL);
+ if (ZERO_OR_NULL_PTR((unsigned long)(uintptr_t)tpath)) {
+ tloge("tpath kmalloc fail\n");
+ return -ENOMEM;
+ }
+
+ path = get_proc_dpath(tpath, MAX_PATH_SIZE);
+ if (IS_ERR_OR_NULL(path)) {
+ tloge("get process path failed\n");
+ kfree(tpath);
+ return -ENOMEM;
+ }
+
+ message_size = snprintf_s(ca->path, MAX_PATH_SIZE,
+ MAX_PATH_SIZE - 1, "%s", path);
+ if (message_size <= 0) {
+ tloge("pack path failed\n");
+ kfree(tpath);
+ return -EFAULT;
+ }
+
+ get_task_struct(current);
+ cred = koadpt_get_task_cred(current);
+ if (!cred) {
+ tloge("cred is NULL\n");
+ kfree(tpath);
+ put_task_struct(current);
+ return -EACCES;
+ }
+
+ ca->uid = cred->uid.val;
+ tlogd("ca_task->comm is %s, path is %s, ca uid is %u\n",
+ current->comm, path, cred->uid.val);
+
+ put_cred(cred);
+ put_task_struct(current);
+ kfree(tpath);
+ return 0;
+}
+
+int check_ext_agent_access(uint32_t agent_id)
+{
+ int ret;
+ struct ca_info agent_ca = { {0}, 0, 0 };
+
+ ret = get_ca_path_and_uid(&agent_ca);
+ if (ret) {
+ tloge("get cp path or uid failed\n");
+ return ret;
+ }
+ agent_ca.agent_id = agent_id;
+
+ return is_allowed_agent_ca(&agent_ca, true);
+}
+
+static int get_buf_len(const uint8_t *inbuf, uint32_t *buf_len)
+{
+ if (copy_from_user(buf_len, inbuf, sizeof(*buf_len))) {
+ tloge("copy from user failed\n");
+ return -EFAULT;
+ }
+
+ if (*buf_len > HASH_FILE_MAX_SIZE) {
+ tloge("ERROR: file size[0x%x] too big\n", *buf_len);
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+static int send_set_smc_cmd(struct mb_cmd_pack *mb_pack,
+ struct tc_ns_smc_cmd *smc_cmd, unsigned int cmd_id,
+ const uint8_t *buf_to_tee, uint32_t buf_len)
+{
+ int ret = 0;
+
+ mb_pack->operation.paramtypes = TEE_PARAM_TYPE_VALUE_INPUT |
+ (TEE_PARAM_TYPE_VALUE_INPUT << TEE_PARAM_NUM);
+ mb_pack->operation.params[0].value.a =
+ (unsigned int)virt_to_phys(buf_to_tee);
+ mb_pack->operation.params[0].value.b =
+ (uint64_t)virt_to_phys(buf_to_tee) >> ADDR_TRANS_NUM;
+ mb_pack->operation.params[1].value.a = buf_len;
+ smc_cmd->cmd_type = CMD_TYPE_GLOBAL;
+ smc_cmd->cmd_id = cmd_id;
+ smc_cmd->operation_phys = virt_to_phys(&mb_pack->operation);
+ smc_cmd->operation_h_phys =
+ (uint64_t)virt_to_phys(&mb_pack->operation) >> ADDR_TRANS_NUM;
+ if (tc_ns_smc(smc_cmd)) {
+ ret = -EPERM;
+ tloge("set native hash failed\n");
+ }
+
+ return ret;
+}
+
+int tc_ns_set_native_hash(unsigned long arg, unsigned int cmd_id)
+{
+ int ret;
+ struct tc_ns_smc_cmd smc_cmd = { {0}, 0 };
+ uint8_t *inbuf = (uint8_t *)(uintptr_t)arg;
+ uint32_t buf_len = 0;
+ uint8_t *buf_to_tee = NULL;
+ struct mb_cmd_pack *mb_pack = NULL;
+
+ if (!inbuf)
+ return -EINVAL;
+
+ if (tc_ns_get_uid()) {
+ tloge("It is a fake tee agent\n");
+ return -EACCES;
+ }
+
+ if (get_buf_len(inbuf, &buf_len))
+ return -EFAULT;
+
+ buf_to_tee = mailbox_alloc(buf_len, 0);
+ if (!buf_to_tee) {
+ tloge("failed to alloc memory!\n");
+ return -ENOMEM;
+ }
+
+ if (copy_from_user(buf_to_tee, inbuf, buf_len)) {
+ tloge("copy from user failed\n");
+ mailbox_free(buf_to_tee);
+ return -EFAULT;
+ }
+
+ mb_pack = mailbox_alloc_cmd_pack();
+ if (!mb_pack) {
+ tloge("alloc cmd pack failed\n");
+ mailbox_free(buf_to_tee);
+ return -ENOMEM;
+ }
+
+ ret = send_set_smc_cmd(mb_pack, &smc_cmd, cmd_id, buf_to_tee, buf_len);
+ mailbox_free(buf_to_tee);
+ mailbox_free(mb_pack);
+
+ return ret;
+}
+
+int tc_ns_late_init(unsigned long arg)
+{
+ int ret = 0;
+ struct tc_ns_smc_cmd smc_cmd = { {0}, 0 };
+ uint32_t index = (uint32_t)arg; /* index is uint32, no truncate risk */
+ struct mb_cmd_pack *mb_pack = NULL;
+
+ if (tc_ns_get_uid()) {
+ tloge("It is a fake tee agent\n");
+ return -EACCES;
+ }
+
+ mb_pack = mailbox_alloc_cmd_pack();
+ if (!mb_pack) {
+ tloge("alloc cmd pack failed\n");
+ return -ENOMEM;
+ }
+
+ mb_pack->operation.paramtypes = TEE_PARAM_TYPE_VALUE_INPUT;
+ mb_pack->operation.params[0].value.a = index;
+
+ smc_cmd.cmd_type = CMD_TYPE_GLOBAL;
+ smc_cmd.cmd_id = GLOBAL_CMD_ID_LATE_INIT;
+ smc_cmd.operation_phys = virt_to_phys(&mb_pack->operation);
+ smc_cmd.operation_h_phys =
+ (uint64_t)virt_to_phys(&mb_pack->operation) >> ADDR_TRANS_NUM;
+
+ if (tc_ns_smc(&smc_cmd)) {
+ ret = -EPERM;
+ tloge("late int failed\n");
+ }
+ mailbox_free(mb_pack);
+
+ return ret;
+}
+
+void send_event_response_single(const struct tc_ns_dev_file *dev_file)
+{
+ struct smc_event_data *event_data = NULL;
+ struct smc_event_data *tmp = NULL;
+ unsigned long flags;
+ unsigned int agent_id = 0;
+
+ if (!dev_file)
+ return;
+
+ spin_lock_irqsave(&g_agent_control.lock, flags);
+ list_for_each_entry_safe(event_data, tmp, &g_agent_control.agent_list,
+ head) {
+ if (event_data->owner == dev_file) {
+ agent_id = event_data->agent_id;
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&g_agent_control.lock, flags);
+ send_event_response(agent_id);
+ return;
+}
+
+struct smc_event_data *find_event_control(unsigned int agent_id)
+{
+ struct smc_event_data *event_data = NULL;
+ struct smc_event_data *tmp_data = NULL;
+ unsigned long flags;
+
+ spin_lock_irqsave(&g_agent_control.lock, flags);
+ list_for_each_entry(event_data, &g_agent_control.agent_list, head) {
+ if (event_data->agent_id == agent_id) {
+ tmp_data = event_data;
+ get_agent_event(event_data);
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&g_agent_control.lock, flags);
+
+ return tmp_data;
+}
+
+static void unmap_agent_buffer(struct smc_event_data *event_data)
+{
+ if (!event_data) {
+ tloge("event data is NULL\n");
+ return;
+ }
+
+ if (IS_ERR_OR_NULL(event_data->agent_buff_user))
+ return;
+
+ if (vm_munmap((unsigned long)(uintptr_t)event_data->agent_buff_user,
+ event_data->agent_buff_size))
+ tloge("unmap failed\n");
+
+ event_data->agent_buff_user = NULL;
+}
+
+static void free_event_control(unsigned int agent_id)
+{
+ struct smc_event_data *event_data = NULL;
+ struct smc_event_data *tmp_event = NULL;
+ unsigned long flags;
+ bool find = false;
+
+ spin_lock_irqsave(&g_agent_control.lock, flags);
+ list_for_each_entry_safe(event_data, tmp_event,
+ &g_agent_control.agent_list, head) {
+ if (event_data->agent_id == agent_id) {
+ list_del(&event_data->head);
+ find = true;
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&g_agent_control.lock, flags);
+
+ if (!find)
+ return;
+
+ unmap_agent_buffer(event_data);
+ mailbox_free(event_data->agent_buff_kernel);
+ event_data->agent_buff_kernel = NULL;
+ put_agent_event(event_data);
+}
+
+static int init_agent_context(unsigned int agent_id,
+ const struct tc_ns_smc_cmd *smc_cmd,
+ struct smc_event_data **event_data)
+{
+ *event_data = find_event_control(agent_id);
+ if (!(*event_data)) {
+ tloge("agent %u not exist\n", agent_id);
+ return -EINVAL;
+ }
+ tlogd("agent-0x%x: returning client command", agent_id);
+
+ isb();
+ wmb();
+
+ return 0;
+}
+
+static int wait_agent_response(struct smc_event_data *event_data)
+{
+ int ret = 0;
+ /* only userspace CA need freeze */
+ bool need_freeze = !(current->flags & PF_KTHREAD);
+ bool sig_pending = !sigisemptyset(¤t->pending.signal);
+ bool answered = true;
+ int rc;
+
+ do {
+ answered = true;
+ /*
+ * wait_event_freezable will be interrupted by signal and
+ * freezer which is called to free a userspace task in suspend.
+ * Freezing a task means wakeup a task by fake_signal_wake_up
+ * and let it have an opportunity to enter into 'refrigerator'
+ * by try_to_freeze used in wait_event_freezable.
+ *
+ * What scenes can be freezed ?
+ * 1. CA is waiting agent -> suspend -- OK
+ * 2. suspend -> CA start agent request -- OK
+ * 3. CA is waiting agent -> CA is killed -> suspend -- NOK
+ */
+ if (need_freeze && !sig_pending) {
+ rc = wait_event_freezable(event_data->ca_pending_wq,
+ atomic_read(&event_data->ca_run));
+ if (rc != -ERESTARTSYS)
+ continue;
+ if (!sigisemptyset(¤t->pending.signal))
+ sig_pending = true;
+ tloge("agent wait event is interrupted by %s\n",
+ sig_pending ? "signal" : "freezer");
+ /*
+ * When freezing a userspace task, fake_signal_wake_up
+ * only set TIF_SIGPENDING but not set a real signal.
+ * After task thawed, CA need wait agent response again
+ * so TIF_SIGPENDING need to be cleared.
+ */
+ if (!sig_pending)
+ clear_thread_flag(TIF_SIGPENDING);
+ answered = false;
+ } else {
+ rc = wait_event_timeout(event_data->ca_pending_wq,
+ atomic_read(&event_data->ca_run),
+ (long)(RESLEEP_TIMEOUT * HZ));
+ if (rc)
+ continue;
+ tloge("agent wait event is timeout\n");
+ /* if no kill signal, just resleep before agent wake */
+ if (!sigkill_pending(current)) {
+ answered = false;
+ } else {
+ tloge("CA is killed, no need to \
+wait agent response\n");
+ event_data->ret_flag = 0;
+ ret = -EFAULT;
+ }
+ }
+ } while (!answered);
+
+ return ret;
+}
+
+int agent_process_work(const struct tc_ns_smc_cmd *smc_cmd,
+ unsigned int agent_id)
+{
+ struct smc_event_data *event_data = NULL;
+ int ret;
+
+ if (!smc_cmd) {
+ tloge("smc_cmd is null\n");
+ return -EINVAL;
+ }
+
+ if (init_agent_context(agent_id, smc_cmd, &event_data))
+ return -EINVAL;
+
+ if (atomic_read(&event_data->agent_ready) == AGENT_CRASHED) {
+ tloge("agent 0x%x is killed and restarting\n", agent_id);
+ put_agent_event(event_data);
+ return -EFAULT;
+ }
+ event_data->ret_flag = 1;
+ /* Wake up the agent that will process the command */
+ tlogd("agent process work: wakeup the agent");
+ wake_up(&event_data->wait_event_wq);
+ tlogd("agent 0x%x request, goto sleep, pe->run=%d\n",
+ agent_id, atomic_read(&event_data->ca_run));
+
+ ret = wait_agent_response(event_data);
+ atomic_set(&event_data->ca_run, 0);
+ put_agent_event(event_data);
+ /*
+ * when agent work is done, reset cmd monitor time
+ * add agent call count, cause it's a new smc cmd.
+ */
+ cmd_monitor_reset_context();
+ return ret;
+}
+
+int is_agent_alive(unsigned int agent_id)
+{
+ struct smc_event_data *event_data = NULL;
+
+ event_data = find_event_control(agent_id);
+ if (event_data) {
+ put_agent_event(event_data);
+ return AGENT_ALIVE;
+ }
+
+ return AGENT_DEAD;
+}
+
+int tc_ns_wait_event(unsigned int agent_id)
+{
+ int ret = -EINVAL;
+ struct smc_event_data *event_data = NULL;
+
+ if (tc_ns_get_uid() && check_ext_agent_access(agent_id)) {
+ tloge("It is a fake tee agent\n");
+ return -EPERM;
+ }
+ tlogd("agent %u waits for command\n", agent_id);
+
+ event_data = find_event_control(agent_id);
+ if (event_data) {
+ /* only when agent wait event, it's in ready state to work */
+ atomic_set(&(event_data->agent_ready), AGENT_READY);
+ ret = wait_event_interruptible(event_data->wait_event_wq,
+ event_data->ret_flag);
+ put_agent_event(event_data);
+ }
+
+ return ret;
+}
+
+int tc_ns_sync_sys_time(const struct tc_ns_client_time *tc_ns_time)
+{
+ struct tc_ns_smc_cmd smc_cmd = { {0}, 0 };
+ int ret = 0;
+ struct tc_ns_client_time tmp_tc_ns_time = {0};
+ struct mb_cmd_pack *mb_pack = NULL;
+
+ if (!tc_ns_time) {
+ tloge("tc_ns_time is NULL input buffer\n");
+ return -EINVAL;
+ }
+
+ if (tc_ns_get_uid()) {
+ tloge("It is a fake tee agent\n");
+ return -EINVAL;
+ }
+
+ if (copy_from_user(&tmp_tc_ns_time, tc_ns_time,
+ sizeof(tmp_tc_ns_time))) {
+ tloge("copy from user failed\n");
+ return -EFAULT;
+ }
+
+ mb_pack = mailbox_alloc_cmd_pack();
+ if (!mb_pack) {
+ tloge("alloc mb pack failed\n");
+ return -ENOMEM;
+ }
+
+ smc_cmd.cmd_type = CMD_TYPE_GLOBAL;
+ smc_cmd.cmd_id = GLOBAL_CMD_ID_ADJUST_TIME;
+ smc_cmd.err_origin = tmp_tc_ns_time.seconds;
+ smc_cmd.ret_val = (int)tmp_tc_ns_time.millis;
+
+ if (tc_ns_smc(&smc_cmd)) {
+ tloge("tee adjust time failed, return error\n");
+ ret = -EPERM;
+ }
+ mailbox_free(mb_pack);
+
+ return ret;
+}
+
+static struct smc_event_data *check_response_access(unsigned int agent_id)
+{
+ struct smc_event_data *event_data = find_event_control(agent_id);
+
+ if (!event_data) {
+ tloge("Can't get event_data\n");
+ return NULL;
+ }
+
+ if (tc_ns_get_uid() &&
+ check_ext_agent_access(agent_id)) {
+ tloge("It is a fake tee agent\n");
+ put_agent_event(event_data);
+ return NULL;
+ }
+
+ return event_data;
+}
+
+static void process_send_event_response(struct smc_event_data *event_data)
+{
+ if (!event_data->ret_flag)
+ return;
+
+ event_data->ret_flag = 0;
+ /* Send the command back to the TA session waiting for it */
+ tlogd("agent wakeup ca\n");
+ atomic_set(&event_data->ca_run, 1);
+ /* make sure reset working_ca before wakeup CA */
+ wake_up(&event_data->ca_pending_wq);
+}
+
+int tc_ns_send_event_response(unsigned int agent_id)
+{
+ struct smc_event_data *event_data = NULL;
+
+ event_data = check_response_access(agent_id);
+ if (!event_data) {
+ tlogd("agent %u pre-check failed\n", agent_id);
+ return -EINVAL;
+ }
+
+ tlogd("agent %u sends answer back\n", agent_id);
+ process_send_event_response(event_data);
+ put_agent_event(event_data);
+
+ return 0;
+}
+
+void send_event_response(unsigned int agent_id)
+{
+ struct smc_event_data *event_data = find_event_control(agent_id);
+
+ if (!event_data) {
+ tloge("Can't get event_data\n");
+ return;
+ }
+
+ tloge("agent 0x%x sends answer back\n", agent_id);
+ atomic_set(&event_data->agent_ready, AGENT_CRASHED);
+ process_send_event_response(event_data);
+ put_agent_event(event_data);
+}
+
+static void init_restart_agent_node(struct tc_ns_dev_file *dev_file,
+ struct smc_event_data *event_data)
+{
+ tloge("agent: 0x%x restarting\n", event_data->agent_id);
+ event_data->ret_flag = 0;
+ event_data->owner = dev_file;
+ event_data->pid = current->tgid;
+ atomic_set(&event_data->agent_ready, AGENT_REGISTERED);
+ init_waitqueue_head(&(event_data->wait_event_wq));
+ init_waitqueue_head(&(event_data->send_response_wq));
+ init_waitqueue_head(&(event_data->ca_pending_wq));
+ atomic_set(&(event_data->ca_run), 0);
+}
+
+static int create_new_agent_node(struct tc_ns_dev_file *dev_file,
+ struct smc_event_data **event_data, unsigned int agent_id,
+ void **agent_buff, uint32_t agent_buff_size)
+{
+ *agent_buff = mailbox_alloc(agent_buff_size, MB_FLAG_ZERO);
+ if (!(*agent_buff)) {
+ tloge("alloc agent buff failed\n");
+ return -ENOMEM;
+ }
+ *event_data = kzalloc(sizeof(**event_data), GFP_KERNEL);
+ if (ZERO_OR_NULL_PTR((unsigned long)(uintptr_t)(*event_data))) {
+ mailbox_free(*agent_buff);
+ *agent_buff = NULL;
+ *event_data = NULL;
+ tloge("alloc event data failed\n");
+ return -ENOMEM;
+ }
+ (*event_data)->agent_id = agent_id;
+ (*event_data)->ret_flag = 0;
+ (*event_data)->agent_buff_kernel = *agent_buff;
+ (*event_data)->agent_buff_size = agent_buff_size;
+ (*event_data)->owner = dev_file;
+ (*event_data)->pid = current->tgid;
+ atomic_set(&(*event_data)->agent_ready, AGENT_REGISTERED);
+ init_waitqueue_head(&(*event_data)->wait_event_wq);
+ init_waitqueue_head(&(*event_data)->send_response_wq);
+ INIT_LIST_HEAD(&(*event_data)->head);
+ init_waitqueue_head(&(*event_data)->ca_pending_wq);
+ atomic_set(&(*event_data)->ca_run, 0);
+
+ return 0;
+}
+
+static bool is_built_in_agent(unsigned int agent_id)
+{
+ if (agent_id == AGENT_FS_ID ||
+ agent_id == AGENT_MISC_ID ||
+ agent_id == AGENT_SOCKET_ID ||
+ agent_id == SECFILE_LOAD_AGENT_ID)
+ return true;
+
+ return false;
+}
+
+static unsigned long agent_buffer_map(unsigned long buffer, uint32_t size)
+{
+ struct vm_area_struct *vma = NULL;
+ unsigned long user_addr;
+ int ret;
+
+ user_addr = vm_mmap(NULL, 0, size, PROT_READ | PROT_WRITE,
+ MAP_SHARED | MAP_ANONYMOUS, 0);
+ if (IS_ERR_VALUE((uintptr_t)user_addr)) {
+ tloge("vm mmap failed\n");
+ return user_addr;
+ }
+
+ down_read(&mm_sem_lock(current->mm));
+ vma = find_vma(current->mm, user_addr);
+ if (!vma) {
+ tloge("user_addr is not valid in vma");
+ goto err_out;
+ }
+
+ ret = remap_pfn_range(vma, user_addr, buffer >> PAGE_SHIFT, size,
+ vma->vm_page_prot);
+ if (ret) {
+ tloge("remap agent buffer failed, err=%d", ret);
+ goto err_out;
+ }
+
+ up_read(&mm_sem_lock(current->mm));
+ return user_addr;
+err_out:
+ up_read(&mm_sem_lock(current->mm));
+ if (vm_munmap(user_addr, size))
+ tloge("munmap failed\n");
+ return -EFAULT;
+}
+
+static bool is_valid_agent(unsigned int agent_id,
+ unsigned int buffer_size, bool user_agent)
+{
+ if (tc_ns_get_uid() &&
+ check_ext_agent_access(agent_id)) {
+ tloge("It is a fake tee agent\n");
+ return false;
+ }
+
+ if (user_agent && (buffer_size > SZ_4K)) {
+ tloge("size: %u of user agent's shared mem is invalid\n",
+ buffer_size);
+ return false;
+ }
+
+ return true;
+}
+
+void clean_agent_pid_info(struct tc_ns_dev_file *dev_file)
+{
+ struct smc_event_data *agent_node = NULL;
+ unsigned long flags;
+
+ spin_lock_irqsave(&g_agent_control.lock, flags);
+ list_for_each_entry(agent_node, &g_agent_control.agent_list, head) {
+ if (agent_node->owner == dev_file)
+ agent_node->pid = 0;
+ }
+ spin_unlock_irqrestore(&g_agent_control.lock, flags);
+}
+
+static int is_agent_already_exist(unsigned int agent_id,
+ struct smc_event_data **event_data, bool *find_flag)
+{
+ unsigned long flags;
+ bool flag = false;
+ struct smc_event_data *agent_node = NULL;
+
+ spin_lock_irqsave(&g_agent_control.lock, flags);
+ list_for_each_entry(agent_node, &g_agent_control.agent_list, head) {
+ if (agent_node->agent_id == agent_id) {
+ if (agent_node->pid == current->tgid) {
+ tloge("no allow agent proc to reg twice\n");
+ spin_unlock_irqrestore(&g_agent_control.lock, flags);
+ return -EINVAL;
+ }
+ flag = true;
+ get_agent_event(agent_node);
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&g_agent_control.lock, flags);
+ *find_flag = flag;
+ if (flag)
+ *event_data = agent_node;
+ return 0;
+}
+
+static void add_event_node_to_list(struct smc_event_data *event_data)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&g_agent_control.lock, flags);
+ list_add_tail(&event_data->head, &g_agent_control.agent_list);
+ atomic_set(&event_data->usage, 1);
+ spin_unlock_irqrestore(&g_agent_control.lock, flags);
+}
+
+static int register_agent_to_tee(unsigned int agent_id, const void *agent_buff,
+ uint32_t agent_buff_size)
+{
+ int ret = 0;
+ struct tc_ns_smc_cmd smc_cmd = { {0}, 0 };
+ struct mb_cmd_pack *mb_pack = NULL;
+
+ mb_pack = mailbox_alloc_cmd_pack();
+ if (!mb_pack) {
+ tloge("alloc mailbox failed\n");
+ return -ENOMEM;
+ }
+
+ mb_pack->operation.paramtypes = TEE_PARAM_TYPE_VALUE_INPUT |
+ (TEE_PARAM_TYPE_VALUE_INPUT << TEE_PARAM_NUM);
+ mb_pack->operation.params[0].value.a =
+ virt_to_phys(agent_buff);
+ mb_pack->operation.params[0].value.b =
+ (uint64_t)virt_to_phys(agent_buff) >> ADDR_TRANS_NUM;
+ mb_pack->operation.params[1].value.a = agent_buff_size;
+ smc_cmd.cmd_type = CMD_TYPE_GLOBAL;
+ smc_cmd.cmd_id = GLOBAL_CMD_ID_REGISTER_AGENT;
+ smc_cmd.operation_phys = virt_to_phys(&mb_pack->operation);
+ smc_cmd.operation_h_phys =
+ (uint64_t)virt_to_phys(&mb_pack->operation) >> ADDR_TRANS_NUM;
+ smc_cmd.agent_id = agent_id;
+
+ if (tc_ns_smc(&smc_cmd)) {
+ ret = -EPERM;
+ tloge("register agent to tee failed\n");
+ }
+ mailbox_free(mb_pack);
+
+ return ret;
+}
+
+static int get_agent_buffer(struct smc_event_data *event_data,
+ bool user_agent, void **buffer)
+{
+ /* agent first start or restart, both need a remap */
+ if (user_agent) {
+ event_data->agent_buff_user =
+ (void *)(uintptr_t)agent_buffer_map(
+ virt_to_phys(event_data->agent_buff_kernel),
+ event_data->agent_buff_size);
+ if (IS_ERR(event_data->agent_buff_user)) {
+ tloge("vm map agent buffer failed\n");
+ return -EFAULT;
+ }
+ *buffer = event_data->agent_buff_user;
+ } else {
+ *buffer = event_data->agent_buff_kernel;
+ }
+
+ return 0;
+}
+
+int tc_ns_register_agent(struct tc_ns_dev_file *dev_file,
+ unsigned int agent_id, unsigned int buffer_size,
+ void **buffer, bool user_agent)
+{
+ struct smc_event_data *event_data = NULL;
+ int ret = -EINVAL;
+ bool find_flag = false;
+ void *agent_buff = NULL;
+ uint32_t size_align;
+
+ /* dev can be null */
+ if (!buffer)
+ return ret;
+
+ if (!is_valid_agent(agent_id, buffer_size, user_agent))
+ return ret;
+
+ size_align = ALIGN(buffer_size, SZ_4K);
+
+ if (is_agent_already_exist(agent_id, &event_data, &find_flag))
+ return ret;
+ /*
+ * We find the agent event_data aready in agent_list, it indicate agent
+ * didn't unregister normally, so the event_data will be reused.
+ */
+ if (find_flag) {
+ init_restart_agent_node(dev_file, event_data);
+ } else {
+ ret = create_new_agent_node(dev_file, &event_data,
+ agent_id, &agent_buff, size_align);
+ if (ret)
+ return ret;
+ }
+
+ if (get_agent_buffer(event_data, user_agent, buffer))
+ goto release_rsrc;
+
+ /* find_flag is false means it's a new agent register */
+ if (!find_flag) {
+ /*
+ * Obtain share memory which is released
+ * in tc_ns_unregister_agent
+ */
+ ret = register_agent_to_tee(agent_id, agent_buff, size_align);
+ if (ret) {
+ unmap_agent_buffer(event_data);
+ goto release_rsrc;
+ }
+ add_event_node_to_list(event_data);
+ }
+ if (find_flag)
+ put_agent_event(event_data); /* match get action */
+ return 0;
+
+release_rsrc:
+ if (find_flag)
+ put_agent_event(event_data); /* match get action */
+ else
+ kfree(event_data); /* here event_data can never be NULL */
+
+ if (agent_buff)
+ mailbox_free(agent_buff);
+ return ret;
+}
+
+static int check_for_unregister_agent(unsigned int agent_id)
+{
+ bool check_value = false;
+
+ if (tc_ns_get_uid() && tc_ns_get_uid() != SYSTEM_UID) {
+ tloge("It is a fake tee agent\n");
+ return -EINVAL;
+ }
+
+ check_value = is_built_in_agent(agent_id);
+
+ if (check_value) {
+ tloge("agent: 0x%x is not allowed to unregister\n", agent_id);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+bool __attribute__((weak)) is_third_party_agent(unsigned int agent_id)
+{
+ (void)agent_id;
+
+ return false;
+}
+
+int tc_ns_unregister_agent(unsigned int agent_id)
+{
+ struct smc_event_data *event_data = NULL;
+ int ret = 0;
+ struct tc_ns_smc_cmd smc_cmd = { {0}, 0 };
+ struct mb_cmd_pack *mb_pack = NULL;
+
+ if (check_for_unregister_agent(agent_id))
+ return -EINVAL;
+ /*
+ * if third party itself trigger unregister agent
+ * we allow them to unregister.
+ */
+ if (!is_third_party_agent(agent_id)) {
+ tloge("invalid agent id: 0x%x\n", agent_id);
+ return -EACCES;
+ }
+
+ event_data = find_event_control(agent_id);
+ if (!event_data || !event_data->agent_buff_kernel) {
+ tloge("agent is not found or kaddr is not allocated\n");
+ return -EINVAL;
+ }
+
+ mb_pack = mailbox_alloc_cmd_pack();
+ if (!mb_pack) {
+ tloge("alloc mailbox failed\n");
+ put_agent_event(event_data);
+ return -ENOMEM;
+ }
+ mb_pack->operation.paramtypes = TEE_PARAM_TYPE_VALUE_INPUT |
+ (TEE_PARAM_TYPE_VALUE_INPUT << TEE_PARAM_NUM);
+ mb_pack->operation.params[0].value.a =
+ virt_to_phys(event_data->agent_buff_kernel);
+ mb_pack->operation.params[0].value.b =
+ (uint64_t)virt_to_phys(event_data->agent_buff_kernel) >> ADDR_TRANS_NUM;
+ mb_pack->operation.params[1].value.a = SZ_4K;
+ smc_cmd.cmd_type = CMD_TYPE_GLOBAL;
+ smc_cmd.cmd_id = GLOBAL_CMD_ID_UNREGISTER_AGENT;
+ smc_cmd.operation_phys = virt_to_phys(&mb_pack->operation);
+ smc_cmd.operation_h_phys =
+ (uint64_t)virt_to_phys(&mb_pack->operation) >> ADDR_TRANS_NUM;
+ smc_cmd.agent_id = agent_id;
+ tlogd("unregistering agent 0x%x\n", agent_id);
+
+ if (!tc_ns_smc(&smc_cmd)) {
+ free_event_control(agent_id);
+ } else {
+ ret = -EPERM;
+ tloge("unregister agent failed\n");
+ }
+ put_agent_event(event_data);
+ mailbox_free(mb_pack);
+ return ret;
+}
+
+bool is_system_agent(const struct tc_ns_dev_file *dev_file)
+{
+ struct smc_event_data *event_data = NULL;
+ struct smc_event_data *tmp = NULL;
+ bool system_agent = false;
+ unsigned long flags;
+
+ if (!dev_file)
+ return system_agent;
+
+ spin_lock_irqsave(&g_agent_control.lock, flags);
+ list_for_each_entry_safe(event_data, tmp, &g_agent_control.agent_list,
+ head) {
+ if (event_data->owner == dev_file) {
+ system_agent = true;
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&g_agent_control.lock, flags);
+
+ return system_agent;
+}
+
+void send_crashed_event_response_all(const struct tc_ns_dev_file *dev_file)
+{
+ struct smc_event_data *event_data = NULL;
+ struct smc_event_data *tmp = NULL;
+ unsigned int agent_id[AGENT_MAX] = {0};
+ unsigned int i = 0;
+ unsigned long flags;
+
+ if (!dev_file)
+ return;
+
+ spin_lock_irqsave(&g_agent_control.lock, flags);
+ list_for_each_entry_safe(event_data, tmp, &g_agent_control.agent_list,
+ head) {
+ if (event_data->owner == dev_file && i < AGENT_MAX)
+ agent_id[i++] = event_data->agent_id;
+ }
+ spin_unlock_irqrestore(&g_agent_control.lock, flags);
+
+ for (i = 0; i < AGENT_MAX; i++) {
+ if (agent_id[i])
+ send_event_response(agent_id[i]);
+ }
+
+ return;
+}
+
+void tee_agent_clear_dev_owner(const struct tc_ns_dev_file *dev_file)
+{
+ struct smc_event_data *event_data = NULL;
+ struct smc_event_data *tmp = NULL;
+ unsigned long flags;
+
+ spin_lock_irqsave(&g_agent_control.lock, flags);
+ list_for_each_entry_safe(event_data, tmp, &g_agent_control.agent_list,
+ head) {
+ if (event_data->owner == dev_file) {
+ event_data->owner = NULL;
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&g_agent_control.lock, flags);
+}
+
+
+static int def_tee_agent_work(void *instance)
+{
+ int ret = 0;
+ struct tee_agent_kernel_ops *agent_instance = NULL;
+
+ agent_instance = instance;
+ while (!kthread_should_stop()) {
+ tlogd("%s agent loop++++\n", agent_instance->agent_name);
+ ret = tc_ns_wait_event(agent_instance->agent_id);
+ if (ret) {
+ tloge("%s wait event fail\n",
+ agent_instance->agent_name);
+ break;
+ }
+ if (agent_instance->tee_agent_work) {
+ ret = agent_instance->tee_agent_work(agent_instance);
+ if (ret)
+ tloge("%s agent work fail\n",
+ agent_instance->agent_name);
+ }
+ ret = tc_ns_send_event_response(agent_instance->agent_id);
+ if (ret) {
+ tloge("%s send event response fail\n",
+ agent_instance->agent_name);
+ break;
+ }
+ tlogd("%s agent loop----\n", agent_instance->agent_name);
+ }
+
+ return ret;
+}
+
+static int def_tee_agent_run(struct tee_agent_kernel_ops *agent_instance)
+{
+ struct tc_ns_dev_file dev = {0};
+ int ret;
+
+ /* 1. Register agent buffer to TEE */
+ ret = tc_ns_register_agent(&dev, agent_instance->agent_id,
+ agent_instance->agent_buff_size, &agent_instance->agent_buff,
+ false);
+ if (ret) {
+ tloge("register agent buffer fail,ret =0x%x\n", ret);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ /* 2. Creat thread to run agent */
+ agent_instance->agent_thread =
+ kthread_create(def_tee_agent_work, agent_instance,
+ "agent_%s", agent_instance->agent_name);
+ if (IS_ERR_OR_NULL(agent_instance->agent_thread)) {
+ tloge("kthread creat fail\n");
+ ret = PTR_ERR(agent_instance->agent_thread);
+ agent_instance->agent_thread = NULL;
+ goto out;
+ }
+ tz_kthread_bind_mask(agent_instance->agent_thread);
+ wake_up_process(agent_instance->agent_thread);
+ return 0;
+
+out:
+ return ret;
+}
+
+static int def_tee_agent_stop(struct tee_agent_kernel_ops *agent_instance)
+{
+ int ret;
+
+ if (tc_ns_send_event_response(agent_instance->agent_id))
+ tloge("failed to send response for agent %u\n",
+ agent_instance->agent_id);
+ ret = tc_ns_unregister_agent(agent_instance->agent_id);
+ if (ret)
+ tloge("failed to unregister agent %u\n",
+ agent_instance->agent_id);
+ if (!IS_ERR_OR_NULL(agent_instance->agent_thread))
+ kthread_stop(agent_instance->agent_thread);
+
+ return 0;
+}
+
+static struct tee_agent_kernel_ops g_def_tee_agent_ops = {
+ .agent_name = "default",
+ .agent_id = 0,
+ .tee_agent_init = NULL,
+ .tee_agent_run = def_tee_agent_run,
+ .tee_agent_work = NULL,
+ .tee_agent_exit = NULL,
+ .tee_agent_stop = def_tee_agent_stop,
+ .tee_agent_crash_work = NULL,
+ .agent_buff_size = PAGE_SIZE,
+ .list = LIST_HEAD_INIT(g_def_tee_agent_ops.list)
+};
+
+static int tee_agent_kernel_init(void)
+{
+ struct tee_agent_kernel_ops *agent_ops = NULL;
+ int ret = 0;
+
+ list_for_each_entry(agent_ops, &g_tee_agent_list, list) {
+ /* Check the agent validity */
+ if (!agent_ops->agent_id ||
+ !agent_ops->agent_name ||
+ !agent_ops->tee_agent_work) {
+ tloge("agent is invalid\n");
+ continue;
+ }
+ tlogd("ready to init %s agent, id=0x%x\n",
+ agent_ops->agent_name, agent_ops->agent_id);
+
+ /* Set agent buff size */
+ if (!agent_ops->agent_buff_size)
+ agent_ops->agent_buff_size =
+ g_def_tee_agent_ops.agent_buff_size;
+
+ /* Initialize the agent */
+ if (agent_ops->tee_agent_init)
+ ret = agent_ops->tee_agent_init(agent_ops);
+ else if (g_def_tee_agent_ops.tee_agent_init)
+ ret = g_def_tee_agent_ops.tee_agent_init(agent_ops);
+ else
+ tlogw("agent id %u has no init function\n",
+ agent_ops->agent_id);
+ if (ret) {
+ tloge("tee_agent_init %s failed\n",
+ agent_ops->agent_name);
+ continue;
+ }
+
+ /* Run the agent */
+ if (agent_ops->tee_agent_run)
+ ret = agent_ops->tee_agent_run(agent_ops);
+ else if (g_def_tee_agent_ops.tee_agent_run)
+ ret = g_def_tee_agent_ops.tee_agent_run(agent_ops);
+ else
+ tlogw("agent id %u has no run function\n",
+ agent_ops->agent_id);
+
+ if (ret) {
+ tloge("tee_agent_run %s failed\n",
+ agent_ops->agent_name);
+ if (agent_ops->tee_agent_exit)
+ agent_ops->tee_agent_exit(agent_ops);
+ continue;
+ }
+ }
+
+ return 0;
+}
+
+static void tee_agent_kernel_exit(void)
+{
+ struct tee_agent_kernel_ops *agent_ops = NULL;
+
+ list_for_each_entry(agent_ops, &g_tee_agent_list, list) {
+ /* Stop the agent */
+ if (agent_ops->tee_agent_stop)
+ agent_ops->tee_agent_stop(agent_ops);
+ else if (g_def_tee_agent_ops.tee_agent_stop)
+ g_def_tee_agent_ops.tee_agent_stop(agent_ops);
+ else
+ tlogw("agent id %u has no stop function\n",
+ agent_ops->agent_id);
+
+ /* Uninitialize the agent */
+ if (agent_ops->tee_agent_exit)
+ agent_ops->tee_agent_exit(agent_ops);
+ else if (g_def_tee_agent_ops.tee_agent_exit)
+ g_def_tee_agent_ops.tee_agent_exit(agent_ops);
+ else
+ tlogw("agent id %u has no exit function\n",
+ agent_ops->agent_id);
+ }
+}
+
+int tee_agent_clear_work(struct tc_ns_client_context *context,
+ unsigned int dev_file_id)
+{
+ struct tee_agent_kernel_ops *agent_ops = NULL;
+
+ list_for_each_entry(agent_ops, &g_tee_agent_list, list) {
+ if (agent_ops->tee_agent_crash_work)
+ agent_ops->tee_agent_crash_work(agent_ops,
+ context, dev_file_id);
+ }
+ return 0;
+}
+
+int tee_agent_kernel_register(struct tee_agent_kernel_ops *new_agent)
+{
+ if (!new_agent)
+ return -EINVAL;
+
+ INIT_LIST_HEAD(&new_agent->list);
+ list_add_tail(&new_agent->list, &g_tee_agent_list);
+
+ return 0;
+}
+
+void agent_init(void)
+{
+ spin_lock_init(&g_agent_control.lock);
+ INIT_LIST_HEAD(&g_agent_control.agent_list);
+ INIT_LIST_HEAD(&g_tee_agent_list);
+
+ if (tee_agent_kernel_init())
+ tloge("tee agent kernel init failed\n");
+ return;
+}
+
+void agent_exit(void)
+{
+ tee_agent_kernel_exit();
+}
diff --git a/linux/core/agent.h b/linux/core/agent.h
new file mode 100644
index 0000000..496b426
--- /dev/null
+++ b/linux/core/agent.h
@@ -0,0 +1,131 @@
+/*
+ * agent.h
+ *
+ * agent manager function definition, such as register and send cmd
+ *
+ * Copyright (C) 2022 Huawei Technologies Co., Ltd.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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 AGENT_H
+#define AGENT_H
+#include
+#include "teek_ns_client.h"
+
+#define MAX_PATH_SIZE 512
+#define AGENT_FS_ID 0x46536673 /* FSfs */
+#define AGENT_MISC_ID 0x4d495343 /* MISC */
+
+#define AGENT_SOCKET_ID 0x69e85664 /* socket */
+#define SECFILE_LOAD_AGENT_ID 0x4c4f4144 /* SECFILE-LOAD-AGENT */
+#define TEE_SECE_AGENT_ID 0x53656345 /* npu agent id */
+#define TEE_FACE_AGENT1_ID 0x46616365 /* face agent id */
+#define TEE_FACE_AGENT2_ID 0x46616345 /* face agent id */
+#define TEE_VLTMM_AGENT_ID 0x564c544d /* vltmm agent id */
+#define SYSTEM_UID 1000
+
+enum agent_state_type {
+ AGENT_CRASHED = 0,
+ AGENT_REGISTERED,
+ AGENT_READY,
+};
+
+enum agent_status {
+ AGENT_ALIVE = 1,
+ AGENT_DEAD = 0,
+};
+
+/* for secure agent */
+struct smc_event_data {
+ unsigned int agent_id;
+ atomic_t agent_ready;
+ wait_queue_head_t wait_event_wq;
+ int ret_flag; /* indicate whether agent is returned from TEE */
+ wait_queue_head_t send_response_wq;
+ struct list_head head;
+ struct tc_ns_smc_cmd cmd;
+ struct tc_ns_dev_file *owner;
+ pid_t pid;
+ void *agent_buff_kernel;
+ void *agent_buff_user; /* used for unmap */
+ unsigned int agent_buff_size;
+ atomic_t usage;
+ wait_queue_head_t ca_pending_wq;
+ /* indicate whether agent is allowed to return to TEE */
+ atomic_t ca_run;
+};
+
+struct tee_agent_kernel_ops {
+ const char *agent_name;
+ unsigned int agent_id;
+ int (*tee_agent_init)(struct tee_agent_kernel_ops *agent_instance);
+ int (*tee_agent_run)(struct tee_agent_kernel_ops *agent_instance);
+ int (*tee_agent_work)(struct tee_agent_kernel_ops *agent_instance);
+ int (*tee_agent_stop)(struct tee_agent_kernel_ops *agent_instance);
+ int (*tee_agent_exit)(struct tee_agent_kernel_ops *agent_instance);
+ int (*tee_agent_crash_work)(
+ struct tee_agent_kernel_ops *agent_instance,
+ struct tc_ns_client_context *context,
+ unsigned int dev_file_id);
+ struct task_struct *agent_thread;
+ void *agent_data;
+ void *agent_buff;
+ unsigned int agent_buff_size;
+ struct list_head list;
+};
+
+struct ca_info {
+ char path[MAX_PATH_SIZE];
+ uint32_t uid;
+ uint32_t agent_id;
+};
+
+static inline void get_agent_event(struct smc_event_data *event_data)
+{
+ if (event_data)
+ atomic_inc(&event_data->usage);
+}
+
+static inline void put_agent_event(struct smc_event_data *event_data)
+{
+ if (event_data) {
+ if (atomic_dec_and_test(&event_data->usage))
+ kfree(event_data);
+ }
+}
+
+int is_allowed_agent_ca(const struct ca_info *ca,
+ bool check_agent_id);
+bool is_third_party_agent(unsigned int agent_id);
+void agent_init(void);
+void agent_exit(void);
+struct smc_event_data *find_event_control(unsigned int agent_id);
+void send_event_response(unsigned int agent_id);
+int agent_process_work(const struct tc_ns_smc_cmd *smc_cmd, unsigned int agent_id);
+int is_agent_alive(unsigned int agent_id);
+int tc_ns_set_native_hash(unsigned long arg, unsigned int cmd_id);
+int tc_ns_late_init(unsigned long arg);
+int tc_ns_register_agent(struct tc_ns_dev_file *dev_file, unsigned int agent_id,
+ unsigned int buffer_size, void **buffer, bool user_agent);
+int tc_ns_unregister_agent(unsigned int agent_id);
+void send_crashed_event_response_all(const struct tc_ns_dev_file *dev_file);
+int tc_ns_wait_event(unsigned int agent_id);
+int tc_ns_send_event_response(unsigned int agent_id);
+void send_event_response_single(const struct tc_ns_dev_file *dev_file);
+int tc_ns_sync_sys_time(const struct tc_ns_client_time *tc_ns_time);
+int tee_agent_clear_work(struct tc_ns_client_context *context,
+ unsigned int dev_file_id);
+int tee_agent_kernel_register(struct tee_agent_kernel_ops *new_agent);
+bool is_system_agent(const struct tc_ns_dev_file *dev_file);
+void tee_agent_clear_dev_owner(const struct tc_ns_dev_file *dev_file);
+char *get_proc_dpath(char *path, int path_len);
+void clean_agent_pid_info(struct tc_ns_dev_file *dev_file);
+
+#endif
diff --git a/linux/core/cmdmonitor.c b/linux/core/cmdmonitor.c
new file mode 100644
index 0000000..68c0320
--- /dev/null
+++ b/linux/core/cmdmonitor.c
@@ -0,0 +1,431 @@
+/*
+ * cmd_monitor.c
+ *
+ * cmdmonitor function, monitor every cmd which is sent to TEE.
+ *
+ * Copyright (C) 2022 Huawei Technologies Co., Ltd.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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 "cmdmonitor.h"
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#if (KERNEL_VERSION(4, 14, 0) <= LINUX_VERSION_CODE)
+#include
+#endif
+
+#include "tc_ns_log.h"
+#include "smc_smp.h"
+#include "mailbox_mempool.h"
+#include "tlogger.h"
+#include "log_cfg_api.h"
+#include "tz_kthread_affinity.h"
+
+static int g_cmd_need_archivelog;
+static LIST_HEAD(g_cmd_monitor_list);
+static int g_cmd_monitor_list_size;
+/* report 2 hours */
+static const long long g_memstat_report_freq = 2 * 60 * 60 * 1000;
+#define MAX_CMD_MONITOR_LIST 200
+#define MAX_AGENT_CALL_COUNT 250
+static DEFINE_MUTEX(g_cmd_monitor_lock);
+
+/* independent wq to avoid block system_wq */
+static struct workqueue_struct *g_cmd_monitor_wq;
+static struct delayed_work g_cmd_monitor_work;
+static struct delayed_work g_cmd_monitor_work_archive;
+static struct delayed_work g_mem_stat;
+static int g_tee_detect_ta_crash;
+
+enum {
+ TYPE_CRASH_TA = 1,
+ TYPE_CRASH_TEE = 2,
+};
+
+static void get_time_spec(struct time_spec *time)
+{
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 19, 0))
+ time->ts = current_kernel_time();
+#else
+ ktime_get_coarse_ts64(&time->ts);
+#endif
+}
+
+static void schedule_memstat_work(struct delayed_work *work,
+ unsigned long delay)
+{
+ schedule_delayed_work(work, delay);
+}
+static void schedule_cmd_monitor_work(struct delayed_work *work,
+ unsigned long delay)
+{
+ if (g_cmd_monitor_wq)
+ queue_delayed_work(g_cmd_monitor_wq, work, delay);
+ else
+ schedule_delayed_work(work, delay);
+}
+
+void tzdebug_memstat(void)
+{
+ schedule_memstat_work(&g_mem_stat, usecs_to_jiffies(S_TO_MS));
+}
+
+void tzdebug_archivelog(void)
+{
+ schedule_cmd_monitor_work(&g_cmd_monitor_work_archive,
+ usecs_to_jiffies(0));
+}
+
+void cmd_monitor_ta_crash(int32_t type)
+{
+ g_tee_detect_ta_crash = ((type == TYPE_CRASH_TEE) ?
+ TYPE_CRASH_TEE : TYPE_CRASH_TA);
+ tzdebug_archivelog();
+}
+
+static int get_pid_name(pid_t pid, char *comm, size_t size)
+{
+ struct task_struct *task = NULL;
+ int sret;
+
+ if (size <= TASK_COMM_LEN - 1 || !comm)
+ return -1;
+
+ rcu_read_lock();
+
+#ifndef CONFIG_TZDRIVER_MODULE
+ task = find_task_by_vpid(pid);
+#else
+ task = pid_task(find_vpid(pid), PIDTYPE_PID);
+#endif
+ if (task)
+ get_task_struct(task);
+ rcu_read_unlock();
+ if (!task) {
+ tloge("get task failed\n");
+ return -1;
+ }
+
+ sret = strncpy_s(comm, size, task->comm, strlen(task->comm));
+ if (sret)
+ tloge("strncpy faild: errno = %d\n", sret);
+ put_task_struct(task);
+
+ return sret;
+}
+
+bool is_thread_reported(unsigned int tid)
+{
+ bool ret = false;
+ struct cmd_monitor *monitor = NULL;
+
+ mutex_lock(&g_cmd_monitor_lock);
+ list_for_each_entry(monitor, &g_cmd_monitor_list, list) {
+ if (monitor->tid == tid) {
+ ret = (monitor->is_reported ||
+ monitor->agent_call_count >
+ MAX_AGENT_CALL_COUNT);
+ break;
+ }
+ }
+ mutex_unlock(&g_cmd_monitor_lock);
+ return ret;
+}
+
+void memstat_report(void)
+{
+ int ret;
+ struct tee_mem *meminfo = NULL;
+
+ meminfo = mailbox_alloc(sizeof(*meminfo), MB_FLAG_ZERO);
+ if (!meminfo) {
+ tloge("mailbox alloc failed\n");
+ return;
+ }
+
+ ret = get_tee_meminfo(meminfo);
+ if (!ret)
+ tlogd("get meminfo failed\n");
+ mailbox_free(meminfo);
+}
+
+static void memstat_work(struct work_struct *work)
+{
+ (void)(work);
+ memstat_report();
+}
+
+void cmd_monitor_reset_context(void)
+{
+ struct cmd_monitor *monitor = NULL;
+ pid_t pid = current->tgid;
+ pid_t tid = current->pid;
+
+ mutex_lock(&g_cmd_monitor_lock);
+ list_for_each_entry(monitor, &g_cmd_monitor_list, list) {
+ if (monitor->pid == pid && monitor->tid == tid) {
+ get_time_spec(&monitor->sendtime);
+ if (monitor->agent_call_count + 1 < 0)
+ tloge("agent call count add overflow\n");
+ else
+ monitor->agent_call_count++;
+ break;
+ }
+ }
+ mutex_unlock(&g_cmd_monitor_lock);
+}
+
+/*
+ * if one session timeout, monitor will print timedifs every step[n] in secends,
+ * if lasted more then 360s, monitor will print timedifs every 360s.
+ */
+const int32_t g_timer_step[] = {1, 1, 1, 2, 5, 10, 40, 120, 180, 360};
+const int32_t g_timer_nums = sizeof(g_timer_step) / sizeof(int32_t);
+static void show_timeout_cmd_info(struct cmd_monitor *monitor)
+{
+ long long timedif, timedif2;
+ struct time_spec nowtime;
+ get_time_spec(&nowtime);
+
+ /*
+ * 1 year means 1000 * (60*60*24*365) = 0x757B12C00
+ * only 5bytes, so timedif (timedif=nowtime-sendtime) will not overflow
+ */
+ timedif = S_TO_MS * (nowtime.ts.tv_sec - monitor->sendtime.ts.tv_sec) +
+ (nowtime.ts.tv_nsec - monitor->sendtime.ts.tv_nsec) / S_TO_US;
+
+ /* timeout to 10s, we log the teeos log, and report */
+ if ((timedif > CMD_MAX_EXECUTE_TIME * S_TO_MS) && (!monitor->is_reported)) {
+ monitor->is_reported = true;
+ tlogw("[cmd_monitor_tick] pid=%d,pname=%s,tid=%d, "
+ "tname=%s, lastcmdid=%u, agent call count:%d, "
+ "running with timedif=%lld ms and report\n",
+ monitor->pid, monitor->pname, monitor->tid,
+ monitor->tname, monitor->lastcmdid,
+ monitor->agent_call_count, timedif);
+ tlogw("monitor: pid-%d", monitor->pid);
+ show_cmd_bitmap();
+ g_cmd_need_archivelog = 1;
+ wakeup_tc_siq(SIQ_DUMP_TIMEOUT);
+ }
+
+ timedif2 = S_TO_MS * (nowtime.ts.tv_sec - monitor->lasttime.ts.tv_sec) +
+ (nowtime.ts.tv_nsec - monitor->lasttime.ts.tv_nsec) / S_TO_US;
+ int32_t time_in_sec = monitor->timer_index >= g_timer_nums ?
+ g_timer_step[g_timer_nums - 1] : g_timer_step[monitor->timer_index];
+ if (timedif2 > time_in_sec * S_TO_MS) {
+ monitor->lasttime = nowtime;
+ monitor->timer_index = monitor->timer_index >= sizeof(g_timer_step) ?
+ sizeof(g_timer_step) : (monitor->timer_index + 1);
+ tlogw("[cmd_monitor_tick] pid=%d,pname=%s,tid=%d, "
+ "lastcmdid=%u,agent call count:%d,timedif=%lld ms\n",
+ monitor->pid, monitor->pname, monitor->tid,
+ monitor->lastcmdid, monitor->agent_call_count,
+ timedif);
+ }
+}
+
+static void cmd_monitor_tick(void)
+{
+ struct cmd_monitor *monitor = NULL;
+ struct cmd_monitor *tmp = NULL;
+
+ mutex_lock(&g_cmd_monitor_lock);
+ list_for_each_entry_safe(monitor, tmp, &g_cmd_monitor_list, list) {
+ if (monitor->returned) {
+ g_cmd_monitor_list_size--;
+ tlogi("[cmd_monitor_tick] pid=%d,pname=%s,tid=%d, "
+ "tname=%s,lastcmdid=%u,count=%d,agent call count=%d, "
+ "timetotal=%lld us returned, remained command(s)=%d\n",
+ monitor->pid, monitor->pname, monitor->tid, monitor->tname,
+ monitor->lastcmdid, monitor->count, monitor->agent_call_count,
+ monitor->timetotal, g_cmd_monitor_list_size);
+ list_del(&monitor->list);
+ kfree(monitor);
+ continue;
+ }
+ show_timeout_cmd_info(monitor);
+ }
+
+ /* if have cmd in monitor list, we need tick */
+ if (g_cmd_monitor_list_size > 0)
+ schedule_cmd_monitor_work(&g_cmd_monitor_work, usecs_to_jiffies(S_TO_US));
+ mutex_unlock(&g_cmd_monitor_lock);
+}
+
+static void cmd_monitor_tickfn(struct work_struct *work)
+{
+ (void)(work);
+ cmd_monitor_tick();
+ /* check tlogcat if have new log */
+ tz_log_write();
+}
+
+static void cmd_monitor_archivefn(struct work_struct *work)
+{
+ (void)(work);
+ if (tlogger_store_msg(CONFIG_TEE_LOG_ACHIVE_PATH,
+ sizeof(CONFIG_TEE_LOG_ACHIVE_PATH)) < 0)
+ tloge("[cmd_monitor_tick]tlogger store lastmsg failed\n");
+
+ if (g_tee_detect_ta_crash == TYPE_CRASH_TA) {
+ }
+
+ if (g_tee_detect_ta_crash == TYPE_CRASH_TEE) {
+ tloge("detect teeos crash, panic\n");
+ report_log_system_panic();
+ }
+
+ g_tee_detect_ta_crash = 0;
+}
+
+static struct cmd_monitor *init_monitor_locked(void)
+{
+ struct cmd_monitor *newitem = NULL;
+
+ newitem = kzalloc(sizeof(*newitem), GFP_KERNEL);
+ if (ZERO_OR_NULL_PTR((unsigned long)(uintptr_t)newitem)) {
+ tloge("[cmd_monitor_tick]kzalloc faild\n");
+ return NULL;
+ }
+
+ get_time_spec(&newitem->sendtime);
+ newitem->lasttime = newitem->sendtime;
+ newitem->timer_index = 0;
+ newitem->count = 1;
+ newitem->agent_call_count = 0;
+ newitem->returned = false;
+ newitem->is_reported = false;
+ newitem->pid = current->tgid;
+ newitem->tid = current->pid;
+ if (get_pid_name(newitem->pid, newitem->pname,
+ sizeof(newitem->pname)))
+ newitem->pname[0] = '\0';
+ if (get_pid_name(newitem->tid, newitem->tname,
+ sizeof(newitem->tname)))
+ newitem->tname[0] = '\0';
+ INIT_LIST_HEAD(&newitem->list);
+ list_add_tail(&newitem->list, &g_cmd_monitor_list);
+ g_cmd_monitor_list_size++;
+ return newitem;
+}
+
+struct cmd_monitor *cmd_monitor_log(const struct tc_ns_smc_cmd *cmd)
+{
+ bool found_flag = false;
+ pid_t pid;
+ pid_t tid;
+ struct cmd_monitor *monitor = NULL;
+
+ if (!cmd)
+ return NULL;
+
+ pid = current->tgid;
+ tid = current->pid;
+ mutex_lock(&g_cmd_monitor_lock);
+ do {
+ list_for_each_entry(monitor, &g_cmd_monitor_list, list) {
+ if (monitor->pid == pid && monitor->tid == tid) {
+ found_flag = true;
+ /* restart */
+ get_time_spec(&monitor->sendtime);
+ monitor->lasttime = monitor->sendtime;
+ monitor->timer_index = 0;
+ monitor->count++;
+ monitor->returned = false;
+ monitor->is_reported = false;
+ monitor->lastcmdid = cmd->cmd_id;
+ monitor->agent_call_count = 0;
+ monitor->timetotal = 0;
+ break;
+ }
+ }
+
+ if (!found_flag) {
+#ifndef CONFIG_BIG_SESSION
+ if (g_cmd_monitor_list_size > MAX_CMD_MONITOR_LIST - 1) {
+ tloge("monitor reach max node num\n");
+ monitor = NULL;
+ break;
+ }
+#endif
+ monitor = init_monitor_locked();
+ if (!monitor) {
+ tloge("init monitor failed\n");
+ break;
+ }
+ monitor->lastcmdid = cmd->cmd_id;
+ /* the first cmd will cause timer */
+ if (g_cmd_monitor_list_size == 1)
+ schedule_cmd_monitor_work(&g_cmd_monitor_work,
+ usecs_to_jiffies(S_TO_US));
+ }
+ } while (0);
+ mutex_unlock(&g_cmd_monitor_lock);
+
+ return monitor;
+}
+
+void cmd_monitor_logend(struct cmd_monitor *item)
+{
+ struct time_spec nowtime;
+ long long timedif;
+
+ if (!item)
+ return;
+
+ get_time_spec(&nowtime);
+ /*
+ * get time value D (timedif=nowtime-sendtime),
+ * we do not care about overflow
+ * 1 year means 1000000 * (60*60*24*365) = 0x1CAE8C13E000
+ * only 6bytes, will not overflow
+ */
+ timedif = S_TO_US * (nowtime.ts.tv_sec - item->sendtime.ts.tv_sec) +
+ (nowtime.ts.tv_nsec - item->sendtime.ts.tv_nsec) / S_TO_MS;
+ item->timetotal += timedif;
+ item->returned = true;
+}
+
+void do_cmd_need_archivelog(void)
+{
+ if (g_cmd_need_archivelog == 1) {
+ g_cmd_need_archivelog = 0;
+ schedule_cmd_monitor_work(&g_cmd_monitor_work_archive,
+ usecs_to_jiffies(S_TO_US));
+ }
+}
+
+void init_cmd_monitor(void)
+{
+ g_cmd_monitor_wq = alloc_workqueue("tz_cmd_monitor_wq",
+ WQ_UNBOUND, TZ_WQ_MAX_ACTIVE);
+ if (!g_cmd_monitor_wq)
+ tloge("alloc cmd monitor wq failed\n");
+ else
+ tz_workqueue_bind_mask(g_cmd_monitor_wq, 0);
+
+ INIT_DEFERRABLE_WORK((struct delayed_work *)
+ (uintptr_t)&g_cmd_monitor_work, cmd_monitor_tickfn);
+ INIT_DEFERRABLE_WORK((struct delayed_work *)
+ (uintptr_t)&g_cmd_monitor_work_archive, cmd_monitor_archivefn);
+ INIT_DEFERRABLE_WORK((struct delayed_work *)
+ (uintptr_t)&g_mem_stat, memstat_work);
+
+}
diff --git a/linux/core/cmdmonitor.h b/linux/core/cmdmonitor.h
new file mode 100644
index 0000000..d87a79f
--- /dev/null
+++ b/linux/core/cmdmonitor.h
@@ -0,0 +1,72 @@
+/*
+ * cmd_monitor.h
+ *
+ * cmdmonitor function declaration
+ *
+ * Copyright (C) 2022 Huawei Technologies Co., Ltd.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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 CMD_MONITOR_H
+#define CMD_MONITOR_H
+
+#include "tzdebug.h"
+#include "teek_ns_client.h"
+#include
+
+#if (KERNEL_VERSION(4, 14, 0) > LINUX_VERSION_CODE)
+#define TASK_COMM_LEN 16
+#endif
+
+/*
+ * when cmd execute more than 25s in tee,
+ * it will be terminated when CA is killed
+ */
+#define CMD_MAX_EXECUTE_TIME 10U
+#define S_TO_MS 1000
+#define S_TO_US 1000000
+
+struct time_spec {
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 19, 0))
+ struct timespec ts;
+#else
+ struct timespec64 ts;
+#endif
+};
+
+struct cmd_monitor {
+ struct list_head list;
+ struct time_spec sendtime;
+ struct time_spec lasttime;
+ int32_t timer_index;
+ int count;
+ bool returned;
+ bool is_reported;
+ pid_t pid;
+ pid_t tid;
+ char pname[TASK_COMM_LEN];
+ char tname[TASK_COMM_LEN];
+ unsigned int lastcmdid;
+ long long timetotal;
+ int agent_call_count;
+};
+
+struct cmd_monitor *cmd_monitor_log(const struct tc_ns_smc_cmd *cmd);
+void cmd_monitor_reset_context(void);
+void cmd_monitor_logend(struct cmd_monitor *item);
+void init_cmd_monitor(void);
+void do_cmd_need_archivelog(void);
+bool is_thread_reported(unsigned int tid);
+void tzdebug_archivelog(void);
+void cmd_monitor_ta_crash(int32_t type);
+void memstat_report(void);
+void tzdebug_memstat(void);
+
+#endif
diff --git a/linux/core/gp_ops.c b/linux/core/gp_ops.c
new file mode 100644
index 0000000..66ea9b0
--- /dev/null
+++ b/linux/core/gp_ops.c
@@ -0,0 +1,1271 @@
+/*
+ * gp_op.c
+ *
+ * alloc global operation and pass params to TEE.
+ *
+ * Copyright (C) 2022 Huawei Technologies Co., Ltd.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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 "gp_ops.h"
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "teek_client_constants.h"
+#include "tc_ns_client.h"
+#include "agent.h"
+#include "tc_ns_log.h"
+#include "smc_smp.h"
+#include "mem.h"
+#include "mailbox_mempool.h"
+#include "reserved_mempool.h"
+#include "tc_client_driver.h"
+#include "tlogger.h"
+
+#define MAX_SHARED_SIZE 0x100000 /* 1 MiB */
+
+static void free_operation(const struct tc_call_params *call_params,
+ struct tc_op_params *op_params);
+
+/* dir: 0-inclue input, 1-include output, 2-both */
+#define INPUT 0
+#define OUTPUT 1
+#define INOUT 2
+
+static inline bool is_input_type(int dir)
+{
+ if (dir == INPUT || dir == INOUT)
+ return true;
+
+ return false;
+}
+
+static inline bool is_output_type(int dir)
+{
+ if (dir == OUTPUT || dir == INOUT)
+ return true;
+
+ return false;
+}
+
+static inline bool teec_value_type(unsigned int type, int dir)
+{
+ return ((is_input_type(dir) && type == TEEC_VALUE_INPUT) ||
+ (is_output_type(dir) && type == TEEC_VALUE_OUTPUT) ||
+ type == TEEC_VALUE_INOUT) ? true : false;
+}
+
+static inline bool teec_tmpmem_type(unsigned int type, int dir)
+{
+ return ((is_input_type(dir) && type == TEEC_MEMREF_TEMP_INPUT) ||
+ (is_output_type(dir) && type == TEEC_MEMREF_TEMP_OUTPUT) ||
+ type == TEEC_MEMREF_TEMP_INOUT) ? true : false;
+}
+
+static inline bool teec_memref_type(unsigned int type, int dir)
+{
+ return ((is_input_type(dir) && type == TEEC_MEMREF_PARTIAL_INPUT) ||
+ (is_output_type(dir) && type == TEEC_MEMREF_PARTIAL_OUTPUT) ||
+ type == TEEC_MEMREF_PARTIAL_INOUT) ? true : false;
+}
+
+static int check_user_param(const struct tc_ns_client_context *client_context,
+ unsigned int index)
+{
+ if (!client_context) {
+ tloge("client_context is null\n");
+ return -EINVAL;
+ }
+
+ if (index >= PARAM_NUM) {
+ tloge("index is invalid, index:%x\n", index);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+bool is_tmp_mem(uint32_t param_type)
+{
+ if (param_type == TEEC_MEMREF_TEMP_INPUT ||
+ param_type == TEEC_MEMREF_TEMP_OUTPUT ||
+ param_type == TEEC_MEMREF_TEMP_INOUT)
+ return true;
+
+ return false;
+}
+
+bool is_ref_mem(uint32_t param_type)
+{
+ if (param_type == TEEC_MEMREF_PARTIAL_INPUT ||
+ param_type == TEEC_MEMREF_PARTIAL_OUTPUT ||
+ param_type == TEEC_MEMREF_PARTIAL_INOUT)
+ return true;
+
+ return false;
+}
+
+bool is_val_param(uint32_t param_type)
+{
+ if (param_type == TEEC_VALUE_INPUT ||
+ param_type == TEEC_VALUE_OUTPUT ||
+ param_type == TEEC_VALUE_INOUT ||
+ param_type == TEEC_ION_INPUT ||
+ param_type == TEEC_ION_SGLIST_INPUT)
+ return true;
+
+ return false;
+}
+
+bool is_ion_param(uint32_t param_type)
+{
+ if (param_type == TEEC_ION_INPUT ||
+ param_type == TEEC_ION_SGLIST_INPUT)
+ return true;
+
+ return false;
+}
+
+static bool is_mem_param(uint32_t param_type)
+{
+ if (is_tmp_mem(param_type) || is_ref_mem(param_type))
+ return true;
+
+ return false;
+}
+
+/* Check the size and buffer addresses have valid userspace addresses */
+static bool is_usr_refmem_valid(union tc_ns_client_param *client_param)
+{
+ uint32_t size = 0;
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 19, 18) || \
+ LINUX_VERSION_CODE == KERNEL_VERSION(4, 19, 71))
+ if (!access_ok(VERIFY_READ,
+ (void *)(uintptr_t)client_param->memref.size_addr,
+ sizeof(uint32_t)))
+#else
+ if (!access_ok(
+ (void *)(uintptr_t)client_param->memref.size_addr,
+ sizeof(uint32_t)))
+#endif
+ return false;
+
+ get_user(size, (uint32_t *)(uintptr_t)client_param->memref.size_addr);
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 19, 18) || \
+ LINUX_VERSION_CODE == KERNEL_VERSION(4, 19, 71))
+ if (!access_ok(VERIFY_READ,
+ (void *)(uintptr_t)client_param->memref.buffer, size))
+#else
+ if (!access_ok((void *)(uintptr_t)client_param->memref.buffer, size))
+#endif
+ return false;
+
+ return true;
+}
+
+static bool is_usr_valmem_valid(union tc_ns_client_param *client_param)
+{
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 19, 18) || \
+ LINUX_VERSION_CODE == KERNEL_VERSION(4, 19, 71))
+ if (!access_ok(VERIFY_READ,
+ (void *)(uintptr_t)client_param->value.a_addr,
+ sizeof(uint32_t)))
+#else
+ if (!access_ok(
+ (void *)(uintptr_t)client_param->value.a_addr,
+ sizeof(uint32_t)))
+#endif
+ return false;
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 19, 18) || \
+ LINUX_VERSION_CODE == KERNEL_VERSION(4, 19, 71))
+ if (!access_ok(VERIFY_READ,
+ (void *)(uintptr_t)client_param->value.b_addr,
+ sizeof(uint32_t)))
+#else
+ if (!access_ok(
+ (void *)(uintptr_t)client_param->value.b_addr,
+ sizeof(uint32_t)))
+#endif
+ return false;
+
+ return true;
+}
+
+bool tc_user_param_valid(struct tc_ns_client_context *client_context,
+ unsigned int index)
+{
+ union tc_ns_client_param *client_param = NULL;
+ unsigned int param_type;
+
+ if (check_user_param(client_context, index))
+ return false;
+
+ client_param = &(client_context->params[index]);
+ param_type = teec_param_type_get(client_context->param_types, index);
+ tlogd("param %u type is %x\n", index, param_type);
+ if (param_type == TEEC_NONE) {
+ tlogd("param type is TEEC_NONE\n");
+ return true;
+ }
+
+ if (is_mem_param(param_type)) {
+ if (!is_usr_refmem_valid(client_param))
+ return false;
+ } else if (is_val_param(param_type)) {
+ if (!is_usr_valmem_valid(client_param))
+ return false;
+ } else {
+ tloge("param types is not supported\n");
+ return false;
+ }
+
+ return true;
+}
+
+/*
+ * These function handle read from client. Because client here can be
+ * kernel client or user space client, we must use the proper function
+ */
+int read_from_client(void *dest, size_t dest_size,
+ const void __user *src, size_t size, uint8_t kernel_api)
+{
+ int ret;
+
+ if (!dest || !src) {
+ tloge("src or dest is NULL input buffer\n");
+ return -EINVAL;
+ }
+
+ if (size > dest_size) {
+ tloge("size is larger than dest_size or size is 0\n");
+ return -EINVAL;
+ }
+ if (!size)
+ return 0;
+
+ if (kernel_api) {
+ ret = memcpy_s(dest, dest_size, src, size);
+ if (ret != EOK) {
+ tloge("memcpy fail. line=%d, s_ret=%d\n",
+ __LINE__, ret);
+ return ret;
+ }
+ return ret;
+ }
+ /* buffer is in user space(CA call TEE API) */
+ if (copy_from_user(dest, src, size)) {
+ tloge("copy from user failed\n");
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+int write_to_client(void __user *dest, size_t dest_size,
+ const void *src, size_t size, uint8_t kernel_api)
+{
+ int ret;
+
+ if (!dest || !src) {
+ tloge("src or dest is NULL input buffer\n");
+ return -EINVAL;
+ }
+
+ if (size > dest_size) {
+ tloge("size is larger than dest_size\n");
+ return -EINVAL;
+ }
+
+ if (!size)
+ return 0;
+
+ if (kernel_api) {
+ ret = memcpy_s(dest, dest_size, src, size);
+ if (ret != EOK) {
+ tloge("write to client fail. line=%d, ret=%d\n",
+ __LINE__, ret);
+ return ret;
+ }
+ return ret;
+ }
+
+ /* buffer is in user space(CA call TEE API) */
+ if (copy_to_user(dest, src, size)) {
+ tloge("copy to user failed\n");
+ return -EFAULT;
+ }
+ return 0;
+}
+
+static bool is_input_tempmem(unsigned int param_type)
+{
+ if (param_type == TEEC_MEMREF_TEMP_INPUT ||
+ param_type == TEEC_MEMREF_TEMP_INOUT)
+ return true;
+
+ return false;
+}
+
+static int update_input_data(const union tc_ns_client_param *client_param,
+ uint32_t buffer_size, void *temp_buf,
+ unsigned int param_type, uint8_t kernel_params)
+{
+ if (!is_input_tempmem(param_type))
+ return 0;
+
+ if (read_from_client(temp_buf, buffer_size,
+ (void *)(uintptr_t)client_param->memref.buffer,
+ buffer_size, kernel_params)) {
+ tloge("copy memref buffer failed\n");
+ return -EFAULT;
+ }
+ return 0;
+}
+
+/*
+ * temp buffers we need to allocate/deallocate
+ * for every operation
+ */
+static int alloc_for_tmp_mem(const struct tc_call_params *call_params,
+ struct tc_op_params *op_params, uint8_t kernel_params,
+ uint32_t param_type, unsigned int index)
+{
+ union tc_ns_client_param *client_param = NULL;
+ void *temp_buf = NULL;
+ uint32_t buffer_size = 0;
+
+ /* this never happens */
+ if (index >= TEE_PARAM_NUM)
+ return -EINVAL;
+
+ /* For compatibility sake we assume buffer size to be 32bits */
+ client_param = &(call_params->context->params[index]);
+ if (read_from_client(&buffer_size, sizeof(buffer_size),
+ (uint32_t __user *)(uintptr_t)client_param->memref.size_addr,
+ sizeof(uint32_t), kernel_params)) {
+ tloge("copy memref.size_addr failed\n");
+ return -EFAULT;
+ }
+
+ if (buffer_size > MAX_SHARED_SIZE) {
+ tloge("buffer size %u from user is too large\n", buffer_size);
+ return -EFAULT;
+ }
+
+ temp_buf = mailbox_alloc(buffer_size, MB_FLAG_ZERO);
+ if (!temp_buf) {
+ tloge("temp buf malloc failed, i = %u\n", index);
+ return -ENOMEM;
+ }
+ op_params->local_tmpbuf[index].temp_buffer = temp_buf;
+ op_params->local_tmpbuf[index].size = buffer_size;
+
+ if (update_input_data(client_param, buffer_size, temp_buf,
+ param_type, kernel_params))
+ return -EFAULT;
+
+ op_params->mb_pack->operation.params[index].memref.buffer =
+ virt_to_phys(temp_buf);
+ op_params->mb_pack->operation.buffer_h_addr[index] =
+ (uint64_t)virt_to_phys(temp_buf) >> ADDR_TRANS_NUM;
+ op_params->mb_pack->operation.params[index].memref.size = buffer_size;
+ /* TEEC_MEMREF_TEMP_INPUT equal to TEE_PARAM_TYPE_MEMREF_INPUT */
+ op_params->trans_paramtype[index] = param_type;
+
+ return 0;
+}
+
+static int check_buffer_for_ref(uint32_t *buffer_size,
+ const union tc_ns_client_param *client_param, uint8_t kernel_params)
+{
+ if (read_from_client(buffer_size, sizeof(*buffer_size),
+ (uint32_t __user *)(uintptr_t)client_param->memref.size_addr,
+ sizeof(uint32_t), kernel_params)) {
+ tloge("copy memref.size_addr failed\n");
+ return -EFAULT;
+ }
+ if (*buffer_size == 0) {
+ tloge("buffer_size from user is 0\n");
+ return -ENOMEM;
+ }
+ return 0;
+}
+
+static bool is_refmem_offset_valid(struct tc_ns_shared_mem *shared_mem,
+ union tc_ns_client_param *client_param, uint32_t buffer_size)
+{
+ /*
+ * arbitrary CA can control offset by ioctl, so in here
+ * offset must be checked, and avoid integer overflow.
+ */
+ if (((shared_mem->len - client_param->memref.offset) >= buffer_size) &&
+ (shared_mem->len > client_param->memref.offset))
+ return true;
+ tloge("Unexpected size %u vs %u", shared_mem->len, buffer_size);
+ return false;
+}
+
+static bool is_phyaddr_valid(struct tc_ns_operation *operation, int index)
+{
+ /*
+ * for 8G physical memory device, there is a chance that
+ * operation->params[i].memref.buffer could be all 0,
+ * buffer_h_addr cannot be 0 in the same time.
+ */
+ if ((!operation->params[index].memref.buffer) &&
+ (!operation->buffer_h_addr[index])) {
+ tloge("can not find shared buffer, exit\n");
+ return false;
+ }
+
+ return true;
+}
+
+static int set_operation_buffer(struct tc_ns_shared_mem *shared_mem, void *buffer_addr,
+ uint32_t buffer_size, unsigned int index, struct tc_op_params *op_params)
+{
+ if (shared_mem->mem_type == RESERVED_TYPE) {
+ /* no copy to mailbox */
+ op_params->mb_pack->operation.mb_buffer[index] = buffer_addr;
+ op_params->mb_pack->operation.params[index].memref.buffer =
+ res_mem_virt_to_phys((uintptr_t)buffer_addr);
+ op_params->mb_pack->operation.buffer_h_addr[index] =
+ res_mem_virt_to_phys((uintptr_t)buffer_addr) >> ADDR_TRANS_NUM;
+ } else {
+ buffer_addr = mailbox_copy_alloc(buffer_addr, buffer_size);
+ if (buffer_addr == NULL)
+ return -ENOMEM;
+
+ op_params->mb_pack->operation.mb_buffer[index] = buffer_addr;
+ op_params->mb_pack->operation.params[index].memref.buffer =
+ virt_to_phys(buffer_addr);
+ op_params->mb_pack->operation.buffer_h_addr[index] =
+ (uint64_t)virt_to_phys(buffer_addr) >> ADDR_TRANS_NUM;
+ }
+ return 0;
+}
+
+/*
+ * MEMREF_PARTIAL buffers are already allocated so we just
+ * need to search for the shared_mem ref;
+ * For interface compatibility we assume buffer size to be 32bits
+ */
+static int alloc_for_ref_mem(const struct tc_call_params *call_params,
+ struct tc_op_params *op_params, uint8_t kernel_params,
+ uint32_t param_type, unsigned int index)
+{
+ union tc_ns_client_param *client_param = NULL;
+ struct tc_ns_shared_mem *shared_mem = NULL;
+ uint32_t buffer_size = 0;
+ void *buffer_addr = NULL;
+ int ret = 0;
+
+ /* this never happens */
+ if (index >= TEE_PARAM_NUM)
+ return -EINVAL;
+
+ client_param = &(call_params->context->params[index]);
+ if (check_buffer_for_ref(&buffer_size, client_param, kernel_params))
+ return -EINVAL;
+
+ op_params->mb_pack->operation.params[index].memref.buffer = 0;
+
+ mutex_lock(&call_params->dev->shared_mem_lock);
+ list_for_each_entry(shared_mem,
+ &call_params->dev->shared_mem_list, head) {
+ if (shared_mem->user_addr !=
+ (void *)(uintptr_t)client_param->memref.buffer)
+ continue;
+ if (!is_refmem_offset_valid(shared_mem, client_param,
+ buffer_size)) {
+ break;
+ }
+ buffer_addr = (void *)(uintptr_t)(
+ (uintptr_t)shared_mem->kernel_addr +
+ client_param->memref.offset);
+
+ ret = set_operation_buffer(shared_mem, buffer_addr, buffer_size, index, op_params);
+ if (ret) {
+ tloge("set operation buffer failed\n");
+ break;
+ }
+ op_params->mb_pack->operation.sharemem[index] = shared_mem;
+ get_sharemem_struct(shared_mem);
+ break;
+ }
+ mutex_unlock(&call_params->dev->shared_mem_lock);
+ if (ret != 0)
+ return ret;
+
+ if (!is_phyaddr_valid(&op_params->mb_pack->operation, index))
+ return -EINVAL;
+
+ op_params->mb_pack->operation.params[index].memref.size = buffer_size;
+ /* Change TEEC_MEMREF_PARTIAL_XXXXX to TEE_PARAM_TYPE_MEMREF_XXXXX */
+ op_params->trans_paramtype[index] = param_type -
+ (TEEC_MEMREF_PARTIAL_INPUT - TEE_PARAM_TYPE_MEMREF_INPUT);
+
+ if (shared_mem->mem_type == RESERVED_TYPE)
+ op_params->trans_paramtype[index] +=
+ (TEE_PARAM_TYPE_RESMEM_INPUT - TEE_PARAM_TYPE_MEMREF_INPUT);
+ return ret;
+}
+
+#ifdef CONFIG_NOCOPY_SHAREDMEM
+static int fill_shared_mem_info(void *start_vaddr, uint32_t pages_no,
+ uint32_t offset, uint32_t buffer_size, void *buff)
+{
+ struct pagelist_info *page_info = NULL;
+ struct page **pages = NULL;
+ uint64_t *phys_addr = NULL;
+ uint32_t page_num;
+ uint32_t i;
+
+ if (pages_no == 0)
+ return -EFAULT;
+
+ pages = (struct page **)vmalloc(pages_no * sizeof(uint64_t));
+ if (pages == NULL)
+ return -EFAULT;
+
+ down_read(&(current->mm->mmap_sem));
+ page_num = get_user_pages(start_vaddr, pages_no, 0, pages, NULL);
+ up_read(&(current->mm->mmap_sem));
+ if (page_num != pages_no) {
+ tloge("get page phy addr failed\n");
+ if (page_num > 0)
+ release_pages(pages, page_num);
+ vfree(pages);
+ return -EFAULT;
+ }
+
+ page_info = buff;
+ page_info->page_num = pages_no;
+ page_info->page_size = PAGE_SIZE;
+ page_info->sharedmem_offset = offset;
+ page_info->sharedmem_size = buffer_size;
+
+ phys_addr = (uint64_t *)buff + (sizeof(*page_info) / sizeof(uint64_t));
+ for (i = 0; i < pages_no; i++) {
+ struct page *page = pages[i];
+ if (page == NULL) {
+ release_pages(pages, page_num);
+ vfree(pages);
+ return -EFAULT;
+ }
+ phys_addr[i] = (uintptr_t)page_to_phys(page);
+ }
+
+ vfree(pages);
+ return 0;
+}
+
+static int check_buffer_for_sharedmem(uint32_t *buffer_size,
+ const union tc_ns_client_param *client_param, uint8_t kernel_params)
+{
+ if (read_from_client(buffer_size, sizeof(*buffer_size),
+ (uint32_t __user *)(uintptr_t)client_param->sharedmem.size_addr,
+ sizeof(uint32_t), kernel_params)) {
+ tloge("copy sharedmem.size_addr failed\n");
+ return -EFAULT;
+ }
+
+ if (*buffer_size == 0 || *buffer_size > SZ_256M) {
+ tloge("invalid buffer size\n");
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static int transfer_shared_mem(const struct tc_call_params *call_params,
+ struct tc_op_params *op_params, uint8_t kernel_params,
+ uint32_t param_type, unsigned int index)
+{
+ void *buff = NULL;
+ void *start_vaddr = NULL;
+ union tc_ns_client_param *client_param = NULL;
+ uint32_t buffer_size;
+ uint32_t pages_no;
+ uint32_t offset;
+ uint32_t buff_len;
+
+ if (index >= TEE_PARAM_NUM)
+ return -EINVAL;
+
+ client_param = &(call_params->context->params[index]);
+ if (check_buffer_for_sharedmem(&buffer_size, client_param, kernel_params))
+ return -EINVAL;
+
+ buff = (void *)(uint64_t)client_param->sharedmem.buffer;
+ start_vaddr = (void *)(((uint64_t)buff) & PAGE_MASK);
+ offset = ((uint32_t)(uintptr_t)buff) & (~PAGE_MASK);
+ pages_no = PAGE_ALIGN(offset + buffer_size) / PAGE_SIZE;
+
+ buff_len = sizeof(struct pagelist_info) + (sizeof(uint64_t) * pages_no);
+ buff = mailbox_alloc(buff_len, MB_FLAG_ZERO);
+ if (buff == NULL)
+ return -EFAULT;
+
+ if (fill_shared_mem_info(start_vaddr, pages_no, offset, buffer_size, buff)) {
+ mailbox_free(buff);
+ return -EFAULT;
+ }
+
+ op_params->local_tmpbuf[index].temp_buffer = buff;
+ op_params->local_tmpbuf[index].size = buff_len;
+
+ op_params->mb_pack->operation.params[index].sharedmem.buffer = virt_to_phys(buff);
+ op_params->mb_pack->operation.buffer_h_addr[index] = (uint64_t)virt_to_phys(buff) >> ADDR_TRANS_NUM;
+ op_params->mb_pack->operation.params[index].sharedmem.size = buff_len;
+ op_params->trans_paramtype[index] = param_type;
+ return 0;
+}
+#else
+static int transfer_shared_mem(const struct tc_call_params *call_params,
+ struct tc_op_params *op_params, uint8_t kernel_params,
+ uint32_t param_type, unsigned int index)
+{
+ tloge("invalid shared mem type\n");
+ return -1;
+}
+#endif
+
+static int transfer_client_value(const struct tc_call_params *call_params,
+ struct tc_op_params *op_params, uint8_t kernel_params,
+ uint32_t param_type, unsigned int index)
+{
+ struct tc_ns_operation *operation = &op_params->mb_pack->operation;
+ union tc_ns_client_param *client_param = NULL;
+
+ /* this never happens */
+ if (index >= TEE_PARAM_NUM)
+ return -EINVAL;
+
+ client_param = &(call_params->context->params[index]);
+ if (read_from_client(&operation->params[index].value.a,
+ sizeof(operation->params[index].value.a),
+ (void *)(uintptr_t)client_param->value.a_addr,
+ sizeof(operation->params[index].value.a),
+ kernel_params)) {
+ tloge("copy valuea failed\n");
+ return -EFAULT;
+ }
+ if (read_from_client(&operation->params[index].value.b,
+ sizeof(operation->params[index].value.b),
+ (void *)(uintptr_t)client_param->value.b_addr,
+ sizeof(operation->params[index].value.b),
+ kernel_params)) {
+ tloge("copy valueb failed\n");
+ return -EFAULT;
+ }
+
+ /* TEEC_VALUE_INPUT equal to TEE_PARAM_TYPE_VALUE_INPUT */
+ op_params->trans_paramtype[index] = param_type;
+ return 0;
+}
+
+static int alloc_for_ion_sglist(const struct tc_call_params *call_params,
+ struct tc_op_params *op_params, uint8_t kernel_params,
+ uint32_t param_type, unsigned int index)
+{
+ tloge("not support seg and releated feature!\n");
+ return -1;
+}
+
+static inline int alloc_for_ion(const struct tc_call_params *call_params,
+ struct tc_op_params *op_params, uint8_t kernel_params,
+ uint32_t param_type, unsigned int index)
+{
+ tloge("not support ion and releated feature!\n");
+ return -1;
+}
+
+static int alloc_operation(const struct tc_call_params *call_params,
+ struct tc_op_params *op_params)
+{
+ int ret = 0;
+ uint32_t index;
+ uint8_t kernel_params;
+ uint32_t param_type;
+
+ kernel_params = call_params->dev->kernel_api;
+ for (index = 0; index < TEE_PARAM_NUM; index++) {
+ /*
+ * Normally kernel_params = kernel_api
+ * But when TC_CALL_LOGIN, params 2/3 will
+ * be filled by kernel. so under this circumstance,
+ * params 2/3 has to be set to kernel mode; and
+ * param 0/1 will keep the same with kernel_api.
+ */
+ if ((call_params->flags & TC_CALL_LOGIN) && (index >= 2))
+ kernel_params = TEE_REQ_FROM_KERNEL_MODE;
+ param_type = teec_param_type_get(
+ call_params->context->param_types, index);
+
+ tlogd("param %u type is %x\n", index, param_type);
+ if (teec_tmpmem_type(param_type, INOUT))
+ ret = alloc_for_tmp_mem(call_params, op_params,
+ kernel_params, param_type, index);
+ else if (teec_memref_type(param_type, INOUT))
+ ret = alloc_for_ref_mem(call_params, op_params,
+ kernel_params, param_type, index);
+ else if (teec_value_type(param_type, INOUT))
+ ret = transfer_client_value(call_params, op_params,
+ kernel_params, param_type, index);
+ else if (param_type == TEEC_ION_INPUT)
+ ret = alloc_for_ion(call_params, op_params,
+ kernel_params, param_type, index);
+ else if (param_type == TEEC_ION_SGLIST_INPUT)
+ ret = alloc_for_ion_sglist(call_params, op_params,
+ kernel_params, param_type, index);
+ else if (param_type == TEEC_MEMREF_SHARED_INOUT)
+ ret = transfer_shared_mem(call_params, op_params,
+ kernel_params, param_type, index);
+ else
+ tlogd("param type = TEEC_NONE\n");
+
+ if (ret)
+ break;
+ }
+ if (ret) {
+ free_operation(call_params, op_params);
+ return ret;
+ }
+ op_params->mb_pack->operation.paramtypes =
+ teec_param_types(op_params->trans_paramtype[0],
+ op_params->trans_paramtype[1],
+ op_params->trans_paramtype[2],
+ op_params->trans_paramtype[3]);
+ op_params->op_inited = true;
+
+ return ret;
+}
+
+static int update_tmp_mem(const struct tc_call_params *call_params,
+ struct tc_op_params *op_params, unsigned int index, bool is_complete)
+{
+ union tc_ns_client_param *client_param = NULL;
+ uint32_t buffer_size;
+ struct tc_ns_operation *operation = &op_params->mb_pack->operation;
+
+ if (index >= TEE_PARAM_NUM) {
+ tloge("tmp buf size or index is invalid\n");
+ return -EFAULT;
+ }
+
+ buffer_size = operation->params[index].memref.size;
+ client_param = &(call_params->context->params[index]);
+ /* Size is updated all the time */
+ if (write_to_client((void *)(uintptr_t)client_param->memref.size_addr,
+ sizeof(buffer_size),
+ &buffer_size, sizeof(buffer_size),
+ call_params->dev->kernel_api)) {
+ tloge("copy tempbuf size failed\n");
+ return -EFAULT;
+ }
+ if (buffer_size > op_params->local_tmpbuf[index].size) {
+ /* incomplete case, when the buffer size is invalid see next param */
+ if (!is_complete)
+ return 0;
+ /*
+ * complete case, operation is allocated from mailbox
+ * and share with gtask, so it's possible to be changed
+ */
+ tloge("memref.size has been changed larger than the initial\n");
+ return -EFAULT;
+ }
+ /* Only update the buffer when the buffer size is valid in complete case */
+ if (write_to_client((void *)(uintptr_t)client_param->memref.buffer,
+ operation->params[index].memref.size,
+ op_params->local_tmpbuf[index].temp_buffer,
+ operation->params[index].memref.size,
+ call_params->dev->kernel_api)) {
+ tloge("copy tempbuf failed\n");
+ return -ENOMEM;
+ }
+ return 0;
+}
+
+static int update_for_ref_mem(const struct tc_call_params *call_params,
+ struct tc_op_params *op_params, unsigned int index)
+{
+ union tc_ns_client_param *client_param = NULL;
+ uint32_t buffer_size;
+ unsigned int orig_size = 0;
+ struct tc_ns_operation *operation = &op_params->mb_pack->operation;
+
+ if (index >= TEE_PARAM_NUM) {
+ tloge("index is invalid\n");
+ return -EFAULT;
+ }
+
+ /* update size */
+ buffer_size = operation->params[index].memref.size;
+ client_param = &(call_params->context->params[index]);
+
+ if (read_from_client(&orig_size,
+ sizeof(orig_size),
+ (uint32_t __user *)(uintptr_t)client_param->memref.size_addr,
+ sizeof(orig_size), call_params->dev->kernel_api)) {
+ tloge("copy orig memref.size_addr failed\n");
+ return -EFAULT;
+ }
+
+ if (write_to_client((void *)(uintptr_t)client_param->memref.size_addr,
+ sizeof(buffer_size),
+ &buffer_size, sizeof(buffer_size),
+ call_params->dev->kernel_api)) {
+ tloge("copy buf size failed\n");
+ return -EFAULT;
+ }
+
+ /* reserved memory no need to copy */
+ if (operation->sharemem[index]->mem_type == RESERVED_TYPE)
+ return 0;
+ /* copy from mb_buffer to sharemem */
+ if (operation->mb_buffer[index] && orig_size >= buffer_size) {
+ void *buffer_addr =
+ (void *)(uintptr_t)((uintptr_t)
+ operation->sharemem[index]->kernel_addr +
+ client_param->memref.offset);
+ if (memcpy_s(buffer_addr,
+ operation->sharemem[index]->len -
+ client_param->memref.offset,
+ operation->mb_buffer[index], buffer_size)) {
+ tloge("copy to sharemem failed\n");
+ return -EFAULT;
+ }
+ }
+ return 0;
+}
+
+static int update_for_value(const struct tc_call_params *call_params,
+ struct tc_op_params *op_params, unsigned int index)
+{
+ union tc_ns_client_param *client_param = NULL;
+ struct tc_ns_operation *operation = &op_params->mb_pack->operation;
+
+ if (index >= TEE_PARAM_NUM) {
+ tloge("index is invalid\n");
+ return -EFAULT;
+ }
+ client_param = &(call_params->context->params[index]);
+ if (write_to_client((void *)(uintptr_t)client_param->value.a_addr,
+ sizeof(operation->params[index].value.a),
+ &operation->params[index].value.a,
+ sizeof(operation->params[index].value.a),
+ call_params->dev->kernel_api)) {
+ tloge("inc copy value.a_addr failed\n");
+ return -EFAULT;
+ }
+ if (write_to_client((void *)(uintptr_t)client_param->value.b_addr,
+ sizeof(operation->params[index].value.b),
+ &operation->params[index].value.b,
+ sizeof(operation->params[index].value.b),
+ call_params->dev->kernel_api)) {
+ tloge("inc copy value.b_addr failed\n");
+ return -EFAULT;
+ }
+ return 0;
+}
+
+static int update_client_operation(const struct tc_call_params *call_params,
+ struct tc_op_params *op_params, bool is_complete)
+{
+ int ret = 0;
+ uint32_t param_type;
+ uint32_t index;
+
+ if (!op_params->op_inited)
+ return 0;
+
+ /* if paramTypes is NULL, no need to update */
+ if (!call_params->context->param_types)
+ return 0;
+
+ for (index = 0; index < TEE_PARAM_NUM; index++) {
+ param_type = teec_param_type_get(
+ call_params->context->param_types, index);
+ if (teec_tmpmem_type(param_type, OUTPUT))
+ ret = update_tmp_mem(call_params, op_params,
+ index, is_complete);
+ else if (teec_memref_type(param_type, OUTPUT))
+ ret = update_for_ref_mem(call_params,
+ op_params, index);
+ else if (is_complete && teec_value_type(param_type, OUTPUT))
+ ret = update_for_value(call_params, op_params, index);
+ else
+ tlogd("param_type:%u don't need to update\n", param_type);
+ if (ret)
+ break;
+ }
+ return ret;
+}
+
+#ifdef CONFIG_NOCOPY_SHAREDMEM
+static void release_page(void *buf)
+{
+ uint32_t i;
+ uint64_t *phys_addr = NULL;
+ struct pagelist_info *page_info = NULL;
+ struct page *page = NULL;
+
+ page_info = buf;
+ phys_addr = (uint64_t *)buf + (sizeof(*page_info) / sizeof(uint64_t));
+ for (i = 0; i < page_info->page_num; i++) {
+ page = (uintptr_t)phys_to_page(phys_addr[i]);
+ if (page == NULL)
+ continue;
+ put_page(page);
+ }
+}
+#endif
+
+static void free_operation(const struct tc_call_params *call_params,
+ struct tc_op_params *op_params)
+{
+ uint32_t param_type;
+ uint32_t index;
+ void *temp_buf = NULL;
+ struct tc_ns_temp_buf *local_tmpbuf = op_params->local_tmpbuf;
+ struct tc_ns_operation *operation = &op_params->mb_pack->operation;
+
+ for (index = 0; index < TEE_PARAM_NUM; index++) {
+ param_type = teec_param_type_get(
+ call_params->context->param_types, index);
+ if (is_tmp_mem(param_type)) {
+ /* free temp buffer */
+ temp_buf = local_tmpbuf[index].temp_buffer;
+ tlogd("free temp buf, i = %u\n", index);
+ if (virt_addr_valid(
+ (unsigned long)(uintptr_t)temp_buf) &&
+ !ZERO_OR_NULL_PTR(
+ (unsigned long)(uintptr_t)temp_buf)) {
+ mailbox_free(temp_buf);
+ temp_buf = NULL;
+ }
+ } else if (is_ref_mem(param_type)) {
+ struct tc_ns_shared_mem *shm = operation->sharemem[index];
+ if (shm != NULL && shm->mem_type == RESERVED_TYPE) {
+ put_sharemem_struct(operation->sharemem[index]);
+ continue;
+ }
+ put_sharemem_struct(operation->sharemem[index]);
+ if (operation->mb_buffer[index])
+ mailbox_free(operation->mb_buffer[index]);
+ } else if (param_type == TEEC_ION_SGLIST_INPUT) {
+ temp_buf = local_tmpbuf[index].temp_buffer;
+ tlogd("free ion sglist buf, i = %u\n", index);
+ if (virt_addr_valid((uint64_t)(uintptr_t)temp_buf) &&
+ !ZERO_OR_NULL_PTR(
+ (unsigned long)(uintptr_t)temp_buf)) {
+ mailbox_free(temp_buf);
+ temp_buf = NULL;
+ }
+ } else if (param_type == TEEC_MEMREF_SHARED_INOUT) {
+#ifdef CONFIG_NOCOPY_SHAREDMEM
+ temp_buf = local_tmpbuf[index].temp_buffer;
+ if (temp_buf != NULL) {
+ release_page(temp_buf);
+ mailbox_free(temp_buf);
+ }
+#endif
+ }
+ }
+}
+
+static int is_clicall_params_vaild(const struct tc_call_params *call_params)
+{
+ if (!call_params) {
+ tloge("call param is null");
+ return false;
+ }
+
+ if (!call_params->dev) {
+ tloge("dev file is null");
+ return false;
+ }
+
+ if (!call_params->context) {
+ tloge("client context is null");
+ return false;
+ }
+
+ return true;
+}
+
+static int alloc_for_client_call(struct tc_op_params *op_params)
+{
+ op_params->smc_cmd = kzalloc(sizeof(*(op_params->smc_cmd)),
+ GFP_KERNEL);
+ if (ZERO_OR_NULL_PTR((unsigned long)(uintptr_t)(op_params->smc_cmd))) {
+ tloge("smc cmd malloc failed\n");
+ return -ENOMEM;
+ }
+
+ op_params->mb_pack = mailbox_alloc_cmd_pack();
+ if (!op_params->mb_pack) {
+ kfree(op_params->smc_cmd);
+ op_params->smc_cmd = NULL;
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static int init_smc_cmd(const struct tc_call_params *call_params,
+ struct tc_op_params *op_params)
+{
+ struct tc_ns_smc_cmd *smc_cmd = op_params->smc_cmd;
+ struct tc_ns_client_context *context = call_params->context;
+ struct tc_ns_operation *operation = &op_params->mb_pack->operation;
+ bool global = call_params->flags & TC_CALL_GLOBAL;
+
+ smc_cmd->cmd_type = global ? CMD_TYPE_GLOBAL : CMD_TYPE_TA;
+ if (memcpy_s(smc_cmd->uuid, sizeof(smc_cmd->uuid),
+ context->uuid, UUID_LEN)) {
+ tloge("memcpy uuid error\n");
+ return -EFAULT;
+ }
+ smc_cmd->cmd_id = context->cmd_id;
+ smc_cmd->dev_file_id = call_params->dev->dev_file_id;
+ smc_cmd->context_id = context->session_id;
+ smc_cmd->err_origin = context->returns.origin;
+ smc_cmd->started = context->started;
+ smc_cmd->ca_pid = current->pid;
+ smc_cmd->pid = current->tgid;
+
+ tlogv("current uid is %u\n", smc_cmd->uid);
+ if (context->param_types) {
+ smc_cmd->operation_phys =
+ virt_to_phys(operation);
+ smc_cmd->operation_h_phys =
+ (uint64_t)virt_to_phys(operation) >> ADDR_TRANS_NUM;
+ } else {
+ smc_cmd->operation_phys = 0;
+ smc_cmd->operation_h_phys = 0;
+ }
+ smc_cmd->login_method = context->login.method;
+
+ return 0;
+}
+
+static bool need_check_login(const struct tc_call_params *call_params,
+ struct tc_op_params *op_params)
+{
+ if (call_params->dev->pub_key_len == sizeof(uint32_t) &&
+ op_params->smc_cmd->cmd_id == GLOBAL_CMD_ID_OPEN_SESSION &&
+ current->mm && (call_params->flags & TC_CALL_GLOBAL))
+ return true;
+
+ return false;
+}
+
+static int check_login_for_encrypt(const struct tc_call_params *call_params,
+ struct tc_op_params *op_params)
+{
+ struct tc_ns_session *sess = call_params->sess;
+ struct tc_ns_smc_cmd *smc_cmd = op_params->smc_cmd;
+ struct mb_cmd_pack *mb_pack = op_params->mb_pack;
+
+ if (need_check_login(call_params, op_params) && sess) {
+ if (memcpy_s(mb_pack->login_data, sizeof(mb_pack->login_data),
+ sess->auth_hash_buf,
+ sizeof(sess->auth_hash_buf))) {
+ tloge("copy login data failed\n");
+ return -EFAULT;
+ }
+ smc_cmd->login_data_phy = virt_to_phys(mb_pack->login_data);
+ smc_cmd->login_data_h_addr =
+ (uint64_t)virt_to_phys(mb_pack->login_data) >> ADDR_TRANS_NUM;
+ smc_cmd->login_data_len = MAX_SHA_256_SZ * (NUM_OF_SO + 1);
+ } else {
+ smc_cmd->login_data_phy = 0;
+ smc_cmd->login_data_h_addr = 0;
+ smc_cmd->login_data_len = 0;
+ }
+ return 0;
+}
+
+static uint32_t get_uid_for_cmd(void)
+{
+ kuid_t kuid;
+
+ kuid = current_uid();
+ return kuid.val;
+}
+
+static void reset_session_id(const struct tc_call_params *call_params,
+ struct tc_op_params *op_params, int tee_ret)
+{
+ bool need_reset = false;
+
+ call_params->context->session_id = op_params->smc_cmd->context_id;
+ /*
+ * if tee_ret error except TEEC_PENDING,
+ * but context_id is seted,need to reset to 0
+ */
+ need_reset = ((call_params->flags & TC_CALL_GLOBAL) &&
+ call_params->context->cmd_id == GLOBAL_CMD_ID_OPEN_SESSION &&
+ tee_ret && tee_ret != TEEC_PENDING);
+ if (need_reset)
+ call_params->context->session_id = 0;
+ return;
+}
+
+static void pend_ca_thread(struct tc_ns_session *session,
+ const struct tc_ns_smc_cmd *smc_cmd)
+{
+ struct tc_wait_data *wq = NULL;
+
+ if (session)
+ wq = &session->wait_data;
+
+ if (wq) {
+ tlogv("before wait event\n");
+ /*
+ * use wait_event instead of wait_event_interruptible so
+ * that ap suspend will not wake up the TEE wait call
+ */
+ wait_event(wq->send_cmd_wq, wq->send_wait_flag);
+ wq->send_wait_flag = 0;
+ }
+ tlogv("operation start is :%d\n", smc_cmd->started);
+ return;
+}
+
+
+static void release_tc_call_resource(const struct tc_call_params *call_params,
+ struct tc_op_params *op_params, int tee_ret)
+{
+ /* kfree(NULL) is safe and this check is probably not required */
+ call_params->context->returns.code = tee_ret;
+ call_params->context->returns.origin = op_params->smc_cmd->err_origin;
+
+ if (op_params->op_inited)
+ free_operation(call_params, op_params);
+
+ kfree(op_params->smc_cmd);
+ mailbox_free(op_params->mb_pack);
+}
+
+static int config_smc_cmd_context(const struct tc_call_params *call_params,
+ struct tc_op_params *op_params)
+{
+ int ret;
+
+ ret = init_smc_cmd(call_params, op_params);
+ if (ret)
+ return ret;
+
+ ret = check_login_for_encrypt(call_params, op_params);
+ if (ret)
+ return ret;
+
+ return ret;
+}
+
+static int handle_ta_pending(const struct tc_call_params *call_params,
+ struct tc_op_params *op_params, int *tee_ret)
+{
+ if (*tee_ret != TEEC_PENDING)
+ return 0;
+
+ while (*tee_ret == TEEC_PENDING) {
+ pend_ca_thread(call_params->sess, op_params->smc_cmd);
+ *tee_ret = tc_ns_smc_with_no_nr(op_params->smc_cmd);
+ }
+
+ return 0;
+}
+
+static int post_proc_smc_return(const struct tc_call_params *call_params,
+ struct tc_op_params *op_params, int tee_ret)
+{
+ int ret;
+
+ if (tee_ret) {
+ tloge("smc call ret 0x%x, cmd ret val 0x%x, origin %u\n", tee_ret,
+ op_params->smc_cmd->ret_val, op_params->smc_cmd->err_origin);
+ /* same as libteec_vendor, err from TEE, set ret positive */
+ ret = EFAULT;
+ if (tee_ret == TEEC_CLIENT_INTR)
+ ret = -ERESTARTSYS;
+
+ if (tee_ret == TEEC_ERROR_SHORT_BUFFER)
+ (void)update_client_operation(call_params, op_params, false);
+ } else {
+ tz_log_write();
+ ret = update_client_operation(call_params, op_params, true);
+ }
+
+ return ret;
+}
+
+int tc_client_call(const struct tc_call_params *call_params)
+{
+ int ret;
+ int tee_ret = 0;
+ struct tc_op_params op_params = { NULL, NULL, {{0}}, {0}, false };
+
+ if (!is_clicall_params_vaild(call_params))
+ return -EINVAL;
+
+ if (alloc_for_client_call(&op_params))
+ return -ENOMEM;
+
+ op_params.smc_cmd->err_origin = TEEC_ORIGIN_COMMS;
+ op_params.smc_cmd->uid = get_uid_for_cmd();
+ if (call_params->context->param_types) {
+ ret = alloc_operation(call_params, &op_params);
+ if (ret)
+ goto free_src;
+ }
+
+ ret = config_smc_cmd_context(call_params, &op_params);
+ if (ret)
+ goto free_src;
+
+ tee_ret = tc_ns_smc(op_params.smc_cmd);
+
+ reset_session_id(call_params, &op_params, tee_ret);
+
+ ret = handle_ta_pending(call_params, &op_params, &tee_ret);
+ if (ret)
+ goto free_src;
+
+ ret = post_proc_smc_return(call_params, &op_params, tee_ret);
+
+free_src:
+ if (ret < 0) /* if ret > 0, means err from TEE */
+ op_params.smc_cmd->err_origin = TEEC_ORIGIN_COMMS;
+ release_tc_call_resource(call_params, &op_params, tee_ret);
+ return ret;
+}
diff --git a/linux/core/gp_ops.h b/linux/core/gp_ops.h
new file mode 100644
index 0000000..32a7401
--- /dev/null
+++ b/linux/core/gp_ops.h
@@ -0,0 +1,56 @@
+/*
+ * gp_op.h
+ *
+ * function declaration for alloc global operation and pass params to TEE.
+ *
+ * Copyright (C) 2022 Huawei Technologies Co., Ltd.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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 GP_OPS_H
+#define GP_OPS_H
+#include "tc_ns_client.h"
+#include "teek_ns_client.h"
+
+struct tc_call_params {
+ struct tc_ns_dev_file *dev;
+ struct tc_ns_client_context *context;
+ struct tc_ns_session *sess;
+ uint8_t flags;
+};
+
+struct tc_op_params {
+ struct mb_cmd_pack *mb_pack;
+ struct tc_ns_smc_cmd *smc_cmd;
+ struct tc_ns_temp_buf local_tmpbuf[TEE_PARAM_NUM];
+ uint32_t trans_paramtype[TEE_PARAM_NUM];
+ bool op_inited;
+};
+
+struct pagelist_info {
+ uint64_t page_num;
+ uint64_t page_size;
+ uint64_t sharedmem_offset;
+ uint64_t sharedmem_size;
+};
+
+int write_to_client(void __user *dest, size_t dest_size,
+ const void *src, size_t size, uint8_t kernel_api);
+int read_from_client(void *dest, size_t dest_size,
+ const void __user *src, size_t size, uint8_t kernel_api);
+bool tc_user_param_valid(struct tc_ns_client_context *client_context,
+ unsigned int index);
+int tc_client_call(const struct tc_call_params *call_params);
+bool is_tmp_mem(uint32_t param_type);
+bool is_ref_mem(uint32_t param_type);
+bool is_val_param(uint32_t param_type);
+bool is_ion_param(uint32_t param_type);
+
+#endif
diff --git a/linux/core/mailbox_mempool.c b/linux/core/mailbox_mempool.c
new file mode 100644
index 0000000..12974e1
--- /dev/null
+++ b/linux/core/mailbox_mempool.c
@@ -0,0 +1,625 @@
+/*
+ * mailbox_mempool.c
+ *
+ * mailbox memory managing for sharing memory with TEE.
+ *
+ * Copyright (C) 2022 Huawei Technologies Co., Ltd.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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 "mailbox_mempool.h"
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#if (KERNEL_VERSION(4, 14, 0) <= LINUX_VERSION_CODE)
+#include
+#endif
+#include "teek_client_constants.h"
+#include "tc_ns_log.h"
+#include "smc_smp.h"
+#include "ko_adapt.h"
+
+#define MAILBOX_PAGE_MAX (MAILBOX_POOL_SIZE >> PAGE_SHIFT)
+static int g_max_oder;
+
+#define OPT_MODE 0660U
+#define STATE_MODE 0440U
+
+struct mb_page_t {
+ struct list_head node;
+ struct page *page;
+ int order;
+ unsigned int count; /* whether be used */
+};
+
+struct mb_free_area_t {
+ struct list_head page_list;
+ int order;
+};
+
+struct mb_zone_t {
+ struct page *all_pages;
+ struct mb_page_t pages[MAILBOX_PAGE_MAX];
+ struct mb_free_area_t free_areas[0];
+};
+
+static struct mb_zone_t *g_m_zone;
+static struct mutex g_mb_lock;
+
+static void mailbox_show_status(void)
+{
+ unsigned int i;
+ struct mb_page_t *pos = NULL;
+ struct list_head *head = NULL;
+ unsigned int used = 0;
+
+ if (!g_m_zone) {
+ tloge("zone struct is NULL\n");
+ return;
+ }
+
+ tloge("########################################\n");
+ mutex_lock(&g_mb_lock);
+ for (i = 0; i < MAILBOX_PAGE_MAX; i++) {
+ if (g_m_zone->pages[i].count) {
+ tloge("page[%02d], order=%02d, count=%d\n",
+ i, g_m_zone->pages[i].order,
+ g_m_zone->pages[i].count);
+ used += (1 << (uint32_t)g_m_zone->pages[i].order);
+ }
+ }
+ tloge("total usage:%u/%u\n", used, MAILBOX_PAGE_MAX);
+ tloge("----------------------------------------\n");
+
+ for (i = 0; i < (unsigned int)g_max_oder; i++) {
+ head = &g_m_zone->free_areas[i].page_list;
+ if (list_empty(head)) {
+ tloge("order[%02d] is empty\n", i);
+ } else {
+ list_for_each_entry(pos, head, node)
+ tloge("order[%02d]\n", i);
+ }
+ }
+ mutex_unlock(&g_mb_lock);
+
+ tloge("########################################\n");
+}
+
+#define MB_SHOW_LINE 64
+#define BITS_OF_BYTE 8
+static void mailbox_show_details(void)
+{
+ unsigned int i;
+ unsigned int used = 0;
+ unsigned int left = 0;
+ unsigned int order = 0;
+
+ if (!g_m_zone) {
+ tloge("zone struct is NULL\n");
+ return;
+ }
+
+ tloge("----- show mailbox details -----");
+ mutex_lock(&g_mb_lock);
+ for (i = 0; i < MAILBOX_PAGE_MAX; i++) {
+ if (i % MB_SHOW_LINE == 0) {
+ tloge("\n");
+ tloge("%04d-%04d:", i, i + MB_SHOW_LINE);
+ }
+
+ if (g_m_zone->pages[i].count) {
+ left = 1 << (uint32_t)g_m_zone->pages[i].order;
+ order = g_m_zone->pages[i].order;
+ used += (1 << (uint32_t)g_m_zone->pages[i].order);
+ }
+
+ if (left) {
+ left--;
+ tloge("%01d", order);
+ } else {
+ tloge("X");
+ }
+
+ if (i > 1 && (i + 1) % (MB_SHOW_LINE / BITS_OF_BYTE) == 0)
+ tloge(" ");
+ }
+ tloge("total usage:%u/%u\n", used, MAILBOX_PAGE_MAX);
+ mutex_unlock(&g_mb_lock);
+}
+
+void *mailbox_alloc(size_t size, unsigned int flag)
+{
+ unsigned int i;
+ struct mb_page_t *pos = (struct mb_page_t *)NULL;
+ struct list_head *head = NULL;
+ int order = get_order(ALIGN(size, SZ_4K));
+ void *addr = NULL;
+
+ if (!size || !g_m_zone) {
+ tlogw("alloc 0 size mailbox or zone struct is NULL\n");
+ return NULL;
+ }
+
+ if (order > g_max_oder || order < 0) {
+ tloge("invalid order %d\n", order);
+ return NULL;
+ }
+
+ mutex_lock(&g_mb_lock);
+ for (i = (unsigned int)order; i <= (unsigned int)g_max_oder; i++) {
+ unsigned int j;
+
+ head = &g_m_zone->free_areas[i].page_list;
+ if (list_empty(head))
+ continue;
+
+ pos = list_first_entry(head, struct mb_page_t, node);
+
+ pos->count = 1;
+ pos->order = order;
+
+ /* split and add free list */
+ for (j = order; j < i; j++) {
+ struct mb_page_t *new_page = NULL;
+
+ new_page = pos + (1 << j);
+ new_page->count = 0;
+ new_page->order = j;
+ list_add_tail(&new_page->node,
+ &g_m_zone->free_areas[j].page_list);
+ }
+ list_del(&pos->node);
+ addr = page_address(pos->page);
+ break;
+ }
+ mutex_unlock(&g_mb_lock);
+
+ if (addr && (flag & MB_FLAG_ZERO)) {
+ if (memset_s(addr, ALIGN(size, SZ_4K),
+ 0, ALIGN(size, SZ_4K))) {
+ tloge("clean mailbox failed\n");
+ mailbox_free(addr);
+ return NULL;
+ }
+ }
+ return addr;
+}
+
+static void add_max_order_block(unsigned int idex)
+{
+ struct mb_page_t *self = NULL;
+
+ if (idex != g_max_oder || !g_m_zone)
+ return;
+
+ /*
+ * when idex equal max order, no one use mailbox mem,
+ * we need to hang all pages in the last free area page list
+ */
+ self = &g_m_zone->pages[0];
+ list_add_tail(&self->node,
+ &g_m_zone->free_areas[g_max_oder].page_list);
+}
+
+static bool is_ptr_valid(struct page *page)
+{
+ if (!g_m_zone)
+ return false;
+
+ if (page < g_m_zone->all_pages ||
+ page >= (g_m_zone->all_pages + MAILBOX_PAGE_MAX)) {
+ tloge("invalid ptr to free in mailbox\n");
+ return false;
+ }
+
+ return true;
+}
+
+void mailbox_free(const void *ptr)
+{
+ unsigned int i;
+ struct page *page = NULL;
+ struct mb_page_t *self = NULL;
+ struct mb_page_t *buddy = NULL;
+ unsigned int self_idx;
+ unsigned int buddy_idx;
+
+ if (!ptr || !g_m_zone) {
+ tloge("invalid ptr\n");
+ return;
+ }
+
+ page = virt_to_page((uint64_t)(uintptr_t)ptr);
+ if (!is_ptr_valid(page))
+ return;
+
+ mutex_lock(&g_mb_lock);
+ self_idx = page - g_m_zone->all_pages;
+ self = &g_m_zone->pages[self_idx];
+ if (!self->count) {
+ tloge("already freed in mailbox\n");
+ mutex_unlock(&g_mb_lock);
+ return;
+ }
+
+ for (i = (unsigned int)self->order; i <
+ (unsigned int)g_max_oder; i++) {
+ self_idx = page - g_m_zone->all_pages;
+ buddy_idx = self_idx ^ (uint32_t)(1 << i);
+ self = &g_m_zone->pages[self_idx];
+ buddy = &g_m_zone->pages[buddy_idx];
+ self->count = 0;
+ /* is buddy free */
+ if ((unsigned int)buddy->order == i && buddy->count == 0) {
+ /* release buddy */
+ list_del(&buddy->node);
+ /* combine self and buddy */
+ if (self_idx > buddy_idx) {
+ page = buddy->page;
+ buddy->order = (int)i + 1;
+ self->order = -1;
+ } else {
+ self->order = (int)i + 1;
+ buddy->order = -1;
+ }
+ } else {
+ /* release self */
+ list_add_tail(&self->node,
+ &g_m_zone->free_areas[i].page_list);
+ mutex_unlock(&g_mb_lock);
+ return;
+ }
+ }
+
+ add_max_order_block(i);
+ mutex_unlock(&g_mb_lock);
+}
+
+struct mb_cmd_pack *mailbox_alloc_cmd_pack(void)
+{
+ void *pack = mailbox_alloc(SZ_4K, MB_FLAG_ZERO);
+
+ if (!pack)
+ tloge("alloc mb cmd pack failed\n");
+
+ return (struct mb_cmd_pack *)pack;
+}
+
+void *mailbox_copy_alloc(const void *src, size_t size)
+{
+ void *mb_ptr = NULL;
+
+ if (!src || !size) {
+ tloge("invali src to alloc mailbox copy\n");
+ return NULL;
+ }
+
+ mb_ptr = mailbox_alloc(size, 0);
+ if (!mb_ptr) {
+ tloge("alloc size %zu mailbox failed\n", size);
+ return NULL;
+ }
+
+ if (memcpy_s(mb_ptr, size, src, size)) {
+ tloge("memcpy to mailbox failed\n");
+ mailbox_free(mb_ptr);
+ return NULL;
+ }
+
+ return mb_ptr;
+}
+
+struct mb_dbg_entry {
+ struct list_head node;
+ unsigned int idx;
+ void *ptr;
+};
+
+static LIST_HEAD(mb_dbg_list);
+static DEFINE_MUTEX(mb_dbg_lock);
+static unsigned int g_mb_dbg_entry_count = 1;
+static unsigned int g_mb_dbg_last_res; /* only cache 1 opt result */
+static struct dentry *g_mb_dbg_dentry;
+
+static unsigned int mb_dbg_add_entry(void *ptr)
+{
+ struct mb_dbg_entry *new_entry = NULL;
+
+ new_entry = kmalloc(sizeof(*new_entry), GFP_KERNEL);
+ if (ZERO_OR_NULL_PTR((unsigned long)(uintptr_t)new_entry)) {
+ tloge("alloc entry failed\n");
+ return 0;
+ }
+
+ INIT_LIST_HEAD(&new_entry->node);
+ new_entry->ptr = ptr;
+ mutex_lock(&mb_dbg_lock);
+ new_entry->idx = g_mb_dbg_entry_count;
+
+ if ((g_mb_dbg_entry_count++) == 0)
+ g_mb_dbg_entry_count++;
+ list_add_tail(&new_entry->node, &mb_dbg_list);
+ mutex_unlock(&mb_dbg_lock);
+
+ return new_entry->idx;
+}
+
+static void mb_dbg_remove_entry(unsigned int idx)
+{
+ struct mb_dbg_entry *pos = NULL;
+ struct mb_dbg_entry *temp = NULL;
+
+ mutex_lock(&mb_dbg_lock);
+ list_for_each_entry_safe(pos, temp, &mb_dbg_list, node) {
+ if (pos->idx == idx) {
+ mailbox_free(pos->ptr);
+ list_del(&pos->node);
+ kfree(pos);
+ mutex_unlock(&mb_dbg_lock);
+ return;
+ }
+ }
+ mutex_unlock(&mb_dbg_lock);
+
+ tloge("entry %u invalid\n", idx);
+}
+
+static void mb_dbg_reset(void)
+{
+ struct mb_dbg_entry *pos = NULL;
+ struct mb_dbg_entry *tmp = NULL;
+
+ mutex_lock(&mb_dbg_lock);
+ list_for_each_entry_safe(pos, tmp, &mb_dbg_list, node) {
+ mailbox_free(pos->ptr);
+ list_del(&pos->node);
+ kfree(pos);
+ }
+ g_mb_dbg_entry_count = 0;
+ mutex_unlock(&mb_dbg_lock);
+}
+
+#define MB_WRITE_SIZE 64
+
+static bool is_opt_write_param_valid(struct file *filp,
+ const char __user *ubuf, size_t cnt, loff_t *ppos)
+{
+ if (!filp || !ppos || !ubuf)
+ return false;
+
+ if (cnt >= MB_WRITE_SIZE || !cnt)
+ return false;
+
+ return true;
+}
+
+static void alloc_dbg_entry(unsigned int alloc_size)
+{
+ unsigned int idx;
+ void *ptr = NULL;
+
+ ptr = mailbox_alloc(alloc_size, 0);
+ if (!ptr) {
+ tloge("alloc order=%u in mailbox failed\n", alloc_size);
+ return;
+ }
+
+ idx = mb_dbg_add_entry(ptr);
+ if (!idx)
+ mailbox_free(ptr);
+ g_mb_dbg_last_res = idx;
+}
+
+static ssize_t mb_dbg_opt_write(struct file *filp,
+ const char __user *ubuf, size_t cnt, loff_t *ppos)
+{
+ char buf[MB_WRITE_SIZE] = {0};
+ char *cmd = NULL;
+ char *value = NULL;
+ unsigned int alloc_size;
+ unsigned int free_idx;
+
+ if (!is_opt_write_param_valid(filp, ubuf, cnt, ppos))
+ return -EINVAL;
+
+ if (copy_from_user(buf, ubuf, cnt))
+ return -EFAULT;
+
+ buf[cnt] = 0;
+ value = buf;
+ if (!strncmp(value, "reset", strlen("reset"))) {
+ tlogi("mb dbg reset\n");
+ mb_dbg_reset();
+ return cnt;
+ }
+
+ cmd = strsep(&value, ":");
+ if (!cmd || !value) {
+ tloge("no valid cmd or value for mb dbg\n");
+ return -EFAULT;
+ }
+
+ if (!strncmp(cmd, "alloc", strlen("alloc"))) {
+ if (kstrtou32(value, 10, &alloc_size) == 0)
+ alloc_dbg_entry(alloc_size);
+ else
+ tloge("invalid value format for mb dbg\n");
+ } else if (!strncmp(cmd, "free", strlen("free"))) {
+ if (kstrtou32(value, 10, &free_idx) == 0)
+ mb_dbg_remove_entry(free_idx);
+ else
+ tloge("invalid value format for mb dbg\n");
+ } else {
+ tloge("invalid format for mb dbg\n");
+ }
+
+ return cnt;
+}
+
+static ssize_t mb_dbg_opt_read(struct file *filp, char __user *ubuf,
+ size_t cnt, loff_t *ppos)
+{
+ char buf[16] = {0};
+ ssize_t ret;
+
+ (void)(filp);
+
+ ret = snprintf_s(buf, sizeof(buf), 15, "%u\n", g_mb_dbg_last_res);
+ if (ret < 0) {
+ tloge("snprintf idx failed\n");
+ return -EINVAL;
+ }
+
+ return simple_read_from_buffer(ubuf, cnt, ppos, buf, ret);
+}
+
+static const struct file_operations g_mb_dbg_opt_fops = {
+ .owner = THIS_MODULE,
+ .read = mb_dbg_opt_read,
+ .write = mb_dbg_opt_write,
+};
+
+static ssize_t mb_dbg_state_read(struct file *filp, char __user *ubuf,
+ size_t cnt, loff_t *ppos)
+{
+ (void)(filp);
+ (void)(ubuf);
+ (void)(ppos);
+ mailbox_show_status();
+ mailbox_show_details();
+ return 0;
+}
+
+static const struct file_operations g_mb_dbg_state_fops = {
+ .owner = THIS_MODULE,
+ .read = mb_dbg_state_read,
+};
+
+static int mailbox_register(const void *mb_pool, unsigned int size)
+{
+ struct tc_ns_operation *operation = NULL;
+ struct tc_ns_smc_cmd *smc_cmd = NULL;
+ int ret = 0;
+
+ smc_cmd = kzalloc(sizeof(*smc_cmd), GFP_KERNEL);
+ if (ZERO_OR_NULL_PTR((unsigned long)(uintptr_t)smc_cmd)) {
+ tloge("alloc smc_cmd failed\n");
+ return -EIO;
+ }
+
+ operation = kzalloc(sizeof(*operation), GFP_KERNEL);
+ if (ZERO_OR_NULL_PTR((unsigned long)(uintptr_t)operation)) {
+ tloge("alloc operation failed\n");
+ ret = -EIO;
+ goto free_smc_cmd;
+ }
+
+ operation->paramtypes = TEE_PARAM_TYPE_VALUE_INPUT |
+ (TEE_PARAM_TYPE_VALUE_INPUT << TEE_PARAM_NUM);
+ operation->params[0].value.a = virt_to_phys(mb_pool);
+ operation->params[0].value.b =
+ (uint64_t)virt_to_phys(mb_pool) >> ADDR_TRANS_NUM;
+ operation->params[1].value.a = size;
+
+ smc_cmd->cmd_type = CMD_TYPE_GLOBAL;
+ smc_cmd->cmd_id = GLOBAL_CMD_ID_REGISTER_MAILBOX;
+ smc_cmd->operation_phys = virt_to_phys(operation);
+ smc_cmd->operation_h_phys =
+ (uint64_t)virt_to_phys(operation) >> ADDR_TRANS_NUM;
+
+ if (tc_ns_smc(smc_cmd)) {
+ tloge("resigter mailbox failed\n");
+ ret = -EIO;
+ }
+
+ kfree(operation);
+ operation = NULL;
+free_smc_cmd:
+ kfree(smc_cmd);
+ smc_cmd = NULL;
+ return ret;
+}
+
+int mailbox_mempool_init(void)
+{
+ int i;
+ struct mb_page_t *mb_page = NULL;
+ struct mb_free_area_t *area = NULL;
+ struct page *all_pages = NULL;
+ size_t zone_len;
+
+ g_max_oder = get_order(MAILBOX_POOL_SIZE);
+ tloge("in this RE, mailbox max order is: %d\n", g_max_oder);
+
+ /* zone len is fixed, will not overflow */
+ zone_len = sizeof(*area) * (g_max_oder + 1) + sizeof(*g_m_zone);
+ g_m_zone = kzalloc(zone_len, GFP_KERNEL);
+ if (ZERO_OR_NULL_PTR((unsigned long)(uintptr_t)g_m_zone)) {
+ tloge("fail to alloc zone struct\n");
+ return -ENOMEM;
+ }
+
+ all_pages = koadpt_alloc_pages(GFP_KERNEL, g_max_oder);
+ if (!all_pages) {
+ tloge("fail to alloc mailbox mempool\n");
+ kfree(g_m_zone);
+ g_m_zone = NULL;
+ return -ENOMEM;
+ }
+
+ if (mailbox_register(page_address(all_pages), MAILBOX_POOL_SIZE)) {
+ tloge("register mailbox failed\n");
+ __free_pages(all_pages, g_max_oder);
+ kfree(g_m_zone);
+ g_m_zone = NULL;
+ return -EIO;
+ }
+
+ for (i = 0; i < MAILBOX_PAGE_MAX; i++) {
+ g_m_zone->pages[i].order = -1;
+ g_m_zone->pages[i].count = 0;
+ g_m_zone->pages[i].page = &all_pages[i];
+ }
+
+ g_m_zone->pages[0].order = g_max_oder;
+
+ for (i = 0; i <= g_max_oder; i++) {
+ area = &g_m_zone->free_areas[i];
+ INIT_LIST_HEAD(&area->page_list);
+ area->order = i;
+ }
+
+ mb_page = &g_m_zone->pages[0];
+ list_add_tail(&mb_page->node, &area->page_list);
+ g_m_zone->all_pages = all_pages;
+ mutex_init(&g_mb_lock);
+ g_mb_dbg_dentry = debugfs_create_dir("tz_mailbox", NULL);
+ debugfs_create_file("opt", OPT_MODE, g_mb_dbg_dentry, NULL,
+ &g_mb_dbg_opt_fops);
+ debugfs_create_file("state", STATE_MODE, g_mb_dbg_dentry, NULL,
+ &g_mb_dbg_state_fops);
+ return 0;
+}
+
+void mailbox_mempool_destroy(void)
+{
+ __free_pages(g_m_zone->all_pages, g_max_oder);
+ g_m_zone->all_pages = NULL;
+ kfree(g_m_zone);
+ g_m_zone = NULL;
+}
diff --git a/linux/core/mailbox_mempool.h b/linux/core/mailbox_mempool.h
new file mode 100644
index 0000000..bc89975
--- /dev/null
+++ b/linux/core/mailbox_mempool.h
@@ -0,0 +1,36 @@
+/*
+ * mailbox_mempool.h
+ *
+ * mailbox memory managing for sharing memory with TEE.
+ *
+ * Copyright (C) 2022 Huawei Technologies Co., Ltd.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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 MAILBOX_MEMPOOOL_H
+#define MAILBOX_MEMPOOOL_H
+
+#include
+#include
+
+#define MAILBOX_POOL_SIZE SZ_4M
+
+/* alloc options */
+#define MB_FLAG_ZERO 0x1 /* set 0 after alloc page */
+#define GLOBAL_UUID_LEN 17 /* first char represent global cmd */
+
+void *mailbox_alloc(size_t size, unsigned int flag);
+void mailbox_free(const void *ptr);
+int mailbox_mempool_init(void);
+void mailbox_mempool_destroy(void);
+struct mb_cmd_pack *mailbox_alloc_cmd_pack(void);
+void *mailbox_copy_alloc(const void *src, size_t size);
+
+#endif
diff --git a/linux/core/mem.c b/linux/core/mem.c
new file mode 100644
index 0000000..af757d1
--- /dev/null
+++ b/linux/core/mem.c
@@ -0,0 +1,102 @@
+/*
+ * mem.c
+ *
+ * memory operation for gp sharedmem.
+ *
+ * Copyright (C) 2022 Huawei Technologies Co., Ltd.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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 "mem.h"
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include "smc_smp.h"
+#include "tc_ns_client.h"
+#include "teek_ns_client.h"
+#include "agent.h"
+#include "tc_ns_log.h"
+#include "mailbox_mempool.h"
+#include "reserved_mempool.h"
+
+void tc_mem_free(struct tc_ns_shared_mem *shared_mem)
+{
+ if (!shared_mem)
+ return;
+ if (shared_mem->mem_type == RESERVED_TYPE) {
+ reserved_mem_free(shared_mem->kernel_addr);
+ kfree(shared_mem);
+ return;
+ }
+
+ if (shared_mem->kernel_addr) {
+ vfree(shared_mem->kernel_addr);
+ shared_mem->kernel_addr = NULL;
+ }
+ kfree(shared_mem);
+}
+
+static void init_shared_mem(struct tc_ns_shared_mem *sh, void *addr, size_t len)
+{
+ sh->kernel_addr = addr;
+ sh->len = len;
+ sh->user_addr = NULL;
+ sh->user_addr_ca = NULL;
+ atomic_set(&sh->usage, 0);
+}
+struct tc_ns_shared_mem *tc_mem_allocate(size_t len)
+{
+ struct tc_ns_shared_mem *shared_mem = NULL;
+ void *addr = NULL;
+
+ shared_mem = kmalloc(sizeof(*shared_mem), GFP_KERNEL | __GFP_ZERO);
+ if (ZERO_OR_NULL_PTR((unsigned long)(uintptr_t)shared_mem)) {
+ tloge("shared_mem kmalloc failed\n");
+ return ERR_PTR(-ENOMEM);
+ }
+ shared_mem->mem_type = VMALLOC_TYPE;
+ len = ALIGN(len, SZ_4K);
+ if (exist_res_mem()) {
+ if (len > RESEVED_MAX_ALLOC_SIZE || len > RESERVED_MEM_POOL_SIZE) {
+ tloge("allocate reserved mem size too large\n");
+ return ERR_PTR(-EINVAL);
+ }
+ addr = reserved_mem_alloc(len);
+ if (addr) {
+ shared_mem->mem_type = RESERVED_TYPE;
+ init_shared_mem(shared_mem, addr, len);
+ return shared_mem;
+ } else {
+ tlogw("no more reserved memory to alloc so we use system vmalloc.\n");
+ }
+ }
+ if (len > MAILBOX_POOL_SIZE) {
+ tloge("alloc sharemem size %zu is too large\n", len);
+ kfree(shared_mem);
+ return ERR_PTR(-EINVAL);
+ }
+ addr = vmalloc_user(len);
+ if (!addr) {
+ tloge("alloc maibox failed\n");
+ kfree(shared_mem);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ init_shared_mem(shared_mem, addr, len);
+ return shared_mem;
+}
diff --git a/linux/core/mem.h b/linux/core/mem.h
new file mode 100644
index 0000000..1152b7d
--- /dev/null
+++ b/linux/core/mem.h
@@ -0,0 +1,44 @@
+/*
+ * mem.h
+ *
+ * memory operation for gp sharedmem.
+ *
+ * Copyright (C) 2022 Huawei Technologies Co., Ltd.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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 MEM_H
+#define MEM_H
+#include
+#include "teek_ns_client.h"
+
+#define PRE_ALLOCATE_SIZE (1024*1024)
+#define MEM_POOL_ELEMENT_SIZE (64*1024)
+#define MEM_POOL_ELEMENT_NR (8)
+#define MEM_POOL_ELEMENT_ORDER (4)
+
+struct tc_ns_shared_mem *tc_mem_allocate(size_t len);
+void tc_mem_free(struct tc_ns_shared_mem *shared_mem);
+
+static inline void get_sharemem_struct(struct tc_ns_shared_mem *sharemem)
+{
+ if (sharemem != NULL)
+ atomic_inc(&sharemem->usage);
+}
+
+static inline void put_sharemem_struct(struct tc_ns_shared_mem *sharemem)
+{
+ if (sharemem != NULL) {
+ if (atomic_dec_and_test(&sharemem->usage))
+ tc_mem_free(sharemem);
+ }
+}
+
+#endif
diff --git a/linux/core/reserved_mempool.c b/linux/core/reserved_mempool.c
new file mode 100644
index 0000000..4d47921
--- /dev/null
+++ b/linux/core/reserved_mempool.c
@@ -0,0 +1,440 @@
+/*
+ * reserved_mempool.c
+ *
+ * memory managing for reserved memory with TEE.
+ *
+ * Copyright (C) 2022 Huawei Technologies Co., Ltd.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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 "reserved_mempool.h"
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+
+#include "teek_client_constants.h"
+#include "tc_ns_log.h"
+#include "smc_smp.h"
+
+#define RESMEM_PAGE_MAX (RESERVED_MEM_POOL_SIZE >> PAGE_SHIFT)
+#define STATE_MODE 0440U
+
+struct virt_page {
+ unsigned long start;
+};
+
+struct reserved_page_t {
+ struct list_head node;
+ struct virt_page *page;
+ int order;
+ unsigned int count; /* whether be used */
+};
+
+struct reserved_free_area_t {
+ struct list_head page_list;
+ int order;
+};
+
+struct reserved_zone_t {
+ struct virt_page *all_pages;
+ struct reserved_page_t pages[RESMEM_PAGE_MAX];
+ struct reserved_free_area_t free_areas[0];
+};
+
+static struct reserved_zone_t *g_res_zone;
+static struct mutex g_res_lock;
+static int g_res_max_order;
+static unsigned long g_start_vaddr = 0;
+static unsigned long g_virt_phy_offset;
+static struct dentry *g_res_mem_dbg_dentry;
+
+bool exist_res_mem(void)
+{
+ return g_start_vaddr != 0;
+}
+
+unsigned long res_mem_virt_to_phys(unsigned long vaddr)
+{
+ return vaddr - g_virt_phy_offset;
+}
+
+int load_reserved_mem(void)
+{
+ struct device_node *np = NULL;
+ struct resource r;
+ int rc;
+ void *p = NULL;
+
+ np = of_find_compatible_node(NULL, NULL, "tz_reserved");
+ if (np == NULL) {
+ tlogd("can not find reserved memory.\n");
+ return 0;
+ }
+
+ rc = of_address_to_resource(np, 0, &r);
+ if (rc) {
+ tloge("of_address_to_resource error\n");
+ return -ENODEV;
+ }
+ p = ioremap(r.start, resource_size(&r));
+ if (p == NULL) {
+ tloge("io remap for reserved memory failed\n");
+ return -ENOMEM;
+ }
+ g_start_vaddr = (unsigned long)(uintptr_t)p;
+ g_virt_phy_offset = g_start_vaddr - (unsigned long)r.start;
+ return 0;
+}
+
+static int create_zone(void)
+{
+ size_t zone_len;
+ int order = get_order(RESERVED_MEM_POOL_SIZE);
+ g_res_max_order = (order > CONFIG_MAX_RES_MEM_ORDER) ? CONFIG_MAX_RES_MEM_ORDER : order;
+ zone_len = sizeof(struct reserved_free_area_t) * (g_res_max_order + 1) + sizeof(*g_res_zone);
+
+ g_res_zone = kzalloc(zone_len, GFP_KERNEL);
+ if (g_res_zone == NULL) {
+ tloge("fail to create zone\n");
+ return -ENOMEM;
+ }
+ return 0;
+}
+
+static struct virt_page *create_virt_pages(void)
+{
+ int i = 0;
+ struct virt_page *pages = NULL;
+
+ pages = kzalloc(RESMEM_PAGE_MAX * sizeof(struct virt_page), GFP_KERNEL);
+ if (pages == NULL) {
+ tloge("alloc pages failed\n");
+ return NULL;
+ }
+ for (i = 0; i < RESMEM_PAGE_MAX; i++)
+ pages[i].start = g_start_vaddr + i * PAGE_SIZE;
+ return pages;
+}
+
+void free_reserved_mempool(void)
+{
+ if (!exist_res_mem())
+ return;
+ kfree(g_res_zone->all_pages);
+ g_res_zone->all_pages = NULL;
+ kfree(g_res_zone);
+ g_res_zone = NULL;
+}
+
+static void show_res_mem_info(void)
+{
+ unsigned int i;
+ struct reserved_page_t *pos = NULL;
+ struct list_head *head = NULL;
+ unsigned int used = 0;
+
+ if (g_res_zone == NULL) {
+ tloge("res zone is NULL\n");
+ return;
+ }
+
+ tloge("################## reserved memory info ######################\n");
+ mutex_lock(&g_res_lock);
+ for (i = 0; i < RESMEM_PAGE_MAX; i++) {
+ if (g_res_zone->pages[i].count) {
+ tloge("page[%02d], order=%02d, count=%d\n",
+ i, g_res_zone->pages[i].order,
+ g_res_zone->pages[i].count);
+ used += (1 << (uint32_t)g_res_zone->pages[i].order);
+ }
+ }
+ tloge("reserved memory total usage:%u/%u\n", used, RESMEM_PAGE_MAX);
+ tloge("--------------------------------------------------------------\n");
+
+ for (i = 0; i < (unsigned int)g_res_max_order; i++) {
+ head = &g_res_zone->free_areas[i].page_list;
+ if (list_empty(head)) {
+ tloge("order[%02d] is empty\n", i);
+ } else {
+ list_for_each_entry(pos, head, node)
+ tloge("order[%02d]\n", i);
+ }
+ }
+ mutex_unlock(&g_res_lock);
+
+ tloge("#############################################################\n");
+}
+
+static ssize_t mb_res_mem_state_read(struct file *filp, char __user *ubuf,
+ size_t cnt, loff_t *ppos)
+{
+ (void)(filp);
+ (void)(ubuf);
+ (void)(ppos);
+ show_res_mem_info();
+ return 0;
+}
+
+static const struct file_operations g_res_mem_dbg_state_fops = {
+ .owner = THIS_MODULE,
+ .read = mb_res_mem_state_read,
+};
+
+static void init_res_mem_dentry(void)
+{
+ g_res_mem_dbg_dentry = debugfs_create_dir("tz_res_mem", NULL);
+ debugfs_create_file("state", STATE_MODE, g_res_mem_dbg_dentry, NULL,
+ &g_res_mem_dbg_state_fops);
+}
+
+static int res_mem_register(unsigned long paddr, unsigned int size)
+{
+ struct tc_ns_operation *operation = NULL;
+ struct tc_ns_smc_cmd *smc_cmd = NULL;
+ int ret = 0;
+
+ smc_cmd = kzalloc(sizeof(*smc_cmd), GFP_KERNEL);
+ if (ZERO_OR_NULL_PTR((unsigned long)(uintptr_t)smc_cmd)) {
+ tloge("alloc smc_cmd failed\n");
+ return -EIO;
+ }
+
+ operation = kzalloc(sizeof(*operation), GFP_KERNEL);
+ if (ZERO_OR_NULL_PTR((unsigned long)(uintptr_t)operation)) {
+ tloge("alloc operation failed\n");
+ ret = -EIO;
+ goto free_smc_cmd;
+ }
+
+ operation->paramtypes = TEE_PARAM_TYPE_VALUE_INPUT |
+ (TEE_PARAM_TYPE_VALUE_INPUT << TEE_PARAM_NUM);
+ operation->params[0].value.a = paddr;
+ operation->params[0].value.b = paddr >> ADDR_TRANS_NUM;
+ operation->params[1].value.a = size;
+
+ smc_cmd->cmd_type = CMD_TYPE_GLOBAL;
+ smc_cmd->cmd_id = GLOBAL_CMD_ID_REGISTER_RESMEM;
+ smc_cmd->operation_phys = virt_to_phys(operation);
+ smc_cmd->operation_h_phys = virt_to_phys(operation) >> ADDR_TRANS_NUM;
+
+ if (tc_ns_smc(smc_cmd)) {
+ tloge("resigter res mem failed\n");
+ ret = -EIO;
+ }
+
+ kfree(operation);
+ operation = NULL;
+free_smc_cmd:
+ kfree(smc_cmd);
+ smc_cmd = NULL;
+ return ret;
+}
+
+int reserved_mempool_init()
+{
+ struct virt_page *all_pages = NULL;
+ struct reserved_free_area_t *area = NULL;
+ struct reserved_page_t *res_page = NULL;
+ int ret = 0;
+ int i;
+ int max_order_cnt;
+ unsigned long paddr;
+
+ if (!exist_res_mem())
+ return 0;
+
+ ret = create_zone();
+ if (ret)
+ return ret;
+
+ all_pages = create_virt_pages();
+ if (all_pages == NULL) {
+ kfree(g_res_zone);
+ g_res_zone = NULL;
+ return -ENOMEM;
+ }
+
+ paddr = res_mem_virt_to_phys(g_start_vaddr);
+ ret = res_mem_register(paddr, RESERVED_MEM_POOL_SIZE);
+ if (ret) {
+ kfree(all_pages);
+ kfree(g_res_zone);
+ g_res_zone = NULL;
+ return -EIO;
+ }
+
+ for (i = 0; i < RESMEM_PAGE_MAX; i++) {
+ g_res_zone->pages[i].order = -1;
+ g_res_zone->pages[i].count = 0;
+ g_res_zone->pages[i].page = &all_pages[i];
+ }
+
+ for (i = 0; i <= g_res_max_order; i++) {
+ area = &g_res_zone->free_areas[i];
+ INIT_LIST_HEAD(&area->page_list);
+ area->order = i;
+ }
+ max_order_cnt = RESMEM_PAGE_MAX / (1 << (unsigned int)g_res_max_order);
+ g_res_zone->all_pages = all_pages;
+ for (i = 0; i < max_order_cnt; i++) {
+ int idx = i * (1 << (unsigned int)g_res_max_order);
+ g_res_zone->pages[idx].order = g_res_max_order;
+ res_page = &g_res_zone->pages[idx];
+ list_add_tail(&res_page->node, &area->page_list);
+ }
+ mutex_init(&g_res_lock);
+ init_res_mem_dentry();
+ return 0;
+}
+
+void *reserved_mem_alloc(size_t size)
+{
+ int i, j;
+ struct reserved_page_t *pos = NULL;
+ struct list_head *head = NULL;
+ int order = get_order(ALIGN(size, SZ_4K));
+ unsigned long addr = 0;
+
+ bool valid_param = (size > 0 && order <= g_res_max_order && order >= 0);
+ if (!valid_param) {
+ tloge("invalid alloc param, size %d, order %d, max %d\n",(int)size, order, g_res_max_order);
+ return NULL;
+ }
+ mutex_lock(&g_res_lock);
+ for (i = order; i <= g_res_max_order; i++) {
+ head = &g_res_zone->free_areas[i].page_list;
+ if (list_empty(head))
+ continue;
+
+ pos = list_first_entry(head, struct reserved_page_t, node);
+ pos->count = 1;
+ pos->order = order;
+
+ for (j = order; j < i; j++) {
+ struct reserved_page_t *new_page = NULL;
+ new_page = pos + (1 << (unsigned int)j);
+ new_page->count = 0;
+ new_page->order = j;
+ list_add_tail(&new_page->node, &g_res_zone->free_areas[j].page_list);
+ }
+ list_del(&pos->node);
+ addr = pos->page->start;
+ break;
+ }
+ mutex_unlock(&g_res_lock);
+ return (void *)(uintptr_t)addr;
+}
+
+static int get_virt_page_index(const void *ptr)
+{
+ unsigned long vaddr = (unsigned long)(uintptr_t)ptr;
+ unsigned long offset = vaddr - g_start_vaddr;
+ int pg_idx = offset / (1 << PAGE_SHIFT);
+ if (pg_idx >= RESMEM_PAGE_MAX || pg_idx < 0)
+ return -1;
+ return pg_idx;
+}
+
+static int buddy_merge(struct virt_page *vpage, int order, unsigned int *page_index)
+{
+ int i;
+ unsigned int cur_idx;
+ unsigned int buddy_idx;
+ struct reserved_page_t *self = NULL;
+ struct reserved_page_t *buddy = NULL;
+
+ for (i = order; i < g_res_max_order; i++) {
+ cur_idx = vpage - g_res_zone->all_pages;
+ buddy_idx = cur_idx ^ (1 << (unsigned int)i);
+ self = &g_res_zone->pages[cur_idx];
+ buddy = &g_res_zone->pages[buddy_idx];
+ self->count = 0;
+ /* is buddy free */
+ if (buddy->order == i && buddy->count == 0) {
+ /* release buddy */
+ list_del(&buddy->node);
+ /* combine self and buddy */
+ if (cur_idx > buddy_idx) {
+ vpage = buddy->page;
+ buddy->order = i + 1;
+ self->order = -1;
+ } else {
+ self->order = i + 1;
+ buddy->order = -1;
+ }
+ } else {
+ /* release self */
+ list_add_tail(&self->node,
+ &g_res_zone->free_areas[i].page_list);
+ return -1;
+ }
+ }
+
+ if (order == g_res_max_order) {
+ cur_idx = vpage - g_res_zone->all_pages;
+ tlogd("no need to find buddy, cur is %u\n", cur_idx);
+ *page_index = cur_idx;
+ return 0;
+ }
+ *page_index = (cur_idx > buddy_idx) ? buddy_idx : cur_idx;
+ return 0;
+}
+
+void reserved_mem_free(const void *ptr)
+{
+ struct reserved_page_t *self = NULL;
+ int self_idx;
+ unsigned int page_index;
+ struct reserved_page_t *max_order_page = NULL;
+
+ if (ptr == NULL) {
+ tloge("invalid ptr\n");
+ return;
+ }
+
+ mutex_lock(&g_res_lock);
+ self_idx = get_virt_page_index(ptr);
+ if (self_idx < 0) {
+ mutex_unlock(&g_res_lock);
+ tloge("invalid page\n");
+ return;
+ }
+ self = &g_res_zone->pages[self_idx];
+ if (!self->count) {
+ tloge("already free in reseverd mempool\n");
+ mutex_unlock(&g_res_lock);
+ return;
+ }
+
+ if (buddy_merge(self->page, self->order, &page_index) < 0) {
+ mutex_unlock(&g_res_lock);
+ return;
+ }
+
+ max_order_page = &g_res_zone->pages[page_index];
+ list_add_tail(&max_order_page->node,
+ &g_res_zone->free_areas[g_res_max_order].page_list);
+ mutex_unlock(&g_res_lock);
+}
diff --git a/linux/core/reserved_mempool.h b/linux/core/reserved_mempool.h
new file mode 100644
index 0000000..5ba31e6
--- /dev/null
+++ b/linux/core/reserved_mempool.h
@@ -0,0 +1,38 @@
+/*
+ * reserved_mempool.h
+ *
+ * reserved memory managing for sharing memory with TEE.
+ *
+ * Copyright (C) 2022 Huawei Technologies Co., Ltd.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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 RESERVED_MEMPOOOL_H
+#define RESERVED_MEMPOOOL_H
+
+#include
+#include
+
+#ifndef CONFIG_MAX_RES_MEM_ORDER
+#define CONFIG_MAX_RES_MEM_ORDER 15
+#endif
+
+#define RESERVED_MEM_POOL_SIZE 0x8000000 //128M
+#define RESEVED_MAX_ALLOC_SIZE (1 << (CONFIG_MAX_RES_MEM_ORDER + PAGE_SHIFT)) //2^max_order * 4K
+#define LIMIT_RES_MEM_ORDER 15
+
+int load_reserved_mem(void);
+void *reserved_mem_alloc(size_t size);
+void free_reserved_mempool(void);
+int reserved_mempool_init(void);
+void reserved_mem_free(const void *ptr);
+bool exist_res_mem(void);
+unsigned long res_mem_virt_to_phys(unsigned long vaddr);
+#endif
diff --git a/linux/core/session_manager.c b/linux/core/session_manager.c
new file mode 100644
index 0000000..8f730bb
--- /dev/null
+++ b/linux/core/session_manager.c
@@ -0,0 +1,1213 @@
+/*
+ * session_manager.c
+ *
+ * function for session management
+ *
+ * Copyright (C) 2022 Huawei Technologies Co., Ltd.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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 "session_manager.h"
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#if (KERNEL_VERSION(4, 14, 0) <= LINUX_VERSION_CODE)
+#include
+#include
+#include
+#endif
+#include
+#include "smc_smp.h"
+#include "mem.h"
+#include "gp_ops.h"
+#include "tc_ns_log.h"
+#include "teek_client_constants.h"
+#include "client_hash_auth.h"
+#include "mailbox_mempool.h"
+#include "tc_client_driver.h"
+#include "tz_kthread_affinity.h"
+
+static DEFINE_MUTEX(g_load_app_lock);
+#define MAX_REF_COUNT (255)
+
+/* record all service node and need mutex to avoid race */
+struct list_head g_service_list;
+DEFINE_MUTEX(g_service_list_lock);
+
+struct load_img_params {
+ struct tc_ns_dev_file *dev_file;
+ const char *file_buffer;
+ unsigned int file_size;
+ struct mb_cmd_pack *mb_pack;
+ char *mb_load_mem;
+ struct tc_uuid *uuid_return;
+ unsigned int mb_load_size;
+};
+
+void init_srvc_list(void)
+{
+ INIT_LIST_HEAD(&g_service_list);
+}
+
+void get_session_struct(struct tc_ns_session *session)
+{
+ if (!session)
+ return;
+
+ atomic_inc(&session->usage);
+}
+
+void put_session_struct(struct tc_ns_session *session)
+{
+ if (!session || !atomic_dec_and_test(&session->usage))
+ return;
+
+ if (memset_s(session, sizeof(*session), 0, sizeof(*session)))
+ tloge("Caution, memset failed!\n");
+ kfree(session);
+}
+
+void get_service_struct(struct tc_ns_service *service)
+{
+ if (!service)
+ return;
+
+ atomic_inc(&service->usage);
+ tlogd("service->usage = %d\n", atomic_read(&service->usage));
+}
+
+void put_service_struct(struct tc_ns_service *service)
+{
+ if (!service)
+ return;
+
+ tlogd("service->usage = %d\n", atomic_read(&service->usage));
+ mutex_lock(&g_service_list_lock);
+ if (atomic_dec_and_test(&service->usage)) {
+ tlogd("del service [0x%x] from service list\n",
+ *(uint32_t *)service->uuid);
+ list_del(&service->head);
+ kfree(service);
+ }
+ mutex_unlock(&g_service_list_lock);
+}
+
+static int add_service_to_dev(struct tc_ns_dev_file *dev,
+ struct tc_ns_service *service)
+{
+ uint32_t i;
+
+ if (!dev || !service)
+ return -EINVAL;
+
+ for (i = 0; i < SERVICES_MAX_COUNT; i++) {
+ if (!dev->services[i]) {
+ tlogd("add service %u to %u\n", i, dev->dev_file_id);
+ dev->services[i] = service;
+ dev->service_ref[i] = 1;
+ return 0;
+ }
+ }
+ return -EFAULT;
+}
+
+void dump_services_status(const char *param)
+{
+ struct tc_ns_service *service = NULL;
+
+ (void)param;
+ mutex_lock(&g_service_list_lock);
+ tlogi("show service list:\n");
+ list_for_each_entry(service, &g_service_list, head) {
+ tlogi("uuid-%x, usage=%d\n", *(uint32_t *)service->uuid,
+ atomic_read(&service->usage));
+ }
+ mutex_unlock(&g_service_list_lock);
+}
+
+static void del_service_from_dev(struct tc_ns_dev_file *dev,
+ struct tc_ns_service *service)
+{
+ uint32_t i;
+
+ for (i = 0; i < SERVICES_MAX_COUNT; i++) {
+ if (dev->services[i] == service) {
+ tlogd("dev service ref-%u = %u\n", i,
+ dev->service_ref[i]);
+ if (dev->service_ref[i] == 0) {
+ tloge("Caution! No service to be deleted!\n");
+ break;
+ }
+ dev->service_ref[i]--;
+ if (!dev->service_ref[i]) {
+ tlogd("del service %u from %u\n",
+ i, dev->dev_file_id);
+ dev->services[i] = NULL;
+ put_service_struct(service);
+ }
+ break;
+ }
+ }
+}
+
+struct tc_ns_session *tc_find_session_withowner(
+ const struct list_head *session_list,
+ unsigned int session_id, struct tc_ns_dev_file *dev_file)
+{
+ struct tc_ns_session *session = NULL;
+
+ if (!session_list || !dev_file) {
+ tloge("session list or dev is null\n");
+ return NULL;
+ }
+
+ list_for_each_entry(session, session_list, head) {
+ if (session->session_id == session_id &&
+ session->owner == dev_file)
+ return session;
+ }
+ return NULL;
+}
+
+struct tc_ns_service *tc_find_service_in_dev(const struct tc_ns_dev_file *dev,
+ const unsigned char *uuid, int uuid_size)
+{
+ uint32_t i;
+
+ if (!dev || !uuid || uuid_size != UUID_LEN)
+ return NULL;
+
+ for (i = 0; i < SERVICES_MAX_COUNT; i++) {
+ if (dev->services[i] != NULL &&
+ !memcmp(dev->services[i]->uuid, uuid, UUID_LEN))
+ return dev->services[i];
+ }
+ return NULL;
+}
+
+struct tc_ns_session *tc_find_session_by_uuid(unsigned int dev_file_id,
+ const struct tc_ns_smc_cmd *cmd)
+{
+ struct tc_ns_dev_file *dev_file = NULL;
+ struct tc_ns_service *service = NULL;
+ struct tc_ns_session *session = NULL;
+
+ if (!cmd) {
+ tloge("parameter is null pointer!\n");
+ return NULL;
+ }
+
+ dev_file = tc_find_dev_file(dev_file_id);
+ if (!dev_file) {
+ tloge("can't find dev file!\n");
+ return NULL;
+ }
+
+ mutex_lock(&dev_file->service_lock);
+ service = tc_find_service_in_dev(dev_file, cmd->uuid, UUID_LEN);
+ get_service_struct(service);
+ mutex_unlock(&dev_file->service_lock);
+ if (!service) {
+ tloge("can't find service!\n");
+ return NULL;
+ }
+
+ mutex_lock(&service->session_lock);
+ session = tc_find_session_withowner(&service->session_list,
+ cmd->context_id, dev_file);
+ get_session_struct(session);
+ mutex_unlock(&service->session_lock);
+ put_service_struct(service);
+ if (!session) {
+ tloge("can't find session-0x%x!\n", cmd->context_id);
+ return NULL;
+ }
+ return session;
+}
+
+static int tc_ns_need_load_image(unsigned int file_id,
+ const unsigned char *uuid, unsigned int uuid_len)
+{
+ int ret;
+ int smc_ret;
+ struct tc_ns_smc_cmd smc_cmd = { {0}, 0 };
+ struct mb_cmd_pack *mb_pack = NULL;
+ char *mb_param = NULL;
+
+ if (!uuid || uuid_len != UUID_LEN) {
+ tloge("invalid uuid\n");
+ return -ENOMEM;
+ }
+ mb_pack = mailbox_alloc_cmd_pack();
+ if (!mb_pack) {
+ tloge("alloc mb pack failed\n");
+ return -ENOMEM;
+ }
+ mb_param = mailbox_copy_alloc(uuid, uuid_len);
+ if (!mb_param) {
+ tloge("alloc mb param failed\n");
+ ret = -ENOMEM;
+ goto clean;
+ }
+ mb_pack->operation.paramtypes = TEEC_MEMREF_TEMP_INOUT;
+ mb_pack->operation.params[0].memref.buffer =
+ virt_to_phys(mb_param);
+ mb_pack->operation.buffer_h_addr[0] =
+ (uint64_t)virt_to_phys(mb_param) >> ADDR_TRANS_NUM;
+ mb_pack->operation.params[0].memref.size = SZ_4K;
+ smc_cmd.cmd_id = GLOBAL_CMD_ID_NEED_LOAD_APP;
+ smc_cmd.cmd_type = CMD_TYPE_GLOBAL;
+ smc_cmd.dev_file_id = file_id;
+ smc_cmd.context_id = 0;
+ smc_cmd.operation_phys = virt_to_phys(&mb_pack->operation);
+ smc_cmd.operation_h_phys =
+ (uint64_t)virt_to_phys(&mb_pack->operation) >> ADDR_TRANS_NUM;
+
+ smc_ret = tc_ns_smc(&smc_cmd);
+ if (smc_ret) {
+ tloge("smc call returns error ret 0x%x\n", smc_ret);
+ ret = -EFAULT;
+ goto clean;
+ } else {
+ ret = *(int *)mb_param;
+ }
+clean:
+ if (mb_param)
+ mailbox_free(mb_param);
+ mailbox_free(mb_pack);
+
+ return ret;
+}
+
+int tc_ns_load_secfile(struct tc_ns_dev_file *dev_file,
+ const void __user *argp)
+{
+ int ret;
+ struct load_secfile_ioctl_struct ioctl_arg = { 0, {0}, 0, {NULL} };
+
+ if (!dev_file || !argp) {
+ tloge("Invalid params !\n");
+ return -EINVAL;
+ }
+
+ if (copy_from_user(&ioctl_arg, argp, sizeof(ioctl_arg))) {
+ tloge("copy from user failed\n");
+ ret = -ENOMEM;
+ return ret;
+ }
+
+ mutex_lock(&g_load_app_lock);
+ if (ioctl_arg.secfile_type == LOAD_TA) {
+ ret = tc_ns_need_load_image(dev_file->dev_file_id, ioctl_arg.uuid,
+ (unsigned int)UUID_LEN);
+ if (ret == 1) /* 1 means we need to load image */
+ ret = tc_ns_load_image(dev_file, ioctl_arg.file_buffer,
+ ioctl_arg.file_size, NULL, ioctl_arg.secfile_type);
+ } else if (ioctl_arg.secfile_type == LOAD_LIB) {
+ ret = tc_ns_load_image(dev_file, ioctl_arg.file_buffer,
+ ioctl_arg.file_size, NULL, ioctl_arg.secfile_type);
+ } else if (ioctl_arg.secfile_type == LOAD_DYNAMIC_DRV) {
+ ret = tc_ns_load_image(dev_file, ioctl_arg.file_buffer,
+ ioctl_arg.file_size, NULL, ioctl_arg.secfile_type);
+ } else {
+ tloge("invalid secfile type: %d!", ioctl_arg.secfile_type);
+ ret = -EINVAL;
+ }
+ if (ret)
+ tloge("load TA secfile: %d failed, ret = %x",
+ ioctl_arg.secfile_type, ret);
+ mutex_unlock(&g_load_app_lock);
+ return ret;
+}
+
+/*
+ * Modify the client context so params id 2 and 3 contain temp pointers to the
+ * public key and package name for the open session. This is used for the
+ * TEEC_LOGIN_IDENTIFY open session method
+ */
+static int set_login_information(struct tc_ns_dev_file *dev_file,
+ struct tc_ns_client_context *context)
+{
+ /* The daemon has failed to get login information or not supplied */
+ if (!dev_file->pkg_name_len)
+ return -EINVAL;
+ /*
+ * The 3rd parameter buffer points to the pkg name buffer in the
+ * device file pointer
+ * get package name len and package name
+ */
+ context->params[3].memref.size_addr =
+ (__u64)(uintptr_t)&dev_file->pkg_name_len;
+ context->params[3].memref.buffer =
+ (__u64)(uintptr_t)dev_file->pkg_name;
+ /* Set public key len and public key */
+ if (dev_file->pub_key_len) {
+ context->params[2].memref.size_addr =
+ (__u64)(uintptr_t)&dev_file->pub_key_len;
+ context->params[2].memref.buffer =
+ (__u64)(uintptr_t)dev_file->pub_key;
+ } else {
+ /* If get public key failed, then get uid in kernel */
+ uint32_t ca_uid = tc_ns_get_uid();
+ if (ca_uid == (uint32_t)(-1)) {
+ tloge("failed to get uid of the task\n");
+ goto error;
+ }
+ dev_file->pub_key_len = sizeof(ca_uid);
+ context->params[2].memref.size_addr =
+ (__u64)(uintptr_t)&dev_file->pub_key_len;
+ if (memcpy_s(dev_file->pub_key, MAX_PUBKEY_LEN, &ca_uid,
+ dev_file->pub_key_len)) {
+ tloge("failed to copy pubkey, pub key len=%u\n",
+ dev_file->pub_key_len);
+ goto error;
+ }
+ context->params[2].memref.buffer =
+ (__u64)(uintptr_t)dev_file->pub_key;
+ }
+ /* Now we mark the 2 parameters as input temp buffers */
+ context->param_types = teec_param_types(
+ teec_param_type_get(context->param_types, 0),
+ teec_param_type_get(context->param_types, 1),
+ TEEC_MEMREF_TEMP_INPUT, TEEC_MEMREF_TEMP_INPUT);
+
+ return 0;
+error:
+ return -EFAULT;
+}
+
+static int check_login_method(struct tc_ns_dev_file *dev_file,
+ struct tc_ns_client_context *context, uint8_t *flags)
+{
+ int ret;
+
+ if (!dev_file || !context || !flags)
+ return -EFAULT;
+
+ if (context->login.method != TEEC_LOGIN_IDENTIFY) {
+ tlogd("login method is not supported\n");
+ return -EINVAL;
+ }
+
+ tlogd("login method is IDENTIFY\n");
+ /* check if usr params 0 and 1 are valid */
+ if (dev_file->kernel_api == TEE_REQ_FROM_USER_MODE &&
+ (!tc_user_param_valid(context, (unsigned int)0) ||
+ !tc_user_param_valid(context, (unsigned int)1)))
+ return -EINVAL;
+
+ ret = set_login_information(dev_file, context);
+ if (ret) {
+ tloge("set login information failed ret =%d\n", ret);
+ return ret;
+ }
+ *flags |= TC_CALL_LOGIN;
+
+ return 0;
+}
+
+static struct tc_ns_service *tc_ref_service_in_dev(struct tc_ns_dev_file *dev,
+ const unsigned char *uuid, int uuid_size, bool *is_full)
+{
+ uint32_t i;
+
+ if (uuid_size != UUID_LEN)
+ return NULL;
+
+ for (i = 0; i < SERVICES_MAX_COUNT; i++) {
+ if (dev->services[i] != NULL &&
+ !memcmp(dev->services[i]->uuid, uuid, UUID_LEN)) {
+ if (dev->service_ref[i] == MAX_REF_COUNT) {
+ *is_full = true;
+ return NULL;
+ }
+ dev->service_ref[i]++;
+ return dev->services[i];
+ }
+ }
+ return NULL;
+}
+
+static int tc_ns_service_init(const unsigned char *uuid, uint32_t uuid_len,
+ struct tc_ns_service **new_service)
+{
+ int ret = 0;
+ struct tc_ns_service *service = NULL;
+
+ if (!uuid || !new_service || uuid_len != UUID_LEN)
+ return -EINVAL;
+
+ service = kzalloc(sizeof(*service), GFP_KERNEL);
+ if (ZERO_OR_NULL_PTR((unsigned long)(uintptr_t)service)) {
+ tloge("kzalloc failed\n");
+ ret = -ENOMEM;
+ return ret;
+ }
+
+ if (memcpy_s(service->uuid, sizeof(service->uuid), uuid, uuid_len)) {
+ kfree(service);
+ return -EFAULT;
+ }
+
+ INIT_LIST_HEAD(&service->session_list);
+ mutex_init(&service->session_lock);
+ list_add_tail(&service->head, &g_service_list);
+ tlogd("add service: 0x%x to service list\n", *(uint32_t *)uuid);
+ atomic_set(&service->usage, 1);
+ mutex_init(&service->operation_lock);
+ *new_service = service;
+
+ return ret;
+}
+
+static struct tc_ns_service *tc_find_service_from_all(
+ const unsigned char *uuid, uint32_t uuid_len)
+{
+ struct tc_ns_service *service = NULL;
+
+ if (!uuid || uuid_len != UUID_LEN)
+ return NULL;
+
+ list_for_each_entry(service, &g_service_list, head) {
+ if (!memcmp(service->uuid, uuid, sizeof(service->uuid)))
+ return service;
+ }
+
+ return NULL;
+}
+
+static struct tc_ns_service *find_service(struct tc_ns_dev_file *dev_file,
+ const struct tc_ns_client_context *context)
+{
+ int ret;
+ struct tc_ns_service *service = NULL;
+ bool is_full = false;
+
+ mutex_lock(&dev_file->service_lock);
+ service = tc_ref_service_in_dev(dev_file, context->uuid,
+ UUID_LEN, &is_full);
+ /* if service has been opened in this dev or ref cnt is full */
+ if (service || is_full) {
+ /*
+ * If service has been reference by this dev, find service in dev
+ * will incre ref count to declaim there's how many callers to
+ * this service from the dev, instead of incre service->usage.
+ * While close session, dev->service_ref[i] will decre and till
+ * it get to 0, put service struct will be called.
+ */
+ mutex_unlock(&dev_file->service_lock);
+ return service;
+ }
+ mutex_lock(&g_service_list_lock);
+ service = tc_find_service_from_all(context->uuid, UUID_LEN);
+ /* if service has been opened in other dev */
+ if (service) {
+ get_service_struct(service);
+ mutex_unlock(&g_service_list_lock);
+ goto add_service;
+ }
+ /* Create a new service if we couldn't find it in list */
+ ret = tc_ns_service_init(context->uuid, UUID_LEN, &service);
+ /* unlock after init to make sure find service from all is correct */
+ mutex_unlock(&g_service_list_lock);
+ if (ret) {
+ tloge("service init failed");
+ mutex_unlock(&dev_file->service_lock);
+ return NULL;
+ }
+add_service:
+ ret = add_service_to_dev(dev_file, service);
+ mutex_unlock(&dev_file->service_lock);
+ if (ret) {
+ /*
+ * for new srvc, match init usage to 1;
+ * for srvc already exist, match get;
+ */
+ put_service_struct(service);
+ service = NULL;
+ tloge("fail to add service to dev\n");
+ return NULL;
+ }
+ return service;
+}
+
+static bool is_valid_ta_size(const char *file_buffer, unsigned int file_size)
+{
+ if (!file_buffer || !file_size) {
+ tloge("invalid load ta size\n");
+ return false;
+ }
+
+ if (file_size > SZ_8M) {
+ tloge("not support TA larger than 8M, size=%u\n", file_size);
+ return false;
+ }
+ return true;
+}
+
+static int alloc_for_load_image(struct load_img_params *params)
+{
+ /* we will try any possible to alloc mailbox mem to load TA */
+ for (; params->mb_load_size > 0; params->mb_load_size >>= 1) {
+ params->mb_load_mem = mailbox_alloc(params->mb_load_size, 0);
+ if (params->mb_load_mem)
+ break;
+ tlogw("alloc mem size=%u for TA load mem fail\n",
+ params->mb_load_size);
+ }
+
+ if (!params->mb_load_mem) {
+ tloge("alloc TA load mem failed\n");
+ return -ENOMEM;
+ }
+
+ params->mb_pack = mailbox_alloc_cmd_pack();
+ if (!params->mb_pack) {
+ mailbox_free(params->mb_load_mem);
+ params->mb_load_mem = NULL;
+ tloge("alloc mb pack failed\n");
+ return -ENOMEM;
+ }
+
+ params->uuid_return = mailbox_alloc(sizeof(*(params->uuid_return)), 0);
+ if (!params->uuid_return) {
+ mailbox_free(params->mb_load_mem);
+ params->mb_load_mem = NULL;
+ mailbox_free(params->mb_pack);
+ params->mb_pack = NULL;
+ tloge("alloc uuid failed\n");
+ return -ENOMEM;
+ }
+ return 0;
+}
+
+static void pack_load_frame_cmd(uint32_t load_size,
+ const struct load_img_params *params, struct tc_ns_smc_cmd *smc_cmd)
+{
+ struct mb_cmd_pack *mb_pack = params->mb_pack;
+ char *mb_load_mem = params->mb_load_mem;
+ struct tc_uuid *uuid_return = params->uuid_return;
+
+ mb_pack->operation.params[0].memref.buffer =
+ virt_to_phys(mb_load_mem);
+ mb_pack->operation.buffer_h_addr[0] =
+ (uint64_t)virt_to_phys(mb_load_mem) >> ADDR_TRANS_NUM;
+ mb_pack->operation.params[0].memref.size = load_size + sizeof(int);
+ mb_pack->operation.params[2].memref.buffer =
+ virt_to_phys(uuid_return);
+ mb_pack->operation.buffer_h_addr[2] =
+ (uint64_t)virt_to_phys(uuid_return) >> ADDR_TRANS_NUM;
+ mb_pack->operation.params[2].memref.size = sizeof(*uuid_return);
+ mb_pack->operation.paramtypes = teec_param_types(TEEC_MEMREF_TEMP_INPUT,
+ TEEC_VALUE_INOUT, TEEC_MEMREF_TEMP_OUTPUT, TEEC_VALUE_INPUT);
+
+ smc_cmd->cmd_type = CMD_TYPE_GLOBAL;
+ smc_cmd->cmd_id = GLOBAL_CMD_ID_LOAD_SECURE_APP;
+ smc_cmd->context_id = 0;
+ smc_cmd->operation_phys = virt_to_phys(&mb_pack->operation);
+ smc_cmd->operation_h_phys =
+ (uint64_t)virt_to_phys(&mb_pack->operation) >> ADDR_TRANS_NUM;
+}
+
+static int32_t load_image_copy_file(struct load_img_params *params, uint32_t load_size,
+ int32_t load_flag, uint32_t loaded_size)
+{
+ if (!current->mm) {
+ if (memcpy_s(params->mb_load_mem + sizeof(load_flag),
+ params->mb_load_size - sizeof(load_flag),
+ params->file_buffer + loaded_size, load_size) != 0) {
+ tloge("memcpy file buf get fail\n");
+ return -EFAULT;
+ }
+ return 0;
+ }
+ if (copy_from_user(params->mb_load_mem + sizeof(load_flag),
+ (void __user *)params->file_buffer + loaded_size, load_size)) {
+ tloge("file buf get fail\n");
+ return -EFAULT;
+ }
+ return 0;
+}
+
+static int load_image_by_frame(struct load_img_params *params,
+ unsigned int load_times, struct tc_ns_client_return *tee_ret, enum secfile_type_t type)
+{
+ char *p = params->mb_load_mem;
+ uint32_t load_size;
+ int load_flag = 1; /* 0:it's last block, 1:not last block */
+ uint32_t loaded_size = 0;
+ unsigned int index;
+ struct tc_ns_smc_cmd smc_cmd = { {0}, 0 };
+ int smc_ret;
+
+ for (index = 0; index < load_times; index++) {
+ smc_cmd.err_origin = TEEC_ORIGIN_COMMS;
+ if (index == (load_times - 1)) {
+ load_flag = 0;
+ load_size = params->file_size - loaded_size;
+ } else {
+ load_size = params->mb_load_size - sizeof(load_flag);
+ }
+ *(int *)p = load_flag;
+ if (load_size > params->mb_load_size - sizeof(load_flag)) {
+ tloge("invalid load size %u/%u\n", load_size,
+ params->mb_load_size);
+ return -EINVAL;
+ }
+
+ if (load_image_copy_file(params, load_size, load_flag, loaded_size) != 0)
+ return -EFAULT;
+
+ pack_load_frame_cmd(load_size, params, &smc_cmd);
+ params->mb_pack->operation.params[3].value.a = index;
+ params->mb_pack->operation.params[1].value.a = (type == LOAD_DYNAMIC_DRV ? 1 : 0);
+ smc_cmd.dev_file_id = params->dev_file->dev_file_id;
+ smc_ret = tc_ns_smc(&smc_cmd);
+ tlogd("configid=%u, ret=%d, load_flag=%d, index=%u\n",
+ params->mb_pack->operation.params[1].value.a, smc_ret,
+ load_flag, index);
+
+ if (smc_ret) {
+ if (tee_ret != NULL) {
+ tee_ret->code = smc_ret;
+ tee_ret->origin = smc_cmd.err_origin;
+ }
+ return -EFAULT;
+ }
+
+ loaded_size += load_size;
+ }
+ return 0;
+}
+
+int tc_ns_load_image_with_lock(struct tc_ns_dev_file *dev, const char *file_buffer,
+ unsigned int file_size, enum secfile_type_t type)
+{
+ int ret;
+
+ if (!dev || !file_buffer) {
+ tloge("dev or file buffer is NULL!\n");
+ return -EINVAL;
+ }
+
+ mutex_lock(&g_load_app_lock);
+ ret = tc_ns_load_image(dev, file_buffer, file_size, NULL, type);
+ mutex_unlock(&g_load_app_lock);
+
+ return ret;
+}
+
+int tc_ns_load_image(struct tc_ns_dev_file *dev, const char *file_buffer,
+ unsigned int file_size, struct tc_ns_client_return *tee_ret, enum secfile_type_t type)
+{
+ int ret;
+ unsigned int load_times;
+ struct load_img_params params = {
+ dev, file_buffer, file_size, NULL, NULL, NULL, 0
+ };
+
+ if (!dev || !file_buffer) {
+ tloge("dev or file buffer is NULL!\n");
+ return -EINVAL;
+ }
+
+ if (!is_valid_ta_size(file_buffer, file_size))
+ return -EINVAL;
+
+ params.mb_load_size = (file_size > (SZ_1M - sizeof(int))) ?
+ SZ_1M : ALIGN(file_size, SZ_4K);
+ ret = alloc_for_load_image(¶ms);
+ if (ret)
+ return ret;
+
+ if (params.mb_load_size <= sizeof(int)) {
+ tloge("mb load size is too small!\n");
+ ret = -ENOMEM;
+ goto free_mem;
+ }
+ load_times = file_size / (params.mb_load_size - sizeof(int));
+ if (file_size % (params.mb_load_size - sizeof(int)))
+ load_times += 1;
+ ret = load_image_by_frame(¶ms, load_times, tee_ret, type);
+free_mem:
+ mailbox_free(params.mb_load_mem);
+ mailbox_free(params.mb_pack);
+ mailbox_free(params.uuid_return);
+ return ret;
+}
+
+static int load_ta_image(struct tc_ns_dev_file *dev_file,
+ struct tc_ns_client_context *context)
+{
+ int ret;
+ struct tc_ns_client_return tee_ret = {0};
+ tee_ret.origin = TEEC_ORIGIN_COMMS;
+
+ mutex_lock(&g_load_app_lock);
+ ret = tc_ns_need_load_image(dev_file->dev_file_id, context->uuid,
+ (unsigned int)UUID_LEN);
+ if (ret == 1) { /* 1 means we need to load image */
+ if (!context->file_buffer) {
+ tloge("context's file_buffer is NULL");
+ mutex_unlock(&g_load_app_lock);
+ return -1;
+ }
+ ret = tc_ns_load_image(dev_file, context->file_buffer,
+ context->file_size, &tee_ret, LOAD_TA);
+ if (ret) {
+ tloge("load image failed, ret=%x", ret);
+ context->returns.code = tee_ret.code;
+ if (tee_ret.origin != TEEC_ORIGIN_COMMS) {
+ context->returns.origin = tee_ret.origin;
+ ret = EFAULT;
+ }
+ mutex_unlock(&g_load_app_lock);
+ return ret;
+ }
+ }
+ mutex_unlock(&g_load_app_lock);
+ return ret;
+}
+
+static void init_new_sess_node(struct tc_ns_dev_file *dev_file,
+ const struct tc_ns_client_context *context,
+ struct tc_ns_service *service,
+ struct tc_ns_session *session)
+{
+ session->session_id = context->session_id;
+ atomic_set(&session->usage, 1);
+ session->owner = dev_file;
+
+ session->wait_data.send_wait_flag = 0;
+ init_waitqueue_head(&session->wait_data.send_cmd_wq);
+
+ mutex_lock(&service->session_lock);
+ list_add_tail(&session->head, &service->session_list);
+ mutex_unlock(&service->session_lock);
+}
+
+static int proc_open_session(struct tc_ns_dev_file *dev_file,
+ struct tc_ns_client_context *context, struct tc_ns_service *service,
+ struct tc_ns_session *session, uint8_t flags)
+{
+ int ret;
+ struct tc_call_params params = {
+ dev_file, context, session, flags
+ };
+
+ mutex_lock(&service->operation_lock);
+ ret = load_ta_image(dev_file, context);
+ if (ret) {
+ tloge("load ta image failed\n");
+ mutex_unlock(&service->operation_lock);
+ return ret;
+ }
+
+ ret = tc_client_call(¶ms);
+ if (ret) {
+ mutex_unlock(&service->operation_lock);
+ tloge("smc call returns error, ret=0x%x\n", ret);
+ return ret;
+ }
+ init_new_sess_node(dev_file, context, service, session);
+ /*
+ * session_id in tee is unique, but in concurrency scene
+ * same session_id may appear in tzdriver, put session_list
+ * add/del in service->operation_lock can avoid it.
+ */
+ mutex_unlock(&service->operation_lock);
+ return ret;
+}
+
+int tc_ns_open_session(struct tc_ns_dev_file *dev_file,
+ struct tc_ns_client_context *context)
+{
+ int ret;
+ struct tc_ns_service *service = NULL;
+ struct tc_ns_session *session = NULL;
+ uint8_t flags = TC_CALL_GLOBAL;
+
+ if (!dev_file || !context) {
+ tloge("invalid dev_file or context\n");
+ return -EINVAL;
+ }
+
+ ret = check_login_method(dev_file, context, &flags);
+ if (ret)
+ return ret;
+
+ context->cmd_id = GLOBAL_CMD_ID_OPEN_SESSION;
+
+ service = find_service(dev_file, context);
+ if (!service) {
+ tloge("find service failed\n");
+ return -ENOMEM;
+ }
+
+ session = kzalloc(sizeof(*session), GFP_KERNEL);
+ if (ZERO_OR_NULL_PTR((unsigned long)(uintptr_t)session)) {
+ tloge("kzalloc failed\n");
+ mutex_lock(&dev_file->service_lock);
+ del_service_from_dev(dev_file, service);
+ mutex_unlock(&dev_file->service_lock);
+ return -ENOMEM;
+ }
+ mutex_init(&session->ta_session_lock);
+
+ ret = calc_client_auth_hash(dev_file, context, session);
+ if (ret) {
+ tloge("calc client auth hash failed\n");
+ goto err_free_rsrc;
+ }
+
+ ret = proc_open_session(dev_file, context, service, session, flags);
+ if (!ret)
+ return ret;
+err_free_rsrc:
+ mutex_lock(&dev_file->service_lock);
+ del_service_from_dev(dev_file, service);
+ mutex_unlock(&dev_file->service_lock);
+
+ kfree(session);
+ return ret;
+}
+
+static struct tc_ns_session *get_session(struct tc_ns_service *service,
+ struct tc_ns_dev_file *dev_file,
+ const struct tc_ns_client_context *context)
+{
+ struct tc_ns_session *session = NULL;
+
+ mutex_lock(&service->session_lock);
+ session = tc_find_session_withowner(&service->session_list,
+ context->session_id, dev_file);
+ get_session_struct(session);
+ mutex_unlock(&service->session_lock);
+
+ return session;
+}
+
+static struct tc_ns_service *get_service(struct tc_ns_dev_file *dev_file,
+ const struct tc_ns_client_context *context)
+{
+ struct tc_ns_service *service = NULL;
+
+ mutex_lock(&dev_file->service_lock);
+ service = tc_find_service_in_dev(dev_file, context->uuid, UUID_LEN);
+ get_service_struct(service);
+ mutex_unlock(&dev_file->service_lock);
+
+ return service;
+}
+
+static int close_session(struct tc_ns_dev_file *dev,
+ struct tc_ns_session *session, const unsigned char *uuid,
+ unsigned int uuid_len, unsigned int session_id)
+{
+ struct tc_ns_client_context context;
+ int ret;
+ struct tc_call_params params = {
+ dev, &context, session, 0
+ };
+
+ if (uuid_len != UUID_LEN)
+ return -EINVAL;
+
+ if (memset_s(&context, sizeof(context), 0, sizeof(context)))
+ return -EFAULT;
+
+ if (memcpy_s(context.uuid, sizeof(context.uuid), uuid, uuid_len))
+ return -EFAULT;
+
+ context.session_id = session_id;
+ context.cmd_id = GLOBAL_CMD_ID_CLOSE_SESSION;
+ params.flags = TC_CALL_GLOBAL | TC_CALL_SYNC;
+ ret = tc_client_call(¶ms);
+ if (ret)
+ tloge("close session failed, ret=0x%x\n", ret);
+
+ return ret;
+}
+
+static void close_session_in_service_list(struct tc_ns_dev_file *dev,
+ struct tc_ns_service *service)
+{
+ struct tc_ns_session *tmp_session = NULL;
+ struct tc_ns_session *session = NULL;
+ int ret;
+
+ list_for_each_entry_safe(session, tmp_session,
+ &service->session_list, head) {
+ if (session->owner != dev)
+ continue;
+ ret = close_session(dev, session, service->uuid,
+ (unsigned int)UUID_LEN, session->session_id);
+ if (ret)
+ tloge("close session smc failed when close fd!\n");
+ mutex_lock(&service->session_lock);
+ list_del(&session->head);
+ mutex_unlock(&service->session_lock);
+
+ put_session_struct(session); /* pair with open session */
+ }
+}
+
+static bool if_exist_unclosed_session(struct tc_ns_dev_file *dev)
+{
+ uint32_t index;
+
+ for (index = 0; index < SERVICES_MAX_COUNT; index++) {
+ if (dev->services[index] != NULL &&
+ !list_empty(&dev->services[index]->session_list))
+ return true;
+ }
+ return false;
+}
+
+static int close_session_thread_fn(void *arg)
+{
+ struct tc_ns_dev_file *dev = arg;
+ uint32_t index;
+ struct tc_ns_service *service = NULL;
+
+ /* close unclosed session */
+ for (index = 0; index < SERVICES_MAX_COUNT; index++) {
+ if (dev->services[index] != NULL &&
+ !list_empty(&dev->services[index]->session_list)) {
+ service = dev->services[index];
+
+ mutex_lock(&service->operation_lock);
+ close_session_in_service_list(dev, service);
+ mutex_unlock(&service->operation_lock);
+
+ put_service_struct(service); /* pair with open session */
+ }
+ }
+
+ tlogd("complete close all unclosed session\n");
+ complete(&dev->close_comp);
+ return 0;
+}
+
+void close_unclosed_session_in_kthread(struct tc_ns_dev_file *dev)
+{
+ struct task_struct *close_thread = NULL;
+
+ if (!dev) {
+ tloge("dev is invalid\n");
+ return;
+ }
+
+ if (!if_exist_unclosed_session(dev))
+ return;
+
+ close_thread = kthread_create(close_session_thread_fn,
+ dev, "close_fn_%6d", dev->dev_file_id);
+ if (unlikely(IS_ERR_OR_NULL(close_thread))) {
+ tloge("fail to create close session thread\n");
+ return;
+ }
+
+ tz_kthread_bind_mask(close_thread);
+ wake_up_process(close_thread);
+ wait_for_completion(&dev->close_comp);
+ tlogd("wait for completion success\n");
+}
+
+int tc_ns_close_session(struct tc_ns_dev_file *dev_file,
+ const struct tc_ns_client_context *context)
+{
+ int ret = -EINVAL;
+ struct tc_ns_service *service = NULL;
+ struct tc_ns_session *session = NULL;
+
+ if (!dev_file || !context) {
+ tloge("invalid dev_file or context\n");
+ return ret;
+ }
+ service = get_service(dev_file, context);
+ if (!service) {
+ tloge("invalid service\n");
+ return ret;
+ }
+ /*
+ * session_id in tee is unique, but in concurrency scene
+ * same session_id may appear in tzdriver, put session_list
+ * add/del in service->operation_lock can avoid it.
+ */
+ mutex_lock(&service->operation_lock);
+ session = get_session(service, dev_file, context);
+ if (session) {
+ int ret2;
+ mutex_lock(&session->ta_session_lock);
+ ret2 = close_session(dev_file, session, context->uuid,
+ (unsigned int)UUID_LEN, context->session_id);
+ mutex_unlock(&session->ta_session_lock);
+ if (ret2)
+ tloge("close session smc failed!\n");
+ mutex_lock(&service->session_lock);
+ list_del(&session->head);
+ mutex_unlock(&service->session_lock);
+
+ put_session_struct(session);
+ put_session_struct(session); /* pair with open session */
+
+ ret = 0;
+ mutex_lock(&dev_file->service_lock);
+ del_service_from_dev(dev_file, service);
+ mutex_unlock(&dev_file->service_lock);
+ } else {
+ tloge("invalid session\n");
+ }
+ mutex_unlock(&service->operation_lock);
+ put_service_struct(service);
+ return ret;
+}
+
+int tc_ns_send_cmd(struct tc_ns_dev_file *dev_file,
+ struct tc_ns_client_context *context)
+{
+ int ret = -EINVAL;
+ struct tc_ns_service *service = NULL;
+ struct tc_ns_session *session = NULL;
+ struct tc_call_params params = {
+ dev_file, context, NULL, 0
+ };
+
+ if (!dev_file || !context) {
+ tloge("invalid dev_file or context\n");
+ return ret;
+ }
+
+ service = get_service(dev_file, context);
+ if (service) {
+ session = get_session(service, dev_file, context);
+ put_service_struct(service);
+ if (session) {
+ tlogd("send cmd find session id %x\n",
+ context->session_id);
+ goto find_session;
+ }
+ tloge("can't find session\n");
+ } else {
+ tloge("can't find service\n");
+ }
+
+ return ret;
+find_session:
+ mutex_lock(&session->ta_session_lock);
+ params.sess = session;
+ ret = tc_client_call(¶ms);
+ mutex_unlock(&session->ta_session_lock);
+ put_session_struct(session);
+ if (ret)
+ tloge("smc call returns error, ret=0x%x\n", ret);
+ return ret;
+}
+
+static int ioctl_session_send_cmd(struct tc_ns_dev_file *dev_file,
+ struct tc_ns_client_context *context, void *argp)
+{
+ int ret;
+
+ ret = tc_ns_send_cmd(dev_file, context);
+ if (ret)
+ tloge("send cmd failed ret is %d\n", ret);
+ if (copy_to_user(argp, context, sizeof(*context))) {
+ if (!ret)
+ ret = -EFAULT;
+ }
+ return ret;
+}
+
+int tc_client_session_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ int ret = -EINVAL;
+ void *argp = (void __user *)(uintptr_t)arg;
+ struct tc_ns_dev_file *dev_file = NULL;
+ struct tc_ns_client_context context;
+
+ if (!argp || !file) {
+ tloge("invalid params\n");
+ return -EINVAL;
+ }
+
+ dev_file = file->private_data;
+ if (copy_from_user(&context, argp, sizeof(context))) {
+ tloge("copy from user failed\n");
+ return -EFAULT;
+ }
+
+ context.returns.origin = TEEC_ORIGIN_COMMS;
+ freezer_do_not_count();
+ switch (cmd) {
+ case TC_NS_CLIENT_IOCTL_SES_OPEN_REQ:
+ ret = tc_ns_open_session(dev_file, &context);
+ if (ret)
+ tloge("open session failed ret is %d\n", ret);
+ if (copy_to_user(argp, &context, sizeof(context)) && !ret)
+ ret = -EFAULT;
+ break;
+ case TC_NS_CLIENT_IOCTL_SES_CLOSE_REQ:
+ ret = tc_ns_close_session(dev_file, &context);
+ break;
+ case TC_NS_CLIENT_IOCTL_SEND_CMD_REQ:
+ ret = ioctl_session_send_cmd(dev_file, &context, argp);
+ break;
+ default:
+ freezer_count();
+ tloge("invalid cmd:0x%x!\n", cmd);
+ return ret;
+ }
+ freezer_count();
+ /*
+ * Don't leak ERESTARTSYS to user space.
+ *
+ * CloseSession is not reentrant, so convert to -EINTR.
+ * In other case, restart_syscall().
+ *
+ * It is better to call it right after the error code
+ * is generated (in tc_client_call), but kernel CAs are
+ * still exist when these words are written. Setting TIF
+ * flags for callers of those CAs is very hard to analysis.
+ *
+ * For kernel CA, when ERESTARTSYS is seen, loop in kernel
+ * instead of notifying user.
+ *
+ * P.S. ret code in this function is in mixed naming space.
+ * See the definition of ret. However, this function never
+ * return its default value, so using -EXXX is safe.
+ */
+ if (ret == -ERESTARTSYS) {
+ if (cmd == TC_NS_CLIENT_IOCTL_SES_CLOSE_REQ)
+ ret = -EINTR;
+ else
+ return restart_syscall();
+ }
+ return ret;
+}
diff --git a/linux/core/session_manager.h b/linux/core/session_manager.h
new file mode 100644
index 0000000..6e16479
--- /dev/null
+++ b/linux/core/session_manager.h
@@ -0,0 +1,53 @@
+/*
+ * session_manager.h
+ *
+ * function declaration for session management
+ *
+ * Copyright (C) 2022 Huawei Technologies Co., Ltd.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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 SESSION_MANAGER_H
+#define SESSION_MANAGER_H
+
+#include
+#include "tc_ns_client.h"
+#include "teek_ns_client.h"
+
+int tc_client_session_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg);
+int tc_ns_open_session(struct tc_ns_dev_file *dev_file,
+ struct tc_ns_client_context *context);
+int tc_ns_close_session(struct tc_ns_dev_file *dev_file,
+ const struct tc_ns_client_context *context);
+int tc_ns_send_cmd(struct tc_ns_dev_file *dev_file,
+ struct tc_ns_client_context *context);
+int tc_ns_load_image(struct tc_ns_dev_file *dev, const char *file_buffer,
+ unsigned int file_size, struct tc_ns_client_return *tee_ret, enum secfile_type_t type);
+int tc_ns_load_image_with_lock(struct tc_ns_dev_file *dev,
+ const char *buffer, unsigned int file_size, enum secfile_type_t type);
+void close_unclosed_session_in_kthread(struct tc_ns_dev_file *dev);
+struct tc_ns_session *tc_find_session_by_uuid(unsigned int dev_file_id,
+ const struct tc_ns_smc_cmd *cmd);
+struct tc_ns_service *tc_find_service_in_dev(const struct tc_ns_dev_file *dev,
+ const unsigned char *uuid, int uuid_size);
+struct tc_ns_session *tc_find_session_withowner(
+ const struct list_head *session_list, unsigned int session_id,
+ struct tc_ns_dev_file *dev_file);
+int tc_ns_load_secfile(struct tc_ns_dev_file *dev_file,
+ const void __user *argp);
+void get_service_struct(struct tc_ns_service *service);
+void put_service_struct(struct tc_ns_service *service);
+void get_session_struct(struct tc_ns_session *session);
+void put_session_struct(struct tc_ns_session *session);
+void dump_services_status(const char *param);
+void init_srvc_list(void);
+
+#endif
diff --git a/linux/core/smc_smp.c b/linux/core/smc_smp.c
new file mode 100644
index 0000000..f155a54
--- /dev/null
+++ b/linux/core/smc_smp.c
@@ -0,0 +1,2042 @@
+/*
+ * smc_smp.c
+ *
+ * function for sending smc cmd
+ *
+ * Copyright (C) 2022 Huawei Technologies Co., Ltd.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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 "smc_smp.h"
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#if (KERNEL_VERSION(4, 14, 0) <= LINUX_VERSION_CODE)
+#include
+#include
+#endif
+#include
+#include
+
+#ifdef CONFIG_TEE_AUDIT
+#include
+#endif
+
+#include "tc_ns_log.h"
+#include "teek_client_constants.h"
+#include "tc_ns_client.h"
+#include "agent.h"
+#include "teek_ns_client.h"
+#include "mailbox_mempool.h"
+#include "cmdmonitor.h"
+#include "tlogger.h"
+#include "ko_adapt.h"
+#include "log_cfg_api.h"
+#include "tz_kthread_affinity.h"
+#include "tee_compat_check.h"
+
+#define PREEMPT_COUNT 10000
+#define HZ_COUNT 10
+#define IDLED_COUNT 100
+/*
+ * when cannot find smc entry, will sleep 1ms
+ * because the task will be killed in 25s if it not return,
+ * so the retry count is 25s/1ms
+ */
+#define FIND_SMC_ENTRY_SLEEP 1
+#define FIND_SMC_ENTRY_RETRY_MAX_COUNT (CMD_MAX_EXECUTE_TIME * S_TO_MS / FIND_SMC_ENTRY_SLEEP)
+
+#define CPU_ZERO 0
+#define CPU_ONE 1
+#define CPU_FOUR 4
+#define CPU_FIVE 5
+#define CPU_SIX 6
+#define CPU_SEVEN 7
+#define LOW_BYTE 0xF
+
+#define PENDING2_RETRY (-1)
+
+#define RETRY_WITH_PM 1
+#define CLEAN_WITHOUT_PM 2
+
+#define MAX_CHAR 0xff
+
+#define MAX_SIQ_NUM 4
+
+/* Current state of the system */
+static uint8_t g_sys_crash;
+
+struct smc_in_params {
+ unsigned long x0;
+ unsigned long x1;
+ unsigned long x2;
+ unsigned long x3;
+ unsigned long x4;
+};
+
+struct smc_out_params {
+ unsigned long ret;
+ unsigned long exit_reason;
+ unsigned long ta;
+ unsigned long target;
+};
+
+struct shadow_work {
+ struct kthread_work kthwork;
+ struct work_struct work;
+ uint64_t target;
+};
+
+unsigned long g_shadow_thread_id = 0;
+static struct task_struct *g_siq_thread;
+static struct task_struct *g_smc_svc_thread;
+static struct task_struct *g_ipi_helper_thread;
+static DEFINE_KTHREAD_WORKER(g_ipi_helper_worker);
+
+enum cmd_reuse {
+ CLEAR, /* clear this cmd index */
+ RESEND, /* use this cmd index resend */
+};
+
+struct cmd_reuse_info {
+ int cmd_index;
+ int saved_index;
+ enum cmd_reuse cmd_usage;
+};
+
+#if CONFIG_CPU_AFF_NR
+static struct cpumask g_cpu_mask;
+static int g_mask_flag = 0;
+#endif
+
+#ifdef CONFIG_DRM_ADAPT
+static struct cpumask g_drm_cpu_mask;
+static int g_drm_mask_flag = 0;
+#endif
+
+struct tc_ns_smc_queue *g_cmd_data;
+phys_addr_t g_cmd_phys;
+
+static struct list_head g_pending_head;
+static spinlock_t g_pend_lock;
+
+static DECLARE_WAIT_QUEUE_HEAD(siq_th_wait);
+static DECLARE_WAIT_QUEUE_HEAD(ipi_th_wait);
+static atomic_t g_siq_th_run;
+static uint32_t g_siq_queue[MAX_SIQ_NUM];
+DEFINE_MUTEX(g_siq_lock);
+
+enum {
+ TYPE_CRASH_TA = 1,
+ TYPE_CRASH_TEE = 2,
+};
+
+enum smc_ops_exit {
+ SMC_OPS_NORMAL = 0x0,
+ SMC_OPS_SCHEDTO = 0x1,
+ SMC_OPS_START_SHADOW = 0x2,
+ SMC_OPS_START_FIQSHD = 0x3,
+ SMC_OPS_PROBE_ALIVE = 0x4,
+ SMC_OPS_ABORT_TASK = 0x5,
+ SMC_EXIT_NORMAL = 0x0,
+ SMC_EXIT_PREEMPTED = 0x1,
+ SMC_EXIT_SHADOW = 0x2,
+ SMC_EXIT_ABORT = 0x3,
+ SMC_EXIT_MAX = 0x4,
+};
+
+#define SHADOW_EXIT_RUN 0x1234dead
+#define SMC_EXIT_TARGET_SHADOW_EXIT 0x1
+
+#define SYM_NAME_LEN_MAX 16
+#define SYM_NAME_LEN_1 7
+#define SYM_NAME_LEN_2 4
+#define CRASH_REG_NUM 3
+#define LOW_FOUR_BITE 4
+
+union crash_inf {
+ uint64_t crash_reg[CRASH_REG_NUM];
+ struct {
+ uint8_t halt_reason : LOW_FOUR_BITE;
+ uint8_t app : LOW_FOUR_BITE;
+ char sym_name[SYM_NAME_LEN_1];
+ uint16_t off;
+ uint16_t size;
+ uint32_t far;
+ uint32_t fault;
+ union {
+ char sym_name_append[SYM_NAME_LEN_2];
+ uint32_t elr;
+ };
+ } crash_msg;
+};
+
+#define compile_time_assert(cond, msg) typedef char ASSERT_##msg[(cond) ? 1 : -1]
+
+#ifndef CONFIG_BIG_SESSION
+compile_time_assert(sizeof(struct tc_ns_smc_queue) <= PAGE_SIZE,
+ size_of_tc_ns_smc_queue_too_large);
+#endif
+
+static void acquire_smc_buf_lock(smc_buf_lock_t *lock)
+{
+ int ret;
+
+ preempt_disable();
+ do
+ ret = cmpxchg(lock, 0, 1);
+ while (ret);
+}
+
+static inline void release_smc_buf_lock(smc_buf_lock_t *lock)
+{
+ (void)cmpxchg(lock, 1, 0);
+ preempt_enable();
+}
+
+static void occupy_setbit_smc_in_doing_entry(int32_t i, int32_t *idx)
+{
+ g_cmd_data->in[i].event_nr = i;
+ isb();
+ wmb();
+ set_bit(i, (unsigned long *)g_cmd_data->in_bitmap);
+ set_bit(i, (unsigned long *)g_cmd_data->doing_bitmap);
+ *idx = i;
+}
+
+static void occupy_clean_in_doing_entry(int32_t i)
+{
+ acquire_smc_buf_lock(&g_cmd_data->smc_lock);
+ clear_bit(i, (unsigned long *)g_cmd_data->in_bitmap);
+ clear_bit(i, (unsigned long *)g_cmd_data->doing_bitmap);
+ release_smc_buf_lock(&g_cmd_data->smc_lock);
+}
+
+static int occupy_free_smc_in_entry(const struct tc_ns_smc_cmd *cmd)
+{
+ int idx = -1;
+ int i;
+ uint32_t retry_count = 0;
+
+ if (!cmd) {
+ tloge("bad parameters! cmd is NULL\n");
+ return -1;
+ }
+ /*
+ * Note:
+ * acquire_smc_buf_lock will disable preempt and kernel will forbid
+ * call mutex_lock in preempt disabled scenes.
+ * To avoid such case(update_timestamp and update_chksum will call
+ * mutex_lock), only cmd copy is done when preempt is disable,
+ * then do update_timestamp and update_chksum.
+ * As soon as this idx of in_bitmap is set, gtask will see this
+ * cmd_in, but the cmd_in is not ready that lack of update_xxx,
+ * so we make a tricky here, set doing_bitmap and in_bitmap both
+ * at first, after update_xxx is done, clear doing_bitmap.
+ */
+get_smc_retry:
+ acquire_smc_buf_lock(&g_cmd_data->smc_lock);
+ for (i = 0; i < MAX_SMC_CMD; i++) {
+ if (test_bit(i, (unsigned long *)g_cmd_data->in_bitmap))
+ continue;
+ if (memcpy_s(&g_cmd_data->in[i], sizeof(g_cmd_data->in[i]),
+ cmd, sizeof(*cmd)) != EOK) {
+ tloge("memcpy failed,%s line:%d", __func__, __LINE__);
+ break;
+ }
+ occupy_setbit_smc_in_doing_entry(i, &idx);
+ break;
+ }
+ release_smc_buf_lock(&g_cmd_data->smc_lock);
+ if (idx == -1) {
+ if (retry_count <= FIND_SMC_ENTRY_RETRY_MAX_COUNT) {
+ msleep(FIND_SMC_ENTRY_SLEEP);
+ retry_count++;
+ tlogd("can't get any free smc entry and retry:%u\n", retry_count);
+ goto get_smc_retry;
+ }
+ tloge("can't get any free smc entry after retry:%u\n", retry_count);
+ return -1;
+ }
+
+ acquire_smc_buf_lock(&g_cmd_data->smc_lock);
+ isb();
+ wmb();
+ clear_bit(idx, (unsigned long *)g_cmd_data->doing_bitmap);
+ release_smc_buf_lock(&g_cmd_data->smc_lock);
+ return idx;
+}
+
+static int reuse_smc_in_entry(uint32_t idx)
+{
+ int rc = 0;
+
+ acquire_smc_buf_lock(&g_cmd_data->smc_lock);
+ if (!(test_bit(idx, (unsigned long *)g_cmd_data->in_bitmap) &&
+ test_bit(idx, (unsigned long *)g_cmd_data->doing_bitmap))) {
+ tloge("invalid cmd to reuse\n");
+ rc = -1;
+ goto out;
+ }
+ if (memcpy_s(&g_cmd_data->in[idx], sizeof(g_cmd_data->in[idx]),
+ &g_cmd_data->out[idx], sizeof(g_cmd_data->out[idx]))) {
+ tloge("memcpy failed,%s line:%d", __func__, __LINE__);
+ rc = -1;
+ goto out;
+ }
+ release_smc_buf_lock(&g_cmd_data->smc_lock);
+ acquire_smc_buf_lock(&g_cmd_data->smc_lock);
+ isb();
+ wmb();
+ clear_bit(idx, (unsigned long *)g_cmd_data->doing_bitmap);
+out:
+ release_smc_buf_lock(&g_cmd_data->smc_lock);
+ return rc;
+}
+
+static int copy_smc_out_entry(uint32_t idx, struct tc_ns_smc_cmd *copy,
+ enum cmd_reuse *usage)
+{
+ acquire_smc_buf_lock(&g_cmd_data->smc_lock);
+ if (!test_bit(idx, (unsigned long *)g_cmd_data->out_bitmap)) {
+ tloge("cmd out %u is not ready\n", idx);
+ release_smc_buf_lock(&g_cmd_data->smc_lock);
+ show_cmd_bitmap();
+ return -ENOENT;
+ }
+ if (memcpy_s(copy, sizeof(*copy), &g_cmd_data->out[idx],
+ sizeof(g_cmd_data->out[idx]))) {
+ tloge("copy smc out failed\n");
+ release_smc_buf_lock(&g_cmd_data->smc_lock);
+ return -EFAULT;
+ }
+
+ isb();
+ wmb();
+ if (g_cmd_data->out[idx].ret_val == TEEC_PENDING2 ||
+ g_cmd_data->out[idx].ret_val == TEEC_PENDING) {
+ *usage = RESEND;
+ } else {
+ clear_bit(idx, (unsigned long *)g_cmd_data->in_bitmap);
+ clear_bit(idx, (unsigned long *)g_cmd_data->doing_bitmap);
+ *usage = CLEAR;
+ }
+ clear_bit(idx, (unsigned long *)g_cmd_data->out_bitmap);
+ release_smc_buf_lock(&g_cmd_data->smc_lock);
+
+ return 0;
+}
+
+static inline void clear_smc_in_entry(uint32_t idx)
+{
+ acquire_smc_buf_lock(&g_cmd_data->smc_lock);
+ clear_bit(idx, (unsigned long *)g_cmd_data->in_bitmap);
+ release_smc_buf_lock(&g_cmd_data->smc_lock);
+}
+
+static void release_smc_entry(uint32_t idx)
+{
+ acquire_smc_buf_lock(&g_cmd_data->smc_lock);
+ clear_bit(idx, (unsigned long *)g_cmd_data->in_bitmap);
+ clear_bit(idx, (unsigned long *)g_cmd_data->doing_bitmap);
+ clear_bit(idx, (unsigned long *)g_cmd_data->out_bitmap);
+ release_smc_buf_lock(&g_cmd_data->smc_lock);
+}
+
+static bool is_cmd_working_done(uint32_t idx)
+{
+ bool ret = false;
+
+ acquire_smc_buf_lock(&g_cmd_data->smc_lock);
+ if (test_bit(idx, (unsigned long *)g_cmd_data->out_bitmap))
+ ret = true;
+ release_smc_buf_lock(&g_cmd_data->smc_lock);
+ return ret;
+}
+
+static void show_in_bitmap(int *cmd_in, uint32_t len)
+{
+ uint32_t idx;
+ uint32_t in = 0;
+ char bitmap[MAX_SMC_CMD + 1];
+
+ if (len != MAX_SMC_CMD || !g_cmd_data)
+ return;
+
+ for (idx = 0; idx < MAX_SMC_CMD; idx++) {
+ if (test_bit(idx, (unsigned long *)g_cmd_data->in_bitmap)) {
+ bitmap[idx] = '1';
+ cmd_in[in++] = idx;
+ } else {
+ bitmap[idx] = '0';
+ }
+ }
+ bitmap[MAX_SMC_CMD] = '\0';
+ tloge("in bitmap: %s\n", bitmap);
+}
+
+static void show_out_bitmap(int *cmd_out, uint32_t len)
+{
+ uint32_t idx;
+ uint32_t out = 0;
+ char bitmap[MAX_SMC_CMD + 1];
+
+ if (len != MAX_SMC_CMD || !g_cmd_data)
+ return;
+
+ for (idx = 0; idx < MAX_SMC_CMD; idx++) {
+ if (test_bit(idx, (unsigned long *)g_cmd_data->out_bitmap)) {
+ bitmap[idx] = '1';
+ cmd_out[out++] = idx;
+ } else {
+ bitmap[idx] = '0';
+ }
+ }
+ bitmap[MAX_SMC_CMD] = '\0';
+ tloge("out bitmap: %s\n", bitmap);
+}
+
+static void show_doing_bitmap(void)
+{
+ uint32_t idx;
+ char bitmap[MAX_SMC_CMD + 1];
+
+ if (!g_cmd_data)
+ return;
+ for (idx = 0; idx < MAX_SMC_CMD; idx++) {
+ if (test_bit(idx, (unsigned long *)g_cmd_data->doing_bitmap))
+ bitmap[idx] = '1';
+ else
+ bitmap[idx] = '0';
+ }
+ bitmap[MAX_SMC_CMD] = '\0';
+ tloge("doing bitmap: %s\n", bitmap);
+}
+
+static void show_single_cmd_info(int *cmd, uint32_t len)
+{
+ uint32_t idx;
+
+ if (len != MAX_SMC_CMD || !g_cmd_data)
+ return;
+
+ for (idx = 0; idx < MAX_SMC_CMD; idx++) {
+ if (cmd[idx] == -1)
+ break;
+ tloge("cmd[%d]: cmd_id=%u, ca_pid=%u, dev_id = 0x%x, "
+ "event_nr=%u, ret_val=0x%x\n",
+ cmd[idx],
+ g_cmd_data->in[cmd[idx]].cmd_id,
+ g_cmd_data->in[cmd[idx]].ca_pid,
+ g_cmd_data->in[cmd[idx]].dev_file_id,
+ g_cmd_data->in[cmd[idx]].event_nr,
+ g_cmd_data->in[cmd[idx]].ret_val);
+ }
+}
+
+void show_cmd_bitmap(void)
+{
+ int *cmd_in = NULL;
+ int *cmd_out = NULL;
+
+ cmd_in = kzalloc(sizeof(int) * MAX_SMC_CMD, GFP_KERNEL);
+ if (ZERO_OR_NULL_PTR((unsigned long)(uintptr_t)cmd_in)) {
+ tloge("out of mem! cannot show in bitmap\n");
+ return;
+ }
+
+ cmd_out = kzalloc(sizeof(int) * MAX_SMC_CMD, GFP_KERNEL);
+ if (ZERO_OR_NULL_PTR((unsigned long)(uintptr_t)cmd_out)) {
+ kfree(cmd_in);
+ tloge("out of mem! cannot show out bitmap\n");
+ return;
+ }
+
+ if (memset_s(cmd_in, sizeof(int)* MAX_SMC_CMD, MAX_CHAR, sizeof(int)* MAX_SMC_CMD) ||
+ memset_s(cmd_out, sizeof(int)* MAX_SMC_CMD, MAX_CHAR, sizeof(int)* MAX_SMC_CMD)) {
+ tloge("memset failed\n");
+ goto error;
+ }
+
+ acquire_smc_buf_lock(&g_cmd_data->smc_lock);
+
+ show_in_bitmap(cmd_in, MAX_SMC_CMD);
+ show_doing_bitmap();
+ show_out_bitmap(cmd_out, MAX_SMC_CMD);
+
+ tloge("cmd in value:\n");
+ show_single_cmd_info(cmd_in, MAX_SMC_CMD);
+
+ tloge("cmd_out value:\n");
+ show_single_cmd_info(cmd_out, MAX_SMC_CMD);
+
+ release_smc_buf_lock(&g_cmd_data->smc_lock);
+
+error:
+ kfree(cmd_in);
+ kfree(cmd_out);
+}
+
+static struct pending_entry *init_pending_entry(void)
+{
+ struct pending_entry *pe = NULL;
+
+ pe = kzalloc(sizeof(*pe), GFP_KERNEL);
+ if (ZERO_OR_NULL_PTR((unsigned long)(uintptr_t)pe)) {
+ tloge("alloc pe failed\n");
+ return NULL;
+ }
+
+ atomic_set(&pe->users, 1);
+ get_task_struct(current);
+ pe->task = current;
+
+#ifdef CONFIG_TA_AFFINITY
+ cpumask_copy(&pe->ca_mask, CURRENT_CPUS_ALLOWED);
+ cpumask_copy(&pe->ta_mask, CURRENT_CPUS_ALLOWED);
+#endif
+
+ init_waitqueue_head(&pe->wq);
+ atomic_set(&pe->run, 0);
+ INIT_LIST_HEAD(&pe->list);
+ spin_lock(&g_pend_lock);
+ list_add_tail(&pe->list, &g_pending_head);
+ spin_unlock(&g_pend_lock);
+
+ return pe;
+}
+
+struct pending_entry *find_pending_entry(pid_t pid)
+{
+ struct pending_entry *pe = NULL;
+
+ spin_lock(&g_pend_lock);
+ list_for_each_entry(pe, &g_pending_head, list) {
+ if (pe->task->pid == pid) {
+ atomic_inc(&pe->users);
+ spin_unlock(&g_pend_lock);
+ return pe;
+ }
+ }
+ spin_unlock(&g_pend_lock);
+ return NULL;
+}
+
+void foreach_pending_entry(void (*func)(struct pending_entry *))
+{
+ struct pending_entry *pe = NULL;
+
+ if (!func)
+ return;
+
+ spin_lock(&g_pend_lock);
+ list_for_each_entry(pe, &g_pending_head, list) {
+ func(pe);
+ }
+ spin_unlock(&g_pend_lock);
+}
+
+void put_pending_entry(struct pending_entry *pe)
+{
+ if (!pe)
+ return;
+
+ if (!atomic_dec_and_test(&pe->users))
+ return;
+
+ put_task_struct(pe->task);
+ kfree(pe);
+}
+
+#ifdef CONFIG_TA_AFFINITY
+static void restore_cpu_mask(struct pending_entry *pe)
+{
+ if (cpumask_equal(&pe->ca_mask, &pe->ta_mask))
+ return;
+
+ set_cpus_allowed_ptr(current, &pe->ca_mask);
+}
+#endif
+
+static void release_pending_entry(struct pending_entry *pe)
+{
+#ifdef CONFIG_TA_AFFINITY
+ restore_cpu_mask(pe);
+#endif
+ spin_lock(&g_pend_lock);
+ list_del(&pe->list);
+ spin_unlock(&g_pend_lock);
+ put_pending_entry(pe);
+}
+
+static inline bool is_shadow_exit(uint64_t target)
+{
+ return target & SMC_EXIT_TARGET_SHADOW_EXIT;
+}
+
+/*
+ * check ca and ta's affinity is match in 2 scene:
+ * 1. when TA is blocked to REE
+ * 2. when CA is wakeup by SPI wakeup
+ * match_ta_affinity return true if affinity is changed
+ */
+#ifdef CONFIG_TA_AFFINITY
+static bool match_ta_affinity(struct pending_entry *pe)
+{
+ if (!cpumask_equal(CURRENT_CPUS_ALLOWED, &pe->ta_mask)) {
+ if (set_cpus_allowed_ptr(current, &pe->ta_mask)) {
+ tlogw("set %s affinity failed\n", current->comm);
+ return false;
+ }
+ return true;
+ }
+
+ return false;
+}
+#else
+static inline bool match_ta_affinity(struct pending_entry *pe)
+{
+ return false;
+}
+#endif
+
+struct smc_cmd_ret {
+ unsigned long exit;
+ unsigned long ta;
+ unsigned long target;
+};
+
+bool sigkill_pending(struct task_struct *tsk)
+{
+ bool flag = false;
+
+ if (!tsk) {
+ tloge("tsk is null!\n");
+ return false;
+ }
+
+ flag = sigismember(&tsk->pending.signal, SIGKILL) ||
+ sigismember(&tsk->pending.signal, SIGUSR1);
+
+ if (tsk->signal)
+ return flag || sigismember(&tsk->signal->shared_pending.signal,
+ SIGKILL);
+ return flag;
+}
+
+#if CONFIG_CPU_AFF_NR
+static void set_cpu_strategy(struct cpumask *old_mask)
+{
+ unsigned int i;
+
+ if (!g_mask_flag) {
+ cpumask_clear(&g_cpu_mask);
+ for (i = 0; i < CONFIG_CPU_AFF_NR; i++)
+ cpumask_set_cpu(i, &g_cpu_mask);
+ g_mask_flag = 1;
+ }
+ cpumask_copy(old_mask, CURRENT_CPUS_ALLOWED);
+ set_cpus_allowed_ptr(current, &g_cpu_mask);
+}
+#endif
+
+#if CONFIG_CPU_AFF_NR
+static void restore_cpu(struct cpumask *old_mask)
+{
+ /* current equal old means no set cpu affinity, no need to restore */
+ if (cpumask_equal(CURRENT_CPUS_ALLOWED, old_mask))
+ return;
+
+ set_cpus_allowed_ptr(current, old_mask);
+ schedule();
+}
+#endif
+
+static bool is_ready_to_kill(bool need_kill)
+{
+ return (need_kill && sigkill_pending(current) &&
+ is_thread_reported(current->pid));
+}
+
+static void set_smc_send_arg(struct smc_in_params *in_param,
+ const struct smc_cmd_ret *secret, unsigned long ops)
+{
+ if (secret->exit == SMC_EXIT_PREEMPTED) {
+ in_param->x1 = SMC_OPS_SCHEDTO;
+ in_param->x3 = secret->ta;
+ in_param->x4 = secret->target;
+ }
+
+ if (ops == SMC_OPS_SCHEDTO || ops == SMC_OPS_START_FIQSHD)
+ in_param->x4 = secret->target;
+
+ tlogd("[cpu %d]begin send x0=%lx x1=%lx x2=%lx x3=%lx x4=%lx\n",
+ raw_smp_processor_id(), in_param->x0, in_param->x1,
+ in_param->x2, in_param->x3, in_param->x4);
+}
+
+#ifndef CONFIG_ARM
+static void send_asm_smc_cmd(struct smc_in_params *in_param,
+ struct smc_out_params *out_param)
+{
+ do {
+ asm volatile(
+ "mov x0, %[fid]\n"
+ "mov x1, %[a1]\n"
+ "mov x2, %[a2]\n"
+ "mov x3, %[a3]\n"
+ "mov x4, %[a4]\n"
+ "smc #0\n"
+ "str x0, [%[re0]]\n"
+ "str x1, [%[re1]]\n"
+ "str x2, [%[re2]]\n"
+ "str x3, [%[re3]]\n" :
+ [fid] "+r"(in_param->x0),
+ [a1] "+r"(in_param->x1),
+ [a2] "+r"(in_param->x2),
+ [a3] "+r"(in_param->x3),
+ [a4] "+r"(in_param->x4) :
+ [re0] "r"(&out_param->ret),
+ [re1] "r"(&out_param->exit_reason),
+ [re2] "r"(&out_param->ta),
+ [re3] "r"(&out_param->target) :
+ "x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7",
+ "x8", "x9", "x10", "x11", "x12", "x13",
+ "x14", "x15", "x16", "x17");
+ } while (0);
+}
+#else
+static void send_asm_smc_cmd(struct smc_in_params *in_param,
+ struct smc_out_params *out_param)
+{
+ do {
+ asm volatile(
+ "mov r0, %[fid]\n"
+ "mov r1, %[a1]\n"
+ "mov r2, %[a2]\n"
+ "mov r3, %[a3]\n"
+ ".arch_extension sec\n"
+ "smc #0\n"
+ "str r0, [%[re0]]\n"
+ "str r1, [%[re1]]\n"
+ "str r2, [%[re2]]\n"
+ "str r3, [%[re3]]\n" :
+ [fid] "+r"(in_param->x0),
+ [a1] "+r"(in_param->x1),
+ [a2] "+r"(in_param->x2),
+ [a3] "+r"(in_param->x3):
+ [re0] "r"(&out_param->ret),
+ [re1] "r"(&out_param->exit_reason),
+ [re2] "r"(&out_param->ta),
+ [re3] "r"(&out_param->target) :
+ "r0", "r1", "r2", "r3");
+ } while (0);
+}
+#endif
+
+static noinline int smp_smc_send(uint32_t cmd, unsigned long ops, unsigned long ca,
+ struct smc_cmd_ret *secret, bool need_kill)
+{
+ struct smc_in_params in_param = { cmd, ops, ca, 0, 0 };
+ struct smc_out_params out_param = {0};
+#if CONFIG_CPU_AFF_NR
+ struct cpumask old_mask;
+#endif
+
+#if CONFIG_CPU_AFF_NR
+ set_cpu_strategy(&old_mask);
+#endif
+retry:
+ set_smc_send_arg(&in_param, secret, ops);
+ isb();
+ wmb();
+ send_asm_smc_cmd(&in_param, &out_param);
+ isb();
+ wmb();
+ tlogd("[cpu %d] return val %lx exit_reason %lx ta %lx targ %lx\n",
+ raw_smp_processor_id(), out_param.ret, out_param.exit_reason,
+ out_param.ta, out_param.target);
+
+ secret->exit = out_param.exit_reason;
+ secret->ta = out_param.ta;
+ secret->target = out_param.target;
+
+ if (out_param.exit_reason == SMC_EXIT_PREEMPTED) {
+ /*
+ * There's 2 ways to send a terminate cmd to kill a running TA,
+ * in current context or another. If send terminate in another
+ * context, may encounter concurrency problem, as terminate cmd
+ * is send but not process, the original cmd has finished.
+ * So we send the terminate cmd in current context.
+ */
+ if (is_ready_to_kill(need_kill)) {
+ secret->exit = SMC_EXIT_ABORT;
+ tloge("receive kill signal\n");
+ } else {
+#if (!defined(CONFIG_PREEMPT)) || defined(CONFIG_RTOS_PREEMPT_OFF)
+ /* yield cpu to avoid soft lockup */
+ cond_resched();
+#endif
+ goto retry;
+ }
+ }
+#if CONFIG_CPU_AFF_NR
+ restore_cpu(&old_mask);
+#endif
+ return out_param.ret;
+}
+
+#ifndef CONFIG_ARM
+static uint64_t send_smc_cmd(uint32_t cmd, phys_addr_t cmd_addr,
+ uint32_t cmd_type, uint8_t wait)
+{
+ struct smc_in_params in_param = { cmd, cmd_addr, cmd_type, cmd_addr >> ADDR_TRANS_NUM };
+ uint64_t ret = 0;
+
+ do {
+ asm volatile(
+ "mov x0, %[fid]\n"
+ "mov x1, %[a1]\n"
+ "mov x2, %[a2]\n"
+ "mov x3, %[a3]\n"
+ "mov x4, %[a4]\n"
+ "smc #0\n"
+ "str x0, [%[re0]]\n":
+ [fid] "+r"(in_param.x0),
+ [a1] "+r"(in_param.x1),
+ [a2] "+r"(in_param.x2),
+ [a3] "+r"(in_param.x3),
+ [a4] "+r"(in_param.x0):
+ [re0] "r"(&ret):
+ "x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7");
+ } while (ret == TSP_REQUEST && wait);
+
+ isb();
+ wmb();
+
+ return ret;
+}
+#else
+static uint32_t send_smc_cmd(uint32_t cmd, phys_addr_t cmd_addr,
+ uint32_t cmd_type, uint8_t wait)
+{
+ struct smc_in_params in_param = { cmd, cmd_addr, cmd_type, (uint64_t)cmd_addr >> ADDR_TRANS_NUM };
+ uint32_t ret = 0;
+
+ do {
+ asm volatile(
+ "mov r0, %[fid]\n"
+ "mov r1, %[a1]\n"
+ "mov r2, %[a2]\n"
+ "mov r3, %[a3]\n"
+ "mov r4, %[a4]\n"
+ ".arch_extension sec\n"
+ "smc #0\n"
+ "str r0, [%[re0]]\n" :
+ [fid] "+r"(in_param.x0),
+ [a1] "+r"(in_param.x1),
+ [a2] "+r"(in_param.x2),
+ [a3] "+r"(in_param.x3),
+ [a4] "+r"(in_param.x0) :
+ [re0] "r"(&ret) :
+ "r0", "r1", "r2", "r3", "r4");
+ } while (ret == TSP_REQUEST && wait);
+
+ isb();
+ wmb();
+
+ return ret;
+}
+#endif
+
+unsigned long raw_smc_send(uint32_t cmd, phys_addr_t cmd_addr,
+ uint32_t cmd_type, uint8_t wait)
+{
+ unsigned long x0;
+
+#if (CONFIG_CPU_AFF_NR != 0)
+ struct cpumask old_mask;
+ set_cpu_strategy(&old_mask);
+#endif
+ x0 = send_smc_cmd(cmd, cmd_addr, cmd_type, wait);
+#if (CONFIG_CPU_AFF_NR != 0)
+ restore_cpu(&old_mask);
+#endif
+ return x0;
+}
+
+static void siq_dump(phys_addr_t mode, uint32_t siq_mode)
+{
+ (void)raw_smc_send(TSP_REE_SIQ, mode, 0, false);
+ if (siq_mode == SIQ_DUMP_TIMEOUT) {
+ tz_log_write();
+ } else if (siq_mode == SIQ_DUMP_SHELL) {
+#ifdef CONFIG_TEE_LOG_DUMP_PATH
+ (void)tlogger_store_msg(CONFIG_TEE_LOG_DUMP_PATH,
+ sizeof(CONFIG_TEE_LOG_DUMP_PATH));
+#else
+ tz_log_write();
+#endif
+ }
+ do_cmd_need_archivelog();
+}
+
+static uint32_t get_free_siq_index(void)
+{
+ uint32_t i;
+
+ for (i = 0; i < MAX_SIQ_NUM; i++) {
+ if (g_siq_queue[i] == 0)
+ return i;
+ }
+
+ return MAX_SIQ_NUM;
+}
+
+static uint32_t get_undo_siq_index(void)
+{
+ uint32_t i;
+
+ for (i = 0; i < MAX_SIQ_NUM; i++) {
+ if (g_siq_queue[i] != 0)
+ return i;
+ }
+
+ return MAX_SIQ_NUM;
+}
+
+static int siq_thread_fn(void *arg)
+{
+ int ret;
+ uint32_t i;
+
+ while (1) {
+ ret = wait_event_interruptible(siq_th_wait,
+ atomic_read(&g_siq_th_run));
+ if (ret) {
+ tloge("wait event interruptible failed!\n");
+ return -EINTR;
+ }
+
+ mutex_lock(&g_siq_lock);
+ do {
+ i = get_undo_siq_index();
+ if (i >= MAX_SIQ_NUM)
+ break;
+ siq_dump((phys_addr_t)(1), g_siq_queue[i]);
+ g_siq_queue[i] = 0;
+ } while (1);
+ atomic_set(&g_siq_th_run, 0);
+ mutex_unlock(&g_siq_lock);
+ }
+}
+
+#ifdef CONFIG_TEE_AUDIT
+#define MAX_UPLOAD_INFO_LEN 4
+#define INFO_HIGH_OFFSET 24U
+#define INFO_MID_OFFSET 16U
+#define INFO_LOW_OFFSET 8U
+
+static void upload_audit_event(unsigned int eventindex)
+{
+ (void)eventindex;
+}
+#endif
+
+static void cmd_result_check(struct tc_ns_smc_cmd *cmd)
+{
+ if (cmd->ret_val == TEEC_PENDING || cmd->ret_val == TEEC_PENDING2)
+ tlogd("wakeup command %u\n", cmd->event_nr);
+
+ if (cmd->ret_val == TEE_ERROR_TAGET_DEAD) {
+ tloge("error smc call: ret = %x and cmd.err_origin=%x\n",
+ cmd->ret_val, cmd->err_origin);
+ cmd_monitor_ta_crash(TYPE_CRASH_TA);
+ ta_crash_report_log();
+ } else if (cmd->ret_val == TEE_ERROR_AUDIT_FAIL) {
+ tloge("error smc call: ret = %x and err-origin=%x\n",
+ cmd->ret_val, cmd->err_origin);
+#ifdef CONFIG_TEE_AUDIT
+ tloge("error smc call: status = %x and err-origin=%x\n",
+ cmd->eventindex, cmd->err_origin);
+ upload_audit_event(cmd->eventindex);
+#endif
+ }
+}
+
+static void set_shadow_smc_param(struct smc_in_params *in_params,
+ const struct smc_out_params *out_params, int *n_idled)
+{
+ if (out_params->exit_reason == SMC_EXIT_PREEMPTED) {
+ in_params->x0 = TSP_REQUEST;
+ in_params->x1 = SMC_OPS_SCHEDTO;
+ in_params->x2 = current->pid;
+ in_params->x3 = out_params->ta;
+ in_params->x4 = out_params->target;
+ } else if (out_params->exit_reason == SMC_EXIT_NORMAL) {
+ in_params->x0 = TSP_REQUEST;
+ in_params->x1 = SMC_OPS_SCHEDTO;
+ in_params->x2 = current->pid;
+ in_params->x3 = 0;
+ in_params->x4 = 0;
+ if (*n_idled > IDLED_COUNT) {
+ *n_idled = 0;
+ in_params->x1 = SMC_OPS_PROBE_ALIVE;
+ }
+ }
+}
+
+#ifndef CONFIG_ARM
+static void shadow_wo_pm(const void *arg, struct smc_out_params *out_params,
+ int *n_idled)
+{
+ struct smc_in_params in_params = {
+ TSP_REQUEST, SMC_OPS_START_SHADOW, current->pid, 0, *(u64 *)arg
+ };
+
+ set_shadow_smc_param(&in_params, out_params, n_idled);
+ isb();
+ wmb();
+ tlogd("%s: [cpu %d] x0=%lx x1=%lx x2=%lx x3=%lx x4=%lx\n",
+ __func__, raw_smp_processor_id(), in_params.x0, in_params.x1,
+ in_params.x2, in_params.x3, in_params.x4);
+ do {
+ asm volatile(
+ "mov x0, %[fid]\n"
+ "mov x1, %[a1]\n"
+ "mov x2, %[a2]\n"
+ "mov x3, %[a3]\n"
+ "mov x4, %[a4]\n"
+ "smc #0\n"
+ "str x0, [%[re0]]\n"
+ "str x1, [%[re1]]\n"
+ "str x2, [%[re2]]\n"
+ "str x3, [%[re3]]\n" :
+ [fid] "+r"(in_params.x0), [a1] "+r"(in_params.x1),
+ [a2] "+r"(in_params.x2), [a3] "+r"(in_params.x3),
+ [a4] "+r"(in_params.x4) :
+ [re0] "r"(&out_params->ret),
+ [re1] "r"(&out_params->exit_reason),
+ [re2] "r"(&out_params->ta),
+ [re3] "r"(&out_params->target) :
+ "x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7",
+ "x8", "x9", "x10", "x11", "x12", "x13",
+ "x14", "x15", "x16", "x17");
+ } while (0);
+
+ isb();
+ wmb();
+}
+#else
+static void shadow_wo_pm(const void *arg, struct smc_out_params *out_params,
+ int *n_idled)
+{
+ struct smc_in_params in_params = {
+ TSP_REQUEST, SMC_OPS_START_SHADOW, current->pid, 0, *(u32 *)arg
+ };
+
+ set_shadow_smc_param(&in_params, out_params, n_idled);
+ isb();
+ wmb();
+ tlogd("%s: [cpu %d] x0=%lx x1=%lx x2=%lx x3=%lx x4=%lx\n",
+ __func__, raw_smp_processor_id(), in_params.x0, in_params.x1,
+ in_params.x2, in_params.x3, in_params.x4);
+ do {
+ asm volatile(
+ "mov r0, %[fid]\n"
+ "mov r1, %[a1]\n"
+ "mov r2, %[a2]\n"
+ "mov r3, %[a3]\n"
+ "mov r4, %[a4]\n"
+ ".arch_extension sec\n"
+ "smc #0\n"
+ "str r0, [%[re0]]\n"
+ "str r1, [%[re1]]\n"
+ "str r2, [%[re2]]\n"
+ "str r3, [%[re3]]\n" :
+ [fid] "+r"(in_params.x0), [a1] "+r"(in_params.x1),
+ [a2] "+r"(in_params.x2), [a3] "+r"(in_params.x3),
+ [a4] "+r"(in_params.x4) :
+ [re0] "r"(&out_params->ret),
+ [re1] "r"(&out_params->exit_reason),
+ [re2] "r"(&out_params->ta),
+ [re3] "r"(&out_params->target) :
+ "r0", "r1", "r2", "r3");
+ } while (0);
+
+ isb();
+ wmb();
+}
+#endif
+
+static void set_preempted_counter(int *n_preempted, int *n_idled,
+ struct pending_entry *pe)
+{
+ *n_idled = 0;
+ (*n_preempted)++;
+
+ if (*n_preempted > PREEMPT_COUNT) {
+ tlogd("counter too large: retry 10K times on CPU%d\n", smp_processor_id());
+ *n_preempted = 0;
+ }
+#ifndef CONFIG_PREEMPT
+ /* yield cpu to avoid soft lockup */
+ cond_resched();
+#endif
+ if (match_ta_affinity(pe))
+ tloge("set shadow pid %d affinity after preempted\n",
+ pe->task->pid);
+}
+
+static int proc_shadow_thread_normal_exit(struct pending_entry *pe,
+ int *n_preempted, int *n_idled, int *ret_val)
+{
+ long long timeout;
+ int rc;
+
+ *n_preempted = 0;
+
+ timeout = HZ * (long)(HZ_COUNT + ((uint8_t)current->pid & LOW_BYTE));
+ rc = wait_event_freezable_timeout(pe->wq,
+ atomic_read(&pe->run), (long)timeout);
+ if (!rc)
+ (*n_idled)++;
+ if (atomic_read(&pe->run) == SHADOW_EXIT_RUN) {
+ tlogd("shadow thread work quit, be killed\n");
+ return CLEAN_WITHOUT_PM;
+ } else {
+ atomic_set(&pe->run, 0);
+ return RETRY_WITH_PM;
+ }
+
+ return 0;
+}
+
+static bool check_shadow_crash(uint64_t crash_reason, int *ret_val)
+{
+ if (crash_reason != TSP_CRASH)
+ return false;
+
+ tloge("TEEOS shadow has crashed!\n");
+
+ g_sys_crash = 1;
+ cmd_monitor_ta_crash(TYPE_CRASH_TEE);
+ report_log_system_error();
+ *ret_val = -1;
+ return true;
+}
+
+static void show_other_exit_reason(struct smc_out_params *params)
+{
+ if (params->exit_reason == SMC_EXIT_SHADOW) {
+ tlogd("probe shadow thread non exit, just quit\n");
+ return;
+ }
+
+ tloge("exit on unknown code %ld\n", (long)params->exit_reason);
+}
+
+static int shadow_thread_fn(void *arg)
+{
+ int n_preempted = 0;
+ int ret = 0;
+ struct smc_out_params params = { 0, SMC_EXIT_MAX, 0, 0 };
+ int n_idled = 0;
+ struct pending_entry *pe = NULL;
+
+ set_freezable();
+ pe = init_pending_entry();
+ if (!pe) {
+ kfree(arg);
+ tloge("init pending entry failed\n");
+ return -ENOMEM;
+ }
+ isb();
+ wmb();
+
+retry_wo_pm:
+ shadow_wo_pm(arg, ¶ms, &n_idled);
+ if (check_shadow_crash(params.ret, &ret))
+ goto clean_wo_pm;
+
+ if (params.exit_reason == SMC_EXIT_PREEMPTED) {
+ set_preempted_counter(&n_preempted, &n_idled, pe);
+ goto retry_wo_pm;
+ } else if (params.exit_reason == SMC_EXIT_NORMAL) {
+ ret = proc_shadow_thread_normal_exit(pe, &n_preempted, &n_idled, &ret);
+ if (ret == CLEAN_WITHOUT_PM) {
+ goto clean_wo_pm;
+ } else if (ret == RETRY_WITH_PM) {
+ if (match_ta_affinity(pe))
+ tlogd("set shadow pid %d\n", pe->task->pid);
+ goto retry_wo_pm;
+ }
+ } else {
+ show_other_exit_reason(¶ms);
+ }
+
+clean_wo_pm:
+ kfree(arg);
+ release_pending_entry(pe);
+ return ret;
+}
+
+static void shadow_work_func(struct kthread_work *work)
+{
+ struct task_struct *shadow_thread = NULL;
+ struct shadow_work *s_work =
+ container_of(work, struct shadow_work, kthwork);
+ uint64_t *target_arg = kzalloc(sizeof(uint64_t), GFP_KERNEL);
+
+ if (ZERO_OR_NULL_PTR((unsigned long)(uintptr_t)target_arg)) {
+ tloge("%s: kmalloc failed\n", __func__);
+ return;
+ }
+
+ *target_arg = s_work->target;
+ shadow_thread = kthread_create(shadow_thread_fn,
+ (void *)(uintptr_t)target_arg, "shadow th/%lu",
+ g_shadow_thread_id++);
+ if (IS_ERR_OR_NULL(shadow_thread)) {
+ kfree(target_arg);
+ tloge("couldn't create shadow_thread %ld\n",
+ PTR_ERR(shadow_thread));
+ return;
+ }
+ tlogd("%s: create shadow thread %lu for target %llx\n",
+ __func__, g_shadow_thread_id, *target_arg);
+ tz_kthread_bind_mask(shadow_thread);
+ wake_up_process(shadow_thread);
+}
+
+static int proc_smc_wakeup_ca(pid_t ca, int which)
+{
+ if (ca <= 0) {
+ tlogw("wakeup for ca <= 0\n");
+ } else {
+ struct pending_entry *pe = find_pending_entry(ca);
+
+ if (!pe) {
+ tlogd("invalid ca pid=%d for pending entry\n",
+ (int)ca);
+ return -1;
+ }
+ atomic_set(&pe->run, which);
+ wake_up(&pe->wq);
+ tlogd("wakeup pending thread %ld\n", (long)ca);
+ put_pending_entry(pe);
+ }
+ return 0;
+}
+
+void wakeup_pe(struct pending_entry *pe)
+{
+ if (!pe)
+ return;
+
+ atomic_set(&pe->run, 1);
+ wake_up(&pe->wq);
+}
+
+int smc_wakeup_broadcast(void)
+{
+ foreach_pending_entry(wakeup_pe);
+ return 0;
+}
+
+int smc_wakeup_ca(pid_t ca)
+{
+ return proc_smc_wakeup_ca(ca, 1);
+}
+
+int smc_shadow_exit(pid_t ca)
+{
+ return proc_smc_wakeup_ca(ca, SHADOW_EXIT_RUN);
+}
+
+void fiq_shadow_work_func(uint64_t target)
+{
+ struct smc_cmd_ret secret = { SMC_EXIT_MAX, 0, target };
+
+ smp_smc_send(TSP_REQUEST, (unsigned long)SMC_OPS_START_FIQSHD,
+ (unsigned long)(uint32_t)(current->pid), &secret, false);
+
+ return;
+}
+
+int smc_queue_shadow_worker(uint64_t target)
+{
+ struct shadow_work work = {
+ KTHREAD_WORK_INIT(work.kthwork, shadow_work_func),
+ .target = target,
+ };
+
+#if (KERNEL_VERSION(4, 9, 0) > LINUX_VERSION_CODE)
+ if (!queue_kthread_work(&g_ipi_helper_worker, &work.kthwork)) {
+#else
+ if (!kthread_queue_work(&g_ipi_helper_worker, &work.kthwork)) {
+#endif
+ tloge("ipi helper work fail queue, was already pending\n");
+ return -1;
+ }
+
+#if (KERNEL_VERSION(4, 9, 0) > LINUX_VERSION_CODE)
+ flush_kthread_work(&work.kthwork);
+#else
+ kthread_flush_work(&work.kthwork);
+#endif
+ return 0;
+}
+
+#ifdef CONFIG_DRM_ADAPT
+#define DRM_USR_PRIOR (-5)
+static void set_drm_strategy(void)
+{
+ if (!g_drm_mask_flag) {
+ cpumask_clear(&g_drm_cpu_mask);
+ cpumask_set_cpu(CPU_FOUR, &g_drm_cpu_mask);
+ cpumask_set_cpu(CPU_FIVE, &g_drm_cpu_mask);
+ cpumask_set_cpu(CPU_SIX, &g_drm_cpu_mask);
+ cpumask_set_cpu(CPU_SEVEN, &g_drm_cpu_mask);
+ g_drm_mask_flag = 1;
+ }
+
+ if (current->group_leader &&
+ strstr(current->group_leader->comm, "drm@1.")) {
+ set_cpus_allowed_ptr(current, &g_drm_cpu_mask);
+ set_user_nice(current, DRM_USR_PRIOR);
+ }
+}
+#endif
+
+static int smc_ops_normal(struct cmd_reuse_info *info,
+ const struct tc_ns_smc_cmd *cmd, u64 ops)
+{
+ if (ops != SMC_OPS_NORMAL)
+ return 0;
+
+ if (info->cmd_usage == RESEND) {
+ if (reuse_smc_in_entry(info->cmd_index)) {
+ tloge("reuse smc entry failed\n");
+ release_smc_entry(info->cmd_index);
+ return -ENOMEM;
+ }
+ } else {
+ info->cmd_index = occupy_free_smc_in_entry(cmd);
+ if (info->cmd_index == -1) {
+ tloge("there's no more smc entry\n");
+ return -ENOMEM;
+ }
+ }
+
+ if (info->cmd_usage != CLEAR) {
+ info->cmd_index = info->saved_index;
+ info->cmd_usage = CLEAR;
+ } else {
+ info->saved_index = info->cmd_index;
+ }
+
+ tlogd("submit new cmd: cmd.ca=%u cmd-id=%x ev-nr=%u "
+ "cmd-index=%u saved-index=%d\n",
+ cmd->ca_pid, cmd->cmd_id,
+ g_cmd_data->in[info->cmd_index].event_nr, info->cmd_index,
+ info->saved_index);
+ return 0;
+}
+
+static int smp_smc_send_cmd_done(int cmd_index, struct tc_ns_smc_cmd *cmd,
+ struct tc_ns_smc_cmd *in)
+{
+ cmd_result_check(cmd);
+ switch (cmd->ret_val) {
+ case TEEC_PENDING2: {
+ unsigned int agent_id = cmd->agent_id;
+ /* If the agent does not exist post
+ * the answer right back to the TEE
+ */
+ if (agent_process_work(cmd, agent_id))
+ tloge("agent process work failed\n");
+ return PENDING2_RETRY;
+ }
+ case TEE_ERROR_TAGET_DEAD:
+ case TEEC_PENDING:
+ /* just copy out, and let out to proceed */
+ default:
+ if (memcpy_s(in, sizeof(*in), cmd, sizeof(*cmd))) {
+ tloge("memcpy failed,%s line:%d", __func__, __LINE__);
+ cmd->ret_val = -1;
+ }
+
+ break;
+ }
+
+ return 0;
+}
+
+#define KERNEL_INDEX 5
+static void print_crash_msg(union crash_inf *crash_info)
+{
+ static const char *tee_critical_app[] = {
+ "gtask",
+ "teesmcmgr",
+ "hmsysmgr",
+ "hmfilemgr",
+ "platdrv",
+ "kernel", /* index must be same with KERNEL_INDEX */
+ "vltmm_service",
+ "tee_drv_server"
+ };
+ int app_num = sizeof(tee_critical_app) / sizeof(tee_critical_app[0]);
+ const char *crash_app_name = "NULL";
+ uint16_t off = crash_info->crash_msg.off;
+ int app_index = crash_info->crash_msg.app & LOW_BYTE;
+ int halt_reason = crash_info->crash_msg.halt_reason;
+
+ crash_info->crash_msg.off = 0;
+
+ if (app_index >= 0 && app_index < app_num)
+ crash_app_name = tee_critical_app[app_index];
+ else
+ tloge("index error: %x\n", crash_info->crash_msg.app);
+
+ if (app_index == KERNEL_INDEX) {
+ tloge("====crash app:%s user sym:%s kernel crash off/size: "
+ "<0x%x/0x%x>\n", crash_app_name,
+ crash_info->crash_msg.sym_name,
+ off, crash_info->crash_msg.size);
+ tloge("====crash halt reason: 0x%x far:0x%x fault:0x%x "
+ "elr:0x%x (ret_ip: 0x%llx)\n",
+ halt_reason, crash_info->crash_msg.far,
+ crash_info->crash_msg.fault, crash_info->crash_msg.elr,
+ crash_info->crash_reg[2]);
+ } else {
+ char syms[SYM_NAME_LEN_MAX] = {0};
+
+ if (memcpy_s(syms, SYM_NAME_LEN_MAX,
+ crash_info->crash_msg.sym_name, SYM_NAME_LEN_1))
+ tloge("memcpy sym name failed!\n");
+
+ if (memcpy_s(syms + SYM_NAME_LEN_1,
+ SYM_NAME_LEN_MAX - SYM_NAME_LEN_1,
+ crash_info->crash_msg.sym_name_append, SYM_NAME_LEN_2))
+ tloge("memcpy sym_name_append failed!\n");
+ tloge("====crash app:%s user_sym:%s + <0x%x/0x%x>\n",
+ crash_app_name, syms, off, crash_info->crash_msg.size);
+ tloge("====crash far:0x%x fault:%x\n",
+ crash_info->crash_msg.far, crash_info->crash_msg.fault);
+ }
+}
+
+static int smp_smc_send_process(struct tc_ns_smc_cmd *cmd, u64 ops,
+ struct smc_cmd_ret *cmd_ret, int cmd_index)
+{
+ int ret;
+
+ tlogd("smc send start cmd_id = %u, ca = %u\n",
+ cmd->cmd_id, cmd->ca_pid);
+
+ ret = smp_smc_send(TSP_REQUEST, (unsigned long)ops,
+ (unsigned long)(uint32_t)(current->pid), cmd_ret, ops != SMC_OPS_ABORT_TASK);
+
+ tlogd("smc send ret = %x, cmd ret.exit=%ld, cmd index=%d\n",
+ ret, (long)cmd_ret->exit, cmd_index);
+ isb();
+ wmb();
+ if (ret == (int)TSP_CRASH) {
+ union crash_inf crash_info;
+ crash_info.crash_reg[0] = cmd_ret->exit;
+ crash_info.crash_reg[1] = cmd_ret->ta;
+ crash_info.crash_reg[2] = cmd_ret->target;
+
+ tloge("TEEOS has crashed!\n");
+ print_crash_msg(&crash_info);
+
+ g_sys_crash = 1;
+ cmd_monitor_ta_crash(TYPE_CRASH_TEE);
+
+ report_log_system_error();
+
+ cmd->ret_val = -1;
+ return -1;
+ }
+
+ return 0;
+}
+
+static int init_for_smc_send(struct tc_ns_smc_cmd *in,
+ struct pending_entry **pe, struct tc_ns_smc_cmd *cmd,
+ bool reuse)
+{
+#ifdef CONFIG_DRM_ADAPT
+ set_drm_strategy();
+#endif
+ *pe = init_pending_entry();
+ if (!(*pe)) {
+ tloge("init pending entry failed\n");
+ return -ENOMEM;
+ }
+
+ in->ca_pid = current->pid;
+ if (reuse)
+ return 0;
+
+ if (memcpy_s(cmd, sizeof(*cmd), in, sizeof(*in))) {
+ tloge("memcpy in cmd failed\n");
+ release_pending_entry(*pe);
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+static bool is_ca_killed(int cmd_index)
+{
+ /* if CA has not been killed */
+ if (sigkill_pending(current)) {
+ /* signal pending, send abort cmd */
+ tloge("wait event timeout and find pending signal\n");
+ return true;
+ }
+ return false;
+}
+
+static void clean_smc_resrc(struct cmd_reuse_info info,
+ const struct tc_ns_smc_cmd *cmd,
+ struct pending_entry *pe)
+{
+ if (info.cmd_usage != CLEAR && cmd->ret_val != TEEC_PENDING)
+ release_smc_entry(info.cmd_index);
+
+ release_pending_entry(pe);
+}
+
+static int set_abort_cmd(int index)
+{
+ acquire_smc_buf_lock(&g_cmd_data->smc_lock);
+ if (!test_bit(index, (unsigned long *)g_cmd_data->doing_bitmap)) {
+ release_smc_buf_lock(&g_cmd_data->smc_lock);
+ tloge("can't abort an unprocess cmd\n");
+ return -1;
+ }
+
+ g_cmd_data->in[index].cmd_id = GLOBAL_CMD_ID_KILL_TASK;
+ g_cmd_data->in[index].cmd_type = CMD_TYPE_GLOBAL;
+ /* these phy addrs are not necessary, clear them to avoid gtask check err */
+ g_cmd_data->in[index].operation_phys = 0;
+ g_cmd_data->in[index].operation_h_phys = 0;
+ g_cmd_data->in[index].login_data_phy = 0;
+ g_cmd_data->in[index].login_data_h_addr = 0;
+
+ clear_bit(index, (unsigned long *)g_cmd_data->doing_bitmap);
+ release_smc_buf_lock(&g_cmd_data->smc_lock);
+ tloge("set abort cmd success\n");
+
+ return 0;
+}
+
+static enum smc_ops_exit process_abort_cmd(int index, struct pending_entry *pe)
+{
+ if (!set_abort_cmd(index))
+ return SMC_OPS_ABORT_TASK;
+
+ return SMC_OPS_SCHEDTO;
+}
+
+#define TO_STEP_SIZE 5
+#define INVALID_STEP_SIZE 0xFFFFFFFFU
+
+struct timeout_step_t {
+ unsigned long steps[TO_STEP_SIZE];
+ uint32_t size;
+ uint32_t cur;
+ bool timeout_reset;
+};
+
+static void init_timeout_step(uint32_t timeout, struct timeout_step_t *step)
+{
+ uint32_t i = 0;
+
+ if (timeout == 0) {
+ step->steps[0] = RESLEEP_TIMEOUT * HZ;
+ step->size = 1;
+ } else {
+ uint32_t timeout_in_jiffies;
+
+ if (timeout > RESLEEP_TIMEOUT * MSEC_PER_SEC)
+ timeout = RESLEEP_TIMEOUT * MSEC_PER_SEC;
+ timeout_in_jiffies = msecs_to_jiffies(timeout);
+
+ /*
+ * [timeout_in_jiffies-1, timeout_in_jiffies+2] jiffies
+ * As REE and TEE tick have deviation, to make sure last REE timeout
+ * is after TEE timeout, we set a timeout step from
+ * 'timeout_in_jiffies -1' to 'timeout_in_jiffies + 2'
+ */
+ if (timeout_in_jiffies > 1) {
+ step->steps[i++] = timeout_in_jiffies - 1;
+ step->steps[i++] = 1;
+ } else {
+ step->steps[i++] = timeout_in_jiffies;
+ }
+ step->steps[i++] = 1;
+ step->steps[i++] = 1;
+
+ if (RESLEEP_TIMEOUT * HZ > (timeout_in_jiffies + 2))
+ step->steps[i++] = RESLEEP_TIMEOUT * HZ - 2 - timeout_in_jiffies;
+ step->size = i;
+ }
+ step->cur = 0;
+}
+
+enum pending_t {
+ PD_WAKEUP,
+ PD_TIMEOUT,
+ PD_DONE,
+ PD_RETRY,
+};
+
+enum smc_status_t {
+ ST_DONE,
+ ST_RETRY,
+};
+
+static enum pending_t proc_ta_pending(struct pending_entry *pe,
+ struct timeout_step_t *step, uint64_t pending_args, uint32_t cmd_index,
+ u64 *ops)
+{
+ bool kernel_call = false;
+ bool woke_up = false;
+
+ /*
+ * if ->mm is NULL, it's a kernel thread and a kthread will never
+ * receive a signal.
+ */
+ if (!current->mm) {
+ kernel_call = true;
+ if (wait_event_interruptible(pe->wq, atomic_read(&pe->run)))
+ tloge("kernel CA is interrupted\n");
+ } else {
+ uint32_t timeout = (uint32_t)pending_args;
+ bool timer_no_irq = (pending_args >> 32) == 0 ? false : true;
+ uint32_t cur_timeout;
+
+ if (step->cur == INVALID_STEP_SIZE)
+ init_timeout_step(timeout, step);
+resleep:
+ cur_timeout = jiffies_to_msecs(step->steps[step->cur]);
+
+ if (!wait_event_timeout(pe->wq, atomic_read(&pe->run),
+ step->steps[step->cur])) {
+ if (step->cur < (step->size - 1)) {
+ step->cur++;
+ /*
+ * As there may no timer irq in TEE, we need a chance to
+ * run timer's irq handler initiatively by SMC_OPS_SCHEDTO.
+ */
+ if (timer_no_irq) {
+ *ops = SMC_OPS_SCHEDTO;
+ return PD_TIMEOUT;
+ } else {
+ goto resleep;
+ }
+ }
+
+ if (is_ca_killed(cmd_index)) {
+ *ops = (u64)process_abort_cmd(cmd_index, pe);
+ return PD_WAKEUP;
+ }
+ } else {
+ woke_up = true;
+ tlogd("%s woke up\n", __func__);
+ }
+ }
+
+ atomic_set(&pe->run, 0);
+ if (!is_cmd_working_done(cmd_index)) {
+ *ops = SMC_OPS_SCHEDTO;
+ return PD_WAKEUP;
+ } else if (!kernel_call && !woke_up) {
+ tloge("cmd done, may miss a spi!\n");
+ show_cmd_bitmap();
+ }
+ tlogd("cmd is done\n");
+
+ return PD_DONE;
+}
+
+static void set_timeout_step(struct timeout_step_t *timeout_step)
+{
+ if (!timeout_step->timeout_reset)
+ return;
+
+ timeout_step->cur = INVALID_STEP_SIZE;
+ timeout_step->timeout_reset = false;
+}
+
+static enum smc_status_t proc_normal_exit(struct pending_entry *pe, u64 *ops,
+ struct timeout_step_t *timeout_step, struct smc_cmd_ret *cmd_ret,
+ int cmd_index)
+{
+ enum pending_t pd_ret;
+
+ /* notify and set affinity came first, goto retry directly */
+ if (match_ta_affinity(pe)) {
+ *ops = SMC_OPS_SCHEDTO;
+ return ST_RETRY;
+ }
+
+ pd_ret = proc_ta_pending(pe, timeout_step,
+ cmd_ret->ta, cmd_index, ops);
+ if (pd_ret == PD_DONE)
+ return ST_DONE;
+
+ if (pd_ret == PD_WAKEUP)
+ timeout_step->timeout_reset = true;
+ return ST_RETRY;
+}
+
+static enum smc_status_t handle_cmd_working_done(
+ struct tc_ns_smc_cmd *cmd, u64 *ops, struct tc_ns_smc_cmd *in,
+ struct cmd_reuse_info *info)
+{
+ if (copy_smc_out_entry(info->cmd_index, cmd, &info->cmd_usage)) {
+ cmd->ret_val = TEEC_ERROR_GENERIC;
+ return ST_DONE;
+ }
+
+ if (smp_smc_send_cmd_done(info->cmd_index, cmd, in)) {
+ *ops = SMC_OPS_NORMAL; /* cmd will be reused */
+ return ST_RETRY;
+ }
+
+ return ST_DONE;
+}
+
+static int smp_smc_send_func(struct tc_ns_smc_cmd *in, bool reuse)
+{
+ struct cmd_reuse_info info = { 0, 0, CLEAR };
+ struct smc_cmd_ret cmd_ret = {0};
+ struct tc_ns_smc_cmd cmd = { {0}, 0 };
+ struct pending_entry *pe = NULL;
+ u64 ops;
+ struct timeout_step_t timeout_step =
+ { {0, 0, 0, 0}, TO_STEP_SIZE, -1, false };
+
+ if (init_for_smc_send(in, &pe, &cmd, reuse))
+ return TEEC_ERROR_GENERIC;
+
+ if (reuse) {
+ info.saved_index = in->event_nr;
+ info.cmd_index = in->event_nr;
+ info.cmd_usage = RESEND;
+ }
+ ops = SMC_OPS_NORMAL;
+
+retry:
+ set_timeout_step(&timeout_step);
+
+ if (smc_ops_normal(&info, &cmd, ops)) {
+ release_pending_entry(pe);
+ return TEEC_ERROR_GENERIC;
+ }
+
+ if (smp_smc_send_process(&cmd, ops, &cmd_ret, info.cmd_index) == -1)
+ goto clean;
+
+ if (!is_cmd_working_done(info.cmd_index)) {
+ if (cmd_ret.exit == SMC_EXIT_NORMAL) {
+ if (proc_normal_exit(pe, &ops, &timeout_step, &cmd_ret,
+ info.cmd_index) == ST_RETRY)
+ goto retry;
+ } else if (cmd_ret.exit == SMC_EXIT_ABORT) {
+ ops = (u64)process_abort_cmd(info.cmd_index, pe);
+ goto retry;
+ } else {
+ tloge("invalid cmd work state\n");
+ cmd.ret_val = TEEC_ERROR_GENERIC;
+ goto clean;
+ }
+ }
+
+ if (handle_cmd_working_done(&cmd, &ops, in, &info) == ST_RETRY)
+ goto retry;
+clean:
+ clean_smc_resrc(info, &cmd, pe);
+ return cmd.ret_val;
+}
+
+static int smc_svc_thread_fn(void *arg)
+{
+ while (!kthread_should_stop()) {
+ struct tc_ns_smc_cmd smc_cmd = { {0}, 0 };
+ int ret;
+
+ smc_cmd.cmd_type = CMD_TYPE_GLOBAL;
+ smc_cmd.cmd_id = GLOBAL_CMD_ID_SET_SERVE_CMD;
+ ret = smp_smc_send_func(&smc_cmd, false);
+ tlogd("smc svc return 0x%x\n", ret);
+ }
+ tloge("smc svc thread stop\n");
+ return 0;
+}
+
+bool is_tee_hungtask(struct task_struct *task)
+{
+ (void)task;
+
+ return false;
+}
+
+void wakeup_tc_siq(uint32_t siq_mode)
+{
+ uint32_t i;
+
+ if (siq_mode == 0)
+ return;
+
+ mutex_lock(&g_siq_lock);
+ i = get_free_siq_index();
+ if (i >= MAX_SIQ_NUM) {
+ tloge("dump is too frequent\n");
+ mutex_unlock(&g_siq_lock);
+ return;
+ }
+ g_siq_queue[i] = siq_mode;
+ atomic_set(&g_siq_th_run, 1);
+ mutex_unlock(&g_siq_lock);
+ wake_up_interruptible(&siq_th_wait);
+}
+
+/*
+ * This function first power on crypto cell, then send smc cmd to trustedcore.
+ * After finished, power off crypto cell.
+ */
+static int proc_tc_ns_smc(struct tc_ns_smc_cmd *cmd, bool reuse)
+{
+ int ret;
+ struct cmd_monitor *item = NULL;
+
+ if (g_sys_crash) {
+ tloge("ERROR: sys crash happened!!!\n");
+ return TEEC_ERROR_GENERIC;
+ }
+ if (!cmd) {
+ tloge("invalid cmd\n");
+ return TEEC_ERROR_GENERIC;
+ }
+ tlogd(KERN_INFO "***smc call start on cpu %d ***\n",
+ raw_smp_processor_id());
+
+ item = cmd_monitor_log(cmd);
+ ret = smp_smc_send_func(cmd, reuse);
+ cmd_monitor_logend(item);
+
+ return ret;
+}
+
+int tc_ns_smc(struct tc_ns_smc_cmd *cmd)
+{
+ return proc_tc_ns_smc(cmd, false);
+}
+
+int tc_ns_smc_with_no_nr(struct tc_ns_smc_cmd *cmd)
+{
+ return proc_tc_ns_smc(cmd, true);
+}
+
+static void smc_work_no_wait(uint32_t type)
+{
+ (void)raw_smc_send(TSP_REQUEST, g_cmd_phys, type, true);
+}
+
+static void smc_work_set_cmd_buffer(struct work_struct *work)
+{
+ (void)work;
+ smc_work_no_wait(TC_NS_CMD_TYPE_SECURE_CONFIG);
+}
+
+static void smc_set_cmd_buffer(void)
+{
+ struct work_struct work;
+
+ INIT_WORK_ONSTACK(&work, smc_work_set_cmd_buffer);
+ /* Run work on CPU 0 */
+ schedule_work_on(0, &work);
+ flush_work(&work);
+ tlogd("smc set cmd buffer done\n");
+}
+
+static int alloc_cmd_buffer(void)
+{
+#ifdef CONFIG_BIG_SESSION
+ /* we should map at least 64 pages for 1000 sessions, 2^6 > 40 */
+ g_cmd_data = (struct tc_ns_smc_queue *)(uintptr_t)__get_free_pages(
+ GFP_KERNEL | __GFP_ZERO, 6);
+#else
+ g_cmd_data = (struct tc_ns_smc_queue *)(uintptr_t)__get_free_page(
+ GFP_KERNEL | __GFP_ZERO);
+#endif
+ if (!g_cmd_data)
+ return -ENOMEM;
+
+ g_cmd_phys = virt_to_phys(g_cmd_data);
+
+ return 0;
+}
+
+static int init_smc_related_rsrc(const struct device *class_dev)
+{
+ struct cpumask new_mask;
+ int ret;
+
+ /*
+ * TEE Dump will disable IRQ/FIQ for about 500 ms, it's not
+ * a good choice to ask CPU0/CPU1 to do the dump.
+ * So, bind this kernel thread to other CPUs rather than CPU0/CPU1.
+ */
+ cpumask_setall(&new_mask);
+ cpumask_clear_cpu(CPU_ZERO, &new_mask);
+ cpumask_clear_cpu(CPU_ONE, &new_mask);
+ koadpt_kthread_bind_mask(g_siq_thread, &new_mask);
+ /* some products specify the cpu that kthread need to bind */
+ tz_kthread_bind_mask(g_siq_thread);
+ g_ipi_helper_thread = kthread_create(kthread_worker_fn,
+ &g_ipi_helper_worker, "ipihelper");
+ if (IS_ERR_OR_NULL(g_ipi_helper_thread)) {
+ dev_err(class_dev, "couldn't create ipi helper threads %ld\n",
+ PTR_ERR(g_ipi_helper_thread));
+ ret = (int)PTR_ERR(g_ipi_helper_thread);
+ return ret;
+ }
+
+ tz_kthread_bind_mask(g_ipi_helper_thread);
+ wake_up_process(g_ipi_helper_thread);
+ wake_up_process(g_siq_thread);
+ init_cmd_monitor();
+ INIT_LIST_HEAD(&g_pending_head);
+ spin_lock_init(&g_pend_lock);
+
+ return 0;
+}
+
+static int parse_params_from_tee(void)
+{
+ int ret;
+ void *buffer = NULL;
+
+ buffer = (void *)(g_cmd_data->in);
+ ret = check_teeos_compat_level((uint32_t *)buffer,
+ COMPAT_LEVEL_BUF_LEN);
+ if (ret) {
+ tloge("check teeos compatibility failed\n");
+ return ret;
+ }
+ if (memset_s(buffer, sizeof(g_cmd_data->in),
+ 0, sizeof(g_cmd_data->in))) {
+ tloge("Clean the command buffer failed\n");
+ ret = -EFAULT;
+ return ret;
+ }
+ return 0;
+}
+
+int smc_context_init(const struct device *class_dev)
+{
+ int ret;
+
+ if (!class_dev || IS_ERR_OR_NULL(class_dev))
+ return -ENOMEM;
+
+ ret = alloc_cmd_buffer();
+ if (ret)
+ return ret;
+
+ /* Send the allocated buffer to TrustedCore for init */
+ smc_set_cmd_buffer();
+
+ ret = parse_params_from_tee();
+ if (ret) {
+ tloge("parse params from tee failed\n");
+ goto free_mem;
+ }
+
+ g_siq_thread = kthread_create(siq_thread_fn, NULL, "siqthread/%d", 0);
+ if (unlikely(IS_ERR_OR_NULL(g_siq_thread))) {
+ dev_err(class_dev, "couldn't create siqthread %ld\n",
+ PTR_ERR(g_siq_thread));
+ ret = (int)PTR_ERR(g_siq_thread);
+ goto free_mem;
+ }
+
+ ret = init_smc_related_rsrc(class_dev);
+ if (ret)
+ goto free_siq_worker;
+
+ return 0;
+
+free_siq_worker:
+ kthread_stop(g_siq_thread);
+ g_siq_thread = NULL;
+free_mem:
+ free_page((unsigned long)(uintptr_t)g_cmd_data);
+ g_cmd_data = NULL;
+ return ret;
+}
+
+int init_smc_svc_thread(void)
+{
+ g_smc_svc_thread = kthread_create(smc_svc_thread_fn, NULL,
+ "smc_svc_thread");
+ if (unlikely(IS_ERR_OR_NULL(g_smc_svc_thread))) {
+ tloge("couldn't create smc_svc_thread %ld\n",
+ PTR_ERR(g_smc_svc_thread));
+ return PTR_ERR(g_smc_svc_thread);
+ }
+ tz_kthread_bind_mask(g_smc_svc_thread);
+ wake_up_process(g_smc_svc_thread);
+ return 0;
+}
+
+int teeos_log_exception_archive(unsigned int eventid,
+ const char *exceptioninfo)
+{
+ return 0;
+}
+
+void smc_free_data(void)
+{
+ free_page((unsigned long)(uintptr_t)g_cmd_data);
+ if (!IS_ERR_OR_NULL(g_smc_svc_thread)) {
+ kthread_stop(g_smc_svc_thread);
+ g_smc_svc_thread = NULL;
+ }
+}
diff --git a/linux/core/smc_smp.h b/linux/core/smc_smp.h
new file mode 100644
index 0000000..cbb07fb
--- /dev/null
+++ b/linux/core/smc_smp.h
@@ -0,0 +1,125 @@
+/*
+ * smc_smp.h
+ *
+ * function declaration for sending smc cmd
+ *
+ * Copyright (C) 2022 Huawei Technologies Co., Ltd.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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 SMC_SMP_H
+#define SMC_SMP_H
+
+#include
+#include "teek_client_constants.h"
+#include "teek_ns_client.h"
+
+#if (KERNEL_VERSION(5, 4, 0) <= LINUX_VERSION_CODE)
+#define CURRENT_CPUS_ALLOWED (¤t->cpus_mask)
+#else
+#define CURRENT_CPUS_ALLOWED (¤t->cpus_allowed)
+#endif
+
+enum tc_ns_cmd_type {
+ TC_NS_CMD_TYPE_INVALID = 0,
+ TC_NS_CMD_TYPE_NS_TO_SECURE,
+ TC_NS_CMD_TYPE_SECURE_TO_NS,
+ TC_NS_CMD_TYPE_SECURE_TO_SECURE,
+ TC_NS_CMD_TYPE_SECURE_CONFIG = 0xf,
+ TC_NS_CMD_TYPE_MAX
+};
+
+struct pending_entry {
+ atomic_t users;
+ struct task_struct *task;
+#ifdef CONFIG_TA_AFFINITY
+ struct cpumask ca_mask;
+ struct cpumask ta_mask;
+#endif
+ pid_t pid;
+ wait_queue_head_t wq;
+ atomic_t run;
+ struct list_head list;
+};
+
+#ifdef CONFIG_BIG_SESSION
+#define MAX_SMC_CMD CONFIG_BIG_SESSION
+#else
+#define MAX_SMC_CMD 18
+#endif
+
+#ifdef DIV_ROUND_UP
+#undef DIV_ROUND_UP
+#endif
+#define DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d))
+
+#define BITS_PER_BYTE 8
+
+#ifdef BITS_TO_LONGS
+#undef BITS_TO_LONGS
+#endif
+#define BITS_TO_LONGS(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(uint64_t))
+
+#ifdef BIT_MASK
+#undef BIT_MASK
+#endif
+#define BIT_MASK(nr) (1UL << (((uint64_t)nr) % sizeof(uint64_t)))
+
+#ifdef BIT_WORD
+#undef BIT_WORD
+#endif
+#define BIT_WORD(nr) ((nr) / sizeof(uint64_t))
+
+#ifdef DECLARE_BITMAP
+#undef DECLARE_BITMAP
+#endif
+#define DECLARE_BITMAP(name, bits) uint64_t name[BITS_TO_LONGS(bits)]
+
+#define SIQ_DUMP_TIMEOUT 1U
+#define SIQ_DUMP_SHELL 2U
+
+typedef uint32_t smc_buf_lock_t;
+
+struct tc_ns_smc_queue {
+ /* set when CA send cmd_in, clear after cmd_out return */
+ DECLARE_BITMAP(in_bitmap, MAX_SMC_CMD);
+ /* set when gtask get cmd_in, clear after cmd_out return */
+ DECLARE_BITMAP(doing_bitmap, MAX_SMC_CMD);
+ /* set when gtask get cmd_out, clear after cmd_out return */
+ DECLARE_BITMAP(out_bitmap, MAX_SMC_CMD);
+ smc_buf_lock_t smc_lock;
+ volatile uint32_t last_in;
+ struct tc_ns_smc_cmd in[MAX_SMC_CMD];
+ volatile uint32_t last_out;
+ struct tc_ns_smc_cmd out[MAX_SMC_CMD];
+};
+
+#define RESLEEP_TIMEOUT 15
+
+bool sigkill_pending(struct task_struct *tsk);
+int smc_context_init(const struct device *class_dev);
+void smc_free_data(void);
+int tc_ns_smc(struct tc_ns_smc_cmd *cmd);
+int tc_ns_smc_with_no_nr(struct tc_ns_smc_cmd *cmd);
+int teeos_log_exception_archive(unsigned int eventid, const char *exceptioninfo);
+void set_cmd_send_state(void);
+int init_smc_svc_thread(void);
+int smc_wakeup_ca(pid_t ca);
+int smc_wakeup_broadcast(void);
+int smc_shadow_exit(pid_t ca);
+int smc_queue_shadow_worker(uint64_t target);
+void fiq_shadow_work_func(uint64_t target);
+struct pending_entry *find_pending_entry(pid_t pid);
+void foreach_pending_entry(void (*func)(struct pending_entry *));
+void put_pending_entry(struct pending_entry *pe);
+void show_cmd_bitmap(void);
+void wakeup_tc_siq(uint32_t siq_mode);
+
+#endif
diff --git a/linux/core/tc_client_driver.c b/linux/core/tc_client_driver.c
new file mode 100644
index 0000000..29bd5e6
--- /dev/null
+++ b/linux/core/tc_client_driver.c
@@ -0,0 +1,1221 @@
+/*
+ * tc_client_driver.c
+ *
+ * function for proc open,close session and invoke
+ *
+ * Copyright (C) 2022 Huawei Technologies Co., Ltd.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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 "tc_client_driver.h"
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#if (KERNEL_VERSION(4, 14, 0) <= LINUX_VERSION_CODE)
+#include
+#include
+#include
+#endif
+#include
+#include
+#include "smc_smp.h"
+#include "teek_client_constants.h"
+#include "agent.h"
+#include "mem.h"
+#include "gp_ops.h"
+#include "tc_ns_log.h"
+#include "tc_ns_client.h"
+#include "mailbox_mempool.h"
+#include "tz_spi_notify.h"
+#include "client_hash_auth.h"
+#include "auth_base_impl.h"
+#include "tlogger.h"
+#include "tzdebug.h"
+#include "session_manager.h"
+#include "ko_adapt.h"
+#include "tz_pm.h"
+#include "tz_kthread_affinity.h"
+#include "reserved_mempool.h"
+
+static dev_t g_tc_ns_client_devt;
+static struct class *g_driver_class;
+static struct cdev g_tc_ns_client_cdev;
+static struct device_node *g_dev_node;
+
+#ifdef CONFIG_ACPI
+static int g_acpi_irq;
+#endif
+
+static unsigned int g_device_file_cnt = 1;
+static DEFINE_MUTEX(g_device_file_cnt_lock);
+
+/* dev node list and itself has mutex to avoid race */
+struct tc_ns_dev_list g_tc_ns_dev_list;
+
+static struct task_struct *g_teecd_task;
+
+struct tc_ns_dev_list *get_dev_list(void)
+{
+ return &g_tc_ns_dev_list;
+}
+
+static int tc_ns_get_tee_version(const struct tc_ns_dev_file *dev_file,
+ void __user *argp)
+{
+ unsigned int version;
+ struct tc_ns_smc_cmd smc_cmd = { {0}, 0 };
+ int ret = 0;
+ struct mb_cmd_pack *mb_pack = NULL;
+ bool is_teecd = (g_teecd_task == current->group_leader) &&
+ (!tc_ns_get_uid());
+
+ if (!argp) {
+ tloge("error input parameter\n");
+ return -EINVAL;
+ }
+
+ if (!is_teecd) {
+ tloge("ioctl is not from teecd and return\n");
+ return -EACCES;
+ }
+
+ mb_pack = mailbox_alloc_cmd_pack();
+ if (!mb_pack) {
+ tloge("alloc mb pack failed\n");
+ return -ENOMEM;
+ }
+
+ mb_pack->operation.paramtypes = TEEC_VALUE_OUTPUT;
+ smc_cmd.cmd_type = CMD_TYPE_GLOBAL;
+ smc_cmd.cmd_id = GLOBAL_CMD_ID_GET_TEE_VERSION;
+ smc_cmd.dev_file_id = dev_file->dev_file_id;
+ smc_cmd.operation_phys = virt_to_phys(&mb_pack->operation);
+ smc_cmd.operation_h_phys =
+ (uint64_t)virt_to_phys(&mb_pack->operation) >> ADDR_TRANS_NUM;
+
+ if (tc_ns_smc(&smc_cmd)) {
+ ret = -EPERM;
+ tloge("smc call returns error ret 0x%x\n", smc_cmd.ret_val);
+ }
+
+ version = mb_pack->operation.params[0].value.a;
+ if (copy_to_user(argp, &version, sizeof(unsigned int)))
+ ret = -EFAULT;
+ mailbox_free(mb_pack);
+
+ return ret;
+}
+
+#ifdef CONFIG_CMS_SIGNATURE
+#define DEVICE_CRL_MAX 0x4000 /* 16KB */
+static char *get_crl_mb_param(void __user *argp, uint32_t *mb_param_size)
+{
+ struct tc_ns_client_crl context = {0};
+ char *mb_param = NULL;
+
+ if (copy_from_user(&context, argp, sizeof(context))) {
+ tloge("copy from user failed\n");
+ return NULL;
+ }
+
+ if (context.size > DEVICE_CRL_MAX) {
+ tloge("crl size is too long\n");
+ return NULL;
+ }
+
+ uint8_t *crl_buffer = kmalloc(context.size, GFP_KERNEL);
+ if (ZERO_OR_NULL_PTR((unsigned long)(uintptr_t)(crl_buffer))) {
+ tloge("failed to allocate crl buffer\n");
+ return NULL;
+ }
+
+ if (copy_from_user(crl_buffer, context.buffer, context.size)) {
+ tloge("copy from user failed\n");
+ goto clean;
+ }
+
+ *mb_param_size = context.size;
+ mb_param = mailbox_copy_alloc(crl_buffer, context.size);
+clean:
+ kfree(crl_buffer);
+ crl_buffer = NULL;
+ return mb_param;
+}
+
+static int tc_ns_update_ta_crl(const struct tc_ns_dev_file *dev_file,
+ void __user *argp)
+{
+ int ret;
+ struct tc_ns_smc_cmd smc_cmd = { {0}, 0 };
+ struct mb_cmd_pack *mb_pack = NULL;
+ char *mb_param = NULL;
+ uint32_t mb_param_size = 0;
+
+ if (!dev_file || !argp) {
+ tloge("invalid params\n");
+ return -EINVAL;
+ }
+
+ mb_pack = mailbox_alloc_cmd_pack();
+ if (!mb_pack) {
+ tloge("alloc mb pack failed\n");
+ return -ENOMEM;
+ }
+ mb_param = get_crl_mb_param(argp, &mb_param_size);
+ if (!mb_param) {
+ tloge("alloc mb param failed\n");
+ ret = -ENOMEM;
+ goto clean;
+ }
+ mb_pack->operation.paramtypes = TEEC_MEMREF_TEMP_INPUT;
+ mb_pack->operation.params[0].memref.buffer = virt_to_phys(mb_param);
+ mb_pack->operation.buffer_h_addr[0] =
+ (uint64_t)virt_to_phys(mb_param) >> ADDR_TRANS_NUM;
+ mb_pack->operation.params[0].memref.size = mb_param_size;
+ smc_cmd.cmd_id = GLOBAL_CMD_ID_UPDATE_TA_CRL;
+ smc_cmd.cmd_type = CMD_TYPE_GLOBAL;
+ smc_cmd.dev_file_id = dev_file->dev_file_id;
+ smc_cmd.context_id = 0;
+ smc_cmd.operation_phys = virt_to_phys(&mb_pack->operation);
+ smc_cmd.operation_h_phys =
+ (uint64_t)virt_to_phys(&mb_pack->operation) >> ADDR_TRANS_NUM;
+
+ ret = tc_ns_smc(&smc_cmd);
+ if (ret != 0)
+ tloge("smc call returns error ret 0x%x\n", ret);
+clean:
+ mailbox_free(mb_pack);
+ mb_pack = NULL;
+ if (mb_param)
+ mailbox_free(mb_param);
+
+ return ret;
+}
+#endif
+/*
+ * This is the login information
+ * and is set teecd when client opens a new session
+ */
+#define MAX_BUF_LEN 4096
+
+static int get_pack_name_len(struct tc_ns_dev_file *dev_file,
+ const uint8_t *cert_buffer)
+{
+ if (memcpy_s(&dev_file->pkg_name_len, sizeof(dev_file->pkg_name_len),
+ cert_buffer, sizeof(dev_file->pkg_name_len)))
+ return -EFAULT;
+
+ if (!dev_file->pkg_name_len ||
+ dev_file->pkg_name_len >= MAX_PACKAGE_NAME_LEN) {
+ tloge("invalid pack name len: %u\n", dev_file->pkg_name_len);
+ return -EINVAL;
+ }
+
+ tlogd("package name len is %u\n", dev_file->pkg_name_len);
+
+ return 0;
+}
+
+static int get_public_key_len(struct tc_ns_dev_file *dev_file,
+ const uint8_t *cert_buffer)
+{
+ if (memcpy_s(&dev_file->pub_key_len, sizeof(dev_file->pub_key_len),
+ cert_buffer, sizeof(dev_file->pub_key_len)))
+ return -EFAULT;
+
+ if (dev_file->pub_key_len > MAX_PUBKEY_LEN) {
+ tloge("invalid public key len: %u\n", dev_file->pub_key_len);
+ return -EINVAL;
+ }
+
+ tlogd("publick key len is %u\n", dev_file->pub_key_len);
+
+ return 0;
+}
+
+static int get_public_key(struct tc_ns_dev_file *dev_file,
+ const uint8_t *cert_buffer)
+{
+ /* get public key */
+ if (!dev_file->pub_key_len)
+ return 0;
+
+ if (memcpy_s(dev_file->pub_key, MAX_PUBKEY_LEN, cert_buffer,
+ dev_file->pub_key_len)) {
+ tloge("failed to copy pub key len\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static bool is_cert_buffer_size_valid(int cert_buffer_size)
+{
+ /*
+ * GET PACKAGE NAME AND APP CERTIFICATE:
+ * The proc_info format is as follows:
+ * package_name_len(4 bytes) || package_name ||
+ * apk_cert_len(4 bytes) || apk_cert.
+ * or package_name_len(4 bytes) || package_name
+ * || exe_uid_len(4 bytes) || exe_uid.
+ * The apk certificate format is as follows:
+ * modulus_size(4bytes) ||modulus buffer
+ * || exponent size || exponent buffer
+ */
+ if (cert_buffer_size > MAX_BUF_LEN || !cert_buffer_size) {
+ tloge("cert buffer size is invalid!\n");
+ return false;
+ }
+
+ return true;
+}
+
+static int alloc_login_buf(struct tc_ns_dev_file *dev_file,
+ uint8_t **cert_buffer, unsigned int *cert_buffer_size)
+{
+ *cert_buffer_size = (unsigned int)(MAX_PACKAGE_NAME_LEN +
+ MAX_PUBKEY_LEN + sizeof(dev_file->pkg_name_len) +
+ sizeof(dev_file->pub_key_len));
+
+ *cert_buffer = kmalloc(*cert_buffer_size, GFP_KERNEL);
+ if (ZERO_OR_NULL_PTR((unsigned long)(uintptr_t)(*cert_buffer))) {
+ tloge("failed to allocate login buffer!");
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static int client_login_prepare(uint8_t *cert_buffer,
+ const void __user *buffer, unsigned int cert_buffer_size)
+{
+ if (!is_cert_buffer_size_valid(cert_buffer_size))
+ return -EINVAL;
+
+ if (copy_from_user(cert_buffer, buffer, cert_buffer_size)) {
+ tloge("Failed to get user login info!\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int tc_ns_client_login_func(struct tc_ns_dev_file *dev_file,
+ const void __user *buffer)
+{
+ int ret;
+ uint8_t *cert_buffer = NULL;
+ uint8_t *temp_cert_buffer = NULL;
+ unsigned int cert_buffer_size = 0;
+
+ if (!dev_file)
+ return -EINVAL;
+
+ if (!buffer) {
+ /*
+ * We accept no debug information
+ * because the daemon might have failed
+ */
+ dev_file->pkg_name_len = 0;
+ dev_file->pub_key_len = 0;
+ return 0;
+ }
+
+ mutex_lock(&dev_file->login_setup_lock);
+ if (dev_file->login_setup) {
+ tloge("login information cannot be set twice!\n");
+ mutex_unlock(&dev_file->login_setup_lock);
+ return -EINVAL;
+ }
+
+ ret = alloc_login_buf(dev_file, &cert_buffer, &cert_buffer_size);
+ if (ret) {
+ mutex_unlock(&dev_file->login_setup_lock);
+ return ret;
+ }
+
+ temp_cert_buffer = cert_buffer;
+ if (client_login_prepare(cert_buffer, buffer, cert_buffer_size)) {
+ ret = -EINVAL;
+ goto error;
+ }
+
+ ret = get_pack_name_len(dev_file, cert_buffer);
+ if (ret)
+ goto error;
+ cert_buffer += sizeof(dev_file->pkg_name_len);
+
+ if (strncpy_s(dev_file->pkg_name, MAX_PACKAGE_NAME_LEN, cert_buffer,
+ dev_file->pkg_name_len)) {
+ ret = -ENOMEM;
+ goto error;
+ }
+ cert_buffer += dev_file->pkg_name_len;
+
+ ret = get_public_key_len(dev_file, cert_buffer);
+ if (ret)
+ goto error;
+ cert_buffer += sizeof(dev_file->pub_key_len);
+
+ ret = get_public_key(dev_file, cert_buffer);
+ dev_file->login_setup = true;
+
+error:
+ kfree(temp_cert_buffer);
+ mutex_unlock(&dev_file->login_setup_lock);
+ return ret;
+}
+
+int tc_ns_client_open(struct tc_ns_dev_file **dev_file, uint8_t kernel_api)
+{
+ struct tc_ns_dev_file *dev = NULL;
+
+ tlogd("tc_client_open\n");
+ if (!dev_file) {
+ tloge("dev_file is NULL\n");
+ return -EINVAL;
+ }
+
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ if (ZERO_OR_NULL_PTR((unsigned long)(uintptr_t)dev)) {
+ tloge("dev malloc failed\n");
+ return -ENOMEM;
+ }
+
+ mutex_lock(&g_tc_ns_dev_list.dev_lock);
+ list_add_tail(&dev->head, &g_tc_ns_dev_list.dev_file_list);
+ mutex_unlock(&g_tc_ns_dev_list.dev_lock);
+ mutex_lock(&g_device_file_cnt_lock);
+ dev->dev_file_id = g_device_file_cnt;
+ g_device_file_cnt++;
+ mutex_unlock(&g_device_file_cnt_lock);
+ INIT_LIST_HEAD(&dev->shared_mem_list);
+ dev->login_setup = 0;
+ dev->kernel_api = kernel_api;
+ dev->load_app_flag = 0;
+ mutex_init(&dev->service_lock);
+ mutex_init(&dev->shared_mem_lock);
+ mutex_init(&dev->login_setup_lock);
+ init_completion(&dev->close_comp);
+ *dev_file = dev;
+
+ return 0;
+}
+
+static void del_dev_node(struct tc_ns_dev_file *dev)
+{
+ if (!dev)
+ return;
+
+ mutex_lock(&g_tc_ns_dev_list.dev_lock);
+ list_del(&dev->head);
+ mutex_unlock(&g_tc_ns_dev_list.dev_lock);
+}
+
+void free_dev(struct tc_ns_dev_file *dev)
+{
+ del_dev_node(dev);
+ tee_agent_clear_dev_owner(dev);
+ if (memset_s(dev, sizeof(*dev), 0, sizeof(*dev)))
+ tloge("Caution, memset dev fail!\n");
+ kfree(dev);
+}
+
+int tc_ns_client_close(struct tc_ns_dev_file *dev)
+{
+ if (!dev) {
+ tloge("invalid dev(null)\n");
+ return -EINVAL;
+ }
+
+ close_unclosed_session_in_kthread(dev);
+
+ /* for thirdparty agent, code runs here only when agent crashed */
+ send_crashed_event_response_all(dev);
+ free_dev(dev);
+
+ return 0;
+}
+
+void shared_vma_open(struct vm_area_struct *vma)
+{
+ (void)vma;
+}
+
+static void release_vma_shared_mem(struct tc_ns_dev_file *dev_file,
+ const struct vm_area_struct *vma)
+{
+ struct tc_ns_shared_mem *shared_mem = NULL;
+ struct tc_ns_shared_mem *shared_mem_temp = NULL;
+ bool find = false;
+
+ mutex_lock(&dev_file->shared_mem_lock);
+ list_for_each_entry_safe(shared_mem, shared_mem_temp,
+ &dev_file->shared_mem_list, head) {
+ if (shared_mem) {
+ if (shared_mem->user_addr ==
+ (void *)(uintptr_t)vma->vm_start) {
+ shared_mem->user_addr = NULL;
+ find = true;
+ } else if (shared_mem->user_addr_ca ==
+ (void *)(uintptr_t)vma->vm_start) {
+ shared_mem->user_addr_ca = NULL;
+ find = true;
+ }
+
+ if (!shared_mem->user_addr &&
+ !shared_mem->user_addr_ca)
+ list_del(&shared_mem->head);
+
+ /* pair with tc client mmap */
+ if (find) {
+ put_sharemem_struct(shared_mem);
+ break;
+ }
+ }
+ }
+ mutex_unlock(&dev_file->shared_mem_lock);
+}
+
+void shared_vma_close(struct vm_area_struct *vma)
+{
+ struct tc_ns_dev_file *dev_file = NULL;
+
+ if (!vma) {
+ tloge("vma is null\n");
+ return;
+ }
+
+ dev_file = vma->vm_private_data;
+ if (!dev_file) {
+ tloge("vm private data is null\n");
+ return;
+ }
+
+ if (g_teecd_task == current->group_leader && !tc_ns_get_uid() &&
+ ((g_teecd_task->flags & PF_EXITING) ||
+ (current->flags & PF_EXITING))) {
+ tlogd("teecd is killed, just return in vma close\n");
+ return;
+ }
+ release_vma_shared_mem(dev_file, vma);
+}
+
+static struct vm_operations_struct g_shared_remap_vm_ops = {
+ .open = shared_vma_open,
+ .close = shared_vma_close,
+};
+
+static struct tc_ns_shared_mem *find_sharedmem(
+ const struct vm_area_struct *vma,
+ const struct tc_ns_dev_file *dev_file, bool *only_remap)
+{
+ struct tc_ns_shared_mem *shm_tmp = NULL;
+ unsigned long len = vma->vm_end - vma->vm_start;
+
+ /*
+ * using vma->vm_pgoff as share_mem index
+ * check if aready allocated
+ */
+ list_for_each_entry(shm_tmp, &dev_file->shared_mem_list, head) {
+ if (atomic_read(&shm_tmp->offset) == vma->vm_pgoff) {
+ tlogd("sharemem already alloc, shm tmp->offset=%d\n",
+ atomic_read(&shm_tmp->offset));
+ /*
+ * args check:
+ * 1. this shared mem is already mapped
+ * 2. remap a different size shared_mem
+ */
+ if (shm_tmp->user_addr_ca ||
+ vma->vm_end - vma->vm_start != shm_tmp->len) {
+ tloge("already remap once!\n");
+ return NULL;
+ }
+ /* return the same sharedmem specified by vm_pgoff */
+ *only_remap = true;
+ get_sharemem_struct(shm_tmp);
+ return shm_tmp;
+ }
+ }
+
+ /* if not find, alloc a new sharemem */
+ return tc_mem_allocate(len);
+}
+
+static int remap_shared_mem(struct vm_area_struct *vma,
+ const struct tc_ns_shared_mem *shared_mem)
+{
+ int ret;
+ if (shared_mem->mem_type == RESERVED_TYPE) {
+ unsigned long pfn = res_mem_virt_to_phys((uintptr_t)(shared_mem->kernel_addr)) >> PAGE_SHIFT;
+ unsigned long size = vma->vm_end-vma->vm_start;
+ ret = remap_pfn_range(vma, vma->vm_start, pfn, size, vma->vm_page_prot); //PAGE_SHARED
+ if (ret)
+ tloge("remap pfn for user failed, ret %d", ret);
+ return ret;
+ }
+
+ vma->vm_flags |= VM_USERMAP;
+ ret = remap_vmalloc_range(vma, shared_mem->kernel_addr, 0);
+ if (ret)
+ tloge("can't remap to user, ret = %d\n", ret);
+
+ return ret;
+}
+
+/*
+ * in this func, we need to deal with follow cases:
+ * vendor CA alloc sharedmem (alloc and remap);
+ * HIDL alloc sharedmem (alloc and remap);
+ * system CA alloc sharedmem (only just remap);
+ */
+static int tc_client_mmap(struct file *filp, struct vm_area_struct *vma)
+{
+ int ret;
+ struct tc_ns_dev_file *dev_file = NULL;
+ struct tc_ns_shared_mem *shared_mem = NULL;
+ bool only_remap = false;
+
+ if (!filp || !vma || !filp->private_data) {
+ tloge("invalid args for tc mmap\n");
+ return -EINVAL;
+ }
+ dev_file = filp->private_data;
+
+ mutex_lock(&dev_file->shared_mem_lock);
+ shared_mem = find_sharedmem(vma, dev_file, &only_remap);
+ if (IS_ERR_OR_NULL(shared_mem)) {
+ tloge("alloc shared mem failed\n");
+ mutex_unlock(&dev_file->shared_mem_lock);
+ return -ENOMEM;
+ }
+
+ ret = remap_shared_mem(vma, shared_mem);
+ if (ret) {
+ if (only_remap)
+ put_sharemem_struct(shared_mem);
+ else
+ tc_mem_free(shared_mem);
+ mutex_unlock(&dev_file->shared_mem_lock);
+ return ret;
+ }
+
+ vma->vm_flags |= VM_DONTCOPY;
+ vma->vm_ops = &g_shared_remap_vm_ops;
+ shared_vma_open(vma);
+ vma->vm_private_data = (void *)dev_file;
+
+ if (only_remap) {
+ shared_mem->user_addr_ca = (void *)(uintptr_t)vma->vm_start;
+ mutex_unlock(&dev_file->shared_mem_lock);
+ return ret;
+ }
+ shared_mem->user_addr = (void *)(uintptr_t)vma->vm_start;
+ atomic_set(&shared_mem->offset, vma->vm_pgoff);
+ get_sharemem_struct(shared_mem);
+ list_add_tail(&shared_mem->head, &dev_file->shared_mem_list);
+ mutex_unlock(&dev_file->shared_mem_lock);
+
+ return ret;
+}
+
+static int ioctl_register_agent(struct tc_ns_dev_file *dev_file, unsigned long arg)
+{
+ int ret;
+ struct agent_ioctl_args args;
+
+ if (!arg) {
+ tloge("arg is NULL\n");
+ return -EFAULT;
+ }
+
+ if (copy_from_user(&args, (void *)(uintptr_t)arg, sizeof(args))) {
+ tloge("copy agent args failed\n");
+ return -EFAULT;
+ }
+
+ ret = tc_ns_register_agent(dev_file, args.id, args.buffer_size,
+ &args.buffer, true);
+ if (!ret) {
+ if (copy_to_user((void *)(uintptr_t)arg, &args, sizeof(args)))
+ tloge("copy agent user addr failed\n");
+ }
+
+ return ret;
+}
+
+static int ioctl_unregister_agent(const struct tc_ns_dev_file *dev_file,
+ unsigned long arg)
+{
+ int ret;
+ struct smc_event_data *event_data = NULL;
+
+ event_data = find_event_control((unsigned int)arg);
+ if (!event_data) {
+ tloge("invalid agent id\n");
+ return -EINVAL;
+ }
+
+ if (event_data->owner != dev_file) {
+ tloge("invalid unregister request\n");
+ put_agent_event(event_data);
+ return -EINVAL;
+ }
+
+ put_agent_event(event_data);
+ ret = tc_ns_unregister_agent((unsigned int)arg);
+
+ return ret;
+}
+
+/* ioctls for the secure storage daemon */
+static long tc_agent_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ int ret = -EINVAL;
+ struct tc_ns_dev_file *dev_file = file->private_data;
+
+ if (!dev_file) {
+ tloge("invalid params\n");
+ return -EINVAL;
+ }
+
+ switch (cmd) {
+ case TC_NS_CLIENT_IOCTL_WAIT_EVENT:
+ ret = tc_ns_wait_event((unsigned int)arg);
+ break;
+ case TC_NS_CLIENT_IOCTL_SEND_EVENT_RESPONSE:
+ ret = tc_ns_send_event_response((unsigned int)arg);
+ break;
+ case TC_NS_CLIENT_IOCTL_REGISTER_AGENT:
+ ret = ioctl_register_agent(dev_file, arg);
+ break;
+ case TC_NS_CLIENT_IOCTL_UNREGISTER_AGENT:
+ ret = ioctl_unregister_agent(dev_file, arg);
+ break;
+ case TC_NS_CLIENT_IOCTL_SYC_SYS_TIME:
+ ret = tc_ns_sync_sys_time(
+ (struct tc_ns_client_time *)(uintptr_t)arg);
+ break;
+ case TC_NS_CLIENT_IOCTL_SET_NATIVECA_IDENTITY:
+ ret = tc_ns_set_native_hash(arg, GLOBAL_CMD_ID_SET_CA_HASH);
+ break;
+ case TC_NS_CLIENT_IOCTL_LATEINIT:
+ ret = tc_ns_late_init(arg);
+ break;
+ default:
+ tloge("invalid cmd!");
+ return ret;
+ }
+ tlogd("client ioctl ret = 0x%x\n", ret);
+ return ret;
+}
+
+static int tc_ns_send_cancel_cmd(struct tc_ns_dev_file *dev_file, void *argp)
+{
+ struct tc_ns_client_context client_context = {{0}};
+
+ if (!argp) {
+ tloge("argp is NULL input buffer\n");
+ return -EINVAL;
+ }
+ if (copy_from_user(&client_context, argp, sizeof(client_context))) {
+ tloge("copy from user failed\n");
+ return -ENOMEM;
+ }
+
+ client_context.returns.code = TEEC_ERROR_GENERIC;
+ client_context.returns.origin = TEEC_ORIGIN_COMMS;
+ tloge("not support send cancle cmd now\n");
+ if (copy_to_user(argp, &client_context, sizeof(client_context)))
+ return -EFAULT;
+
+ return 0;
+}
+
+uint32_t tc_ns_get_uid(void)
+{
+ struct task_struct *task = NULL;
+ const struct cred *cred = NULL;
+ uint32_t uid;
+
+ rcu_read_lock();
+ task = get_current();
+ get_task_struct(task);
+ rcu_read_unlock();
+ cred = koadpt_get_task_cred(task);
+ if (!cred) {
+ tloge("failed to get uid of the task\n");
+ put_task_struct(task);
+ return (uint32_t)(-1);
+ }
+
+ uid = cred->uid.val;
+ put_cred(cred);
+ put_task_struct(task);
+ tlogd("current uid is %u\n", uid);
+ return uid;
+}
+
+static long tc_client_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ int ret = -EFAULT;
+ void *argp = (void __user *)(uintptr_t)arg;
+
+ switch (cmd) {
+ case TC_NS_CLIENT_IOCTL_SES_OPEN_REQ:
+ case TC_NS_CLIENT_IOCTL_SES_CLOSE_REQ:
+ case TC_NS_CLIENT_IOCTL_SEND_CMD_REQ:
+ ret = tc_client_session_ioctl(file, cmd, arg);
+ break;
+ case TC_NS_CLIENT_IOCTL_LOAD_APP_REQ:
+ ret = tc_ns_load_secfile(file->private_data, argp);
+ break;
+ case TC_NS_CLIENT_IOCTL_CANCEL_CMD_REQ:
+ ret = tc_ns_send_cancel_cmd(file->private_data, argp);
+ break;
+ case TC_NS_CLIENT_IOCTL_LOGIN:
+ ret = tc_ns_client_login_func(file->private_data, argp);
+ break;
+ case TC_NS_CLIENT_IOCTL_WAIT_EVENT:
+ case TC_NS_CLIENT_IOCTL_SEND_EVENT_RESPONSE:
+ case TC_NS_CLIENT_IOCTL_REGISTER_AGENT:
+ case TC_NS_CLIENT_IOCTL_UNREGISTER_AGENT:
+ case TC_NS_CLIENT_IOCTL_SYC_SYS_TIME:
+ case TC_NS_CLIENT_IOCTL_SET_NATIVECA_IDENTITY:
+ case TC_NS_CLIENT_IOCTL_LATEINIT:
+ ret = tc_agent_ioctl(file, cmd, arg);
+ break;
+ case TC_NS_CLIENT_IOCTL_TST_CMD_REQ:
+ ret = tc_ns_tst_cmd(argp);
+ break;
+ case TC_NS_CLIENT_IOCTL_GET_TEE_VERSION:
+ ret = tc_ns_get_tee_version(file->private_data, argp);
+ break;
+#ifdef CONFIG_CMS_SIGNATURE
+ case TC_NS_CLIENT_IOCTL_UPDATE_TA_CRL:
+ ret = tc_ns_update_ta_crl(file->private_data, argp);
+ break;
+#endif
+ case TC_NS_CLIENT_IOCTL_UNMAP_SHARED_MEM:
+ ret = 0; /* this cmd only for liteos, no need to do this on linux */
+ break;
+ default:
+ tloge("invalid cmd 0x%x!", cmd);
+ break;
+ }
+
+ tlogd("tc client ioctl ret = 0x%x\n", ret);
+ return (long)ret;
+}
+
+static int tc_client_open(struct inode *inode, struct file *file)
+{
+ int ret;
+ struct tc_ns_dev_file *dev = NULL;
+
+ g_teecd_task = current->group_leader;
+ file->private_data = NULL;
+ ret = tc_ns_client_open(&dev, TEE_REQ_FROM_USER_MODE);
+ if (!ret)
+ file->private_data = dev;
+
+ return ret;
+}
+
+static int teec_daemon_close(struct tc_ns_dev_file *dev)
+{
+ if (!dev) {
+ tloge("invalid dev(null)\n");
+ return -EINVAL;
+ }
+
+ del_dev_node(dev);
+ kfree(dev);
+ return 0;
+}
+
+static int tc_client_close(struct inode *inode, struct file *file)
+{
+ int ret = 0;
+ struct tc_ns_dev_file *dev = file->private_data;
+
+ clean_agent_pid_info(dev);
+ if (g_teecd_task == current->group_leader && !tc_ns_get_uid()) {
+ /* for teecd close fd */
+ if ((g_teecd_task->flags & PF_EXITING) ||
+ (current->flags & PF_EXITING)) {
+ tloge("teecd exit!\n");
+ if (is_system_agent(dev)) {
+ /* for teecd agent close fd */
+ send_event_response_single(dev);
+ free_dev(dev);
+ } else {
+ /* for ca damon close fd */
+ ret = teec_daemon_close(dev);
+ }
+ } else {
+ /*
+ * for ca damon close fd when ca damon close fd
+ * later than HIDL thread
+ */
+ ret = tc_ns_client_close(dev);
+ }
+ } else {
+ /* for CA(HIDL thread) close fd */
+ ret = tc_ns_client_close(dev);
+ }
+ file->private_data = NULL;
+
+ return ret;
+}
+
+struct tc_ns_dev_file *tc_find_dev_file(unsigned int dev_file_id)
+{
+ struct tc_ns_dev_file *dev_file = NULL;
+
+ mutex_lock(&g_tc_ns_dev_list.dev_lock);
+ list_for_each_entry(dev_file, &g_tc_ns_dev_list.dev_file_list, head) {
+ if (dev_file->dev_file_id == dev_file_id) {
+ mutex_unlock(&g_tc_ns_dev_list.dev_lock);
+ return dev_file;
+ }
+ }
+ mutex_unlock(&g_tc_ns_dev_list.dev_lock);
+ return NULL;
+}
+
+#ifdef CONFIG_COMPAT
+long tc_compat_client_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ long ret;
+
+ if (!file)
+ return -EINVAL;
+
+ arg = (unsigned long)(uintptr_t)compat_ptr(arg);
+ ret = tc_client_ioctl(file, cmd, arg);
+ return ret;
+}
+#endif
+
+static const struct file_operations g_tc_ns_client_fops = {
+ .owner = THIS_MODULE,
+ .open = tc_client_open,
+ .release = tc_client_close,
+ .unlocked_ioctl = tc_client_ioctl,
+ .mmap = tc_client_mmap,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = tc_compat_client_ioctl,
+#endif
+};
+
+#ifdef CONFIG_ACPI
+
+static int tzdriver_probe(struct platform_device *pdev)
+{
+ tlogd("tzdriver probe is running");
+ g_acpi_irq = platform_get_irq(pdev, 0);
+ if (g_acpi_irq < 0) {
+ dev_err(&pdev->dev, "get irq fail; irq:%d\n", g_acpi_irq);
+ return g_acpi_irq;
+ }
+
+ return 0;
+}
+
+int get_acpi_tz_irq(void)
+{
+ return g_acpi_irq;
+}
+
+static const struct acpi_device_id g_tzdriver_acpi_match[] = {
+ { "HISI03C1", 0 },
+ {},
+};
+
+MODULE_DEVICE_TABLE(acpi, g_tzdriver_acpi_match);
+
+#else
+
+static int tzdriver_probe(struct platform_device *pdev)
+{
+ return 0;
+}
+
+struct of_device_id g_tzdriver_platform_match[] = {
+ { .compatible = "trusted_core" },
+ {},
+};
+
+MODULE_DEVICE_TABLE(of, g_tzdriver_platform_match);
+
+#endif
+
+const struct dev_pm_ops g_tzdriver_pm_ops = {
+ .freeze_noirq = tc_s4_pm_suspend,
+ .restore_noirq = tc_s4_pm_resume,
+};
+
+static struct platform_driver g_tz_platform_driver = {
+ .driver = {
+ .name = "trusted_core",
+ .owner = THIS_MODULE,
+#ifdef CONFIG_ACPI
+ .acpi_match_table = ACPI_PTR(g_tzdriver_acpi_match),
+#else
+ .of_match_table = of_match_ptr(g_tzdriver_platform_match),
+#endif
+ .pm = &g_tzdriver_pm_ops,
+ },
+ .probe = tzdriver_probe,
+};
+
+static int create_cdev(void)
+{
+ int ret;
+
+ cdev_init(&g_tc_ns_client_cdev, &g_tc_ns_client_fops);
+ g_tc_ns_client_cdev.owner = THIS_MODULE;
+
+ ret = cdev_add(&g_tc_ns_client_cdev,
+ MKDEV(MAJOR(g_tc_ns_client_devt), 0), 1);
+ if (ret < 0) {
+ tloge("cdev_add failed %d", ret);
+ return -ENOENT;
+ }
+
+ return 0;
+}
+
+static int load_hw_info(void)
+{
+ if (platform_driver_register(&g_tz_platform_driver)) {
+ tloge("platform register driver failed\n");
+ return -EFAULT;
+ }
+
+ /* load hardware info from dts and acpi */
+ g_dev_node = of_find_compatible_node(NULL, NULL, "trusted_core");
+ if (!g_dev_node) {
+ tloge("no trusted_core compatible node found\n");
+#ifndef CONFIG_ACPI
+ return -ENODEV;
+#endif
+ }
+
+ return 0;
+}
+
+static int tc_ns_client_init(struct device **class_dev)
+{
+ int ret;
+
+ tlogd("tc_ns_client_init");
+ ret = load_hw_info();
+ if (ret)
+ return ret;
+
+ ret = load_reserved_mem();
+ if (ret)
+ return ret;
+
+ if (alloc_chrdev_region(&g_tc_ns_client_devt, 0, 1,
+ TC_NS_CLIENT_DEV)) {
+ tloge("alloc chrdev region failed");
+ return -EFAULT;
+ }
+
+ g_driver_class = class_create(THIS_MODULE, TC_NS_CLIENT_DEV);
+ if (IS_ERR_OR_NULL(g_driver_class)) {
+ tloge("class create failed");
+ ret = -ENOMEM;
+ goto chrdev_region_unregister;
+ }
+
+ *class_dev = device_create(g_driver_class, NULL, g_tc_ns_client_devt,
+ NULL, TC_NS_CLIENT_DEV);
+ if (IS_ERR_OR_NULL(*class_dev)) {
+ tloge("class device create failed");
+ ret = -ENOMEM;
+ goto destroy_class;
+ }
+
+ (*class_dev)->of_node = g_dev_node;
+ if (create_cdev())
+ goto class_device_destroy;
+
+ INIT_LIST_HEAD(&g_tc_ns_dev_list.dev_file_list);
+ mutex_init(&g_tc_ns_dev_list.dev_lock);
+ init_crypto_hash_lock();
+ init_srvc_list();
+ return ret;
+
+class_device_destroy:
+ device_destroy(g_driver_class, g_tc_ns_client_devt);
+destroy_class:
+ class_destroy(g_driver_class);
+chrdev_region_unregister:
+ unregister_chrdev_region(g_tc_ns_client_devt, 1);
+ return ret;
+}
+
+static int tc_teeos_init(struct device *class_dev)
+{
+ int ret;
+
+ ret = smc_context_init(class_dev);
+ if (ret) {
+ tloge("smc context init failed\n");
+ return ret;
+ }
+
+ ret = reserved_mempool_init();
+ if (ret) {
+ tloge("reserved memory init failed\n");
+ goto smc_data_free;
+ }
+
+ ret = mailbox_mempool_init();
+ if (ret) {
+ tloge("tz mailbox init failed\n");
+ goto smc_data_free;
+ }
+
+ ret = tz_spi_init(class_dev, g_dev_node);
+ if (ret) {
+ tloge("tz spi init failed\n");
+ goto release_mempool;
+ }
+ return 0;
+release_mempool:
+ mailbox_mempool_destroy();
+ free_reserved_mempool();
+smc_data_free:
+ smc_free_data();
+ return ret;
+}
+
+static void tc_re_init(const struct device *class_dev)
+{
+ int ret = 0;
+
+ agent_init();
+
+#ifdef CONFIG_TZDRIVER_MODULE
+ ret = init_tlogger_service();
+ if (ret)
+ tloge("tlogger init failed\n");
+#endif
+ if (tzdebug_init())
+ tloge("tzdebug init failed\n");
+
+#ifndef CONFIG_MINI_PLATFORM
+ if (init_smc_svc_thread()) {
+ tloge("init svc thread\n");
+ ret = -EFAULT;
+ }
+#endif
+
+ if (ret)
+ tloge("Caution! Running environment init failed!\n");
+}
+
+static __init int tc_init(void)
+{
+ struct device *class_dev = NULL;
+ int ret = 0;
+
+ init_kthread_cpumask();
+ ret = tc_ns_client_init(&class_dev);
+ if (ret)
+ return ret;
+
+ ret = tc_teeos_init(class_dev);
+ if (ret) {
+ tloge("tc teeos init failed\n");
+ goto class_device_destroy;
+ }
+ /* run-time environment init failure don't block tzdriver init proc */
+ tc_re_init(class_dev);
+ return 0;
+
+class_device_destroy:
+ device_destroy(g_driver_class, g_tc_ns_client_devt);
+ class_destroy(g_driver_class);
+ unregister_chrdev_region(g_tc_ns_client_devt, 1);
+
+ return ret;
+}
+
+static void tc_exit(void)
+{
+ tlogd("tz client exit");
+ tz_spi_exit();
+ /* run-time environment exit should before teeos exit */
+ device_destroy(g_driver_class, g_tc_ns_client_devt);
+ class_destroy(g_driver_class);
+ unregister_chrdev_region(g_tc_ns_client_devt, 1);
+ smc_free_data();
+ agent_exit();
+#ifdef CONFIG_TZDRIVER_MODULE
+ tzdebug_exit();
+ exit_tlogger_service();
+#endif
+ mailbox_mempool_destroy();
+ free_reserved_mempool();
+ tee_exit_shash_handle();
+}
+
+MODULE_AUTHOR("TEE");
+MODULE_DESCRIPTION("TrustCore ns-client driver");
+MODULE_VERSION("1.10");
+
+#ifdef CONFIG_TZDRIVER_MODULE
+module_init(tc_init);
+#else
+fs_initcall_sync(tc_init);
+#endif
+module_exit(tc_exit);
+MODULE_LICENSE("GPL");
diff --git a/linux/core/tc_client_driver.h b/linux/core/tc_client_driver.h
new file mode 100644
index 0000000..5291df3
--- /dev/null
+++ b/linux/core/tc_client_driver.h
@@ -0,0 +1,34 @@
+/*
+ * tc_client_driver.h
+ *
+ * function declaration for proc open,close session and invoke
+ *
+ * Copyright (C) 2022 Huawei Technologies Co., Ltd.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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 TC_CLIENT_DRIVER_H
+#define TC_CLIENT_DRIVER_H
+
+#include
+#include "teek_ns_client.h"
+
+struct tc_ns_dev_list *get_dev_list(void);
+uint32_t tc_ns_get_uid(void);
+struct tc_ns_dev_file *tc_find_dev_file(unsigned int dev_file_id);
+int tc_ns_client_open(struct tc_ns_dev_file **dev_file, uint8_t kernel_api);
+int tc_ns_client_close(struct tc_ns_dev_file *dev);
+int is_agent_alive(unsigned int agent_id);
+
+#ifdef CONFIG_ACPI
+int get_acpi_tz_irq(void);
+#endif
+
+#endif
diff --git a/linux/core/tee_compat_check.c b/linux/core/tee_compat_check.c
new file mode 100644
index 0000000..0e344fb
--- /dev/null
+++ b/linux/core/tee_compat_check.c
@@ -0,0 +1,48 @@
+/*
+ * tee_compat_check.c
+ *
+ * check compatibility between tzdriver and tee.
+ *
+ * Copyright (C) 2022 Huawei Technologies Co., Ltd.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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 "tee_compat_check.h"
+#include
+#include
+#include "teek_ns_client.h"
+#include "tc_ns_log.h"
+
+int32_t check_teeos_compat_level(uint32_t *buffer, uint32_t size)
+{
+ const uint16_t major = TEEOS_COMPAT_LEVEL_MAJOR;
+ const uint16_t minor = TEEOS_COMPAT_LEVEL_MINOR;
+
+ if (!buffer || size != COMPAT_LEVEL_BUF_LEN) {
+ tloge("check teeos compat level failed, invalid param\n");
+ return -EINVAL;
+ }
+
+ if (buffer[0] != VER_CHECK_MAGIC_NUM) {
+ tloge("check ver magic num %u failed\n", buffer[0]);
+ return -EPERM;
+ }
+ if (buffer[1] != major) {
+ tloge("check major ver failed, major tz=%u, major tee=%u\n",
+ major, buffer[1]);
+ return -EPERM;
+ }
+ /* just print warning */
+ if (buffer[2] != minor)
+ tlogw("check minor ver failed, minor tz=%u, minor tee=%u\n",
+ minor, buffer[2]);
+ return 0;
+}
diff --git a/linux/core/tee_compat_check.h b/linux/core/tee_compat_check.h
new file mode 100644
index 0000000..2cb85f9
--- /dev/null
+++ b/linux/core/tee_compat_check.h
@@ -0,0 +1,34 @@
+/*
+ * tee_compat_check.h
+ *
+ * check compatibility between tzdriver and teeos.
+ *
+ * Copyright (C) 2022 Huawei Technologies Co., Ltd.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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_COMPAT_CHECK_H
+#define TEE_COMPAT_CHECK_H
+
+#include
+
+/*
+ * this version number MAJOR.MINOR is used
+ * to identify the compatibility of tzdriver and teeos
+ */
+#define TEEOS_COMPAT_LEVEL_MAJOR 0
+#define TEEOS_COMPAT_LEVEL_MINOR 1
+
+#define VER_CHECK_MAGIC_NUM 0x5A5A5A5A
+#define COMPAT_LEVEL_BUF_LEN 12
+
+int32_t check_teeos_compat_level(uint32_t *buffer, uint32_t size);
+#endif
diff --git a/linux/core/teek_app_load.c b/linux/core/teek_app_load.c
new file mode 100644
index 0000000..ccf97b2
--- /dev/null
+++ b/linux/core/teek_app_load.c
@@ -0,0 +1,112 @@
+/*
+ * teek_app_load.c
+ *
+ * function declaration for load app operations for kernel CA.
+ *
+ * Copyright (C) 2022 Huawei Technologies Co., Ltd.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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 "teek_app_load.h"
+#include
+#include
+#include
+#include "session_manager.h"
+#include "ko_adapt.h"
+
+static int32_t teek_open_app_file(struct file *fp, char **file_buf, uint32_t total_img_len)
+{
+ loff_t pos = 0;
+ mm_segment_t old_fs;
+ uint32_t read_size;
+ char *file_buffer = NULL;
+
+ if (total_img_len == 0 || total_img_len > MAX_IMAGE_LEN) {
+ tloge("img len is invalied %u\n", total_img_len);
+ return TEEC_ERROR_BAD_PARAMETERS;
+ }
+
+ file_buffer = vmalloc(total_img_len);
+ if (!file_buffer) {
+ tloge("alloc TA file buffer(size=%u) failed\n", total_img_len);
+ return TEEC_ERROR_GENERIC;
+ }
+
+ old_fs = get_fs();
+ set_fs(KERNEL_DS);
+
+ read_size = (uint32_t)koadpt_vfs_read(fp, file_buffer, total_img_len, &pos);
+
+ set_fs(old_fs);
+
+ if (read_size != total_img_len) {
+ tloge("read ta file failed, read size/total size=%u/%u\n", read_size, total_img_len);
+ vfree(file_buffer);
+ return TEEC_ERROR_GENERIC;
+ }
+
+ *file_buf = file_buffer;
+
+ return TEEC_SUCCESS;
+}
+
+static int32_t teek_read_app(const char *load_file, char **file_buf, uint32_t *file_len)
+{
+ int32_t ret;
+ struct file *fp = NULL;
+
+ fp = filp_open(load_file, O_RDONLY, 0);
+ if (!fp || IS_ERR(fp)) {
+ tloge("open file error %ld\n", PTR_ERR(fp));
+ return TEEC_ERROR_BAD_PARAMETERS;
+ }
+
+ *file_len = (uint32_t)(fp->f_inode->i_size);
+
+ ret = teek_open_app_file(fp, file_buf, *file_len);
+ if (ret != TEEC_SUCCESS)
+ tloge("do read app fail\n");
+
+ if (fp != NULL) {
+ filp_close(fp, 0);
+ fp = NULL;
+ }
+
+ return ret;
+}
+
+void teek_free_app(bool load_app_flag, char **file_buf)
+{
+ if (load_app_flag && file_buf != NULL && *file_buf != NULL) {
+ vfree(*file_buf);
+ *file_buf = NULL;
+ }
+}
+
+int32_t teek_get_app(const char *ta_path, char **file_buf, uint32_t *file_len)
+{
+ int32_t ret;
+
+ /* ta path is NULL or user CA means no need to load TA */
+ if (!ta_path || current->mm != NULL)
+ return TEEC_SUCCESS;
+
+ if (!file_buf || !file_len) {
+ tloge("load app params invalied\n");
+ return TEEC_ERROR_BAD_PARAMETERS;
+ }
+
+ ret = teek_read_app(ta_path, file_buf, file_len);
+ if (ret != TEEC_SUCCESS)
+ tloge("teec load app error %d\n", ret);
+
+ return ret;
+}
diff --git a/linux/core/teek_app_load.h b/linux/core/teek_app_load.h
new file mode 100644
index 0000000..ba7aacf
--- /dev/null
+++ b/linux/core/teek_app_load.h
@@ -0,0 +1,28 @@
+/*
+ * teek_load_api.h
+ *
+ * function declaration for load app operations for kernel CA.
+ *
+ * Copyright (C) 2022 Huawei Technologies Co., Ltd.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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 TEEK_APP_LOAD_H
+#define TEEK_APP_LOAD_H
+
+#include "teek_client_api.h"
+#include "tc_ns_client.h"
+
+#define MAX_IMAGE_LEN 0x800000 /* max image len */
+
+int32_t teek_get_app(const char *ta_path, char **file_buf, uint32_t *file_len);
+void teek_free_app(bool load_app_flag, char **file_buf);
+
+#endif
diff --git a/linux/core/teek_client_api.c b/linux/core/teek_client_api.c
new file mode 100644
index 0000000..0ca64c0
--- /dev/null
+++ b/linux/core/teek_client_api.c
@@ -0,0 +1,701 @@
+/*
+ * teek_client_api.c
+ *
+ * function definition for libteec interface for kernel CA.
+ *
+ * Copyright (C) 2022 Huawei Technologies Co., Ltd.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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 "teek_client_api.h"
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include "teek_client_id.h"
+#include "tc_ns_log.h"
+#include "tc_ns_client.h"
+#include "gp_ops.h"
+#include "session_manager.h"
+#include "tc_client_driver.h"
+#include "teek_app_load.h"
+
+static void encode_for_part_mem(struct tc_ns_client_context *context,
+ const struct teec_operation *oper, uint32_t idex, uint32_t *param_type)
+{
+ uint32_t diff = (uint32_t)TEEC_MEMREF_PARTIAL_INPUT -
+ (uint32_t)TEEC_MEMREF_TEMP_INPUT;
+
+ if (idex >= TEE_PARAM_NUM)
+ return;
+
+ if (param_type[idex] == TEEC_MEMREF_WHOLE) {
+ context->params[idex].memref.offset = 0;
+ context->params[idex].memref.size_addr =
+ (__u64)(uintptr_t)
+ (&(oper->params[idex].memref.parent->size));
+ } else {
+ context->params[idex].memref.offset =
+ oper->params[idex].memref.offset;
+ context->params[idex].memref.size_addr =
+ (__u64)(uintptr_t)
+ (&(oper->params[idex].memref.size));
+ }
+ if (oper->params[idex].memref.parent->is_allocated) {
+ context->params[idex].memref.buffer =
+ (__u64)(uintptr_t)
+ oper->params[idex].memref.parent->buffer;
+ } else {
+ context->params[idex].memref.buffer =
+ (__u64)(uintptr_t)
+ oper->params[idex].memref.parent->buffer +
+ oper->params[idex].memref.offset;
+ context->params[idex].memref.offset = 0;
+ }
+
+ /* translate the paramType to know the driver */
+ if (param_type[idex] == TEEC_MEMREF_WHOLE) {
+ switch (oper->params[idex].memref.parent->flags) {
+ case TEEC_MEM_INPUT:
+ param_type[idex] = TEEC_MEMREF_PARTIAL_INPUT;
+ break;
+ case TEEC_MEM_OUTPUT:
+ param_type[idex] = TEEC_MEMREF_PARTIAL_OUTPUT;
+ break;
+ case TEEC_MEM_INOUT:
+ param_type[idex] = TEEC_MEMREF_PARTIAL_INOUT;
+ break;
+ default:
+ param_type[idex] = TEEC_MEMREF_PARTIAL_INOUT;
+ break;
+ }
+ }
+
+ /* if not allocated, trans PARTIAL_XXX to MEMREF_TEMP_XXX */
+ if (!oper->params[idex].memref.parent->is_allocated)
+ param_type[idex] = param_type[idex] - diff;
+}
+
+static uint32_t proc_teek_encode(struct tc_ns_client_context *cli_context,
+ const struct teec_operation *operation)
+{
+ uint32_t param_type[TEE_PARAM_NUM];
+ uint32_t idex;
+
+ param_type[0] =
+ teec_param_type_get(operation->paramtypes, 0);
+ param_type[1] =
+ teec_param_type_get(operation->paramtypes, 1);
+ param_type[2] =
+ teec_param_type_get(operation->paramtypes, 2);
+ param_type[3] =
+ teec_param_type_get(operation->paramtypes, 3);
+ for (idex = 0; idex < TEE_PARAM_NUM; idex++) {
+ if (is_tmp_mem(param_type[idex])) {
+ cli_context->params[idex].memref.buffer =
+ (__u64)(uintptr_t)
+ (operation->params[idex].tmpref.buffer);
+ cli_context->params[idex].memref.size_addr =
+ (__u64)(uintptr_t)
+ (&operation->params[idex].tmpref.size);
+ } else if (is_ref_mem(param_type[idex])) {
+ encode_for_part_mem(cli_context, operation,
+ idex, param_type);
+ } else if (is_val_param(param_type[idex])) {
+ cli_context->params[idex].value.a_addr =
+ (__u64)(uintptr_t)
+ (&(operation->params[idex].value.a));
+ cli_context->params[idex].value.b_addr =
+ (__u64)(uintptr_t)
+ (&(operation->params[idex].value.b));
+ } else if (is_ion_param(param_type[idex])) {
+ cli_context->params[idex].value.a_addr =
+ (__u64)(uintptr_t)
+ (&(operation->params[idex].ionref.ion_share_fd));
+ cli_context->params[idex].value.b_addr =
+ (__u64)(uintptr_t)
+ (&(operation->params[idex].ionref.ion_size));
+ } else if (param_type[idex] == TEEC_NONE) {
+ /* do nothing */
+ } else {
+ tloge("param_type[%u]=%u not correct\n", idex,
+ param_type[idex]);
+ return TEEC_ERROR_BAD_PARAMETERS;
+ }
+ }
+ cli_context->param_types = teec_param_types(param_type[0],
+ param_type[1], param_type[2], param_type[3]);
+
+ tlogv("cli param type %u\n", cli_context->param_types);
+ return TEEC_SUCCESS;
+}
+
+static uint32_t teek_init_context(struct tc_ns_client_context *cli_context,
+ struct teec_uuid service_id, uint32_t session_id, uint32_t cmd_id,
+ const struct tc_ns_client_login *cli_login)
+{
+ uint32_t diff;
+
+ diff = (uint32_t)TEEC_MEMREF_PARTIAL_INPUT -
+ (uint32_t)TEEC_MEMREF_TEMP_INPUT;
+
+ if (memset_s(cli_context, sizeof(*cli_context),
+ 0x00, sizeof(*cli_context))) {
+ tloge("memset error, init cli context failed\n");
+ return TEEC_ERROR_BAD_PARAMETERS;
+ }
+
+ cli_context->returns.origin = TEEC_ORIGIN_COMMS;
+
+ if (memcpy_s(cli_context->uuid, sizeof(cli_context->uuid),
+ (uint8_t *)&service_id, sizeof(service_id))) {
+ tloge("memcpy error, init cli context failed\n");
+ return TEEC_ERROR_BAD_PARAMETERS;
+ }
+ cli_context->session_id = session_id;
+ cli_context->cmd_id = cmd_id;
+ cli_context->returns.code = 0;
+ cli_context->login.method = cli_login->method;
+ cli_context->login.mdata = cli_login->mdata;
+
+ return TEEC_SUCCESS;
+}
+
+static uint32_t teek_check_tmp_mem(
+ const struct teec_tempmemory_reference *tmpref)
+{
+ if (!tmpref->buffer || !tmpref->size) {
+ tloge("tmpref buffer is null, or size is zero\n");
+ return TEEC_ERROR_BAD_PARAMETERS;
+ }
+
+ return TEEC_SUCCESS;
+}
+
+static bool is_partical_mem(uint32_t param_type)
+{
+ if (param_type == TEEC_MEMREF_PARTIAL_INPUT ||
+ param_type == TEEC_MEMREF_PARTIAL_OUTPUT ||
+ param_type == TEEC_MEMREF_PARTIAL_INOUT)
+ return true;
+
+ return false;
+}
+
+static bool is_offset_invalid(
+ const struct teec_registeredmemory_reference *memref)
+{
+ if ((memref->offset + memref->size > memref->parent->size) ||
+ (memref->offset + memref->size < memref->offset) ||
+ (memref->offset + memref->size < memref->size))
+ return true;
+
+ return false;
+}
+
+static uint32_t teek_check_ref_mem(
+ const struct teec_registeredmemory_reference *memref,
+ uint32_t param_type)
+{
+ if (!memref->parent || !memref->parent->buffer) {
+ tloge("parent of memref is null, or the buffer is zero\n");
+ return TEEC_ERROR_BAD_PARAMETERS;
+ }
+ if (param_type == TEEC_MEMREF_PARTIAL_INPUT) {
+ if (!(memref->parent->flags & TEEC_MEM_INPUT))
+ return TEEC_ERROR_BAD_PARAMETERS;
+ } else if (param_type == TEEC_MEMREF_PARTIAL_OUTPUT) {
+ if (!(memref->parent->flags & TEEC_MEM_OUTPUT))
+ return TEEC_ERROR_BAD_PARAMETERS;
+ } else if (param_type == TEEC_MEMREF_PARTIAL_INOUT) {
+ if (!(memref->parent->flags & TEEC_MEM_INPUT))
+ return TEEC_ERROR_BAD_PARAMETERS;
+ if (!(memref->parent->flags & TEEC_MEM_OUTPUT))
+ return TEEC_ERROR_BAD_PARAMETERS;
+ } else if (param_type == TEEC_MEMREF_WHOLE) {
+ /* if type is TEEC_MEMREF_WHOLE, ignore it */
+ } else {
+ return TEEC_ERROR_BAD_PARAMETERS;
+ }
+
+ if (is_partical_mem(param_type)) {
+ if (is_offset_invalid(memref)) {
+ tloge("offset + size exceed the parent size\n");
+ return TEEC_ERROR_BAD_PARAMETERS;
+ }
+ }
+ return TEEC_SUCCESS;
+}
+
+/*
+ * This function checks a operation is valid or not.
+ */
+uint32_t teek_check_operation(const struct teec_operation *operation)
+{
+ uint32_t param_type[TEE_PARAM_NUM] = {0};
+ uint32_t idex;
+ uint32_t ret = TEEC_SUCCESS;
+
+ /*
+ * GP Support operation is NULL
+ * operation: a pointer to a Client Application initialized struct,
+ * or NULL if there is no payload to send or
+ * if the Command does not need to support cancellation.
+ */
+ if (!operation)
+ return TEEC_SUCCESS;
+
+ if (!operation->started) {
+ tloge("sorry, cancellation not support\n");
+ return TEEC_ERROR_NOT_IMPLEMENTED;
+ }
+
+ param_type[0] =
+ teec_param_type_get(operation->paramtypes, 0);
+ param_type[1] =
+ teec_param_type_get(operation->paramtypes, 1);
+ param_type[2] =
+ teec_param_type_get(operation->paramtypes, 2);
+ param_type[3] =
+ teec_param_type_get(operation->paramtypes, 3);
+ for (idex = 0; idex < TEE_PARAM_NUM; idex++) {
+ if (is_tmp_mem(param_type[idex])) {
+ ret = teek_check_tmp_mem(
+ &(operation->params[idex].tmpref));
+ if (ret != TEEC_SUCCESS)
+ break;
+ } else if (is_ref_mem(param_type[idex])) {
+ ret = teek_check_ref_mem(
+ &(operation->params[idex].memref),
+ param_type[idex]);
+ if (ret != TEEC_SUCCESS)
+ break;
+ } else if (is_val_param(param_type[idex])) {
+ /* do nothing */
+ } else if (is_ion_param(param_type[idex])) {
+ if (operation->params[idex].ionref.ion_share_fd < 0) {
+ tloge("ion_handle is invalid!\n");
+ ret = TEEC_ERROR_BAD_PARAMETERS;
+ break;
+ }
+ } else if (param_type[idex] == TEEC_NONE) {
+ /* do nothing */
+ } else {
+ tloge("paramType[%u]=%x is not support\n", idex,
+ param_type[idex]);
+ ret = TEEC_ERROR_BAD_PARAMETERS;
+ break;
+ }
+ }
+
+ return ret;
+}
+
+/*
+ * This function check if the special agent is launched.Used For HDCP key.
+ * e.g. If sfs agent is not alive, you can not do HDCP key write to SRAM.
+ */
+int teek_is_agent_alive(unsigned int agent_id)
+{
+ return is_agent_alive(agent_id);
+}
+
+/*
+ * This function initializes a new TEE Context,
+ * forming a connection between this Client Application
+ * and the TEE identified by the string identifier name.
+ */
+uint32_t teek_initialize_context(const char *name,
+ struct teec_context *context)
+{
+ int32_t ret;
+
+ /* name current not used */
+ (void)(name);
+ tlogd("teek_initialize_context Started:\n");
+ /* First, check parameters is valid or not */
+ if (!context) {
+ tloge("context is null, not correct\n");
+ return TEEC_ERROR_BAD_PARAMETERS;
+ }
+ context->dev = NULL;
+ context->ta_path = NULL;
+ /* Paramters right, start execution */
+ ret = tc_ns_client_open((struct tc_ns_dev_file **)&context->dev,
+ TEE_REQ_FROM_KERNEL_MODE);
+ if (ret != TEEC_SUCCESS) {
+ tloge("open device failed\n");
+ return TEEC_ERROR_GENERIC;
+ }
+ tlogd("open device success\n");
+ return TEEC_SUCCESS;
+}
+EXPORT_SYMBOL(teek_initialize_context);
+
+/*
+ * This function finalizes an initialized TEE Context.
+ */
+void teek_finalize_context(struct teec_context *context)
+{
+ tlogd("teek_finalize_context started\n");
+ if (!context || !context->dev) {
+ tloge("context or dev is null, not correct\n");
+ return;
+ }
+
+ tlogd("close device\n");
+ tc_ns_client_close(context->dev);
+ context->dev = NULL;
+}
+EXPORT_SYMBOL(teek_finalize_context);
+
+static bool is_oper_param_valid(const struct teec_operation *operation)
+{
+ uint32_t param_type[TEE_PARAM_NUM] = {0};
+
+ param_type[3] =
+ teec_param_type_get(operation->paramtypes, 3);
+ param_type[2] =
+ teec_param_type_get(operation->paramtypes, 2);
+
+ if (param_type[3] != TEEC_MEMREF_TEMP_INPUT ||
+ param_type[2] != TEEC_MEMREF_TEMP_INPUT) {
+ tloge("invalid param type 0x%x\n", operation->paramtypes);
+ return false;
+ }
+
+ if (!operation->params[3].tmpref.buffer ||
+ !operation->params[2].tmpref.buffer ||
+ operation->params[3].tmpref.size == 0 ||
+ operation->params[2].tmpref.size == 0) {
+ tloge("invalid operation params(NULL)\n");
+ return false;
+ }
+ return true;
+}
+
+static uint32_t check_open_sess_params(struct teec_context *context,
+ const struct teec_operation *operation)
+{
+ struct tc_ns_dev_file *dev_file = NULL;
+ uint32_t teec_ret;
+
+ if (!is_oper_param_valid(operation))
+ return TEEC_ERROR_BAD_PARAMETERS;
+
+ dev_file = (struct tc_ns_dev_file *)(context->dev);
+ if (!dev_file) {
+ tloge("invalid context->dev (NULL)\n");
+ return TEEC_ERROR_BAD_PARAMETERS;
+ }
+
+ dev_file->pkg_name_len = operation->params[3].tmpref.size;
+ if (operation->params[3].tmpref.size > MAX_PACKAGE_NAME_LEN - 1) {
+ return TEEC_ERROR_BAD_PARAMETERS;
+ } else {
+ if (memset_s(dev_file->pkg_name, sizeof(dev_file->pkg_name),
+ 0, MAX_PACKAGE_NAME_LEN)) {
+ tloge("memset error\n");
+ return TEEC_ERROR_BAD_PARAMETERS;
+ }
+ if (memcpy_s(dev_file->pkg_name, sizeof(dev_file->pkg_name),
+ operation->params[3].tmpref.buffer,
+ operation->params[3].tmpref.size)) {
+ tloge("memcpy error\n");
+ return TEEC_ERROR_BAD_PARAMETERS;
+ }
+ }
+
+ dev_file->pub_key_len = 0;
+ dev_file->login_setup = 1;
+ teec_ret = teek_check_operation(operation);
+ if (teec_ret != TEEC_SUCCESS) {
+ tloge("operation is invalid\n");
+ return TEEC_ERROR_BAD_PARAMETERS;
+ }
+
+ return teec_ret;
+}
+
+static uint32_t open_session_and_switch_ret(struct teec_session *session,
+ struct teec_context *context, const struct teec_uuid *destination,
+ struct tc_ns_client_context *cli_context, uint32_t *origin)
+{
+ int32_t ret;
+ uint32_t teec_ret;
+
+ ret = tc_ns_open_session(context->dev, cli_context);
+ if (!ret) {
+ tlogd("open session success\n");
+ session->session_id = cli_context->session_id;
+ session->service_id = *destination;
+ session->ops_cnt = 0;
+ session->context = context;
+ return TEEC_SUCCESS;
+ } else if (ret < 0) {
+ tloge("open session failed, ioctl errno = %d\n", ret);
+ if (ret == -EFAULT)
+ teec_ret = TEEC_ERROR_ACCESS_DENIED;
+ else if (ret == -ENOMEM)
+ teec_ret = TEEC_ERROR_OUT_OF_MEMORY;
+ else if (ret == -EINVAL)
+ teec_ret = TEEC_ERROR_BAD_PARAMETERS;
+ else if (ret == -ERESTARTSYS)
+ teec_ret = TEEC_CLIENT_INTR;
+ else
+ teec_ret = TEEC_ERROR_GENERIC;
+ *origin = TEEC_ORIGIN_COMMS;
+ return teec_ret;
+ } else {
+ tloge("open session failed, code=0x%x, origin=%u\n",
+ cli_context->returns.code,
+ cli_context->returns.origin);
+ teec_ret = (uint32_t)cli_context->returns.code;
+ *origin = cli_context->returns.origin;
+ }
+ return teec_ret;
+}
+
+static uint32_t proc_teek_open_session(struct teec_context *context,
+ struct teec_session *session, const struct teec_uuid *destination,
+ uint32_t connection_method, const void *connection_data,
+ const struct teec_operation *operation, uint32_t *return_origin)
+{
+ uint32_t teec_ret;
+ uint32_t origin = TEEC_ORIGIN_API;
+ struct tc_ns_client_context cli_context;
+ struct tc_ns_client_login cli_login = {0};
+ bool load_app_flag = false;
+
+ /* connectionData current not used */
+ (void)(connection_data);
+ if (return_origin)
+ *return_origin = origin;
+
+ /* First, check parameters is valid or not */
+ if (!context || !operation || !destination ||
+ !session || connection_method != TEEC_LOGIN_IDENTIFY) {
+ tloge("invalid input params\n");
+ teec_ret = TEEC_ERROR_BAD_PARAMETERS;
+ goto set_ori;
+ }
+
+ cli_login.method = TEEC_LOGIN_IDENTIFY;
+ teec_ret = check_open_sess_params(context, operation);
+ if (teec_ret != TEEC_SUCCESS)
+ goto set_ori;
+
+ teec_ret = teek_init_context(&cli_context, *destination, 0,
+ GLOBAL_CMD_ID_OPEN_SESSION, &cli_login);
+ if (teec_ret != TEEC_SUCCESS)
+ goto set_ori;
+
+ /* support when operation is null */
+ if (operation) {
+ cli_context.started = operation->cancel_flag;
+ teec_ret = proc_teek_encode(&cli_context, operation);
+ if (teec_ret != TEEC_SUCCESS)
+ goto set_ori;
+ }
+
+ teec_ret = teek_get_app(context->ta_path, &cli_context.file_buffer,
+ &cli_context.file_size);
+ if (teec_ret != TEEC_SUCCESS)
+ goto set_ori;
+ load_app_flag = true;
+
+ teec_ret = open_session_and_switch_ret(session, context,
+ destination, &cli_context, &origin);
+
+set_ori:
+ if (teec_ret != TEEC_SUCCESS && return_origin != NULL)
+ *return_origin = origin;
+
+ teek_free_app(load_app_flag, &cli_context.file_buffer);
+ return teec_ret;
+}
+
+#define RETRY_TIMES 5
+uint32_t teek_open_session(struct teec_context *context,
+ struct teec_session *session, const struct teec_uuid *destination,
+ uint32_t connection_method, const void *connection_data,
+ const struct teec_operation *operation, uint32_t *return_origin)
+{
+ int i;
+ uint32_t ret;
+
+ for (i = 0; i < RETRY_TIMES; i++) {
+ ret = proc_teek_open_session(context, session,
+ destination, connection_method, connection_data,
+ operation, return_origin);
+ if (ret != (uint32_t)TEEC_CLIENT_INTR)
+ return ret;
+ }
+ return ret;
+}
+EXPORT_SYMBOL(teek_open_session);
+
+/*
+ * This function closes an opened Session.
+ */
+
+static bool is_close_sess_param_valid(struct teec_session *session)
+{
+ tlogd("teek_close_session started\n");
+
+ if (!session || !session->context || !session->context->dev) {
+ tloge("input invalid param\n");
+ return false;
+ }
+
+ return true;
+}
+
+void teek_close_session(struct teec_session *session)
+{
+ int32_t ret;
+ struct tc_ns_client_context cli_context;
+ struct tc_ns_client_login cli_login = {0};
+
+ if (!is_close_sess_param_valid(session))
+ return;
+
+ if (teek_init_context(&cli_context, session->service_id,
+ session->session_id, GLOBAL_CMD_ID_CLOSE_SESSION,
+ &cli_login) != TEEC_SUCCESS) {
+ tloge("init cli context failed just return\n");
+ return;
+ }
+ ret = tc_ns_close_session(session->context->dev, &cli_context);
+ if (!ret) {
+ session->session_id = 0;
+ if (memset_s((uint8_t *)(&session->service_id),
+ sizeof(session->service_id), 0x00, UUID_LEN))
+ tloge("memset error\n");
+ session->ops_cnt = 0;
+ session->context = NULL;
+ } else {
+ tloge("close session failed\n");
+ }
+}
+EXPORT_SYMBOL(teek_close_session);
+
+static uint32_t proc_invoke_cmd(struct teec_session *session,
+ struct tc_ns_client_context *cli_context, uint32_t *origin)
+{
+ int32_t ret;
+ uint32_t teec_ret;
+
+ ret = tc_ns_send_cmd(session->context->dev, cli_context);
+ if (!ret) {
+ tlogd("invoke cmd success\n");
+ teec_ret = TEEC_SUCCESS;
+ } else if (ret < 0) {
+ tloge("invoke cmd failed, ioctl errno = %d\n", ret);
+ if (ret == -EFAULT)
+ teec_ret = TEEC_ERROR_ACCESS_DENIED;
+ else if (ret == -ENOMEM)
+ teec_ret = TEEC_ERROR_OUT_OF_MEMORY;
+ else if (ret == -EINVAL)
+ teec_ret = TEEC_ERROR_BAD_PARAMETERS;
+ else
+ teec_ret = TEEC_ERROR_GENERIC;
+ *origin = TEEC_ORIGIN_COMMS;
+ } else {
+ tloge("invoke cmd failed, code=0x%x, origin=%d\n",
+ cli_context->returns.code,
+ cli_context->returns.origin);
+ teec_ret = (uint32_t)cli_context->returns.code;
+ *origin = cli_context->returns.origin;
+ }
+ return teec_ret;
+}
+
+/* This function invokes a Command within the specified Session. */
+uint32_t teek_invoke_command(struct teec_session *session, uint32_t cmd_id,
+ struct teec_operation *operation, uint32_t *return_origin)
+{
+ uint32_t teec_ret = TEEC_ERROR_BAD_PARAMETERS;
+ uint32_t origin = TEEC_ORIGIN_API;
+ struct tc_ns_client_context cli_context;
+ struct tc_ns_client_login cli_login = { 0, 0 };
+
+ /* First, check parameters is valid or not */
+ if (!session || !session->context) {
+ tloge("input invalid session or session->context is null\n");
+ goto set_ori;
+ }
+
+ teec_ret = teek_check_operation(operation);
+ if (teec_ret) {
+ tloge("operation is invalid\n");
+ goto set_ori;
+ }
+
+ /* Paramters all right, start execution */
+ teec_ret = teek_init_context(&cli_context, session->service_id,
+ session->session_id, cmd_id, &cli_login);
+ if (teec_ret) {
+ tloge("init cli context failed\n");
+ goto set_ori;
+ }
+
+ /* support when operation is null */
+ if (operation) {
+ cli_context.started = operation->cancel_flag;
+ teec_ret = proc_teek_encode(&cli_context, operation);
+ if (teec_ret) {
+ goto set_ori;
+ }
+ }
+
+ teec_ret = proc_invoke_cmd(session, &cli_context, &origin);
+
+set_ori:
+ if (teec_ret && return_origin)
+ *return_origin = origin;
+ return teec_ret;
+}
+EXPORT_SYMBOL(teek_invoke_command);
+
+uint32_t teek_send_secfile(struct teec_session *session,
+ const char *file_buffer, unsigned int file_size)
+{
+ if (!file_buffer || !file_size || !session ||
+ !session->context || !session->context->dev) {
+ tloge("params error!\n");
+ return TEEC_ERROR_BAD_PARAMETERS;
+ }
+
+ return (uint32_t)tc_ns_load_image_with_lock(session->context->dev,
+ file_buffer, file_size, LOAD_TA);
+}
+EXPORT_SYMBOL(teek_send_secfile);
+
+/*
+ * This function registers a block of existing Client Application memory
+ * as a block of Shared Memory within the scope of the specified TEE Context.
+ */
+uint32_t teek_register_shared_memory(struct teec_context *context,
+ struct teec_sharedmemory *sharedmem)
+{
+ tloge("teek_register_shared_memory not supported\n");
+ return TEEC_ERROR_NOT_SUPPORTED;
+}
+
diff --git a/linux/core/tz_pm.c b/linux/core/tz_pm.c
new file mode 100644
index 0000000..eea272a
--- /dev/null
+++ b/linux/core/tz_pm.c
@@ -0,0 +1,331 @@
+/*
+ * tz_pm.c
+ *
+ * function for proc open,close session and invoke
+ *
+ * Copyright (C) 2022 Huawei Technologies Co., Ltd.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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 "tz_pm.h"
+#include
+#include
+#include
+#include
+#include
+#include "tc_ns_client.h"
+#include "teek_ns_client.h"
+#include "tc_ns_log.h"
+
+#define S4_ADDR_4G 0xffffffff
+#define RESERVED_SECOS_PHYMEM_BASE 0x22800000
+#define RESERVED_SECOS_PHYMEM_SIZE (0x3000000)
+#define RESERVED_SECOS_S4_BASE 0x27760000
+#define RESERVED_SECOS_S4_SIZE (0x100000)
+
+static char *g_s4_kernel_mem_addr;
+static char *g_s4_buffer_vaddr;
+static uint64_t g_s4_buffer_paddr;
+static uint32_t g_s4_buffer_size;
+
+static void *tc_vmap(phys_addr_t paddr, size_t size)
+{
+ uint32_t i;
+ void *vaddr = NULL;
+ pgprot_t pgprot = PAGE_KERNEL;
+ uintptr_t offset;
+ uint32_t pages_count;
+ struct page **pages = NULL;
+
+ offset = paddr & ~PAGE_MASK;
+ paddr &= PAGE_MASK;
+ pages_count = PAGE_ALIGN(size + offset) / PAGE_SIZE;
+
+ pages = kzalloc(sizeof(struct page *) * pages_count, GFP_KERNEL);
+ if (pages == NULL)
+ return NULL;
+
+ for (i = 0; i < pages_count; i++)
+ *(pages + i) = phys_to_page((uintptr_t)(paddr + PAGE_SIZE * i));
+
+ vaddr = vmap(pages, pages_count, VM_MAP, pgprot);
+ kfree(pages);
+ if (vaddr == NULL)
+ return NULL;
+
+ return offset + (char *)vaddr;
+}
+
+static int tc_s4_alloc_crypto_buffer(struct device *dev,
+ char **kernel_mem_addr)
+{
+ if (RESERVED_SECOS_S4_BASE > S4_ADDR_4G) {
+ tloge("addr is invalid\n");
+ return -EFAULT;
+ }
+
+ g_s4_buffer_vaddr = tc_vmap(RESERVED_SECOS_S4_BASE, RESERVED_SECOS_S4_SIZE);
+ if (g_s4_buffer_vaddr == NULL) {
+ tloge("vmap failed for s4\n");
+ return -EFAULT;
+ }
+ g_s4_buffer_paddr = RESERVED_SECOS_S4_BASE;
+ g_s4_buffer_size = RESERVED_SECOS_S4_SIZE;
+
+ *kernel_mem_addr = vmalloc(RESERVED_SECOS_PHYMEM_SIZE);
+ if (*kernel_mem_addr == NULL) {
+ vunmap(g_s4_buffer_vaddr);
+ g_s4_buffer_paddr = 0;
+ g_s4_buffer_vaddr = NULL;
+ g_s4_buffer_size = 0;
+ tloge("vmalloc failed for s4\n");
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static void free_resource(const char *kernel_mem_addr)
+{
+ vunmap(g_s4_buffer_vaddr);
+ vfree(kernel_mem_addr);
+ g_s4_kernel_mem_addr = NULL;
+ g_s4_buffer_paddr = 0;
+ g_s4_buffer_vaddr = NULL;
+ g_s4_buffer_size = 0;
+}
+
+#ifndef CONFIG_ARM
+static uint64_t tc_s4_suspend_or_resume(uint32_t power_op)
+{
+ u64 smc_id = (u64)power_op;
+ u64 smc_ret = 0xffff;
+
+ do {
+ asm volatile (
+ "mov x0, %[fid]\n"
+ "smc #0\n"
+ "str x0, [%[re0]]\n" :
+ [fid] "+r"(smc_id) :
+ [re0] "r"(&smc_ret) :
+ "x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7",
+ "x8", "x9", "x10", "x11", "x12", "x13",
+ "x14", "x15", "x16", "x17");
+ } while (0);
+
+ isb();
+ wmb();
+ return smc_ret;
+}
+
+static uint64_t tc_s4_crypto_and_copy(uint32_t crypt_op,
+ uint64_t middle_mem_addr,
+ uintptr_t secos_mem,
+ uint32_t size, uint32_t index)
+{
+ u64 smc_id = (u64)crypt_op;
+ u64 arg0 = (u64)middle_mem_addr;
+ u64 arg1 = (u64)secos_mem;
+ u64 arg2 = (u64)size;
+ u64 arg3 = (u64)index;
+ u64 smc_ret = 0xffff;
+
+ do {
+ asm volatile (
+ "mov x0, %[fid]\n"
+ "mov x1, %[a1]\n"
+ "mov x2, %[a2]\n"
+ "mov x3, %[a3]\n"
+ "mov x4, %[a4]\n"
+ "smc #0\n"
+ "str x0, [%[re0]]\n" :
+ [fid] "+r"(smc_id),
+ [a1] "+r"(arg0),
+ [a2] "+r"(arg1),
+ [a3] "+r"(arg2),
+ [a4] "+r"(arg3) :
+ [re0] "r"(&smc_ret) :
+ "x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7",
+ "x8", "x9", "x10", "x11", "x12", "x13",
+ "x14", "x15", "x16", "x17");
+ } while (0);
+
+ isb();
+ wmb();
+ return smc_ret;
+}
+#else
+static uint32_t tc_s4_suspend_or_resume(uint32_t power_op)
+{
+ u32 smc_id = power_op;
+ u32 smc_ret = 0xffff;
+
+ do {
+ asm volatile (
+ "mov r0, %[fid]\n"
+ ".arch_extension sec\n"
+ "smc #0\n"
+ "str r0, [%[re0]]\n" :
+ [fid] "+r"(smc_id) :
+ [re0] "r"(&smc_ret) :
+ "r0");
+ } while (0);
+
+ isb();
+ wmb();
+ return smc_ret;
+}
+
+static uint32_t tc_s4_crypto_and_copy(uint32_t crypt_op,
+ uint64_t middle_mem_addr,
+ uintptr_t secos_mem,
+ uint32_t size, uint32_t index)
+{
+ u32 smc_id = crypt_op;
+ u32 arg0 = (u32)middle_mem_addr;
+ u32 arg1 = (u32)secos_mem;
+ u32 arg2 = size;
+ u32 arg3 = index;
+ u32 smc_ret = 0xffff;
+
+ do {
+ asm volatile (
+ "mov r0, %[fid]\n"
+ "mov r1, %[a1]\n"
+ "mov r2, %[a2]\n"
+ "mov r3, %[a3]\n"
+ "mov r4, %[a4]\n"
+ ".arch_extension sec\n"
+ "smc #0\n"
+ "str r0, [%[re0]]\n" :
+ [fid] "+r"(smc_id),
+ [a1] "+r"(arg0),
+ [a2] "+r"(arg1),
+ [a3] "+r"(arg2),
+ [a4] "+r"(arg3) :
+ [re0] "r"(&smc_ret) :
+ "r0", "r1", "r2", "r3", "r4");
+ } while (0);
+
+ isb();
+ wmb();
+ return smc_ret;
+}
+#endif
+
+static int tc_s4_transfer_data(char *kernel_mem_addr, uint32_t crypt_op)
+{
+ uint32_t index = 0;
+ uint32_t copied_size = 0;
+
+ while (copied_size < RESERVED_SECOS_PHYMEM_SIZE) {
+ if (crypt_op == TSP_S4_DECRYPT_AND_COPY) {
+ if (memcpy_s(g_s4_buffer_vaddr, g_s4_buffer_size,
+ kernel_mem_addr + copied_size,
+ g_s4_buffer_size) != EOK) {
+ tloge("mem copy for decrypt failed\n");
+ return -EFAULT;
+ }
+ }
+
+ if (tc_s4_crypto_and_copy(crypt_op, g_s4_buffer_paddr,
+ RESERVED_SECOS_PHYMEM_BASE + copied_size,
+ g_s4_buffer_size, index) != 0) {
+ tloge("crypto and copy failed\n");
+ return -EFAULT;
+ }
+
+ if (crypt_op == TSP_S4_ENCRYPT_AND_COPY) {
+ if (memcpy_s(kernel_mem_addr + copied_size,
+ g_s4_buffer_size, g_s4_buffer_vaddr,
+ g_s4_buffer_size) != EOK) {
+ tloge("mem copy for encrypt failed\n");
+ return -EFAULT;
+ }
+ }
+
+ copied_size += g_s4_buffer_size;
+ index++;
+ }
+
+ return 0;
+}
+
+static int tc_s4_pm_ops(struct device *dev, uint32_t power_op,
+ uint32_t crypt_op, char *kernel_mem_addr)
+{
+ int ret;
+
+ if (power_op == TSP_S4_SUSPEND)
+ g_s4_kernel_mem_addr = kernel_mem_addr;
+ else
+ kernel_mem_addr = g_s4_kernel_mem_addr;
+
+ isb();
+ wmb();
+
+ /* notify TEEOS to suspend all pm driver */
+ if (power_op == TSP_S4_SUSPEND) {
+ ret = tc_s4_suspend_or_resume(power_op);
+ if (ret != 0) {
+ tloge("tc s4 suspend failed\n");
+ return ret;
+ }
+ }
+
+ ret = tc_s4_transfer_data(kernel_mem_addr, crypt_op);
+ if (ret != 0) {
+ tloge("transfer data failed, power_op=0x%x\n", power_op);
+ return ret;
+ }
+
+ /* notify TEEOS to resume all pm driver */
+ if (power_op == TSP_S4_RESUME) {
+ ret = tc_s4_suspend_or_resume(power_op);
+ if (ret != 0) {
+ tloge("tc s4 resume failed\n");
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+int tc_s4_pm_suspend(struct device *dev)
+{
+ int ret;
+ char *kernel_mem_addr = NULL;
+
+ ret = tc_s4_alloc_crypto_buffer(dev, &kernel_mem_addr);
+ if (ret != 0) {
+ tloge("alloc buffer failed\n");
+ return ret;
+ }
+
+ ret = tc_s4_pm_ops(dev, TSP_S4_SUSPEND, TSP_S4_ENCRYPT_AND_COPY, kernel_mem_addr);
+ if (ret != 0) {
+ free_resource(kernel_mem_addr);
+ tloge("s4 suspend failed\n");
+ }
+
+ return ret;
+}
+
+int tc_s4_pm_resume(struct device *dev)
+{
+ int ret;
+
+ ret = tc_s4_pm_ops(dev, TSP_S4_RESUME, TSP_S4_DECRYPT_AND_COPY, g_s4_kernel_mem_addr);
+ if (ret != 0)
+ tloge("s4 resume failed\n");
+
+ free_resource(g_s4_kernel_mem_addr);
+ return ret;
+}
diff --git a/linux/core/tz_pm.h b/linux/core/tz_pm.h
new file mode 100644
index 0000000..d7af0b2
--- /dev/null
+++ b/linux/core/tz_pm.h
@@ -0,0 +1,31 @@
+/*
+ * tz_pm.h
+ *
+ * suspend or freeze func declaration for tzdriver
+ *
+ * Copyright (C) 2022 Huawei Technologies Co., Ltd.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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 TZ_PM_H
+#define TZ_PM_H
+#include
+
+#define TSP_S4_SUSPEND 0xB200000C
+#define TSP_S4_RESUME 0xB200000D
+#define TSP_S4_ENCRYPT_AND_COPY 0xB2000010
+#define TSP_S4_DECRYPT_AND_COPY 0xB2000011
+
+int tc_s4_pm_suspend(struct device *dev);
+
+int tc_s4_pm_resume(struct device *dev);
+
+#endif
diff --git a/linux/core/tz_spi_notify.c b/linux/core/tz_spi_notify.c
new file mode 100644
index 0000000..2e6ec9c
--- /dev/null
+++ b/linux/core/tz_spi_notify.c
@@ -0,0 +1,840 @@
+/*
+ * tz_spi_notify.c
+ *
+ * exported funcs for spi interrupt actions
+ *
+ * Copyright (C) 2022 Huawei Technologies Co., Ltd.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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 "tz_spi_notify.h"
+#include
+#include
+#include
+#include