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 +#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 "tc_ns_log.h" +#include "tc_client_driver.h" +#include "gp_ops.h" +#include "mailbox_mempool.h" +#include "smc_smp.h" +#include "session_manager.h" +#include "tz_kthread_affinity.h" + +#define DEFAULT_SPI_NUM 111 + +#define MAX_CALLBACK_COUNT 100 +#define UUID_SIZE 16 +struct teec_timer_property; + +#ifdef DEF_ENG +static int g_timer_type; +#endif + +enum timer_class_type { + /* timer event using timer10 */ + TIMER_GENERIC, + /* timer event using RTC */ + TIMER_RTC +}; + +struct teec_timer_property { + unsigned int type; + unsigned int timer_id; + unsigned int timer_class; + unsigned int reserved2; +}; + +struct notify_context_timer { + unsigned int dev_file_id; + unsigned char uuid[UUID_SIZE]; + unsigned int session_id; + struct teec_timer_property property; + uint32_t expire_time; +}; + + +struct notify_context_wakeup { + pid_t ca_thread_id; +}; + +struct notify_context_shadow { + uint64_t target_tcb; +}; + +#ifdef CONFIG_TA_AFFINITY + +#define AFF_BITS_SIZE 64 + +#define AFF_BITS_NUM ((CONFIG_TA_AFFINITY_CPU_NUMS % AFF_BITS_SIZE == 0) ? \ + (CONFIG_TA_AFFINITY_CPU_NUMS / AFF_BITS_SIZE) : \ + (CONFIG_TA_AFFINITY_CPU_NUMS / AFF_BITS_SIZE + 1)) + +#define aff_bits_mask(cpuid) \ + (1LLU << (cpuid - (cpuid / AFF_BITS_SIZE) * AFF_BITS_SIZE)) + +struct aff_bits_t { + uint64_t aff_bits[AFF_BITS_NUM]; +}; + +struct notify_context_set_affinity { + pid_t ca_thread_id; + struct aff_bits_t aff; +}; + +#endif + +struct notify_context_stats { + uint32_t send_s; + uint32_t recv_s; + uint32_t send_w; + uint32_t recv_w; +#ifdef CONFIG_TA_AFFINITY + uint32_t send_af; + uint32_t recv_af; +#endif + uint32_t missed; +}; + +union notify_context { + struct notify_context_timer timer; + struct notify_context_wakeup wakeup; + struct notify_context_shadow shadow; +#ifdef CONFIG_TA_AFFINITY + struct notify_context_set_affinity affinity; +#endif + struct notify_context_stats meta; +}; + +struct notify_data_entry { + uint32_t entry_type : 31; + uint32_t filled : 1; + union notify_context context; +}; + +#ifdef CONFIG_BIG_SESSION + +#define NOTIFY_DATA_ENTRY_COUNT \ + (((PAGE_SIZE * ((1U) << (CONFIG_NOTIFY_PAGE_ORDER))) \ + / sizeof(struct notify_data_entry)) - 1) +#else +#define NOTIFY_DATA_ENTRY_COUNT \ + ((PAGE_SIZE / sizeof(struct notify_data_entry)) - 1) +#endif + +struct notify_data_struct { + struct notify_data_entry entry[NOTIFY_DATA_ENTRY_COUNT]; + struct notify_data_entry meta; +}; + +static struct notify_data_struct *g_notify_data; +static struct notify_data_entry *g_notify_data_entry_shadow; +static spinlock_t g_notify_lock; + +enum notify_data_type { + NOTIFY_DATA_ENTRY_UNUSED, + NOTIFY_DATA_ENTRY_TIMER, + NOTIFY_DATA_ENTRY_RTC, + NOTIFY_DATA_ENTRY_WAKEUP, + NOTIFY_DATA_ENTRY_SHADOW, + NOTIFY_DATA_ENTRY_FIQSHD, + NOTIFY_DATA_ENTRY_SHADOW_EXIT, +#ifdef CONFIG_TA_AFFINITY + NOTIFY_DATA_ENTRY_SET_AFFINITY, +#endif + NOTIFY_DATA_ENTRY_MAX, +}; + +struct tc_ns_callback { + unsigned char uuid[UUID_SIZE]; + struct mutex callback_lock; + void (*callback_func)(void *); + struct list_head head; +}; + +struct tc_ns_callback_list { + unsigned int callback_count; + struct mutex callback_list_lock; + struct list_head callback_list; +}; + +static void tc_notify_fn(struct work_struct *dummy); +static struct tc_ns_callback_list g_ta_callback_func_list; +static DECLARE_WORK(tc_notify_work, tc_notify_fn); +static struct workqueue_struct *g_tz_spi_wq; + +static void walk_callback_list( + struct notify_context_timer *tc_notify_data_timer) +{ + struct tc_ns_callback *callback_func_t = NULL; + + mutex_lock(&g_ta_callback_func_list.callback_list_lock); + list_for_each_entry(callback_func_t, + &g_ta_callback_func_list.callback_list, head) { + if (memcmp(callback_func_t->uuid, tc_notify_data_timer->uuid, + UUID_SIZE)) + continue; + + if (tc_notify_data_timer->property.timer_class == + TIMER_RTC) { + tlogd("start to call callback func\n"); + callback_func_t->callback_func( + &(tc_notify_data_timer->property)); + tlogd("end to call callback func\n"); + } else if (tc_notify_data_timer->property.timer_class == + TIMER_GENERIC) { + tlogd("timer60 no callback func\n"); + } + } + mutex_unlock(&g_ta_callback_func_list.callback_list_lock); +} + +static int find_notify_sess( + const struct notify_context_timer *tc_notify_data_timer, + struct tc_ns_session **temp_ses, bool *enc_found) +{ + struct tc_ns_dev_file *temp_dev_file = NULL; + struct tc_ns_dev_list *dev_list = NULL; + struct tc_ns_service *temp_svc = NULL; + + dev_list = get_dev_list(); + if (!dev_list) { + tloge("dev list is invalid\n"); + return -ENOENT; + } + + mutex_lock(&dev_list->dev_lock); + list_for_each_entry(temp_dev_file, &dev_list->dev_file_list, head) { + tlogd("dev file id1 = %u, id2 = %u\n", + temp_dev_file->dev_file_id, + tc_notify_data_timer->dev_file_id); + if (temp_dev_file->dev_file_id == + tc_notify_data_timer->dev_file_id) { + mutex_lock(&temp_dev_file->service_lock); + temp_svc = + tc_find_service_in_dev(temp_dev_file, + tc_notify_data_timer->uuid, UUID_LEN); + mutex_unlock(&temp_dev_file->service_lock); + if (!temp_svc) + break; + get_service_struct(temp_svc); + mutex_lock(&temp_svc->session_lock); + *temp_ses = + tc_find_session_withowner( + &temp_svc->session_list, + tc_notify_data_timer->session_id, + temp_dev_file); + get_session_struct(*temp_ses); + mutex_unlock(&temp_svc->session_lock); + put_service_struct(temp_svc); + temp_svc = NULL; + if (*temp_ses) { + tlogd("send cmd ses id %u\n", + (*temp_ses)->session_id); + *enc_found = true; + break; + } + break; + } + } + mutex_unlock(&dev_list->dev_lock); + + return 0; +} + +static void tc_notify_timer_fn(struct notify_data_entry *notify_data_entry) +{ + struct tc_ns_session *temp_ses = NULL; + bool enc_found = false; + struct notify_context_timer *tc_notify_data_timer = NULL; + + tc_notify_data_timer = &(notify_data_entry->context.timer); + notify_data_entry->filled = 0; + tlogd("notify data timer type is 0x%x, timer ID is 0x%x\n", + tc_notify_data_timer->property.type, + tc_notify_data_timer->property.timer_id); + walk_callback_list(tc_notify_data_timer); + + if (find_notify_sess(tc_notify_data_timer, &temp_ses, &enc_found)) + return; + + if (tc_notify_data_timer->property.timer_class == TIMER_GENERIC) { + tlogd("timer60 wake up event\n"); + if (enc_found && temp_ses) { + temp_ses->wait_data.send_wait_flag = 1; + wake_up(&temp_ses->wait_data.send_cmd_wq); + put_session_struct(temp_ses); + temp_ses = NULL; + } + } else { + tlogd("RTC do not need to wakeup\n"); + } +} + +static noinline int get_notify_data_entry(struct notify_data_entry *copy) +{ + uint32_t i; + int filled; + int ret = -1; + + if (!copy || !g_notify_data) { + tloge("bad parameters or notify data is NULL"); + return ret; + } + + spin_lock(&g_notify_lock); + /* TIMER and RTC use fix entry, skip them. */ + for (i = NOTIFY_DATA_ENTRY_UNUSED; i < NOTIFY_DATA_ENTRY_COUNT; i++) { + struct notify_data_entry *e = &g_notify_data->entry[i]; + filled = e->filled; + smp_mb(); + if (!filled) + continue; + switch (e->entry_type) { + case NOTIFY_DATA_ENTRY_TIMER: + case NOTIFY_DATA_ENTRY_RTC: + break; + case NOTIFY_DATA_ENTRY_SHADOW: + case NOTIFY_DATA_ENTRY_SHADOW_EXIT: + case NOTIFY_DATA_ENTRY_FIQSHD: + g_notify_data->meta.context.meta.recv_s++; + break; + case NOTIFY_DATA_ENTRY_WAKEUP: + g_notify_data->meta.context.meta.recv_w++; + break; +#ifdef CONFIG_TA_AFFINITY + case NOTIFY_DATA_ENTRY_SET_AFFINITY: + g_notify_data->meta.context.meta.recv_af++; + break; +#endif + default: + tloge("invalid notify type=%u\n", e->entry_type); + goto exit; + } + if (memcpy_s(copy, sizeof(*copy), e, sizeof(*e)) != EOK) { + tloge("memcpy entry failed\n"); + break; + } + smp_mb(); + e->filled = 0; + ret = 0; + break; + } +exit: + spin_unlock(&g_notify_lock); + return ret; +} + +static void tc_notify_wakeup_fn(const struct notify_data_entry *entry) +{ + const struct notify_context_wakeup *tc_notify_wakeup = NULL; + + tc_notify_wakeup = &(entry->context.wakeup); + smc_wakeup_ca(tc_notify_wakeup->ca_thread_id); + tlogd("notify data entry wakeup ca: %d\n", + tc_notify_wakeup->ca_thread_id); +} + +static void tc_notify_shadow_fn(const struct notify_data_entry *entry) +{ + const struct notify_context_shadow *tc_notify_shadow = NULL; + + tc_notify_shadow = &(entry->context.shadow); + smc_queue_shadow_worker(tc_notify_shadow->target_tcb); +} + +static void tc_notify_fiqshd_fn(const struct notify_data_entry *entry) +{ + const struct notify_context_shadow *tc_notify_shadow = NULL; + + if (!entry) { + /* for NOTIFY_DATA_ENTRY_FIQSHD missed */ + fiq_shadow_work_func(0); + return; + } + tc_notify_shadow = &(entry->context.shadow); + fiq_shadow_work_func(tc_notify_shadow->target_tcb); +} + +static void tc_notify_shadowexit_fn(const struct notify_data_entry *entry) +{ + const struct notify_context_wakeup *tc_notify_wakeup = NULL; + + tc_notify_wakeup = &(entry->context.wakeup); + if (smc_shadow_exit(tc_notify_wakeup->ca_thread_id)) + tloge("shadow ca exit failed: %d\n", + (int)tc_notify_wakeup->ca_thread_id); +} + +#ifdef CONFIG_TA_AFFINITY +static void tc_notify_set_affinity(struct notify_data_entry *entry) +{ + struct notify_context_set_affinity *af_data = NULL; + struct pending_entry *pe = NULL; + + af_data = &(entry->context.affinity); + pe = find_pending_entry(af_data->ca_thread_id); + if (pe != NULL) { + struct cpumask mask; + uint32_t i; + + cpumask_clear(&mask); + for (i = 0; i < (uint32_t)NR_CPUS; i++) { + struct aff_bits_t *aff = &af_data->aff; + if (aff->aff_bits[i / AFF_BITS_SIZE] & aff_bits_mask(i)) + cpumask_set_cpu(i, &mask); + } + + /* + * we don't set ca's cpumask here but in ca's own thread + * context after ca is wakeup in smc_send_func, or + * scheduler will set task's allow cpumask failure in that case. + */ + cpumask_copy(&pe->ta_mask, &mask); + smc_wakeup_ca(af_data->ca_thread_id); + tlogd("set affinity for ca thread id %u\n", af_data->ca_thread_id); + put_pending_entry(pe); + } else { + tloge("invalid ca thread id %u for set affinity\n", + af_data->ca_thread_id); + /* + * if a TEE tcb without CA bind(CA is 0) cause a affinity set, + * the CA tid(current cpu context) may wrong + * (in tc_notify_fiqshd_fn, don't init_pending_entry, + * in this case, cannot find pending_entry), + * but we must set affinity for CA otherwise the TA can't run, + * so we wakeup all blocked CA. + */ + (void)smc_wakeup_broadcast(); + } +} +#endif + +#define MISSED_COUNT 4 +static void spi_broadcast_notifications(void) +{ + uint32_t missed; + + smp_mb(); + + if (!g_notify_data) { + tloge("notify data is NULL\n"); + return; + } + + missed = __xchg(0, &g_notify_data->meta.context.meta.missed, + MISSED_COUNT); + if (!missed) + return; + if (missed & (1U << NOTIFY_DATA_ENTRY_WAKEUP)) { + smc_wakeup_broadcast(); + missed &= ~(1U << NOTIFY_DATA_ENTRY_WAKEUP); + } + if (missed & (1U << NOTIFY_DATA_ENTRY_FIQSHD)) { + tc_notify_fiqshd_fn(NULL); + missed &= ~(1U << NOTIFY_DATA_ENTRY_FIQSHD); + } + if (missed) + tloge("missed spi notification mask %x\n", missed); +} + +static void tc_notify_fn(struct work_struct *dummy) +{ + struct notify_data_entry copy = {0}; + + while (get_notify_data_entry(©) == 0) { + switch (copy.entry_type) { + case NOTIFY_DATA_ENTRY_TIMER: + case NOTIFY_DATA_ENTRY_RTC: + tc_notify_timer_fn(©); + break; + case NOTIFY_DATA_ENTRY_WAKEUP: + tc_notify_wakeup_fn(©); + break; + case NOTIFY_DATA_ENTRY_SHADOW: + tc_notify_shadow_fn(©); + break; + case NOTIFY_DATA_ENTRY_FIQSHD: + tc_notify_fiqshd_fn(©); + break; + case NOTIFY_DATA_ENTRY_SHADOW_EXIT: + tc_notify_shadowexit_fn(©); + break; +#ifdef CONFIG_TA_AFFINITY + case NOTIFY_DATA_ENTRY_SET_AFFINITY: + tc_notify_set_affinity(©); + break; +#endif + default: + tloge("invalid entry type = %u\n", copy.entry_type); + } + if (memset_s(©, sizeof(copy), 0, sizeof(copy))) + tloge("memset copy failed\n"); + } + spi_broadcast_notifications(); +} + +static irqreturn_t tc_secure_notify(int irq, void *dev_id) +{ +#define N_WORK 8 + int i; + static struct work_struct tc_notify_works[N_WORK]; + static int init; + + if (!init) { + for (i = 0; i < N_WORK; i++) + INIT_WORK(&tc_notify_works[i], tc_notify_fn); + init = 1; + } + for (i = 0; i < N_WORK; i++) { + if (queue_work(g_tz_spi_wq, &tc_notify_works[i])) + break; + } +#undef N_WORK + + return IRQ_HANDLED; +} + +int tc_ns_register_service_call_back_func(const char *uuid, void *func, + const void *private_data) +{ + struct tc_ns_callback *callback_func = NULL; + struct tc_ns_callback *new_callback = NULL; + int ret = 0; + + if (!uuid || !func) + return -EINVAL; + + (void)private_data; + mutex_lock(&g_ta_callback_func_list.callback_list_lock); + if (g_ta_callback_func_list.callback_count > MAX_CALLBACK_COUNT) { + mutex_unlock(&g_ta_callback_func_list.callback_list_lock); + tloge("callback_count is out\n"); + return -ENOMEM; + } + list_for_each_entry(callback_func, + &g_ta_callback_func_list.callback_list, head) { + if (!memcmp(callback_func->uuid, uuid, UUID_SIZE)) { + callback_func->callback_func = (void (*)(void *))func; + tlogd("succeed to find uuid ta_callback_func_list\n"); + goto find_callback; + } + } + /* create a new callback struct if we couldn't find it in list */ + new_callback = kzalloc(sizeof(*new_callback), GFP_KERNEL); + if (ZERO_OR_NULL_PTR((unsigned long)(uintptr_t)new_callback)) { + tloge("kzalloc failed\n"); + ret = -ENOMEM; + goto find_callback; + } + + if (memcpy_s(new_callback->uuid, UUID_SIZE, uuid, UUID_SIZE)) { + kfree(new_callback); + new_callback = NULL; + ret = -ENOMEM; + goto find_callback; + } + g_ta_callback_func_list.callback_count++; + tlogd("callback count is %u\n", + g_ta_callback_func_list.callback_count); + INIT_LIST_HEAD(&new_callback->head); + new_callback->callback_func = (void (*)(void *))func; + mutex_init(&new_callback->callback_lock); + list_add_tail(&new_callback->head, + &g_ta_callback_func_list.callback_list); +find_callback: + mutex_unlock(&g_ta_callback_func_list.callback_list_lock); + return ret; +} + +#ifdef DEF_ENG + +static void timer_callback_func(void *param) +{ + struct teec_timer_property *timer_property = + (struct teec_timer_property *)param; + tlogd("timer property type = %x, timer property timer id = %x\n", + timer_property->type, timer_property->timer_id); + g_timer_type = (int)timer_property->type; +} + +static void tst_get_timer_type(int *type) +{ + *type = g_timer_type; +} + +static void callback_demo_main(const char *uuid) +{ + int ret; + + tlogd("step into callback_demo_main\n"); + ret = tc_ns_register_service_call_back_func(uuid, + timer_callback_func, NULL); + if (ret) + tloge("failed to tc_ns_register_service_call_back_func\n"); +} + +static int init_tst_test_context(struct tc_ns_client_context *client_context, + const void *argp, int *cmd_id) +{ + struct tc_uuid secure_timer_uuid = { + 0x19b39980, 0x2487, 0x7b84, + {0xf4, 0x1a, 0xbc, 0x89, 0x22, 0x62, 0xbb, 0x3d} + }; + + if (copy_from_user(client_context, argp, sizeof(*client_context))) { + tloge("copy from user failed\n"); + return -ENOMEM; + } + + if (!tc_user_param_valid(client_context, 0)) { + tloge("param 0 is invalid\n"); + return -EFAULT; + } + + /* a_addr contain the command id */ + if (copy_from_user(cmd_id, + (void *)(uintptr_t)client_context->params[0].value.a_addr, + sizeof(*cmd_id))) { + tloge("copy from user failed:cmd_id\n"); + return -ENOMEM; + } + + if (memcmp((char *)client_context->uuid, (char *)&secure_timer_uuid, + sizeof(struct tc_uuid))) { + tloge("request not from secure_timer\n"); + tloge("request uuid: %x %x %x %x\n", + *(client_context->uuid + 0), + *(client_context->uuid + 1), + *(client_context->uuid + 2), + *(client_context->uuid + 3)); + /* just wanna print the first four characters of uuid */ + return -EACCES; + } + + return 0; +} + +int tc_ns_tst_cmd(void *argp) +{ + struct tc_ns_client_context client_context; + int ret; + int cmd_id; + int timer_type; + + if (!argp) { + tloge("argp is NULL input buffer\n"); + return -EINVAL; + } + + ret = init_tst_test_context(&client_context, argp, &cmd_id); + if (ret) + return ret; + + switch (cmd_id) { + case TST_CMD_01: + callback_demo_main((char *)client_context.uuid); + break; + case TST_CMD_02: + tst_get_timer_type(&timer_type); + if (!tc_user_param_valid(&client_context, (unsigned int)1)) { + tloge("param 1 is invalid\n"); + ret = -EINVAL; + return ret; + } + if (copy_to_user( + (void *)(uintptr_t) + client_context.params[1].value.a_addr, + &timer_type, sizeof(timer_type))) { + tloge("copy to user failed:timer_type\n"); + ret = -ENOMEM; + return ret; + } + break; + default: + ret = -EINVAL; + return ret; + } + if (copy_to_user(argp, &client_context, sizeof(client_context))) { + tloge("copy to user failed:client context\n"); + ret = -ENOMEM; + return ret; + } + return ret; +} + +#else + +int tc_ns_tst_cmd(void *argp) +{ + (void)argp; + tloge("usr img do not support this cmd\n"); + return 0; +} + +#endif + +static int send_notify_cmd(unsigned int cmd_id) +{ + struct tc_ns_smc_cmd smc_cmd = { {0}, 0 }; + int ret = 0; + struct mb_cmd_pack *mb_pack = NULL; + + mb_pack = mailbox_alloc_cmd_pack(); + if (!mb_pack) + 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(g_notify_data); + mb_pack->operation.params[0].value.b = (uint64_t)virt_to_phys(g_notify_data) >> ADDR_TRANS_NUM; + mb_pack->operation.params[1].value.a = SZ_4K; + 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("register notify mem failed\n"); + } + mailbox_free(mb_pack); + + return ret; +} + +static int config_spi_context(struct device *class_dev, struct device_node *np) +{ + unsigned int irq = DEFAULT_SPI_NUM; + int ret; + +#ifndef CONFIG_ACPI + if (!np) { + tloge("device node not found\n"); + return -EINVAL; + } +#endif + + /* Map IRQ 0 from the OF interrupts list */ +#ifdef CONFIG_ACPI + irq = (unsigned int)get_acpi_tz_irq(); +#else + irq = irq_of_parse_and_map(np, 0); +#endif + ret = devm_request_irq(class_dev, irq, tc_secure_notify, + IRQF_NO_SUSPEND, TC_NS_CLIENT_DEV, NULL); + if (ret < 0) { + tloge("device irq %u request failed %d", irq, ret); + return ret; + } + + g_ta_callback_func_list.callback_count = 0; + INIT_LIST_HEAD(&g_ta_callback_func_list.callback_list); + mutex_init(&g_ta_callback_func_list.callback_list_lock); + + return 0; +} + +int tz_spi_init(struct device *class_dev, struct device_node *np) +{ + int ret; + + if (!class_dev) /* here np can be NULL */ + return -EINVAL; + + spin_lock_init(&g_notify_lock); + g_tz_spi_wq = alloc_workqueue("g_tz_spi_wq", + WQ_UNBOUND | WQ_HIGHPRI, TZ_WQ_MAX_ACTIVE); + if (!g_tz_spi_wq) { + tloge("it failed to create workqueue g_tz_spi_wq\n"); + return -ENOMEM; + } + tz_workqueue_bind_mask(g_tz_spi_wq, WQ_HIGHPRI); + + ret = config_spi_context(class_dev, np); + if (ret) + goto clean; + + if (!g_notify_data) { +#ifdef CONFIG_BIG_SESSION + /* we should map at least 3 pages for 100 sessions, 2^2 > 3 */ + g_notify_data = (struct notify_data_struct *) + (uintptr_t)__get_free_pages( + GFP_KERNEL | __GFP_ZERO, CONFIG_NOTIFY_PAGE_ORDER); +#else + g_notify_data = (struct notify_data_struct *) + (uintptr_t)__get_free_page(GFP_KERNEL | __GFP_ZERO); +#endif + if (!g_notify_data) { + tloge("get free page failed for notification data\n"); + ret = -ENOMEM; + goto clean; + } + + ret = send_notify_cmd(GLOBAL_CMD_ID_REGISTER_NOTIFY_MEMORY); + if (ret) { + tloge("shared memory failed ret is 0x%x\n", ret); + ret = -EFAULT; + free_page((unsigned long)(uintptr_t)g_notify_data); + g_notify_data = NULL; + goto clean; + } + + g_notify_data_entry_shadow = + &g_notify_data->entry[NOTIFY_DATA_ENTRY_SHADOW - 1]; + tlogd("target is: %llx\n", + g_notify_data_entry_shadow->context.shadow.target_tcb); + } + + return 0; +clean: + tz_spi_exit(); + return ret; +} + +void tz_spi_exit(void) +{ + if (g_notify_data) { + if (send_notify_cmd(GLOBAL_CMD_ID_UNREGISTER_NOTIFY_MEMORY)) + tloge("unregister notify data mem failed\n"); +#ifdef CONFIG_BIG_SESSION + free_pages((unsigned long)(uintptr_t)g_notify_data, + CONFIG_NOTIFY_PAGE_ORDER); +#else + free_page((unsigned long)(uintptr_t)g_notify_data); +#endif + g_notify_data = NULL; + } + + if (g_tz_spi_wq) { + flush_workqueue(g_tz_spi_wq); + destroy_workqueue(g_tz_spi_wq); + g_tz_spi_wq = NULL; + } +} diff --git a/linux/core/tz_spi_notify.h b/linux/core/tz_spi_notify.h new file mode 100644 index 0000000..01f7a8b --- /dev/null +++ b/linux/core/tz_spi_notify.h @@ -0,0 +1,27 @@ +/* + * tz_spi_notify.h + * + * 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. + */ +#ifndef TZ_SPI_NOTIFY_H +#define TZ_SPI_NOTIFY_H +#include +#include +#include "teek_ns_client.h" + +int tz_spi_init(struct device *class_dev, struct device_node *np); +void tz_spi_exit(void); +int tc_ns_tst_cmd(void *argp); + +#endif diff --git a/linux/core/tzdebug.c b/linux/core/tzdebug.c new file mode 100644 index 0000000..d870b5a --- /dev/null +++ b/linux/core/tzdebug.c @@ -0,0 +1,521 @@ +/* + * tzdebug.c + * + * for tzdriver debug + * + * 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 "tzdebug.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "tc_ns_log.h" +#include "tc_ns_client.h" +#include "tc_client_driver.h" +#include "teek_ns_client.h" +#include "smc_smp.h" +#include "teek_client_constants.h" +#include "mailbox_mempool.h" +#include "tlogger.h" +#include "cmdmonitor.h" +#include "session_manager.h" + +#define DEBUG_OPT_LEN 128 + +#ifdef CONFIG_TA_MEM_INUSE_ONLY +#define TA_MEMSTAT_ALL 0 +#else +#define TA_MEMSTAT_ALL 1 +#endif + +static struct dentry *g_tz_dbg_dentry; + +typedef void (*tzdebug_opt_func)(const char *param); + +struct opt_ops { + char *name; + tzdebug_opt_func func; +}; + +static DEFINE_MUTEX(g_meminfo_lock); +static struct tee_mem g_tee_meminfo; +static void tzmemdump(const char *param); +static int send_dump_mem(int flag, int history, const struct tee_mem *statmem) +{ + struct tc_ns_smc_cmd smc_cmd = { {0}, 0 }; + struct mb_cmd_pack *mb_pack = NULL; + int ret = 0; + + if (!statmem) { + tloge("statmem is NULL\n"); + return -EINVAL; + } + mb_pack = mailbox_alloc_cmd_pack(); + if (!mb_pack) + return -ENOMEM; + + smc_cmd.cmd_id = GLOBAL_CMD_ID_DUMP_MEMINFO; + smc_cmd.cmd_type = CMD_TYPE_GLOBAL; + mb_pack->operation.paramtypes = teec_param_types( + TEE_PARAM_TYPE_MEMREF_INOUT, TEE_PARAM_TYPE_VALUE_INPUT, + TEE_PARAM_TYPE_NONE, TEE_PARAM_TYPE_NONE); + mb_pack->operation.params[0].memref.buffer = virt_to_phys(statmem); + mb_pack->operation.params[0].memref.size = sizeof(*statmem); + mb_pack->operation.buffer_h_addr[0] = + (uint64_t)virt_to_phys(statmem) >> ADDR_TRANS_NUM; + mb_pack->operation.params[1].value.a = flag; + mb_pack->operation.params[1].value.b = history; + smc_cmd.operation_phys = + (unsigned int)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("send dump mem failed\n"); + } + tz_log_write(); + mailbox_free(mb_pack); + return ret; +} + +void tee_dump_mem(void) +{ + tzmemdump(NULL); + 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"); +} + +/* get meminfo (tee_mem + N * ta_mem < 4Kbyte) from tee */ +static int get_tee_meminfo_cmd(void) +{ + int ret; + struct tee_mem *mem = NULL; + + mem = mailbox_alloc(sizeof(*mem), MB_FLAG_ZERO); + if (!mem) + return -ENOMEM; + + ret = send_dump_mem(0, TA_MEMSTAT_ALL, mem); + if (ret) { + tloge("send dump failed\n"); + mailbox_free(mem); + return ret; + } + + mutex_lock(&g_meminfo_lock); + ret = memcpy_s(&g_tee_meminfo, sizeof(g_tee_meminfo), mem, sizeof(*mem)); + if (ret) + tloge("memcpy failed\n"); + mutex_unlock(&g_meminfo_lock); + mailbox_free(mem); + + return ret; +} + +static atomic_t g_cmd_send = ATOMIC_INIT(1); + +void set_cmd_send_state(void) +{ + atomic_set(&g_cmd_send, 1); +} + +int get_tee_meminfo(struct tee_mem *meminfo) +{ + errno_t s_ret; + + if (!meminfo) + return -EINVAL; + + if (atomic_read(&g_cmd_send)) { + if (get_tee_meminfo_cmd()) + return -EFAULT; + } else { + atomic_set(&g_cmd_send, 0); + } + + mutex_lock(&g_meminfo_lock); + s_ret = memcpy_s(meminfo, sizeof(*meminfo), + &g_tee_meminfo, sizeof(g_tee_meminfo)); + mutex_unlock(&g_meminfo_lock); + if (s_ret) + return -1; + + return 0; +} +EXPORT_SYMBOL(get_tee_meminfo); + +static void archivelog(const char *param) +{ + (void)param; + tzdebug_archivelog(); +} + +static void tzdump(const char *param) +{ + (void)param; + show_cmd_bitmap(); + wakeup_tc_siq(SIQ_DUMP_SHELL); +} + +static void tzmemdump(const char *param) +{ + struct tee_mem *mem = NULL; + + (void)param; + mem = mailbox_alloc(sizeof(*mem), MB_FLAG_ZERO); + if (!mem) { + tloge("mailbox alloc failed\n"); + return; + } + + if (send_dump_mem(1, 1, mem)) + tloge("send dump mem failed\n"); + mailbox_free(mem); +} + +static void tz_srv_sess_dump(const char *param) +{ + struct tc_ns_smc_cmd smc_cmd = { {0}, 0 }; + + (void)param; + smc_cmd.cmd_id = GLOBAL_CMD_ID_DUMP_SRV_SESS; + smc_cmd.cmd_type = CMD_TYPE_GLOBAL; + if (tc_ns_smc(&smc_cmd)) + tloge("send dump service session failed\n"); +} + +static void tzmemstat(const char *param) +{ + (void)param; + tzdebug_memstat(); +} + +static void tzlogwrite(const char *param) +{ + (void)param; + (void)tz_log_write(); +} + +#define OFFSET_VALUE_BIT 8U +static void get_value(const char *param, uint32_t *value, uint32_t *index) +{ + uint32_t i; + uint32_t val = 0; + + for (i = 0; i < OFFSET_VALUE_BIT; i++) { + if (param[i] > '9' || param[i] < '0') { + *value = val; + *index = i; + return; + } + val = (val * 10) + param[i] - '0'; + } + + *value = val; + *index = i; + return; +} + +#define MAX_CMD_NUM 3 +#define MAX_CMD_LINE 20 +#define MAX_PARAM_LINE 60 +#define MAX_MEM_SIZE 0x4000U +#define MAX_VALUE_LEN 8U +#define MAX_ADDRSTR_LEN 18U +struct addr_ops { + uint32_t pos[MAX_CMD_NUM]; + char address[MAX_CMD_LINE]; + char value[MAX_CMD_LINE]; +}; + +static struct addr_ops g_sec_addr; + +static int parse_param(const char *param, uint32_t len) +{ + uint32_t i; + uint32_t j; + + /* + * param format as follows: + * (r)/(w):addr(Decimal):value + * 'w' for writing a value to a given addr + * 'r' for reading a value form a giver addr + */ + j = 0; + for (i = 0; i < len; i++) { + if (param[i] == ':') { + if (j < MAX_CMD_NUM - 1) + g_sec_addr.pos[j] = i; + j++; + } + } + if (j >= MAX_CMD_NUM ) { /* Max number of ':' is 2 */ + tloge("cmd invalid\n"); + return -EINVAL; + } + + if ((g_sec_addr.pos[1] - g_sec_addr.pos[0] - 1 >= MAX_ADDRSTR_LEN ) || (len - g_sec_addr.pos[1] >= MAX_VALUE_LEN)) { + tloge("invalid param\n"); + return -EINVAL; + } + if (param[0] != 'r' && param[0] != 'w' ) { + tloge("cmd not support\n"); + return -EINVAL; + } + + i = 0; + for (j = g_sec_addr.pos[0] + 1; j <= g_sec_addr.pos[1] - 1; j++) /* skip ':'*/ + g_sec_addr.address[i++] = param[j]; + g_sec_addr.address[i] = '\0'; + + i = 0; + for (j = g_sec_addr.pos[1] + 1; j <= len; j++) + g_sec_addr.value[i++] = param[j]; + g_sec_addr.value[i] = '\0'; + return 0; +} +void tz_secmem_test(const char *param) +{ + void __iomem *remap_addr = NULL; + void *addr = NULL; + uint32_t val; + unsigned long long tmp; + uint32_t len; + + /* if strlen is negative, len must be larger than MAX_PARAM_LINE, so it's invalied */ + len = (uint32_t)strlen(param); + if (len >= MAX_PARAM_LINE) { + tloge("param invalid\n"); + return; + } + + if (parse_param(param, len) != 0) { + tloge("parse params fail\n"); + return; + } + tloge("the second string : %s\n", g_sec_addr.address); + + if (kstrtoull(g_sec_addr.address + 2, 16, &tmp) < 0) { /* add 2 for skip '0x' */ + tloge("str address to unsigned long fail\n"); + return; + } + addr = (void *)(uintptr_t)tmp; + + if (kstrtoull(g_sec_addr.value, 10, &tmp) < 0) { + tloge("str val to unsigned long fail\n"); + return; + } + val = (uint32_t)tmp; + tloge("val = %u\n", val); + + remap_addr = ioremap((phys_addr_t)(uintptr_t)addr, MAX_MEM_SIZE); + if (!remap_addr) { + tloge("remap_addr failed\n"); + return; + } + tloge("remap_addr succ\n"); + if (param[0] == 'w') { + tloge("read before write : %u \n", *((uint32_t *)remap_addr)); + tloge("write val : %u \n", val); + *((uint32_t *)remap_addr) = val; + } else { + tloge("read addr value : %u \n", *((uint32_t *)remap_addr)); + } + iounmap(remap_addr); +} + +static struct opt_ops g_opt_arr[] = { + {"archivelog", archivelog}, + {"dump", tzdump}, + {"memdump", tzmemdump}, + {"logwrite", tzlogwrite}, + {"dump_service", dump_services_status}, + {"memstat", tzmemstat}, + {"secmem_test", tz_secmem_test}, + {"srv_sess_dump", tz_srv_sess_dump}, +}; + +static ssize_t tz_dbg_opt_read(struct file *filp, char __user *ubuf, + size_t cnt, loff_t *ppos) +{ + char *obuf = NULL; + char *p = NULL; + ssize_t ret; + uint32_t oboff = 0; + uint32_t i; + + (void)(filp); + + obuf = kzalloc(DEBUG_OPT_LEN, GFP_KERNEL); + if (!obuf) + return -ENOMEM; + p = obuf; + + for (i = 0; i < ARRAY_SIZE(g_opt_arr); i++) { + int len = snprintf_s(p, DEBUG_OPT_LEN - oboff, DEBUG_OPT_LEN - oboff - 1, + "%s ", g_opt_arr[i].name); + if (len < 0) { + kfree(obuf); + tloge("snprintf opt name of idx %d failed\n", i); + return -EINVAL; + } + p += len; + oboff += len; + } + obuf[oboff - 1] = '\n'; + + ret = simple_read_from_buffer(ubuf, cnt, ppos, obuf, oboff); + kfree(obuf); + + return ret; +} + +static ssize_t tz_dbg_opt_write(struct file *filp, + const char __user *ubuf, size_t cnt, loff_t *ppos) +{ + char buf[128] = {0}; + char *value = NULL; + char *p = NULL; + uint32_t i = 0; + + if (!ubuf || !filp || !ppos) + return -EINVAL; + + if (cnt >= sizeof(buf)) + return -EINVAL; + + if (!cnt) + return -EINVAL; + + if (copy_from_user(buf, ubuf, cnt)) + return -EFAULT; + + buf[cnt] = 0; + if (cnt > 0 && buf[cnt - 1] == '\n') + buf[cnt - 1] = 0; + value = buf; + p = strsep(&value, ":"); /* when buf has no :, value may be NULL */ + if (!p) + return -EINVAL; + + for (i = 0; i < ARRAY_SIZE(g_opt_arr); i++) { + if (!strncmp(p, g_opt_arr[i].name, + strlen(g_opt_arr[i].name)) && + strlen(p) == strlen(g_opt_arr[i].name)) { + g_opt_arr[i].func(value); + return cnt; + } + } + return -EFAULT; +} + +#ifdef CONFIG_MEMSTAT_DEBUGFS +static int memstat_debug_show(struct seq_file *m, void *v) +{ + struct tee_mem *mem_stat = NULL; + int ret; + uint32_t i; + + mem_stat = kzalloc(sizeof(*mem_stat), GFP_KERNEL); + if (!mem_stat) + return -ENOMEM; + + ret = get_tee_meminfo(mem_stat); + if (ret != 0) { + tloge("get tee meminfo failed\n"); + kfree(mem_stat); + mem_stat = NULL; + return -EINVAL; + } + + seq_printf(m, "TotalMem:%u Pmem:%u Free_Mem:%u Free_Mem_Min:%u TA_Num:%u\n", + mem_stat->total_mem, mem_stat->pmem, mem_stat->free_mem, mem_stat->free_mem_min, mem_stat->ta_num); + + for (i = 0; i < mem_stat->ta_num; i++) + seq_printf(m, "ta_name:%s ta_pmem:%u pmem_max:%u pmem_limit:%u\n", + mem_stat->ta_mem_info[i].ta_name, mem_stat->ta_mem_info[i].pmem, + mem_stat->ta_mem_info[i].pmem_max, mem_stat->ta_mem_info[i].pmem_limit); + + kfree(mem_stat); + mem_stat = NULL; + return 0; +} + +static int tz_memstat_open(struct inode *inode, struct file *file) +{ + return single_open(file, memstat_debug_show, NULL); +} + +static const struct file_operations g_tz_dbg_memstat_fops = { + .owner = THIS_MODULE, + .open = tz_memstat_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; +#endif + +static const struct file_operations g_tz_dbg_opt_fops = { + .owner = THIS_MODULE, + .read = tz_dbg_opt_read, + .write = tz_dbg_opt_write, +}; + +int tzdebug_init(void) +{ +#if defined(DEF_ENG) || defined(CONFIG_TZDRIVER_MODULE) + g_tz_dbg_dentry = debugfs_create_dir("tzdebug", NULL); + if (!g_tz_dbg_dentry) + return -1; + + debugfs_create_file("opt", 0660, g_tz_dbg_dentry, NULL, + &g_tz_dbg_opt_fops); + +#ifdef CONFIG_MEMSTAT_DEBUGFS + debugfs_create_file("memstat", 0444, g_tz_dbg_dentry, NULL, + &g_tz_dbg_memstat_fops); +#endif + +#else + (void)g_tz_dbg_dentry; + (void)g_tz_dbg_opt_fops; +#endif + return 0; +} + +void tzdebug_exit(void) +{ +#if defined(DEF_ENG) || defined(CONFIG_TZDRIVER_MODULE) + if (!g_tz_dbg_dentry) + return; + + debugfs_remove_recursive(g_tz_dbg_dentry); + g_tz_dbg_dentry = NULL; +#endif +} diff --git a/linux/core/tzdebug.h b/linux/core/tzdebug.h new file mode 100644 index 0000000..bb3be10 --- /dev/null +++ b/linux/core/tzdebug.h @@ -0,0 +1,43 @@ +/* + * tzdebug.h + * + * for tzdriver debug + * + * 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 TZDEBUG_H +#define TZDEBUG_H + +#include +struct ta_mem { + char ta_name[64]; + uint32_t pmem; + uint32_t pmem_max; + uint32_t pmem_limit; +}; + +#define MEMINFO_TA_MAX 100 +struct tee_mem { + uint32_t total_mem; + uint32_t pmem; + uint32_t free_mem; + uint32_t free_mem_min; + uint32_t ta_num; + struct ta_mem ta_mem_info[MEMINFO_TA_MAX]; +}; + +int get_tee_meminfo(struct tee_mem *meminfo); +void tee_dump_mem(void); +int tzdebug_init(void); +void tzdebug_exit(void); + +#endif diff --git a/linux/include/ko_adapt.h b/linux/include/ko_adapt.h new file mode 100644 index 0000000..ea5acf1 --- /dev/null +++ b/linux/include/ko_adapt.h @@ -0,0 +1,109 @@ +/* + * ko_adapt.h + * + * function for find symbols not exported + * + * 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 KO_ADAPT_H +#define KO_ADAPT_H + +#include +#include +#include +#if (KERNEL_VERSION(4, 14, 0) <= LINUX_VERSION_CODE) +#include +#endif +#include +#include +#include +#include +#include + +#ifdef CONFIG_TZDRIVER_MODULE + +const struct cred *koadpt_get_task_cred(struct task_struct *task); +void koadpt_kthread_bind_mask(struct task_struct *task, + const struct cpumask *mask); +long koadpt_sys_chown(const char __user *filename, uid_t user, gid_t group); +ssize_t koadpt_vfs_write(struct file *file, const char __user *buf, + size_t count, loff_t *pos); +ssize_t koadpt_vfs_read(struct file *file, char __user *buf, + size_t count, loff_t *pos); +struct page *koadpt_alloc_pages(gfp_t gfp_mask, unsigned int order); +struct workqueue_attrs *koadpt_alloc_workqueue_attrs(gfp_t gfp_mask); +void koadpt_free_workqueue_attrs(struct workqueue_attrs *attrs); + +#else + +static inline const struct cred *koadpt_get_task_cred(struct task_struct *task) +{ + return get_task_cred(task); +} + +static inline void koadpt_kthread_bind_mask(struct task_struct *task, + const struct cpumask *mask) +{ + kthread_bind_mask(task, mask); +} + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 19, 0)) +static inline long koadpt_sys_chown(const char __user *filename, + uid_t user, gid_t group) +{ + return sys_chown(filename, user, group); +} +#else +static inline long koadpt_sys_chown(const char __user *filename, + uid_t user, gid_t group) +{ + return ksys_chown(filename, user, group); +} +#endif + +static inline ssize_t koadpt_vfs_read(struct file *file, char __user *buf, + size_t count, loff_t *pos) +{ + return vfs_read(file, buf, count, pos); +} + +static inline ssize_t koadpt_vfs_write(struct file *file, const char __user *buf, + size_t count, loff_t *pos) +{ + return vfs_write(file, buf, count, pos); +} + +static inline struct page *koadpt_alloc_pages(gfp_t gfp_mask, unsigned int order) +{ + return alloc_pages(gfp_mask, order); +} + +static inline struct workqueue_attrs *koadpt_alloc_workqueue_attrs( + gfp_t gfp_mask) +{ +#if (LINUX_VERSION_CODE <= KERNEL_VERSION(4, 19, 0)) + return alloc_workqueue_attrs(gfp_mask); +#else + (void)gfp_mask; + return alloc_workqueue_attrs(); +#endif +} + +static inline void koadpt_free_workqueue_attrs(struct workqueue_attrs *attrs) +{ + return free_workqueue_attrs(attrs); +} + +#endif + +#endif diff --git a/linux/include/tc_ns_client.h b/linux/include/tc_ns_client.h new file mode 100644 index 0000000..e3a3c0e --- /dev/null +++ b/linux/include/tc_ns_client.h @@ -0,0 +1,180 @@ +/* + * tc_ns_client.h + * + * data structure declaration for nonsecure world + * + * 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_NS_CLIENT_H +#define TC_NS_CLIENT_H + +#include +#include + +#define UUID_LEN 16 +#define PARAM_NUM 4 +#define ADDR_TRANS_NUM 32 + +#define teec_param_types(param0_type, param1_type, param2_type, param3_type) \ + ((param3_type) << 12 | (param2_type) << 8 | \ + (param1_type) << 4 | (param0_type)) + +#define teec_param_type_get(param_types, index) \ + (((param_types) >> ((index) << 2)) & 0x0F) + +#ifndef ZERO_SIZE_PTR +#define ZERO_SIZE_PTR ((void *)16) +#define ZERO_OR_NULL_PTR(x) ((unsigned long)(x) <= (unsigned long)ZERO_SIZE_PTR) +#endif + +#if (KERNEL_VERSION(5, 10, 0) <= LINUX_VERSION_CODE) +#define mm_sem_lock(mm) mm->mmap_lock +#else +#define mm_sem_lock(mm) mm->mmap_sem +#endif + +struct tc_ns_client_login { + __u32 method; + __u32 mdata; +}; + +union tc_ns_client_param { + struct { + __u64 buffer; + __u64 offset; + __u64 size_addr; + } memref; + struct { + __u64 a_addr; + __u64 b_addr; + } value; + struct { + __u64 buffer; + __u64 size_addr; + } sharedmem; +}; + +struct tc_ns_client_return { + int code; + __u32 origin; +}; + +struct tc_ns_client_context { + unsigned char uuid[UUID_LEN]; + __u32 session_id; + __u32 cmd_id; + struct tc_ns_client_return returns; + struct tc_ns_client_login login; + union tc_ns_client_param params[PARAM_NUM]; + __u32 param_types; + __u8 started; + __u32 calling_pid; + unsigned int file_size; + union { + char *file_buffer; + unsigned long long file_addr; + }; +}; + +struct tc_ns_client_time { + uint32_t seconds; + uint32_t millis; +}; + +enum secfile_type_t { + LOAD_TA = 0, + LOAD_SERVICE, + LOAD_LIB, + LOAD_DYNAMIC_DRV, +}; + +struct load_secfile_ioctl_struct { + enum secfile_type_t secfile_type; + unsigned char uuid[UUID_LEN]; + uint32_t file_size; + union { + char *file_buffer; + unsigned long long file_addr; + }; +}; + +struct agent_ioctl_args { + uint32_t id; + uint32_t buffer_size; + union { + void *buffer; + unsigned long long addr; + }; +}; + +struct tc_ns_client_crl { + union { + uint8_t *buffer; + unsigned long long addr; + }; + uint32_t size; +}; + +#define TST_CMD_01 1 +#define TST_CMD_02 2 +#define TST_CMD_03 3 +#define TST_CMD_04 4 +#define TST_CMD_05 5 + +#define MAX_SHA_256_SZ 32 + +#define TC_NS_CLIENT_IOCTL_SES_OPEN_REQ \ + _IOW(TC_NS_CLIENT_IOC_MAGIC, 1, struct tc_ns_client_context) +#define TC_NS_CLIENT_IOCTL_SES_CLOSE_REQ \ + _IOWR(TC_NS_CLIENT_IOC_MAGIC, 2, struct tc_ns_client_context) +#define TC_NS_CLIENT_IOCTL_SEND_CMD_REQ \ + _IOWR(TC_NS_CLIENT_IOC_MAGIC, 3, struct tc_ns_client_context) +#define TC_NS_CLIENT_IOCTL_SHRD_MEM_RELEASE \ + _IOWR(TC_NS_CLIENT_IOC_MAGIC, 4, unsigned int) +#define TC_NS_CLIENT_IOCTL_WAIT_EVENT \ + _IOWR(TC_NS_CLIENT_IOC_MAGIC, 5, unsigned int) +#define TC_NS_CLIENT_IOCTL_SEND_EVENT_RESPONSE \ + _IOWR(TC_NS_CLIENT_IOC_MAGIC, 6, unsigned int) +#define TC_NS_CLIENT_IOCTL_REGISTER_AGENT \ + _IOWR(TC_NS_CLIENT_IOC_MAGIC, 7, struct agent_ioctl_args) +#define TC_NS_CLIENT_IOCTL_UNREGISTER_AGENT \ + _IOWR(TC_NS_CLIENT_IOC_MAGIC, 8, unsigned int) +#define TC_NS_CLIENT_IOCTL_LOAD_APP_REQ \ + _IOWR(TC_NS_CLIENT_IOC_MAGIC, 9, struct load_secfile_ioctl_struct) +#define TC_NS_CLIENT_IOCTL_NEED_LOAD_APP \ + _IOWR(TC_NS_CLIENT_IOC_MAGIC, 10, struct tc_ns_client_context) +#define TC_NS_CLIENT_IOCTL_ALLOC_EXCEPTING_MEM \ + _IOWR(TC_NS_CLIENT_IOC_MAGIC, 12, unsigned int) +#define TC_NS_CLIENT_IOCTL_CANCEL_CMD_REQ \ + _IOWR(TC_NS_CLIENT_IOC_MAGIC, 13, struct tc_ns_client_context) +#define TC_NS_CLIENT_IOCTL_LOGIN \ + _IOWR(TC_NS_CLIENT_IOC_MAGIC, 14, int) +#define TC_NS_CLIENT_IOCTL_TST_CMD_REQ \ + _IOWR(TC_NS_CLIENT_IOC_MAGIC, 15, int) +#define TC_NS_CLIENT_IOCTL_TUI_EVENT \ + _IOWR(TC_NS_CLIENT_IOC_MAGIC, 16, int) +#define TC_NS_CLIENT_IOCTL_SYC_SYS_TIME \ + _IOWR(TC_NS_CLIENT_IOC_MAGIC, 17, struct tc_ns_client_time) +#define TC_NS_CLIENT_IOCTL_SET_NATIVECA_IDENTITY \ + _IOWR(TC_NS_CLIENT_IOC_MAGIC, 18, int) +#define TC_NS_CLIENT_IOCTL_LOAD_TTF_FILE_AND_NOTCH_HEIGHT \ + _IOWR(TC_NS_CLIENT_IOC_MAGIC, 19, unsigned int) +#define TC_NS_CLIENT_IOCTL_LATEINIT \ + _IOWR(TC_NS_CLIENT_IOC_MAGIC, 20, unsigned int) +#define TC_NS_CLIENT_IOCTL_GET_TEE_VERSION \ + _IOWR(TC_NS_CLIENT_IOC_MAGIC, 21, unsigned int) +#define TC_NS_CLIENT_IOCTL_UNMAP_SHARED_MEM \ + _IOWR(TC_NS_CLIENT_IOC_MAGIC, 22, unsigned int) +#define TC_NS_CLIENT_IOCTL_UPDATE_TA_CRL\ + _IOWR(TC_NS_CLIENT_IOC_MAGIC, 23, struct tc_ns_client_crl) + +#endif diff --git a/linux/include/tc_ns_log.h b/linux/include/tc_ns_log.h new file mode 100644 index 0000000..de325ce --- /dev/null +++ b/linux/include/tc_ns_log.h @@ -0,0 +1,67 @@ +/* + * tc_ns_log.h + * + * log func 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 TC_NS_LOG_H +#define TC_NS_LOG_H + +#include +#if (KERNEL_VERSION(4, 14, 0) <= LINUX_VERSION_CODE) +#include +#endif +#include +enum { + TZ_DEBUG_VERBOSE = 0, + TZ_DEBUG_DEBUG, + TZ_DEBUG_INFO, + TZ_DEBUG_WARN, + TZ_DEBUG_ERROR, +}; +#define MOD_TEE "tzdriver" + +#define TEE_LOG_MASK TZ_DEBUG_INFO + +#define tlogv(fmt, args...) \ +do { \ + if (TZ_DEBUG_VERBOSE >= TEE_LOG_MASK) \ + pr_info("([%s] %i, %s)%s: " fmt, MOD_TEE, current->pid, current->comm, __func__, ## args); \ +} while (0) + + +#define tlogd(fmt, args...) \ +do { \ + if (TZ_DEBUG_DEBUG >= TEE_LOG_MASK) \ + pr_info("([%s] %i, %s)%s: " fmt, MOD_TEE, current->pid, current->comm, __func__, ## args); \ +} while (0) + + +#define tlogi(fmt, args...) \ +do { \ + if (TZ_DEBUG_INFO >= TEE_LOG_MASK) \ + pr_info("([%s] %i, %s)%s: " fmt, MOD_TEE, current->pid, current->comm, __func__, ## args); \ +} while (0) + + +#define tlogw(fmt, args...) \ +do { \ + if (TZ_DEBUG_WARN >= TEE_LOG_MASK) \ + pr_warn("([%s] %i, %s)%s: " fmt, MOD_TEE, current->pid, current->comm, __func__, ## args); \ +} while (0) + + +#define tloge(fmt, args...) \ + pr_err("([%s] %i, %s)%s: " fmt, MOD_TEE, current->pid, current->comm, __func__, ## args) + +#endif diff --git a/linux/include/teek_client_api.h b/linux/include/teek_client_api.h new file mode 100644 index 0000000..f24b8e7 --- /dev/null +++ b/linux/include/teek_client_api.h @@ -0,0 +1,122 @@ +/* + * teek_client_api.h + * + * function declaration 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. + */ + +#ifndef TEEK_CLIENT_API_H +#define TEEK_CLIENT_API_H +#include "teek_ns_client.h" +#include "teek_client_type.h" + +#define TEEC_PARAM_TYPES(param0_type, param1_type, param2_type, param3_type) \ + ((param3_type) << 12 | (param2_type) << 8 | \ + (param1_type) << 4 | (param0_type)) + +#define TEEC_PARAM_TYPE_GET(param_types, index) \ + (((param_types) >> ((index) << 2)) & 0x0F) + +#define TEEC_VALUE_UNDEF 0xFFFFFFFF + +#ifdef CONFIG_KERNEL_CLIENT + +/* + * for history reason, we supply two set interface + * first set is uncapitalized and satisfies kernel code rule + * second set is capitalized for compatibility + */ +int teek_is_agent_alive(unsigned int agent_id); + +uint32_t teek_initialize_context(const char *name, + struct teec_context *context); + +void teek_finalize_context(struct teec_context *context); + +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); + +void teek_close_session(struct teec_session *session); + +uint32_t teek_send_secfile(struct teec_session *session, + const char *file_buffer, unsigned int file_size); + +uint32_t teek_invoke_command(struct teec_session *session, + uint32_t cmd_id, struct teec_operation *operation, + uint32_t *return_origin); + +uint32_t teek_register_shared_memory(struct teec_context *context, + struct teec_sharedmemory *sharedmem); + +uint32_t teek_allocate_shared_memory(struct teec_context *context, + struct teec_sharedmemory *sharedmem); + +void teek_release_shared_memory(struct teec_sharedmemory *sharedmem); + +void teek_request_cancellation(struct teec_operation *operation); + +#else + +static inline int teek_is_agent_alive(unsigned int agent_id) +{ + return TEEC_SUCCESS; +} + +static inline uint32_t teek_initialize_context(const char *name, + struct teec_context *context) +{ + return TEEC_SUCCESS; +} + +static inline void teek_finalize_context(struct teec_context *context) +{ + (void)context; +} + +static inline 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) +{ + return TEEC_SUCCESS; +} + +static inline void teek_close_session(struct teec_session *session) +{ + (void)session; +} + +static inline uint32_t teek_invoke_command(struct teec_session *session, + uint32_t cmd_id, struct teec_operation *operation, + uint32_t *return_origin) +{ + return TEEC_SUCCESS; +} + +static inline uint32_t teek_send_secfile(struct teec_session *session, + const char *file_buffer, unsigned int file_size) +{ + return TEEC_SUCCESS; +} + +#endif + +#endif diff --git a/linux/include/teek_client_constants.h b/linux/include/teek_client_constants.h new file mode 100644 index 0000000..c3a9543 --- /dev/null +++ b/linux/include/teek_client_constants.h @@ -0,0 +1,178 @@ +/* + * teek_client_constants.h + * + * macro declaration 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 + +#ifndef TEEK_CLIENT_CONSTANTS_H +#define TEEK_CLIENT_CONSTANTS_H + +enum global_service_cmd_id { + GLOBAL_CMD_ID_INVALID = 0x0, + GLOBAL_CMD_ID_BOOT_ACK = 0x1, + GLOBAL_CMD_ID_OPEN_SESSION = 0x2, + GLOBAL_CMD_ID_CLOSE_SESSION = 0x3, + GLOBAL_CMD_ID_LOAD_SECURE_APP = 0x4, + GLOBAL_CMD_ID_NEED_LOAD_APP = 0x5, + GLOBAL_CMD_ID_REGISTER_AGENT = 0x6, + GLOBAL_CMD_ID_UNREGISTER_AGENT = 0x7, + GLOBAL_CMD_ID_REGISTER_NOTIFY_MEMORY = 0x8, + GLOBAL_CMD_ID_UNREGISTER_NOTIFY_MEMORY = 0x9, + GLOBAL_CMD_ID_INIT_CONTENT_PATH = 0xa, + GLOBAL_CMD_ID_TERMINATE_CONTENT_PATH = 0xb, + GLOBAL_CMD_ID_ALLOC_EXCEPTION_MEM = 0xc, + GLOBAL_CMD_ID_TEE_TIME = 0xd, + GLOBAL_CMD_ID_TEE_INFO = 0xe, + GLOBAL_CMD_ID_REGISTER_LOG_MEM = 0xf, + GLOBAL_CMD_ID_KILL_TASK = 0x10, + GLOBAL_CMD_ID_TUI_EXCEPTION = 0x11, + GLOBAL_CMD_ID_ADJUST_TIME = 0x12, + GLOBAL_CMD_ID_SET_CA_HASH = 0x13, + /* set the Android's build version */ + GLOBAL_CMD_ID_SET_BUILD_VERSION = 0x14, + GLOBAL_CMD_ID_REGISTER_TTF_MEM = 0x15, + /* get session key for encrypting dialog */ + GLOBAL_CMD_ID_GET_SESSION_SECURE_PARAMS = 0x16, + GLOBAL_CMD_ID_REGISTER_MAILBOX = 0x17, + GLOBAL_CMD_ID_REGISTER_UNUSUAL_TTF_MEM = 0x18, + GLOBAL_CMD_ID_REGISTER_ION_MEM = 0x19, + GLOBAL_CMD_ID_DUMP_MEMINFO = 0x1a, + /* this cmd will be used to service no ca handle cmd */ + GLOBAL_CMD_ID_SET_SERVE_CMD = 0x1b, + GLOBAL_CMD_ID_ADD_DYNAMIC_ION = 0x1c, + GLOBAL_CMD_ID_DEL_DYNAMIC_ION = 0x1d, + GLOBAL_CMD_ID_RELEASE_ION_SRV = 0x1e, + /* this cmd for tui to get notch_size */ + GLOBAL_CMD_ID_TUI_NOTCH = 0x1f, + GLOBAL_CMD_ID_LATE_INIT = 0x20, + /* this cmd for tui to get information of foldable screen */ + GLOBAL_CMD_ID_TUI_FOLD = 0x21, + GLOBAL_CMD_ID_GET_TEE_VERSION = 0x22, +#ifdef CONFIG_CMS_SIGNATURE + GLOBAL_CMD_ID_UPDATE_TA_CRL = 0x23, +#endif + GLOBAL_CMD_ID_REGISTER_RESMEM = 0x24, + GLOBAL_CMD_ID_DUMP_SRV_SESS = 0x25, + GLOBAL_CMD_ID_UNKNOWN = 0x7FFFFFFE, + GLOBAL_CMD_ID_MAX = 0x7FFFFFFF +}; + +enum teec_result { + TEEC_SUCCESS = 0x0, + TEEC_ERROR_INVALID_CMD = 0x1, + TEEC_ERROR_SERVICE_NOT_EXIST = 0x2, + TEEC_ERROR_SESSION_NOT_EXIST = 0x3, + TEEC_ERROR_SESSION_MAXIMUM, + TEEC_ERROR_REGISTER_EXIST_SERVICE, + TEEC_ERROR_TAGET_DEAD_FATAL, + TEEC_ERROR_READ_DATA, + TEEC_ERROR_WRITE_DATA, + TEEC_ERROR_TRUNCATE_OBJECT, + TEEC_ERROR_SEEK_DATA, + TEEC_ERROR_RENAME_OBJECT, + TEEC_ERROR_TRUSTED_APP_LOAD_ERROR, + TEEC_ERROR_GENERIC = 0xFFFF0000, + TEEC_ERROR_ACCESS_DENIED = 0xFFFF0001, + TEEC_ERROR_CANCEL = 0xFFFF0002, + TEEC_ERROR_ACCESS_CONFLICT = 0xFFFF0003, + TEEC_ERROR_EXCESS_DATA = 0xFFFF0004, + TEEC_ERROR_BAD_FORMAT = 0xFFFF0005, + TEEC_ERROR_BAD_PARAMETERS = 0xFFFF0006, + TEEC_ERROR_BAD_STATE = 0xFFFF0007, + TEEC_ERROR_ITEM_NOT_FOUND = 0xFFFF0008, + TEEC_ERROR_NOT_IMPLEMENTED = 0xFFFF0009, + TEEC_ERROR_NOT_SUPPORTED = 0xFFFF000A, + TEEC_ERROR_NO_DATA = 0xFFFF000B, + TEEC_ERROR_OUT_OF_MEMORY = 0xFFFF000C, + TEEC_ERROR_BUSY = 0xFFFF000D, + TEEC_ERROR_COMMUNICATION = 0xFFFF000E, + TEEC_ERROR_SECURITY = 0xFFFF000F, + TEEC_ERROR_SHORT_BUFFER = 0xFFFF0010, + TEEC_PENDING = 0xFFFF2000, + TEEC_PENDING2 = 0xFFFF2001, + TEE_ERROR_TAGET_DEAD = 0xFFFF3024, + TEE_ERROR_GT_DEAD = 0xFFFF3124, + TEEC_ERROR_MAC_INVALID = 0xFFFF3071, + TEEC_CLIENT_INTR = 0xFFFF4000, + TEEC_ERROR_TUI_IN_USE = 0xFFFF7110, + TEEC_ERROR_TUI_SWITCH_CHANNAL, + TEEC_ERROR_TUI_CFG_DRIVER, + TEEC_ERROR_TUI_INVALID_EVENT, + TEEC_ERROR_TUI_POLL_EVENT, + TEEC_ERROR_TUI_CANCELED, + TEEC_ERROR_TUI_EXIT, + TEEC_ERROR_TUI_NOT_AVAILABLE, + TEEC_ERROR_SEC_FLASH_NOT_AVAILABLE, + TEEC_ERROR_CA_AUTH_FAIL = 0xFFFFCFE5, + TEE_ERROR_AUDIT_FAIL = 0xFFFF9112, +}; + +enum TEEC_ReturnCodeOrigin { + TEEC_ORIGIN_API = 0x1, + TEEC_ORIGIN_COMMS = 0x2, + TEEC_ORIGIN_TEE = 0x3, + TEEC_ORIGIN_TRUSTED_APP = 0x4, +}; + +enum TEEC_SharedMemCtl { + TEEC_MEM_INPUT = 0x1, + TEEC_MEM_OUTPUT = 0x2, + TEEC_MEM_INOUT = 0x3, +}; + +enum TEEC_ParamType { + TEEC_NONE = 0x0, + TEEC_VALUE_INPUT = 0x01, + TEEC_VALUE_OUTPUT = 0x02, + TEEC_VALUE_INOUT = 0x03, + TEEC_MEMREF_TEMP_INPUT = 0x05, + TEEC_MEMREF_TEMP_OUTPUT = 0x06, + TEEC_MEMREF_TEMP_INOUT = 0x07, + TEEC_ION_INPUT = 0x08, + TEEC_ION_SGLIST_INPUT = 0x09, + TEEC_MEMREF_SHARED_INOUT = 0x0a, + TEEC_MEMREF_WHOLE = 0xc, + TEEC_MEMREF_PARTIAL_INPUT = 0xd, + TEEC_MEMREF_PARTIAL_OUTPUT = 0xe, + TEEC_MEMREF_PARTIAL_INOUT = 0xf +}; + +enum TEE_ParamType { + TEE_PARAM_TYPE_NONE = 0x0, + TEE_PARAM_TYPE_VALUE_INPUT = 0x1, + TEE_PARAM_TYPE_VALUE_OUTPUT = 0x2, + TEE_PARAM_TYPE_VALUE_INOUT = 0x3, + TEE_PARAM_TYPE_MEMREF_INPUT = 0x5, + TEE_PARAM_TYPE_MEMREF_OUTPUT = 0x6, + TEE_PARAM_TYPE_MEMREF_INOUT = 0x7, + TEE_PARAM_TYPE_ION_INPUT = 0x8, + TEE_PARAM_TYPE_ION_SGLIST_INPUT = 0x9, + TEE_PARAM_TYPE_MEMREF_SHARED_INOUT = 0x0a, + TEE_PARAM_TYPE_RESMEM_INPUT = 0xc, + TEE_PARAM_TYPE_RESMEM_OUTPUT = 0xd, + TEE_PARAM_TYPE_RESMEM_INOUT = 0xe +}; + +enum TEEC_LoginMethod { + TEEC_LOGIN_PUBLIC = 0x0, + TEEC_LOGIN_USER, + TEEC_LOGIN_GROUP, + TEEC_LOGIN_APPLICATION = 0x4, + TEEC_LOGIN_USER_APPLICATION = 0x5, + TEEC_LOGIN_GROUP_APPLICATION = 0x6, + TEEC_LOGIN_IDENTIFY = 0x7, +}; + +#endif diff --git a/linux/include/teek_client_id.h b/linux/include/teek_client_id.h new file mode 100644 index 0000000..d7cf3b1 --- /dev/null +++ b/linux/include/teek_client_id.h @@ -0,0 +1,78 @@ +/* + * teek_client_id.h + * + * define exported data for secboot 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 TEE_CLIENT_ID_H +#define TEE_CLIENT_ID_H + +#define TEE_SERVICE_SECBOOT \ +{ \ + 0x08080808, \ + 0x0808, \ + 0x0808, \ + { \ + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08 \ + } \ +} + +/* e7ed1f64-4687-41da-96dc-cbe4f27c838f */ +#define TEE_SERVICE_ANTIROOT \ +{ \ + 0xE7ED1F64, \ + 0x4687, \ + 0x41DA, \ + { \ + 0x96, 0xDC, 0xCB, 0xE4, 0xF2, 0x7C, 0x83, 0x8F \ + } \ +} +/* dca5ae8a-769e-4e24-896b-7d06442c1c0e */ +#define TEE_SERVICE_SECISP \ +{ \ + 0xDCA5AE8A, \ + 0x769E, \ + 0x4E24, \ + { \ + 0x89, 0x6B, 0x7D, 0x06, 0x44, 0x2C, 0x1C, 0x0E \ + } \ +} +/* 5700f837-8b8e-4661-800b-42bb3fc3141f */ +#define TEE_SERVICE_DRM_GRALLOC \ +{ \ + 0x5700F837, \ + 0x8B8E, \ + 0x4661, \ + { \ + 0x80, 0x0B, 0x42, 0xBB, 0x3F, 0xC3, 0x14, 0x1F \ + } \ +} + +enum SVC_SECBOOT_CMD_ID { + SECBOOT_CMD_ID_INVALID = 0x0, + SECBOOT_CMD_ID_COPY_VRL, + SECBOOT_CMD_ID_COPY_DATA, + SECBOOT_CMD_ID_VERIFY_DATA, + SECBOOT_CMD_ID_RESET_IMAGE, + SECBOOT_CMD_ID_COPY_VRL_TYPE, + SECBOOT_CMD_ID_COPY_DATA_TYPE, + SECBOOT_CMD_ID_VERIFY_DATA_TYPE, + SECBOOT_CMD_ID_VERIFY_DATA_TYPE_LOCAL, + SECBOOT_CMD_ID_COPY_IMG_TYPE, + SECBOOT_CMD_ID_BSP_MODEM_CALL, + SECBOOT_CMD_ID_BSP_MODULE_VERIFY, + SECBOOT_CMD_ID_BSP_ICC_OPEN_THREAD, + SECBOOT_CMD_ID_BSP_RFILE_RW_THREAD, + SECBOOT_CMD_ID_GET_RNG_NUM, +}; +#endif diff --git a/linux/include/teek_client_type.h b/linux/include/teek_client_type.h new file mode 100644 index 0000000..810d05c --- /dev/null +++ b/linux/include/teek_client_type.h @@ -0,0 +1,192 @@ +/* + * teek_client_type.h + * + * define exported structures + * + * 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_CLIENT_TYPE_H +#define TEE_CLIENT_TYPE_H + +#include +#include "teek_client_constants.h" + +#ifndef NULL +#define NULL 0 +#endif + +struct teec_uuid { + uint32_t time_low; + uint16_t time_mid; + uint16_t timehi_and_version; + uint8_t clockseq_and_node[8]; +}; + +struct teec_context { + void *dev; + uint8_t *ta_path; + struct list_head shrd_mem_list; +}; + +struct teec_session { + uint32_t session_id; + struct teec_uuid service_id; + uint32_t ops_cnt; + struct teec_context *context; +}; + +struct teec_sharedmemory { + void *buffer; + size_t size; + uint32_t flags; + uint32_t ops_cnt; + bool is_allocated; + struct list_head head; + struct teec_context *context; +}; + +struct teec_tempmemory_reference { + void *buffer; + size_t size; +}; + +struct teec_registeredmemory_reference { + struct teec_sharedmemory *parent; + size_t size; + size_t offset; +}; + + +struct teec_value { + uint32_t a; + uint32_t b; +}; + +struct teec_ion_reference { + int ion_share_fd; + size_t ion_size; +}; + +union teec_parameter { + struct teec_tempmemory_reference tmpref; + struct teec_registeredmemory_reference memref; + struct teec_value value; + struct teec_ion_reference ionref; +}; + +struct teec_tui_parameter { + uint32_t event_type; + /* tui event type */ + uint32_t value; + /* return value, is keycode if tui event is getkeycode */ + uint32_t notch; /* notch size of device */ + uint32_t width; /* width of foldable screen */ + uint32_t height; /* height of foldable screen */ + uint32_t fold_state; /* state of foldable screen */ + uint32_t display_state; /* one state of folded state */ + uint32_t phy_width; /* real width of the mobile */ + uint32_t phy_height; /* real height of the mobile */ +}; + +struct teec_operation { + uint32_t started; + uint32_t paramtypes; + union teec_parameter params[4]; /* GP has four params */ + struct teec_session *session; + bool cancel_flag; +}; + +typedef uint32_t TEEC_Result; + +typedef struct { + uint32_t timeLow; + uint16_t timeMid; + uint16_t timeHiAndVersion; + uint8_t clockSeqAndNode[8]; +} TEEC_UUID; + +typedef struct { + void *dev; + uint8_t *ta_path; + struct list_head session_list; + struct list_head shrd_mem_list; +} TEEC_Context; + +typedef struct { + uint32_t session_id; + TEEC_UUID service_id; + uint32_t ops_cnt; + struct list_head head; + TEEC_Context *context; +} TEEC_Session; + +typedef struct { + void *buffer; + size_t size; + uint32_t flags; + uint32_t ops_cnt; + bool is_allocated; + struct list_head head; + TEEC_Context *context; +} TEEC_SharedMemory; + +typedef struct { + void *buffer; + size_t size; +} TEEC_TempMemoryReference; + +typedef struct { + TEEC_SharedMemory *parent; + size_t size; + size_t offset; +} TEEC_RegisteredMemoryReference; + +typedef struct { + uint32_t a; + uint32_t b; +} TEEC_Value; + +typedef struct { + int ion_share_fd; + size_t ion_size; +} TEEC_IonReference; + +typedef union { + TEEC_TempMemoryReference tmpref; + TEEC_RegisteredMemoryReference memref; + TEEC_Value value; + TEEC_IonReference ionref; +} TEEC_Parameter; + +typedef struct { + uint32_t event_type; + /* Tui event type */ + uint32_t value; + /* return value, is keycode if tui event is getKeycode */ + uint32_t notch; /* notch size of device */ + uint32_t width; /* width of foldable screen */ + uint32_t height; /* height of foldable screen */ + uint32_t fold_state; /* state of foldable screen */ + uint32_t display_state; /* one state of folded state */ + uint32_t phy_width; /* real width of the mobile */ + uint32_t phy_height; /* real height of the mobile */ +} TEEC_TUI_Parameter; + +typedef struct { + uint32_t started; + uint32_t paramTypes; + TEEC_Parameter params[4]; + TEEC_Session *session; + bool cancel_flag; +} TEEC_Operation; + +#endif diff --git a/linux/include/teek_ns_client.h b/linux/include/teek_ns_client.h new file mode 100644 index 0000000..e5127f2 --- /dev/null +++ b/linux/include/teek_ns_client.h @@ -0,0 +1,205 @@ +/* + * teek_ns_client.h + * + * define structures and IOCTLs. + * + * 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_NS_CLIENT_H +#define TEEK_NS_CLIENT_H + +#include +#include +#include +#include +#include +#include "tc_ns_client.h" +#include "tc_ns_log.h" + +#define TC_NS_CLIENT_IOC_MAGIC 't' +#define TC_NS_CLIENT_DEV "tc_ns_client" +#define TC_NS_CLIENT_DEV_NAME "/dev/tc_ns_client" + +#define EXCEPTION_MEM_SIZE (8*1024) /* mem for exception handling */ +#define TSP_REQUEST 0xB2000008 +#define TSP_RESPONSE 0xB2000009 +#define TSP_REE_SIQ 0xB200000A +#define TSP_CRASH 0xB200000B +#define TSP_PREEMPTED 0xB2000005 +#define TC_CALL_GLOBAL 0x01 +#define TC_CALL_SYNC 0x02 +#define TC_CALL_LOGIN 0x04 +#define TEE_REQ_FROM_USER_MODE 0U +#define TEE_REQ_FROM_KERNEL_MODE 1U +#define TEE_PARAM_NUM 4 +#define VMALLOC_TYPE 0 +#define RESERVED_TYPE 1 + +/* Max sizes for login info buffer comming from teecd */ +#define MAX_PACKAGE_NAME_LEN 255 +/* The apk certificate format is as follows: + * modulus_size(4 bytes) + modulus buffer(512 bytes) + * + exponent size(4 bytes) + exponent buffer(1 bytes) + */ +#define MAX_PUBKEY_LEN 1024 + +struct tc_ns_dev_list { + struct mutex dev_lock; /* for dev_file_list */ + struct list_head dev_file_list; +}; + +struct tc_uuid { + uint32_t time_low; + uint16_t time_mid; + uint16_t timehi_and_version; + uint8_t clockseq_and_node[8]; /* clock len is 8 */ +}; + +struct tc_ns_shared_mem { + void *kernel_addr; + void *user_addr; + void *user_addr_ca; /* for ca alloc share mem */ + unsigned int len; + int mem_type; + struct list_head head; + atomic_t usage; + atomic_t offset; +}; + +struct tc_ns_service { + unsigned char uuid[UUID_LEN]; + struct mutex session_lock; /* for session_list */ + struct list_head session_list; + struct list_head head; + struct mutex operation_lock; /* for session's open/close */ + atomic_t usage; +}; + +#define SERVICES_MAX_COUNT 32 /* service limit can opened on 1 fd */ +struct tc_ns_dev_file { + unsigned int dev_file_id; + struct mutex service_lock; /* for service_ref[], services[] */ + uint8_t service_ref[SERVICES_MAX_COUNT]; /* a judge if set services[i]=NULL */ + struct tc_ns_service *services[SERVICES_MAX_COUNT]; + struct mutex shared_mem_lock; /* for shared_mem_list */ + struct list_head shared_mem_list; + struct list_head head; + /* Device is linked to call from kernel */ + uint8_t kernel_api; + /* client login info provided by teecd, can be either package name and public + * key or uid(for non android services/daemons) + * login information can only be set once, dont' allow subsequent calls + */ + bool login_setup; + struct mutex login_setup_lock; /* for login_setup */ + uint32_t pkg_name_len; + uint8_t pkg_name[MAX_PACKAGE_NAME_LEN]; + uint32_t pub_key_len; + uint8_t pub_key[MAX_PUBKEY_LEN]; + int load_app_flag; + struct completion close_comp; /* for kthread close unclosed session */ +}; + +union tc_ns_parameter { + struct { + unsigned int buffer; + unsigned int size; + } memref; + struct { + unsigned int a; + unsigned int b; + } value; + struct { + unsigned int buffer; + unsigned int size; + } sharedmem; +}; + +struct tc_ns_login { + unsigned int method; + unsigned int mdata; +}; + +struct tc_ns_operation { + unsigned int paramtypes; + union tc_ns_parameter params[TEE_PARAM_NUM]; + unsigned int buffer_h_addr[TEE_PARAM_NUM]; + struct tc_ns_shared_mem *sharemem[TEE_PARAM_NUM]; + void *mb_buffer[TEE_PARAM_NUM]; +}; + +struct tc_ns_temp_buf { + void *temp_buffer; + unsigned int size; +}; + +enum smc_cmd_type { + CMD_TYPE_GLOBAL, + CMD_TYPE_TA, + CMD_TYPE_TA_AGENT, + CMD_TYPE_TA2TA_AGENT, /* compatible with TA2TA2TA->AGENT etc. */ + CMD_TYPE_BUILDIN_AGENT, +}; + +struct tc_ns_smc_cmd { + uint8_t uuid[sizeof(struct tc_uuid)]; + unsigned int cmd_type; + unsigned int cmd_id; + unsigned int dev_file_id; + unsigned int context_id; + unsigned int agent_id; + unsigned int operation_phys; + unsigned int operation_h_phys; + unsigned int login_method; + unsigned int login_data_phy; + unsigned int login_data_h_addr; + unsigned int login_data_len; + unsigned int err_origin; + int ret_val; + unsigned int event_nr; + unsigned int uid; + unsigned int ca_pid; /* pid */ + unsigned int pid; /* tgid */ + unsigned int eventindex; /* tee audit event index for upload */ + bool started; +} __attribute__((__packed__)); + +/* + * @brief + */ +struct tc_wait_data { + wait_queue_head_t send_cmd_wq; + int send_wait_flag; +}; + +#define NUM_OF_SO 1 +#ifdef CONFIG_CMS_CAHASH_AUTH +#define KIND_OF_SO 1 +#else +#define KIND_OF_SO 2 +#endif +struct tc_ns_session { + unsigned int session_id; + struct list_head head; + struct tc_wait_data wait_data; + struct mutex ta_session_lock; /* for open/close/invoke on 1 session */ + struct tc_ns_dev_file *owner; + uint8_t auth_hash_buf[MAX_SHA_256_SZ * NUM_OF_SO + MAX_SHA_256_SZ]; + atomic_t usage; +}; + +struct mb_cmd_pack { + struct tc_ns_operation operation; + unsigned char login_data[MAX_SHA_256_SZ * NUM_OF_SO + MAX_SHA_256_SZ]; +}; + +#endif diff --git a/linux/ko_adapt.c b/linux/ko_adapt.c new file mode 100644 index 0000000..72784e7 --- /dev/null +++ b/linux/ko_adapt.c @@ -0,0 +1,196 @@ +/* + * ko_adapt.c + * + * function for find symbols not exported + * + * 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 "ko_adapt.h" +#include +#include +#include +#include +#include +#if (KERNEL_VERSION(4, 14, 0) <= LINUX_VERSION_CODE) +#include +#endif +#include +#include +#include +#include "tc_ns_log.h" + +typedef const struct cred *(get_task_cred_func)(struct task_struct *); +typedef void (kthread_bind_mask_func)(struct task_struct *, const struct cpumask *); +typedef long (sys_chown_func)(const char __user *filename, + uid_t user, gid_t group); +typedef ssize_t (vfs_write_func)(struct file *file, const char __user *buf, + size_t count, loff_t *pos); +typedef ssize_t (vfs_read_func)(struct file *file, char __user *buf, + size_t count, loff_t *pos); + +typedef struct page *(alloc_pages_func)(gfp_t gfp_mask, unsigned int order); + +typedef struct workqueue_attrs *(alloc_workqueue_attrs_func)(gfp_t gfp_mask); +typedef void (free_workqueue_attrs_func)(struct workqueue_attrs *attrs); + +const struct cred *koadpt_get_task_cred(struct task_struct *task) +{ + static get_task_cred_func *get_task_cred_pt = NULL; + + if (!task) + return NULL; + + if (!get_task_cred_pt) { + get_task_cred_pt = (get_task_cred_func *) + (uintptr_t)kallsyms_lookup_name("get_task_cred"); + if (IS_ERR_OR_NULL(get_task_cred_pt)) { + tloge("fail to find symbol get task cred\n"); + return NULL; + } + } + return get_task_cred_pt(task); +} + +void koadpt_kthread_bind_mask(struct task_struct *task, + const struct cpumask *mask) +{ + static kthread_bind_mask_func *kthread_bind_mask_pt = NULL; + + if (!task || !mask) + return; + + if (!kthread_bind_mask_pt) { + kthread_bind_mask_pt = (kthread_bind_mask_func *) + (uintptr_t)kallsyms_lookup_name("kthread_bind_mask"); + if (IS_ERR_OR_NULL(kthread_bind_mask_pt)) { + tloge("fail to find symbol kthread bind mask\n"); + return; + } + } + kthread_bind_mask_pt(task, mask); +} + +long koadpt_sys_chown(const char __user *filename, uid_t user, gid_t group) +{ + static sys_chown_func *sys_chown_pt = NULL; + + if (!filename) + return -EINVAL; + + if (!sys_chown_pt) { + sys_chown_pt = (sys_chown_func *) + (uintptr_t)kallsyms_lookup_name("sys_chown"); + if (IS_ERR_OR_NULL(sys_chown_pt)) { + tloge("fail to find symbol kthread bind mask\n"); + return -EINVAL; + } + } + return sys_chown_pt(filename, user, group); +} + +ssize_t koadpt_vfs_write(struct file *file, const char __user *buf, + size_t count, loff_t *pos) +{ +#if (LINUX_VERSION_CODE <= KERNEL_VERSION(4, 19, 116)) + static vfs_write_func *vfs_write_pt = NULL; + + if (!file || !buf || !pos) + return -EINVAL; + + if (!vfs_write_pt) { + vfs_write_pt = (vfs_write_func *) + (uintptr_t)kallsyms_lookup_name("vfs_write"); + if (IS_ERR_OR_NULL(vfs_write_pt)) { + tloge("fail to find symbol vfs write\n"); + return -EINVAL; + } + } + return vfs_write_pt(file, buf, count, pos); +#else + return vfs_write(file, buf, count, pos); +#endif +} + +ssize_t koadpt_vfs_read(struct file *file, char __user *buf, + size_t count, loff_t *pos) +{ +#if (LINUX_VERSION_CODE <= KERNEL_VERSION(4, 19, 116)) + static vfs_read_func *vfs_read_pt = NULL; + + if (!file || !buf || !pos) + return -EINVAL; + + if (!vfs_read_pt) { + vfs_read_pt = (vfs_read_func *) + (uintptr_t)kallsyms_lookup_name("vfs_read"); + if (IS_ERR_OR_NULL(vfs_read_pt)) { + tloge("fail to find symbol vfs read\n"); + return -EINVAL; + } + } + return vfs_read_pt(file, buf, count, pos); +#else + return vfs_read(file, buf, count, pos); +#endif +} + +struct page *koadpt_alloc_pages(gfp_t gfp_mask, unsigned int order) +{ +#ifdef CONFIG_NUMA + static alloc_pages_func *alloc_pages_pt = NULL; + + if (!alloc_pages_pt) { + alloc_pages_pt = (alloc_pages_func *) + (uintptr_t)kallsyms_lookup_name("alloc_pages_current"); + if (IS_ERR_OR_NULL(alloc_pages_pt)) { + tloge("fail to find symbol alloc pages current\n"); + return NULL; + } + } + return alloc_pages_pt(gfp_mask, order); +#else + return alloc_pages(gfp_mask, order); +#endif +} + +struct workqueue_attrs *koadpt_alloc_workqueue_attrs(gfp_t gfp_mask) +{ + static alloc_workqueue_attrs_func *alloc_workqueue_attrs_pt = NULL; + + if (!alloc_workqueue_attrs_pt) { + alloc_workqueue_attrs_pt = (alloc_workqueue_attrs_func *) + (uintptr_t)kallsyms_lookup_name("alloc_workqueue_attrs"); + if (IS_ERR_OR_NULL(alloc_workqueue_attrs_pt)) { + tloge("fail to find symbol alloc workqueue attrs\n"); + return NULL; + } + } + return alloc_workqueue_attrs_pt(gfp_mask); +} + +void koadpt_free_workqueue_attrs(struct workqueue_attrs *attrs) +{ + static free_workqueue_attrs_func *free_workqueue_attrs_pt = NULL; + + if (!attrs) + return; + + if (!free_workqueue_attrs_pt) { + free_workqueue_attrs_pt = (free_workqueue_attrs_func *) + (uintptr_t)kallsyms_lookup_name("free_workqueue_attrs"); + if (IS_ERR_OR_NULL(free_workqueue_attrs_pt)) { + tloge("fail to find symbol free workqueue attrs\n"); + return; + } + } + free_workqueue_attrs_pt(attrs); +} diff --git a/linux/kthread_affinity/tz_kthread_affinity.c b/linux/kthread_affinity/tz_kthread_affinity.c new file mode 100644 index 0000000..1fcc392 --- /dev/null +++ b/linux/kthread_affinity/tz_kthread_affinity.c @@ -0,0 +1,72 @@ +/* + * tz_kthread_affinity.c + * + * function for set kthread affinity + * + * 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_kthread_affinity.h" +#include +#include +#include "tc_ns_log.h" +#include "ko_adapt.h" +#include "tz_kthread_cpumask.h" + +static struct cpumask g_kthread_cpumask; + +void init_kthread_cpumask(void) +{ + int ret; + + cpumask_clear(&g_kthread_cpumask); + + ret = get_kthread_cpumask(&g_kthread_cpumask); + if (ret < 0) + tloge("get kthread cpumask failed\n"); +} + +void tz_kthread_bind_mask(struct task_struct *kthread) +{ + if (!kthread) + return; + + if (cpumask_empty(&g_kthread_cpumask)) + return; + + koadpt_kthread_bind_mask(kthread, &g_kthread_cpumask); +} + +void tz_workqueue_bind_mask(struct workqueue_struct *wq, uint32_t flag) +{ + int ret; + struct workqueue_attrs *attrs = NULL; + + if (!wq) + return; + + if (cpumask_empty(&g_kthread_cpumask)) + return; + + attrs = koadpt_alloc_workqueue_attrs(GFP_KERNEL); + if (!attrs) { + tloge("alloc workqueue attrs failed\n"); + return; + } + attrs->nice = (flag & WQ_HIGHPRI) ? MIN_NICE : 0; + attrs->no_numa = true; + cpumask_copy(attrs->cpumask, &g_kthread_cpumask); + + ret = apply_workqueue_attrs(wq, attrs); + if (ret) + tloge("apply workqueue attrs failed %d\n", ret); + koadpt_free_workqueue_attrs(attrs); +} diff --git a/linux/kthread_affinity/tz_kthread_affinity.h b/linux/kthread_affinity/tz_kthread_affinity.h new file mode 100644 index 0000000..60bd45e --- /dev/null +++ b/linux/kthread_affinity/tz_kthread_affinity.h @@ -0,0 +1,46 @@ +/* + * tz_kthread_affinity.h + * + * exported funcs for kthread affinity + * + * 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_KTHREAD_AFFINITY_H +#define TZ_KTHREAD_AFFINITY_H + +#include +#include + +#define TZ_WQ_MAX_ACTIVE 1 + +#ifdef CONFIG_KTHREAD_AFFINITY +void init_kthread_cpumask(void); +void tz_kthread_bind_mask(struct task_struct *kthread); +void tz_workqueue_bind_mask(struct workqueue_struct *wq, uint32_t flag); +#else +static inline void init_kthread_cpumask(void) +{ +} + +static inline void tz_kthread_bind_mask(struct task_struct *kthread) +{ + (void)kthread; +} + +static inline void tz_workqueue_bind_mask(struct workqueue_struct *wq, + uint32_t flag) +{ + (void)wq; + (void)flag; +} +#endif +#endif diff --git a/linux/kthread_affinity/tz_kthread_cpumask.h b/linux/kthread_affinity/tz_kthread_cpumask.h new file mode 100644 index 0000000..f897f6e --- /dev/null +++ b/linux/kthread_affinity/tz_kthread_cpumask.h @@ -0,0 +1,24 @@ +/* + * tz_kthread_cpumask.h + * + * exported funcs for get kthread cpumask + * + * 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_KTHREAD_CPUMASK_H +#define TZ_KTHREAD_CPUMASK_H + +#include + +int get_kthread_cpumask(struct cpumask *cpumask); + +#endif diff --git a/linux/kthread_affinity/tz_kthread_cpumask_dts.c b/linux/kthread_affinity/tz_kthread_cpumask_dts.c new file mode 100644 index 0000000..4d03737 --- /dev/null +++ b/linux/kthread_affinity/tz_kthread_cpumask_dts.c @@ -0,0 +1,53 @@ +/* + * tz_kthread_cpumask.c + * + * function for get kthread cpumask + * + * 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_kthread_cpumask.h" +#include +#include +#include "tc_ns_log.h" + +/* on mdc, kthread should be bind to ctrlcpu, which is read from dts */ +int get_kthread_cpumask(struct cpumask *cpumask) +{ + int ret; + uint32_t i; + uint32_t ctrl_cpu_num; + struct device_node *ctrl_cpu_node = NULL; + + if (!cpumask) + return -1; + + ctrl_cpu_node = of_find_node_by_name(NULL, "ascend_ctl"); + if (ctrl_cpu_node == NULL) { + tloge("get ctrl cpu node failed\n"); + return -1; + } + + ret = of_property_read_u32_index(ctrl_cpu_node, "ctrl_cpu_num", 0, &ctrl_cpu_num); + if (ret < 0) { + tloge("get ctrl cpu num failed, err=%d\n", ret); + return ret; + } + if (ctrl_cpu_num > num_online_cpus()) { + tloge("ctrl cpu num is invalid\n"); + return -1; + } + + for (i = 0; i < ctrl_cpu_num; i++) + cpumask_set_cpu(i, cpumask); + + return 0; +} diff --git a/linux/tlogger/Kconfig b/linux/tlogger/Kconfig new file mode 100644 index 0000000..1bfd14e --- /dev/null +++ b/linux/tlogger/Kconfig @@ -0,0 +1,25 @@ +config TEELOG + bool "Secure Execution Log Driver" + default n + depends on TZDRIVER + help + TEEOS log + +config TEE_LOG_ACHIVE_PATH + string "Tee log achive path" + default "/data/log/tee/last_teemsg" + depends on TEELOG + help + Last tee msg log path + +choice + prompt "Register tee log Mem" + default PAGES_MEM + depends on TEELOG + +config PAGES_MEM + bool "Register pages log mem" + help + Register pages log mem + +endchoice diff --git a/linux/tlogger/Makefile b/linux/tlogger/Makefile new file mode 100644 index 0000000..12cb814 --- /dev/null +++ b/linux/tlogger/Makefile @@ -0,0 +1,22 @@ +ifeq ($(strip $(TARGET_PRODUCT)), mdc) + ccflags-y += -DCONFIG_MDC_PLATFORM +endif + +ifeq ($(strip $(TARGET_PRODUCT)), mini) + ccflags-y += -DCONFIG_MINI_PLATFORM +endif + +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/core + +obj-$(CONFIG_TEELOG) += tlogger.o + +# If no log mechanism is available, the pages memory can be used. +obj-$(CONFIG_PAGES_MEM) += log_pages_cfg.o diff --git a/linux/tlogger/log_cfg_api.h b/linux/tlogger/log_cfg_api.h new file mode 100644 index 0000000..f54f04b --- /dev/null +++ b/linux/tlogger/log_cfg_api.h @@ -0,0 +1,77 @@ +/* + * log_cfg_api.h + * + * for log cfg api define + * + * 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 LOG_CFG_API_H +#define LOG_CFG_API_H + +#include + +#if (defined(CONFIG_PAGES_MEM) && defined(CONFIG_TEELOG)) +int register_log_mem(u64 *addr, u32 *len); +int register_log_exception(void); +void report_log_system_error(void); +void report_log_system_panic(void); +int *map_log_mem(u64 mem_addr, u32 mem_len); +void unmap_log_mem(int *log_buffer); +void get_log_chown(uid_t *user, gid_t *group); +void unregister_log_exception(void); +void ta_crash_report_log(void); +#else +static inline int register_log_mem(const u64 *addr, const u32 *len) +{ + (void)addr; + (void)len; + return 0; +} + +static inline int register_log_exception(void) +{ + return 0; +} + +static inline void report_log_system_error(void) +{ +} + +static inline void report_log_system_panic(void) +{ +} + +static inline int *map_log_mem(u64 mem_addr, u32 mem_len) +{ + (void)mem_addr; + (void)mem_len; + return NULL; +} +static inline void unmap_log_mem(const int *log_buffer) +{ + (void)log_buffer; +} + +static inline void get_log_chown(const uid_t *user, const gid_t *group) +{ + (void)user; + (void)group; +} +static inline void unregister_log_exception(void) +{ +} + +static inline void ta_crash_report_log(void) +{ +} +#endif +#endif diff --git a/linux/tlogger/log_pages_cfg.c b/linux/tlogger/log_pages_cfg.c new file mode 100644 index 0000000..3e02e8d --- /dev/null +++ b/linux/tlogger/log_pages_cfg.c @@ -0,0 +1,143 @@ +/* + * log_pages_cfg.c + * + * for pages log cfg api define + * + * 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 "log_cfg_api.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "tc_ns_log.h" +#include "tlogger.h" + +void unregister_log_exception(void) +{ +} +int register_log_exception(void) +{ + return 0; +} + +struct pages_module_result { + u64 log_addr; + unsigned int log_len; +}; + +struct pages_module_result g_mem_info = {0}; + +#ifdef CONFIG_512K_LOG_PAGES_MEM +#define PAGES_LOG_MEM_LEN (512 * SZ_1K) /* mem size: 512 k */ +#else +#define PAGES_LOG_MEM_LEN (256 * SZ_1K) /* mem size: 256 k */ +#endif + +static int tee_pages_register_core(void) +{ + g_mem_info.log_addr = (uintptr_t)__get_free_pages( + GFP_KERNEL | __GFP_ZERO, get_order(PAGES_LOG_MEM_LEN)); + if (IS_ERR_OR_NULL((void *)(uintptr_t)g_mem_info.log_addr)) { + tloge("get log mem error\n"); + return -1; + } + + g_mem_info.log_len = PAGES_LOG_MEM_LEN; + return 0; +} + +/* Register log memory */ +int register_log_mem(u64 *addr, u32 *len) +{ + int ret; + u64 mem_addr; + u32 mem_len; + + if (!addr || !len) { + tloge("check addr or len is failed\n"); + return -1; + } + + ret = tee_pages_register_core(); + if (ret) + return ret; + + mem_addr = virt_to_phys((void *)(uintptr_t)g_mem_info.log_addr); + mem_len = g_mem_info.log_len; + + ret = register_mem_to_teeos(mem_addr, mem_len, true); + if (ret) + return ret; + + *addr = g_mem_info.log_addr; + *len = g_mem_info.log_len; + return ret; +} + +void report_log_system_error(void) +{ +} + +void report_log_system_panic(void) +{ +/* default support trigger ap reset */ +#ifndef NOT_TRIGGER_AP_RESET + panic("TEEOS panic\n"); +#endif +} + +void ta_crash_report_log(void) +{ +} + +int *map_log_mem(u64 mem_addr, u32 mem_len) +{ + (void)mem_len; + return (int *)(uintptr_t)mem_addr; +} + +void unmap_log_mem(int *log_buffer) +{ + free_pages((unsigned long)(uintptr_t)log_buffer, + get_order(PAGES_LOG_MEM_LEN)); +} + +#define ROOT_UID 0 + +#ifdef LAST_TEE_MSG_ROOT_GID +#define FILE_CHOWN_GID 0 +#else +/* system gid for last_teemsg file sys chown */ +#define FILE_CHOWN_GID 1000 +#endif + +void get_log_chown(uid_t *user, gid_t *group) +{ + if (!user || !group) { + tloge("user or group buffer is null\n"); + return; + } + + *user = ROOT_UID; + *group = FILE_CHOWN_GID; +} diff --git a/linux/tlogger/tlogger.c b/linux/tlogger/tlogger.c new file mode 100644 index 0000000..5a99d50 --- /dev/null +++ b/linux/tlogger/tlogger.c @@ -0,0 +1,1269 @@ +/* + * tlogger.c + * + * TEE Logging Subsystem, read the tee os log from log memory + * + * 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 "tlogger.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "smc_smp.h" +#include "mailbox_mempool.h" +#include "teek_client_constants.h" +#include "tc_ns_client.h" +#include "teek_ns_client.h" +#include "log_cfg_api.h" +#include "tc_ns_log.h" +#include "ko_adapt.h" + +/* for log item ----------------------------------- */ +#define LOG_ITEM_MAGIC 0x5A5A +#define LOG_ITEM_LEN_ALIGN 64 +#define LOG_ITEM_MAX_LEN 1024 +#define LOG_READ_STATUS_ERROR 0x000FFFF + +/* =================================================== */ +#define LOGGER_LOG_TEEOS "teelog" /* tee os log */ +#define __TEELOGGERIO 0xBE /* for ioctl */ + +#define DUMP_START_MAGIC "Dump SPI notification" +#define DUMP_END_MAGIC "Dump task states END" + +#define GET_VERSION_BASE 5 +#define SET_READERPOS_CUR_BASE 6 +#define SET_TLOGCAT_STAT_BASE 7 +#define GET_TLOGCAT_STAT_BASE 8 + +/* get tee verison */ +#define MAX_TEE_VERSION_LEN 256 +#define TEELOGGER_GET_VERSION \ + _IOR(__TEELOGGERIO, GET_VERSION_BASE, char[MAX_TEE_VERSION_LEN]) +/* set the log reader pos to current pos */ +#define TEELOGGER_SET_READERPOS_CUR \ + _IO(__TEELOGGERIO, SET_READERPOS_CUR_BASE) +#define TEELOGGER_SET_TLOGCAT_STAT \ + _IO(__TEELOGGERIO, SET_TLOGCAT_STAT_BASE) +#define TEELOGGER_GET_TLOGCAT_STAT \ + _IO(__TEELOGGERIO, GET_TLOGCAT_STAT_BASE) + +int g_tlogcat_f = 0; + +#ifndef CONFIG_TEE_LOG_ACHIVE_PATH +#define CONFIG_TEE_LOG_ACHIVE_PATH "/data/log/tee/last_teemsg" +#endif +#define TEE_LOG_FILE_NAME_MAX 256 + +u32 g_last_read_offset = 0; + +#define NEVER_USED_LEN 32U +#define LOG_ITEM_RESERVED_LEN 1U + +/* 64 byte head + user log */ +struct log_item { + u8 never_used[NEVER_USED_LEN]; + u16 magic; + u16 reserved0; + u32 serial_no; + s16 real_len; /* log real len */ + u16 buffer_len; /* log buffer's len, multiple of 32 bytes */ + u8 uuid[UUID_LEN]; + u8 log_source_type; + u8 reserved[LOG_ITEM_RESERVED_LEN]; + u8 log_level; + u8 new_line; /* '\n' char, easy viewing log in bbox.bin file */ + u8 log_buffer[0]; +}; + +/* --- for log mem --------------------------------- */ +#define TEMP_LOG_MEM_SIZE (10 * SZ_1K) + +#define LOG_BUFFER_RESERVED_LEN 11U +#define VERSION_INFO_LEN 156U + +/* + * Log's buffer flag info, size: 64 bytes head + 156 bytes's version info. + * For filed description: + * last_pos : current log's end position, last log's start position. + * write_loops: Write cyclically. Init value is 0, when memory is used + * up, the value add 1. + */ +struct log_buffer_flag { + u32 reserved0; + u32 last_pos; + u32 write_loops; + u32 log_level; + u32 reserved[LOG_BUFFER_RESERVED_LEN]; + u32 max_len; + u8 version_info[VERSION_INFO_LEN]; +}; + +struct log_buffer { + struct log_buffer_flag flag; + u8 buffer_start[0]; +}; + +static struct log_buffer *g_log_buffer = NULL; + +struct tlogger_log { + unsigned char *buffer_info; /* ring buffer info */ + struct mutex mutex_info; /* this mutex protects buffer_info */ + wait_queue_head_t wait_queue_head; /* wait queue head for reader */ + struct list_head logs; /* log channels list */ + struct miscdevice misc_device; /* misc device log */ + struct list_head readers; /* log's readers */ +}; + +static LIST_HEAD(m_log_list); + +struct tlogger_reader { + struct tlogger_log *log; /* tlogger_log info data */ + struct list_head list; /* log entry in tlogger_log's list */ + /* Current reading position, start position of next read again */ + u32 r_off; + u32 r_loops; + u32 r_sn; + u32 r_failtimes; + u32 r_from_cur; + u32 r_is_tlogf; + bool r_all; /* whether this reader can read all entries */ + unsigned int r_ver; +}; + +static u32 g_log_mem_len = 0; +static u32 g_tlogcat_count = 0; +static struct tlogger_log *g_log; + +static struct tlogger_log *get_reader_log(const struct file *file) +{ + struct tlogger_reader *reader = NULL; + + reader = file->private_data; + if (!reader) + return NULL; + + return reader->log; +} + +static bool check_log_item_validite(struct log_item *item, + u32 item_max_size) +{ + bool con = (item && (item->magic == LOG_ITEM_MAGIC) && + (item->buffer_len > 0) && + (item->real_len > 0) && + (item->buffer_len % LOG_ITEM_LEN_ALIGN == 0) && + (item->real_len <= item->buffer_len) && + ((item->buffer_len - item->real_len) < LOG_ITEM_LEN_ALIGN) && + (item->buffer_len + sizeof(*item) <= item_max_size)); + + return con; +} + +static struct log_item *get_next_log_item(const u8 *buffer_start, + u32 max_len, u32 read_pos, u32 scope_len, u32 *pos) +{ + u32 i = 0; + struct log_item *item = NULL; + u32 max_size; + + if ((read_pos + scope_len) > max_len) + return NULL; + + while ((i + sizeof(*item) + LOG_ITEM_LEN_ALIGN) <= scope_len) { + *pos = read_pos + i; + item = (struct log_item *)(buffer_start + read_pos + i); + max_size = (((scope_len - i) > LOG_ITEM_MAX_LEN) ? + LOG_ITEM_MAX_LEN : (scope_len - i)); + if (check_log_item_validite(item, max_size)) + break; + + i += LOG_ITEM_LEN_ALIGN; + item = NULL; + } + + return item; +} + +struct reader_position { + const u8 *buffer_start; + u32 max_len; + u32 start_pos; + u32 end_pos; +}; + +static u32 parse_log_item(char __user *buf, size_t count, + struct reader_position *position, u32 *read_off, + bool *user_buffer_left) +{ + struct log_item *next_item = NULL; + u32 buf_left; + u32 buf_written; + u32 item_len; + bool con = false; + u32 start_pos = position->start_pos; + + buf_written = 0; + buf_left = count; + + con = (!read_off || !position->buffer_start); + if (con) + return buf_written; + + *user_buffer_left = true; + while (start_pos < position->end_pos) { + next_item = get_next_log_item(position->buffer_start, + position->max_len, start_pos, + position->end_pos - start_pos, &start_pos); + if (!next_item) + break; + + /* copy to user */ + item_len = next_item->buffer_len + sizeof(*next_item); + if (buf_left < item_len) { + *user_buffer_left = false; + break; + } + + start_pos += item_len; + if (copy_to_user(buf + buf_written, + (void *)next_item, item_len)) + tloge("copy failed, item len %u\n", item_len); + + buf_written += item_len; + buf_left -= item_len; + } + + *read_off = start_pos; + return buf_written; +} + +static ssize_t get_buffer_info(struct tlogger_reader *reader, + struct log_buffer_flag *buffer_flag, struct log_buffer **log_buffer) +{ + struct tlogger_log *log = NULL; + errno_t ret; + struct log_buffer *buffer_tmp = NULL; + + log = reader->log; + if (!log) + return -EINVAL; + + buffer_tmp = (struct log_buffer*)log->buffer_info; + if (!buffer_tmp) + return -EINVAL; + + __asm__ volatile ("isb"); + __asm__ volatile ("dsb sy"); + + mutex_lock(&log->mutex_info); + ret = memcpy_s(buffer_flag, sizeof(*buffer_flag), &buffer_tmp->flag, + sizeof(buffer_tmp->flag)); + mutex_unlock(&log->mutex_info); + if (ret) { + tloge("memcpy failed %d\n", ret); + return -EAGAIN; + } + + *log_buffer = buffer_tmp; + return 0; +} + +#define LOG_BUFFER_MAX_LEN 0x100000 + +static ssize_t get_last_read_pos(struct log_buffer_flag *log_flag, + struct tlogger_reader *reader, u32 *log_last_pos, u32 *is_read) +{ + u32 buffer_max_len = g_log_mem_len - sizeof(*g_log_buffer); + + *is_read = 0; + + if (buffer_max_len > LOG_BUFFER_MAX_LEN) + return -EINVAL; + + *log_last_pos = log_flag->last_pos; + if (*log_last_pos == reader->r_off && + log_flag->write_loops == reader->r_loops) + return 0; + + if (log_flag->max_len < *log_last_pos || + log_flag->max_len > buffer_max_len) { + tloge("invalid data maxlen %x pos %x\n", + log_flag->max_len , *log_last_pos); + return -EFAULT; + } + + if (reader->r_off > log_flag->max_len) { + tloge("invalid data roff %x maxlen %x\n", + reader->r_off , log_flag->max_len); + return -EFAULT; + } + + *is_read = 1; + return 0; +} + +static void set_reader_position(struct reader_position *position, + const u8 *buffer_start, u32 max_len, u32 start_pos, u32 end_pos) +{ + position->buffer_start = buffer_start; + position->max_len = max_len; + position->start_pos = start_pos; + position->end_pos = end_pos; +} + +static ssize_t proc_read_ret(u32 buf_written, + const struct tlogger_reader *reader) +{ + ssize_t ret; + + if (!buf_written) { + ret = LOG_READ_STATUS_ERROR; + } else { + ret = buf_written; + tlogd("read length %u\n", buf_written); + g_last_read_offset = reader->r_off; + } + return ret; +} + +static ssize_t check_read_params(struct file *file, + char __user *buf, size_t count) +{ + if (count < LOG_ITEM_MAX_LEN) + return -EINVAL; + + if (!file || !buf) + return -EINVAL; + + return 0; +} + +/* + * If the sequence number of the last read position is smaller + * than the current minimum sequence number, the last read + * position is overwritten. And this time read data from + * minimum number, or read data from last position. + */ +static ssize_t trigger_parse_log(char __user *buf, size_t count, + u32 log_last_pos, struct log_buffer *log_buffer, + struct tlogger_reader *reader) +{ + bool user_buffer_left = false; + u32 buf_written; + struct reader_position position = {0}; + struct log_buffer_flag *buffer_flag = &(log_buffer->flag); + + if (buffer_flag->write_loops == reader->r_loops) { + set_reader_position(&position, log_buffer->buffer_start, + buffer_flag->max_len, reader->r_off, log_last_pos); + + buf_written = parse_log_item(buf, count, &position, + &reader->r_off, &user_buffer_left); + + return proc_read_ret(buf_written, reader); + } + + if (buffer_flag->write_loops > (reader->r_loops +1) || + ((buffer_flag->write_loops == (reader->r_loops + 1)) && + (reader->r_off < log_last_pos))) { + reader->r_off = log_last_pos; + reader->r_loops = buffer_flag->write_loops - 1; + } + + set_reader_position(&position, log_buffer->buffer_start, + buffer_flag->max_len, reader->r_off, buffer_flag->max_len); + + buf_written = parse_log_item(buf, count, &position, + &reader->r_off, &user_buffer_left); + + if (count > buf_written && user_buffer_left) { + set_reader_position(&position, log_buffer->buffer_start, + buffer_flag->max_len, 0, log_last_pos); + + buf_written += parse_log_item(buf + buf_written, + count - buf_written, &position, + &reader->r_off, &user_buffer_left); + + reader->r_loops = buffer_flag->write_loops; + } + + return proc_read_ret(buf_written, reader); +} + +static ssize_t process_tlogger_read(struct file *file, + char __user *buf, size_t count, loff_t *pos) +{ + struct tlogger_reader *reader = NULL; + struct log_buffer *log_buffer = NULL; + ssize_t ret; + u32 last_pos; + u32 is_read; + struct log_buffer_flag buffer_flag; + + (void)pos; + + ret = check_read_params(file, buf, count); + if (ret) + return ret; + + reader = file->private_data; + if (!reader) + return -EINVAL; + + ret = get_buffer_info(reader, &buffer_flag, &log_buffer); + if (ret) + return ret; + + ret = get_last_read_pos(&buffer_flag, reader, &last_pos, &is_read); + if (!is_read) + return ret; + + return trigger_parse_log(buf, count, last_pos, log_buffer, reader); +} + +void tz_log_write(void) +{ + struct log_buffer *log_buffer = NULL; + + if (!g_log) + return; + + log_buffer = (struct log_buffer*)g_log->buffer_info; + if (!log_buffer) + return; + + if (g_last_read_offset != log_buffer->flag.last_pos) { + tlogd("wake up write tz log\n"); + wake_up_interruptible(&g_log->wait_queue_head); + } + + return; +} + +static struct tlogger_log *get_tlogger_log_by_minor(int minor) +{ + struct tlogger_log *log = NULL; + + list_for_each_entry(log, &m_log_list, logs) { + if (log->misc_device.minor == minor) + return log; + } + + return NULL; +} + +static int process_tlogger_open(struct inode *inode, + struct file *file) +{ + struct tlogger_log *log = NULL; + int ret; + struct tlogger_reader *reader = NULL; + + tlogd("open logger open ++\n"); + /* not support seek */ + ret = nonseekable_open(inode, file); + if (ret) + return ret; + + tlogd("Before get log from minor\n"); + log = get_tlogger_log_by_minor(MINOR(inode->i_rdev)); + if (!log) + return -ENODEV; + + reader = kmalloc(sizeof(*reader), GFP_KERNEL); + if (ZERO_OR_NULL_PTR((unsigned long)(uintptr_t)reader)) + return -ENOMEM; + + reader->log = log; + reader->r_all = true; + reader->r_off = 0; + reader->r_loops = 0; + reader->r_sn = 0; + reader->r_failtimes = 0; + reader->r_is_tlogf = 0; + reader->r_from_cur = 0; + + INIT_LIST_HEAD(&reader->list); + + mutex_lock(&log->mutex_info); + list_add_tail(&reader->list, &log->readers); + g_tlogcat_count++; + mutex_unlock(&log->mutex_info); + + file->private_data = reader; + tlogd("tlogcat count %u\n", g_tlogcat_count); + return 0; +} + +static int process_tlogger_release(struct inode *ignored, + struct file *file) +{ + struct tlogger_reader *reader = NULL; + struct tlogger_log *log = NULL; + + (void)ignored; + + tlogd("logger_release ++\n"); + + if (!file) + return -1; + + reader = file->private_data; + if (!reader) { + tloge("reader is null\n"); + return -1; + } + + log = reader->log; + if (!log) { + tloge("log is null\n"); + return -1; + } + + mutex_lock(&log->mutex_info); + list_del(&reader->list); + if (g_tlogcat_count >= 1) + g_tlogcat_count--; + mutex_unlock(&log->mutex_info); + + tlogi("logger_release r_is_tlogf-%u\n", reader->r_is_tlogf); + if (reader->r_is_tlogf) + g_tlogcat_f = 0; + + kfree(reader); + tlogd("tlogcat count %u\n", g_tlogcat_count); + return 0; +} + +static unsigned int process_tlogger_poll(struct file *file, + poll_table *wait) +{ + struct tlogger_reader *reader = NULL; + struct tlogger_log *log = NULL; + struct log_buffer *buffer = NULL; + unsigned int ret = POLLOUT | POLLWRNORM; + + tlogd("logger_poll ++\n"); + if (!file) { + tloge("file is null\n"); + return ret; + } + + reader = file->private_data; + if (!reader) { + tloge("the private data is null\n"); + return ret; + } + + log = reader->log; + if (!log) { + tloge("log is null\n"); + return ret; + } + + buffer = (struct log_buffer*)log->buffer_info; + if (!buffer) { + tloge("buffer is null\n"); + return ret; + } + + poll_wait(file, &log->wait_queue_head, wait); + + if (buffer->flag.last_pos != reader->r_off) + ret |= POLLIN | POLLRDNORM; + + return ret; +} + +#define SET_READ_POS 1U +static void set_reader_cur_pos(const struct file *file) +{ + struct tlogger_reader *reader = NULL; + struct tlogger_log *log = NULL; + struct log_buffer *buffer = NULL; + + reader = file->private_data; + if (!reader) + return; + + log = reader->log; + if (!log) + return; + + buffer = (struct log_buffer*)log->buffer_info; + if (!buffer) + return; + + reader->r_from_cur = SET_READ_POS; + reader->r_off = buffer->flag.last_pos; + reader->r_loops = buffer->flag.write_loops; +} + +static void set_tlogcat_f_stat(const struct file *file) +{ + struct tlogger_reader *reader = NULL; + + if (!file) + return; + + reader = file->private_data; + if (!reader) + return; + + reader->r_is_tlogf = 1; + g_tlogcat_f = 1; + + tlogi("set tlogcat_f-%d\n", g_tlogcat_f); + return; +} + +static int get_tlogcat_f_stat(void) +{ + tlogi("get tlogcat_f-%d\n", g_tlogcat_f); + return g_tlogcat_f; +} + +static int check_user_arg(unsigned long arg, size_t arg_len) +{ +#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 19, 18) || \ + LINUX_VERSION_CODE == KERNEL_VERSION(4, 19, 71)) + return (int)access_ok(VERIFY_READ, + (void __user *)(uintptr_t)arg, arg_len); +#else + return (int)access_ok((void __user *)(uintptr_t)arg, arg_len); +#endif +} + +static int get_teeos_version(unsigned int cmd, unsigned long arg) +{ + int ret; + + if (!(_IOC_DIR(cmd) & _IOC_READ)) { + tloge("check get version cmd failed\n"); + return -1; + } + + ret = check_user_arg(arg, + sizeof(g_log_buffer->flag.version_info)); + if (!ret) { + tloge("check version info arg failed\n"); + return -1; + } + + if (copy_to_user((void __user *)(uintptr_t)arg, + (void *)g_log_buffer->flag.version_info, + sizeof(g_log_buffer->flag.version_info))) { + tloge("version info copy failed\n"); + return -1; + } + + return 0; +} + +static long process_tlogger_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct tlogger_log *log = NULL; + long ret = -EINVAL; + + if (!file) + return -1; + + log = get_reader_log(file); + if (!log) { + tloge("log is null\n"); + return -1; + } + + tlogd("logger_ioctl start ++\n"); + mutex_lock(&log->mutex_info); + + switch (cmd) { + case TEELOGGER_GET_VERSION: + if (!get_teeos_version(cmd, arg)) + ret = 0; + break; + case TEELOGGER_SET_READERPOS_CUR: + set_reader_cur_pos(file); + ret = 0; + break; + case TEELOGGER_SET_TLOGCAT_STAT: + set_tlogcat_f_stat(file); + ret = 0; + break; + case TEELOGGER_GET_TLOGCAT_STAT: + ret = get_tlogcat_f_stat(); + break; + default: + tloge("ioctl error default\n"); + break; + } + + mutex_unlock(&log->mutex_info); + return ret; +} + +#ifdef CONFIG_COMPAT +static long process_tlogger_compat_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + tlogd("logger_compat_ioctl ++\n"); + arg = (unsigned long)(uintptr_t)compat_ptr(arg); + return process_tlogger_ioctl(file, cmd, arg); +} +#endif + +static const struct file_operations g_logger_fops = { + .owner = THIS_MODULE, + .read = process_tlogger_read, + .poll = process_tlogger_poll, + .unlocked_ioctl = process_tlogger_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = process_tlogger_compat_ioctl, +#endif + .open = process_tlogger_open, + .release = process_tlogger_release, +}; + +static int __init register_device(const char *log_name, + uintptr_t addr, int size) +{ + int ret; + struct tlogger_log *log = NULL; + unsigned char *buffer = (unsigned char *)addr; + + log = kzalloc(sizeof(*log), GFP_KERNEL); + if (!log) { + tloge("kzalloc is failed\n"); + return -ENOMEM; + } + log->buffer_info = buffer; + log->misc_device.minor = MISC_DYNAMIC_MINOR; + log->misc_device.name = kstrdup(log_name, GFP_KERNEL); + if (!log->misc_device.name) { + ret = -ENOMEM; + tloge("kstrdup is failed\n"); + goto out_free_log; + } + log->misc_device.fops = &g_logger_fops; + log->misc_device.parent = NULL; + + init_waitqueue_head(&log->wait_queue_head); + INIT_LIST_HEAD(&log->readers); + mutex_init(&log->mutex_info); + INIT_LIST_HEAD(&log->logs); + list_add_tail(&log->logs, &m_log_list); + + /* register misc device for this log */ + ret = misc_register(&log->misc_device); + if (unlikely(ret)) { + tloge("failed to register misc device:%s\n", + log->misc_device.name); + goto out_free_log; + } + g_log = log; + return 0; + +out_free_log: + if (log->misc_device.name) + kfree(log->misc_device.name); + + kfree(log); + return ret; +} + +static struct log_item *msg_get_next(const u8 *buffer_start, + u32 read_pos, u32 scope_len, u32 max_len) +{ + u32 i = 0; + struct log_item *item = NULL; + u32 item_max_size; + u32 len; + + while (i <= scope_len && + ((read_pos + i + sizeof(*item)) < max_len)) { + len = (u32)(scope_len - i); + item_max_size = + ((len > LOG_ITEM_MAX_LEN) ? LOG_ITEM_MAX_LEN : len); + item = (struct log_item *)(buffer_start + read_pos + i); + + if (check_log_item_validite(item, item_max_size)) { + if ((read_pos + i + sizeof(*item) + + item->buffer_len) > max_len) { + tloge("check item len error\n"); + return NULL; + } + + return item; + } + + i += LOG_ITEM_LEN_ALIGN; + item = NULL; + } + + return NULL; +} + +#define OPEN_FILE_MODE 0640U +#define ROOT_UID 0 +#define ROOT_GID 0 + +static int tlogger_chown(const char *file_path, u32 file_path_len) +{ + uid_t user = ROOT_UID; + gid_t group = ROOT_GID; + int ret; + + (void)file_path_len; + get_log_chown(&user, &group); + + /* not need modify chown attr */ + if (group == ROOT_GID && user == ROOT_UID) + return 0; + + ret = (int)koadpt_sys_chown( + (const char __user *)file_path, user, group); + if (ret) { + tloge("sys chown for last teemsg file error\n"); + return -1; + } + + return 0; +} + +static int write_version_to_msg(struct file *filep, + loff_t *pos) +{ + ssize_t write_len; + + /* first write tee versino info */ + write_len = koadpt_vfs_write(filep, g_log_buffer->flag.version_info, + strlen(g_log_buffer->flag.version_info), pos); + if (write_len < 0) { + tloge("Failed to write to last teemsg version\n"); + return -1; + } + + tlogd("Succeed to Write to last teemsg version, len=%zd\n", write_len); + return 0; +} + +static int write_part_log_to_msg(struct file *filep, + const u8 *buffer, u32 buffer_max_len, loff_t *pos, + u32 read_off, u32 read_off_end) +{ + struct log_item *next_item = NULL; + u32 item_len; + u32 total_len = 0; + ssize_t write_len; + + next_item = msg_get_next(buffer, read_off, + LOG_ITEM_MAX_LEN, buffer_max_len); + + while (next_item && read_off <= read_off_end) { + item_len = next_item->buffer_len + sizeof(*next_item); + write_len = koadpt_vfs_write(filep, next_item->log_buffer, + next_item->real_len, pos); + if (write_len < 0) { + tloge("Failed to write last teemsg %zd\n", write_len); + return -1; + } + + tlogd("Succeed to Write last teemsg, len=%zd\n", write_len); + total_len += item_len; + read_off = (u8 *)next_item - buffer + item_len; + if (total_len >= buffer_max_len) + break; + + next_item = msg_get_next(buffer, read_off, + LOG_ITEM_MAX_LEN, buffer_max_len); + } + + return 0; +} + +static int write_log_to_msg(struct file *filep, + const u8 *buffer, u32 buffer_max_len, loff_t *pos, + u32 read_off, u32 read_off_end) +{ + if (read_off < read_off_end) { + return write_part_log_to_msg(filep, buffer, buffer_max_len, pos, + read_off, read_off_end); + } else { + if (write_part_log_to_msg(filep, buffer, buffer_max_len, pos, + read_off, buffer_max_len) != 0) + return -1; + return write_part_log_to_msg(filep, buffer, buffer_max_len, pos, + 0, read_off_end); + } +} + +static void update_dumpmsg_offset(u32 *read_start, u32 *read_end, + u32 read_off, u32 read_off_end, u32 *dump_start_flag, u32 *dump_end_flag) +{ + struct log_item *next_item = NULL; + u8 *buffer = g_log_buffer->buffer_start; + u32 buffer_max_len = g_log_mem_len - sizeof(*g_log_buffer); + ssize_t item_len; + ssize_t total_len = 0; + + next_item = msg_get_next(buffer, read_off, + LOG_ITEM_MAX_LEN, buffer_max_len); + + while (next_item && read_off <= read_off_end) { + item_len = next_item->buffer_len + sizeof(*next_item); + if (strstr(next_item->log_buffer, DUMP_START_MAGIC)) { + *read_start = read_off; + *dump_start_flag = 1; + } else if (strstr(next_item->log_buffer, DUMP_END_MAGIC)) { + *read_end = read_off; + *dump_end_flag = 1; + } + read_off = (u8 *)next_item - buffer + item_len; + total_len += item_len; + if (total_len >= buffer_max_len) + break; + + next_item = msg_get_next(buffer, read_off, + LOG_ITEM_MAX_LEN, buffer_max_len); + } +} + +#ifdef CONFIG_TEE_LOG_DUMP_PATH +static int get_dumpmsg_offset(u32 *read_start, u32 *read_end) +{ + u32 read_off = *read_start; + u32 read_off_end = *read_end; + u32 buffer_max_len = g_log_mem_len - sizeof(*g_log_buffer); + u32 dump_start_flag = 0; + u32 dump_end_flag = 0; + + if (read_off < read_off_end) { + update_dumpmsg_offset(read_start, read_end, read_off, read_off_end, + &dump_start_flag, &dump_end_flag); + } else { + update_dumpmsg_offset(read_start, read_end, read_off, buffer_max_len, + &dump_start_flag, &dump_end_flag); + update_dumpmsg_offset(read_start, read_end, 0, read_off_end, + &dump_start_flag, &dump_end_flag); + } + + if (dump_start_flag == 0 || dump_end_flag == 0) { + tloge("can't find dump start or end\n"); + return -1; + } else { + return 0; + } +} +#endif + +static int get_msg_buffer(u8 **buffer, u32 *buffer_max_len, + u32 *read_start, u32 *read_end, + const char *file_path, u32 file_path_len) +{ + errno_t rc; + int ret; + u8 *addr = NULL; + + (void)file_path_len; + if (!g_log_buffer) + return -1; + + *buffer_max_len = g_log_mem_len - sizeof(*g_log_buffer); + + if (*buffer_max_len > LOG_BUFFER_MAX_LEN) + return 0; + + *read_start = 0; + *read_end = *buffer_max_len; +#ifdef CONFIG_TEE_LOG_DUMP_PATH + if (strcmp(file_path, CONFIG_TEE_LOG_DUMP_PATH) == 0) { + *read_start = g_last_read_offset; + *read_end = ((struct log_buffer*)g_log->buffer_info)->flag.last_pos; + if (get_dumpmsg_offset(read_start, read_end) != 0) { + tloge("get dump offset failed\n"); + return -1; + } + } +#endif + addr = kmalloc(*buffer_max_len, GFP_KERNEL); + if (ZERO_OR_NULL_PTR((unsigned long)(uintptr_t)addr)) { + ret = -ENOMEM; + goto free_res; + } + + rc = memcpy_s(addr, *buffer_max_len, g_log_buffer->buffer_start, + *buffer_max_len); + if (rc) { + tloge("memcpy failed %d\n", rc); + ret = -EAGAIN; + goto free_res; + } + + *buffer = addr; + return 0; + +free_res: + if (addr) + kfree(addr); + + return ret; +} + +static int open_msg_file(struct file **file, + const char *file_path, u32 file_path_len) +{ + struct file *filep = NULL; + + (void)file_path_len; + filep = filp_open(file_path, O_CREAT | O_RDWR | O_TRUNC, OPEN_FILE_MODE); + if (!filep || IS_ERR(filep)) { + tloge("open last teemsg file err %ld\n", PTR_ERR(filep)); + return -1; + } + + *file = filep; + return 0; +} + +int tlogger_store_msg(const char *file_path, u32 file_path_len) +{ + struct file *filep = NULL; + mm_segment_t old_fs; + loff_t pos = 0; + int ret; + u32 buffer_max_len = 0; + u8 *buffer = NULL; + u32 read_start = 0; + u32 read_end = 0; + + if (!file_path || file_path_len > TEE_LOG_FILE_NAME_MAX) { + tloge("file path is invalid\n"); + return -1; + } + + if (!g_tlogcat_count) { + tlogd("tlogcat count %u\n", g_tlogcat_count); + return 0; + } + + /* copy logs from log memory, then parse the logs */ + ret = get_msg_buffer(&buffer, &buffer_max_len, + &read_start, &read_end, file_path, file_path_len); + if (ret) + return ret; + + /* exception handling, store trustedcore exception info to file */ + ret = open_msg_file(&filep, file_path, file_path_len); + if (ret) + goto free_res; + + old_fs = get_fs(); + set_fs(KERNEL_DS); + + ret = tlogger_chown(file_path, file_path_len); + if (ret) + goto clear_fs; + + ret = write_version_to_msg(filep, &pos); + if (ret) + goto clear_fs; + + ret = write_log_to_msg(filep, buffer, buffer_max_len, + &pos, read_start, read_end); + +clear_fs: + set_fs(old_fs); +free_res: + if (buffer) { + kfree(buffer); + buffer = NULL; + } + + if (filep != NULL) { + vfs_fsync(filep, 0); + filp_close(filep, 0); + } + + /* trigger write teeos log */ + tz_log_write(); + return ret; +} + +#ifdef DEF_ENG +#define KERNEL_IMG_IS_ENG 1 +#endif +int register_mem_to_teeos(uint64_t mem_addr, u32 mem_len, bool is_cache_mem) +{ + struct tc_ns_smc_cmd smc_cmd = { {0}, 0 }; + struct mb_cmd_pack *mb_pack = NULL; + int ret; + + mb_pack = mailbox_alloc_cmd_pack(); + if (!mb_pack) { + tloge("mailbox alloc failed\n"); + return -ENOMEM; + } + + smc_cmd.cmd_type = CMD_TYPE_GLOBAL; + smc_cmd.cmd_id = GLOBAL_CMD_ID_REGISTER_LOG_MEM; + mb_pack->operation.paramtypes = teec_param_types( + TEE_PARAM_TYPE_VALUE_INPUT, + TEE_PARAM_TYPE_VALUE_INPUT, + TEE_PARAM_TYPE_VALUE_INPUT, + TEE_PARAM_TYPE_NONE); + mb_pack->operation.params[0].value.a = mem_addr; + mb_pack->operation.params[0].value.b = mem_addr >> ADDR_TRANS_NUM; + mb_pack->operation.params[1].value.a = mem_len; +#ifdef DEF_ENG + mb_pack->operation.params[1].value.b = KERNEL_IMG_IS_ENG; +#endif + /* + * is_cache_mem: true, teeos map this memory for cache + * style; or else map to no cache style + */ + mb_pack->operation.params[2].value.a = is_cache_mem; + + 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); + mailbox_free(mb_pack); + if (ret) + tloge("Send log mem info failed\n"); + + return ret; +} + +static int register_mem_cfg(uint64_t *addr, u32 *len) +{ + int ret; + ret = register_log_mem(addr, len); + if (ret) + tloge("register log mem failed %x\n", ret); + + ret = register_log_exception(); + if (ret) + tloge("teeos register exception to log module failed\n"); + + return ret; +} + +static int check_log_mem(uint64_t mem_addr, u32 mem_len) +{ + if (mem_len < TEMP_LOG_MEM_SIZE) { + tloge("log mem init error, too small len:0x%x\n", mem_len); + return -1; + } + + if (!mem_addr) { + tloge("mem init failed!!! addr is 0\n"); + return -1; + } + return 0; +} + +static int register_tloger(void) +{ + int ret; + uint64_t mem_addr = 0; + + ret = register_mem_cfg(&mem_addr, &g_log_mem_len); + if (ret) + return ret; + + ret = check_log_mem(mem_addr, g_log_mem_len); + if (ret) + return ret; + + g_log_buffer = + (struct log_buffer *)map_log_mem(mem_addr, g_log_mem_len); + if (!g_log_buffer) + return -ENOMEM; + + g_log_buffer->flag.max_len = g_log_mem_len - sizeof(*g_log_buffer); + + tloge("tlogcat verison 1.0.0\n"); + ret = register_device(LOGGER_LOG_TEEOS, (uintptr_t)g_log_buffer, + sizeof(*g_log_buffer) + g_log_buffer->flag.max_len); + if (ret) { + unmap_log_mem((int *)g_log_buffer); + g_log_buffer = NULL; + g_log_mem_len = 0; + } + + return ret; +} + +static void unregister_mem_cfg(void) +{ + if (g_log_buffer) + unmap_log_mem((int *)g_log_buffer); + + unregister_log_exception(); +} + +static void unregister_tlogger(void) +{ + struct tlogger_log *current_log = NULL; + struct tlogger_log *next_log = NULL; + + list_for_each_entry_safe(current_log, next_log, &m_log_list, logs) { + /* we have to delete all the entry inside m_log_list */ + misc_deregister(¤t_log->misc_device); + kfree(current_log->misc_device.name); + list_del(¤t_log->logs); + kfree(current_log); + } + + unregister_mem_cfg(); + g_log_buffer = NULL; + g_log_mem_len = 0; +} + +#ifdef CONFIG_TZDRIVER_MODULE +int init_tlogger_service(void) +{ + return register_tloger(); +} + +void exit_tlogger_service(void) +{ + unregister_tlogger(); +} +#else +static int __init init_tlogger_service(void) +{ + return register_tloger(); +} + +static void __exit exit_tlogger_service(void) +{ + unregister_tlogger(); +} +#endif + +#ifdef CONFIG_TZDRIVER +device_initcall(init_tlogger_service); +module_exit(exit_tlogger_service); + +MODULE_AUTHOR("TEE"); +MODULE_DESCRIPTION("TrustCore Logger"); +MODULE_VERSION("1.00"); +#endif diff --git a/linux/tlogger/tlogger.h b/linux/tlogger/tlogger.h new file mode 100644 index 0000000..baa9dca --- /dev/null +++ b/linux/tlogger/tlogger.h @@ -0,0 +1,59 @@ +/* + * tlogger.h + * + * TEE Logging Subsystem, read the tee os log from rdr memory + * + * 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 TLOGGER_H +#define TLOGGER_H + +#include + +#ifdef CONFIG_TEELOG +void tz_log_write(void); +int tlogger_store_msg(const char *file_path, u32 file_path_len); +int register_mem_to_teeos(u64 mem_addr, u32 mem_len, bool is_cache_mem); + +#ifdef CONFIG_TZDRIVER_MODULE +int init_tlogger_service(void); +void exit_tlogger_service(void); +#endif + +#else +static inline void tz_log_write(void) +{ + return; +} + +static inline int tlogger_store_msg(const char *file_path, u32 file_path_len) +{ + (void)file_path; + (void)file_path_len; + return 0; +} +static inline int register_mem_to_teeos(u64 mem_addr, u32 mem_len, + bool is_cache_mem) +{ + (void)mem_addr; + (void)mem_len; + return 0; +} +static inline int init_tlogger_service(void) +{ + return 0; +} +static inline void exit_tlogger_service(void) +{ +} +#endif +#endif diff --git a/liteos/BUILD.gn b/liteos/BUILD.gn new file mode 100644 index 0000000..1d05db3 --- /dev/null +++ b/liteos/BUILD.gn @@ -0,0 +1,67 @@ +# 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. + +import("//kernel/liteos_a/liteos.gni") + +module_switch = defined(LOSCFG_DRIVERS_TZDRIVER) +module_name = "tzdriver" + +kernel_module(module_name) { + open_tlogger_switch = true + defines = [ + "CONFIG_TZDRIVER=y", + "CONFIG_LITEOS_TZDRIVER=y", + "CONFIG_CPU_AFF_NR=1", + "CONFIG_KERNEL_CLIENT=y", + "CONFIG_TEECD_PATH=\"/vendor/bin/teecd\"", + ] + if (open_tlogger_switch) { + defines += [ + "CONFIG_PAGES_MEM=y", + "CONFIG_TEELOG=y", + ] + sources = [ + "tlogger/log_pages_cfg.c", + "tlogger/tlogger.c", + ] + } else { + sources = [] + } + sources += [ + "los_adapt.c", + "core/agent.c", + "core/cmdmonitor.c", + "core/gp_ops.c", + "core/mailbox_mempool.c", + "core/mem.c", + "core/teek_client_api.c", + "core/session_manager.c", + "core/smc_smp.c", + "core/tc_client_driver.c", + "core/tzdebug.c", + "core/tz_spi_notify.c", + ] + include_dirs = [ + ".", + "auth", + "core", + "include", + "tlogger", + "//third_party/mbedtls/include", + "//third_party/musl/porting/liteos_a/kernel", + "//device/hisilicon/hispark_taurus/sdk_liteos/board/include", + ] +} + +config("public") { + cflags = [ "-Wno-unused-const-variable", "-Wno-unused-function" ] + include_dirs = [ "include" ] +} diff --git a/liteos/Kconfig b/liteos/Kconfig new file mode 100644 index 0000000..82fef4b --- /dev/null +++ b/liteos/Kconfig @@ -0,0 +1,8 @@ +config DRIVERS_TZDRIVER + bool "Secure Execution Communicator driver" + default n + depends on TEE_ENABLE + help + Provides a communication interface between userspace and + TrustZone Operating Environment. + diff --git a/liteos/Makefile b/liteos/Makefile new file mode 100644 index 0000000..da61622 --- /dev/null +++ b/liteos/Makefile @@ -0,0 +1,37 @@ +include $(LITEOSTOPDIR)/config.mk +MODULE_NAME := tzdriver + +LOCAL_FLAGS += -fstack-protector-strong -Wno-unused-const-variable -Wno-unused-function +LOCAL_FLAGS += -DCONFIG_TZDRIVER=y +LOCAL_FLAGS += -DCONFIG_LITEOS_TZDRIVER=y +LOCAL_FLAGS += -DCONFIG_CPU_AFF_NR=1 +LOCAL_FLAGS += -DCONFIG_KERNEL_CLIENT=y +LOCAL_FLAGS += -DCONFIG_PAGES_MEM=y +LOCAL_FLAGS += -DCONFIG_TEELOG=y +LOCAL_FLAGS += -DCONFIG_TEECD_PATH=\"/vendor/bin/teecd\" + +LOCAL_FLAGS += -I. +LOCAL_FLAGS += -Iauth +LOCAL_FLAGS += -Icore +LOCAL_FLAGS += -Iinclude +LOCAL_FLAGS += -Itlogger +LOCAL_FLAGS += -I$(LITEOSTOPDIR)/../../third_party/mbedtls/include +LOCAL_FLAGS += -I$(LITEOSTOPDIR)/../../third_party/musl/porting/liteos_a/kernel +LOCAL_FLAGS += -I$(LITEOSTOPDIR)/../../device/hisilicon/hispark_taurus/sdk_liteos/board/include + +LOCAL_SRCS += los_adapt.c +LOCAL_SRCS += core/agent.c +LOCAL_SRCS += core/cmdmonitor.c +LOCAL_SRCS += core/gp_ops.c +LOCAL_SRCS += core/mailbox_mempool.c +LOCAL_SRCS += core/mem.c +LOCAL_SRCS += core/teek_client_api.c +LOCAL_SRCS += core/session_manager.c +LOCAL_SRCS += core/smc_smp.c +LOCAL_SRCS += core/tc_client_driver.c +LOCAL_SRCS += core/tzdebug.c +LOCAL_SRCS += core/tz_spi_notify.c +LOCAL_SRCS += tlogger/tlogger.c +LOCAL_SRCS += tlogger/log_pages_cfg.c + +include $(MODULE) diff --git a/liteos/auth/auth_base_impl.c b/liteos/auth/auth_base_impl.c new file mode 100644 index 0000000..902ccbd --- /dev/null +++ b/liteos/auth/auth_base_impl.c @@ -0,0 +1,46 @@ +/* + * 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 "tc_ns_log.h" +#include "tc_ns_client.h" +#include "agent.h" /* for get_proc_dpath */ +#include "los_adapt.h" + +int calc_task_hash(unsigned char *digest, uint32_t dig_len, + LosTaskCB *cur_struct) +{ + tee_sha256_context ctx; + + if (!cur_struct || !digest || dig_len != SHA256_DIGEST_LENGTH) { + tloge("tee hash: input param is error\n"); + return -EFAULT; + } + + LosVmSpace *space = OS_PCB_FROM_PID(cur_struct->processID)->vmSpace; + if (space == NULL) + return -EFAULT; + + init_tee_sha256(&ctx); + /* search the region list */ + if (space->codeStart != 0 && space->codeEnd > space->codeStart) + update_tee_sha256(&ctx, (void *)space->codeStart, space->codeEnd - space->codeStart); + else + return -EFAULT; + finish_tee_sha256(&ctx, digest); + return 0; +} diff --git a/liteos/auth/auth_base_impl.h b/liteos/auth/auth_base_impl.h new file mode 100644 index 0000000..9338aa8 --- /dev/null +++ b/liteos/auth/auth_base_impl.h @@ -0,0 +1,39 @@ +/* + * 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 +#include +#include "los_adapt.h" + +#if ((defined CONFIG_CLIENT_AUTH) || (defined CONFIG_TEECD_AUTH)) + +#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 + +int calc_task_hash(unsigned char *digest, uint32_t dig_len, LosTaskCB *cur_struct); + +#endif /* CLIENT_AUTH || TEECD_AUTH */ + +#endif diff --git a/liteos/auth/client_hash_auth.c b/liteos/auth/client_hash_auth.c new file mode 100644 index 0000000..65c648f --- /dev/null +++ b/liteos/auth/client_hash_auth.c @@ -0,0 +1,78 @@ +/* + * 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 "tc_ns_log.h" +#include "auth_base_impl.h" +#include "los_adapt.h" + +static int proc_calc_hash(uint8_t kernel_api, struct tc_ns_session *session, + LosTaskCB *cur_struct) +{ + int rc, i; + int so_found = 0; + + 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) { + 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) { + tloge("tee calc ca hash failed\n"); + return -EFAULT; + } + 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; + LosTaskCB *cur_struct = NULL; + bool check = false; + + check = (!dev_file || !context || !session); + if (check) { + tloge("bad params\n"); + return -EFAULT; + } + + cur_struct = OsCurrTaskGet(); + + ret = proc_calc_hash(dev_file->kernel_api, session, cur_struct); + return ret; +} diff --git a/liteos/auth/client_hash_auth.h b/liteos/auth/client_hash_auth.h new file mode 100644 index 0000000..48f148b --- /dev/null +++ b/liteos/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/liteos/core/agent.c b/liteos/core/agent.c new file mode 100755 index 0000000..c7a5692 --- /dev/null +++ b/liteos/core/agent.c @@ -0,0 +1,1189 @@ +/* + * 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 "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 "los_adapt.h" +#include "cmdmonitor.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 get_ca_path_and_uid(LosTaskCB *ca_task, struct ca_info *ca) +{ + char *path = NULL; + int message_size; + char *tpath = NULL; + int task_uid; + + 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_process_path(ca_task, 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; + } + + task_uid = get_task_uid(ca_task); + if (task_uid < 0) { + kfree(tpath); + return -EPERM; + } + ca->uid = (uint32_t)task_uid; + tlogd("ca_task->comm is %s, path is %s, ca uid is %u\n", OsCurrTaskGet()->taskName, path, ca->uid); + + kfree(tpath); + return 0; +} + +int check_ext_agent_access(LosTaskCB *ca_task, uint32_t agent_id) +{ + int ret; + struct ca_info agent_ca = { {0}, 0, 0 }; + + ret = get_ca_path_and_uid(ca_task, &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((char *)buf_to_tee); + mb_pack->operation.params[0].value.b = 0; + 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 = 0; + 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() != TEECD_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_t, no truncate risk */ + struct mb_cmd_pack *mb_pack = NULL; + + if (tc_ns_get_uid() != TEECD_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 = 0; + + 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 (LOS_UnMMap((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; + DSB; + + return 0; +} + +static int wait_agent_response(struct smc_event_data *event_data) +{ + int ret = 0; + bool answered = true; + int rc; + + do { + answered = true; + rc = wait_event_interruptible_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(OsCurrTaskGet())) { + 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() != TEECD_UID) && check_ext_agent_access(OsCurrTaskGet(), 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() != TEECD_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() != TEECD_UID) && + check_ext_agent_access(OsCurrTaskGet(), 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 = OsCurrTaskGet()->processID; + 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, + uintptr_t *agent_buff, uint32_t agent_buff_size) +{ + *agent_buff = (uintptr_t)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((void *)*agent_buff); + *agent_buff = (uintptr_t)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 = (void *)*agent_buff; + (*event_data)->agent_buff_size = agent_buff_size; + (*event_data)->owner = dev_file; + (*event_data)->pid = OsCurrTaskGet()->processID; + 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) +{ + vaddr_t user_addr; + int ret; + + if (!IS_PAGE_ALIGNED(buffer) || !IS_PAGE_ALIGNED(size)) + return -EFAULT; + + user_addr = LOS_MMap(0, size, PROT_READ | PROT_WRITE, + MAP_SHARED | MAP_ANONYMOUS, 0, 0); + if (IS_ERR_OR_NULL(user_addr)) { + tloge("vm mmap failed\n"); + return user_addr; + } + + for (int i = 0; i < (size >> PAGE_SHIFT); i++) { + LosVmPage *page = LOS_VmPageGet(buffer + PAGE_SIZE * i); + if (page == NULL) { + goto err_out; + } + LOS_AtomicInc(&page->refCounts); + } + + ret = remap_pfn_range(user_addr, buffer >> PAGE_SHIFT, size, + VM_MAP_REGION_FLAG_PERM_USER | VM_MAP_REGION_FLAG_PERM_READ | VM_MAP_REGION_FLAG_PERM_WRITE); + if (ret) { + tloge("remap agent buffer failed, err=%d", ret); + goto err_out; + } + + return user_addr; +err_out: + if (LOS_UnMMap(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() != TEECD_UID) && + check_ext_agent_access(OsCurrTaskGet(), 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 == OsCurrTaskGet()->processID) { + 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 uintptr_t 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((void *)agent_buff); + mb_pack->operation.params[0].value.b = 0; + 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 = 0; + 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_OR_NULL(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; + uintptr_t agent_buff = (uintptr_t)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((void *)agent_buff); + return ret; +} + +static int check_for_unregister_agent(unsigned int agent_id) +{ + bool check_value = false; + + if ((tc_ns_get_uid() != TEECD_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 = 0; + 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 = 0; + 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(uintptr_t instance, int len) +{ + int ret = 0; + struct tee_agent_kernel_ops *agent_instance = NULL; + + agent_instance = (void *)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; + char agent_name[OS_TCB_NAME_LEN] = {0}; + + /* 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 */ + ret = sprintf_s(agent_name, OS_TCB_NAME_LEN, "agent_%s", agent_instance->agent_name); + if (ret == -1) + goto out; + agent_instance->agent_thread = + kthread_run(def_tee_agent_work, agent_instance, + sizeof(struct tee_agent_kernel_ops), (char *)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; + } + 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 = LINUX_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; +} + +int agent_exit(void) +{ + tee_agent_kernel_exit(); + return 0; +} diff --git a/liteos/core/agent.h b/liteos/core/agent.h new file mode 100644 index 0000000..b744e68 --- /dev/null +++ b/liteos/core/agent.h @@ -0,0 +1,129 @@ +/* + * 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 "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); + LosTaskCB *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); +int 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); +void clean_agent_pid_info(struct tc_ns_dev_file *dev_file); + +#endif diff --git a/liteos/core/cmdmonitor.c b/liteos/core/cmdmonitor.c new file mode 100644 index 0000000..d6af819 --- /dev/null +++ b/liteos/core/cmdmonitor.c @@ -0,0 +1,380 @@ +/* + * 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 "tc_ns_log.h" +#include "smc_smp.h" +#include "mailbox_mempool.h" +#include "tlogger.h" +#include "log_cfg_api.h" +#include "los_adapt.h" + +static int g_cmd_need_archivelog; +static LINUX_LIST_HEAD(g_cmd_monitor_list); +static int g_cmd_monitor_list_size; +/* report 2 hours */ + +#define MAX_CMD_MONITOR_LIST 200 +#define MAX_AGENT_CALL_COUNT 250 +static mutex_t g_cmd_monitor_lock = PTHREAD_MUTEX_INITIALIZER; + +/* 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, +}; + +#ifndef CONFIG_TEE_LOG_ACHIVE_PATH +#define CONFIG_TEE_LOG_ACHIVE_PATH "/data/log/tee/last_teemsg" +#endif + +static void get_time_spec(struct time_spec *time) +{ + time->ts = current_kernel_time(); +} + +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, msecs_to_jiffies(S_TO_MS)); +} + +void tzdebug_archivelog(void) +{ + schedule_cmd_monitor_work(&g_cmd_monitor_work_archive, + msecs_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(); +} + +bool is_thread_reported(unsigned int tid) +{ + bool ret = false; + struct cmd_monitor *monitor = NULL; + + if (mutex_lock(&g_cmd_monitor_lock) != 0) { + tloge("cmd monitor lock fail\n"); + return ret; + } + + 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 = OsCurrTaskGet()->processID; + pid_t tid = OsCurrTaskGet()->taskID; + + if (mutex_lock(&g_cmd_monitor_lock) != 0) { + tloge("cmdmonitor lock fail\n"); + return; + } + + 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); +} + +static void show_timeout_cmd_info(struct cmd_monitor *monitor) +{ + long long timedif; + 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 25s, we log the teeos log, and report */ + if ((timedif > CMD_MAX_EXECUTE_TIME * S_TO_MS) && (!monitor->is_reported)) { + monitor->is_reported = true; + tloge("[cmd_monitor_tick] pid=%d,pname=%s,tid=%d, " + "tname=%s, lastcmdid=%u, agent call count:%d, " + "timedif=%lld ms and report\n", + monitor->pid, monitor->pname, monitor->tid, + monitor->tname, monitor->lastcmdid, + monitor->agent_call_count, timedif); + tloge("monitor: pid-%d", monitor->pid); + show_cmd_bitmap(); + g_cmd_need_archivelog = 1; + wakeup_tc_siq(); + return; + } + + if (timedif > 1 * S_TO_MS) + tloge("[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; + + if (mutex_lock(&g_cmd_monitor_lock) != 0) { + tloge("cmd_monitor lock fail\n"); + return; + } + + list_for_each_entry_safe(monitor, tmp, &g_cmd_monitor_list, list) { + if (monitor->returned) { + g_cmd_monitor_list_size--; + tloge("[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, msecs_to_jiffies(S_TO_MS)); + 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_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->count = 1; + newitem->agent_call_count = 0; + newitem->returned = false; + newitem->is_reported = false; + newitem->pid = OsCurrTaskGet()->processID; + newitem->tid = OsCurrTaskGet()->taskID; + + LosProcessCB *run_process = OS_PCB_FROM_PID(newitem->pid); + if (strncpy_s(newitem->pname, TASK_COMM_LEN, run_process->processName, OS_PCB_NAME_LEN) != EOK) + newitem->pname[0] = '\0'; + if (strncpy_s(newitem->tname, TASK_COMM_LEN, OsCurrTaskGet()->taskName, OS_TCB_NAME_LEN) != EOK) + 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 = OsCurrTaskGet()->processID; + tid = OsCurrTaskGet()->taskID; + if (mutex_lock(&g_cmd_monitor_lock) != 0) { + tloge("cmd monitor lock failed\n"); + return NULL; + } + + 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->count++; + monitor->returned = false; + monitor->is_reported = false; + monitor->lastcmdid = cmd->cmd_id; + monitor->agent_call_count = 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, + msecs_to_jiffies(S_TO_MS)); + } + } 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 + */ + tloge("time : item s %lld ns %lld nowtime s %lld ns %lld total %lld\n", + (long long)item->sendtime.ts.tv_sec, (long long)item->sendtime.ts.tv_nsec, + (long long)nowtime.ts.tv_sec, (long long)nowtime.ts.tv_nsec, (long long)item->timetotal); + 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, + msecs_to_jiffies(S_TO_MS)); + } +} + +void init_cmd_monitor(void) +{ + g_cmd_monitor_wq = alloc_ordered_workqueue("tz_cmd_monitor_wq", 0); + if (!g_cmd_monitor_wq) + tloge("alloc cmd monitor wq failed\n"); + 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/liteos/core/cmdmonitor.h b/liteos/core/cmdmonitor.h new file mode 100644 index 0000000..631d773 --- /dev/null +++ b/liteos/core/cmdmonitor.h @@ -0,0 +1,63 @@ +/* + * 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" + +#define TASK_COMM_LEN OS_TCB_NAME_LEN + +/* + * when cmd execute more than 25s in tee, + * it will be terminated when CA is killed + */ +#define CMD_MAX_EXECUTE_TIME 25U +#define S_TO_MS 1000 +#define S_TO_US 1000000 + +struct time_spec { + struct timespec ts; +}; + +struct cmd_monitor { + struct list_head list; + struct time_spec sendtime; + 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/liteos/core/gp_ops.c b/liteos/core/gp_ops.c new file mode 100644 index 0000000..7263085 --- /dev/null +++ b/liteos/core/gp_ops.c @@ -0,0 +1,1056 @@ +/* + * 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 "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 "tc_client_driver.h" +#include "tlogger.h" +#include "los_adapt.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 (!access_ok(VERIFY_READ, + (unsigned long)(uintptr_t)client_param->memref.size_addr, + sizeof(uint32_t))) + + return false; + + get_user(&size, (uint32_t *)(uintptr_t)client_param->memref.size_addr); + + if (!access_ok(VERIFY_READ, + (unsigned long)(uintptr_t)client_param->memref.buffer, size)) + return false; + + return true; +} + +static bool is_usr_valmem_valid(union tc_ns_client_param *client_param) +{ + if (!access_ok(VERIFY_READ, + (unsigned long)(uintptr_t)client_param->value.a_addr, + sizeof(uint32_t))) + return false; + + if (!access_ok(VERIFY_READ, + (unsigned long)(uintptr_t)client_param->value.b_addr, + sizeof(uint32_t))) + 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; +} + +static bool is_kernel_addr_valid(const void *addr) +{ + if ((!LOS_IsUserAddress((uintptr_t)addr) && LOS_PaddrQuery((void *)addr))) + return true; + + return false; +} + +/* + * 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) { + if (!is_kernel_addr_valid(src)) { + tloge("invalid addr\n"); + return -EFAULT; + } + 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) { + if (!is_kernel_addr_valid(dest)) { + tloge("invalid addr\n"); + return -EFAULT; + } + 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((void *)temp_buf); + op_params->mb_pack->operation.buffer_h_addr[index] = 0; + 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; +} + +/* + * 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); + buffer_addr = mailbox_copy_alloc(buffer_addr, buffer_size); + if (!buffer_addr) { + ret = -ENOMEM; + break; + } + 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] = 0; + 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); + return ret; +} + +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 + 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; + } + + /* 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; +} + +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(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)) { + 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(temp_buf) && + !ZERO_OR_NULL_PTR( + (unsigned long)(uintptr_t)temp_buf)) { + mailbox_free(temp_buf); + temp_buf = NULL; + } + } + } +} + +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 = OsCurrTaskGet()->taskID; + smc_cmd->pid = OsCurrTaskGet()->processID; + + 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 = 0; + } 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 && + !is_kernel_thread(OsCurrTaskGet()) && (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 = 0; + 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) +{ + return get_task_uid(OsCurrTaskGet()); +} + +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); + op_params->smc_cmd = NULL; + 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\n", tee_ret, + op_params->smc_cmd->ret_val); + /* 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) { + if (update_client_operation(call_params, + op_params, false)) + op_params->smc_cmd->err_origin = + TEEC_ORIGIN_COMMS; + } + } else { + tz_log_write(); + ret = update_client_operation(call_params, op_params, true); + if (ret) + op_params->smc_cmd->err_origin = TEEC_ORIGIN_COMMS; + } + + 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->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: + release_tc_call_resource(call_params, &op_params, tee_ret); + return ret; +} diff --git a/liteos/core/gp_ops.h b/liteos/core/gp_ops.h new file mode 100644 index 0000000..89a516e --- /dev/null +++ b/liteos/core/gp_ops.h @@ -0,0 +1,49 @@ +/* + * 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; +}; + +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/liteos/core/mailbox_mempool.c b/liteos/core/mailbox_mempool.c new file mode 100644 index 0000000..b69b77e --- /dev/null +++ b/liteos/core/mailbox_mempool.c @@ -0,0 +1,614 @@ +/* + * 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 "teek_client_constants.h" +#include "tc_ns_log.h" +#include "smc_smp.h" +#include "los_adapt.h" + +#define MAILBOX_PAGE_MAX (MAILBOX_POOL_SIZE >> PAGE_SHIFT) +#define MAILBOX_ORDER_MAX GET_ORDER(MAILBOX_POOL_SIZE) +static int g_max_oder = MAILBOX_ORDER_MAX; + +#define OPT_MODE 0660U +#define STATE_MODE 0440U + +struct mb_page_t { + struct list_head node; + LosVmPage *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 { + LosVmPage *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 mutex_t 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 = (void *)OsVmPageToVaddr(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(LosVmPage *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; + LosVmPage *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 = OsVmVaddrToPage((void *)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 LINUX_LIST_HEAD(mb_dbg_list); +static mutex_t mb_dbg_lock = PTHREAD_MUTEX_INITIALIZER; +static unsigned int g_mb_dbg_entry_count = 1; +static unsigned int g_mb_dbg_last_res; /* only cache 1 opt result */ + +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) +{ + if (!filp || !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) +{ + char buf[MB_WRITE_SIZE] = {0}; + char *cmd = NULL; + char *value = NULL; + char *end_ptr = NULL; + unsigned int alloc_size; + unsigned int free_idx; + + if (!is_opt_write_param_valid(filp, ubuf, cnt)) + 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"))) { + alloc_size = strtoul(value, &end_ptr, 0); + if (end_ptr == NULL || *end_ptr != 0) + tloge("invalid value format for mb dbg\n"); + else + alloc_dbg_entry(alloc_size); + } else if (!strncmp(cmd, "free", strlen("free"))) { + free_idx = strtoul(value, &end_ptr, 0); + if (end_ptr == NULL || *end_ptr != 0) + tloge("invalid value format for mb dbg\n"); + else + mb_dbg_remove_entry(free_idx); + } 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) +{ + 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, buf, ret); +} + +static const struct file_operations_vfs g_mb_dbg_opt_fops = { + .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) +{ + (void)(filp); + (void)(ubuf); + mailbox_show_status(); + mailbox_show_details(); + return 0; +} + +static const struct file_operations_vfs g_mb_dbg_state_fops = { + .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((void *)mb_pool); + operation->params[0].value.b = 0; + 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 = 0; + + 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; +} + +#define TC_NS_CLIENT_MEILBOX_OPT_NAME "/dev/tz_mailbox_opt" +#define TC_NS_CLIENT_MEILBOX_STATE_NAME "/dev/tz_mailbox_state" + +int mailbox_mempool_init(void) +{ + int i; + struct mb_page_t *mb_page = NULL; + struct mb_free_area_t *area = NULL; + LosVmPage *all_pages = NULL; + size_t zone_len; + + 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 = mailbox_pool_alloc_pages(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((void *)page_address(all_pages), MAILBOX_POOL_SIZE)) { + tloge("register mailbox failed\n"); + mailbox_pool_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); + + int ret = create_tc_client_device(TC_NS_CLIENT_MEILBOX_OPT_NAME, &g_mb_dbg_opt_fops); + if (ret != EOK) + return ret; + ret = create_tc_client_device(TC_NS_CLIENT_MEILBOX_STATE_NAME, &g_mb_dbg_state_fops); + if (ret != EOK) + return ret; + + return 0; +} + +void mailbox_mempool_destroy(void) +{ + mailbox_pool_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/liteos/core/mailbox_mempool.h b/liteos/core/mailbox_mempool.h new file mode 100644 index 0000000..bc89975 --- /dev/null +++ b/liteos/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/liteos/core/mem.c b/liteos/core/mem.c new file mode 100644 index 0000000..fab7b78 --- /dev/null +++ b/liteos/core/mem.c @@ -0,0 +1,74 @@ +/* + * 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 "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 "los_adapt.h" + +void tc_mem_free(struct tc_ns_shared_mem *shared_mem) +{ + if (!shared_mem) + return; + + if (shared_mem->kernel_addr) { + LOS_VFree(shared_mem->kernel_addr); + shared_mem->kernel_addr = NULL; + } + kfree(shared_mem); +} + +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); + } + + len = ALIGN(len, SZ_4K); + if (len > MAILBOX_POOL_SIZE) { + tloge("alloc sharemem size %zu is too large\n", len); + kfree(shared_mem); + return ERR_PTR(-EINVAL); + } + addr = LOS_VMalloc(len); + if (!addr) { + tloge("alloc mailbox failed\n"); + kfree(shared_mem); + return ERR_PTR(-ENOMEM); + } + if (memset_s(addr, len, 0, len)) { + tloge("memset mailbox failed\n"); + kfree(shared_mem); + LOS_VFree(addr); + return ERR_PTR(-ENOMEM); + } + shared_mem->kernel_addr = addr; + shared_mem->len = len; + shared_mem->user_addr = NULL; + shared_mem->user_addr_ca = NULL; + atomic_set(&shared_mem->usage, 0); + return shared_mem; +} \ No newline at end of file diff --git a/liteos/core/mem.h b/liteos/core/mem.h new file mode 100644 index 0000000..1152b7d --- /dev/null +++ b/liteos/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/liteos/core/session_manager.c b/liteos/core/session_manager.c new file mode 100644 index 0000000..e4dc4ca --- /dev/null +++ b/liteos/core/session_manager.c @@ -0,0 +1,1178 @@ +/* + * 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 "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 "auth_base_impl.h" +#include "mailbox_mempool.h" +#include "tc_client_driver.h" +#include "los_adapt.h" + +static mutex_t g_load_app_lock = PTHREAD_MUTEX_INITIALIZER; +#define MAX_REF_COUNT 255 + +/* record all service node and need mutex to avoid race */ +struct list_head g_service_list; +static mutex_t g_service_list_lock = PTHREAD_MUTEX_INITIALIZER; + +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] = 0; + 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 = 0; + + 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 = + (uint64_t)(uintptr_t)&dev_file->pkg_name_len; + context->params[3].memref.buffer = + (uint64_t)(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 = + (uint64_t)(uintptr_t)&dev_file->pub_key_len; + context->params[2].memref.buffer = + (uint64_t)(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 = + (uint64_t)(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 = + (uint64_t)(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] = 0; + 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] = 0; + 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 = 0; +} + +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 (!is_kernel_thread(OsCurrTaskGet())) { + 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, unsigned int *ret_origin, 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++) { + 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 (ret_origin != NULL) + *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, unsigned int *ret_origin, 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, ret_origin, 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; + unsigned int 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, &ret_origin, LOAD_TA); + if (ret) { + tloge("load image failed, ret=%x", ret); + if (ret_origin != TEEC_ORIGIN_COMMS) { + context->returns.origin = 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) { + /* Clean this session secure information */ + 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(uintptr_t arg, int len) +{ + struct tc_ns_dev_file *dev = (void *)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"); + return 0; +} + +void close_unclosed_session_in_kthread(struct tc_ns_dev_file *dev) +{ + LosTaskCB *close_thread = NULL; + + if (!dev) { + tloge("dev is invalid\n"); + return; + } + + if (!if_exist_unclosed_session(dev)) + return; + + char thread_name[OS_TCB_NAME_LEN] = { 0 }; + if (sprintf_s(thread_name, OS_TCB_NAME_LEN, "close_fn_%6d", dev->dev_file_id) < 0) { + tloge("sprintf fail\n"); + return; + } + close_thread = kthread_run(close_session_thread_fn, + dev, sizeof(uint64_t), thread_name); + if (unlikely(IS_ERR_OR_NULL(close_thread))) { + tloge("fail to create close session thread\n"); + return; + } + + wake_up_process(close_thread); + 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->f_priv; + if (copy_from_user(&context, argp, sizeof(context))) { + tloge("copy from user failed\n"); + return -EFAULT; + } + + context.returns.origin = TEEC_ORIGIN_COMMS; + 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: + tloge("invalid cmd:0x%x!\n", cmd); + return ret; + } + /* + * 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/liteos/core/session_manager.h b/liteos/core/session_manager.h new file mode 100644 index 0000000..40b14eb --- /dev/null +++ b/liteos/core/session_manager.h @@ -0,0 +1,52 @@ +/* + * 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 "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, unsigned int *ret_origin, 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/liteos/core/smc_smp.c b/liteos/core/smc_smp.c new file mode 100644 index 0000000..c772ffa --- /dev/null +++ b/liteos/core/smc_smp.c @@ -0,0 +1,1557 @@ +/* + * 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 "agent.h" +#include "tc_ns_client.h" +#include "tc_ns_log.h" +#include "teek_client_constants.h" +#include "teek_ns_client.h" +#include "los_adapt.h" +#include "cmdmonitor.h" +#include "tlogger.h" + +#define SECS_SUSPEND_STATUS 0xA5A5 +#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 MAX_EMPTY_RUNS 100 +#define TZ_CPU_ZERO 0 +#define TZ_CPU_ONE 1 +#define TZ_CPU_FOUR 4 +#define TZ_CPU_FIVE 5 +#define TZ_CPU_SIX 6 +#define TZ_CPU_SEVEN 7 +#define LOW_BYTE 0xF + +#define PENDING2_RETRY (-1) + +#define MAX_CHAR 0xff + +/* Current state of the system */ +static uint8_t g_sys_crash; + +enum SPI_CLK_MODE { + SPI_CLK_OFF = 0, + SPI_CLK_ON, +}; + +typedef struct { + int *n_idled; + uint64_t *ret; + uint64_t *exit_reason; + uint64_t *ta; + uint64_t *target; +} wo_pm_params; + + +struct shadow_work { + struct work_struct work; + uint64_t target; +}; + +unsigned long g_shadow_thread_id = 0; +static LosTaskCB *g_siq_thread = NULL; +static LosTaskCB *g_smc_svc_thread = NULL; +struct workqueue_struct *g_ipi_helper_worker = NULL; + +enum cmd_reuse { + CLEAR, /* clear this cmd index */ + RESEND, /* use this cmd index resend */ +}; + +#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; +paddr_t g_cmd_phys; + +static struct list_head g_pending_head; +static spinlock_t g_pend_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 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; +}; + +struct tc_ns_smc_queue *get_cmd_data_buffer(void) +{ + return g_cmd_data; +} + +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; + DSB; + 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; + + 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. + */ + 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) { + tloge("can't get any free smc entry\n"); + return -1; + } + + acquire_smc_buf_lock(&g_cmd_data->smc_lock); + ISB; + DSB; + 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; + DSB; + 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; + DSB; + 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); +} + +void show_cmd_bitmap_with_lock(void) +{ + if (g_cmd_data == NULL) + return; + acquire_smc_buf_lock(&g_cmd_data->smc_lock); + show_cmd_bitmap(); + release_smc_buf_lock(&g_cmd_data->smc_lock); +} + +static void show_single_cmd_info(const 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 = calloc(1, sizeof(int) * MAX_SMC_CMD); + if (ZERO_OR_NULL_PTR((unsigned long)(uintptr_t)cmd_in)) { + tloge("out of mem! cannot show in bitmap\n"); + return; + } + + cmd_out = calloc(1, sizeof(int) * MAX_SMC_CMD); + if (ZERO_OR_NULL_PTR((unsigned long)(uintptr_t)cmd_out)) { + free(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: + free(cmd_in); + free(cmd_out); +} + +static struct pending_entry *init_pending_entry(pid_t pid) +{ + struct pending_entry *pe = NULL; + + pe = calloc(1, sizeof(*pe)); + if (ZERO_OR_NULL_PTR((unsigned long)(uintptr_t)pe)) { + tloge("alloc pe failed\n"); + return NULL; + } + + atomic_set(&pe->users, 1); + pe->pid = pid; + 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->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; + + free(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 DECLARE_WAIT_QUEUE_HEAD(siq_th_wait); +static DECLARE_WAIT_QUEUE_HEAD(ipi_th_wait); +static atomic_t g_siq_th_run; + +#define SHADOW_EXIT_RUN 0x1234dead + +/* + * 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(¤t->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; +}; + +static inline void secret_fill(struct smc_cmd_ret *ret, uint64_t exit, uint64_t ta, uint64_t target) +{ + if (ret != NULL) { + ret->exit = exit; + ret->ta = ta; + ret->target = target; + } +} + +bool sigkill_pending(LosTaskCB *tsk) +{ + if (!tsk) { + tloge("tsk is null!\n"); + return 0; + } + + return OsSigIsMember(&tsk->sig.sigwaitmask, SIGKILL) || + OsSigIsMember(&tsk->sig.sigwaitmask, SIGUSR1); +} + +enum cmd_state { + START, + KILLING, + KILLED, +}; + +#define CPU0_ONLY_MASK 0x0001 + +#if CONFIG_CPU_AFF_NR +static void set_cpu_strategy(UINT16 *old_mask) +{ + LosTaskCB *curr = OsCurrTaskGet(); + const UINT16 new_mask = CPU0_ONLY_MASK; + + *old_mask = curr->cpuAffiMask; + kthread_bind_mask(curr, new_mask); +} +#endif + +#if CONFIG_CPU_AFF_NR +static void restore_cpu(UINT16 *old_mask) +{ + LosTaskCB *curr = OsCurrTaskGet(); + kthread_bind_mask(curr, *old_mask); +} +#endif + +struct smc_param { + uint32_t r0; + uint32_t r1; + uint32_t r2; + uint32_t r3; + uint32_t r4; + struct smc_cmd_ret *secret; + uint32_t cmd; + uint64_t ca; + uint32_t ta; + uint32_t exit_reason; + uint32_t target; + enum cmd_state state; + uint64_t ops; +}; + +static int do_smp_smc_send(struct smc_param *param) +{ + int ret; + if (param->secret != NULL && param->secret->exit == SMC_EXIT_PREEMPTED) { + param->r0 = param->cmd; + if (param->state == KILLING) { + param->state = KILLED; + param->r1 = SMC_OPS_ABORT_TASK; + param->r2 = param->ca; + } else { + param->r1 = SMC_OPS_SCHEDTO; + param->r2 = param->ca; + param->r3 = param->secret->ta; + param->r4 = param->secret->target; + } + } + int check_value = param->ops == SMC_OPS_SCHEDTO || param->ops == SMC_OPS_START_FIQSHD; + if (param->secret != NULL && check_value) + param->r4 = param->secret->target; + ISB; + DSB; + + 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" (param->r0), [a1] "+r" (param->r1), [a2] "+r" (param->r2), + [a3] "+r" (param->r3) + : [re0] "r" (&ret), [re1] "r" (¶m->exit_reason), + [re2] "r" (¶m->ta), [re3] "r" (¶m->target) + : "r0", "r1", "r2", "r3"); + } while (0); + ISB; + DSB; + return ret; +} + +static noinline int smp_smc_send(uint32_t cmd, unsigned long ops, unsigned long ca, + struct smc_cmd_ret *secret, bool needKill) +{ + uint32_t ret = 0; + bool check_value = false; +#if CONFIG_CPU_AFF_NR + UINT16 old_mask; +#endif + struct smc_param param; + param.r0 = cmd; + param.r1 = ops; + param.r2 = ca; + param.r3 = 0; + param.r4 = 0; + param.exit_reason = 0; + param.ta = 0; + param.target = 0; + param.state = START; + param.cmd = cmd; + param.ca = ca; + param.secret = secret; + param.ops = ops; + +RETRY: +#if CONFIG_CPU_AFF_NR + set_cpu_strategy(&old_mask); +#endif + + ret = do_smp_smc_send(¶m); + + if (secret == NULL) + return ret; + secret_fill(secret, param.exit_reason, param.ta, param.target); + if (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. + */ + check_value = needKill && sigkill_pending(OsCurrTaskGet()) && param.state == START && + is_thread_reported(OsCurrTaskGet()->taskID); + if (check_value == true) { + param.state = KILLING; + tloge("receive kill signal\n"); + } +#ifndef CONFIG_PREEMPT + /* yield cpu to avoid soft lockup */ + cond_resched(); +#endif + goto RETRY; + } +#if CONFIG_CPU_AFF_NR + restore_cpu(&old_mask); +#endif + return ret; +} + +struct smc_in_params { + unsigned long x0; + unsigned long x1; + unsigned long x2; + unsigned long x3; + unsigned long x4; +}; + +static uint32_t send_smc_cmd(uint32_t cmd, paddr_t cmd_addr, + uint32_t cmd_type, uint8_t wait) +{ + struct smc_in_params in_param = { cmd, cmd_addr, cmd_type, 0 }; + 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; + DSB; + + return ret; +} + +int raw_smc_send(uint32_t cmd, paddr_t cmd_addr, + uint32_t cmd_type, uint8_t wait) +{ + uint32_t r0; + +#if (CONFIG_CPU_AFF_NR != 0) + UINT16 old_mask; + set_cpu_strategy(&old_mask); +#endif + r0 = send_smc_cmd(cmd, cmd_addr, cmd_type, wait); +#if (CONFIG_CPU_AFF_NR != 0) + restore_cpu(&old_mask); +#endif + return r0; +} + +void siq_dump(paddr_t mode) +{ + raw_smc_send(TSP_REE_SIQ, mode, 0, false); + tz_log_write(); + do_cmd_need_archivelog(); +} + +static int siq_thread_fn(uintptr_t arg, int len) +{ + int ret; + + 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; + } + atomic_set(&g_siq_th_run, 0); + siq_dump((paddr_t)(1)); // set this addr to 1 + } +} + +static void cmd_result_check(struct tc_ns_smc_cmd *cmd) +{ + bool check_value = false; + check_value = cmd->ret_val == TEEC_PENDING || + cmd->ret_val == TEEC_PENDING2; + + if (check_value == true) + 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); +#ifdef CONFIG_TEELOG + cmd_monitor_ta_crash(TYPE_CRASH_TA); +#endif + } else if (cmd->ret_val == TEE_ERROR_AUDIT_FAIL) { + tloge("error smc call: ret = %x and cmd.err_origin=%x\n", + cmd->ret_val, cmd->err_origin); + } +} + +static int shadow_wo_pm(const void *arg, const wo_pm_params *params) +{ + uint32_t r0 = TSP_REQUEST; + uint32_t r1 = SMC_OPS_START_SHADOW; + uint32_t r2 = OsCurrTaskGet()->taskID; + uint32_t r3 = 0; + uint32_t r4 = *(uint32_t *)arg; + + if (*(params->exit_reason) == SMC_EXIT_PREEMPTED) { + r0 = TSP_REQUEST; + r1 = SMC_OPS_SCHEDTO; + r2 = OsCurrTaskGet()->taskID; + r3 = *(params->ta); + r4 = *(params->target); + } else if (*(params->exit_reason) == SMC_EXIT_NORMAL) { + r0 = TSP_REQUEST; + r1 = SMC_OPS_SCHEDTO; + r2 = OsCurrTaskGet()->taskID; + r3 = 0; + r4 = 0; + if (*(params->n_idled) > IDLED_COUNT) { + *(params->n_idled) = 0; + r1 = SMC_OPS_PROBE_ALIVE; + } + } + ISB; + DSB; + tlogd("%s: [cpu %d] r0=%x r1=%x r2=%x r3=%x r4=%x\n", __func__, + raw_smp_processor_id(), r0, r1, r2, r3, r4); + 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"(r0), [a1] "+r"(r1), [a2] "+r"(r2), + [a3] "+r"(r3), [a4] "+r"(r4) + :[re0] "r"(params->ret), [re1] "r"(params->exit_reason), + [re2] "r"(params->ta), [re3] "r"(params->target) + : "r0", "r1", "r2", "r3"); + } while (0); + + ISB; + DSB; + + return 0; +} + +static int check_shadow_param(uintptr_t arg, int len, struct pending_entry **pe) +{ + if (arg == 0) + return -ENOMEM; + if (len != sizeof(uint64_t)) { + free((void *)arg); + return -ENOMEM; + } + + *pe = init_pending_entry(get_current_pid()); + if (*pe == NULL) { + tloge("init pending entry failed\n"); + free((void *)arg); + return -ENOMEM; + } + + ISB; + DSB; + return 0; +} + +static int shadow_thread_fn(uintptr_t arg, int len) +{ + uint64_t ret = 0; + uint64_t exit_reason = SMC_EXIT_MAX; + uint64_t ta = 0; + uint64_t target = 0; + int n_preempted = 0; + int n_idled = 0; + int ret_val; + struct pending_entry *pe = NULL; + int rc; + wo_pm_params params = {&n_idled, &ret, &exit_reason, &ta, &target}; + + ret = check_shadow_param(arg, len, &pe); + if (ret) + return ret; + +RETRY_WO_PM: + ret_val = shadow_wo_pm((void *)arg, ¶ms); + if (ret_val == -1) + goto CLEAN_WO_PM; + tlogd("shadow thread return %lld\n", exit_reason); + if (exit_reason == SMC_EXIT_PREEMPTED) { + n_idled = 0; + if (++n_preempted > PREEMPT_COUNT) { + tlogi("%s: retry 10K times on CPU%d\n", __func__, raw_smp_processor_id()); + n_preempted = 0; + } + goto RETRY_WO_PM; + } else if (exit_reason == SMC_EXIT_NORMAL) { + n_preempted = 0; + long long timeout = HZ * (long)(HZ_COUNT + ((uint8_t)get_current_pid() & LOW_BYTE)); + rc = wait_event_interruptible_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"); + goto CLEAN_WO_PM; + } else { + atomic_set(&pe->run, 0); + goto RETRY_WO_PM; + } + } else if (exit_reason == SMC_EXIT_SHADOW) { + tlogd("shadow thread exit, it self\n"); + } else { + tlogd("shadow thread exit with unknown code %ld\n", (long)exit_reason); + } + +CLEAN_WO_PM: + free((void *)arg); + release_pending_entry(pe); + return ret_val; +} + +static void shadow_work_func(struct work_struct *work) +{ + LosTaskCB *shadow_thread = NULL; + if (work == NULL) + return; + struct shadow_work *s_work = + container_of(work, struct shadow_work, work); + uint64_t *target_arg = calloc(1, sizeof(uint64_t)); + + if (ZERO_OR_NULL_PTR((unsigned long)(uintptr_t)target_arg)) { + tloge("%s: kmalloc failed\n", __func__); + return; + } + + *target_arg = s_work->target; + + char shadow_name[OS_TCB_NAME_LEN] = {0}; + if (sprintf_s(shadow_name, OS_TCB_NAME_LEN, "shadow_th/%lu", g_shadow_thread_id++) < 0) { + tloge("gen shadow name fail\n"); + free(target_arg); + return; + } + shadow_thread = kthread_run(shadow_thread_fn, target_arg, sizeof(uint64_t), shadow_name); + if (IS_ERR_OR_NULL(shadow_thread)) { + free(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); + 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 == NULL) { + tloge("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 != NULL) { + 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); // set pe->run to 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)get_current_pid(), &secret, false); + + return; +} + +int smc_queue_shadow_worker(uint64_t target) +{ + struct shadow_work shadow_work; + INIT_WORK_ONSTACK(&shadow_work.work, shadow_work_func); + shadow_work.target = target; + + /* Run work on CPU 0 */ + queue_work(g_ipi_helper_worker, &shadow_work.work); + flush_work(&shadow_work.work); + return 0; +} + +#ifdef CONFIG_DRM_ADAPT +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(enum cmd_reuse *cmd_usage, int *cmd_index, + int *last_index, const struct tc_ns_smc_cmd *cmd, uint64_t ops) +{ + if (ops != SMC_OPS_NORMAL) + return 0; + + if (*cmd_usage == RESEND) { + if (reuse_smc_in_entry(*cmd_index)) { + tloge("reuse smc entry failed\n"); + release_smc_entry(*cmd_index); + return -ENOMEM; + } + } else { + *cmd_index = occupy_free_smc_in_entry(cmd); + if (*cmd_index == -1) { + tloge("there's no more smc entry\n"); + return -ENOMEM; + } + } + + if (*cmd_usage != CLEAR) { + *cmd_index = *last_index; + *cmd_usage = CLEAR; + } else { + *last_index = *cmd_index; + } + + tlogd("submit new cmd: cmd.ca=%u cmd-id=%x ev-nr=%u " + "cmd-index=%u last-index=%d\n", + cmd->ca_pid, cmd->cmd_id, + g_cmd_data->in[*cmd_index].event_nr, *cmd_index, + *last_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, uint64_t 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)get_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; + DSB; + 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); + + 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(get_current_pid()); + if (!(*pe)) { + tloge("init pending entry failed\n"); + return -ENOMEM; + } + + in->ca_pid = get_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; +} + +#define GOTO_RESLEEP 1 +#define GOTO_RETRY_WITH_CMD 2 +#define GOTO_RETRY 3 +#define GOTO_CLEAN 4 + +static int check_is_ca_killed(int cmd_index, uint64_t *ops) +{ + /* if CA has not been killed */ + if (!sigkill_pending(OsCurrTaskGet())) { + if (!is_cmd_working_done(cmd_index)) { + return GOTO_RESLEEP; + } else { + tloge("cmd done, may miss a spi!\n"); + show_cmd_bitmap_with_lock(); + } + } else { + /* if CA killed, send terminate cmd */ + *ops = SMC_OPS_ABORT_TASK; + tloge("CA is killed, send terminate!\n"); + return GOTO_RETRY_WITH_CMD; + } + return 0; +} + +struct cmd_pram { + struct tc_ns_smc_cmd *cmd; + int cmd_index; + enum cmd_reuse *cmd_usage; +}; + +static int cmd_done_process(struct tc_ns_smc_cmd *in, struct cmd_pram *c_param, uint64_t *ops) +{ + if ((in == NULL) || (c_param == NULL) || (ops == NULL)) + return 0; + + if (copy_smc_out_entry(c_param->cmd_index, c_param->cmd, c_param->cmd_usage)) { + c_param->cmd->ret_val = -1; + return GOTO_CLEAN; + } + + if (smp_smc_send_cmd_done(c_param->cmd_index, c_param->cmd, in) == -1) { + *ops = SMC_OPS_NORMAL; + /* cmd will be reused */ + return GOTO_RETRY; + } + + return 0; +} + +static int retry_with_fill_cmd_process(struct tc_ns_smc_cmd *in, struct cmd_pram *c_param, struct pending_entry *pe, + uint64_t *ops) +{ + struct smc_cmd_ret cmd_ret = {0}; + + if ((in == NULL) || (c_param == NULL) || (pe == NULL) || (ops == NULL)) + return 0; + + while (1) { + if (smp_smc_send_process(c_param->cmd, *ops, &cmd_ret, c_param->cmd_index) == -1) + return GOTO_CLEAN; + if (is_cmd_working_done(c_param->cmd_index)) + return cmd_done_process(in, c_param, ops); + + if (cmd_ret.exit != SMC_EXIT_NORMAL) { + tloge("invalid cmd work state\n"); + c_param->cmd->ret_val = -1; + return GOTO_CLEAN; + } + /* task pending exit */ + tlogd("goto sleep, exit_reason=%lld\n", cmd_ret.exit); +RESLEEP: + if (wait_event_interruptible_timeout(pe->wq, atomic_read(&pe->run), + (long)(RESLEEP_TIMEOUT * HZ)) == 0) { + tlogd("CA wait event for %d s\n", RESLEEP_TIMEOUT); + int ret = check_is_ca_killed(c_param->cmd_index, ops); + if (ret == GOTO_RESLEEP) + goto RESLEEP; + else if (ret == GOTO_RETRY_WITH_CMD) + continue; + } + atomic_set(&pe->run, 0); + if (is_cmd_working_done(c_param->cmd_index)) { + tlogd("cmd is done\n"); + return cmd_done_process(in, c_param, ops); + } + *ops = SMC_OPS_SCHEDTO; + } + + return 0; +} + +static int smp_smc_send_func(struct tc_ns_smc_cmd *in, bool reuse) +{ + int cmd_index = 0; + int last_index = 0; + struct tc_ns_smc_cmd cmd = { {0}, 0 }; + struct pending_entry *pe = NULL; + uint64_t ops; + enum cmd_reuse cmd_usage = CLEAR; + + int ret; + bool check = false; + + if (init_for_smc_send(in, &pe, &cmd, reuse)) + return TEEC_ERROR_GENERIC; + if (reuse) { + last_index = in->event_nr; + cmd_index = in->event_nr; + cmd_usage = RESEND; + } + ops = SMC_OPS_NORMAL; + +retry: + if (smc_ops_normal(&cmd_usage, &cmd_index, &last_index, &cmd, ops)) { + release_pending_entry(pe); + return TEEC_ERROR_GENERIC; + } + + struct cmd_pram c_param; + c_param.cmd = &cmd; + c_param.cmd_index = cmd_index; + c_param.cmd_usage = &cmd_usage; + + ret = retry_with_fill_cmd_process(in, &c_param, pe, &ops); + if (ret == GOTO_CLEAN) + goto clean; + else if (ret == GOTO_RETRY) + goto retry; + +clean: + check = (cmd_usage != CLEAR && cmd.ret_val != TEEC_PENDING); + if (check == true) + release_smc_entry(cmd_index); + release_pending_entry(pe); + return cmd.ret_val; +} + +static int smc_svc_thread_fn(uintptr_t arg, int len) +{ + 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; +} + +void wakeup_tc_siq(void) +{ + atomic_set(&g_siq_th_run, 1); + 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) +{ + 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); +} + +static int alloc_cmd_buffer(void) +{ + g_cmd_data = (struct tc_ns_smc_queue *)__get_free_page(); + if (!g_cmd_data) + return -ENOMEM; + if (memset_s(g_cmd_data, sizeof(struct tc_ns_smc_queue), 0, sizeof(struct tc_ns_smc_queue))) { + free_page(g_cmd_data); + g_cmd_data = NULL; + return -ENOMEM; + } + g_cmd_phys = LOS_PaddrQuery(g_cmd_data); + return 0; +} + +static int init_smc_related_rsrc(void) +{ + g_ipi_helper_worker = create_workqueue("ipihelper"); + if (g_ipi_helper_worker == NULL) { + tloge("couldn't create workqueue.\n"); + return -ENOMEM; + } + + wake_up_process(g_siq_thread); + init_cmd_monitor(); + INIT_LIST_HEAD(&g_pending_head); + spin_lock_init(&g_pend_lock); + + return 0; +} + +int smc_context_init(void) +{ + int ret; + + ret = alloc_cmd_buffer(); + if (ret) + return ret; + + /* Send the allocated buffer to TrustedCore for init */ + smc_set_cmd_buffer(); + + g_siq_thread = kthread_run(siq_thread_fn, NULL, 0, "siqthread/0"); + if (unlikely(IS_ERR_OR_NULL(g_siq_thread))) { + pr_err("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(); + if (ret) + goto free_siq_worker; + + return 0; + +free_siq_worker: + kthread_stop(g_siq_thread); + g_siq_thread = NULL; +free_mem: + free_page(g_cmd_data); + g_cmd_data = NULL; + return ret; +} + +int init_smc_svc_thread(void) +{ + g_smc_svc_thread = kthread_run(smc_svc_thread_fn, NULL, + 0, "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); + } + wake_up_process(g_smc_svc_thread); + return 0; +} + +void smc_free_data(void) +{ + free_page(g_cmd_data); + g_cmd_data = NULL; + if (!IS_ERR_OR_NULL(g_smc_svc_thread)) { + kthread_stop(g_smc_svc_thread); + g_smc_svc_thread = NULL; + } +} + diff --git a/liteos/core/smc_smp.h b/liteos/core/smc_smp.h new file mode 100644 index 0000000..f46ec12 --- /dev/null +++ b/liteos/core/smc_smp.h @@ -0,0 +1,142 @@ +/* + * 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 "teek_client_constants.h" +#include "teek_ns_client.h" + +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)] + +static inline void set_bit(int nr, volatile unsigned long *addr) +{ + if (addr == NULL) + return; + const unsigned long mask = BIT_MASK(nr); + unsigned long *p = ((unsigned long *)addr) + BIT_WORD(nr); + *p |= mask; +} + +static inline void clear_bit(int nr, volatile unsigned long *addr) +{ + if (addr == NULL) + return; + + const unsigned long mask = BIT_MASK(nr); + unsigned long *p = ((unsigned long *)addr) + BIT_WORD(nr); + *p &= ~mask; +} + +static inline int test_bit(int nr, const volatile unsigned long *addr) +{ + if (addr == NULL) + return 0; + + return 1UL & (addr[BIT_WORD(nr)] >> ((unsigned int)nr & (BITS_PER_BYTE * sizeof(uint64_t) - 1))); +} + +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(LosTaskCB *tsk); +int smc_context_init(void); +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); +void SetCmdSendState(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(void); +struct tc_ns_smc_queue *get_cmd_data_buffer(void); + +#endif diff --git a/liteos/core/tc_client_driver.c b/liteos/core/tc_client_driver.c new file mode 100644 index 0000000..d02b13f --- /dev/null +++ b/liteos/core/tc_client_driver.c @@ -0,0 +1,967 @@ +/* + * 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 "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 "auth_base_impl.h" +#include "client_hash_auth.h" +#include "auth_base_impl.h" +#include "tlogger.h" +#include "tzdebug.h" +#include "session_manager.h" +#include "los_adapt.h" + +struct workqueue_struct *g_tzdriver_wq = NULL; + +#ifdef CONFIG_ACPI +static int g_acpi_irq; +#endif + +static unsigned int g_device_file_cnt = 1; +static mutex_t g_device_file_cnt_lock = PTHREAD_MUTEX_INITIALIZER; + +/* dev node list and itself has mutex to avoid race */ +struct tc_ns_dev_list g_tc_ns_dev_list; + +static LosTaskCB *g_teecd_task; + +void set_teecd_task(LosTaskCB* task) +{ + g_teecd_task = task; +} + +LosTaskCB *get_teecd_task(void) +{ + return 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; + if (!argp) { + tloge("error input parameter\n"); + return -EINVAL; + } + + 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 = 0; + + 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; +} + +/* + * 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_without_cert(struct tc_ns_dev_file *dev_file) +{ + int ret; + uint8_t *cert_buffer = NULL; + uint8_t *temp_cert_buffer = NULL; + unsigned int cert_buffer_size = 0; + char *path = NULL; + errno_t sret; + + if (!dev_file) + return -EINVAL; + + 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 != 0) { + tloge("alloc fail\n"); + goto error; + } + temp_cert_buffer = cert_buffer; + path = get_process_path(OsCurrTaskGet(), (char *)cert_buffer, MAX_PACKAGE_NAME_LEN); + if (path == NULL) { + tloge("get path fail\n"); + ret = -EFAULT; + goto error; + } + dev_file->pkg_name_len = strlen(path); + sret = strncpy_s((char *)dev_file->pkg_name, MAX_PACKAGE_NAME_LEN, (char *)cert_buffer, dev_file->pkg_name_len); + if (sret != EOK) { + tloge("str cpy fail\n"); + ret = -ENOMEM; + goto error; + } + + int uid = get_task_uid(OsCurrTaskGet()); + dev_file->pub_key_len = sizeof(uid); + if (memcpy_s((char *)dev_file->pub_key, MAX_PUBKEY_LEN, (char *)&uid, dev_file->pub_key_len)) { + tloge("failed to copy cert, pubkeylen = %u\n", dev_file->pub_key_len); + ret = -EINVAL; + goto error; + } + + dev_file->login_setup = true; + +error: + kfree(temp_cert_buffer); + mutex_unlock(&dev_file->login_setup_lock); + return ret; +} + +static int tc_ns_client_login_func_with_cert(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((char *)dev_file->pkg_name, MAX_PACKAGE_NAME_LEN, (char *)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; +} + +static int tc_ns_client_login_func(struct tc_ns_dev_file *dev_file, + const void __user *buffer) +{ + if (buffer == NULL) + return tc_ns_client_login_func_without_cert(dev_file); + else + return tc_ns_client_login_func_with_cert(dev_file, buffer); +} + +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); + *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; +} + +static void release_vma_shared_mem(struct tc_ns_dev_file *dev_file, + const LosVmMapRegion *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->range.base) { + shared_mem->user_addr = NULL; + find = true; + } else if (shared_mem->user_addr_ca == + (void *)(uintptr_t)vma->range.base) { + 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); +} + +static int shared_vma_close(struct tc_ns_dev_file *dev_file, const unsigned int argp) +{ + bool check_value = false; + + if (dev_file == NULL) { + tloge("unmap input error\n"); + return -EINVAL; + } + + LosVmMapRegion *vma = LOS_RegionFind(OsCurrProcessGet()->vmSpace, (vaddr_t)argp); + if (!vma) { + tloge("vma is null\n"); + return -EINVAL; + } + + check_value = (is_teecd_process(g_teecd_task, OsCurrTaskGet())) && + (!tc_ns_get_uid()); + if (check_value) { + check_value = (g_teecd_task->taskStatus & OS_TASK_STATUS_EXIT) || + (OsCurrTaskGet()->taskStatus & OS_TASK_STATUS_EXIT); + if (check_value) { + tlogd("teecd is killed, just return in vma close\n"); + return -EINVAL; + } + } + + release_vma_shared_mem(dev_file, vma); + return 0; +} + +static struct tc_ns_shared_mem *find_sharedmem( + const LosVmMapRegion *vma, + const struct tc_ns_dev_file *dev_file, bool *only_remap) +{ + struct tc_ns_shared_mem *shm_tmp = NULL; + unsigned long len = vma->range.size; + + /* + * 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->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->range.size != 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(LosVmMapRegion *vma, + const struct tc_ns_shared_mem *shared_mem) +{ + int ret; + + 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, LosVmMapRegion *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->f_priv) { + tloge("invalid args for tc mmap\n"); + return -EINVAL; + } + dev_file = filp->f_priv; + + 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; + } + + if (only_remap) { + shared_mem->user_addr_ca = (void *)vma->range.base; + mutex_unlock(&dev_file->shared_mem_lock); + return ret; + } + shared_mem->user_addr = (void *)vma->range.base; + atomic_set(&shared_mem->offset, vma->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->f_priv; + + 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_NATIVE_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! 0x%x\n", cmd); + break; + } + 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) +{ + 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) +{ + return get_task_uid(OsCurrTaskGet()); +} + +static int tc_client_ioctl(struct file *file, int cmd, + unsigned long arg) +{ + int ret = -EFAULT; + void *argp = (void __user *)(uintptr_t)arg; + struct tc_ns_dev_file *dev_file = file->f_priv; + struct tc_ns_client_context client_context = {{0}}; + + 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(dev_file, argp); + break; + case TC_NS_CLIENT_IOCTL_CANCEL_CMD_REQ: + ret = tc_ns_send_cancel_cmd(dev_file, argp, &client_context); + break; + case TC_NS_CLIENT_IOCTL_LOGIN: + ret = tc_ns_client_login_func(dev_file, 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_NATIVE_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(dev_file, argp); + break; + case TC_NS_CLIENT_IOCTL_UNMAP_SHARED_MEM: + ret = shared_vma_close(file->f_priv, (unsigned int)(uintptr_t)argp); + break; + default: + tloge("invalid cmd 0x%x! arg 0x%lx\n", cmd, arg); + break; + } + + return ret; +} + +static int tc_client_open(struct file *file) +{ + int ret; + struct tc_ns_dev_file *dev = NULL; + + check_teecd_process(); + + file->f_priv = NULL; + ret = tc_ns_client_open(&dev, TEE_REQ_FROM_USER_MODE); + if (!ret) + file->f_priv = 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 file *file) +{ + int ret = 0; + struct tc_ns_dev_file *dev = file->f_priv; + bool check_value = false; + + clean_agent_pid_info(dev); + check_value = (is_teecd_process(g_teecd_task, OsCurrTaskGet())) && + (!tc_ns_get_uid()); + if (check_value) { + /* for teecd close fd */ + check_value = (g_teecd_task->taskStatus & OS_TASK_STATUS_EXIT) || + (OsCurrTaskGet()->taskStatus & OS_TASK_STATUS_EXIT); + if (check_value) { + 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->f_priv = 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_vfs g_tc_ns_client_fops = { + .open = tc_client_open, + .close = tc_client_close, + .ioctl = tc_client_ioctl, + .mmap = tc_client_mmap, +}; + +bool schedule_work_on(int cpu, struct work_struct *work) +{ + return queue_work(g_tzdriver_wq, work); +} + +static int tc_ns_client_init(void) +{ + int ret; + + tlogd("tc_ns_client_init"); + + ret = create_tc_client_device(TC_NS_CLIENT_DEV_NAME, &g_tc_ns_client_fops); + if (ret != EOK) { + tloge("create tee device error.\n"); + return ret; + } + + ret = memset_s(&g_tc_ns_dev_list, sizeof(g_tc_ns_dev_list), 0, + sizeof(g_tc_ns_dev_list)); + if (ret != EOK) + goto destroy_dev; + + INIT_LIST_HEAD(&g_tc_ns_dev_list.dev_file_list); + mutex_init(&g_tc_ns_dev_list.dev_lock); + init_srvc_list(); + + g_tzdriver_wq = create_workqueue("g_tzalloc_ordered_workqueuedriver_wq"); + if (g_tzdriver_wq == NULL) { + tloge("create tzdriver workqueue failed\n"); + ret = -EFAULT; + goto destroy_dev; + } + + return ret; + +destroy_dev: + (void)unregister_driver(TC_NS_CLIENT_DEV_NAME); + return ret; +} + +static int tc_teeos_init(void) +{ + int ret; + ret = smc_context_init(); + if (ret) + return ret; + ret = mailbox_mempool_init(); + if (ret) { + tloge("tz mailbox init failed\n"); + goto smc_data_free; + } + ret = tz_spi_init(); + if (ret) + goto release_mailbox; + + return 0; +release_mailbox: + mailbox_mempool_destroy(); +smc_data_free: + smc_free_data(); + return ret; +} + +static void tc_re_init(void) +{ + int ret; + + agent_init(); + + if (tzdebug_init()) + tloge("tzdebug init failed\n"); + + ret = init_tlogger_service(); + if (ret) + tloge("tlogger 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"); +} + +__init int tc_init(void) +{ + int ret = 0; + ret = tc_ns_client_init(); + if (ret) + return ret; + ret = tc_teeos_init(); + if (ret) + goto class_device_destroy; + /* run-time environment init failure don't block tzdriver init proc */ + tc_re_init(); + return 0; + +class_device_destroy: + (void)unregister_driver(TC_NS_CLIENT_DEV_NAME); + if (g_tzdriver_wq != NULL) + destroy_workqueue(g_tzdriver_wq); + return ret; +} + +#ifndef CONFIG_LITEOS_TZDRIVER +static void tc_exit(void) +{ + tlogd("tz client exit"); + tz_spi_exit(); + /* run-time environment exit should before teeos exit */ + smc_free_data(); + agent_exit(); +#ifdef CONFIG_TZDRIVER_MODULE + tzdebug_exit(); + exit_tlogger_service(); +#endif + mailbox_mempool_destroy(); + tee_exit_shash_handle(); +} +#endif diff --git a/liteos/core/tc_client_driver.h b/liteos/core/tc_client_driver.h new file mode 100644 index 0000000..5291df3 --- /dev/null +++ b/liteos/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/liteos/core/teek_client_api.c b/liteos/core/teek_client_api.c new file mode 100644 index 0000000..8bd3811 --- /dev/null +++ b/liteos/core/teek_client_api.c @@ -0,0 +1,668 @@ +/* + * 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 "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" + +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 = + (uint64_t)(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 = + (uint64_t)(uintptr_t) + (&(oper->params[idex].memref.size)); + } + if (oper->params[idex].memref.parent->is_allocated) { + context->params[idex].memref.buffer = + (uint64_t)(uintptr_t) + oper->params[idex].memref.parent->buffer; + } else { + context->params[idex].memref.buffer = + (uint64_t)(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 = + (uint64_t)(uintptr_t) + (operation->params[idex].tmpref.buffer); + cli_context->params[idex].memref.size_addr = + (uint64_t)(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 = + (uint64_t)(uintptr_t) + (&(operation->params[idex].value.a)); + cli_context->params[idex].value.b_addr = + (uint64_t)(uintptr_t) + (&(operation->params[idex].value.b)); + } else if (is_ion_param(param_type[idex])) { + cli_context->params[idex].value.a_addr = + (uint64_t)(uintptr_t) + (&(operation->params[idex].ionref.ion_share_fd)); + cli_context->params[idex].value.b_addr = + (uint64_t)(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) +{ + 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; + } + + 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->returns.origin = 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; +} + +/* + * 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; +} + +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}; + + /* 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 = open_session_and_switch_ret(session, context, + destination, &cli_context, &origin); + +set_ori: + if (teec_ret != TEEC_SUCCESS && return_origin != NULL) + *return_origin = origin; + + 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; +} + +/* + * 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"); + } +} + +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; +} + +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); +} + +/* + * 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/liteos/core/tz_pm.h b/liteos/core/tz_pm.h new file mode 100644 index 0000000..d7af0b2 --- /dev/null +++ b/liteos/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/liteos/core/tz_spi_notify.c b/liteos/core/tz_spi_notify.c new file mode 100644 index 0000000..1fea506 --- /dev/null +++ b/liteos/core/tz_spi_notify.c @@ -0,0 +1,805 @@ +/* + * 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 "teek_client_constants.h" +#include "tc_ns_client.h" +#include "tc_ns_log.h" +#include "tc_client_driver.h" +#include "gp_ops.h" +#include "mailbox_mempool.h" +#include "smc_smp.h" +#include "session_manager.h" + +#define MAX_CALLBACK_COUNT 100 +#define UUID_SIZE 16 +struct teec_timer_property; + +#ifdef DEF_ENG +static int g_timer_type; +#endif + +enum timer_class_type { + /* timer event using timer10 */ + TIMER_GENERIC, + /* timer event using RTC */ + TIMER_RTC +}; + +struct teec_timer_property { + unsigned int type; + unsigned int timer_id; + unsigned int timer_class; + unsigned int reserved2; +}; + +struct notify_context_timer { + unsigned int dev_file_id; + unsigned char uuid[UUID_SIZE]; + unsigned int session_id; + struct teec_timer_property property; + uint32_t expire_time; +}; + + +struct notify_context_wakeup { + pid_t ca_thread_id; +}; + +struct notify_context_shadow { + uint64_t target_tcb; +}; + +#ifdef CONFIG_TA_AFFINITY + +#define AFF_BITS_SIZE 64 + +#define AFF_BITS_NUM ((CONFIG_TA_AFFINITY_CPU_NUMS % AFF_BITS_SIZE == 0) ? \ + (CONFIG_TA_AFFINITY_CPU_NUMS / AFF_BITS_SIZE) : \ + (CONFIG_TA_AFFINITY_CPU_NUMS / AFF_BITS_SIZE + 1)) + +#define aff_bits_mask(cpuid) \ + (1LLU << (cpuid - (cpuid / AFF_BITS_SIZE) * AFF_BITS_SIZE)) + +struct aff_bits_t { + uint64_t aff_bits[AFF_BITS_NUM]; +}; + +struct notify_context_set_affinity { + pid_t ca_thread_id; + struct aff_bits_t aff; +}; + +#endif + +struct notify_context_stats { + uint32_t send_s; + uint32_t recv_s; + uint32_t send_w; + uint32_t recv_w; +#ifdef CONFIG_TA_AFFINITY + uint32_t send_af; + uint32_t recv_af; +#endif + uint32_t missed; +}; + +union notify_context { + struct notify_context_timer timer; + struct notify_context_wakeup wakeup; + struct notify_context_shadow shadow; +#ifdef CONFIG_TA_AFFINITY + struct notify_context_set_affinity affinity; +#endif + struct notify_context_stats meta; +}; + +struct notify_data_entry { + uint32_t entry_type : 31; + uint32_t filled : 1; + union notify_context context; +}; + +#ifdef CONFIG_BIG_SESSION + +#define NOTIFY_DATA_ENTRY_COUNT \ + (((PAGE_SIZE * ((1U) << (CONFIG_NOTIFY_PAGE_ORDER))) \ + / sizeof(struct notify_data_entry)) - 1) +#else +#define NOTIFY_DATA_ENTRY_COUNT \ + ((PAGE_SIZE / sizeof(struct notify_data_entry)) - 1) +#endif + +struct notify_data_struct { + struct notify_data_entry entry[NOTIFY_DATA_ENTRY_COUNT]; + struct notify_data_entry meta; +}; + +static struct notify_data_struct *g_notify_data; +static struct notify_data_entry *g_notify_data_entry_shadow; + +enum notify_data_type { + NOTIFY_DATA_ENTRY_UNUSED, + NOTIFY_DATA_ENTRY_TIMER, + NOTIFY_DATA_ENTRY_RTC, + NOTIFY_DATA_ENTRY_WAKEUP, + NOTIFY_DATA_ENTRY_SHADOW, + NOTIFY_DATA_ENTRY_FIQSHD, + NOTIFY_DATA_ENTRY_SHADOW_EXIT, +#ifdef CONFIG_TA_AFFINITY + NOTIFY_DATA_ENTRY_SET_AFFINITY, +#endif + NOTIFY_DATA_ENTRY_MAX, +}; + +struct tc_ns_callback { + unsigned char uuid[UUID_SIZE]; + mutex_t callback_lock; + void (*callback_func)(void *); + struct list_head head; +}; + +struct tc_ns_callback_list { + unsigned int callback_count; + mutex_t callback_list_lock; + struct list_head callback_list; +}; + +static void tc_notify_fn(struct work_struct *dummy); +static struct tc_ns_callback_list g_ta_callback_func_list; +static DECLARE_WORK(tc_notify_work, tc_notify_fn); +static struct workqueue_struct *g_tz_spi_wq; + +static void walk_callback_list( + struct notify_context_timer *tc_notify_data_timer) +{ + struct tc_ns_callback *callback_func_t = NULL; + + mutex_lock(&g_ta_callback_func_list.callback_list_lock); + list_for_each_entry(callback_func_t, + &g_ta_callback_func_list.callback_list, head) { + if (memcmp(callback_func_t->uuid, tc_notify_data_timer->uuid, + UUID_SIZE)) + continue; + + if (tc_notify_data_timer->property.timer_class == + TIMER_RTC) { + tlogd("start to call callback func\n"); + callback_func_t->callback_func( + &(tc_notify_data_timer->property)); + tlogd("end to call callback func\n"); + } else if (tc_notify_data_timer->property.timer_class == + TIMER_GENERIC) { + tlogd("timer60 no callback func\n"); + } + } + mutex_unlock(&g_ta_callback_func_list.callback_list_lock); +} + +static int find_notify_sess( + const struct notify_context_timer *tc_notify_data_timer, + struct tc_ns_session **temp_ses, bool *enc_found) +{ + struct tc_ns_dev_file *temp_dev_file = NULL; + struct tc_ns_dev_list *dev_list = NULL; + struct tc_ns_service *temp_svc = NULL; + + dev_list = get_dev_list(); + if (!dev_list) { + tloge("dev list is invalid\n"); + return -ENOENT; + } + + mutex_lock(&dev_list->dev_lock); + list_for_each_entry(temp_dev_file, &dev_list->dev_file_list, head) { + tlogd("dev file id1 = %u, id2 = %u\n", + temp_dev_file->dev_file_id, + tc_notify_data_timer->dev_file_id); + if (temp_dev_file->dev_file_id == + tc_notify_data_timer->dev_file_id) { + mutex_lock(&temp_dev_file->service_lock); + temp_svc = + tc_find_service_in_dev(temp_dev_file, + tc_notify_data_timer->uuid, UUID_LEN); + mutex_unlock(&temp_dev_file->service_lock); + if (!temp_svc) + break; + get_service_struct(temp_svc); + mutex_lock(&temp_svc->session_lock); + *temp_ses = + tc_find_session_withowner( + &temp_svc->session_list, + tc_notify_data_timer->session_id, + temp_dev_file); + get_session_struct(*temp_ses); + mutex_unlock(&temp_svc->session_lock); + put_service_struct(temp_svc); + temp_svc = NULL; + if (*temp_ses) { + tlogd("send cmd ses id %u\n", + (*temp_ses)->session_id); + *enc_found = true; + break; + } + break; + } + } + mutex_unlock(&dev_list->dev_lock); + + return 0; +} + +static void tc_notify_timer_fn(struct notify_data_entry *notify_data_entry) +{ + struct tc_ns_session *temp_ses = NULL; + bool enc_found = false; + struct notify_context_timer *tc_notify_data_timer = NULL; + + tc_notify_data_timer = &(notify_data_entry->context.timer); + notify_data_entry->filled = 0; + tlogd("notify data timer type is 0x%x, timer ID is 0x%x\n", + tc_notify_data_timer->property.type, + tc_notify_data_timer->property.timer_id); + walk_callback_list(tc_notify_data_timer); + + if (find_notify_sess(tc_notify_data_timer, &temp_ses, &enc_found)) + return; + + if (tc_notify_data_timer->property.timer_class == TIMER_GENERIC) { + tlogd("timer60 wake up event\n"); + if (enc_found && temp_ses) { + temp_ses->wait_data.send_wait_flag = 1; + wake_up(&temp_ses->wait_data.send_cmd_wq); + put_session_struct(temp_ses); + temp_ses = NULL; + } + } else { + tlogd("RTC do not need to wakeup\n"); + } +} + +static noinline int get_notify_data_entry(struct notify_data_entry *copy) +{ + uint32_t i; + int filled; + int ret = -1; + + if (!copy || !g_notify_data) { + tloge("bad parameters or notify data is NULL"); + return ret; + } + + /* TIMER and RTC use fix entry, skip them. */ + for (i = NOTIFY_DATA_ENTRY_UNUSED; i < NOTIFY_DATA_ENTRY_COUNT; + i++) { + struct notify_data_entry *e = NULL; + e = &g_notify_data->entry[i]; + filled = e->filled; + DMB; + if (!filled) + continue; + switch (e->entry_type) { + case NOTIFY_DATA_ENTRY_TIMER: + case NOTIFY_DATA_ENTRY_RTC: + break; + case NOTIFY_DATA_ENTRY_SHADOW: + case NOTIFY_DATA_ENTRY_SHADOW_EXIT: + case NOTIFY_DATA_ENTRY_FIQSHD: + g_notify_data->meta.context.meta.recv_s++; + break; + case NOTIFY_DATA_ENTRY_WAKEUP: + g_notify_data->meta.context.meta.recv_w++; + break; +#ifdef CONFIG_TA_AFFINITY + case NOTIFY_DATA_ENTRY_SET_AFFINITY: + g_notify_data->meta.context.meta.recv_af++; + break; +#endif + default: + tloge("invalid notify type=%u\n", e->entry_type); + goto exit; + } + if (memcpy_s(copy, sizeof(*copy), e, sizeof(*e)) != EOK) { + tloge("memcpy entry failed\n"); + break; + } + DMB; + e->filled = 0; + ret = 0; + break; + } +exit: + return ret; +} + +static void tc_notify_wakeup_fn(const struct notify_data_entry *entry) +{ + const struct notify_context_wakeup *tc_notify_wakeup = NULL; + + tc_notify_wakeup = &(entry->context.wakeup); + smc_wakeup_ca(tc_notify_wakeup->ca_thread_id); + tlogd("notify data entry wakeup ca: %d\n", + tc_notify_wakeup->ca_thread_id); +} + +static void tc_notify_shadow_fn(const struct notify_data_entry *entry) +{ + const struct notify_context_shadow *tc_notify_shadow = NULL; + + tc_notify_shadow = &(entry->context.shadow); + smc_queue_shadow_worker(tc_notify_shadow->target_tcb); +} + +static void tc_notify_fiqshd_fn(const struct notify_data_entry *entry) +{ + const struct notify_context_shadow *tc_notify_shadow = NULL; + + if (!entry) { + /* for NOTIFY_DATA_ENTRY_FIQSHD missed */ + fiq_shadow_work_func(0); + return; + } + tc_notify_shadow = &(entry->context.shadow); + fiq_shadow_work_func(tc_notify_shadow->target_tcb); +} + +static void tc_notify_shadowexit_fn(const struct notify_data_entry *entry) +{ + const struct notify_context_wakeup *tc_notify_wakeup = NULL; + + tc_notify_wakeup = &(entry->context.wakeup); + if (smc_shadow_exit(tc_notify_wakeup->ca_thread_id)) + tloge("shadow ca exit failed: %d\n", + (int)tc_notify_wakeup->ca_thread_id); +} + +#ifdef CONFIG_TA_AFFINITY +static void tc_notify_set_affinity(struct notify_data_entry *entry) +{ + struct notify_context_set_affinity *af_data = NULL; + struct pending_entry *pe = NULL; + + af_data = &(entry->context.affinity); + pe = find_pending_entry(af_data->ca_thread_id); + if (pe != NULL) { + struct cpumask mask; + uint32_t i; + + cpumask_clear(&mask); + for (i = 0; i < (uint32_t)NR_CPUS; i++) { + struct aff_bits_t *aff = &af_data->aff; + if (aff->aff_bits[i / AFF_BITS_SIZE] & aff_bits_mask(i)) + cpumask_set_cpu(i, &mask); + } + + /* + * we don't set ca's cpumask here but in ca's own thread + * context after ca is wakeup in smc_send_func, or + * scheduler will set task's allow cpumask failure in that case. + */ + cpumask_copy(&pe->ta_mask, &mask); + smc_wakeup_ca(af_data->ca_thread_id); + tlogi("set affinity for ca thread id %u\n", af_data->ca_thread_id); + put_pending_entry(pe); + } else { + tloge("invalid ca thread id %u for set affinity\n", + af_data->ca_thread_id); + /* + * if a TEE tcb without CA bind(CA is 0) cause a affinity set, + * the CA tid(current cpu context) may wrong + * (in tc_notify_fiqshd_fn, don't init_pending_entry, + * in this case, cannot find pending_entry), + * but we must set affinity for CA otherwise the TA can't run, + * so we wakeup all blocked CA. + */ + (void)smc_wakeup_broadcast(); + } +} +#endif + +static void spi_broadcast_notifications(void) +{ + uint32_t missed; + + DMB; + + if (!g_notify_data) { + tloge("notify data is NULL\n"); + return; + } + + missed = LOS_AtomicXchg32bits((Atomic *)&g_notify_data->meta.context.meta.missed, 0); + if (!missed) + return; + if (missed & (1U << NOTIFY_DATA_ENTRY_WAKEUP)) { + smc_wakeup_broadcast(); + missed &= ~(1U << NOTIFY_DATA_ENTRY_WAKEUP); + } + if (missed & (1U << NOTIFY_DATA_ENTRY_FIQSHD)) { + tc_notify_fiqshd_fn(NULL); + missed &= ~(1U << NOTIFY_DATA_ENTRY_FIQSHD); + } + if (missed) + tloge("missed spi notification mask %x\n", missed); +} + +static int g_spi_inited = 0; +static void tc_notify_fn(struct work_struct *dummy) +{ + struct notify_data_entry copy = {0}; + + if (!g_spi_inited) + return; + + while (get_notify_data_entry(©) == 0) { + switch (copy.entry_type) { + case NOTIFY_DATA_ENTRY_TIMER: + case NOTIFY_DATA_ENTRY_RTC: + tc_notify_timer_fn(©); + break; + case NOTIFY_DATA_ENTRY_WAKEUP: + tc_notify_wakeup_fn(©); + break; + case NOTIFY_DATA_ENTRY_SHADOW: + tc_notify_shadow_fn(©); + break; + case NOTIFY_DATA_ENTRY_FIQSHD: + tc_notify_fiqshd_fn(©); + break; + case NOTIFY_DATA_ENTRY_SHADOW_EXIT: + tc_notify_shadowexit_fn(©); + break; +#ifdef CONFIG_TA_AFFINITY + case NOTIFY_DATA_ENTRY_SET_AFFINITY: + tc_notify_set_affinity(©); + break; +#endif + default: + tloge("invalid entry type = %u\n", copy.entry_type); + } + if (memset_s(©, sizeof(copy), 0, sizeof(copy))) + tloge("memset copy failed\n"); + } + spi_broadcast_notifications(); +} + +static irqreturn_t tc_secure_notify(int irq, void *dev_id) +{ +#define N_WORK 8 + int i; + static struct work_struct tc_notify_works[N_WORK]; + static int init; + + if (!init) { + for (i = 0; i < N_WORK; i++) + INIT_WORK(&tc_notify_works[i], tc_notify_fn); + init = 1; + } + for (i = 0; i < N_WORK; i++) { + if (queue_work(g_tz_spi_wq, &tc_notify_works[i])) + break; + } +#undef N_WORK + + return IRQ_HANDLED; +} + +int tc_ns_register_service_call_back_func(const char *uuid, void *func, + const void *private_data) +{ + struct tc_ns_callback *callback_func = NULL; + struct tc_ns_callback *new_callback = NULL; + int ret = 0; + + if (!uuid || !func) + return -EINVAL; + + (void)private_data; + mutex_lock(&g_ta_callback_func_list.callback_list_lock); + if (g_ta_callback_func_list.callback_count > MAX_CALLBACK_COUNT) { + mutex_unlock(&g_ta_callback_func_list.callback_list_lock); + tloge("callback_count is out\n"); + return -ENOMEM; + } + list_for_each_entry(callback_func, + &g_ta_callback_func_list.callback_list, head) { + if (!memcmp(callback_func->uuid, uuid, UUID_SIZE)) { + callback_func->callback_func = (void (*)(void *))func; + tlogd("succeed to find uuid ta_callback_func_list\n"); + goto find_callback; + } + } + /* create a new callback struct if we couldn't find it in list */ + new_callback = kzalloc(sizeof(*new_callback), GFP_KERNEL); + if (ZERO_OR_NULL_PTR((unsigned long)(uintptr_t)new_callback)) { + tloge("kzalloc failed\n"); + ret = -ENOMEM; + goto find_callback; + } + + if (memcpy_s(new_callback->uuid, UUID_SIZE, uuid, UUID_SIZE)) { + kfree(new_callback); + new_callback = NULL; + ret = -ENOMEM; + goto find_callback; + } + g_ta_callback_func_list.callback_count++; + tlogd("callback count is %u\n", + g_ta_callback_func_list.callback_count); + INIT_LIST_HEAD(&new_callback->head); + new_callback->callback_func = (void (*)(void *))func; + mutex_init(&new_callback->callback_lock); + list_add_tail(&new_callback->head, + &g_ta_callback_func_list.callback_list); +find_callback: + mutex_unlock(&g_ta_callback_func_list.callback_list_lock); + return ret; +} + +#ifdef DEF_ENG + +static void timer_callback_func(void *param) +{ + struct teec_timer_property *timer_property = + (struct teec_timer_property *)param; + tlogd("timer property type = %x, timer property timer id = %x\n", + timer_property->type, timer_property->timer_id); + g_timer_type = (int)timer_property->type; +} + +static void tst_get_timer_type(int *type) +{ + *type = g_timer_type; +} + +static void callback_demo_main(const char *uuid) +{ + int ret; + + tlogd("step into callback_demo_main\n"); + ret = tc_ns_register_service_call_back_func(uuid, + timer_callback_func, NULL); + if (ret) + tloge("failed to tc_ns_register_service_call_back_func\n"); +} + +static int init_tst_test_context(struct tc_ns_client_context *client_context, + const void *argp, int *cmd_id) +{ + struct tc_uuid secure_timer_uuid = { + 0x19b39980, 0x2487, 0x7b84, + {0xf4, 0x1a, 0xbc, 0x89, 0x22, 0x62, 0xbb, 0x3d} + }; + + if (copy_from_user(client_context, argp, sizeof(*client_context))) { + tloge("copy from user failed\n"); + return -ENOMEM; + } + + if (!tc_user_param_valid(client_context, 0)) { + tloge("param 0 is invalid\n"); + return -EFAULT; + } + + /* a_addr contain the command id */ + if (copy_from_user(cmd_id, + (void *)(uintptr_t)client_context->params[0].value.a_addr, + sizeof(*cmd_id))) { + tloge("copy from user failed:cmd_id\n"); + return -ENOMEM; + } + + if (memcmp((char *)client_context->uuid, (char *)&secure_timer_uuid, + sizeof(struct tc_uuid))) { + tloge("request not from secure_timer\n"); + tloge("request uuid: %x %x %x %x\n", + *(client_context->uuid + 0), + *(client_context->uuid + 1), + *(client_context->uuid + 2), + *(client_context->uuid + 3)); + /* just wanna print the first four characters of uuid */ + return -EACCES; + } + + return 0; +} + +int tc_ns_tst_cmd(void *argp) +{ + struct tc_ns_client_context client_context; + int ret; + int cmd_id; + int timer_type; + + if (!argp) { + tloge("argp is NULL input buffer\n"); + return -EINVAL; + } + + ret = init_tst_test_context(&client_context, argp, &cmd_id); + if (ret) + return ret; + + switch (cmd_id) { + case TST_CMD_01: + callback_demo_main((char *)client_context.uuid); + break; + case TST_CMD_02: + tst_get_timer_type(&timer_type); + if (!tc_user_param_valid(&client_context, (unsigned int)1)) { + tloge("param 1 is invalid\n"); + ret = -EINVAL; + return ret; + } + if (copy_to_user( + (void *)(uintptr_t) + client_context.params[1].value.a_addr, + &timer_type, sizeof(timer_type))) { + tloge("copy to user failed:timer_type\n"); + ret = -ENOMEM; + return ret; + } + break; + default: + ret = -EINVAL; + return ret; + } + if (copy_to_user(argp, &client_context, sizeof(client_context))) { + tloge("copy to user failed:client context\n"); + ret = -ENOMEM; + return ret; + } + return ret; +} + +#else + +int tc_ns_tst_cmd(void *argp) +{ + (void)argp; + tloge("usr img do not support this cmd\n"); + return 0; +} + +#endif + +static int send_notify_cmd(unsigned int cmd_id) +{ + struct tc_ns_smc_cmd smc_cmd = { {0}, 0 }; + int ret = 0; + struct mb_cmd_pack *mb_pack = NULL; + + mb_pack = mailbox_alloc_cmd_pack(); + if (!mb_pack) + 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(g_notify_data); + mb_pack->operation.params[0].value.b = 0; + mb_pack->operation.params[1].value.a = SZ_4K; + 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 = 0; + + if (tc_ns_smc(&smc_cmd)) { + ret = -EPERM; + tloge("register notify mem failed\n"); + } + mailbox_free(mb_pack); + + return ret; +} + +static int config_spi_context(void) +{ + unsigned int irq; + int ret; + + /* Map IRQ 0 from the OF interrupts list */ + irq = NUM_HAL_INTERRUPT_TEE_SPI_NOTIFY; + ret = devm_request_irq(irq, tc_secure_notify, + IRQF_NO_SUSPEND, TC_NS_CLIENT_DEV, NULL); + if (ret < 0) { + tloge("device irq %u request failed %u", irq, ret); + return ret; + } + + g_ta_callback_func_list.callback_count = 0; + INIT_LIST_HEAD(&g_ta_callback_func_list.callback_list); + mutex_init(&g_ta_callback_func_list.callback_list_lock); + + return 0; +} + +int tz_spi_init() +{ + int ret; + + g_tz_spi_wq = alloc_ordered_workqueue("g_tz_spi_wq", WQ_HIGHPRI); + if (!g_tz_spi_wq) { + tloge("it failed to create workqueue g_tz_spi_wq\n"); + return -ENOMEM; + } + + ret = config_spi_context(); + if (ret) + goto clean; + + if (!g_notify_data) { +#ifdef CONFIG_BIG_SESSION + /* we should map at least 3 pages for 100 sessions, 2^2 > 3 */ + g_notify_data = (struct notify_data_struct *) + (uintptr_t)__get_free_pages( + GFP_KERNEL | __GFP_ZERO, CONFIG_NOTIFY_PAGE_ORDER); +#else + g_notify_data = (struct notify_data_struct *) + (uintptr_t)__get_free_page(GFP_KERNEL | __GFP_ZERO); +#endif + if (!g_notify_data) { + tloge("get free page failed for notification data\n"); + ret = -ENOMEM; + goto clean; + } + + (void)memset_s(g_notify_data, sizeof(*g_notify_data), 0, sizeof(struct notify_data_struct)); + + ret = send_notify_cmd(GLOBAL_CMD_ID_REGISTER_NOTIFY_MEMORY); + if (ret) { + tloge("shared memory failed ret is 0x%x\n", ret); + ret = -EFAULT; + free_page(g_notify_data); + g_notify_data = NULL; + goto clean; + } + + g_notify_data_entry_shadow = + &g_notify_data->entry[NOTIFY_DATA_ENTRY_SHADOW - 1]; + tlogi("target is: %llx\n", + g_notify_data_entry_shadow->context.shadow.target_tcb); + } + g_spi_inited = 1; + return 0; +clean: + tz_spi_exit(); + return ret; +} + +void tz_spi_exit(void) +{ + g_spi_inited = 0; + if (g_notify_data) { + if (send_notify_cmd(GLOBAL_CMD_ID_UNREGISTER_NOTIFY_MEMORY)) + tloge("unregister notify data mem failed\n"); +#ifdef CONFIG_BIG_SESSION + free_pages(g_notify_data, + CONFIG_NOTIFY_PAGE_ORDER); +#else + free_page(g_notify_data); +#endif + g_notify_data = NULL; + } + + if (g_tz_spi_wq) { + destroy_workqueue(g_tz_spi_wq); + g_tz_spi_wq = NULL; + } +} diff --git a/liteos/core/tz_spi_notify.h b/liteos/core/tz_spi_notify.h new file mode 100644 index 0000000..1026b06 --- /dev/null +++ b/liteos/core/tz_spi_notify.h @@ -0,0 +1,25 @@ +/* + * tz_spi_notify.h + * + * 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. + */ +#ifndef TZ_SPI_NOTIFY_H +#define TZ_SPI_NOTIFY_H +#include "teek_ns_client.h" + +int tz_spi_init(void); +void tz_spi_exit(void); +int tc_ns_tst_cmd(void *argp); + +#endif diff --git a/liteos/core/tzdebug.c b/liteos/core/tzdebug.c new file mode 100644 index 0000000..1d21d21 --- /dev/null +++ b/liteos/core/tzdebug.c @@ -0,0 +1,315 @@ +/* + * tzdebug.c + * + * for tzdriver debug + * + * 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 "tzdebug.h" +#include "tc_ns_log.h" +#include "tc_ns_client.h" +#include "tc_client_driver.h" +#include "teek_ns_client.h" +#include "smc_smp.h" +#include "teek_client_constants.h" +#include "mailbox_mempool.h" +#include "tlogger.h" +#include "session_manager.h" +#include "cmdmonitor.h" + +#define DEBUG_OPT_LEN 128 + +#ifdef CONFIG_TA_MEM_INUSE_ONLY +#define TA_MEMSTAT_ALL 0 +#else +#define TA_MEMSTAT_ALL 1 +#endif + +typedef void (*tzdebug_opt_func)(const char *param); + +struct opt_ops { + char *name; + tzdebug_opt_func func; +}; + +static mutex_t g_meminfo_lock = PTHREAD_MUTEX_INITIALIZER; +static struct tee_mem g_tee_meminfo; +static void tzmemdump(const char *param); +static int send_dump_mem(int flag, int history, const struct tee_mem *statmem) +{ + struct tc_ns_smc_cmd smc_cmd = { {0}, 0 }; + struct mb_cmd_pack *mb_pack = NULL; + int ret = 0; + + if (!statmem) { + tloge("statmem is NULL\n"); + return -EINVAL; + } + mb_pack = mailbox_alloc_cmd_pack(); + if (!mb_pack) + return -ENOMEM; + + smc_cmd.cmd_id = GLOBAL_CMD_ID_DUMP_MEMINFO; + smc_cmd.cmd_type = CMD_TYPE_GLOBAL; + mb_pack->operation.paramtypes = teec_param_types( + TEE_PARAM_TYPE_MEMREF_INOUT, TEE_PARAM_TYPE_VALUE_INPUT, + TEE_PARAM_TYPE_NONE, TEE_PARAM_TYPE_NONE); + mb_pack->operation.params[0].memref.buffer = virt_to_phys((void *)statmem); + mb_pack->operation.params[0].memref.size = sizeof(*statmem); + mb_pack->operation.buffer_h_addr[0] = 0; + mb_pack->operation.params[1].value.a = flag; + mb_pack->operation.params[1].value.b = history; + smc_cmd.operation_phys = + (unsigned int)virt_to_phys((void *)&mb_pack->operation); + smc_cmd.operation_h_phys = 0; + + if (tc_ns_smc(&smc_cmd)) { + ret = -EPERM; + tloge("send dump mem failed\n"); + } + mailbox_free(mb_pack); + return ret; +} + +/* get meminfo (tee_mem + N * ta_mem < 4Kbyte) from tee */ +static int get_tee_meminfo_cmd(void) +{ + int ret; + struct tee_mem *mem = NULL; + + mem = mailbox_alloc(sizeof(*mem), MB_FLAG_ZERO); + if (!mem) + return -ENOMEM; + + ret = send_dump_mem(0, TA_MEMSTAT_ALL, mem); + if (ret) { + tloge("send dump failed\n"); + mailbox_free(mem); + return ret; + } + + mutex_lock(&g_meminfo_lock); + ret = memcpy_s(&g_tee_meminfo, sizeof(g_tee_meminfo), mem, sizeof(*mem)); + if (ret) + tloge("memcpy failed\n"); + mutex_unlock(&g_meminfo_lock); + mailbox_free(mem); + + return ret; +} + +static atomic_t g_cmd_send = ATOMIC_INIT(1); + +void set_cmd_send_state(void) +{ + atomic_set(&g_cmd_send, 1); +} + +int get_tee_meminfo(struct tee_mem *meminfo) +{ + errno_t s_ret; + + if (!meminfo) + return -EINVAL; + + if (atomic_read(&g_cmd_send)) { + if (get_tee_meminfo_cmd()) + return -EFAULT; + } else { + atomic_set(&g_cmd_send, 0); + } + + mutex_lock(&g_meminfo_lock); + s_ret = memcpy_s(meminfo, sizeof(*meminfo), + &g_tee_meminfo, sizeof(g_tee_meminfo)); + mutex_unlock(&g_meminfo_lock); + if (s_ret) + return -1; + + return 0; +} + +static void archivelog(const char *param) +{ + (void)param; + tzdebug_archivelog(); +} + +static void tzdump(const char *param) +{ + (void)param; + show_cmd_bitmap(); + wakeup_tc_siq(); +} + +static void tzmemdump(const char *param) +{ + struct tee_mem *mem = NULL; + + (void)param; + mem = mailbox_alloc(sizeof(*mem), MB_FLAG_ZERO); + if (!mem) { + tloge("mailbox alloc failed\n"); + return; + } + + if (send_dump_mem(1, 1, mem)) + tloge("send dump mem failed\n"); + mailbox_free(mem); +} + +static void tzmemstat(const char *param) +{ + (void)param; + tzdebug_memstat(); +} + +static void tzlogwrite(const char *param) +{ + (void)param; +} + +#define OFFSET_VALUE_BIT 8U +static void get_value(const char *param, uint32_t *value, uint32_t *index) +{ + uint32_t i; + uint32_t val = 0; + + for (i = 0; i < OFFSET_VALUE_BIT; i++) { + if (param[i] > '9' || param[i] < '0') { + *value = val; + *index = i; + return; + } + val = (val * 10) + param[i] - '0'; + } + + *value = val; + *index = i; + return; +} + +#define MAX_CMD_NUM 3 +#define MAX_CMD_LINE 20 +#define MAX_PARAM_LINE 60 +#define MAX_MEM_SIZE 0x4000U +#define MAX_VALUE_LEN 8 +#define MAX_ADDRSTR_LEN 18 + +static struct opt_ops g_opt_arr[] = { + {"archivelog", archivelog}, + {"dump", tzdump}, + {"memdump", tzmemdump}, + {"logwrite", tzlogwrite}, + {"dump_service", dump_services_status}, + {"memstat", tzmemstat}, +}; + +static ssize_t tz_dbg_opt_write(struct file *filp, + const char __user *ubuf, size_t cnt) +{ + char buf[128] = {0}; + char *value = NULL; + char *p = NULL; + uint32_t i = 0; + + if (!ubuf || !filp) + return -EINVAL; + + if (cnt >= sizeof(buf)) + return -EINVAL; + + if (!cnt) + return -EINVAL; + + if (copy_from_user(buf, ubuf, cnt)) + return -EFAULT; + + buf[cnt] = 0; + if (cnt > 0 && buf[cnt - 1] == '\n') + buf[cnt - 1] = 0; + value = buf; + p = strsep(&value, ":"); /* when buf has no :, value may be NULL */ + if (!p) + return -EINVAL; + + for (i = 0; i < ARRAY_SIZE(g_opt_arr); i++) { + if (!strncmp(p, g_opt_arr[i].name, + strlen(g_opt_arr[i].name)) && + strlen(p) == strlen(g_opt_arr[i].name)) { + g_opt_arr[i].func(value); + return cnt; + } + } + return -EFAULT; +} + +#ifdef CONFIG_MEMSTAT_DEBUGFS +static int memstat_debug_show(struct seq_file *m, void *v) +{ + struct tee_mem *mem_stat = NULL; + int ret; + uint32_t i; + + mem_stat = kzalloc(sizeof(*mem_stat), GFP_KERNEL); + if (!mem_stat) + return -ENOMEM; + + ret = get_tee_meminfo(mem_stat); + if (ret != 0) { + tloge("get tee meminfo failed\n"); + kfree(mem_stat); + mem_stat = NULL; + return -EINVAL; + } + + seq_printf(m, "TotalMem:%u Pmem:%u Free_Mem:%u Free_Mem_Min:%u TA_Num:%u\n", + mem_stat->total_mem, mem_stat->pmem, mem_stat->free_mem, mem_stat->free_mem_min, mem_stat->ta_num); + + for (i = 0; i < mem_stat->ta_num; i++) + seq_printf(m, "ta_name:%s ta_pmem:%u pmem_max:%u pmem_limit:%u\n", + mem_stat->ta_mem_info[i].ta_name, mem_stat->ta_mem_info[i].pmem, + mem_stat->ta_mem_info[i].pmem_max, mem_stat->ta_mem_info[i].pmem_limit); + + kfree(mem_stat); + mem_stat = NULL; + return 0; +} + +static int tz_memstat_open(struct inode *inode, struct file *file) +{ + return single_open(file, memstat_debug_show, NULL); +} + +static const struct file_operations g_tz_dbg_memstat_fops = { + .open = tz_memstat_open, +}; +#endif + +static const struct file_operations_vfs g_tz_dbg_opt_fops = { + .write = tz_dbg_opt_write, +}; + +#define TC_NS_CLIENT_TZDEBUG "/dev/tzdebug" +int tzdebug_init(void) +{ + int ret = create_tc_client_device(TC_NS_CLIENT_TZDEBUG, &g_tz_dbg_opt_fops); + if (ret != EOK) + return ret; + + return 0; +} + +void tzdebug_exit(void) +{ + +} diff --git a/liteos/core/tzdebug.h b/liteos/core/tzdebug.h new file mode 100644 index 0000000..bb3be10 --- /dev/null +++ b/liteos/core/tzdebug.h @@ -0,0 +1,43 @@ +/* + * tzdebug.h + * + * for tzdriver debug + * + * 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 TZDEBUG_H +#define TZDEBUG_H + +#include +struct ta_mem { + char ta_name[64]; + uint32_t pmem; + uint32_t pmem_max; + uint32_t pmem_limit; +}; + +#define MEMINFO_TA_MAX 100 +struct tee_mem { + uint32_t total_mem; + uint32_t pmem; + uint32_t free_mem; + uint32_t free_mem_min; + uint32_t ta_num; + struct ta_mem ta_mem_info[MEMINFO_TA_MAX]; +}; + +int get_tee_meminfo(struct tee_mem *meminfo); +void tee_dump_mem(void); +int tzdebug_init(void); +void tzdebug_exit(void); + +#endif diff --git a/liteos/include/los_adapt.h b/liteos/include/los_adapt.h new file mode 100644 index 0000000..a44cbd3 --- /dev/null +++ b/liteos/include/los_adapt.h @@ -0,0 +1,363 @@ +/* + * los_adapt.h + * + * macros and interfaces for liteos 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 LOS_ADAPT_H +#define LOS_ADAPT_H + +#include +#include +#include +#include "arm.h" +#include "fs/fs.h" +#include "fs_poll_pri.h" +#include "hisoc/random.h" +#include "los_process_pri.h" +#include "los_task.h" +#include "los_vm_lock.h" +#include "los_vm_map.h" +#include "los_vm_phys.h" +#include "mbedtls/sha256.h" + +#define TEECD_UID 97 + +#define VERIFY_READ 0 +#define VERIFY_WRITE 1 +#define MAX_DEV_NAME_SIZE 32 +#define SHA256_DIGEST_LENGTH 32 +#define ALIGN_TZ(x, boundary) (((x) + ((boundary) - 1)) & ~((boundary) - 1)) + +#define MISC_DYNAMIC_MINOR 255 + +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) +#endif + +#define LIBTEEC_SO "/vendor/lib/libteec_vendor.so" + +struct miscdevice_adapt { + int minor; + char *name; + const struct file_operations_vfs *op; +}; + +int misc_register_adapt(struct miscdevice_adapt *misc_device); + +typedef pthread_mutex_t mutex_t; + +#define MAX_PATH_SIZE 512 + +#ifndef IS_ERR_OR_NULL +#ifndef IS_ERR_VALUE +#define IS_ERR_VALUE(x) unlikely((unsigned long)(void *)(x) >= (unsigned long) - 4095) +#endif +#define IS_ERR_OR_NULL(x) ((!x) || IS_ERR_VALUE((uintptr_t)x)) +#endif + +#define TEE_DEV_PRI 0660 + +#define TASK_COMM_LEN OS_TCB_NAME_LEN + +#define WQ_HIGHPRI (1 << 4) +#define IRQF_NO_SUSPEND 0x00004000 +#define __GFP_ZERO 0x8000u + +#define SZ_4K 0x1000UL +#define SZ_1M (1024 * 1024) +#define SZ_4M (4 * SZ_1M) +#define SZ_8M (8 * SZ_1M) + +#define MAX_POW_TWO(n) \ +( \ +((n) >> 31) ? 31 : ((n) >> 30) ? 30 : \ +((n) >> 29) ? 29 : ((n) >> 28) ? 28 : \ +((n) >> 27) ? 27 : ((n) >> 26) ? 26 : \ +((n) >> 25) ? 25 : ((n) >> 25) ? 25 : \ +((n) >> 23) ? 23 : ((n) >> 22) ? 22 : \ +((n) >> 21) ? 21 : ((n) >> 20) ? 20 : \ +((n) >> 19) ? 19 : ((n) >> 18) ? 18 : \ +((n) >> 17) ? 17 : ((n) >> 16) ? 16 : \ +((n) >> 15) ? 15 : ((n) >> 14) ? 14 : \ +((n) >> 13) ? 13 : ((n) >> 12) ? 12 : \ +((n) >> 11) ? 11 : ((n) >> 10) ? 10 : \ +((n) >> 9) ? 9: ((n) >> 8) ? 8 : \ +((n) >> 7) ? 7: ((n) >> 6) ? 6 : \ +((n) >> 5) ? 5: ((n) >> 4) ? 4 : \ +((n) >> 3) ? 3: ((n) >> 2) ? 2 : 1) + +#define GET_ORDER(n) \ +( \ + n <= PAGE_SIZE ? 0 : (MAX_POW_TWO(n - 1) - PAGE_SHIFT + 1) \ +) + +#ifndef MSEC_PER_SEC +#define MSEC_PER_SEC 1000 +#endif + +#ifndef NSEC_PER_MSEC +#define NSEC_PER_MSEC 1000000L +#endif + +#ifndef USEC_PER_SEC +#define USEC_PER_SEC 1000000L +#endif + +#ifndef NSEC_PER_USEC +#define NSEC_PER_USEC 1000 +#endif + +#define CRASH_RET_EXIT 0 +#define CRASH_RET_TA 1 +#define CRASH_RET_IP 2 + +#define INIT_WORK_ONSTACK(_work, _func) \ +do { \ + INIT_WORK(_work, _func); \ +} while (0) + +#define __WORK_INIT(n, f) { \ + .data = 0, \ + .entry = { &(n).entry, &(n).entry }, \ + .func = f \ +} +#define DECLARE_WORK(work, func) \ + struct work_struct work = __WORK_INIT(work, func); + +#define noinline __attribute__((noinline)) + +struct aes_param { + unsigned char *iv; + const unsigned char *key; + int size; + unsigned int encrypto_type; +}; + +bool schedule_work_on(int cpu, struct work_struct *work); +LosTaskCB *kthread_run(int (*threadfn)(uintptr_t data, int data_len), void *data, int len, char *name); +void kthread_stop(const LosTaskCB *k); +int kthread_should_stop(void); +int32_t do_vmalloc_remap(LosVmMapRegion *vma, void *kvaddr); +int remap_vmalloc_range(LosVmMapRegion *vma, void *addr, unsigned long pgoff); +int create_tc_client_device(const char *dev_name, const struct file_operations_vfs *op); +ssize_t simple_read_from_buffer(void *to, size_t count, const void *from, size_t available); +LosVmPage *mailbox_pool_alloc_pages(unsigned int order); +void mailbox_pool_free_pages(LosVmPage *page_array, size_t order); +char *get_process_path(LosTaskCB *task, char *tpath, int path_len); +int calc_task_so_hash(unsigned char *digest, uint32_t dig_len, LosTaskCB *cur_struct, int so_index); +LosTaskCB *get_teecd_task(void); +void set_teecd_task(LosTaskCB* task); +int crypto_aescbckey256(unsigned char *output, const unsigned char *input, struct aes_param *param); +void check_teecd_process(void); + +#define INT_SIZE 4 + +static inline struct workqueue_struct *alloc_ordered_workqueue(const char *fmt, unsigned int flags) +{ + return create_workqueue((char *)fmt); +} + +static inline int access_ok(int type, unsigned long ptr, unsigned int size) +{ + if (ptr + size < ptr) + return false; + return LOS_IsUserAddress(ptr + size); +} + +static inline int get_task_uid(LosTaskCB *task) +{ +#ifdef LOSCFG_SECURITY_CAPABILITY + int int_save = LOS_IntLock(); + int uid = -1; + + LosProcessCB *process = OS_PCB_FROM_PID(task->processID); + if (process->user) + uid = process->user->userID; + LOS_IntRestore(int_save); + return uid; +#else + return 0; +#endif +} + +static inline int devm_request_irq(unsigned int irq, irq_handler_t handler, + unsigned long irq_flags, const char *devname, void *dev_id) +{ + return request_irq(irq, handler, irq_flags, devname, NULL); +} + +static inline void *get_phy_page(void) +{ + LosVmPage *page = LOS_PhysPageAlloc(); + if (page == NULL) + return NULL; + return OsVmPageToVaddr(page); +} + +static inline void *get_phy_pages(uint32_t size) +{ + void *page = LOS_PhysPagesAllocContiguous(ROUNDUP(size, PAGE_SIZE) >> PAGE_SHIFT); + return page; +} + +static inline void free_phy_page(void *ptr) +{ + if (ptr == NULL) + return; + LosVmPage *page = OsVmVaddrToPage(ptr); + if (page != NULL) + LOS_PhysPageFree(page); +} + +static inline void kthread_bind_mask(LosTaskCB *p, UINT16 mask) +{ + if (p == NULL) + return; + LOS_TaskCpuAffiSet(p->taskID, mask); +} + +static inline void preempt_disable(void) +{ + uint32_t int_save = LOS_IntLock(); + OsPercpuGet()->taskLockCnt++; + LOS_IntRestore(int_save); +} + +static inline void preempt_enable(void) +{ + uint32_t int_save = LOS_IntLock(); + OsPercpuGet()->taskLockCnt--; + LOS_IntRestore(int_save); +} + +static inline int cmpxchg(unsigned int *lock, int old, int new) +{ + return LOS_AtomicCmpXchg32bits((Atomic *)lock, new, old); +} + +static inline int raw_smp_processor_id(void) +{ + return ArchCurrCpuid(); +} + +static inline int wake_up_process(LosTaskCB *p) +{ + LOS_TaskYield(); + return 0; +} + +static inline void get_user(unsigned int *value, const unsigned int *user_ptr) +{ + copy_from_user(value, user_ptr, sizeof(unsigned int)); +} + +static inline int get_current_pid(void) +{ + return OsCurrTaskGet()->taskID; +} + +/* unsupport restart syscall */ +static inline int restart_syscall(void) +{ + return 0; +} + +static inline LosTaskCB *get_process_group_leader(LosTaskCB *task) +{ + if (task == NULL) + return NULL; + return OS_TCB_FROM_TID(OS_PCB_FROM_PID(task->processID)->threadGroupID); +} + +static inline unsigned long msecs_to_jiffies(const unsigned int m) +{ + if ((int)m < 0) + return 0; + + return (m + (MSEC_PER_SEC / HZ) - 1) / (MSEC_PER_SEC / HZ); +} + +static inline struct timespec current_kernel_time(void) +{ + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC_RAW, &ts); + return ts; +} + +static inline void init_deferrable_work(struct delayed_work *w, void(* wq)(struct work_struct *)) +{ + INIT_DELAYED_WORK(w, wq); +} + +static inline int is_kernel_thread(LosTaskCB *task) +{ + if (task == NULL) + return true; + return (OS_PCB_FROM_PID(task->processID)->processMode == OS_KERNEL_MODE); +} + +static inline int is_teecd_process(LosTaskCB *teecd, LosTaskCB *task) +{ + if (teecd == NULL || task == NULL) + return 0; + return teecd->processID == task->processID; +} + +typedef mbedtls_sha256_context tee_sha256_context; + +static inline void init_tee_sha256(tee_sha256_context *ctx) +{ + mbedtls_sha256_init(ctx); + (void)mbedtls_sha256_starts_ret(ctx, 0); +} + +static inline void update_tee_sha256(tee_sha256_context *ctx, const unsigned char *input, size_t ilen) +{ + (void)mbedtls_sha256_update_ret(ctx, input, ilen); +} + +static inline void finish_tee_sha256(tee_sha256_context *ctx, unsigned char output[32]) +{ + (void)mbedtls_sha256_finish_ret(ctx, output); +} + +#undef kmalloc +#define kmalloc(size, flags) malloc(size) + +#undef kzalloc +#define kzalloc(size, flags) calloc(1, size) + +#undef kfree +#define kfree(ptr) free(ptr) + +#define virt_to_phys(addr) LOS_PaddrQuery(addr) + +#define phys_to_virt(addr) LOS_PaddrToKVaddr(addr) + +#define virt_addr_valid(addr) LOS_PaddrQuery(addr) + +#define __get_free_page(flags) get_phy_page() + +#define __get_free_pages(flags, size) get_phy_pages(size) + +#define free_pages(page, flags) free_phy_page(page) +#define __free_pages(page, flags) free_phy_page(page) +#define free_page(page) free_phy_page(page) + +#define page_address(page) OsVmPageToVaddr(page) + +#define __init + +#endif diff --git a/liteos/include/tc_ns_client.h b/liteos/include/tc_ns_client.h new file mode 100644 index 0000000..4d0ab19 --- /dev/null +++ b/liteos/include/tc_ns_client.h @@ -0,0 +1,173 @@ +/* + * tc_ns_client.h + * + * data structure declaration for nonsecure world + * + * 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_NS_CLIENT_H +#define TC_NS_CLIENT_H + +#include "teek_client_type.h" +#include "los_adapt.h" + +#define UUID_LEN 16 +#define PARAM_NUM 4 +#define ADDR_TRANS_NUM 32 + +#define teec_param_types(param0_type, param1_type, param2_type, param3_type) \ + ((param3_type) << 12 | (param2_type) << 8 | \ + (param1_type) << 4 | (param0_type)) + +#define teec_param_type_get(param_types, index) \ + (((param_types) >> ((index) << 2)) & 0x0F) + +#ifndef ZERO_SIZE_PTR +#define ZERO_SIZE_PTR ((void *)16) +#define ZERO_OR_NULL_PTR(x) ((unsigned long)(x) <= (unsigned long)ZERO_SIZE_PTR) +#endif + +struct tc_ns_client_login { + uint32_t method; + uint32_t mdata; +}; + +union tc_ns_client_param { + struct { + unsigned int buffer; + unsigned int buffer_h_addr; + unsigned int offset; + unsigned int h_offset; + unsigned int size_addr; + unsigned int size_h_addr; + } memref; + struct { + unsigned int a_addr; + unsigned int a_h_addr; + unsigned int b_addr; + unsigned int b_h_addr; + } value; +}; + +struct tc_ns_client_return { + int code; + uint32_t origin; +}; + +struct tc_ns_client_context { + unsigned char uuid[UUID_LEN]; + uint32_t session_id; + uint32_t cmd_id; + struct tc_ns_client_return returns; + struct tc_ns_client_login login; + union tc_ns_client_param params[PARAM_NUM]; + uint32_t param_types; + bool started; + uint32_t calling_pid; + unsigned int file_size; + union { + char *file_buffer; + unsigned long long file_addr; + }; +}; + +struct tc_ns_client_time { + uint32_t seconds; + uint32_t millis; +}; + +enum secfile_type_t { + LOAD_TA = 0, + LOAD_SERVICE, + LOAD_LIB, + LOAD_DYNAMIC_DRV, +}; + +struct load_secfile_ioctl_struct { + enum secfile_type_t secfile_type; + unsigned char uuid[UUID_LEN]; + uint32_t file_size; + union { + char *file_buffer; + unsigned long long file_addr; + }; +}; + +struct agent_ioctl_args { + uint32_t id; + uint32_t buffer_size; + union { + void *buffer; + unsigned long long addr; + }; +}; + +#define vmalloc_addr_valid(kaddr) \ + (((void *)(kaddr) >= (void *)VMALLOC_START) && \ + ((void *)(kaddr) < (void *)VMALLOC_END)) + +#define modules_addr_valid(kaddr) \ + (((void *)(kaddr) >= (void *)MODULES_VADDR) && \ + ((void *)(kaddr) < (void *)MODULES_END)) + +#define TST_CMD_01 1 +#define TST_CMD_02 2 +#define TST_CMD_03 3 +#define TST_CMD_04 4 +#define TST_CMD_05 5 + +#define MAX_SHA_256_SZ 32 + +#define TC_NS_CLIENT_IOCTL_SES_OPEN_REQ \ + _IOW(TC_NS_CLIENT_IOC_MAGIC, 1, struct tc_ns_client_context) +#define TC_NS_CLIENT_IOCTL_SES_CLOSE_REQ \ + _IOWR(TC_NS_CLIENT_IOC_MAGIC, 2, struct tc_ns_client_context) +#define TC_NS_CLIENT_IOCTL_SEND_CMD_REQ \ + _IOWR(TC_NS_CLIENT_IOC_MAGIC, 3, struct tc_ns_client_context) +#define TC_NS_CLIENT_IOCTL_SHRD_MEM_RELEASE \ + _IOWR(TC_NS_CLIENT_IOC_MAGIC, 4, unsigned int) +#define TC_NS_CLIENT_IOCTL_WAIT_EVENT \ + _IO(TC_NS_CLIENT_IOC_MAGIC, 5) +#define TC_NS_CLIENT_IOCTL_SEND_EVENT_RESPONSE \ + _IO(TC_NS_CLIENT_IOC_MAGIC, 6) +#define TC_NS_CLIENT_IOCTL_REGISTER_AGENT \ + _IOWR(TC_NS_CLIENT_IOC_MAGIC, 7, struct agent_ioctl_args) +#define TC_NS_CLIENT_IOCTL_UNREGISTER_AGENT \ + _IO(TC_NS_CLIENT_IOC_MAGIC, 8) +#define TC_NS_CLIENT_IOCTL_LOAD_APP_REQ \ + _IOWR(TC_NS_CLIENT_IOC_MAGIC, 9, struct load_secfile_ioctl_struct) +#define TC_NS_CLIENT_IOCTL_NEED_LOAD_APP \ + _IOWR(TC_NS_CLIENT_IOC_MAGIC, 10, struct tc_ns_client_context) +#define TC_NS_CLIENT_IOCTL_ALLOC_EXCEPTING_MEM \ + _IOWR(TC_NS_CLIENT_IOC_MAGIC, 12, unsigned int) +#define TC_NS_CLIENT_IOCTL_CANCEL_CMD_REQ \ + _IOWR(TC_NS_CLIENT_IOC_MAGIC, 13, struct tc_ns_client_context) +#define TC_NS_CLIENT_IOCTL_LOGIN \ + _IO(TC_NS_CLIENT_IOC_MAGIC, 14) +#define TC_NS_CLIENT_IOCTL_TST_CMD_REQ \ + _IOWR(TC_NS_CLIENT_IOC_MAGIC, 15, int) +#define TC_NS_CLIENT_IOCTL_TUI_EVENT \ + _IOWR(TC_NS_CLIENT_IOC_MAGIC, 16, int) +#define TC_NS_CLIENT_IOCTL_SYC_SYS_TIME \ + _IOWR(TC_NS_CLIENT_IOC_MAGIC, 17, struct tc_ns_client_time) +#define TC_NS_CLIENT_IOCTL_SET_NATIVE_IDENTITY \ + _IOWR(TC_NS_CLIENT_IOC_MAGIC, 18, int) +#define TC_NS_CLIENT_IOCTL_LOAD_TTF_FILE_AND_NOTCH_HEIGHT \ + _IOWR(TC_NS_CLIENT_IOC_MAGIC, 19, unsigned int) +#define TC_NS_CLIENT_IOCTL_LATEINIT \ + _IO(TC_NS_CLIENT_IOC_MAGIC, 20) +#define TC_NS_CLIENT_IOCTL_GET_TEE_VERSION \ + _IOWR(TC_NS_CLIENT_IOC_MAGIC, 21, unsigned int) +#define TC_NS_CLIENT_IOCTL_UNMAP_SHARED_MEM \ + _IOWR(TC_NS_CLIENT_IOC_MAGIC, 22, unsigned int) + +#endif diff --git a/liteos/include/tc_ns_log.h b/liteos/include/tc_ns_log.h new file mode 100644 index 0000000..55fc188 --- /dev/null +++ b/liteos/include/tc_ns_log.h @@ -0,0 +1,69 @@ +/* + * tc_ns_log.h + * + * log func 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 TC_NS_LOG_H +#define TC_NS_LOG_H + +#include +enum { + TZ_DEBUG_VERBOSE = 0, + TZ_DEBUG_DEBUG, + TZ_DEBUG_INFO, + TZ_DEBUG_WARN, + TZ_DEBUG_ERROR, +}; +#define MOD_TEE "tzdriver" + +#ifdef DEF_ENG +#define TEE_ENG_LOG_MASK 2 +#define TEE_LOG_MASK TEE_ENG_LOG_MASK +#else +#define TEE_USR_LOG_MASK 3 +#define TEE_LOG_MASK TEE_USR_LOG_MASK +#endif + +#define tlogv(fmt, args...) \ +do { \ + if (TZ_DEBUG_VERBOSE >= TEE_LOG_MASK) \ + pr_info("([%s] %i, %s)%s: " fmt, MOD_TEE, OsCurrTaskGet()->taskID, OsCurrTaskGet()->taskName, __func__, ## args); \ +} while (0) + + +#define tlogd(fmt, args...) \ +do { \ + if (TZ_DEBUG_DEBUG >= TEE_LOG_MASK) \ + pr_info("([%s] %i, %s)%s: " fmt, MOD_TEE, OsCurrTaskGet()->taskID, OsCurrTaskGet()->taskName, __func__, ## args); \ +} while (0) + + +#define tlogi(fmt, args...) \ +do { \ + if (TZ_DEBUG_INFO >= TEE_LOG_MASK) \ + pr_info("([%s] %i, %s)%s: " fmt, MOD_TEE, OsCurrTaskGet()->taskID, OsCurrTaskGet()->taskName, __func__, ## args); \ +} while (0) + + +#define tlogw(fmt, args...) \ +do { \ + if (TZ_DEBUG_WARN >= TEE_LOG_MASK) \ + pr_warn("([%s] %i, %s)%s: " fmt, MOD_TEE, OsCurrTaskGet()->taskID, OsCurrTaskGet()->taskName, __func__, ## args); \ +} while (0) + + +#define tloge(fmt, args...) \ + pr_err("([%s] %i, %s)%s: " fmt, MOD_TEE, OsCurrTaskGet()->taskID, OsCurrTaskGet()->taskName, __func__, ## args) + +#endif diff --git a/liteos/include/teek_client_api.h b/liteos/include/teek_client_api.h new file mode 100644 index 0000000..f24b8e7 --- /dev/null +++ b/liteos/include/teek_client_api.h @@ -0,0 +1,122 @@ +/* + * teek_client_api.h + * + * function declaration 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. + */ + +#ifndef TEEK_CLIENT_API_H +#define TEEK_CLIENT_API_H +#include "teek_ns_client.h" +#include "teek_client_type.h" + +#define TEEC_PARAM_TYPES(param0_type, param1_type, param2_type, param3_type) \ + ((param3_type) << 12 | (param2_type) << 8 | \ + (param1_type) << 4 | (param0_type)) + +#define TEEC_PARAM_TYPE_GET(param_types, index) \ + (((param_types) >> ((index) << 2)) & 0x0F) + +#define TEEC_VALUE_UNDEF 0xFFFFFFFF + +#ifdef CONFIG_KERNEL_CLIENT + +/* + * for history reason, we supply two set interface + * first set is uncapitalized and satisfies kernel code rule + * second set is capitalized for compatibility + */ +int teek_is_agent_alive(unsigned int agent_id); + +uint32_t teek_initialize_context(const char *name, + struct teec_context *context); + +void teek_finalize_context(struct teec_context *context); + +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); + +void teek_close_session(struct teec_session *session); + +uint32_t teek_send_secfile(struct teec_session *session, + const char *file_buffer, unsigned int file_size); + +uint32_t teek_invoke_command(struct teec_session *session, + uint32_t cmd_id, struct teec_operation *operation, + uint32_t *return_origin); + +uint32_t teek_register_shared_memory(struct teec_context *context, + struct teec_sharedmemory *sharedmem); + +uint32_t teek_allocate_shared_memory(struct teec_context *context, + struct teec_sharedmemory *sharedmem); + +void teek_release_shared_memory(struct teec_sharedmemory *sharedmem); + +void teek_request_cancellation(struct teec_operation *operation); + +#else + +static inline int teek_is_agent_alive(unsigned int agent_id) +{ + return TEEC_SUCCESS; +} + +static inline uint32_t teek_initialize_context(const char *name, + struct teec_context *context) +{ + return TEEC_SUCCESS; +} + +static inline void teek_finalize_context(struct teec_context *context) +{ + (void)context; +} + +static inline 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) +{ + return TEEC_SUCCESS; +} + +static inline void teek_close_session(struct teec_session *session) +{ + (void)session; +} + +static inline uint32_t teek_invoke_command(struct teec_session *session, + uint32_t cmd_id, struct teec_operation *operation, + uint32_t *return_origin) +{ + return TEEC_SUCCESS; +} + +static inline uint32_t teek_send_secfile(struct teec_session *session, + const char *file_buffer, unsigned int file_size) +{ + return TEEC_SUCCESS; +} + +#endif + +#endif diff --git a/liteos/include/teek_client_constants.h b/liteos/include/teek_client_constants.h new file mode 100644 index 0000000..f78ae7b --- /dev/null +++ b/liteos/include/teek_client_constants.h @@ -0,0 +1,168 @@ +/* + * teek_client_constants.h + * + * macro declaration 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 + +#ifndef TEEK_CLIENT_CONSTANTS_H +#define TEEK_CLIENT_CONSTANTS_H + +enum global_service_cmd_id { + GLOBAL_CMD_ID_INVALID = 0x0, + GLOBAL_CMD_ID_BOOT_ACK = 0x1, + GLOBAL_CMD_ID_OPEN_SESSION = 0x2, + GLOBAL_CMD_ID_CLOSE_SESSION = 0x3, + GLOBAL_CMD_ID_LOAD_SECURE_APP = 0x4, + GLOBAL_CMD_ID_NEED_LOAD_APP = 0x5, + GLOBAL_CMD_ID_REGISTER_AGENT = 0x6, + GLOBAL_CMD_ID_UNREGISTER_AGENT = 0x7, + GLOBAL_CMD_ID_REGISTER_NOTIFY_MEMORY = 0x8, + GLOBAL_CMD_ID_UNREGISTER_NOTIFY_MEMORY = 0x9, + GLOBAL_CMD_ID_INIT_CONTENT_PATH = 0xa, + GLOBAL_CMD_ID_TERMINATE_CONTENT_PATH = 0xb, + GLOBAL_CMD_ID_ALLOC_EXCEPTION_MEM = 0xc, + GLOBAL_CMD_ID_TEE_TIME = 0xd, + GLOBAL_CMD_ID_TEE_INFO = 0xe, + GLOBAL_CMD_ID_REGISTER_LOG_MEM = 0xf, + GLOBAL_CMD_ID_KILL_TASK = 0x10, + GLOBAL_CMD_ID_TUI_EXCEPTION = 0x11, + GLOBAL_CMD_ID_ADJUST_TIME = 0x12, + GLOBAL_CMD_ID_SET_CA_HASH = 0x13, + /* set the Android's build version */ + GLOBAL_CMD_ID_SET_BUILD_VERSION = 0x14, + GLOBAL_CMD_ID_REGISTER_TTF_MEM = 0x15, + /* get session key for encrypting dialog */ + GLOBAL_CMD_ID_GET_SESSION_SECURE_PARAMS = 0x16, + GLOBAL_CMD_ID_REGISTER_MAILBOX = 0x17, + GLOBAL_CMD_ID_REGISTER_UNUSUAL_TTF_MEM = 0x18, + GLOBAL_CMD_ID_REGISTER_ION_MEM = 0x19, + GLOBAL_CMD_ID_DUMP_MEMINFO = 0x1a, + /* this cmd will be used to service no ca handle cmd */ + GLOBAL_CMD_ID_SET_SERVE_CMD = 0x1b, + GLOBAL_CMD_ID_ADD_DYNAMIC_ION = 0x1c, + GLOBAL_CMD_ID_DEL_DYNAMIC_ION = 0x1d, + GLOBAL_CMD_ID_LOAD_SECURE_APP_ION = 0x1e, + /* this cmd for tui to get notch_size */ + GLOBAL_CMD_ID_TUI_NOTCH = 0x1f, + GLOBAL_CMD_ID_LATE_INIT = 0x20, + /* this cmd for tui to get information of foldable screen */ + GLOBAL_CMD_ID_TUI_FOLD = 0x21, + GLOBAL_CMD_ID_GET_TEE_VERSION = 0x22, + GLOBAL_CMD_ID_UNKNOWN = 0x7FFFFFFE, + GLOBAL_CMD_ID_MAX = 0x7FFFFFFF +}; + +enum teec_result { + TEEC_SUCCESS = 0x0, + TEEC_ERROR_INVALID_CMD = 0x1, + TEEC_ERROR_SERVICE_NOT_EXIST = 0x2, + TEEC_ERROR_SESSION_NOT_EXIST = 0x3, + TEEC_ERROR_SESSION_MAXIMUM, + TEEC_ERROR_REGISTER_EXIST_SERVICE, + TEEC_ERROR_TAGET_DEAD_FATAL, + TEEC_ERROR_READ_DATA, + TEEC_ERROR_WRITE_DATA, + TEEC_ERROR_TRUNCATE_OBJECT, + TEEC_ERROR_SEEK_DATA, + TEEC_ERROR_RENAME_OBJECT, + TEEC_ERROR_TRUSTED_APP_LOAD_ERROR, + TEEC_ERROR_GENERIC = 0xFFFF0000, + TEEC_ERROR_ACCESS_DENIED = 0xFFFF0001, + TEEC_ERROR_CANCEL = 0xFFFF0002, + TEEC_ERROR_ACCESS_CONFLICT = 0xFFFF0003, + TEEC_ERROR_EXCESS_DATA = 0xFFFF0004, + TEEC_ERROR_BAD_FORMAT = 0xFFFF0005, + TEEC_ERROR_BAD_PARAMETERS = 0xFFFF0006, + TEEC_ERROR_BAD_STATE = 0xFFFF0007, + TEEC_ERROR_ITEM_NOT_FOUND = 0xFFFF0008, + TEEC_ERROR_NOT_IMPLEMENTED = 0xFFFF0009, + TEEC_ERROR_NOT_SUPPORTED = 0xFFFF000A, + TEEC_ERROR_NO_DATA = 0xFFFF000B, + TEEC_ERROR_OUT_OF_MEMORY = 0xFFFF000C, + TEEC_ERROR_BUSY = 0xFFFF000D, + TEEC_ERROR_COMMUNICATION = 0xFFFF000E, + TEEC_ERROR_SECURITY = 0xFFFF000F, + TEEC_ERROR_SHORT_BUFFER = 0xFFFF0010, + TEEC_PENDING = 0xFFFF2000, + TEEC_PENDING2 = 0xFFFF2001, + TEE_ERROR_TAGET_DEAD = 0xFFFF3024, + TEE_ERROR_GT_DEAD = 0xFFFF3124, + TEEC_ERROR_MAC_INVALID = 0xFFFF3071, + TEEC_CLIENT_INTR = 0xFFFF4000, + TEEC_ERROR_TUI_IN_USE = 0xFFFF7110, + TEEC_ERROR_TUI_SWITCH_CHANNAL, + TEEC_ERROR_TUI_CFG_DRIVER, + TEEC_ERROR_TUI_INVALID_EVENT, + TEEC_ERROR_TUI_POLL_EVENT, + TEEC_ERROR_TUI_CANCELED, + TEEC_ERROR_TUI_EXIT, + TEEC_ERROR_TUI_NOT_AVAILABLE, + TEEC_ERROR_SEC_FLASH_NOT_AVAILABLE, + TEEC_ERROR_CA_AUTH_FAIL = 0xFFFFCFE5, + TEE_ERROR_AUDIT_FAIL = 0xFFFF9112, +}; + +enum TEEC_ReturnCodeOrigin { + TEEC_ORIGIN_API = 0x1, + TEEC_ORIGIN_COMMS = 0x2, + TEEC_ORIGIN_TEE = 0x3, + TEEC_ORIGIN_TRUSTED_APP = 0x4, +}; + +enum TEEC_SharedMemCtl { + TEEC_MEM_INPUT = 0x1, + TEEC_MEM_OUTPUT = 0x2, + TEEC_MEM_INOUT = 0x3, +}; + +enum TEEC_ParamType { + TEEC_NONE = 0x0, + TEEC_VALUE_INPUT = 0x01, + TEEC_VALUE_OUTPUT = 0x02, + TEEC_VALUE_INOUT = 0x03, + TEEC_MEMREF_TEMP_INPUT = 0x05, + TEEC_MEMREF_TEMP_OUTPUT = 0x06, + TEEC_MEMREF_TEMP_INOUT = 0x07, + TEEC_ION_INPUT = 0x08, + TEEC_ION_SGLIST_INPUT = 0x09, + TEEC_MEMREF_WHOLE = 0xc, + TEEC_MEMREF_PARTIAL_INPUT = 0xd, + TEEC_MEMREF_PARTIAL_OUTPUT = 0xe, + TEEC_MEMREF_PARTIAL_INOUT = 0xf +}; + +enum TEE_ParamType { + TEE_PARAM_TYPE_NONE = 0x0, + TEE_PARAM_TYPE_VALUE_INPUT = 0x1, + TEE_PARAM_TYPE_VALUE_OUTPUT = 0x2, + TEE_PARAM_TYPE_VALUE_INOUT = 0x3, + TEE_PARAM_TYPE_MEMREF_INPUT = 0x5, + TEE_PARAM_TYPE_MEMREF_OUTPUT = 0x6, + TEE_PARAM_TYPE_MEMREF_INOUT = 0x7, + TEE_PARAM_TYPE_ION_INPUT = 0x8, + TEE_PARAM_TYPE_ION_SGLIST_INPUT = 0x9, +}; + +enum TEEC_LoginMethod { + TEEC_LOGIN_PUBLIC = 0x0, + TEEC_LOGIN_USER, + TEEC_LOGIN_GROUP, + TEEC_LOGIN_APPLICATION = 0x4, + TEEC_LOGIN_USER_APPLICATION = 0x5, + TEEC_LOGIN_GROUP_APPLICATION = 0x6, + TEEC_LOGIN_IDENTIFY = 0x7, +}; + +#endif diff --git a/liteos/include/teek_client_id.h b/liteos/include/teek_client_id.h new file mode 100644 index 0000000..3654c58 --- /dev/null +++ b/liteos/include/teek_client_id.h @@ -0,0 +1,79 @@ +/* + * teek_client_id.h + * + * define exported data for secboot 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 TEE_CLIENT_ID_H +#define TEE_CLIENT_ID_H + +#define TEE_SERVICE_SECBOOT \ +{ \ + 0x08080808, \ + 0x0808, \ + 0x0808, \ + { \ + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08 \ + } \ +} + +/* e7ed1f64-4687-41da-96dc-cbe4f27c838f */ +#define TEE_SERVICE_ANTIROOT \ +{ \ + 0xE7ED1F64, \ + 0x4687, \ + 0x41DA, \ + { \ + 0x96, 0xDC, 0xCB, 0xE4, 0xF2, 0x7C, 0x83, 0x8F \ + } \ +} +/* dca5ae8a-769e-4e24-896b-7d06442c1c0e */ +#define TEE_SERVICE_SECISP \ +{ \ + 0xDCA5AE8A, \ + 0x769E, \ + 0x4E24, \ + { \ + 0x89, 0x6B, 0x7D, 0x06, 0x44, 0x2C, 0x1C, 0x0E \ + } \ +} +/* 5700f837-8b8e-4661-800b-42bb3fc3141f */ +#define TEE_SERVICE_DRM_GRALLOC \ +{ \ + 0x5700F837, \ + 0x8B8E, \ + 0x4661, \ + { \ + 0x80, 0x0B, 0x42, 0xBB, 0x3F, 0xC3, 0x14, 0x1F \ + } \ +} + +enum SVC_SECBOOT_CMD_ID { + SECBOOT_CMD_ID_INVALID = 0x0, + SECBOOT_CMD_ID_COPY_VRL, + SECBOOT_CMD_ID_COPY_DATA, + SECBOOT_CMD_ID_VERIFY_DATA, + SECBOOT_CMD_ID_RESET_IMAGE, + SECBOOT_CMD_ID_COPY_VRL_TYPE, + SECBOOT_CMD_ID_COPY_DATA_TYPE, + SECBOOT_CMD_ID_VERIFY_DATA_TYPE, + SECBOOT_CMD_ID_VERIFY_DATA_TYPE_LOCAL, + SECBOOT_CMD_ID_COPY_IMG_TYPE, + SECBOOT_CMD_ID_BSP_MODEM_CALL, + SECBOOT_CMD_ID_BSP_MODULE_VERIFY, + SECBOOT_CMD_ID_BSP_ICC_OPEN_THREAD, + SECBOOT_CMD_ID_BSP_RFILE_RW_THREAD, + SECBOOT_CMD_ID_GET_RNG_NUM, +}; + +#endif diff --git a/liteos/include/teek_client_type.h b/liteos/include/teek_client_type.h new file mode 100644 index 0000000..810d05c --- /dev/null +++ b/liteos/include/teek_client_type.h @@ -0,0 +1,192 @@ +/* + * teek_client_type.h + * + * define exported structures + * + * 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_CLIENT_TYPE_H +#define TEE_CLIENT_TYPE_H + +#include +#include "teek_client_constants.h" + +#ifndef NULL +#define NULL 0 +#endif + +struct teec_uuid { + uint32_t time_low; + uint16_t time_mid; + uint16_t timehi_and_version; + uint8_t clockseq_and_node[8]; +}; + +struct teec_context { + void *dev; + uint8_t *ta_path; + struct list_head shrd_mem_list; +}; + +struct teec_session { + uint32_t session_id; + struct teec_uuid service_id; + uint32_t ops_cnt; + struct teec_context *context; +}; + +struct teec_sharedmemory { + void *buffer; + size_t size; + uint32_t flags; + uint32_t ops_cnt; + bool is_allocated; + struct list_head head; + struct teec_context *context; +}; + +struct teec_tempmemory_reference { + void *buffer; + size_t size; +}; + +struct teec_registeredmemory_reference { + struct teec_sharedmemory *parent; + size_t size; + size_t offset; +}; + + +struct teec_value { + uint32_t a; + uint32_t b; +}; + +struct teec_ion_reference { + int ion_share_fd; + size_t ion_size; +}; + +union teec_parameter { + struct teec_tempmemory_reference tmpref; + struct teec_registeredmemory_reference memref; + struct teec_value value; + struct teec_ion_reference ionref; +}; + +struct teec_tui_parameter { + uint32_t event_type; + /* tui event type */ + uint32_t value; + /* return value, is keycode if tui event is getkeycode */ + uint32_t notch; /* notch size of device */ + uint32_t width; /* width of foldable screen */ + uint32_t height; /* height of foldable screen */ + uint32_t fold_state; /* state of foldable screen */ + uint32_t display_state; /* one state of folded state */ + uint32_t phy_width; /* real width of the mobile */ + uint32_t phy_height; /* real height of the mobile */ +}; + +struct teec_operation { + uint32_t started; + uint32_t paramtypes; + union teec_parameter params[4]; /* GP has four params */ + struct teec_session *session; + bool cancel_flag; +}; + +typedef uint32_t TEEC_Result; + +typedef struct { + uint32_t timeLow; + uint16_t timeMid; + uint16_t timeHiAndVersion; + uint8_t clockSeqAndNode[8]; +} TEEC_UUID; + +typedef struct { + void *dev; + uint8_t *ta_path; + struct list_head session_list; + struct list_head shrd_mem_list; +} TEEC_Context; + +typedef struct { + uint32_t session_id; + TEEC_UUID service_id; + uint32_t ops_cnt; + struct list_head head; + TEEC_Context *context; +} TEEC_Session; + +typedef struct { + void *buffer; + size_t size; + uint32_t flags; + uint32_t ops_cnt; + bool is_allocated; + struct list_head head; + TEEC_Context *context; +} TEEC_SharedMemory; + +typedef struct { + void *buffer; + size_t size; +} TEEC_TempMemoryReference; + +typedef struct { + TEEC_SharedMemory *parent; + size_t size; + size_t offset; +} TEEC_RegisteredMemoryReference; + +typedef struct { + uint32_t a; + uint32_t b; +} TEEC_Value; + +typedef struct { + int ion_share_fd; + size_t ion_size; +} TEEC_IonReference; + +typedef union { + TEEC_TempMemoryReference tmpref; + TEEC_RegisteredMemoryReference memref; + TEEC_Value value; + TEEC_IonReference ionref; +} TEEC_Parameter; + +typedef struct { + uint32_t event_type; + /* Tui event type */ + uint32_t value; + /* return value, is keycode if tui event is getKeycode */ + uint32_t notch; /* notch size of device */ + uint32_t width; /* width of foldable screen */ + uint32_t height; /* height of foldable screen */ + uint32_t fold_state; /* state of foldable screen */ + uint32_t display_state; /* one state of folded state */ + uint32_t phy_width; /* real width of the mobile */ + uint32_t phy_height; /* real height of the mobile */ +} TEEC_TUI_Parameter; + +typedef struct { + uint32_t started; + uint32_t paramTypes; + TEEC_Parameter params[4]; + TEEC_Session *session; + bool cancel_flag; +} TEEC_Operation; + +#endif diff --git a/liteos/include/teek_ns_client.h b/liteos/include/teek_ns_client.h new file mode 100644 index 0000000..452b07e --- /dev/null +++ b/liteos/include/teek_ns_client.h @@ -0,0 +1,196 @@ +/* + * teek_ns_client.h + * + * define structures and IOCTLs. + * + * 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_NS_CLIENT_H +#define TEEK_NS_CLIENT_H + +#include +#include +#include +#include "tc_ns_client.h" +#include "tc_ns_log.h" +#include "los_adapt.h" + +#define TC_NS_CLIENT_IOC_MAGIC 't' +#define TC_NS_CLIENT_DEV "tc_ns_client" +#define TC_NS_CLIENT_DEV_NAME "/dev/tc_ns_client" + +#define EXCEPTION_MEM_SIZE (8*1024) /* mem for exception handling */ +#define TSP_REQUEST 0xB2000008 +#define TSP_RESPONSE 0xB2000009 +#define TSP_REE_SIQ 0xB200000A +#define TSP_CRASH 0xB200000B +#define TSP_PREEMPTED 0xB2000005 +#define TC_CALL_GLOBAL 0x01 +#define TC_CALL_SYNC 0x02 +#define TC_CALL_LOGIN 0x04 +#define TEE_REQ_FROM_USER_MODE 0U +#define TEE_REQ_FROM_KERNEL_MODE 1U +#define TEE_PARAM_NUM 4 + +/* Max sizes for login info buffer comming from teecd */ +#define MAX_PACKAGE_NAME_LEN 255 +/* The apk certificate format is as follows: + * modulus_size(4 bytes) + modulus buffer(512 bytes) + * + exponent size(4 bytes) + exponent buffer(1 bytes) + */ +#define MAX_PUBKEY_LEN 1024 + +struct tc_ns_dev_list { + mutex_t dev_lock; /* for dev_file_list */ + struct list_head dev_file_list; +}; + +struct tc_uuid { + uint32_t time_low; + uint16_t time_mid; + uint16_t timehi_and_version; + uint8_t clockseq_and_node[8]; /* clock len is 8 */ +}; + +struct tc_ns_shared_mem { + void *kernel_addr; + void *user_addr; + void *user_addr_ca; /* for ca alloc share mem */ + unsigned int len; + struct list_head head; + atomic_t usage; + atomic_t offset; +}; + +struct tc_ns_service { + unsigned char uuid[UUID_LEN]; + mutex_t session_lock; /* for session_list */ + struct list_head session_list; + struct list_head head; + mutex_t operation_lock; /* for session's open/close */ + atomic_t usage; +}; + +#define SERVICES_MAX_COUNT 32 /* service limit can opened on 1 fd */ +struct tc_ns_dev_file { + unsigned int dev_file_id; + mutex_t service_lock; /* for service_ref[], services[] */ + uint8_t service_ref[SERVICES_MAX_COUNT]; /* a judge if set services[i]=NULL */ + struct tc_ns_service *services[SERVICES_MAX_COUNT]; + mutex_t shared_mem_lock; /* for shared_mem_list */ + struct list_head shared_mem_list; + struct list_head head; + /* Device is linked to call from kernel */ + uint8_t kernel_api; + /* client login info provided by teecd, can be either package name and public + * key or uid(for non android services/daemons) + * login information can only be set once, dont' allow subsequent calls + */ + bool login_setup; + mutex_t login_setup_lock; /* for login setup */ + uint32_t pkg_name_len; + uint8_t pkg_name[MAX_PACKAGE_NAME_LEN]; + uint32_t pub_key_len; + uint8_t pub_key[MAX_PUBKEY_LEN]; + int load_app_flag; +}; + +union tc_ns_parameter { + struct { + unsigned int buffer; + unsigned int size; + } memref; + struct { + unsigned int a; + unsigned int b; + } value; +}; + +struct tc_ns_login { + unsigned int method; + unsigned int mdata; +}; + +struct tc_ns_operation { + unsigned int paramtypes; + union tc_ns_parameter params[TEE_PARAM_NUM]; + unsigned int buffer_h_addr[TEE_PARAM_NUM]; + struct tc_ns_shared_mem *sharemem[TEE_PARAM_NUM]; + void *mb_buffer[TEE_PARAM_NUM]; +}; + +struct tc_ns_temp_buf { + void *temp_buffer; + unsigned int size; +}; + +enum smc_cmd_type { + CMD_TYPE_GLOBAL, + CMD_TYPE_TA, + CMD_TYPE_TA_AGENT, + CMD_TYPE_TA2TA_AGENT, /* compatible with TA2TA2TA->AGENT etc. */ + CMD_TYPE_BUILDIN_AGENT, +}; + +struct tc_ns_smc_cmd { + uint8_t uuid[sizeof(struct tc_uuid)]; + unsigned int cmd_type; + unsigned int cmd_id; + unsigned int dev_file_id; + unsigned int context_id; + unsigned int agent_id; + unsigned int operation_phys; + unsigned int operation_h_phys; + unsigned int login_method; + unsigned int login_data_phy; + unsigned int login_data_h_addr; + unsigned int login_data_len; + unsigned int err_origin; + int ret_val; + unsigned int event_nr; + unsigned int uid; + unsigned int ca_pid; /* pid */ + unsigned int pid; /* tgid */ + unsigned int eventindex; /* tee audit event index for upload */ + bool started; +} __attribute__((__packed__)); + +/* + * @brief + */ +struct tc_wait_data { + wait_queue_head_t send_cmd_wq; + int send_wait_flag; +}; + +#define NUM_OF_SO 1 +#ifdef CONFIG_CMS_CAHASH_AUTH +#define KIND_OF_SO 1 +#else +#define KIND_OF_SO 2 +#endif +struct tc_ns_session { + unsigned int session_id; + struct list_head head; + struct tc_wait_data wait_data; + mutex_t ta_session_lock; /* for open/close/invoke on 1 session */ + struct tc_ns_dev_file *owner; + uint8_t auth_hash_buf[MAX_SHA_256_SZ * NUM_OF_SO + MAX_SHA_256_SZ]; + atomic_t usage; +}; + +struct mb_cmd_pack { + struct tc_ns_operation operation; + unsigned char login_data[MAX_SHA_256_SZ * NUM_OF_SO + MAX_SHA_256_SZ]; +}; + +#endif diff --git a/liteos/include/tzdriver.h b/liteos/include/tzdriver.h new file mode 100644 index 0000000..3f1bd6d --- /dev/null +++ b/liteos/include/tzdriver.h @@ -0,0 +1,24 @@ +/* + * tzdriver.h + * + * functions 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 TZDRIVER_H +#define TZDRIVER_H + +int tc_init(void); +void set_vmm_region_code_start(uintptr_t code_start, uint32_t code_size); + +#endif diff --git a/liteos/los_adapt.c b/liteos/los_adapt.c new file mode 100644 index 0000000..dfe4534 --- /dev/null +++ b/liteos/los_adapt.c @@ -0,0 +1,310 @@ +/* + * los_adapt.c + * + * functions for liteos 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. + */ + +#include "los_adapt.h" +#include "mbedtls/aes.h" +#include "tc_ns_log.h" + +LosTaskCB *kthread_run(int (*threadfn)(uintptr_t data, int data_len), void *data, int data_len, char *name) +{ + LosTaskCB *ktask = NULL; + uint32_t task_id = 0; + uint32_t ret; + TSK_INIT_PARAM_S task_init_param; + + if (threadfn == NULL) + return NULL; + + if (memset_s(&task_init_param, sizeof(TSK_INIT_PARAM_S), 0, sizeof(TSK_INIT_PARAM_S)) != EOK) + return NULL; + + task_init_param.pfnTaskEntry = (TSK_ENTRY_FUNC)threadfn; + task_init_param.uwStackSize = LOSCFG_BASE_CORE_TSK_DEFAULT_STACK_SIZE; + task_init_param.pcName = name; + task_init_param.usTaskPrio = 1; + task_init_param.auwArgs[0] = (uintptr_t)data; + task_init_param.auwArgs[1] = data_len; + task_init_param.uwResved = LOS_TASK_STATUS_DETACHED; + + ret = LOS_TaskCreate(&task_id, &task_init_param); + if (ret != LOS_OK) + return NULL; + + ktask = (LosTaskCB *)OS_TCB_FROM_TID(task_id); + (void)LOS_TaskYield(); + return ktask; +} + +void kthread_stop(const LosTaskCB *k) +{ + if (k != NULL) + LOS_TaskDelete(k->taskID); +} + +int kthread_should_stop(void) +{ + return (OsCurrTaskGet()->signal == SIGNAL_KILL); +} + +ssize_t simple_read_from_buffer(void __user *to, size_t count, + const void *from, size_t available) +{ + size_t ret; + + if (count == 0 || available == 0) + return 0; + + if (count > available) + count = available; + + ret = copy_to_user(to, from, count); + if (ret == count) + return -EFAULT; + count -= ret; + return count; +} + +#define MAX_ORDER 31 + +LosVmPage *mailbox_pool_alloc_pages(unsigned int order) +{ + if (order > MAX_ORDER) + return NULL; + void *ptr = LOS_PhysPagesAllocContiguous(1UL << order); + if (ptr == NULL) { + PRINTK("mailbox pool contiguous ptr null size %x\n", 1 << order); + return NULL; + } + for (int i = 0; i < (1UL << order); i++) { + // mempool is used to mmap, add ref to prevent pmm free page to free list. + LosVmPage *page = OsVmVaddrToPage((void *)((intptr_t)ptr + PAGE_SIZE * i)); + if (page != NULL) + LOS_AtomicInc(&page->refCounts); + } + + return OsVmVaddrToPage(ptr); +} + +void mailbox_pool_free_pages(LosVmPage *page_array, size_t order) +{ + if (page_array == NULL || order > MAX_ORDER) + return; + + for (int i = 0; i < (1UL << order); i++) { + LOS_AtomicDec(&(page_array[i].refCounts)); + LOS_PhysPageFree(&page_array[i]); + } + LOS_PhysPagesFreeContiguous(page_array, (1UL << order)); +} + +int32_t do_vmalloc_remap(LosVmMapRegion *vma, void *kvaddr) +{ + int i; + int ret = 0; + paddr_t pa; + uint32_t uflags = VM_MAP_REGION_FLAG_PERM_READ | VM_MAP_REGION_FLAG_PERM_WRITE | VM_MAP_REGION_FLAG_PERM_USER; + LosVmPage *vm_page = NULL; + + if (vma == NULL || kvaddr == NULL) + return -EINVAL; + + LosVmSpace *vm_space = LOS_SpaceGet(vma->range.base); + if (vm_space == NULL) + return -EINVAL; + + vaddr_t kva = (vaddr_t)(uintptr_t)kvaddr; + vaddr_t uva = vma->range.base; + unsigned int page; + + (void)LOS_MuxAcquire(&vm_space->regionMux); + for (i = 0; i < (vma->range.size >> PAGE_SHIFT); i++) { + page = (unsigned int)i; + pa = LOS_PaddrQuery((void *)(uintptr_t)(kva + (page << PAGE_SHIFT))); + if (pa == 0) { + PRINT_ERR("%s, %d\n", __FUNCTION__, __LINE__); + ret = -EINVAL; + break; + } + vm_page = LOS_VmPageGet(pa); + if (vm_page == NULL) { + PRINT_ERR("%s, %d\n", __FUNCTION__, __LINE__); + ret = -EINVAL; + break; + } + status_t err = LOS_ArchMmuMap(&vm_space->archMmu, uva + (page << PAGE_SHIFT), pa, 1, uflags); + if (err < 0) { + ret = err; + PRINT_ERR("%s, %d\n", __FUNCTION__, __LINE__); + break; + } + LOS_AtomicInc(&vm_page->refCounts); + } + /* if any failure happened, rollback */ + if (i < (vma->range.size >> PAGE_SHIFT)) { + for (i = i - 1; i >= 0; i--) { + page = (unsigned int)i; + pa = LOS_PaddrQuery((void *)(uintptr_t)(kva + (page << PAGE_SHIFT))); + vm_page = LOS_VmPageGet(pa); + (void)LOS_ArchMmuUnmap(&vm_space->archMmu, uva + (page << PAGE_SHIFT), 1); + (void)LOS_PhysPageFree(vm_page); + } + } + + (void)LOS_MuxRelease(&vm_space->regionMux); + return ret; +} + +int remap_vmalloc_range(LosVmMapRegion *vma, void *addr, unsigned long pgoff) +{ + if (pgoff != 0) + return -1; + return do_vmalloc_remap(vma, addr); +} + +int create_tc_client_device(const char *dev_name, const struct file_operations_vfs *op) +{ + int ret = register_driver(dev_name, op, TEE_DEV_PRI, NULL); + if (unlikely(ret)) + return -1; + + return EOK; +} + +int misc_register_adapt(struct miscdevice_adapt *misc_device) +{ + if (misc_device == NULL) + return -1; + + int ret = register_driver(misc_device->name, misc_device->op, TEE_DEV_PRI, NULL); + if (unlikely(ret)) + return -1; + + return EOK; +} + +#define IV_LEN 16 +#define KEY_BITS 256 +#define MAX_AES_CRYPT_SIZE SZ_4M +int crypto_aescbckey256(unsigned char *output, const unsigned char *input, struct aes_param *param) +{ + mbedtls_aes_context ctx; + int ret; + if (!output || !input) + return -1; + + if (!param || !param->iv || !param->key || + param->size < 0 || param->size > MAX_AES_CRYPT_SIZE) + return -1; + + int mode = param->encrypto_type ? MBEDTLS_AES_ENCRYPT : MBEDTLS_AES_DECRYPT; + unsigned char iv_tmp[IV_LEN] = {0}; + + ret = memcpy_s(iv_tmp, IV_LEN, param->iv, IV_LEN); + if (ret != EOK) + return -1; + mbedtls_aes_init(&ctx); + + if (mode == MBEDTLS_AES_ENCRYPT) + ret = mbedtls_aes_setkey_enc(&ctx, param->key, KEY_BITS); + else + ret = mbedtls_aes_setkey_dec(&ctx, param->key, KEY_BITS); + if (ret) + return -1; + return mbedtls_aes_crypt_cbc(&ctx, mode, param->size, iv_tmp, input, output); +} + +void set_vmm_region_code_start(uintptr_t code_start, uint32_t code_size) +{ + LosVmSpace *space = NULL; + space = OsCurrProcessGet()->vmSpace; + if (space->codeStart != 0) + return; + + if (code_size == 0 || code_start + code_size < code_start) + return; + + space->codeStart = code_start; + space->codeEnd = code_start + code_size; +} + +char *get_process_path(LosTaskCB *task, char *tpath, int path_len) +{ + if (task == NULL || tpath == NULL || path_len < 0 || path_len > MAX_PATH_SIZE) + return NULL; + + struct Vnode *vnode = OS_PCB_FROM_PID(task->processID)->execVnode; + if (vnode == NULL) + return NULL; + int ret = memset_s(tpath, path_len, '\0', path_len); + if (ret != EOK) { + tloge("memset error ret is %d\n", ret); + return NULL; + } + + ret = memcpy_s(tpath, path_len - 1, vnode->filePath, strlen(vnode->filePath)); + if (ret != EOK) { + tloge("memcpy error ret is %d\n", ret); + return NULL; + } + return tpath; +} + +int calc_task_so_hash(unsigned char *digest, uint32_t dig_len, + LosTaskCB *cur_struct, int so_index) +{ + tee_sha256_context ctx; + bool find_flag = false; + LosRbNode *rb_node = NULL; + LosRbNode *rb_node_next = NULL; + LosVmSpace *space = OS_PCB_FROM_PID(cur_struct->processID)->vmSpace; + if (space == NULL) + return -EFAULT; + + init_tee_sha256(&ctx); + /* search the region list */ + (void)LOS_MuxAcquire(&space->regionMux); + RB_SCAN_SAFE(&space->regionRbTree, rb_node, rb_node_next) + LosVmMapRegion *region = (LosVmMapRegion *)rb_node; + if (!LOS_IsRegionFileValid(region)) + continue; + struct Vnode *vnode = region->unTypeData.rf.vnode; + if (vnode != NULL && !strncmp(vnode->filePath, LIBTEEC_SO, strlen(LIBTEEC_SO))) { + update_tee_sha256(&ctx, (void *)region->range.base, region->range.size); + find_flag = true; + break; + } + RB_SCAN_SAFE_END(&space->regionRbTree, rb_node, rb_node_next) + (void)LOS_MuxRelease(&space->regionMux); + + if (!find_flag) + return -EFAULT; + finish_tee_sha256(&ctx, digest); + return 0; +} + +void check_teecd_process(void) +{ + char path[MAX_PATH_SIZE]; + + if (get_process_path(OsCurrTaskGet(), path, MAX_PATH_SIZE) == NULL) { + tloge("get path failed\n"); + return; + } + + if (strcmp(path, CONFIG_TEECD_PATH) == 0 && get_task_uid(OsCurrTaskGet()) == TEECD_UID) + set_teecd_task(OsCurrTaskGet()); +} diff --git a/liteos/tlogger/log_cfg_api.h b/liteos/tlogger/log_cfg_api.h new file mode 100644 index 0000000..d8cd510 --- /dev/null +++ b/liteos/tlogger/log_cfg_api.h @@ -0,0 +1,77 @@ +/* + * log_cfg_api.h + * + * for log cfg api define + * + * 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 LOG_CFG_API_H +#define LOG_CFG_API_H + +#include + +#if (defined(CONFIG_PAGES_MEM) && defined(CONFIG_TEELOG)) +int register_log_mem(uint64_t *addr, uint32_t *len); +int register_log_exception(void); +void report_log_system_error(void); +void report_log_system_panic(void); +int *map_log_mem(uint64_t mem_addr, uint32_t mem_len); +void unmap_log_mem(int *log_buffer); +void get_log_chown(uid_t *user, gid_t *group); +void unregister_log_exception(void); +void ta_crash_report_log(void); +#else +static inline int register_log_mem(const uint64_t *addr, const uint32_t *len) +{ + (void)addr; + (void)len; + return 0; +} + +static inline int register_log_exception(void) +{ + return 0; +} + +static inline void report_log_system_error(void) +{ +} + +static inline void report_log_system_panic(void) +{ +} + +static inline int *map_log_mem(uint64_t mem_addr, uint32_t mem_len) +{ + (void)mem_addr; + (void)mem_len; + return NULL; +} +static inline void unmap_log_mem(const int *log_buffer) +{ + (void)log_buffer; +} + +static inline void get_log_chown(const uid_t *user, const gid_t *group) +{ + (void)user; + (void)group; +} +static inline void unregister_log_exception(void) +{ +} + +static inline void ta_crash_report_log(void) +{ +} +#endif +#endif diff --git a/liteos/tlogger/log_pages_cfg.c b/liteos/tlogger/log_pages_cfg.c new file mode 100644 index 0000000..322622f --- /dev/null +++ b/liteos/tlogger/log_pages_cfg.c @@ -0,0 +1,141 @@ +/* + * log_pages_cfg.c + * + * for pages log cfg api define + * + * 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 "log_cfg_api.h" + +#include +#include +#include +#include +#include +#include + +#include +#include "tc_ns_log.h" +#include "tlogger.h" +#include "los_adapt.h" + +void unregister_log_exception(void) +{ +} +int register_log_exception(void) +{ + return 0; +} + +struct pages_module_result { + uint64_t log_addr; + uint32_t log_len; +}; + +struct pages_module_result g_mem_info = {0}; + +#define SZ_1K (0x00000400) + +#ifdef CONFIG_512K_LOG_PAGES_MEM +#define PAGES_LOG_MEM_LEN (512 * SZ_1K) /* mem size: 512 k */ +#else +#define PAGES_LOG_MEM_LEN (256 * SZ_1K) /* mem size: 256 k */ +#endif + +static int tee_pages_register_core(void) +{ + g_mem_info.log_addr = (uintptr_t)__get_free_pages( + GFP_KERNEL | __GFP_ZERO, PAGES_LOG_MEM_LEN); + if (IS_ERR_OR_NULL((void *)(uintptr_t)g_mem_info.log_addr)) { + tloge("get log mem error\n"); + return -1; + } + + g_mem_info.log_len = PAGES_LOG_MEM_LEN; + return 0; +} + +/* Register log memory */ +int register_log_mem(uint64_t *addr, uint32_t *len) +{ + int ret; + uint64_t mem_addr; + uint32_t mem_len; + + if (!addr || !len) { + tloge("check addr or len is failed\n"); + return -1; + } + + ret = tee_pages_register_core(); + if (ret) + return ret; + + mem_addr = virt_to_phys((void *)(uintptr_t)g_mem_info.log_addr); + mem_len = g_mem_info.log_len; + + ret = register_mem_to_teeos(mem_addr, mem_len, true); + if (ret) + return ret; + + *addr = g_mem_info.log_addr; + *len = g_mem_info.log_len; + return ret; +} + +void report_log_system_error(void) +{ +} + +void report_log_system_panic(void) +{ +/* default support trigger ap reset */ +#ifndef NOT_TRIGGER_AP_RESET + panic("TEEOS panic\n"); +#endif +} + +void ta_crash_report_log(void) +{ +} + +int *map_log_mem(uint64_t mem_addr, uint32_t mem_len) +{ + (void)mem_len; + return (int *)(uintptr_t)mem_addr; +} + +void unmap_log_mem(int *log_buffer) +{ + free_pages((void *)(unsigned long)(uintptr_t)log_buffer, + get_order(PAGES_LOG_MEM_LEN)); +} + +#define ROOT_UID 0 + +#ifdef LAST_TEE_MSG_ROOT_GID +#define FILE_CHOWN_GID 0 +#else +/* system gid for last_teemsg file sys chown */ +#define FILE_CHOWN_GID 1000 +#endif + +void get_log_chown(uid_t *user, gid_t *group) +{ + if (!user || !group) { + tloge("user or group buffer is null\n"); + return; + } + + *user = ROOT_UID; + *group = FILE_CHOWN_GID; +} diff --git a/liteos/tlogger/tlogger.c b/liteos/tlogger/tlogger.c new file mode 100644 index 0000000..8be74a9 --- /dev/null +++ b/liteos/tlogger/tlogger.c @@ -0,0 +1,1230 @@ +/* + * tlogger.c + * + * TEE Logging Subsystem, read the tee os log from log memory + * + * 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 "tlogger.h" +#include /* copy_to_user */ +#include +#include +#include +#include "smc_smp.h" +#include "mailbox_mempool.h" +#include "teek_client_constants.h" +#include "tc_ns_client.h" +#include "teek_ns_client.h" +#include "log_cfg_api.h" +#include "tc_ns_log.h" +#include "los_adapt.h" + +/* for log item ----------------------------------- */ +#define LOG_ITEM_MAGIC 0x5A5A +#define LOG_ITEM_LEN_ALIGN 64 +#define LOG_ITEM_MAX_LEN 1024 +#define LOG_READ_STATUS_ERROR 0x000FFFF + +/* =================================================== */ +#define LOGGER_LOG_TEEOS "teelog" /* tee os log */ +#define TLOGCAT_DEV_NAME "/dev/teelog" +#define __TEELOGGERIO 0xBE /* for ioctl */ + +#define DUMP_START_MAGIC "Dump SPI notification" +#define DUMP_END_MAGIC "Dump task states END" + +#define GET_VERSION_BASE 5 +#define SET_READERPOS_CUR_BASE 6 +#define SET_TLOGCAT_STAT_BASE 7 +#define GET_TLOGCAT_STAT_BASE 8 + +/* get tee verison */ +#define MAX_TEE_VERSION_LEN 256 +#define TEELOGGER_GET_VERSION \ + _IOR(__TEELOGGERIO, GET_VERSION_BASE, char[MAX_TEE_VERSION_LEN]) +/* set the log reader pos to current pos */ +#define TEELOGGER_SET_READERPOS_CUR \ + _IO(__TEELOGGERIO, SET_READERPOS_CUR_BASE) +#define TEELOGGER_SET_TLOGCAT_STAT \ + _IO(__TEELOGGERIO, SET_TLOGCAT_STAT_BASE) +#define TEELOGGER_GET_TLOGCAT_STAT \ + _IO(__TEELOGGERIO, GET_TLOGCAT_STAT_BASE) + +int g_tlogcat_f = 0; + +#ifndef CONFIG_TEE_LOG_ACHIVE_PATH +#define CONFIG_TEE_LOG_ACHIVE_PATH "/data/log/tee/last_teemsg" +#endif +#define TEE_LOG_FILE_NAME_MAX 256 + +uint32_t g_last_read_offset = 0; + +#define NEVER_USED_LEN 32U +#define LOG_ITEM_RESERVED_LEN 1U + +/* 64 byte head + user log */ +struct log_item { + unsigned char never_used[NEVER_USED_LEN]; + unsigned short magic; + unsigned short reserved0; + uint32_t serial_no; + short real_len; /* log real len */ + unsigned short buffer_len; /* log buffer's len, multiple of 32 bytes */ + unsigned char uuid[UUID_LEN]; + unsigned char log_source_type; + unsigned char reserved[LOG_ITEM_RESERVED_LEN]; + unsigned char log_level; + unsigned char new_line; /* '\n' char, easy viewing log in bbox.bin file */ + unsigned char log_buffer[0]; +}; + +/* --- for log mem --------------------------------- */ +#define TEMP_LOG_MEM_SIZE (10 * SZ_1K) + +#define LOG_BUFFER_RESERVED_LEN 11U +#define VERSION_INFO_LEN 156U + +/* + * Log's buffer flag info, size: 64 bytes head + 156 bytes's version info. + * For filed description: + * last_pos : current log's end position, last log's start position. + * write_loops: Write cyclically. Init value is 0, when memory is used + * up, the value add 1. + */ +struct log_buffer_flag { + uint32_t reserved0; + uint32_t last_pos; + uint32_t write_loops; + uint32_t log_level; + uint32_t reserved[LOG_BUFFER_RESERVED_LEN]; + uint32_t max_len; + unsigned char version_info[VERSION_INFO_LEN]; +}; + +struct log_buffer { + struct log_buffer_flag flag; + unsigned char buffer_start[0]; +}; + +static struct log_buffer *g_log_buffer = NULL; + +struct tlogger_log { + unsigned char *buffer_info; /* ring buffer info */ + mutex_t mutex_info; /* this mutex protects buffer_info */ + wait_queue_head_t wait_queue_head; /* wait queue head for reader */ + struct list_head logs; /* log channels list */ + struct miscdevice_adapt misc_device; /* misc device log */ + struct list_head readers; /* log's readers */ +}; + +static LINUX_LIST_HEAD(m_log_list); + +struct tlogger_reader { + struct tlogger_log *log; /* tlogger_log info data */ + struct list_head list; /* log entry in tlogger_log's list */ + /* Current reading position, start position of next read again */ + uint32_t r_off; + uint32_t r_loops; + uint32_t r_sn; + uint32_t r_failtimes; + uint32_t r_from_cur; + uint32_t r_is_tlogf; + bool r_all; /* whether this reader can read all entries */ + uint32_t r_ver; +}; + +static uint32_t g_log_mem_len = 0; +static uint32_t g_tlogcat_count = 0; +static struct tlogger_log *g_log; + +static struct tlogger_log *get_reader_log(const struct file *file) +{ + struct tlogger_reader *reader = NULL; + + reader = file->f_priv; + if (!reader) + return NULL; + + return reader->log; +} + +static bool check_log_item_validite(struct log_item *item, + uint32_t item_max_size) +{ + bool con = (item && (item->magic == LOG_ITEM_MAGIC) && + (item->buffer_len > 0) && + (item->real_len > 0) && + (item->buffer_len % LOG_ITEM_LEN_ALIGN == 0) && + (item->real_len <= item->buffer_len) && + ((item->buffer_len - item->real_len) < LOG_ITEM_LEN_ALIGN) && + (item->buffer_len + sizeof(*item) <= item_max_size)); + + return con; +} + +static struct log_item *get_next_log_item(const unsigned char *buffer_start, + uint32_t max_len, uint32_t read_pos, uint32_t scope_len, uint32_t *pos) +{ + uint32_t i = 0; + struct log_item *item = NULL; + uint32_t max_size; + + if ((read_pos + scope_len) > max_len) + return NULL; + + while ((i + sizeof(*item) + LOG_ITEM_LEN_ALIGN) <= scope_len) { + *pos = read_pos + i; + item = (struct log_item *)(buffer_start + read_pos + i); + max_size = (((scope_len - i) > LOG_ITEM_MAX_LEN) ? + LOG_ITEM_MAX_LEN : (scope_len - i)); + if (check_log_item_validite(item, max_size)) + break; + + i += LOG_ITEM_LEN_ALIGN; + item = NULL; + } + + return item; +} + +struct reader_position { + const unsigned char *buffer_start; + uint32_t max_len; + uint32_t start_pos; + uint32_t end_pos; +}; + +static uint32_t parse_log_item(char __user *buf, size_t count, + struct reader_position *position, uint32_t *read_off, + bool *user_buffer_left) +{ + struct log_item *next_item = NULL; + uint32_t buf_left; + uint32_t buf_written; + uint32_t item_len; + bool con = false; + uint32_t start_pos = position->start_pos; + + buf_written = 0; + buf_left = count; + + con = (!read_off || !position->buffer_start); + if (con) + return buf_written; + + *user_buffer_left = true; + while (start_pos < position->end_pos) { + next_item = get_next_log_item(position->buffer_start, + position->max_len, start_pos, + position->end_pos - start_pos, &start_pos); + if (!next_item) + break; + + /* copy to user */ + item_len = next_item->buffer_len + sizeof(*next_item); + if (buf_left < item_len) { + *user_buffer_left = false; + break; + } + + start_pos += item_len; + if (copy_to_user(buf + buf_written, + (void *)next_item, item_len)) + tloge("copy failed, item len %u\n", item_len); + + buf_written += item_len; + buf_left -= item_len; + } + + *read_off = start_pos; + return buf_written; +} + +static ssize_t get_buffer_info(struct tlogger_reader *reader, + struct log_buffer_flag *buffer_flag, struct log_buffer **log_buffer) +{ + struct tlogger_log *log = NULL; + errno_t ret; + struct log_buffer *buffer_tmp = NULL; + + log = reader->log; + if (!log) + return -EINVAL; + + buffer_tmp = (struct log_buffer*)log->buffer_info; + if (!buffer_tmp) + return -EINVAL; + + __asm__ volatile ("isb"); + __asm__ volatile ("dsb sy"); + + mutex_lock(&log->mutex_info); + ret = memcpy_s(buffer_flag, sizeof(*buffer_flag), &buffer_tmp->flag, + sizeof(buffer_tmp->flag)); + mutex_unlock(&log->mutex_info); + if (ret) { + tloge("memcpy failed %d\n", ret); + return -EAGAIN; + } + + *log_buffer = buffer_tmp; + return 0; +} + +#define LOG_BUFFER_MAX_LEN 0x100000 + +static ssize_t get_last_read_pos(struct log_buffer_flag *log_flag, + struct tlogger_reader *reader, uint32_t *log_last_pos, uint32_t *is_read) +{ + uint32_t buffer_max_len = g_log_mem_len - sizeof(*g_log_buffer); + + *is_read = 0; + + if (buffer_max_len > LOG_BUFFER_MAX_LEN) + return -EINVAL; + + *log_last_pos = log_flag->last_pos; + if (*log_last_pos == reader->r_off && + log_flag->write_loops == reader->r_loops) + return 0; + + if (log_flag->max_len < *log_last_pos || + log_flag->max_len > buffer_max_len) { + tloge("invalid data maxlen %x pos %x\n", + log_flag->max_len , *log_last_pos); + return -EFAULT; + } + + if (reader->r_off > log_flag->max_len) { + tloge("invalid data roff %x maxlen %x\n", + reader->r_off , log_flag->max_len); + return -EFAULT; + } + + *is_read = 1; + return 0; +} + +static void set_reader_position(struct reader_position *position, + const unsigned char *buffer_start, uint32_t max_len, uint32_t start_pos, uint32_t end_pos) +{ + position->buffer_start = buffer_start; + position->max_len = max_len; + position->start_pos = start_pos; + position->end_pos = end_pos; +} + +static ssize_t proc_read_ret(uint32_t buf_written, + const struct tlogger_reader *reader) +{ + ssize_t ret; + + if (!buf_written) { + ret = LOG_READ_STATUS_ERROR; + } else { + ret = buf_written; + tlogd("read length %u\n", buf_written); + g_last_read_offset = reader->r_off; + } + return ret; +} + +static ssize_t check_read_params(struct file *file, + char __user *buf, size_t count) +{ + if (count < LOG_ITEM_MAX_LEN) + return -EINVAL; + + if (!file || !buf) + return -EINVAL; + + return 0; +} + +/* + * If the sequence number of the last read position is smaller + * than the current minimum sequence number, the last read + * position is overwritten. And this time read data from + * minimum number, or read data from last position. + */ +static ssize_t trigger_parse_log(char __user *buf, size_t count, + uint32_t log_last_pos, struct log_buffer *log_buffer, + struct tlogger_reader *reader) +{ + bool user_buffer_left = false; + uint32_t buf_written; + struct reader_position position = {0}; + struct log_buffer_flag *buffer_flag = &(log_buffer->flag); + + if (buffer_flag->write_loops == reader->r_loops) { + set_reader_position(&position, log_buffer->buffer_start, + buffer_flag->max_len, reader->r_off, log_last_pos); + + buf_written = parse_log_item(buf, count, &position, + &reader->r_off, &user_buffer_left); + + return proc_read_ret(buf_written, reader); + } + + if (buffer_flag->write_loops > (reader->r_loops +1) || + ((buffer_flag->write_loops == (reader->r_loops + 1)) && + (reader->r_off < log_last_pos))) { + reader->r_off = log_last_pos; + reader->r_loops = buffer_flag->write_loops - 1; + } + + set_reader_position(&position, log_buffer->buffer_start, + buffer_flag->max_len, reader->r_off, buffer_flag->max_len); + + buf_written = parse_log_item(buf, count, &position, + &reader->r_off, &user_buffer_left); + + if (count > buf_written && user_buffer_left) { + set_reader_position(&position, log_buffer->buffer_start, + buffer_flag->max_len, 0, log_last_pos); + + buf_written += parse_log_item(buf + buf_written, + count - buf_written, &position, + &reader->r_off, &user_buffer_left); + + reader->r_loops = buffer_flag->write_loops; + } + + return proc_read_ret(buf_written, reader); +} + +static ssize_t process_tlogger_read(struct file *file, + char __user *buf, size_t count) +{ + struct tlogger_reader *reader = NULL; + struct log_buffer *log_buffer = NULL; + ssize_t ret; + uint32_t last_pos; + uint32_t is_read; + struct log_buffer_flag buffer_flag; + + ret = check_read_params(file, buf, count); + if (ret) + return ret; + + reader = file->f_priv; + if (!reader) + return -EINVAL; + + ret = get_buffer_info(reader, &buffer_flag, &log_buffer); + if (ret) + return ret; + + ret = get_last_read_pos(&buffer_flag, reader, &last_pos, &is_read); + if (!is_read) + return ret; + + return trigger_parse_log(buf, count, last_pos, log_buffer, reader); +} + +void tz_log_write(void) +{ + struct log_buffer *log_buffer = NULL; + + if (!g_log) + return; + + log_buffer = (struct log_buffer*)g_log->buffer_info; + if (!log_buffer) + return; + + if (g_last_read_offset != log_buffer->flag.last_pos) { + tlogd("wake up write tz log\n"); + wake_up_interruptible(&g_log->wait_queue_head); + } + + return; +} + +static struct tlogger_log *get_tlogger_log_by_minor(int minor) +{ + struct tlogger_log *log = NULL; + + list_for_each_entry(log, &m_log_list, logs) { + if (log->misc_device.minor == MISC_DYNAMIC_MINOR) + return log; + } + + return NULL; +} + +static int process_tlogger_open(struct file *file) +{ + struct tlogger_log *log = NULL; + struct tlogger_reader *reader = NULL; + + tlogd("open logger open ++\n"); + + tlogd("Before get log from minor\n"); + log = get_tlogger_log_by_minor(MISC_DYNAMIC_MINOR); + if (!log) + return -ENODEV; + + reader = kmalloc(sizeof(*reader), GFP_KERNEL); + if (ZERO_OR_NULL_PTR((unsigned long)(uintptr_t)reader)) + return -ENOMEM; + + reader->log = log; + reader->r_all = true; + reader->r_off = 0; + reader->r_loops = 0; + reader->r_sn = 0; + reader->r_failtimes = 0; + reader->r_is_tlogf = 0; + reader->r_from_cur = 0; + + INIT_LIST_HEAD(&reader->list); + + mutex_lock(&log->mutex_info); + list_add_tail(&reader->list, &log->readers); + g_tlogcat_count++; + mutex_unlock(&log->mutex_info); + + file->f_priv = reader; + tlogd("tlogcat count %u\n", g_tlogcat_count); + return 0; +} + +static int process_tlogger_release(struct file *file) +{ + struct tlogger_reader *reader = NULL; + struct tlogger_log *log = NULL; + + tlogd("logger_release ++\n"); + + if (!file) + return -1; + + reader = file->f_priv; + if (!reader) { + tloge("reader is null\n"); + return -1; + } + + log = reader->log; + if (!log) { + tloge("log is null\n"); + return -1; + } + + mutex_lock(&log->mutex_info); + list_del(&reader->list); + if (g_tlogcat_count >= 1) + g_tlogcat_count--; + mutex_unlock(&log->mutex_info); + + tlogi("logger_release r_is_tlogf-%u\n", reader->r_is_tlogf); + if (reader->r_is_tlogf) + g_tlogcat_f = 0; + + kfree(reader); + tlogd("tlogcat count %u\n", g_tlogcat_count); + return 0; +} + +static ssize_t process_tlogger_poll(struct file *file, + poll_table *wait) +{ + struct tlogger_reader *reader = NULL; + struct tlogger_log *log = NULL; + struct log_buffer *buffer = NULL; + uint32_t ret = POLLOUT | POLLWRNORM; + + tlogd("logger_poll ++\n"); + if (!file) { + tloge("file is null\n"); + return ret; + } + + reader = file->f_priv; + if (!reader) { + tloge("the private data is null\n"); + return ret; + } + + log = reader->log; + if (!log) { + tloge("log is null\n"); + return ret; + } + + buffer = (struct log_buffer*)log->buffer_info; + if (!buffer) { + tloge("buffer is null\n"); + return ret; + } + + poll_wait(file, &log->wait_queue_head, wait); + + if (buffer->flag.last_pos != reader->r_off) + ret |= POLLIN | POLLRDNORM; + + return ret; +} + +#define SET_READ_POS 1U +static void set_reader_cur_pos(const struct file *file) +{ + struct tlogger_reader *reader = NULL; + struct tlogger_log *log = NULL; + struct log_buffer *buffer = NULL; + + reader = file->f_priv; + if (!reader) + return; + + log = reader->log; + if (!log) + return; + + buffer = (struct log_buffer*)log->buffer_info; + if (!buffer) + return; + + reader->r_from_cur = SET_READ_POS; + reader->r_off = buffer->flag.last_pos; + reader->r_loops = buffer->flag.write_loops; +} + +static void set_tlogcat_f_stat(const struct file *file) +{ + struct tlogger_reader *reader = NULL; + + if (!file) + return; + + reader = file->f_priv; + if (!reader) + return; + + reader->r_is_tlogf = 1; + g_tlogcat_f = 1; + + tlogi("set tlogcat_f-%d\n", g_tlogcat_f); + return; +} + +static int get_tlogcat_f_stat(void) +{ + tlogi("get tlogcat_f-%d\n", g_tlogcat_f); + return g_tlogcat_f; +} + +static int check_user_arg(unsigned long arg, size_t arg_len) +{ + return (int)access_ok(VERIFY_READ, (unsigned long)(uintptr_t)arg, arg_len); +} + +static int get_teeos_version(uint32_t cmd, unsigned long arg) +{ + int ret; + + if (!(_IOC_DIR(cmd) & _IOC_READ)) { + tloge("check get version cmd failed\n"); + return -1; + } + + ret = check_user_arg(arg, + sizeof(g_log_buffer->flag.version_info)); + if (!ret) { + tloge("check version info arg failed\n"); + return -1; + } + + if (copy_to_user((void __user *)(uintptr_t)arg, + (void *)g_log_buffer->flag.version_info, + sizeof(g_log_buffer->flag.version_info))) { + tloge("version info copy failed\n"); + return -1; + } + + return 0; +} + +static int process_tlogger_ioctl(struct file *file, + int cmd, unsigned long arg) +{ + struct tlogger_log *log = NULL; + long ret = -EINVAL; + + if (!file) + return -1; + + log = get_reader_log(file); + if (!log) { + tloge("log is null\n"); + return -1; + } + + tlogd("logger_ioctl start ++\n"); + mutex_lock(&log->mutex_info); + + switch (cmd) { + case TEELOGGER_GET_VERSION: + if (!get_teeos_version(cmd, arg)) + ret = 0; + break; + case TEELOGGER_SET_READERPOS_CUR: + set_reader_cur_pos(file); + ret = 0; + break; + case TEELOGGER_SET_TLOGCAT_STAT: + set_tlogcat_f_stat(file); + ret = 0; + break; + case TEELOGGER_GET_TLOGCAT_STAT: + ret = get_tlogcat_f_stat(); + break; + default: + tloge("ioctl error default\n"); + break; + } + + mutex_unlock(&log->mutex_info); + return ret; +} + +#ifdef CONFIG_COMPAT +static int process_tlogger_compat_ioctl(struct file *file, + int cmd, unsigned long arg) +{ + tlogd("logger_compat_ioctl ++\n"); + arg = (unsigned long)(uintptr_t)compat_ptr(arg); + return process_tlogger_ioctl(file, cmd, arg); +} +#endif + +static const struct file_operations_vfs g_logger_fops = { + .read = process_tlogger_read, + .poll = process_tlogger_poll, + .ioctl = process_tlogger_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = process_tlogger_compat_ioctl, +#endif + .open = process_tlogger_open, + .close = process_tlogger_release, +}; + +static int __init register_device(const char *log_name, + uintptr_t addr, int size) +{ + int ret; + struct tlogger_log *log = NULL; + unsigned char *buffer = (unsigned char *)addr; + + log = kzalloc(sizeof(*log), GFP_KERNEL); + if (!log) { + tloge("kzalloc is failed\n"); + return -ENOMEM; + } + log->buffer_info = buffer; + log->misc_device.minor = MISC_DYNAMIC_MINOR; + + log->misc_device.name = TLOGCAT_DEV_NAME; + log->misc_device.op = &g_logger_fops; + + init_waitqueue_head(&log->wait_queue_head); + INIT_LIST_HEAD(&log->readers); + mutex_init(&log->mutex_info); + INIT_LIST_HEAD(&log->logs); + list_add_tail(&log->logs, &m_log_list); + + /* register misc device for this log */ + ret = misc_register_adapt(&log->misc_device); + if (unlikely(ret)) { + tloge("failed to register misc device:%s\n", + log->misc_device.name); + goto out_free_log; + } + g_log = log; + return 0; + +out_free_log: + if (log->misc_device.name) + kfree(log->misc_device.name); + + kfree(log); + return ret; +} + +#ifdef CONFIG_LOGFILE +static struct log_item *msg_get_next(const unsigned char *buffer_start, + uint32_t read_pos, int scope_len, uint32_t max_len) +{ + int i = 0; + struct log_item *item = NULL; + uint32_t item_max_size; + uint32_t len; + + while (i <= scope_len && + ((read_pos + i + sizeof(*item)) < max_len)) { + len = (uint32_t)(scope_len - i); + item_max_size = + ((len > LOG_ITEM_MAX_LEN) ? LOG_ITEM_MAX_LEN : len); + item = (struct log_item *)(buffer_start + read_pos + i); + + if (check_log_item_validite(item, item_max_size)) { + if ((read_pos + i + sizeof(*item) + + item->buffer_len) > max_len) { + tloge("check item len error\n"); + return NULL; + } + + return item; + } + + i += LOG_ITEM_LEN_ALIGN; + item = NULL; + } + + return NULL; +} + +#define OPEN_FILE_MODE 0640U +#define ROOT_UID 0 +#define ROOT_GID 0 + +static int tlogger_chown(const char *file_path, uint32_t file_path_len) +{ + (void)file_path_len; + uid_t user = ROOT_UID; + gid_t group = ROOT_GID; + int ret; + + get_log_chown(&user, &group); + + /* not need modify chown attr */ + if (group == ROOT_GID && user == ROOT_UID) + return 0; + + ret = (int)koadpt_sys_chown( + (const char __user *)file_path, user, group); + if (ret) { + tloge("sys chown for last teemsg file error\n"); + return -1; + } + + return 0; +} + +static int write_version_to_msg(struct file *filep, + loff_t *pos) +{ + ssize_t write_len; + + /* first write tee versino info */ + write_len = koadpt_vfs_write(filep, g_log_buffer->flag.version_info, + strlen(g_log_buffer->flag.version_info), pos); + if (write_len < 0) { + tloge("Failed to write to last teemsg version\n"); + return -1; + } + + tlogd("Succeed to Write to last teemsg version, len=%zd\n", write_len); + return 0; +} + +static int write_part_log_to_msg(struct file *filep, + const unsigned char *buffer, uint32_t buffer_max_len, loff_t *pos, + uint32_t read_off, uint32_t read_off_end) +{ + struct log_item *next_item = NULL; + ssize_t item_len; + ssize_t total_len = 0; + ssize_t write_len; + + next_item = msg_get_next(buffer, read_off, + LOG_ITEM_MAX_LEN, buffer_max_len); + + while (next_item && read_off <= read_off_end) { + item_len = next_item->buffer_len + sizeof(*next_item); + write_len = koadpt_vfs_write(filep, next_item->log_buffer, + next_item->real_len, pos); + if (write_len < 0) { + tloge("Failed to write last teemsg %zd\n", write_len); + return -1; + } + + tlogd("Succeed to Write last teemsg, len=%zd\n", write_len); + total_len += item_len; + read_off = (unsigned char *)next_item - buffer + item_len; + if (total_len >= buffer_max_len) + break; + + next_item = msg_get_next(buffer, read_off, + LOG_ITEM_MAX_LEN, buffer_max_len); + } + + return 0; +} + +static int write_log_to_msg(struct file *filep, + const unsigned char *buffer, uint32_t buffer_max_len, loff_t *pos, + uint32_t read_off, uint32_t read_off_end) +{ + if (read_off < read_off_end) { + return write_part_log_to_msg(filep, buffer, buffer_max_len, pos, + read_off, read_off_end); + } else { + if (write_part_log_to_msg(filep, buffer, buffer_max_len, pos, + read_off, buffer_max_len) != 0) + return -1; + return write_part_log_to_msg(filep, buffer, buffer_max_len, pos, + 0, read_off_end); + } +} + +static void update_dumpmsg_offset(uint32_t *read_start, uint32_t *read_end, + uint32_t read_off, uint32_t read_off_end, uint32_t *dump_start_flag, uint32_t *dump_end_flag) +{ + struct log_item *next_item = NULL; + unsigned char *buffer = g_log_buffer->buffer_start; + uint32_t buffer_max_len = g_log_mem_len - sizeof(*g_log_buffer); + ssize_t item_len; + ssize_t total_len = 0; + + next_item = msg_get_next(buffer, read_off, + LOG_ITEM_MAX_LEN, buffer_max_len); + + while (next_item && read_off <= read_off_end) { + item_len = next_item->buffer_len + sizeof(*next_item); + if (strstr(next_item->log_buffer, DUMP_START_MAGIC)) { + *read_start = read_off; + *dump_start_flag = 1; + } else if (strstr(next_item->log_buffer, DUMP_END_MAGIC)) { + *read_end = read_off; + *dump_end_flag = 1; + } + read_off = (unsigned char *)next_item - buffer + item_len; + total_len += item_len; + if (total_len >= buffer_max_len) + break; + + next_item = msg_get_next(buffer, read_off, + LOG_ITEM_MAX_LEN, buffer_max_len); + } +} + +static int get_dumpmsg_offset(uint32_t *read_start, uint32_t *read_end) +{ + uint32_t read_off = *read_start; + uint32_t read_off_end = *read_end; + uint32_t buffer_max_len = g_log_mem_len - sizeof(*g_log_buffer); + uint32_t dump_start_flag = 0; + uint32_t dump_end_flag = 0; + + if (read_off < read_off_end) { + update_dumpmsg_offset(read_start, read_end, read_off, read_off_end, + &dump_start_flag, &dump_end_flag); + } else { + update_dumpmsg_offset(read_start, read_end, read_off, buffer_max_len, + &dump_start_flag, &dump_end_flag); + update_dumpmsg_offset(read_start, read_end, 0, read_off_end, + &dump_start_flag, &dump_end_flag); + } + + if (dump_start_flag == 0 || dump_end_flag == 0) { + tloge("can't find dump start or end\n"); + return -1; + } else { + return 0; + } +} + +static int get_msg_buffer(unsigned char **buffer, uint32_t *buffer_max_len, + uint32_t *read_start, uint32_t *read_end, + const char *file_path, uint32_t file_path_len) +{ + (void)file_path_len; + errno_t rc; + int ret; + unsigned char *addr = NULL; + + if (!g_log_buffer) + return -1; + + *buffer_max_len = g_log_mem_len - sizeof(*g_log_buffer); + + if (*buffer_max_len > LOG_BUFFER_MAX_LEN) + return 0; + + *read_start = 0; + *read_end = *buffer_max_len; +#ifdef CONFIG_TEE_LOG_DUMP_PATH + if (strcmp(file_path, CONFIG_TEE_LOG_DUMP_PATH) == 0) { + *read_start = g_last_read_offset; + *read_end = ((struct log_buffer*)g_log->buffer_info)->flag.last_pos; + if (get_dumpmsg_offset(read_start, read_end) != 0) { + tloge("get dump offset failed\n"); + return -1; + } + } +#endif + addr = kmalloc(*buffer_max_len, GFP_KERNEL); + if (ZERO_OR_NULL_PTR((unsigned long)(uintptr_t)addr)) { + ret = -ENOMEM; + goto free_res; + } + + rc = memcpy_s(addr, *buffer_max_len, g_log_buffer->buffer_start, + *buffer_max_len); + if (rc) { + tloge("memcpy failed %d\n", rc); + ret = -EAGAIN; + goto free_res; + } + + *buffer = addr; + return 0; + +free_res: + if (addr) + kfree(addr); + + return ret; +} + +static int open_msg_file(struct file **file, + const char *file_path, uint32_t file_path_len) +{ + (void)file_path_len; + struct file *filep = NULL; + + filep = filp_open(file_path, O_CREAT | O_RDWR | O_TRUNC, OPEN_FILE_MODE); + if (!filep || IS_ERR(filep)) { + tloge("open last teemsg file err %ld\n", PTR_ERR(filep)); + return -1; + } + + *file = filep; + return 0; +} +#endif + +#ifdef CONFIG_LOGFILE +int tlogger_store_msg(const char *file_path, uint32_t file_path_len) +{ + struct file *filep = NULL; + mm_segment_t old_fs; + loff_t pos = 0; + int ret; + uint32_t buffer_max_len = 0; + unsigned char *buffer = NULL; + uint32_t read_start = 0; + uint32_t read_end = 0; + + if (!file_path || file_path_len > TEE_LOG_FILE_NAME_MAX) { + tloge("file path is invalid\n"); + return -1; + } + + if (!g_tlogcat_count) { + tlogd("tlogcat count %u\n", g_tlogcat_count); + return 0; + } + + /* copy logs from log memory, then parse the logs */ + ret = get_msg_buffer(&buffer, &buffer_max_len, + &read_start, &read_end, file_path, file_path_len); + if (ret) + return ret; + + /* exception handling, store trustedcore exception info to file */ + ret = open_msg_file(&filep, file_path, file_path_len); + if (ret) + goto free_res; + + old_fs = get_fs(); + set_fs(KERNEL_DS); + + ret = tlogger_chown(file_path, file_path_len); + if (ret) + goto clear_fs; + + ret = write_version_to_msg(filep, &pos); + if (ret) + goto clear_fs; + + ret = write_log_to_msg(filep, buffer, buffer_max_len, + &pos, read_start, read_end); + +clear_fs: + set_fs(old_fs); +free_res: + if (buffer) { + kfree(buffer); + buffer = NULL; + } + + if (filep != NULL) { + vfs_fsync(filep, 0); + filp_close(filep, 0); + } + + /* trigger write teeos log */ + tz_log_write(); + return ret; +} +#else +int tlogger_store_msg(const char *file_path, uint32_t file_path_len) +{ + (void)file_path; + (void)file_path_len; + return 0; +} +#endif + +#ifdef DEF_ENG +#define KERNEL_IMG_IS_ENG 1 +#endif +int register_mem_to_teeos(uint64_t mem_addr, uint32_t mem_len, bool is_cache_mem) +{ + struct tc_ns_smc_cmd smc_cmd = { {0}, 0 }; + struct mb_cmd_pack *mb_pack = NULL; + int ret; + + mb_pack = mailbox_alloc_cmd_pack(); + if (!mb_pack) { + tloge("mailbox alloc failed\n"); + return -ENOMEM; + } + + smc_cmd.cmd_type = CMD_TYPE_GLOBAL; + smc_cmd.cmd_id = GLOBAL_CMD_ID_REGISTER_LOG_MEM; + mb_pack->operation.paramtypes = teec_param_types( + TEE_PARAM_TYPE_VALUE_INPUT, + TEE_PARAM_TYPE_VALUE_INPUT, + TEE_PARAM_TYPE_VALUE_INPUT, + TEE_PARAM_TYPE_NONE); + mb_pack->operation.params[0].value.a = mem_addr; + mb_pack->operation.params[0].value.b = mem_addr >> ADDR_TRANS_NUM; + mb_pack->operation.params[1].value.a = mem_len; +#ifdef DEF_ENG + mb_pack->operation.params[1].value.b = KERNEL_IMG_IS_ENG; +#endif + /* + * is_cache_mem: true, teeos map this memory for cache + * style; or else map to no cache style + */ + mb_pack->operation.params[2].value.a = is_cache_mem; + + 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); + mailbox_free(mb_pack); + if (ret) + tloge("Send log mem info failed\n"); + + return ret; +} + +static int register_mem_cfg(uint64_t *addr, uint32_t *len) +{ + int ret; + ret = register_log_mem(addr, len); + if (ret) + tloge("register log mem failed %x\n", ret); + + ret = register_log_exception(); + if (ret) + tloge("teeos register exception to log module failed\n"); + + return ret; +} + +static int check_log_mem(uint64_t mem_addr, uint32_t mem_len) +{ + if (mem_len < TEMP_LOG_MEM_SIZE) { + tloge("log mem init error, too small len:0x%x\n", mem_len); + return -1; + } + + if (!mem_addr) { + tloge("mem init failed!!! addr is 0\n"); + return -1; + } + return 0; +} + +static int register_tloger(void) +{ + int ret; + uint64_t mem_addr = 0; + + ret = register_mem_cfg(&mem_addr, &g_log_mem_len); + if (ret) + return ret; + + ret = check_log_mem(mem_addr, g_log_mem_len); + if (ret) + return ret; + + g_log_buffer = + (struct log_buffer *)map_log_mem(mem_addr, g_log_mem_len); + if (!g_log_buffer) + return -ENOMEM; + + g_log_buffer->flag.max_len = g_log_mem_len - sizeof(*g_log_buffer); + + tloge("tlogcat verison 1.0.0\n"); + ret = register_device(LOGGER_LOG_TEEOS, (uintptr_t)g_log_buffer, + sizeof(*g_log_buffer) + g_log_buffer->flag.max_len); + if (ret) { + unmap_log_mem((int *)g_log_buffer); + g_log_buffer = NULL; + g_log_mem_len = 0; + } + + return ret; +} + +static void unregister_mem_cfg(void) +{ + if (g_log_buffer) + unmap_log_mem((int *)g_log_buffer); + + unregister_log_exception(); +} + +static void unregister_tlogger(void) +{ + struct tlogger_log *current_log = NULL; + struct tlogger_log *next_log = NULL; + + list_for_each_entry_safe(current_log, next_log, &m_log_list, logs) { + /* we have to delete all the entry inside m_log_list */ + misc_register_adapt(¤t_log->misc_device); + kfree(current_log->misc_device.name); + list_del(¤t_log->logs); + kfree(current_log); + } + + unregister_mem_cfg(); + g_log_buffer = NULL; + g_log_mem_len = 0; +} + +int init_tlogger_service(void) +{ + return register_tloger(); +} + +void exit_tlogger_service(void) +{ + unregister_tlogger(); +} diff --git a/liteos/tlogger/tlogger.h b/liteos/tlogger/tlogger.h new file mode 100644 index 0000000..0b758f6 --- /dev/null +++ b/liteos/tlogger/tlogger.h @@ -0,0 +1,57 @@ +/* + * tlogger.h + * + * TEE Logging Subsystem, read the tee os log from rdr memory + * + * 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 TLOGGER_H +#define TLOGGER_H + +#include + +#ifdef CONFIG_TEELOG +void tz_log_write(void); +int tlogger_store_msg(const char *file_path, uint32_t file_path_len); +int register_mem_to_teeos(uint64_t mem_addr, uint32_t mem_len, bool is_cache_mem); + +int init_tlogger_service(void); +void exit_tlogger_service(void); + +#else +static inline void tz_log_write(void) +{ + return; +} + +static inline int tlogger_store_msg(const char *file_path, uint32_t file_path_len) +{ + (void)file_path; + (void)file_path_len; + return 0; +} +static inline int register_mem_to_teeos(uint64_t mem_addr, uint32_t mem_len, + bool is_cache_mem) +{ + (void)mem_addr; + (void)mem_len; + return 0; +} +static inline int init_tlogger_service(void) +{ + return 0; +} +static inline void exit_tlogger_service(void) +{ +} +#endif +#endif