// SPDX-License-Identifier: GPL-2.0-only/* * Copyright (c) 2015-2021, Linaro Limited * Copyright (c) 2016, EPAM Systems */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include <linux/crash_dump.h>#include <linux/errno.h>#include <linux/io.h>#include <linux/module.h>#include <linux/rpmb.h>#include <linux/slab.h>#include <linux/string.h>#include <linux/tee_core.h>#include <linux/types.h>#include "optee_private.h" struct blocking_notifier_head optee_rpmb_intf_added = BLOCKING_NOTIFIER_INIT(optee_rpmb_intf_added); static int rpmb_add_dev(struct device *dev){ blocking_notifier_call_chain(&optee_rpmb_intf_added, 0, to_rpmb_dev(dev)); return 0;} static struct class_interface rpmb_class_intf = { .add_dev = rpmb_add_dev,}; void optee_bus_scan_rpmb(struct work_struct *work){ struct optee *optee = container_of(work, struct optee, rpmb_scan_bus_work); int ret; if (!optee->rpmb_scan_bus_done) { ret = optee_enumerate_devices(PTA_CMD_GET_DEVICES_RPMB); optee->rpmb_scan_bus_done = !ret; if (ret && ret != -ENODEV) pr_info("Scanning for RPMB device: ret %d\n", ret); }} int optee_rpmb_intf_rdev(struct notifier_block *intf, unsigned long action, void *data){ struct optee *optee = container_of(intf, struct optee, rpmb_intf); schedule_work(&optee->rpmb_scan_bus_work); return 0;} int optee_set_dma_mask(struct optee *optee, u_int pa_width){ u64 mask = DMA_BIT_MASK(min(64, pa_width)); return dma_coerce_mask_and_coherent(&optee->teedev->dev, mask);} int optee_get_revision(struct tee_device *teedev, char *buf, size_t len){ struct optee *optee = tee_get_drvdata(teedev); u64 build_id; if (!optee) return -ENODEV; if (!buf || !len) return -EINVAL; build_id = optee->revision.os_build_id; if (build_id) scnprintf(buf, len, "%u.%u (%016llx)", optee->revision.os_major, optee->revision.os_minor, (unsigned long long)build_id); else scnprintf(buf, len, "%u.%u", optee->revision.os_major, optee->revision.os_minor); return 0;} static void optee_bus_scan(struct work_struct *work){ WARN_ON(optee_enumerate_devices(PTA_CMD_GET_DEVICES_SUPP));} static ssize_t rpmb_routing_model_show(struct device *dev, struct device_attribute *attr, char *buf){ struct optee *optee = dev_get_drvdata(dev); const char *s; if (optee->in_kernel_rpmb_routing) s = "kernel"; else s = "user"; return sysfs_emit(buf, "%s\n", s);}static DEVICE_ATTR_RO(rpmb_routing_model); static struct attribute *optee_dev_attrs[] = { &dev_attr_rpmb_routing_model.attr, NULL}; ATTRIBUTE_GROUPS(optee_dev); void optee_set_dev_group(struct optee *optee){ tee_device_set_dev_groups(optee->teedev, optee_dev_groups); tee_device_set_dev_groups(optee->supp_teedev, optee_dev_groups);} int optee_open(struct tee_context *ctx, bool cap_memref_null){ struct optee_context_data *ctxdata; struct tee_device *teedev = ctx->teedev; struct optee *optee = tee_get_drvdata(teedev); ctxdata = kzalloc_obj(*ctxdata); if (!ctxdata) return -ENOMEM; if (teedev == optee->supp_teedev) { bool busy = true; mutex_lock(&optee->supp.mutex); if (!optee->supp.ctx) { busy = false; optee->supp.ctx = ctx; } mutex_unlock(&optee->supp.mutex); if (busy) { kfree(ctxdata); return -EBUSY; } if (!optee->scan_bus_done) { INIT_WORK(&optee->scan_bus_work, optee_bus_scan); schedule_work(&optee->scan_bus_work); optee->scan_bus_done = true; } } mutex_init(&ctxdata->mutex); INIT_LIST_HEAD(&ctxdata->sess_list); ctx->cap_memref_null = cap_memref_null; ctx->data = ctxdata; return 0;} static void optee_release_helper(struct tee_context *ctx, int (*close_session)(struct tee_context *ctx, u32 session, bool system_thread)){ struct optee_context_data *ctxdata = ctx->data; struct optee_session *sess; struct optee_session *sess_tmp; if (!ctxdata) return; list_for_each_entry_safe(sess, sess_tmp, &ctxdata->sess_list, list_node) { list_del(&sess->list_node); close_session(ctx, sess->session_id, sess->use_sys_thread); kfree(sess); } kfree(ctxdata); ctx->data = NULL;} void optee_release(struct tee_context *ctx){ optee_release_helper(ctx, optee_close_session_helper);} void optee_release_supp(struct tee_context *ctx){ struct optee *optee = tee_get_drvdata(ctx->teedev); optee_release_helper(ctx, optee_close_session_helper); optee_supp_release(&optee->supp);} void optee_remove_common(struct optee *optee){ blocking_notifier_chain_unregister(&optee_rpmb_intf_added, &optee->rpmb_intf); cancel_work_sync(&optee->rpmb_scan_bus_work); /* Unregister OP-TEE specific client devices on TEE bus */ optee_unregister_devices(); optee_notif_uninit(optee); optee_shm_arg_cache_uninit(optee); teedev_close_context(optee->ctx); /* * The two devices have to be unregistered before we can free the * other resources. */ tee_device_unregister(optee->supp_teedev); tee_device_unregister(optee->teedev); tee_shm_pool_free(optee->pool); optee_supp_uninit(&optee->supp); mutex_destroy(&optee->call_queue.mutex); rpmb_dev_put(optee->rpmb_dev); mutex_destroy(&optee->rpmb_dev_mutex);} static int smc_abi_rc;static int ffa_abi_rc;static bool intf_is_regged; static int __init optee_core_init(void){ int rc; /* * The kernel may have crashed at the same time that all available * secure world threads were suspended and we cannot reschedule the * suspended threads without access to the crashed kernel's wait_queue. * Therefore, we cannot reliably initialize the OP-TEE driver in the * kdump kernel. */ if (is_kdump_kernel()) return -ENODEV; if (IS_REACHABLE(CONFIG_RPMB)) { rc = rpmb_interface_register(&rpmb_class_intf); if (rc) return rc; intf_is_regged = true; } smc_abi_rc = optee_smc_abi_register(); ffa_abi_rc = optee_ffa_abi_register(); /* If both failed there's no point with this module */ if (smc_abi_rc && ffa_abi_rc) { if (IS_REACHABLE(CONFIG_RPMB)) { rpmb_interface_unregister(&rpmb_class_intf); intf_is_regged = false; } return smc_abi_rc; } return 0;}module_init(optee_core_init); static void __exit optee_core_exit(void){ if (IS_REACHABLE(CONFIG_RPMB) && intf_is_regged) { rpmb_interface_unregister(&rpmb_class_intf); intf_is_regged = false; } if (!smc_abi_rc) optee_smc_abi_unregister(); if (!ffa_abi_rc) optee_ffa_abi_unregister();}module_exit(optee_core_exit); MODULE_AUTHOR("Linaro");MODULE_DESCRIPTION("OP-TEE driver");MODULE_VERSION("1.0");MODULE_LICENSE("GPL v2");MODULE_ALIAS("platform:optee");
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2015-2021, Linaro Limited
* Copyright (c) 2016, EPAM Systems
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/crash_dump.h>
#include <linux/errno.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/rpmb.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/tee_core.h>
#include <linux/types.h>
#include "optee_private.h"
struct blocking_notifier_head optee_rpmb_intf_added =
BLOCKING_NOTIFIER_INIT(optee_rpmb_intf_added);
static int rpmb_add_dev(struct device *dev)
{
blocking_notifier_call_chain(&optee_rpmb_intf_added, 0,
to_rpmb_dev(dev));
return 0;
}
static struct class_interface rpmb_class_intf = {
.add_dev = rpmb_add_dev,
};
void optee_bus_scan_rpmb(struct work_struct *work)
{
struct optee *optee = container_of(work, struct optee,
rpmb_scan_bus_work);
int ret;
if (!optee->rpmb_scan_bus_done) {
ret = optee_enumerate_devices(PTA_CMD_GET_DEVICES_RPMB);
optee->rpmb_scan_bus_done = !ret;
if (ret && ret != -ENODEV)
pr_info("Scanning for RPMB device: ret %d\n", ret);
}
}
int optee_rpmb_intf_rdev(struct notifier_block *intf, unsigned long action,
void *data)
{
struct optee *optee = container_of(intf, struct optee, rpmb_intf);
schedule_work(&optee->rpmb_scan_bus_work);
return 0;
}
int optee_set_dma_mask(struct optee *optee, u_int pa_width)
{
u64 mask = DMA_BIT_MASK(min(64, pa_width));
return dma_coerce_mask_and_coherent(&optee->teedev->dev, mask);
}
int optee_get_revision(struct tee_device *teedev, char *buf, size_t len)
{
struct optee *optee = tee_get_drvdata(teedev);
u64 build_id;
if (!optee)
return -ENODEV;
if (!buf || !len)
return -EINVAL;
build_id = optee->revision.os_build_id;
if (build_id)
scnprintf(buf, len, "%u.%u (%016llx)",
optee->revision.os_major,
optee->revision.os_minor,
(unsigned long long)build_id);
else
scnprintf(buf, len, "%u.%u", optee->revision.os_major,
optee->revision.os_minor);
return 0;
}
static void optee_bus_scan(struct work_struct *work)
{
WARN_ON(optee_enumerate_devices(PTA_CMD_GET_DEVICES_SUPP));
}
static ssize_t rpmb_routing_model_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct optee *optee = dev_get_drvdata(dev);
const char *s;
if (optee->in_kernel_rpmb_routing)
s = "kernel";
else
s = "user";
return sysfs_emit(buf, "%s\n", s);
}
static DEVICE_ATTR_RO(rpmb_routing_model);
static struct attribute *optee_dev_attrs[] = {
&dev_attr_rpmb_routing_model.attr,
NULL
};
ATTRIBUTE_GROUPS(optee_dev);
void optee_set_dev_group(struct optee *optee)
{
tee_device_set_dev_groups(optee->teedev, optee_dev_groups);
tee_device_set_dev_groups(optee->supp_teedev, optee_dev_groups);
}
int optee_open(struct tee_context *ctx, bool cap_memref_null)
{
struct optee_context_data *ctxdata;
struct tee_device *teedev = ctx->teedev;
struct optee *optee = tee_get_drvdata(teedev);
ctxdata = kzalloc_obj(*ctxdata);
if (!ctxdata)
return -ENOMEM;
if (teedev == optee->supp_teedev) {
bool busy = true;
mutex_lock(&optee->supp.mutex);
if (!optee->supp.ctx) {
busy = false;
optee->supp.ctx = ctx;
}
mutex_unlock(&optee->supp.mutex);
if (busy) {
kfree(ctxdata);
return -EBUSY;
}
if (!optee->scan_bus_done) {
INIT_WORK(&optee->scan_bus_work, optee_bus_scan);
schedule_work(&optee->scan_bus_work);
optee->scan_bus_done = true;
}
}
mutex_init(&ctxdata->mutex);
INIT_LIST_HEAD(&ctxdata->sess_list);
ctx->cap_memref_null = cap_memref_null;
ctx->data = ctxdata;
return 0;
}
static void optee_release_helper(struct tee_context *ctx,
int (*close_session)(struct tee_context *ctx,
u32 session,
bool system_thread))
{
struct optee_context_data *ctxdata = ctx->data;
struct optee_session *sess;
struct optee_session *sess_tmp;
if (!ctxdata)
return;
list_for_each_entry_safe(sess, sess_tmp, &ctxdata->sess_list,
list_node) {
list_del(&sess->list_node);
close_session(ctx, sess->session_id, sess->use_sys_thread);
kfree(sess);
}
kfree(ctxdata);
ctx->data = NULL;
}
void optee_release(struct tee_context *ctx)
{
optee_release_helper(ctx, optee_close_session_helper);
}
void optee_release_supp(struct tee_context *ctx)
{
struct optee *optee = tee_get_drvdata(ctx->teedev);
optee_release_helper(ctx, optee_close_session_helper);
optee_supp_release(&optee->supp);
}
void optee_remove_common(struct optee *optee)
{
blocking_notifier_chain_unregister(&optee_rpmb_intf_added,
&optee->rpmb_intf);
cancel_work_sync(&optee->rpmb_scan_bus_work);
/* Unregister OP-TEE specific client devices on TEE bus */
optee_unregister_devices();
optee_notif_uninit(optee);
optee_shm_arg_cache_uninit(optee);
teedev_close_context(optee->ctx);
/*
* The two devices have to be unregistered before we can free the
* other resources.
*/
tee_device_unregister(optee->supp_teedev);
tee_device_unregister(optee->teedev);
tee_shm_pool_free(optee->pool);
optee_supp_uninit(&optee->supp);
mutex_destroy(&optee->call_queue.mutex);
rpmb_dev_put(optee->rpmb_dev);
mutex_destroy(&optee->rpmb_dev_mutex);
}
static int smc_abi_rc;
static int ffa_abi_rc;
static bool intf_is_regged;
static int __init optee_core_init(void)
{
int rc;
/*
* The kernel may have crashed at the same time that all available
* secure world threads were suspended and we cannot reschedule the
* suspended threads without access to the crashed kernel's wait_queue.
* Therefore, we cannot reliably initialize the OP-TEE driver in the
* kdump kernel.
*/
if (is_kdump_kernel())
return -ENODEV;
if (IS_REACHABLE(CONFIG_RPMB)) {
rc = rpmb_interface_register(&rpmb_class_intf);
if (rc)
return rc;
intf_is_regged = true;
}
smc_abi_rc = optee_smc_abi_register();
ffa_abi_rc = optee_ffa_abi_register();
/* If both failed there's no point with this module */
if (smc_abi_rc && ffa_abi_rc) {
if (IS_REACHABLE(CONFIG_RPMB)) {
rpmb_interface_unregister(&rpmb_class_intf);
intf_is_regged = false;
}
return smc_abi_rc;
}
return 0;
}
module_init(optee_core_init);
static void __exit optee_core_exit(void)
{
if (IS_REACHABLE(CONFIG_RPMB) && intf_is_regged) {
rpmb_interface_unregister(&rpmb_class_intf);
intf_is_regged = false;
}
if (!smc_abi_rc)
optee_smc_abi_unregister();
if (!ffa_abi_rc)
optee_ffa_abi_unregister();
}
module_exit(optee_core_exit);
MODULE_AUTHOR("Linaro");
MODULE_DESCRIPTION("OP-TEE driver");
MODULE_VERSION("1.0");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:optee");