821 lines
19 KiB
C
821 lines
19 KiB
C
/*
|
|
* Copyright (C) 2012-2019 Samsung Electronics, Inc.
|
|
*
|
|
* 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 <linux/atomic.h>
|
|
#include <linux/buffer_head.h>
|
|
#include <linux/completion.h>
|
|
#include <linux/cpu.h>
|
|
#include <linux/file.h>
|
|
#include <linux/fs.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/ioctl.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/kfifo.h>
|
|
#include <linux/list.h>
|
|
#include <linux/miscdevice.h>
|
|
#include <linux/mmzone.h>
|
|
#include <linux/module.h>
|
|
#include <linux/mutex.h>
|
|
#include <linux/of.h>
|
|
#include <linux/of_irq.h>
|
|
#include <linux/pid.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/sched.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/spinlock.h>
|
|
#include <linux/syscore_ops.h>
|
|
#include <linux/time.h>
|
|
#include <linux/uaccess.h>
|
|
#include <linux/workqueue.h>
|
|
|
|
#include <asm/segment.h>
|
|
#include <asm/uaccess.h>
|
|
|
|
#include "sysdep.h"
|
|
#include "tzdev.h"
|
|
#include "tz_boost.h"
|
|
#include "tz_cdev.h"
|
|
#include "tz_cma.h"
|
|
#include "tz_iw_boot_log.h"
|
|
#include "tz_iwio.h"
|
|
#include "tz_iwlog.h"
|
|
#include "tz_kernel_api_internal.h"
|
|
#include "tzlog.h"
|
|
#include "tz_mem.h"
|
|
#include "tz_panic_dump.h"
|
|
#include "tz_platform.h"
|
|
#include "tzprofiler.h"
|
|
#include "tz_wormhole.h"
|
|
|
|
MODULE_AUTHOR("Jaemin Ryu <jm77.ryu@samsung.com>");
|
|
MODULE_AUTHOR("Vasily Leonenko <v.leonenko@samsung.com>");
|
|
MODULE_AUTHOR("Alex Matveev <alex.matveev@samsung.com>");
|
|
MODULE_DESCRIPTION("TZDEV driver");
|
|
MODULE_LICENSE("GPL");
|
|
|
|
int tzdev_verbosity;
|
|
|
|
module_param(tzdev_verbosity, int, 0644);
|
|
MODULE_PARM_DESC(tzdev_verbosity, "0: normal, 1: verbose, 2: debug");
|
|
|
|
enum tzdev_swd_state {
|
|
TZDEV_SWD_DOWN,
|
|
TZDEV_SWD_UP,
|
|
TZDEV_SWD_DEAD
|
|
};
|
|
|
|
static int tzdev_fd_open;
|
|
static DEFINE_MUTEX(tzdev_fd_mutex);
|
|
|
|
static atomic_t tzdev_swd_state = ATOMIC_INIT(TZDEV_SWD_DOWN);
|
|
|
|
DECLARE_COMPLETION(tzdev_iwi_event_done);
|
|
|
|
static struct tzio_sysconf tz_sysconf;
|
|
|
|
static DEFINE_SPINLOCK(tzdev_rsp_lock);
|
|
static unsigned int tzdev_rsp_event_mask;
|
|
|
|
#define TZDEV_RSP_UFIFO_DEPTH 1024
|
|
#define TZDEV_RSP_KFIFO_DEPTH 16
|
|
|
|
static DEFINE_KFIFO(tzdev_rsp_ufifo, unsigned int, TZDEV_RSP_UFIFO_DEPTH);
|
|
static DEFINE_KFIFO(tzdev_rsp_kfifo, unsigned int, TZDEV_RSP_KFIFO_DEPTH);
|
|
|
|
static struct tzio_service_channel *tzdev_service_channel;
|
|
/* We need this due to currently Tzdaemon use SWd cpu mask to
|
|
* wake up worker threads. Will be completely removed after
|
|
* moving Tzdaemon worker threads to TZDEV */
|
|
static unsigned long tzdaemon_cpu_mask;
|
|
|
|
int __tzdev_smc_cmd(struct tzio_smc_data *data,
|
|
unsigned int swd_ctx_present)
|
|
{
|
|
int ret;
|
|
|
|
#ifdef TZIO_DEBUG
|
|
printk(KERN_ERR "tzdev_smc_cmd: args[0]=0x%lx, args[1]=0x%lx, args[2]=0x%lx, args[3]=0x%lx\n",
|
|
data->args[0], data->args[1], data->args[2], data->args[3]);
|
|
#endif
|
|
tzprofiler_enter_sw();
|
|
tz_iwlog_schedule_delayed_work();
|
|
|
|
ret = tzdev_platform_smc_call(data);
|
|
|
|
tz_iwlog_cancel_delayed_work();
|
|
|
|
if (swd_ctx_present) {
|
|
tzdev_notify_swd_cpu_mask_update();
|
|
tz_iwnotify_call_chains(data->iwnotify_oem_flags);
|
|
data->iwnotify_oem_flags = 0;
|
|
}
|
|
|
|
tzprofiler_wait_for_bufs();
|
|
tzprofiler_exit_sw();
|
|
tz_iwlog_read_buffers();
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* TZDEV interface functions */
|
|
unsigned int tzdev_is_up(void)
|
|
{
|
|
return atomic_read(&tzdev_swd_state) == TZDEV_SWD_UP;
|
|
}
|
|
|
|
static int tzdev_alloc_aux_channels(void)
|
|
{
|
|
int ret;
|
|
unsigned int i;
|
|
|
|
for (i = 0; i < NR_SW_CPU_IDS; ++i) {
|
|
ret = tz_iwio_alloc_aux_channel(i);
|
|
if (ret)
|
|
return ret;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int tzdev_alloc_service_channel(void)
|
|
{
|
|
tzdev_service_channel = tz_iwio_alloc_iw_channel(TZ_IWIO_CONNECT_SERVICE, 1);
|
|
if (!tzdev_service_channel)
|
|
return -ENOMEM;
|
|
|
|
return 0;
|
|
}
|
|
|
|
unsigned long tzdev_get_swd_cpu_mask(void)
|
|
{
|
|
if (!tzdev_service_channel)
|
|
return 0;
|
|
|
|
return tzdev_service_channel->mask;
|
|
}
|
|
|
|
static void tzdev_get_nwd_sysconf(struct tzio_sysconf *s)
|
|
{
|
|
s->flags = 0;
|
|
#if defined(CONFIG_TZDEV_QC_CRYPTO_CLOCKS_USR_MNG)
|
|
s->flags |= SYSCONF_CRYPTO_CLOCK_MANAGEMENT;
|
|
#endif
|
|
}
|
|
|
|
static int tzdev_get_sysconf(struct tzio_sysconf *s)
|
|
{
|
|
struct tzio_sysconf nwd_sysconf;
|
|
struct tz_iwio_aux_channel *ch;
|
|
int ret = 0;
|
|
|
|
/* Get sysconf from SWd */
|
|
ch = tz_iwio_get_aux_channel();
|
|
ret = tzdev_smc_get_swd_sysconf();
|
|
tz_iwio_put_aux_channel();
|
|
|
|
if (ret) {
|
|
tzdev_print(0, "tzdev_smc_get_swd_sysconf() failed with %d\n", ret);
|
|
return ret;
|
|
}
|
|
memcpy(s, ch->buffer, sizeof(struct tzio_sysconf));
|
|
|
|
tzdev_get_nwd_sysconf(&nwd_sysconf);
|
|
|
|
/* Merge NWd and SWd sysconf structures */
|
|
s->flags |= nwd_sysconf.flags;
|
|
|
|
tz_boost_set_boost_mask(s->big_cpus_mask);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static irqreturn_t tzdev_event_handler(int irq, void *ptr)
|
|
{
|
|
complete(&tzdev_iwi_event_done);
|
|
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
#if CONFIG_TZDEV_IWI_PANIC != 0
|
|
static void dump_kernel_panic_bh(struct work_struct *work)
|
|
{
|
|
atomic_set(&tzdev_swd_state, TZDEV_SWD_DEAD);
|
|
tz_iw_boot_log_read();
|
|
tz_iwlog_read_buffers();
|
|
}
|
|
|
|
static DECLARE_WORK(dump_kernel_panic, dump_kernel_panic_bh);
|
|
|
|
static irqreturn_t tzdev_panic_handler(int irq, void *ptr)
|
|
{
|
|
schedule_work(&dump_kernel_panic);
|
|
return IRQ_HANDLED;
|
|
}
|
|
#endif
|
|
|
|
void tzdev_resolve_iwis_id(unsigned int *iwi_event, unsigned int *iwi_panic)
|
|
{
|
|
struct device_node *node;
|
|
|
|
node = of_find_compatible_node(NULL, NULL, "samsung,blowfish");
|
|
if (!node) {
|
|
*iwi_event = CONFIG_TZDEV_IWI_EVENT;
|
|
#if CONFIG_TZDEV_IWI_PANIC != 0
|
|
*iwi_panic = CONFIG_TZDEV_IWI_PANIC;
|
|
#endif
|
|
tzdev_print(0, "of_find_compatible_node() failed\n");
|
|
return;
|
|
}
|
|
|
|
*iwi_event = irq_of_parse_and_map(node, 0);
|
|
if (!*iwi_event) {
|
|
*iwi_event = CONFIG_TZDEV_IWI_EVENT;
|
|
tzdev_print(0, "Use IWI event IRQ number from config %d\n",
|
|
CONFIG_TZDEV_IWI_EVENT);
|
|
}
|
|
|
|
#if CONFIG_TZDEV_IWI_PANIC != 0
|
|
*iwi_panic = irq_of_parse_and_map(node, 1);
|
|
if (!*iwi_panic) {
|
|
*iwi_panic = CONFIG_TZDEV_IWI_PANIC;
|
|
tzdev_print(0, "Use IWI panic IRQ number from config %d\n",
|
|
CONFIG_TZDEV_IWI_PANIC);
|
|
}
|
|
#endif
|
|
of_node_put(node);
|
|
}
|
|
|
|
static void tzdev_register_iwis(void)
|
|
{
|
|
int ret;
|
|
int iwi_event;
|
|
int iwi_panic;
|
|
|
|
tzdev_resolve_iwis_id(&iwi_event, &iwi_panic);
|
|
|
|
ret = request_irq(iwi_event, tzdev_event_handler, 0, "tzdev_iwi_event", NULL);
|
|
if (ret)
|
|
tzdev_print(0, "TZDEV_IWI_EVENT registration failed: %d\n", ret);
|
|
|
|
#if CONFIG_TZDEV_IWI_PANIC != 0
|
|
ret = request_irq(iwi_panic, tzdev_panic_handler, 0, "tzdev_iwi_panic", NULL);
|
|
if (ret)
|
|
tzdev_print(0, "TZDEV_IWI_PANIC registration failed: %d\n", ret);
|
|
#endif
|
|
}
|
|
|
|
static int tzdev_run_init_sequence(void)
|
|
{
|
|
int ret = 0;
|
|
|
|
if (atomic_read(&tzdev_swd_state) == TZDEV_SWD_DOWN) {
|
|
/* check kernel and driver version compatibility with Blowfish */
|
|
ret = tzdev_smc_check_version();
|
|
if (ret == -ENOSYS) {
|
|
tzdev_print(0, "Minor version of TZDev driver is newer than version of"
|
|
"Blowfish secure kernel.\nNot critical, continue...\n");
|
|
ret = 0;
|
|
} else if (ret) {
|
|
tzdev_print(0, "The version of the Linux kernel or "
|
|
"TZDev driver is not compatible with Blowfish "
|
|
"secure kernel\n");
|
|
goto out;
|
|
}
|
|
|
|
if (tzdev_cma_mem_register()) {
|
|
tzdev_print(0, "tzdev_cma_mem_register() failed\n");
|
|
ret = -ESHUTDOWN;
|
|
goto out;
|
|
}
|
|
|
|
if (tzdev_alloc_aux_channels()) {
|
|
tzdev_print(0, "tzdev_alloc_AUX_Channels() failed\n");
|
|
ret = -ESHUTDOWN;
|
|
goto out;
|
|
}
|
|
|
|
if (tzdev_alloc_service_channel()) {
|
|
tzdev_print(0, "tzdev_alloc_service_channel() failed\n");
|
|
ret = -ESHUTDOWN;
|
|
goto out;
|
|
}
|
|
|
|
if (tzdev_mem_init()) {
|
|
tzdev_print(0, "tzdev_mem_init() failed\n");
|
|
ret = -ESHUTDOWN;
|
|
goto out;
|
|
}
|
|
|
|
if (tz_iwlog_initialize()) {
|
|
tzdev_print(0, "tz_iwlog_initialize() failed\n");
|
|
ret = -ESHUTDOWN;
|
|
goto out;
|
|
}
|
|
|
|
if (tzprofiler_initialize()) {
|
|
tzdev_print(0, "tzprofiler_initialize() failed\n");
|
|
ret = -ESHUTDOWN;
|
|
goto out;
|
|
}
|
|
|
|
if (tz_panic_dump_alloc_buffer()) {
|
|
tzdev_print(0, "tz_panic_dump_alloc_buffer() failed\n");
|
|
ret = -ESHUTDOWN;
|
|
goto out;
|
|
}
|
|
|
|
if (tzdev_get_sysconf(&tz_sysconf)) {
|
|
tzdev_print(0, "tzdev_get_sysconf() failed\n");
|
|
ret = -ESHUTDOWN;
|
|
goto out;
|
|
}
|
|
|
|
tzdev_register_iwis();
|
|
tz_iwnotify_initialize();
|
|
tzdev_kapi_init();
|
|
|
|
if (atomic_cmpxchg(&tzdev_swd_state, TZDEV_SWD_DOWN, TZDEV_SWD_UP)) {
|
|
ret = -ESHUTDOWN;
|
|
goto out;
|
|
}
|
|
}
|
|
out:
|
|
if (ret == -ESHUTDOWN) {
|
|
atomic_set(&tzdev_swd_state, TZDEV_SWD_DEAD);
|
|
tz_iw_boot_log_read();
|
|
tz_iwlog_read_buffers();
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static int tzdev_get_access_info(struct tzio_access_info *s)
|
|
{
|
|
struct task_struct *task;
|
|
struct mm_struct *mm;
|
|
struct file *exe_file;
|
|
|
|
rcu_read_lock();
|
|
|
|
task = find_task_by_vpid(s->pid);
|
|
if (!task) {
|
|
rcu_read_unlock();
|
|
return -ESRCH;
|
|
}
|
|
|
|
get_task_struct(task);
|
|
rcu_read_unlock();
|
|
|
|
s->gid = task->tgid;
|
|
|
|
mm = get_task_mm(task);
|
|
put_task_struct(task);
|
|
if (!mm)
|
|
return -ESRCH;
|
|
|
|
exe_file = get_mm_exe_file(mm);
|
|
mmput(mm);
|
|
if (!exe_file)
|
|
return -ESRCH;
|
|
|
|
strncpy(s->ca_name, exe_file->f_path.dentry->d_name.name, CA_ID_LEN);
|
|
if (s->ca_name[CA_ID_LEN - 1])
|
|
return -ENAMETOOLONG;
|
|
|
|
fput(exe_file);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct tzio_smc_data tzdev_process_reply(struct tzio_smc_data d, unsigned int is_user)
|
|
{
|
|
unsigned int pipe = 0;
|
|
unsigned int is_pipe_user = 1;
|
|
unsigned int notify_user = 0;
|
|
unsigned int num_written, num_read;
|
|
|
|
if (!tzdev_is_mem_exist(d.pipe, &is_pipe_user))
|
|
tzdev_print(2, "Pipe 0x%x not found in TZDEV shmem idr\n", d.args[0]);
|
|
|
|
pipe = d.args[0] & TZDEV_PIPE_TARGET_DEAD_MASK;
|
|
d.args[0] &= ~TZDEV_TARGET_DEAD_MASK;
|
|
|
|
spin_lock(&tzdev_rsp_lock);
|
|
tzdev_rsp_event_mask |= d.event_mask;
|
|
/* Here we need to notify user space about pending
|
|
* event or reply in case current request was sent by kernel */
|
|
if (!is_user) {
|
|
/* Has pending flags */
|
|
if (tzdev_rsp_event_mask)
|
|
notify_user = 1;
|
|
/* CPU mask is outdated */
|
|
if (tzdaemon_cpu_mask != tzdev_get_swd_cpu_mask())
|
|
notify_user = 1;
|
|
/* Got user space pipe but user space reply fifo is empty,
|
|
* in this case user space can sleep and we need to notify
|
|
* it */
|
|
if (is_pipe_user && kfifo_is_empty(&tzdev_rsp_ufifo))
|
|
notify_user = 1;
|
|
}
|
|
/* Got zero pipe, no meaningful reply from
|
|
* secure kernel, try to read reply from fifo */
|
|
if (!pipe)
|
|
goto read_reply;
|
|
|
|
num_written = is_pipe_user ?
|
|
sysdep_kfifo_put(&tzdev_rsp_ufifo, pipe) :
|
|
sysdep_kfifo_put(&tzdev_rsp_kfifo, pipe);
|
|
if (!num_written) {
|
|
tzdev_print(0, "Putting response to %s queue failed\n",
|
|
is_pipe_user ? "user" : "kernel");
|
|
/* User FIFO overflow is handled by dropping the message. */
|
|
BUG_ON(!is_pipe_user);
|
|
}
|
|
|
|
read_reply:
|
|
memset(&d, 0, sizeof(d));
|
|
|
|
if (is_user) {
|
|
num_read = kfifo_get(&tzdev_rsp_ufifo, &pipe);
|
|
/* Check if fifo with user space replies
|
|
* contains more data, if yes we need to notify
|
|
* user space about it */
|
|
if (num_read && !kfifo_is_empty(&tzdev_rsp_ufifo))
|
|
notify_user = 1;
|
|
|
|
d.args[0] = num_read ? pipe : 0;
|
|
/* Update user space notification flags */
|
|
d.event_mask |= tzdev_rsp_event_mask;
|
|
tzdev_rsp_event_mask = 0;
|
|
} else {
|
|
num_read = kfifo_get(&tzdev_rsp_kfifo, &pipe);
|
|
d.args[0] = num_read ? pipe : 0;
|
|
}
|
|
spin_unlock(&tzdev_rsp_lock);
|
|
|
|
if (notify_user)
|
|
complete(&tzdev_iwi_event_done);
|
|
|
|
return d;
|
|
}
|
|
|
|
static struct tzio_smc_data tzdev_send_command_user(unsigned int tid, unsigned int shm_id)
|
|
{
|
|
struct tzio_smc_data d;
|
|
|
|
d = tzdev_smc_command(tid, shm_id);
|
|
|
|
return tzdev_process_reply(d, 1);
|
|
}
|
|
|
|
struct tzio_smc_data tzdev_send_command(unsigned int tid, unsigned int shm_id)
|
|
{
|
|
struct tzio_smc_data d;
|
|
|
|
d = tzdev_smc_command(tid, shm_id);
|
|
|
|
return tzdev_process_reply(d, 0);
|
|
}
|
|
|
|
static struct tzio_smc_data tzdev_get_event_user(void)
|
|
{
|
|
struct tzio_smc_data d;
|
|
|
|
d = tzdev_smc_get_event();
|
|
|
|
return tzdev_process_reply(d, 1);
|
|
}
|
|
|
|
struct tzio_smc_data tzdev_get_event(void)
|
|
{
|
|
struct tzio_smc_data d;
|
|
|
|
d = tzdev_smc_get_event();
|
|
|
|
return tzdev_process_reply(d, 0);
|
|
}
|
|
|
|
static struct tzio_smc_data tzdev_update_ree_time(void)
|
|
{
|
|
struct timespec ts;
|
|
struct tzio_smc_data d;
|
|
|
|
getnstimeofday(&ts);
|
|
d = tzdev_smc_update_ree_time(ts.tv_sec, ts.tv_nsec);
|
|
|
|
return tzdev_process_reply(d, 1);
|
|
}
|
|
|
|
static int tzdev_restart_swd_userspace(void)
|
|
{
|
|
#define TZDEV_SWD_USERSPACE_RESTART_TIMEOUT 10
|
|
|
|
int ret;
|
|
struct timespec ts, te;
|
|
|
|
/* FIXME Ugly, but works.
|
|
* Will be corrected in the course of SMC handler work */
|
|
getrawmonotonic(&ts);
|
|
do {
|
|
ret = tzdev_smc_swd_restart_userspace();
|
|
getrawmonotonic(&te);
|
|
if (te.tv_sec - ts.tv_sec > TZDEV_SWD_USERSPACE_RESTART_TIMEOUT) {
|
|
atomic_set(&tzdev_swd_state, TZDEV_SWD_DEAD);
|
|
tzdev_print(0, "Timeout on starting SWd userspace.\n");
|
|
return -ETIME;
|
|
}
|
|
} while (ret != 1);
|
|
|
|
/* We need to release all iwshmems here to avoid
|
|
* situation when we have alive SWd applications that
|
|
* are able to write to iwshmems and released iwshmems on
|
|
* the Linux kernel side, otherwise
|
|
* we can have random memory corruptions */
|
|
tzdev_mem_release_all_user();
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int tzdev_open(struct inode *inode, struct file *filp)
|
|
{
|
|
int ret = 0;
|
|
|
|
if (atomic_read(&tzdev_swd_state) == TZDEV_SWD_DEAD)
|
|
return -ESHUTDOWN;
|
|
|
|
mutex_lock(&tzdev_fd_mutex);
|
|
|
|
if (tzdev_fd_open != 0) {
|
|
ret = -EBUSY;
|
|
goto out;
|
|
}
|
|
|
|
tzdev_platform_open();
|
|
|
|
#if !defined(CONFIG_TZDEV_EARLY_SWD_INIT)
|
|
ret = tzdev_run_init_sequence();
|
|
if (ret)
|
|
goto out;
|
|
|
|
ret = tzdev_restart_swd_userspace();
|
|
if (ret)
|
|
goto out;
|
|
#endif /* CONFIG_TZDEV_EARLY_SWD_INIT */
|
|
|
|
tzdev_fd_open++;
|
|
|
|
out:
|
|
mutex_unlock(&tzdev_fd_mutex);
|
|
return ret;
|
|
}
|
|
|
|
static int tzdev_release(struct inode *inode, struct file *filp)
|
|
{
|
|
#if defined(CONFIG_TZDEV_NWD_PANIC_ON_CLOSE)
|
|
panic("tzdev invalid close\n");
|
|
#endif /* CONFIG_TZDEV_NWD_PANIC_ON_CLOSE */
|
|
|
|
mutex_lock(&tzdev_fd_mutex);
|
|
tz_boost_disable();
|
|
|
|
tzdev_fd_open--;
|
|
BUG_ON(tzdev_fd_open);
|
|
|
|
mutex_unlock(&tzdev_fd_mutex);
|
|
|
|
tzdev_platform_close();
|
|
|
|
#if defined(CONFIG_TZDEV_EARLY_SWD_INIT)
|
|
tzdev_restart_swd_userspace();
|
|
#endif /* CONFIG_TZDEV_EARLY_SWD_INIT */
|
|
|
|
tz_wormhole_close_connection();
|
|
|
|
return 0;
|
|
}
|
|
|
|
int tzdev_is_opened(void)
|
|
{
|
|
return tzdev_fd_open;
|
|
}
|
|
|
|
static long tzdev_unlocked_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
|
{
|
|
if (atomic_read(&tzdev_swd_state) == TZDEV_SWD_DEAD)
|
|
return -ESHUTDOWN;
|
|
|
|
switch (cmd) {
|
|
case TZIO_SMC: {
|
|
struct tzio_smc_data __user *argp = (struct tzio_smc_data __user *)arg;
|
|
struct tzio_smc_data s;
|
|
|
|
if (copy_from_user(&s, argp, sizeof(struct tzio_smc_data)))
|
|
return -EFAULT;
|
|
|
|
s = tzdev_send_command_user(s.args[0], s.args[1]);
|
|
|
|
if (copy_to_user(argp, &s, sizeof(struct tzio_smc_data)))
|
|
return -EFAULT;
|
|
|
|
return 0;
|
|
}
|
|
case TZIO_GET_ACCESS_INFO: {
|
|
int ret = 0;
|
|
struct tzio_access_info __user *argp = (struct tzio_access_info __user *)arg;
|
|
struct tzio_access_info s;
|
|
|
|
if (copy_from_user(&s, argp, sizeof(struct tzio_access_info)))
|
|
return -EFAULT;
|
|
|
|
ret = tzdev_get_access_info(&s);
|
|
if (ret)
|
|
return ret;
|
|
if (copy_to_user(argp, &s, sizeof(struct tzio_access_info)))
|
|
return -EFAULT;
|
|
|
|
return 0;
|
|
}
|
|
case TZIO_GET_SYSCONF: {
|
|
struct tzio_sysconf __user *argp = (struct tzio_sysconf __user *)arg;
|
|
|
|
if (copy_to_user(argp, &tz_sysconf, sizeof(struct tzio_sysconf)))
|
|
return -EFAULT;
|
|
|
|
return 0;
|
|
}
|
|
case TZIO_MEM_REGISTER: {
|
|
int ret = 0;
|
|
struct tzio_mem_register __user *argp = (struct tzio_mem_register __user *)arg;
|
|
struct tzio_mem_register s;
|
|
|
|
if (copy_from_user(&s, argp, sizeof(struct tzio_mem_register)))
|
|
return -EFAULT;
|
|
|
|
ret = tzdev_mem_register_user(&s);
|
|
if (ret)
|
|
return ret;
|
|
|
|
if (copy_to_user(argp, &s, sizeof(struct tzio_mem_register)))
|
|
return -EFAULT;
|
|
|
|
return 0;
|
|
}
|
|
case TZIO_MEM_RELEASE: {
|
|
return tzdev_mem_release_user(arg);
|
|
}
|
|
case TZIO_WAIT_EVT: {
|
|
return wait_for_completion_interruptible(&tzdev_iwi_event_done);
|
|
}
|
|
case TZIO_GET_PIPE: {
|
|
struct tzio_smc_data __user *argp = (struct tzio_smc_data __user *)arg;
|
|
struct tzio_smc_data s;
|
|
|
|
s = tzdev_get_event_user();
|
|
|
|
if (copy_to_user(argp, &s, sizeof(struct tzio_smc_data)))
|
|
return -EFAULT;
|
|
|
|
return 0;
|
|
}
|
|
case TZIO_UPDATE_REE_TIME: {
|
|
struct tzio_smc_data __user *argp = (struct tzio_smc_data __user *)arg;
|
|
struct tzio_smc_data s;
|
|
|
|
if (copy_from_user(&s, argp, sizeof(struct tzio_smc_data)))
|
|
return -EFAULT;
|
|
|
|
s = tzdev_update_ree_time();
|
|
|
|
if (copy_to_user(argp, &s, sizeof(struct tzio_smc_data)))
|
|
return -EFAULT;
|
|
|
|
return 0;
|
|
}
|
|
case TZIO_BOOST: {
|
|
tz_boost_enable();
|
|
return 0;
|
|
}
|
|
case TZIO_RELAX: {
|
|
tz_boost_disable();
|
|
return 0;
|
|
}
|
|
case TZIO_GET_CPU_MASK: {
|
|
tzdaemon_cpu_mask = tzdev_get_swd_cpu_mask();
|
|
return tzdaemon_cpu_mask;
|
|
}
|
|
case TZIO_ACCEPT_NEW_HOLE:
|
|
return tz_wormhole_tzdev_accept();
|
|
default:
|
|
return tzdev_platform_ioctl(cmd, arg);
|
|
}
|
|
}
|
|
|
|
static void tzdev_shutdown(void)
|
|
{
|
|
if (atomic_read(&tzdev_swd_state) != TZDEV_SWD_DOWN)
|
|
tzdev_smc_shutdown();
|
|
}
|
|
|
|
static const struct file_operations tzdev_fops = {
|
|
.owner = THIS_MODULE,
|
|
.open = tzdev_open,
|
|
.release = tzdev_release,
|
|
.unlocked_ioctl = tzdev_unlocked_ioctl,
|
|
#ifdef CONFIG_COMPAT
|
|
.compat_ioctl = tzdev_unlocked_ioctl,
|
|
#endif /* CONFIG_COMPAT */
|
|
.poll = tz_wormhole_tzdev_poll,
|
|
};
|
|
|
|
static struct tz_cdev tzdev_cdev = {
|
|
.name = "tzdev",
|
|
.fops = &tzdev_fops,
|
|
.owner = THIS_MODULE,
|
|
};
|
|
|
|
static struct syscore_ops tzdev_syscore_ops = {
|
|
.shutdown = tzdev_shutdown
|
|
};
|
|
|
|
static int __init init_tzdev(void)
|
|
{
|
|
int rc;
|
|
|
|
rc = tz_cdev_register(&tzdev_cdev);
|
|
if (rc)
|
|
return rc;
|
|
|
|
rc = tzdev_platform_register();
|
|
if (rc) {
|
|
tzdev_print(0, "tzdev_platform_register() failed with error=%d\n", rc);
|
|
goto platform_driver_registration_failed;
|
|
}
|
|
|
|
rc = tzdev_init_hotplug();
|
|
if (rc) {
|
|
tzdev_print(0, "tzdev_init_hotplug() failed with error=%d\n", rc);
|
|
goto hotplug_initialization_failed;
|
|
}
|
|
|
|
tzdev_cma_mem_init(tzdev_cdev.device);
|
|
|
|
#if defined(CONFIG_TZDEV_EARLY_SWD_INIT)
|
|
rc = tzdev_run_init_sequence();
|
|
if (rc) {
|
|
tzdev_print(0, "tzdev_run_init_sequence() failed with error=%d\n", rc);
|
|
goto tzdev_initialization_failed;
|
|
}
|
|
|
|
rc = tzdev_restart_swd_userspace();
|
|
if (rc) {
|
|
tzdev_print(0, "tzdev_restart_swd_userspace() failed with error=%d\n", rc);
|
|
goto tzdev_swd_restart_failed;
|
|
}
|
|
#endif /* CONFIG_TZDEV_EARLY_SWD_INIT */
|
|
|
|
register_syscore_ops(&tzdev_syscore_ops);
|
|
|
|
return rc;
|
|
|
|
#if defined(CONFIG_TZDEV_EARLY_SWD_INIT)
|
|
tzdev_swd_restart_failed:
|
|
tzdev_initialization_failed:
|
|
tzdev_cma_mem_release(tzdev_cdev.device);
|
|
#endif /* CONFIG_TZDEV_EARLY_SWD_INIT */
|
|
hotplug_initialization_failed:
|
|
tzdev_platform_unregister();
|
|
platform_driver_registration_failed:
|
|
tz_cdev_unregister(&tzdev_cdev);
|
|
|
|
return rc;
|
|
}
|
|
|
|
static void __exit exit_tzdev(void)
|
|
{
|
|
tzdev_platform_unregister();
|
|
|
|
tz_cdev_unregister(&tzdev_cdev);
|
|
|
|
tzdev_mem_fini();
|
|
|
|
tzdev_cma_mem_release(tzdev_cdev.device);
|
|
|
|
unregister_syscore_ops(&tzdev_syscore_ops);
|
|
|
|
tzdev_shutdown();
|
|
|
|
tzdev_exit_hotplug();
|
|
}
|
|
|
|
module_init(init_tzdev);
|
|
module_exit(exit_tzdev);
|