WIP: security: Add tlk

Change-Id: I259e788bf7e1e2be9682135b46a7b2a6c31e3184
This commit is contained in:
Aaron Kling
2025-08-17 14:56:50 -05:00
parent 34842b2455
commit 6e0184cc42
14 changed files with 2357 additions and 0 deletions
+29
View File
@@ -0,0 +1,29 @@
/*
* Copyright (c) 2013-2018 NVIDIA Corporation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef __OTE_PROTOCOL_H__
#define __OTE_PROTOCOL_H__
int tegra_set_vpr_params(void *vpr_base, size_t vpr_size);
void trusty_restore_keyslots(void);
void tlk_restore_keyslots(void);
int te_is_secos_dev_enabled(void);
#endif
+91
View File
@@ -0,0 +1,91 @@
/*
* Copyright (c) 2013-2019 NVIDIA Corporation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef __UAPI_TLK_OTE_PROTOCOL_H__
#define __UAPI_TLK_OTE_PROTOCOL_H__
#include <linux/ioctl.h>
#include <linux/types.h>
#define TE_IOCTL_MAGIC_NUMBER ('t')
#define TE_IOCTL_OPEN_CLIENT_SESSION \
_IOWR(TE_IOCTL_MAGIC_NUMBER, 0x10, union te_cmd)
#define TE_IOCTL_CLOSE_CLIENT_SESSION \
_IOWR(TE_IOCTL_MAGIC_NUMBER, 0x11, union te_cmd)
#define TE_IOCTL_LAUNCH_OPERATION \
_IOWR(TE_IOCTL_MAGIC_NUMBER, 0x14, union te_cmd)
/* secure storage ioctl */
#define TE_IOCTL_SS_CMD \
_IOR(TE_IOCTL_MAGIC_NUMBER, 0x30, int)
#define TE_IOCTL_SS_CMD_GET_NEW_REQ 1
#define TE_IOCTL_SS_CMD_REQ_COMPLETE 2
#define TE_IOCTL_MIN_NR _IOC_NR(TE_IOCTL_OPEN_CLIENT_SESSION)
#define TE_IOCTL_MAX_NR _IOC_NR(TE_IOCTL_SS_CMD)
struct te_service_id {
uint32_t time_low;
uint16_t time_mid;
uint16_t time_hi_and_version;
uint8_t clock_seq_and_node[8];
};
struct te_operation {
uint32_t command;
uint32_t status;
uint64_t list_head;
uint64_t list_tail;
uint32_t list_count;
uint32_t interface_side;
};
/*
* OpenSession
*/
struct te_opensession {
struct te_service_id dest_uuid;
struct te_operation operation;
uint64_t answer;
};
/*
* CloseSession
*/
struct te_closesession {
uint32_t session_id;
uint64_t answer;
};
/*
* LaunchOperation
*/
struct te_launchop {
uint32_t session_id;
struct te_operation operation;
uint64_t answer;
};
union te_cmd {
struct te_opensession opensession;
struct te_closesession closesession;
struct te_launchop launchop;
};
#endif
+2
View File
@@ -228,6 +228,8 @@ source "security/ipe/Kconfig"
source "security/integrity/Kconfig"
source "security/tlk_driver/Kconfig"
choice
prompt "First legacy 'major LSM' to be initialized"
default DEFAULT_SECURITY_SELINUX if SECURITY_SELINUX
+2
View File
@@ -29,3 +29,5 @@ obj-$(CONFIG_SECURITY_IPE) += ipe/
# Object integrity file lists
obj-$(CONFIG_INTEGRITY) += integrity/
obj-$(CONFIG_TRUSTED_LITTLE_KERNEL) += tlk_driver/
+7
View File
@@ -0,0 +1,7 @@
config TRUSTED_LITTLE_KERNEL
bool "Enable Open Trusted Execution driver"
help
This option adds kernel support for communication with the
Trusted LK secure OS monitor/runtime support.
If you are unsure how to answer this question, answer N.
+27
View File
@@ -0,0 +1,27 @@
#
# Copyright (c) 2013-2016, NVIDIA Corporation. All rights reserved.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
# more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
subdir-ccflags-y += -Werror
tlk_driver-objs += ote_device.o
tlk_driver-objs += ote_comms.o
tlk_driver-objs += ote_fs.o
tlk_driver-objs += ote_asm.o
tlk_driver-objs += ote_log.o
tlk_driver-objs += ote_memory.o
obj-$(CONFIG_TRUSTED_LITTLE_KERNEL) += tlk_driver.o
+46
View File
@@ -0,0 +1,46 @@
/*
* Copyright (c) 2014-2016, NVIDIA Corporation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License.
*
* 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 <asm/asm-offsets.h>
#include <asm/ptrace.h>
#include <linux/linkage.h>
#include <linux/init.h>
#ifdef CONFIG_ARM64
/* uint32_t tlk_generic_smc(uint32_t arg0, uint32_t arg1, uint32_t arg2) */
SYM_FUNC_START(_tlk_generic_smc)
smc #0
ret
SYM_FUNC_END(_tlk_generic_smc)
SYM_FUNC_START(_tlk_get_mair)
mrs x0, mair_el1
ret
SYM_FUNC_END(_tlk_get_mair)
#else
.arch_extension sec
SYM_FUNC_START(_tlk_generic_smc)
smc #0
mov pc, lr
SYM_FUNC_END(_tlk_generic_smc)
SYM_FUNC_START(_tlk_get_mair)
mrc p15, 0, r0, c10, c2, 0
mrc p15, 0, r1, c10, c2, 1
mov pc, lr
SYM_FUNC_END(_tlk_get_mair)
#endif
+499
View File
@@ -0,0 +1,499 @@
/*
* Copyright (c) 2012-2019 NVIDIA Corporation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <linux/atomic.h>
#include <linux/uaccess.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/fs.h>
#include <linux/printk.h>
#include <linux/ioctl.h>
#include <linux/sched.h>
#include <linux/mm.h>
#include <linux/pagemap.h>
#include <linux/ote_protocol.h>
#include <asm/smp_plat.h>
#include "ote_protocol.h"
bool verbose_smc;
core_param(verbose_smc, verbose_smc, bool, 0644);
#define SET_RESULT(req, r, ro) { req->result = r; req->result_origin = ro; }
static struct te_session *te_get_session(struct tlk_context *context,
uint32_t session_id)
{
struct te_session *session, *tmp_session;
list_for_each_entry_safe(session, tmp_session,
&context->session_list, list) {
if (session->session_id == session_id)
return session;
}
return NULL;
}
struct tlk_smc_work_args {
uint32_t arg0;
uintptr_t arg1;
uint32_t arg2;
};
static long tlk_generic_smc_on_cpu0(void *args)
{
struct tlk_smc_work_args *work;
int callback_status = 0;
uint32_t retval;
work = (struct tlk_smc_work_args *)args;
retval = _tlk_generic_smc(work->arg0, work->arg1, work->arg2);
while (retval == TE_ERROR_PREEMPT_BY_IRQ ||
retval == TE_ERROR_PREEMPT_BY_FS) {
if (retval == TE_ERROR_PREEMPT_BY_FS)
callback_status = tlk_ss_op();
retval = _tlk_generic_smc(TE_SMC_RESTART, callback_status, 0);
}
/* Print TLK logs if any */
ote_print_logs();
return retval;
}
/*
* This routine is called both from normal threads and worker threads.
* The worker threads have PF_NO_SETAFFINITY set, so any calls to
* sched_setaffinity will fail.
*
* If it's a worker thread, always schedule work on CPU0.
* If it's not a worker thread, try to switch to CPU0. If this fails,
* then schedule work on CPU0.
*/
uint32_t tlk_send_smc(uint32_t arg0, uintptr_t arg1, uintptr_t arg2)
{
struct tlk_smc_work_args work_args;
work_args.arg0 = arg0;
work_args.arg1 = arg1;
work_args.arg2 = arg2;
return work_on_cpu(0,
tlk_generic_smc_on_cpu0, &work_args);
}
/*
* Do an SMC call
*/
static void do_smc(struct te_request *request, struct tlk_device *dev)
{
uint32_t smc_args;
uint32_t smc_params = 0;
uint32_t retval = 0;
smc_args = (uintptr_t)request - (uintptr_t)dev->req_addr;
if (request->params) {
smc_params =
(uintptr_t)request->params - (uintptr_t)dev->req_addr;
}
retval = tlk_send_smc(request->type, smc_args, smc_params);
/**
* Check for return code from TLK kernel and propagate to NS userspace
*
* Under certain situations, TLK kernel may return an error code
* _without_ updating the shared request buffer.
*
* This could give a false impression to the user-app about status of
* the request (if default value of request->result is OTE_SUCCESS).
*
* Propagate the error code to NS user-app in case of such scenario.
*/
if ((retval != OTE_SUCCESS) && (request->result == OTE_SUCCESS)) {
pr_err("%s: overriding result_origin field\n", __func__);
request->result = retval;
request->result_origin = OTE_RESULT_ORIGIN_KERNEL;
}
}
void tlk_restore_keyslots(void)
{
uint32_t retval;
if (!te_is_secos_dev_enabled())
return;
/* Share the same lock used when request is send from user side */
mutex_lock(&smc_lock);
retval = tlk_send_smc(TE_SMC_TA_EVENT, TA_EVENT_RESTORE_KEYS, 0);
mutex_unlock(&smc_lock);
if (retval != OTE_SUCCESS) {
pr_err("%s: smc failed err (0x%x)\n", __func__, retval);
}
}
EXPORT_SYMBOL(tlk_restore_keyslots);
/*
* Open session SMC (supporting client-based te_open_session() calls)
*/
void te_open_session(struct te_opensession *cmd,
struct te_request *request,
struct tlk_context *context)
{
struct te_session *session;
int ret;
session = kzalloc(sizeof(struct te_session), GFP_KERNEL);
if (!session) {
SET_RESULT(request, OTE_ERROR_OUT_OF_MEMORY,
OTE_RESULT_ORIGIN_API);
return;
}
INIT_LIST_HEAD(&session->list);
INIT_LIST_HEAD(&session->temp_shmem_list);
INIT_LIST_HEAD(&session->inactive_persist_shmem_list);
INIT_LIST_HEAD(&session->persist_shmem_list);
request->type = TE_SMC_OPEN_SESSION;
ret = te_prep_mem_buffers(request, session);
if (ret != OTE_SUCCESS) {
pr_err("%s: te_prep_mem_buffers failed err (0x%x)\n",
__func__, ret);
SET_RESULT(request, ret, OTE_RESULT_ORIGIN_API);
kfree(session);
return;
}
memcpy(&request->dest_uuid,
&cmd->dest_uuid,
sizeof(struct te_service_id));
pr_debug("OPEN_CLIENT_SESSION: 0x%x 0x%x 0x%x 0x%x\n",
request->dest_uuid[0],
request->dest_uuid[1],
request->dest_uuid[2],
request->dest_uuid[3]);
do_smc(request, context->dev);
if (request->result)
goto error;
/* release temporary mem buffers */
te_release_mem_buffers(&session->temp_shmem_list);
/* move persistent mem buffers to active list */
te_activate_persist_mem_buffers(session);
/* save off session_id and add to list */
session->session_id = request->session_id;
list_add_tail(&session->list, &context->session_list);
return;
error:
/* release buffers allocated in te_prep_mem_buffers */
te_release_mem_buffers(&session->temp_shmem_list);
te_release_mem_buffers(&session->inactive_persist_shmem_list);
kfree(session);
}
/*
* Close session SMC (supporting client-based te_close_session() calls)
*/
void te_close_session(struct te_closesession *cmd,
struct te_request *request,
struct tlk_context *context)
{
struct te_session *session;
request->session_id = cmd->session_id;
request->type = TE_SMC_CLOSE_SESSION;
do_smc(request, context->dev);
if (request->result)
pr_info("%s: error closing session: 0x%08x\n",
__func__, request->result);
session = te_get_session(context, cmd->session_id);
if (!session) {
pr_info("%s: session_id not found: 0x%x\n",
__func__, cmd->session_id);
return;
}
/* free session state */
te_release_mem_buffers(&session->persist_shmem_list);
list_del(&session->list);
kfree(session);
}
/*
* Launch operation SMC (supporting client-based te_launch_operation() calls)
*/
void te_launch_operation(struct te_launchop *cmd,
struct te_request *request,
struct tlk_context *context)
{
struct te_session *session;
int ret;
session = te_get_session(context, cmd->session_id);
if (!session) {
pr_info("%s: session_id not found: 0x%x\n",
__func__, cmd->session_id);
SET_RESULT(request, OTE_ERROR_BAD_PARAMETERS,
OTE_RESULT_ORIGIN_API);
return;
}
request->session_id = cmd->session_id;
request->command_id = cmd->operation.command;
request->type = TE_SMC_LAUNCH_OPERATION;
ret = te_prep_mem_buffers(request, session);
if (ret != OTE_SUCCESS) {
pr_err("%s: te_prep_mem_buffers failed err (0x%x)\n",
__func__, ret);
SET_RESULT(request, ret, OTE_RESULT_ORIGIN_API);
return;
}
do_smc(request, context->dev);
if (request->result)
goto error;
/* move persistent mem buffers to active list */
te_activate_persist_mem_buffers(session);
/* release temporary mem buffers */
te_release_mem_buffers(&session->temp_shmem_list);
return;
error:
/* release buffers allocated in te_prep_mem_buffers */
te_release_mem_buffers(&session->temp_shmem_list);
te_release_mem_buffers(&session->inactive_persist_shmem_list);
}
/*
* Command to open a session with the trusted app.
* This API should only be called from the kernel space.
* Takes UUID of the TA and size as argument
* Returns Session ID if success or ERR when failure
*/
int te_open_trusted_session_tlk(u32 *ta_uuid, u32 uuid_size,
u32 *session_id)
{
struct te_request *request;
struct te_cmd_req_desc *cmd_desc = NULL;
mutex_lock(&smc_lock);
/* Open & submit the work to SMC */
cmd_desc = te_get_free_cmd_desc(&tlk_dev);
if (!cmd_desc) {
pr_err("%s: failed to get cmd_desc\n", __func__);
goto error;
}
request = cmd_desc->req_addr;
memset(request, 0, sizeof(struct te_request));
request->type = TE_SMC_OPEN_SESSION;
if (uuid_size != sizeof(request->dest_uuid)) {
pr_err("%s: Invalid size sent!\n", __func__);
goto error;
}
if (ta_uuid == NULL || session_id == NULL) {
pr_err("%s: Null parameters sent!\n", __func__);
goto error;
}
memcpy(request->dest_uuid, ta_uuid, sizeof(*ta_uuid)*4);
do_smc(request, &tlk_dev);
if (request->result) {
pr_err("%s: error opening session: 0x%08x, 0x%08x\n",
__func__, request->result, request->result_origin);
goto error;
}
*session_id = request->session_id;
if (cmd_desc)
te_put_used_cmd_desc(&tlk_dev, cmd_desc);
mutex_unlock(&smc_lock);
return OTE_SUCCESS;
error:
if (cmd_desc)
te_put_used_cmd_desc(&tlk_dev, cmd_desc);
mutex_unlock(&smc_lock);
return OTE_ERROR_GENERIC;
}
EXPORT_SYMBOL(te_open_trusted_session_tlk);
/*
* Command to close session opened with the trusted app.
* This API should only be called from the kernel space.
* Takes session Id and UUID of the TA as arguments
*/
void te_close_trusted_session_tlk(u32 session_id, u32 *ta_uuid,
u32 uuid_size)
{
struct te_request *request;
struct te_cmd_req_desc *cmd_desc = NULL;
mutex_lock(&smc_lock);
/* Open & submit the work to SMC */
cmd_desc = te_get_free_cmd_desc(&tlk_dev);
if (!cmd_desc) {
pr_err("%s: failed to get cmd_desc\n", __func__);
goto error;
}
/* Close the session */
request = cmd_desc->req_addr;
memset(request, 0, sizeof(struct te_request));
request->type = TE_SMC_CLOSE_SESSION;
request->session_id = session_id;
if (uuid_size != sizeof(request->dest_uuid)) {
pr_err("%s: Invalid size sent!\n", __func__);
goto error;
}
memcpy(request->dest_uuid, ta_uuid, sizeof(*ta_uuid)*4);
do_smc(request, &tlk_dev);
if (request->result) {
pr_err("%s: error closing session: 0x%08x, 0x%08x\n",
__func__, request->result, request->result_origin);
goto error;
}
error:
if (cmd_desc)
te_put_used_cmd_desc(&tlk_dev, cmd_desc);
mutex_unlock(&smc_lock);
}
EXPORT_SYMBOL(te_close_trusted_session_tlk);
/*
* Command to launch operations from the linux kernel to
* the trusted app.
* This API should be called only from the kernel space.
* The pointer to the buffer from the kernel, length of thus buffer,
* session Id, UUID of the TA and the command requested from the
* TA should be passed as arguments
* Returns SUCCESS or FAILURE
*/
int te_launch_trusted_oper_tlk(u8 *buf_ptr, u32 buf_len, u32 session_id,
u32 *ta_uuid, u32 ta_cmd, u32 uuid_size)
{
u32 i;
struct te_request *request;
struct te_oper_param user_param;
struct te_oper_param *param_array;
struct te_oper_param *params = NULL;
struct te_cmd_req_desc *cmd_desc = NULL;
u32 no_of_params = 1;
mutex_lock(&smc_lock);
/* Open & submit the work to SMC */
cmd_desc = te_get_free_cmd_desc(&tlk_dev);
params = te_get_free_params(&tlk_dev, no_of_params);
if (!cmd_desc || !params) {
pr_err("%s: failed to get cmd_desc/params\n", __func__);
goto error;
}
/* launch operation */
request = cmd_desc->req_addr;
memset(request, 0, sizeof(struct te_request));
request->params = (uintptr_t)params;
request->params_size = no_of_params;
request->session_id = session_id;
request->command_id = ta_cmd;
request->type = TE_SMC_LAUNCH_OPERATION;
user_param.index = 0;
user_param.u.Mem.len = buf_len;
user_param.type = TE_PARAM_TYPE_MEM_RW;
user_param.u.Mem.type = TE_MEM_TYPE_NS_KERNEL;
user_param.u.Mem.base = (uint64_t)(uintptr_t)buf_ptr;
if (uuid_size != sizeof(request->dest_uuid)) {
pr_err("%s: Invalid size sent\n", __func__);
goto error;
}
memcpy(request->dest_uuid, ta_uuid, sizeof(*ta_uuid)*4);
param_array = (struct te_oper_param *)(uintptr_t)request->params;
for (i = 0; i < no_of_params; i++)
memcpy(param_array + i, &user_param,
sizeof(struct te_oper_param));
do_smc(request, &tlk_dev);
if (request->result) {
pr_err("%s: error launching session: 0x%08x, 0x%08x\n",
__func__, request->result, request->result_origin);
goto error;
} else {
if (cmd_desc)
te_put_used_cmd_desc(&tlk_dev, cmd_desc);
if (params)
te_put_free_params(&tlk_dev, params, no_of_params);
mutex_unlock(&smc_lock);
return OTE_SUCCESS;
}
error:
if (cmd_desc)
te_put_used_cmd_desc(&tlk_dev, cmd_desc);
if (params)
te_put_free_params(&tlk_dev, params, no_of_params);
mutex_unlock(&smc_lock);
return OTE_ERROR_GENERIC;
}
EXPORT_SYMBOL(te_launch_trusted_oper_tlk);
+714
View File
@@ -0,0 +1,714 @@
/*
* Copyright (c) 2013-2021 NVIDIA Corporation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <linux/atomic.h>
#include <linux/uaccess.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/fs.h>
#include <linux/platform_device.h>
#include <linux/printk.h>
#include <linux/ioctl.h>
#include <linux/miscdevice.h>
#include <linux/mm.h>
#include <asm/cacheflush.h>
#include <linux/list.h>
#include <linux/dma-mapping.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/clk.h>
#include <linux/reset.h>
#include "ote_protocol.h"
#define SET_ANSWER(a, r, ro) { a.result = r; a.result_origin = ro; }
struct tlk_device tlk_dev;
DEFINE_MUTEX(smc_lock);
static int te_create_free_cmd_list(struct tlk_device *dev)
{
int cmd_desc_count, ret = 0;
struct te_cmd_req_desc *req_desc = NULL, *tmp_req_desc = NULL;
int bitmap_size;
int req_buf_size;
void *req_buf;
/*
* TLK can map in the shared req/param buffers and do_smc
* only needs to send the offsets within each (with cache coherency
* being maintained by HW through an NS mapping).
*/
req_buf_size = (TE_TOTAL_PAGE_COUNT * PAGE_SIZE);
req_buf = kmalloc(req_buf_size, GFP_KERNEL);
if (!req_buf) {
pr_err("%s: Failed to allocate param buffer!\n", __func__);
ret = -ENOMEM;
goto error;
}
/* requests in 1st page, params in 2nd, pagelists in 3rd and 4th pages */
dev->req_addr = (struct te_request *)
(req_buf + (0 * PAGE_SIZE));
dev->param_addr = (struct te_oper_param *)
(req_buf + (1 * PAGE_SIZE));
dev->plist_addr = (uint64_t *)
(req_buf + (2 * PAGE_SIZE));
/* alloc param bitmap allocator */
bitmap_size = BITS_TO_LONGS(TE_PARAM_MAX) * sizeof(long);
dev->param_bitmap = kzalloc(bitmap_size, GFP_KERNEL);
if (!dev->param_bitmap) {
pr_err("%s: Failed to allocate param bitmap\n", __func__);
ret = -ENOMEM;
goto error;
}
/* alloc plist bitmap allocator */
bitmap_size = BITS_TO_LONGS(TE_PLIST_MAX) * sizeof(long);
dev->plist_bitmap = kzalloc(bitmap_size, GFP_KERNEL);
if (!dev->plist_bitmap) {
pr_err("%s: Failed to allocate plist bitmap\n", __func__);
ret = -ENOMEM;
goto error;
}
tlk_send_smc(TE_SMC_REGISTER_REQ_BUF,
(uintptr_t)dev->req_addr, req_buf_size);
if (!dev->req_addr || !dev->param_addr || !dev->plist_addr) {
pr_err("%s: Bad dev request addr/param addr/plist addr!\n",
__func__);
ret = -ENOMEM;
goto error;
}
for (cmd_desc_count = 0;
cmd_desc_count < TE_CMD_DESC_MAX; cmd_desc_count++) {
req_desc = kzalloc(sizeof(struct te_cmd_req_desc), GFP_KERNEL);
if (req_desc == NULL) {
pr_err("%s: Failed to allocate cmd req descriptor\n",
__func__);
ret = -ENOMEM;
goto error;
}
req_desc->req_addr = dev->req_addr + cmd_desc_count;
INIT_LIST_HEAD(&(req_desc->list));
/* Add the cmd param descriptor to free list */
list_add_tail(&req_desc->list, &(dev->free_cmd_list));
}
return 0;
error:
pr_err("%s: Error, returning %d\n", __func__, ret);
kfree(req_buf);
kfree(dev->param_bitmap);
kfree(dev->plist_bitmap);
list_for_each_entry_safe(req_desc, tmp_req_desc,
&(dev->free_cmd_list), list)
kfree(req_desc);
return ret;
}
static void te_release_free_cmd_list(struct tlk_device *dev)
{
kfree(dev->req_addr);
kfree(dev->param_bitmap);
kfree(dev->plist_bitmap);
}
struct te_oper_param *te_get_free_params(struct tlk_device *dev,
unsigned int nparams)
{
struct te_oper_param *params = NULL;
int idx, nbits;
if (nparams) {
nbits = get_count_order(nparams);
idx = bitmap_find_free_region(dev->param_bitmap,
TE_PARAM_MAX, nbits);
if (idx >= 0)
params = dev->param_addr + idx;
}
return params;
}
void te_put_free_params(struct tlk_device *dev,
struct te_oper_param *params, uint32_t nparams)
{
int idx, nbits;
idx = (params - dev->param_addr);
nbits = get_count_order(nparams);
bitmap_release_region(dev->param_bitmap, idx, nbits);
}
struct te_cmd_req_desc *te_get_free_cmd_desc(struct tlk_device *dev)
{
struct te_cmd_req_desc *cmd_desc = NULL;
if (!(list_empty(&(dev->free_cmd_list)))) {
cmd_desc = list_first_entry(&(dev->free_cmd_list),
struct te_cmd_req_desc, list);
list_del(&(cmd_desc->list));
list_add_tail(&cmd_desc->list, &(dev->used_cmd_list));
}
return cmd_desc;
}
void te_put_used_cmd_desc(struct tlk_device *dev,
struct te_cmd_req_desc *cmd_desc)
{
struct te_cmd_req_desc *param_desc, *tmp_param_desc;
if (cmd_desc) {
list_for_each_entry_safe(param_desc, tmp_param_desc,
&(dev->used_cmd_list), list) {
if (cmd_desc->req_addr == param_desc->req_addr) {
list_del(&param_desc->list);
list_add_tail(&param_desc->list,
&(dev->free_cmd_list));
}
}
}
}
static void __attribute__((unused)) te_print_cmd_list(
struct tlk_device *dev, int used_list)
{
struct te_cmd_req_desc *param_desc;
if (!used_list) {
pr_info("Printing free cmd list\n");
if (!(list_empty(&(dev->free_cmd_list)))) {
list_for_each_entry(param_desc, &(dev->free_cmd_list),
list)
pr_info("Phys addr for cmd req desc (%p)\n",
param_desc->req_addr);
}
} else {
pr_info("Printing used cmd list\n");
if (!(list_empty(&(dev->used_cmd_list)))) {
list_for_each_entry(param_desc, &(dev->used_cmd_list),
list)
pr_info("Phys addr for cmd req desc (%p)\n",
param_desc->req_addr);
}
}
}
static void te_close_sessions(struct tlk_context *context)
{
struct tlk_device *dev = context->dev;
union te_cmd cmd;
struct te_cmd_req_desc *cmd_desc = NULL;
struct te_request *request;
struct te_session *session, *tmp_session;
if (list_empty(&context->session_list))
return;
cmd_desc = te_get_free_cmd_desc(dev);
if (!cmd_desc) {
pr_err("%s: failed to get cmd_desc\n", __func__);
return;
}
request = cmd_desc->req_addr;
list_for_each_entry_safe(session, tmp_session,
&context->session_list, list) {
memset(request, 0, sizeof(struct te_request));
cmd.closesession.session_id = session->session_id;
te_close_session(&cmd.closesession, request, context);
}
te_put_used_cmd_desc(dev, cmd_desc);
}
static int tlk_device_open(struct inode *inode, struct file *file)
{
struct tlk_context *context;
int ret = 0;
context = kzalloc(sizeof(struct tlk_context), GFP_KERNEL);
if (!context) {
ret = -ENOMEM;
goto error;
}
context->dev = &tlk_dev;
INIT_LIST_HEAD(&context->session_list);
file->private_data = context;
return 0;
error:
return ret;
}
static int tlk_device_release(struct inode *inode, struct file *file)
{
struct tlk_context *context = file->private_data;
/* close any open sessions */
mutex_lock(&smc_lock);
te_close_sessions(context);
mutex_unlock(&smc_lock);
kfree(file->private_data);
file->private_data = NULL;
return 0;
}
static int copy_params_from_user(struct te_request *req,
struct te_operation *operation, struct te_oper_param *caller_params)
{
struct te_oper_param *param_array;
struct te_oper_param *user_param;
uint32_t i;
if (operation->list_count == 0)
return 0;
param_array = (struct te_oper_param *)(uintptr_t)req->params;
if (param_array == NULL) {
pr_err("param_array empty\n");
return 1;
}
user_param = (struct te_oper_param *)(uintptr_t)operation->list_head;
for (i = 0; i < operation->list_count && user_param != NULL; i++) {
if (copy_from_user(param_array + i, user_param,
sizeof(struct te_oper_param))) {
pr_err("Failed to copy operation parameter:%d, %p, " \
"list_count: %d\n",
i, user_param, operation->list_count);
return 1;
}
user_param = (struct te_oper_param *)(uintptr_t)
param_array[i].next_ptr_user;
}
memcpy(caller_params, param_array,
sizeof(struct te_oper_param) * operation->list_count);
return 0;
}
static int copy_params_to_user(struct te_request *req,
struct te_operation *operation, struct te_oper_param *caller_params)
{
struct te_oper_param *param_array;
struct te_oper_param *user_param;
uint32_t i;
if (operation->list_count == 0)
return 0;
param_array = (struct te_oper_param *)(uintptr_t)req->params;
if (param_array == NULL) {
pr_err("param_array empty\n");
return 1;
}
user_param =
(struct te_oper_param *)(uintptr_t)operation->list_head;
for (i = 0; i < req->params_size; i++) {
/* clear flags */
param_array[i].type &= ~TE_PARAM_TYPE_ALL_FLAGS;
switch(param_array[i].type) {
/*
* Restore the memory base address as it can be overridden
* while sending it to secure world
*/
case TE_PARAM_TYPE_MEM_RO:
case TE_PARAM_TYPE_MEM_RW:
case TE_PARAM_TYPE_PERSIST_MEM_RO:
case TE_PARAM_TYPE_PERSIST_MEM_RW:
param_array[i].u.Mem.base = caller_params[i].u.Mem.base;
}
if (copy_to_user(user_param, param_array + i,
sizeof(struct te_oper_param))) {
pr_err("Failed to copy back parameter:%d %p\n", i,
user_param);
return 1;
}
user_param = (struct te_oper_param *)(uintptr_t)
param_array[i].next_ptr_user;
}
return 0;
}
static long te_handle_trustedapp_ioctl(struct file *file,
unsigned int ioctl_num, unsigned long ioctl_param)
{
long err = 0;
union te_cmd cmd;
struct te_operation *operation = NULL;
struct te_oper_param *params = NULL;
struct te_oper_param *caller_params = NULL;
struct te_request *request;
void __user *ptr_user_answer = NULL;
struct te_answer answer;
struct te_cmd_req_desc *cmd_desc = NULL;
struct tlk_context *context = file->private_data;
struct tlk_device *dev = context->dev;
if (copy_from_user(&cmd, (void __user *)ioctl_param,
sizeof(union te_cmd))) {
pr_err("Failed to copy command request\n");
err = -EFAULT;
goto error;
}
memset(&answer, 0, sizeof(struct te_answer));
switch (ioctl_num) {
case TE_IOCTL_OPEN_CLIENT_SESSION:
operation = &cmd.opensession.operation;
ptr_user_answer = (void *)(uintptr_t)cmd.opensession.answer;
cmd_desc = te_get_free_cmd_desc(dev);
params = te_get_free_params(dev, operation->list_count);
if (!cmd_desc || (operation->list_count && !params)) {
SET_ANSWER(answer,
OTE_ERROR_OUT_OF_MEMORY,
OTE_RESULT_ORIGIN_COMMS);
pr_err("failed to get cmd_desc/params\n");
goto error;
}
request = cmd_desc->req_addr;
memset(request, 0, sizeof(struct te_request));
request->params = (uintptr_t)params;
request->params_size = operation->list_count;
if (operation->list_count > 0) {
caller_params = kmalloc(sizeof(struct te_oper_param) *
operation->list_count, GFP_KERNEL);
if (!caller_params) {
pr_err("%s: failed to allocate caller params\n", __func__);
err = -ENOMEM;
goto error;
}
}
if (copy_params_from_user(request, operation, caller_params)) {
err = -EFAULT;
pr_err("%s: failed to copy params from user\n", __func__);
goto error;
}
te_open_session(&cmd.opensession, request, context);
SET_ANSWER(answer, request->result, request->result_origin);
answer.session_id = request->session_id;
break;
case TE_IOCTL_CLOSE_CLIENT_SESSION:
ptr_user_answer = (void *)(uintptr_t)cmd.closesession.answer;
cmd_desc = te_get_free_cmd_desc(dev);
if (!cmd_desc) {
SET_ANSWER(answer,
OTE_ERROR_OUT_OF_MEMORY,
OTE_RESULT_ORIGIN_COMMS);
pr_err("failed to get cmd_desc\n");
goto error;
}
request = cmd_desc->req_addr;
memset(request, 0, sizeof(struct te_request));
/* close session cannot fail */
te_close_session(&cmd.closesession, request, context);
break;
case TE_IOCTL_LAUNCH_OPERATION:
operation = &cmd.launchop.operation;
ptr_user_answer = (void *)(uintptr_t)cmd.launchop.answer;
cmd_desc = te_get_free_cmd_desc(dev);
params = te_get_free_params(dev, operation->list_count);
if (!cmd_desc || (operation->list_count && !params)) {
SET_ANSWER(answer,
OTE_ERROR_OUT_OF_MEMORY,
OTE_RESULT_ORIGIN_COMMS);
pr_err("failed to get cmd_desc/params\n");
goto error;
}
request = cmd_desc->req_addr;
memset(request, 0, sizeof(struct te_request));
request->params = (uintptr_t)params;
request->params_size = operation->list_count;
if (operation->list_count > 0) {
caller_params = kmalloc(sizeof(struct te_oper_param) *
operation->list_count, GFP_KERNEL);
if (!caller_params) {
pr_err("%s: failed to allocate caller params\n", __func__);
err = -ENOMEM;
goto error;
}
}
if (copy_params_from_user(request, operation, caller_params)) {
err = -EFAULT;
pr_info("%s: failed to copy params from user\n", __func__);
goto error;
}
te_launch_operation(&cmd.launchop, request, context);
SET_ANSWER(answer, request->result, request->result_origin);
break;
default:
pr_err("Invalid IOCTL Cmd\n");
err = -EINVAL;
goto error;
}
if (ptr_user_answer && !err) {
if (copy_to_user(ptr_user_answer, &answer,
sizeof(struct te_answer))) {
pr_err("Failed to copy answer\n");
err = -EFAULT;
}
}
if (request->params && !err) {
if (copy_params_to_user(request, operation, caller_params)) {
pr_err("Failed to copy return params\n");
err = -EFAULT;
}
}
error:
if (cmd_desc)
te_put_used_cmd_desc(dev, cmd_desc);
if (params)
te_put_free_params(dev, params, operation->list_count);
kfree(caller_params);
return err;
}
static long tlk_device_ioctl(struct file *file, unsigned int ioctl_num,
unsigned long ioctl_param)
{
int err;
switch (ioctl_num) {
case TE_IOCTL_OPEN_CLIENT_SESSION:
case TE_IOCTL_CLOSE_CLIENT_SESSION:
case TE_IOCTL_LAUNCH_OPERATION:
mutex_lock(&smc_lock);
err = te_handle_trustedapp_ioctl(file, ioctl_num, ioctl_param);
mutex_unlock(&smc_lock);
break;
case TE_IOCTL_SS_CMD:
err = te_handle_ss_ioctl(file, ioctl_num, ioctl_param);
break;
default:
pr_err("%s: Invalid IOCTL (0x%x) id 0x%x max 0x%lx\n",
__func__, ioctl_num, _IOC_NR(ioctl_num),
(unsigned long)TE_IOCTL_MAX_NR);
err = -EINVAL;
break;
}
return err;
}
static struct device_node *get_tlk_device_node(void)
{
struct device_node *node = NULL;
node = of_find_compatible_node(NULL, NULL, "android,tlk-driver");
if (!node)
pr_debug("TLK node not present in FDT\n");
return node;
}
int te_is_secos_dev_enabled(void)
{
static int tlk_dev_status = 0;
struct device_node *node = NULL;
if (unlikely(tlk_dev_status == 0)) {
node = get_tlk_device_node();
tlk_dev_status = (node && of_device_is_available(node));
}
return tlk_dev_status;
}
/*
* tlk_driver function definitions.
*/
static const struct file_operations tlk_device_fops = {
.owner = THIS_MODULE,
.open = tlk_device_open,
.release = tlk_device_release,
.unlocked_ioctl = tlk_device_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = tlk_device_ioctl,
#endif
};
struct miscdevice tlk_misc_device = {
.minor = MISC_DYNAMIC_MINOR,
.name = "tlk_device",
.fops = &tlk_device_fops,
};
static int tlk_driver_probe(struct platform_device *pdev)
{
struct reset_control *rst;
struct clk *clk;
int ret;
clk = devm_clk_get(&pdev->dev, "nvdec");
if (!IS_ERR(clk)) {
ret = clk_prepare_enable(clk);
if (ret)
return ret;
} else if (PTR_ERR(clk) != -ENOENT) {
return PTR_ERR(clk);
} else {
pr_err("%s: nvdec clock not available\n", __func__);
}
rst = devm_reset_control_get(&pdev->dev, "nvdec");
if (!IS_ERR(rst)) {
ret = reset_control_deassert(rst);
if (ret)
return ret;
} else if (PTR_ERR(rst) != -ENOENT) {
return PTR_ERR(rst);
} else {
pr_err("%s: nvdec reset not available\n", __func__);
}
platform_set_drvdata(pdev, clk);
ret = of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev);
if (ret) {
pr_err("%s: of_platform_populate failed\n", __func__);
goto disable_clk;
}
if (!te_is_secos_dev_enabled()) {
ret = -ENODEV;
goto disable_clk;
}
return 0;
disable_clk:
if (!IS_ERR(clk))
clk_disable_unprepare(clk);
return ret;
}
static void tlk_driver_remove(struct platform_device *pdev) {
struct clk *clk = platform_get_drvdata(pdev);
if (!IS_ERR(clk))
clk_disable_unprepare(clk);
}
static const struct of_device_id tlk_driver_of_match[] = {
{ .compatible = "android,tlk-driver",},
{},
};
static struct platform_driver tlk_driver = {
.probe = tlk_driver_probe,
.remove = tlk_driver_remove,
.driver = {
.name = "tlk-driver",
.owner = THIS_MODULE,
.of_match_table = tlk_driver_of_match,
}
};
static int __init tlk_driver_init(void)
{
if (get_tlk_device_node()) {
int ret;
INIT_LIST_HEAD(&(tlk_dev.used_cmd_list));
INIT_LIST_HEAD(&(tlk_dev.free_cmd_list));
ret = te_create_free_cmd_list(&tlk_dev);
if (ret != 0) {
pr_err("%s: failed to create free_list\n", __func__);
return ret;
}
}
return platform_driver_register(&tlk_driver);
}
static void __exit tlk_driver_exit(void)
{
platform_driver_unregister(&tlk_driver);
if (get_tlk_device_node())
te_release_free_cmd_list(&tlk_dev);
}
/* Initialize early so that other device drivers can use it during boot */
subsys_initcall(tlk_driver_init);
module_exit(tlk_driver_exit);
static int __init tlk_driver_misc_init(void)
{
int ret;
if (!te_is_secos_dev_enabled()) {
pr_info("%s: tlk not enabled on this device\n", __func__);
return -ENODEV;
}
ret = misc_register(&tlk_misc_device);
if (ret)
pr_err("%s: misc_register failed: %d\n", __func__, ret);
return ret;
}
static void __exit tlk_driver_misc_exit(void)
{
if (te_is_secos_dev_enabled())
misc_deregister(&tlk_misc_device);
}
module_init(tlk_driver_misc_init);
module_exit(tlk_driver_misc_exit);
+89
View File
@@ -0,0 +1,89 @@
/*
* Copyright (c) 2013-2016 NVIDIA Corporation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <linux/slab.h>
#include <linux/syscalls.h>
#include <linux/list.h>
#include <linux/completion.h>
#include <linux/workqueue.h>
#include <linux/bitops.h>
#include <linux/uaccess.h>
#include <linux/dma-mapping.h>
#include "ote_protocol.h"
/* SS operation timeout in milliseconds */
#define SS_OP_TIME_OUT 5000
static DECLARE_COMPLETION(req_ready);
static DECLARE_COMPLETION(req_complete);
static void tlk_ss_reset(void)
{
/* reset completion vars to default state */
init_completion(&req_ready);
init_completion(&req_complete);
}
int te_handle_ss_ioctl(struct file *file, unsigned int ioctl_num,
unsigned long ioctl_param)
{
int ss_cmd;
if (ioctl_num != TE_IOCTL_SS_CMD)
return -EINVAL;
if (copy_from_user(&ss_cmd, (void __user *)ioctl_param,
sizeof(ss_cmd))) {
pr_err("%s: copy from user space failed\n", __func__);
return -EFAULT;
}
switch (ss_cmd) {
case TE_IOCTL_SS_CMD_GET_NEW_REQ:
/* wait for a new request */
if (wait_for_completion_interruptible(&req_ready))
return -ENODATA;
break;
case TE_IOCTL_SS_CMD_REQ_COMPLETE:
/* signal the producer */
complete(&req_complete);
break;
default:
pr_err("%s: unknown ss_cmd 0x%x\n", __func__, ss_cmd);
return -EINVAL;
}
return 0;
}
int tlk_ss_op(void)
{
/* signal consumer */
complete(&req_ready);
/* wait for the consumer's signal */
if (!wait_for_completion_timeout(&req_complete,
msecs_to_jiffies(SS_OP_TIME_OUT))) {
/* daemon didn't respond */
tlk_ss_reset();
return OTE_ERROR_CANCEL;
}
return OTE_SUCCESS;
}
+212
View File
@@ -0,0 +1,212 @@
/*
* Copyright (c) 2013-2018 NVIDIA Corporation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <linux/slab.h>
#include <linux/syscalls.h>
#include <linux/list.h>
#include <linux/completion.h>
#include <linux/workqueue.h>
#include <linux/bitops.h>
#include <linux/uaccess.h>
#include <linux/of.h>
#include <asm/page.h>
#include <linux/dma-mapping.h>
#include <linux/string.h>
#include <linux/platform_device.h>
#include "ote_protocol.h"
#define LOGBUF_SIZE 8192
struct circular_buffer {
uint32_t size; /* Indicates the total size of the buffer */
uint32_t start; /* Starting point of valid data in buffer */
uint32_t end; /* First character which is empty (can be written to) */
uint32_t overflow; /* Indicator whether buffer has overwritten itself */
char *buf;
};
static int ote_logging_enabled;
struct circular_buffer *cb;
/*
* Initialize the shared buffer for TLK logging.
* The shared buffer is allocated in DMA memory to get uncached memory
* since TLK directly writes to the physical address of the shared buffer.
* The structure is declared in DMA memory too since it's members will
* also be updated by the TLK directly to their physical addresses.
*/
static int circ_buf_init(struct circular_buffer **cbptr)
{
dma_addr_t tp;
*cbptr = (struct circular_buffer *) dma_alloc_coherent(NULL,
sizeof(struct circular_buffer), &tp, GFP_KERNEL);
if (!*cbptr) {
pr_err("%s: no memory available for circular buffer struct\n",
__func__);
return -ENOMEM;
}
memset(*cbptr, 0, sizeof(struct circular_buffer));
(*cbptr)->start = 0;
(*cbptr)->end = 0;
(*cbptr)->size = LOGBUF_SIZE;
(*cbptr)->buf = (char *) dma_alloc_coherent(NULL, LOGBUF_SIZE,
&tp, GFP_KERNEL);
if (!(*cbptr)->buf) {
pr_err("%s: no memory available for shared buffer\n",
__func__);
/* Frees the memory allocated using dma_alloc_coherent */
dma_free_coherent(NULL,
sizeof(struct circular_buffer), cbptr, tp);
return -ENOMEM;
}
memset((*cbptr)->buf, 0, LOGBUF_SIZE);
(*cbptr)->overflow = 0;
return 0;
}
/*
* Copy the contents of the circular buffer into a char buffer in order.
* This helps to treat the buffer like a string and use it to tokenize it
* into lines, tag and display it.
*/
static int circ_buf_copy(struct circular_buffer *cb, char *text)
{
if (cb->end == cb->start)
return 0;
if (cb->end > cb->start) {
if (abs(cb->end - cb->start) > LOGBUF_SIZE) {
pr_err("%s: cbuf pointers corrupted\n", __func__);
return -EINVAL;
}
memcpy(text, cb->buf + cb->start, cb->end - cb->start);
} else if (cb->start > cb->end) {
if (abs(cb->end - cb->start) > LOGBUF_SIZE) {
pr_err("%s: cbuf pointers corrupted\n", __func__);
return -EINVAL;
}
memcpy(text, cb->buf + cb->start, cb->size - cb->start);
memcpy(text + cb->size - cb->start, cb->buf, cb->end);
}
return 0;
}
/*
* Function which prints TLK logs.
* Tokenizes the TLK logs into lines, tags each line
* and prints it out to kmsg file.
*/
void ote_print_logs(void)
{
char *text = NULL;
char *temp = NULL;
char *buffer = NULL;
if (!ote_logging_enabled)
return;
buffer = kzalloc(LOGBUF_SIZE, GFP_KERNEL);
if (!buffer) {
pr_err("%s: memory allocation failed\n", __func__);
return;
}
/* This detects if the buffer proved to be too small to hold the data.
* If buffer is not large enough, it overwrites it's oldest data,
* This warning serves to alert the user to possibly use a bigger buffer
*/
if (cb->overflow == 1) {
pr_warn("[TLK] **WARNING** TLK buffer overwritten.\n");
cb->overflow = 0;
}
if (circ_buf_copy(cb, buffer) != 0) {
kfree(buffer);
return;
}
cb->buf[cb->end] = '\0';
/*
* In case no delimiter was found, strsep returns the entire string as
* token and the original string is made as NULL
*/
text = buffer;
temp = strsep(&text, "\n");
while (temp != NULL) {
if (strnlen(temp, LOGBUF_SIZE))
pr_info("[TLK] %s\n", temp);
temp = strsep(&text, "\n");
}
/* Indicate that buffer is empty */
cb->start = cb->end;
kfree(buffer);
}
/*
* Call function to initialize circular buffer.
* An SMC is made to send the virtual address of the structure to
* the secure OS.
*/
static int ote_logger_probe(struct platform_device *pdev)
{
if (circ_buf_init(&cb) != 0)
return -1;
/* enable logging only if secure firmware supports it */
if (!tlk_send_smc(TE_SMC_INIT_LOGGER, (uintptr_t)cb, 0))
ote_logging_enabled = 1;
ote_print_logs();
return 0;
}
static const struct of_device_id ote_logger_of_match[] = {
{ .compatible = "android,ote-logger",},
{},
};
static struct platform_driver ote_logger_driver = {
.probe = ote_logger_probe,
.driver = {
.name = "ote-logger",
.owner = THIS_MODULE,
.of_match_table = ote_logger_of_match,
}
};
static int __init ote_logger_driver_init(void)
{
return platform_driver_register(&ote_logger_driver);
}
subsys_initcall(ote_logger_driver_init);
+345
View File
@@ -0,0 +1,345 @@
/*
* Copyright (c) 2016-2018 NVIDIA Corporation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program.
*/
#include <linux/atomic.h>
#include <linux/uaccess.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/fs.h>
#include <linux/printk.h>
#include <linux/ioctl.h>
#include <linux/sched.h>
#include <linux/mm.h>
#include <linux/pagemap.h>
#include <linux/syscalls.h>
#include <asm/smp_plat.h>
#include "ote_protocol.h"
#define PHYS_PAGELIST_ALIGNED_PA(ent) ((ent) & 0xFFFFFFFFF000ULL)
#ifndef CONFIG_ARM64
#define PTE_RDONLY PTE_AP2
#endif
extern uint64_t _tlk_get_mair(void);
static uint64_t te_get_mair_attrs(void) { return _tlk_get_mair(); }
static uint64_t te_get_pte_attrs(uint64_t pte, uint64_t mair)
{
uint64_t pte_attrs, shareable;
uint32_t idx;
uint8_t attrs;
/* Bits 2:4 holds attributes index */
idx = (pte >> 2) & 0x7;
attrs = *((uint8_t *)&mair + idx);
/* Bits 8:9 holds shareability field */
shareable = (pte >> 8) & 0x3;
/*
* TLK expects that attributes are stored in the first byte of PTE.
* MSB holds inner(4 bits), outer(4 bits) and shareable (2 bits)
* attributes respectively.
*/
pte_attrs = attrs;
pte_attrs = pte_attrs << 56;
pte_attrs |= (shareable << 54);
return pte_attrs;
}
static void te_put_free_plist(struct te_shmem_desc *shmem_desc)
{
struct tlk_device *dev = &tlk_dev;
int idx, nbits;
idx = shmem_desc->plist_idx;
nbits = get_count_order(shmem_desc->nr_pages);
bitmap_release_region(dev->plist_bitmap, idx, nbits);
}
static void te_release_mem_buffer(struct te_shmem_desc *shmem_desc)
{
uint32_t i;
list_del(&shmem_desc->list);
for (i = 0; i < shmem_desc->nr_pages; i++) {
if ((shmem_desc->type == TE_PARAM_TYPE_MEM_RW) ||
(shmem_desc->type == TE_PARAM_TYPE_PERSIST_MEM_RW))
set_page_dirty_lock(shmem_desc->pages[i]);
put_page(shmem_desc->pages[i]);
}
te_put_free_plist(shmem_desc);
kfree(shmem_desc->pages);
kfree(shmem_desc);
}
void te_release_mem_buffers(struct list_head *buflist)
{
struct te_shmem_desc *shmem_desc, *tmp_shmem_desc;
list_for_each_entry_safe(shmem_desc, tmp_shmem_desc, buflist, list) {
te_release_mem_buffer(shmem_desc);
}
}
static int te_load_page_list(unsigned long start,
unsigned int npages,
struct page **pages,
struct vm_area_struct **vmas)
{
struct tlk_device *dev = &tlk_dev;
uint64_t *ptes;
uint64_t mair;
int i, idx, nbits;
nbits = get_count_order(npages);
idx = bitmap_find_free_region(dev->plist_bitmap,
TE_PLIST_MAX, nbits);
if (idx < 0) {
pr_err("%s: ERROR: plist bitmap is full, order %d max size %lu\n",
__func__, nbits, TE_PLIST_MAX);
return -ENOMEM;
}
mair = te_get_mair_attrs();
ptes = dev->plist_addr + idx;
for (i = 0; i < npages; i++, start += PAGE_SIZE) {
uint64_t pte;
#ifdef CONFIG_ARM64
/*
* Not performing this sanity check for 32 bit kernel as
* PTE_ATTRINDX* macros are not available in 32 bit headers
*/
if ((pgprot_val(vmas[i]->vm_page_prot) & PTE_ATTRINDX_MASK) !=
PTE_ATTRINDX(MT_NORMAL)) {
pr_err("%s: unsupported memory type: %llx\n",
__func__,
pgprot_val(vmas[i]->vm_page_prot) & PTE_ATTRINDX_MASK);
bitmap_release_region(dev->plist_bitmap, idx, nbits);
return -EINVAL;
}
#endif
/*
* Recreate the pte of the page - we can't access it
* safely here race-free.
*/
pte = page_to_phys(pages[i]);
pte |= pgprot_val(vmas[i]->vm_page_prot);
if (vmas[i]->vm_flags & VM_WRITE)
pte &= ~PTE_RDONLY;
ptes[i] = PHYS_PAGELIST_ALIGNED_PA(pte);
ptes[i] |= (start & (PAGE_SIZE-1));
ptes[i] |= te_get_pte_attrs(pte, mair);
}
return idx;
}
static int te_pin_user_pages(struct te_oper_param *param,
struct page ***pages,
struct vm_area_struct **vmas,
uint32_t nr_pages, uint32_t *plist_idx)
{
struct tlk_device *dev = &tlk_dev;
int idx, ret = 0;
int nr_pinned = 0;
unsigned long start;
uint32_t length;
bool writable;
int i;
start = (unsigned long)param->u.Mem.base,
length = param->u.Mem.len;
*pages = kzalloc(nr_pages * sizeof(struct page **), GFP_KERNEL);
if (!*pages) {
pr_err("%s: Error allocating %d pages!\n",
__func__, (int)nr_pages);
return OTE_ERROR_OUT_OF_MEMORY;
}
writable = (param->type == TE_PARAM_TYPE_MEM_RW ||
param->type == TE_PARAM_TYPE_PERSIST_MEM_RW);
down_read(&current->mm->mmap_lock);
/*
* vmas are valid only when mmap_lock is held. Hence holding the lock
* across get user pages and process returned vmas.
*/
nr_pinned = get_user_pages(start, nr_pages,
writable ? FOLL_WRITE : 0, *pages);
if (nr_pinned != nr_pages) {
pr_err("%s: Error %d in get_user_pages for buffer 0x%lx\n",
__func__, nr_pinned, start);
ret = OTE_ERROR_GENERIC;
goto error;
}
idx = te_load_page_list(start, nr_pages, *pages, vmas);
if (idx < 0) {
pr_err("%s: Error loading page list, idx = %d!\n",
__func__, idx);
ret = OTE_ERROR_OUT_OF_MEMORY;
goto error;
}
up_read(&current->mm->mmap_lock);
/* stores the index from the beginning of shared buffer */
param->u.Mem.base = (uintptr_t)(dev->plist_addr + idx) -
(uintptr_t)dev->req_addr;
param->type |= TE_PARAM_TYPE_FLAGS_PHYS_LIST;
*plist_idx = idx;
return OTE_SUCCESS;
error:
up_read(&current->mm->mmap_lock);
for (i = 0; i < nr_pinned; i++)
put_page((*pages)[i]);
kfree(*pages);
return ret;
}
static int te_prep_mem_buffer(struct te_oper_param *param,
struct te_session *session)
{
struct page **pages = NULL;
struct te_shmem_desc *shmem_desc = NULL;
int ret = 0, nr_pages = 0;
uint32_t buf_type;
unsigned long start;
uint32_t length;
struct vm_area_struct **vmas;
uint32_t plist_idx = 0;
/* allocate new shmem descriptor */
shmem_desc = kzalloc(sizeof(struct te_shmem_desc), GFP_KERNEL);
if (!shmem_desc) {
pr_err("%s: out of memory for shmem_desc!\n", __func__);
ret = OTE_ERROR_OUT_OF_MEMORY;
goto error;
}
/* Need this for vma alloc prior to pin_user_pages */
start = (unsigned long)param->u.Mem.base,
length = param->u.Mem.len;
nr_pages = (((uintptr_t)start & (PAGE_SIZE - 1)) +
(length + PAGE_SIZE - 1)) >> PAGE_SHIFT;
vmas = kzalloc(sizeof(*vmas) * nr_pages, GFP_KERNEL);
if (!vmas) {
pr_err("%s: out of memory for vmas! (%d pages)\n",
__func__, nr_pages);
ret = OTE_ERROR_OUT_OF_MEMORY;
goto error_alloc_vmas;
}
/* pin pages */
ret = te_pin_user_pages(param, &pages, vmas, nr_pages, &plist_idx);
if (ret != OTE_SUCCESS) {
pr_err("%s: te_pin_user_pages failed (%d)\n", __func__, ret);
goto error_pin_pages;
}
kfree(vmas);
/* initialize shmem descriptor */
INIT_LIST_HEAD(&(shmem_desc->list));
shmem_desc->plist_idx = plist_idx;
shmem_desc->size = param->u.Mem.len;
shmem_desc->nr_pages = nr_pages;
shmem_desc->pages = pages;
/* just type (no flags) */
buf_type = param->type & ~TE_PARAM_TYPE_ALL_FLAGS;
/* add shmem descriptor to proper list */
if ((buf_type == TE_PARAM_TYPE_MEM_RO) ||
(buf_type == TE_PARAM_TYPE_MEM_RW))
list_add_tail(&shmem_desc->list, &session->temp_shmem_list);
else {
list_add_tail(&shmem_desc->list,
&session->inactive_persist_shmem_list);
}
return OTE_SUCCESS;
error_pin_pages:
kfree(vmas);
error_alloc_vmas:
kfree(shmem_desc);
error:
return ret;
}
int te_prep_mem_buffers(struct te_request *request,
struct te_session *session)
{
uint32_t i;
int ret = OTE_SUCCESS;
struct te_oper_param *params;
params = (struct te_oper_param *)(uintptr_t)request->params;
for (i = 0; i < request->params_size; i++) {
switch (params[i].type) {
case TE_PARAM_TYPE_NONE:
case TE_PARAM_TYPE_INT_RO:
case TE_PARAM_TYPE_INT_RW:
break;
case TE_PARAM_TYPE_MEM_RO:
case TE_PARAM_TYPE_MEM_RW:
case TE_PARAM_TYPE_PERSIST_MEM_RO:
case TE_PARAM_TYPE_PERSIST_MEM_RW:
ret = te_prep_mem_buffer(params + i, session);
if (ret != OTE_SUCCESS) {
pr_err("%s failed with err (%d)\n",
__func__, ret);
ret = OTE_ERROR_BAD_PARAMETERS;
goto error;
}
break;
default:
pr_err("%s: OTE_ERROR_BAD_PARAMETERS\n", __func__);
ret = OTE_ERROR_BAD_PARAMETERS;
break;
}
}
return OTE_SUCCESS;
error:
/* release mem buffers */
te_release_mem_buffers(&session->temp_shmem_list);
te_release_mem_buffers(&session->inactive_persist_shmem_list);
return ret;
}
void te_activate_persist_mem_buffers(struct te_session *session)
{
struct te_shmem_desc *shmem_desc, *tmp_shmem_desc;
/* move persist mem buffers from inactive list to active list */
list_for_each_entry_safe(shmem_desc, tmp_shmem_desc,
&session->inactive_persist_shmem_list, list) {
list_move_tail(&shmem_desc->list, &session->persist_shmem_list);
}
}
+215
View File
@@ -0,0 +1,215 @@
/*
* Copyright (c) 2013-2021 NVIDIA Corporation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef __TLK_OTE_PROTOCOL_H__
#define __TLK_OTE_PROTOCOL_H__
#include "ote_types.h"
#include <uapi/misc/ote_protocol.h>
/*
* shared buffer is 6 pages: 1st are requests, 2nd are params and the
* remaining 4 are for params physical pages list
*/
#define TE_REQUEST_PAGE_COUNT 1
#define TE_PARAM_PAGE_COUNT 1
#define TE_PHYS_PAGE_LIST_PAGE_COUNT 4
#define TE_TOTAL_PAGE_COUNT (TE_REQUEST_PAGE_COUNT + \
TE_PARAM_PAGE_COUNT + \
TE_PHYS_PAGE_LIST_PAGE_COUNT)
#define TE_CMD_DESC_MAX ((PAGE_SIZE * TE_REQUEST_PAGE_COUNT) / sizeof(struct te_request))
#define TE_PARAM_MAX ((PAGE_SIZE * TE_PARAM_PAGE_COUNT) / sizeof(struct te_oper_param))
#define TE_PLIST_MAX ((PAGE_SIZE * TE_PHYS_PAGE_LIST_PAGE_COUNT) / sizeof(uint64_t))
#define MAX_BUFFER_MAP_SIZE (TE_PLIST_MAX * PAGE_SIZE)
#define MAX_EXT_SMC_ARGS 12
extern struct mutex smc_lock;
extern struct tlk_device tlk_dev;
extern void tlk_fiq_glue_aarch64(void);
uint32_t tlk_send_smc(uint32_t arg0, uintptr_t arg1, uintptr_t arg2);
uint32_t _tlk_generic_smc(uint32_t arg0, uintptr_t arg1, uintptr_t arg2);
void tlk_irq_handler(void);
struct te_oper_param *te_get_free_params(struct tlk_device *dev,
unsigned int nparams);
void te_put_free_params(struct tlk_device *dev,
struct te_oper_param *params, uint32_t nparams);
struct te_cmd_req_desc *te_get_free_cmd_desc(struct tlk_device *dev);
void te_put_used_cmd_desc(struct tlk_device *dev,
struct te_cmd_req_desc *cmd_desc);
/* errors returned by secure world in reponse to SMC calls */
enum {
TE_ERROR_PREEMPT_BY_IRQ = 0xFFFFFFFD,
TE_ERROR_PREEMPT_BY_FS = 0xFFFFFFFE,
};
struct tlk_device {
struct te_request *req_addr;
dma_addr_t req_addr_phys;
struct te_oper_param *param_addr;
dma_addr_t param_addr_phys;
uint64_t *plist_addr;
dma_addr_t plist_addr_phys;
unsigned long *param_bitmap;
unsigned long *plist_bitmap;
struct list_head used_cmd_list;
struct list_head free_cmd_list;
};
struct te_cmd_req_desc {
struct te_request *req_addr;
struct list_head list;
};
struct te_shmem_desc {
struct list_head list;
uint32_t type;
uint32_t plist_idx;
size_t size;
struct page **pages;
unsigned int nr_pages;
};
/*
* Per-session data structure.
*
* Both temp (freed upon completion of the associated op) and persist
* (freed upon session close) memory references are tracked by this
* structure.
*
* Persistent memory references stay on an inactive list until the
* associated op completes. If it completes successfully then the
* references are moved to the active list.
*/
struct te_session {
struct list_head list;
uint32_t session_id;
struct list_head temp_shmem_list;
struct list_head inactive_persist_shmem_list;
struct list_head persist_shmem_list;
};
struct tlk_context {
struct tlk_device *dev;
struct list_head session_list;
};
enum {
/* Trusted Application Calls */
TE_SMC_OPEN_SESSION = 0x70000001,
TE_SMC_CLOSE_SESSION = 0x70000002,
TE_SMC_LAUNCH_OPERATION = 0x70000003,
TE_SMC_TA_EVENT = 0x70000004,
/* Trusted OS (64-bit) calls */
TE_SMC_REGISTER_REQ_BUF = 0x72000001,
TE_SMC_INIT_LOGGER = 0x72000002,
TE_SMC_RESTART = 0x72000100,
/* SIP (SOC specific) calls. */
TE_SMC_PROGRAM_VPR = 0x82000003,
TE_SMC_REGISTER_FIQ_GLUE = 0x82000005,
TE_SMC_VRR_SET_BUF = 0x82000011,
TE_SMC_VRR_SEC = 0x82000012,
};
enum {
TE_PARAM_TYPE_NONE = 0x0,
TE_PARAM_TYPE_INT_RO = 0x1,
TE_PARAM_TYPE_INT_RW = 0x2,
TE_PARAM_TYPE_MEM_RO = 0x3,
TE_PARAM_TYPE_MEM_RW = 0x4,
TE_PARAM_TYPE_PERSIST_MEM_RO = 0x100,
TE_PARAM_TYPE_PERSIST_MEM_RW = 0x101,
TE_PARAM_TYPE_FLAGS_PHYS_LIST = 0x1000,
TE_PARAM_TYPE_ALL_FLAGS = TE_PARAM_TYPE_FLAGS_PHYS_LIST
};
enum {
TE_MEM_TYPE_NS_USER = 0x0,
TE_MEM_TYPE_NS_KERNEL = 0x1,
};
struct te_oper_param {
uint32_t index;
uint32_t type;
union {
struct {
uint32_t val;
} Int;
struct {
uint64_t base;
uint32_t len;
uint32_t type;
} Mem;
} u;
uint64_t next_ptr_user;
};
struct te_request {
uint32_t type;
uint32_t session_id;
uint32_t command_id;
uint64_t params;
uint32_t params_size;
uint32_t dest_uuid[4];
uint32_t result;
uint32_t result_origin;
};
struct te_answer {
uint32_t result;
uint32_t session_id;
uint32_t result_origin;
};
void te_open_session(struct te_opensession *cmd,
struct te_request *request,
struct tlk_context *context);
void te_close_session(struct te_closesession *cmd,
struct te_request *request,
struct tlk_context *context);
void te_launch_operation(struct te_launchop *cmd,
struct te_request *request,
struct tlk_context *context);
enum ta_event_id {
TA_EVENT_RESTORE_KEYS = 0,
TA_EVENT_MASK = (1 << TA_EVENT_RESTORE_KEYS),
};
int te_handle_ss_ioctl(struct file *file, unsigned int ioctl_num,
unsigned long ioctl_param);
void ote_print_logs(void);
int tlk_ss_op(void);
int ote_property_is_disabled(const char *str);
int te_prep_mem_buffers(struct te_request *request,
struct te_session *session);
void te_release_mem_buffers(struct list_head *buflist);
void te_activate_persist_mem_buffers(struct te_session *session);
#endif
+79
View File
@@ -0,0 +1,79 @@
/*
* Copyright (c) 2013 NVIDIA Corporation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef __OTE_TYPES_H__
#define __OTE_TYPES_H__
/*
* Return Codes
*/
enum {
/* Success */
OTE_SUCCESS = 0x00000000,
OTE_ERROR_NO_ERROR = OTE_SUCCESS,
/* Non-specific cause */
OTE_ERROR_GENERIC = 0xFFFF0000,
/* Access priviledge not sufficient */
OTE_ERROR_ACCESS_DENIED = 0xFFFF0001,
/* The operation was cancelled */
OTE_ERROR_CANCEL = 0xFFFF0002,
/* Concurrent accesses conflict */
OTE_ERROR_ACCESS_CONFLICT = 0xFFFF0003,
/* Too much data for req was passed */
OTE_ERROR_EXCESS_DATA = 0xFFFF0004,
/* Input data was of invalid format */
OTE_ERROR_BAD_FORMAT = 0xFFFF0005,
/* Input parameters were invalid */
OTE_ERROR_BAD_PARAMETERS = 0xFFFF0006,
/* Oper invalid in current state */
OTE_ERROR_BAD_STATE = 0xFFFF0007,
/* The req data item not found */
OTE_ERROR_ITEM_NOT_FOUND = 0xFFFF0008,
/* The req oper not implemented */
OTE_ERROR_NOT_IMPLEMENTED = 0xFFFF0009,
/* The req oper not supported */
OTE_ERROR_NOT_SUPPORTED = 0xFFFF000A,
/* Expected data was missing */
OTE_ERROR_NO_DATA = 0xFFFF000B,
/* System ran out of resources */
OTE_ERROR_OUT_OF_MEMORY = 0xFFFF000C,
/* The system is busy */
OTE_ERROR_BUSY = 0xFFFF000D,
/* Communication failed */
OTE_ERROR_COMMUNICATION = 0xFFFF000E,
/* A security fault was detected */
OTE_ERROR_SECURITY = 0xFFFF000F,
/* The supplied buffer is too short */
OTE_ERROR_SHORT_BUFFER = 0xFFFF0010,
};
/*
* Return Code origins
*/
enum {
/* Originated from OTE Client API */
OTE_RESULT_ORIGIN_API = 1,
/* Originated from Underlying Communication Stack */
OTE_RESULT_ORIGIN_COMMS = 2,
/* Originated from Common OTE Code */
OTE_RESULT_ORIGIN_KERNEL = 3,
/* Originated from Trusted APP Code */
OTE_RESULT_ORIGIN_TRUSTED_APP = 4,
};
#endif