kernel-brax3-ubuntu-touch/drivers/tee/gud/510/MobiCoreDriver/user.c
erascape f319b992b1 kernel-5.15: Initial import brax3 UT kernel
* halium configs enabled

Signed-off-by: erascape <erascape@proton.me>
2025-09-23 15:17:10 +00:00

436 lines
9 KiB
C

// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2013-2020 TRUSTONIC LIMITED
* 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
* version 2 as published by the Free Software Foundation.
*
* 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/cdev.h>
#include <linux/device.h>
#include <linux/export.h>
#include <linux/fs.h>
#include <linux/mm_types.h> /* struct vm_area_struct */
#include <linux/uaccess.h>
#include "public/mc_user.h"
#include "main.h"
#include "admin.h" /* tee_object* */
#include "mmu.h" /* tee_mmu* */
#include "client.h"
#include "mcp.h" /* mcp_get_version */
#include "protocol.h"
#include "user.h"
/*
* Get client object from file pointer
*/
static inline struct tee_client *get_client(struct file *file)
{
return (struct tee_client *)file->private_data;
}
/*
* Callback for system open()
* A set of internal client data are created and initialized.
*
* @inode
* @file
* Returns 0 if OK or -ENOMEM if no allocation was possible.
*/
static int user_open(struct inode *inode, struct file *file)
{
struct tee_client *client;
/* Create client */
mc_dev_devel("from %s (%d)", current->comm, current->pid);
client = client_create(false, protocol_vm_id());
if (!client)
return -ENOMEM;
/* Store client in user file */
file->private_data = client;
return 0;
}
/*
* Callback for system close()
* The client object is freed.
* @inode
* @file
* Returns 0
*/
static int user_release(struct inode *inode, struct file *file)
{
struct tee_client *client = get_client(file);
/* Close client */
mc_dev_devel("from %s (%d)", current->comm, current->pid);
if (!client)
return -EPROTO;
/* Detach client from user file */
file->private_data = NULL;
/* Destroy client, including remaining sessions */
client_close(client);
return 0;
}
/*
* Check r/w access to referenced memory
*/
static inline int ioctl_check_pointer(unsigned int cmd, int __user *uarg)
{
int err = 0;
if (_IOC_DIR(cmd) & _IOC_READ)
#if KERNEL_VERSION(5, 0, 0) > LINUX_VERSION_CODE
err = !access_ok(VERIFY_WRITE, uarg, _IOC_SIZE(cmd));
#else
err = !access_ok(uarg, _IOC_SIZE(cmd));
#endif
else if (_IOC_DIR(cmd) & _IOC_WRITE)
#if KERNEL_VERSION(5, 0, 0) > LINUX_VERSION_CODE
err = !access_ok(VERIFY_READ, uarg, _IOC_SIZE(cmd));
#else
err = !access_ok(uarg, _IOC_SIZE(cmd));
#endif
if (err)
return -EFAULT;
return 0;
}
/*
* Callback for system ioctl()
* Implement most of ClientLib API functions
* @file pointer to file
* @cmd command
* @arg arguments
*
* Returns 0 for OK and an errno in case of error
*/
static long user_ioctl(struct file *file, unsigned int id, unsigned long arg)
{
struct tee_client *client = get_client(file);
int __user *uarg = (int __user *)arg;
int ret = -EINVAL;
mc_dev_devel("%u from %s", _IOC_NR(id), current->comm);
if (!client)
return -EPROTO;
if (ioctl_check_pointer(id, uarg))
return -EFAULT;
switch (id) {
case MC_IO_HAS_SESSIONS:
/* Freeze the client */
if (client_has_sessions(client))
ret = -ENOTEMPTY;
else
ret = 0;
break;
case MC_IO_OPEN_SESSION: {
struct mc_ioctl_open_session session;
if (copy_from_user(&session, uarg, sizeof(session))) {
ret = -EFAULT;
break;
}
ret = client_mc_open_session(client, &session.uuid,
session.tci, session.tci_len,
&session.sid);
if (ret)
break;
if (copy_to_user(uarg, &session, sizeof(session))) {
ret = -EFAULT;
client_remove_session(client, session.sid);
break;
}
break;
}
case MC_IO_OPEN_TRUSTLET: {
struct mc_ioctl_open_trustlet trustlet;
if (copy_from_user(&trustlet, uarg, sizeof(trustlet))) {
ret = -EFAULT;
break;
}
ret = client_mc_open_trustlet(client,
trustlet.tl, trustlet.tl_len,
trustlet.tci, trustlet.tci_len,
&trustlet.sid);
if (ret)
break;
if (copy_to_user(uarg, &trustlet, sizeof(trustlet))) {
ret = -EFAULT;
client_remove_session(client, trustlet.sid);
break;
}
break;
}
case MC_IO_CLOSE_SESSION: {
u32 sid = (u32)arg;
ret = client_remove_session(client, sid);
break;
}
case MC_IO_NOTIFY: {
u32 sid = (u32)arg;
ret = client_notify_session(client, sid);
break;
}
case MC_IO_WAIT: {
struct mc_ioctl_wait wait;
if (copy_from_user(&wait, uarg, sizeof(wait))) {
ret = -EFAULT;
break;
}
ret = client_waitnotif_session(client, wait.sid, wait.timeout);
break;
}
case MC_IO_MAP: {
struct mc_ioctl_map map;
if (copy_from_user(&map, uarg, sizeof(map))) {
ret = -EFAULT;
break;
}
ret = client_mc_map(client, map.sid, NULL, &map.buf);
if (ret)
break;
/* Fill in return struct */
if (copy_to_user(uarg, &map, sizeof(map))) {
ret = -EFAULT;
break;
}
break;
}
case MC_IO_UNMAP: {
struct mc_ioctl_map map;
if (copy_from_user(&map, uarg, sizeof(map))) {
ret = -EFAULT;
break;
}
ret = client_mc_unmap(client, map.sid, &map.buf);
break;
}
case MC_IO_ERR: {
struct mc_ioctl_geterr __user *uerr =
(struct mc_ioctl_geterr __user *)uarg;
u32 sid;
s32 exit_code;
if (get_user(sid, &uerr->sid)) {
ret = -EFAULT;
break;
}
ret = client_get_session_exitcode(client, sid, &exit_code);
if (ret)
break;
/* Fill in return struct */
if (put_user(exit_code, &uerr->value)) {
ret = -EFAULT;
break;
}
break;
}
case MC_IO_VERSION: {
struct mc_version_info version_info;
ret = mcp_get_version(&version_info);
if (ret)
break;
if (copy_to_user(uarg, &version_info, sizeof(version_info)))
ret = -EFAULT;
break;
}
case MC_IO_GP_INITIALIZE_CONTEXT: {
struct mc_ioctl_gp_initialize_context context;
if (copy_from_user(&context, uarg, sizeof(context))) {
ret = -EFAULT;
break;
}
ret = client_gp_initialize_context(client, &context.ret);
if (copy_to_user(uarg, &context, sizeof(context))) {
ret = -EFAULT;
break;
}
break;
}
case MC_IO_GP_REGISTER_SHARED_MEM: {
struct mc_ioctl_gp_register_shared_mem shared_mem;
if (copy_from_user(&shared_mem, uarg, sizeof(shared_mem))) {
ret = -EFAULT;
break;
}
ret = client_gp_register_shared_mem(client, NULL, NULL,
&shared_mem.memref,
&shared_mem.ret);
if (copy_to_user(uarg, &shared_mem, sizeof(shared_mem))) {
ret = -EFAULT;
break;
}
break;
}
case MC_IO_GP_RELEASE_SHARED_MEM: {
struct mc_ioctl_gp_release_shared_mem shared_mem;
if (copy_from_user(&shared_mem, uarg, sizeof(shared_mem))) {
ret = -EFAULT;
break;
}
ret = client_gp_release_shared_mem(client, &shared_mem.memref);
break;
}
case MC_IO_GP_OPEN_SESSION: {
struct mc_ioctl_gp_open_session session;
struct tee_object *obj = NULL;
struct tee_mmu *ta_mmu = NULL;
if (copy_from_user(&session, uarg, sizeof(session))) {
ret = -EFAULT;
break;
}
/* Create object from user-space binary */
if (session.ta_len) {
obj = tee_object_read(session.ta, session.ta_len);
if (IS_ERR(obj)) {
ret = PTR_ERR(obj);
break;
}
{
struct mc_ioctl_buffer buf = {
.va = (uintptr_t)obj->data,
.len = obj->length,
.flags = MC_IO_MAP_INPUT,
};
ta_mmu = tee_mmu_create(NULL, &buf);
if (IS_ERR(ta_mmu)) {
ret = PTR_ERR(ta_mmu);
goto err_mmu;
}
}
}
ret = client_gp_open_session(client, &session.uuid, ta_mmu,
&session.operation,
&session.identity,
&session.ret, &session.session_id);
if (ta_mmu)
tee_mmu_put(ta_mmu);
err_mmu:
if (obj)
tee_object_free(obj);
if (copy_to_user(uarg, &session, sizeof(session))) {
ret = -EFAULT;
break;
}
break;
}
case MC_IO_GP_CLOSE_SESSION: {
struct mc_ioctl_gp_close_session session;
if (copy_from_user(&session, uarg, sizeof(session))) {
ret = -EFAULT;
break;
}
ret = client_gp_close_session(client, session.session_id);
break;
}
case MC_IO_GP_INVOKE_COMMAND: {
struct mc_ioctl_gp_invoke_command command;
if (copy_from_user(&command, uarg, sizeof(command))) {
ret = -EFAULT;
break;
}
ret = client_gp_invoke_command(client, command.session_id,
command.command_id,
&command.operation,
&command.ret);
if (copy_to_user(uarg, &command, sizeof(command))) {
ret = -EFAULT;
break;
}
break;
}
case MC_IO_GP_REQUEST_CANCELLATION: {
struct mc_ioctl_gp_request_cancellation cancel;
if (copy_from_user(&cancel, uarg, sizeof(cancel))) {
ret = -EFAULT;
break;
}
client_gp_request_cancellation(client,
cancel.operation.started);
ret = 0;
break;
}
default:
ret = -ENOIOCTLCMD;
mc_dev_err(ret, "unsupported command no %d", id);
}
return ret;
}
static const struct file_operations mc_user_fops = {
.owner = THIS_MODULE,
.open = user_open,
.release = user_release,
.unlocked_ioctl = user_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = user_ioctl,
#endif
};
int mc_user_init(struct cdev *cdev)
{
cdev_init(cdev, &mc_user_fops);
return 0;
}