mirror of
https://github.com/openharmony/tee_tee_tzdriver.git
synced 2026-07-01 06:41:57 -04:00
60950d3b60
signed-off-by: YuanHao<yuanhao34@huawei.com>
1316 lines
32 KiB
C
1316 lines
32 KiB
C
/*
|
|
* 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 <linux/sched.h>
|
|
#include <linux/list.h>
|
|
#include <linux/mutex.h>
|
|
#include <linux/kthread.h>
|
|
#include <linux/freezer.h>
|
|
#include <linux/module.h>
|
|
#include <linux/version.h>
|
|
#include <linux/atomic.h>
|
|
#include <linux/fs.h>
|
|
#include <linux/file.h>
|
|
#include <linux/path.h>
|
|
#include <linux/uaccess.h>
|
|
#include <linux/mm.h>
|
|
#include <linux/mm_types.h>
|
|
#if (KERNEL_VERSION(4, 14, 0) <= LINUX_VERSION_CODE)
|
|
#include <linux/sched/mm.h>
|
|
#include <linux/sched/task.h>
|
|
#endif
|
|
#if (KERNEL_VERSION(5, 4, 0) <= LINUX_VERSION_CODE)
|
|
#include <linux/mman.h>
|
|
#else
|
|
#include <asm/mman.h>
|
|
#endif
|
|
#include <linux/signal.h>
|
|
#include <securec.h>
|
|
#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();
|
|
}
|