kernel-brax3-ubuntu-touch/drivers/tee/gud/510/MobiCoreDriver/main.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

766 lines
17 KiB
C

// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2013-2019 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/platform_device.h>
#include <linux/module.h>
#include <linux/cdev.h>
#include <linux/debugfs.h>
#include <linux/delay.h>
#include <linux/of.h>
#include <linux/reboot.h>
#include <linux/suspend.h>
#include "public/mc_user.h"
#include "public/mc_admin.h" /* MC_ADMIN_DEVNODE */
#include "platform.h" /* MC_PM_RUNTIME */
#include "main.h"
#include "arm.h"
#include "admin.h"
#include "user.h"
#include "iwp.h"
#include "mcp.h"
#include "nq.h"
#include "protocol.h"
#include "client.h"
#include "build_tag.h"
#ifdef MC_TEE_HOTPLUG
#include <linux/cpuhotplug.h>
#endif
/* Default entry for our driver in device tree */
#ifndef MC_DEVICE_PROPNAME
#define MC_DEVICE_PROPNAME "arm,mcd"
#endif
/* Define a MobiCore device structure for use with dev_debug() etc */
static struct device_driver driver = {
.name = "Trustonic"
};
static struct device device = {
.driver = &driver
};
struct mc_device_ctx g_ctx = {
.mcd = &device
};
static struct {
/* Device tree compatibility */
bool use_platform_driver;
/* TEE start return code mutex */
struct mutex start_mutex;
/* TEE start return code */
int start_ret;
#ifdef MC_PM_RUNTIME
/* Whether hibernation succeeded */
bool did_hibernate;
/* Reboot notifications */
struct notifier_block reboot_notifier;
/* PM notifications */
struct notifier_block pm_notifier;
#endif
/* Devices */
dev_t device;
struct class *class;
/* Admin device */
struct cdev admin_cdev;
/* User device */
dev_t user_dev;
struct cdev user_cdev;
/* Debug counters */
struct mutex struct_counters_buf_mutex;
char struct_counters_buf[256];
int struct_counters_buf_len;
} main_ctx;
static int mobicore_start(void);
static void mobicore_stop(void);
int kasnprintf(struct kasnprintf_buf *buf, const char *fmt, ...)
{
va_list args;
int max_size = buf->size - buf->off;
int i;
va_start(args, fmt);
i = vsnprintf(buf->buf + buf->off, max_size, fmt, args);
if (i >= max_size) {
int new_size = PAGE_ALIGN(buf->size + i + 1);
char *new_buf = krealloc(buf->buf, new_size, buf->gfp);
if (!new_buf) {
i = -ENOMEM;
} else {
buf->buf = new_buf;
buf->size = new_size;
max_size = buf->size - buf->off;
i = vsnprintf(buf->buf + buf->off, max_size, fmt, args);
}
}
if (i > 0)
buf->off += i;
va_end(args);
return i;
}
static inline void kasnprintf_buf_reset(struct kasnprintf_buf *buf)
{
kfree(buf->buf);
buf->buf = NULL;
buf->size = 0;
buf->off = 0;
}
ssize_t debug_generic_read(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos,
int (*function)(struct kasnprintf_buf *buf))
{
struct kasnprintf_buf *buf = file->private_data;
int ret = 0;
mutex_lock(&buf->mutex);
/* Add/update buffer */
if (!*ppos) {
kasnprintf_buf_reset(buf);
ret = function(buf);
if (ret < 0) {
kasnprintf_buf_reset(buf);
goto end;
}
}
ret = simple_read_from_buffer(user_buf, count, ppos, buf->buf,
buf->off);
end:
mutex_unlock(&buf->mutex);
return ret;
}
int debug_generic_open(struct inode *inode, struct file *file)
{
struct kasnprintf_buf *buf;
file->private_data = kzalloc(sizeof(*buf), GFP_KERNEL);
if (!file->private_data)
return -ENOMEM;
buf = file->private_data;
mutex_init(&buf->mutex);
buf->gfp = GFP_KERNEL;
return 0;
}
int debug_generic_release(struct inode *inode, struct file *file)
{
struct kasnprintf_buf *buf = file->private_data;
if (!buf)
return 0;
kasnprintf_buf_reset(buf);
kfree(buf);
return 0;
}
static ssize_t debug_structs_read(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
{
return debug_generic_read(file, user_buf, count, ppos,
clients_debug_structs);
}
static const struct file_operations debug_structs_ops = {
.read = debug_structs_read,
.llseek = default_llseek,
.open = debug_generic_open,
.release = debug_generic_release,
};
static ssize_t debug_struct_counters_read(struct file *file,
char __user *user_buf,
size_t count, loff_t *ppos)
{
if (!*ppos) {
int ret;
mutex_lock(&main_ctx.struct_counters_buf_mutex);
ret = snprintf(main_ctx.struct_counters_buf,
sizeof(main_ctx.struct_counters_buf),
"clients: %d\n"
"cwsms: %d\n"
"sessions: %d\n"
"swsms: %d\n"
"mmus: %d\n"
"maps: %d\n"
"slots: %d\n"
"vm fes: %d\n"
"vm maps: %d\n",
atomic_read(&g_ctx.c_clients),
atomic_read(&g_ctx.c_cwsms),
atomic_read(&g_ctx.c_sessions),
atomic_read(&g_ctx.c_wsms),
atomic_read(&g_ctx.c_mmus),
atomic_read(&g_ctx.c_maps),
atomic_read(&g_ctx.c_slots),
atomic_read(&g_ctx.c_vm_fes),
atomic_read(&g_ctx.c_vm_maps));
mutex_unlock(&main_ctx.struct_counters_buf_mutex);
if (ret > 0)
main_ctx.struct_counters_buf_len = ret;
}
return simple_read_from_buffer(user_buf, count, ppos,
main_ctx.struct_counters_buf,
main_ctx.struct_counters_buf_len);
}
static const struct file_operations debug_struct_counters_ops = {
.read = debug_struct_counters_read,
.llseek = default_llseek,
};
static inline int device_user_init(void)
{
struct device *dev;
int ret = 0;
main_ctx.user_dev = MKDEV(MAJOR(main_ctx.device), 1);
/* Create the user node */
mc_user_init(&main_ctx.user_cdev);
ret = cdev_add(&main_ctx.user_cdev, main_ctx.user_dev, 1);
if (ret) {
mc_dev_err(ret, "user cdev_add failed");
return ret;
}
main_ctx.user_cdev.owner = THIS_MODULE;
dev = device_create(main_ctx.class, NULL, main_ctx.user_dev, NULL,
MC_USER_DEVNODE);
if (IS_ERR(dev)) {
ret = PTR_ERR(dev);
cdev_del(&main_ctx.user_cdev);
mc_dev_err(ret, "user device_create failed");
return ret;
}
/* Create debugfs structs entry */
debugfs_create_file("structs", 0400, g_ctx.debug_dir, NULL,
&debug_structs_ops);
return 0;
}
static inline void device_user_exit(void)
{
device_destroy(main_ctx.class, main_ctx.user_dev);
cdev_del(&main_ctx.user_cdev);
}
#ifdef MC_PM_RUNTIME
static int reboot_notifier(struct notifier_block *nb, unsigned long event,
void *dummy)
{
switch (event) {
case SYS_HALT:
case SYS_POWER_OFF:
main_ctx.did_hibernate = true;
break;
}
return 0;
}
static int suspend_notifier(struct notifier_block *nb, unsigned long event,
void *dummy)
{
int ret = 0;
main_ctx.did_hibernate = false;
#ifdef TRUSTONIC_HIBERNATION_SUPPORT
switch (event) {
case PM_HIBERNATION_PREPARE:
/* Try to stop the TEE nicely (ignore failure) */
nq_stop();
break;
case PM_POST_HIBERNATION:
if (main_ctx.did_hibernate) {
/* Really did hibernate */
client_cleanup();
main_ctx.start_ret = TEE_START_NOT_TRIGGERED;
return mobicore_start();
}
/* Did not hibernate, just restart the TEE */
ret = nq_start();
}
#endif
return ret;
}
#endif /* MC_PM_RUNTIME */
static inline int check_version(void)
{
struct mc_version_info version_info;
int ret;
/* Must be called before creating the user device node to avoid race */
ret = mcp_get_version(&version_info);
if (ret)
return ret;
/* CMP version is meaningless in this case and is thus not printed */
mc_dev_info("\n"
" product_id = %s\n"
" version_mci = 0x%08x\n"
" version_so = 0x%08x\n"
" version_mclf = 0x%08x\n"
" version_container = 0x%08x\n"
" version_mc_config = 0x%08x\n"
" version_tl_api = 0x%08x\n"
" version_dr_api = 0x%08x\n"
" version_nwd = 0x%08x\n",
version_info.product_id,
version_info.version_mci,
version_info.version_so,
version_info.version_mclf,
version_info.version_container,
version_info.version_mc_config,
version_info.version_tl_api,
version_info.version_dr_api,
version_info.version_nwd);
/* Determine which features are supported */
if (version_info.version_mci != MC_VERSION(1, 9)) {
ret = -EHOSTDOWN;
mc_dev_err(ret, "TEE incompatible with this driver");
return ret;
}
/* Verify NWd version number */
if (version_info.version_nwd != MC_VERSION(
MCDRVMODULEAPI_VERSION_MAJOR, MCDRVMODULEAPI_VERSION_MINOR)) {
ret = -EHOSTDOWN;
mc_dev_err(ret, "Bad NWd version ");
return ret;
}
return 0;
}
static int mobicore_start_fe(void)
{
mutex_lock(&main_ctx.start_mutex);
if (main_ctx.start_ret != TEE_START_NOT_TRIGGERED)
goto end;
/* Must be called before creating the user device node to avoid race */
main_ctx.start_ret = check_version();
if (main_ctx.start_ret)
goto end;
main_ctx.start_ret = device_user_init();
end:
mutex_unlock(&main_ctx.start_mutex);
return main_ctx.start_ret;
}
static int mobicore_start(void)
{
int ret;
mutex_lock(&main_ctx.start_mutex);
if (main_ctx.start_ret != TEE_START_NOT_TRIGGERED)
goto got_ret;
ret = nq_start();
if (ret) {
mc_dev_err(ret, "NQ start failed");
goto err_nq;
}
ret = mcp_start();
if (ret) {
mc_dev_err(ret, "MCP start failed");
goto err_mcp;
}
ret = iwp_start();
if (ret) {
mc_dev_err(ret, "IWP start failed");
goto err_iwp;
}
/* Must be called before creating the user device node to avoid race */
ret = check_version();
if (ret)
goto err_version;
#ifdef MC_PM_RUNTIME
main_ctx.reboot_notifier.notifier_call = reboot_notifier;
ret = register_reboot_notifier(&main_ctx.reboot_notifier);
if (ret) {
mc_dev_err(ret, "reboot notifier registration failed");
goto err_pm_notif;
}
main_ctx.pm_notifier.notifier_call = suspend_notifier;
ret = register_pm_notifier(&main_ctx.pm_notifier);
if (ret) {
unregister_reboot_notifier(&main_ctx.reboot_notifier);
mc_dev_err(ret, "PM notifier register failed");
goto err_pm_notif;
}
#endif
#ifdef MC_TEE_HOTPLUG
#if KERNEL_VERSION(4, 6, 0) <= LINUX_VERSION_CODE
ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "Trustonic",
nq_cpu_on, nq_cpu_off);
#endif
#endif
ret = protocol_start();
if (ret) {
mc_dev_err(ret, "protocol start failed");
goto err_protocol;
}
ret = device_user_init();
if (ret)
goto err_device_user;
main_ctx.start_ret = 0;
goto got_ret;
err_device_user:
protocol_stop();
err_protocol:
#ifdef MC_PM_RUNTIME
unregister_reboot_notifier(&main_ctx.reboot_notifier);
unregister_pm_notifier(&main_ctx.pm_notifier);
err_pm_notif:
#endif
err_version:
iwp_stop();
err_iwp:
mcp_stop();
err_mcp:
nq_stop();
err_nq:
main_ctx.start_ret = ret;
got_ret:
mutex_unlock(&main_ctx.start_mutex);
return main_ctx.start_ret;
}
static void mobicore_stop(void)
{
device_user_exit();
protocol_stop();
if (!protocol_is_fe()) {
#ifdef MC_PM_RUNTIME
unregister_reboot_notifier(&main_ctx.reboot_notifier);
unregister_pm_notifier(&main_ctx.pm_notifier);
#endif
iwp_stop();
mcp_stop();
nq_stop();
}
}
int mc_wait_tee_start(void)
{
int ret;
mutex_lock(&main_ctx.start_mutex);
while (main_ctx.start_ret == TEE_START_NOT_TRIGGERED) {
mutex_unlock(&main_ctx.start_mutex);
ssleep(1);
mutex_lock(&main_ctx.start_mutex);
}
ret = main_ctx.start_ret;
mutex_unlock(&main_ctx.start_mutex);
return ret;
}
static inline int device_common_init(void)
{
int ret;
ret = alloc_chrdev_region(&main_ctx.device, 0, 2, "trustonic_tee");
if (ret) {
mc_dev_err(ret, "alloc_chrdev_region failed");
return ret;
}
main_ctx.class = class_create(THIS_MODULE, "trustonic_tee");
if (IS_ERR(main_ctx.class)) {
ret = PTR_ERR(main_ctx.class);
mc_dev_err(ret, "class_create failed");
unregister_chrdev_region(main_ctx.device, 2);
return ret;
}
return 0;
}
static inline void device_common_exit(void)
{
class_destroy(main_ctx.class);
unregister_chrdev_region(main_ctx.device, 2);
}
static inline int device_admin_init(void)
{
struct device *dev;
int ret = 0;
/* Create the ADMIN node */
ret = mc_admin_init(&main_ctx.admin_cdev, mobicore_start,
mobicore_stop);
if (ret)
goto err_init;
ret = cdev_add(&main_ctx.admin_cdev, main_ctx.device, 1);
if (ret) {
mc_dev_err(ret, "admin cdev_add failed");
goto err_cdev;
}
main_ctx.admin_cdev.owner = THIS_MODULE;
dev = device_create(main_ctx.class, NULL, main_ctx.device, NULL,
MC_ADMIN_DEVNODE);
if (IS_ERR(dev)) {
ret = PTR_ERR(dev);
mc_dev_err(ret, "admin device_create failed");
goto err_device;
}
return 0;
err_device:
cdev_del(&main_ctx.admin_cdev);
err_cdev:
mc_admin_exit();
err_init:
return ret;
}
static inline void device_admin_exit(void)
{
device_destroy(main_ctx.class, main_ctx.device);
cdev_del(&main_ctx.admin_cdev);
mc_admin_exit();
}
static int mobicore_probe(struct platform_device *pdev)
{
int ret = 0;
if (pdev)
g_ctx.mcd->of_node = pdev->dev.of_node;
#ifdef MOBICORE_COMPONENT_BUILD_TAG
mc_dev_info("MobiCore %s", MOBICORE_COMPONENT_BUILD_TAG);
#endif
ret = of_property_read_u32(g_ctx.mcd->of_node,
"trustonic,real-drv", &g_ctx.real_drv);
if (ret || !g_ctx.real_drv) {
mc_dev_info("MobiCore dummy driver");
return 0;
}
/* Hardware does not support ARM TrustZone -> Cannot continue! */
if (!protocol_is_fe() && !has_security_extensions()) {
ret = -ENODEV;
mc_dev_err(ret, "Hardware doesn't support ARM TrustZone!");
return ret;
}
/* Running in secure mode -> Cannot load the driver! */
if (is_secure_mode()) {
ret = -ENODEV;
mc_dev_err(ret, "Running in secure MODE!");
return ret;
}
/* Make sure we can create debugfs entries */
g_ctx.debug_dir = debugfs_create_dir("trustonic_tee", NULL);
/* Initialize debug counters */
atomic_set(&g_ctx.c_clients, 0);
atomic_set(&g_ctx.c_cwsms, 0);
atomic_set(&g_ctx.c_sessions, 0);
atomic_set(&g_ctx.c_wsms, 0);
atomic_set(&g_ctx.c_mmus, 0);
atomic_set(&g_ctx.c_maps, 0);
atomic_set(&g_ctx.c_slots, 0);
atomic_set(&g_ctx.c_vm_fes, 0);
atomic_set(&g_ctx.c_vm_maps, 0);
main_ctx.start_ret = TEE_START_NOT_TRIGGERED;
mutex_init(&main_ctx.start_mutex);
mutex_init(&main_ctx.struct_counters_buf_mutex);
/* Create debugfs info entries */
debugfs_create_file("structs_counters", 0400, g_ctx.debug_dir, NULL,
&debug_struct_counters_ops);
/* Initialize common API layer */
client_init();
/* Initialize plenty of nice features */
ret = nq_init();
if (ret) {
mc_dev_err(ret, "NQ init failed");
goto fail_nq_init;
}
ret = mcp_init();
if (ret) {
mc_dev_err(ret, "MCP init failed");
goto err_mcp;
}
ret = iwp_init();
if (ret) {
mc_dev_err(ret, "IWP init failed");
goto err_iwp;
}
ret = device_common_init();
if (ret)
goto err_common;
if (!protocol_is_fe()) {
/* Admin dev is for the daemon to communicate with the driver */
ret = device_admin_init();
if (ret)
goto err_admin;
#ifndef MC_DELAYED_TEE_START
ret = mobicore_start();
if (ret)
goto err_start;
#endif
}
ret = protocol_init();
if (ret) {
mc_dev_err(ret, "protocol init failed");
goto err_protocol;
}
return 0;
err_protocol:
#ifndef MC_DELAYED_TEE_START
err_start:
#endif
if (!protocol_is_fe())
device_admin_exit();
err_admin:
device_common_exit();
err_common:
iwp_exit();
err_iwp:
mcp_exit();
err_mcp:
nq_exit();
fail_nq_init:
debugfs_remove_recursive(g_ctx.debug_dir);
return ret;
}
static int mobicore_probe_not_of(void)
{
return mobicore_probe(NULL);
}
static const struct of_device_id of_match_table[] = {
{ .compatible = MC_DEVICE_PROPNAME },
{ }
};
static struct platform_driver mc_plat_driver = {
.probe = mobicore_probe,
.driver = {
.name = "mcd",
.owner = THIS_MODULE,
.of_match_table = of_match_table,
}
};
static int __init mobicore_init(void)
{
int ret;
dev_set_name(g_ctx.mcd, "TEE");
/*
* Do not remove or change the following trace.
* The string "MobiCore" is used to detect if the TEE is in of the image
*/
mc_dev_info("MobiCore mcDrvModuleApi version is %d.%d",
MCDRVMODULEAPI_VERSION_MAJOR,
MCDRVMODULEAPI_VERSION_MINOR);
/* In a Xen DomU, just register the front-end */
ret = protocol_early_init(mobicore_probe_not_of, mobicore_start_fe);
if (ret) {
if (ret == 1) {
mc_dev_devel("delayed init reported");
return 0;
}
return ret;
}
main_ctx.use_platform_driver =
of_find_compatible_node(NULL, NULL, MC_DEVICE_PROPNAME);
if (main_ctx.use_platform_driver)
return platform_driver_register(&mc_plat_driver);
return mobicore_probe_not_of();
}
static void __exit mobicore_exit(void)
{
if (main_ctx.use_platform_driver)
platform_driver_unregister(&mc_plat_driver);
if (!protocol_is_fe())
device_admin_exit();
device_common_exit();
protocol_exit();
iwp_exit();
mcp_exit();
nq_exit();
debugfs_remove_recursive(g_ctx.debug_dir);
}
module_init(mobicore_init);
module_exit(mobicore_exit);
MODULE_AUTHOR("Trustonic Limited");
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("MobiCore driver");