kernel_samsung_a34x-permissive/drivers/misc/mediatek/iommu/m4u_secure.c
2024-04-28 15:51:13 +02:00

645 lines
13 KiB
C

// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2019 MediaTek Inc.
*/
#define pr_fmt(fmt) "[M4U] " fmt
#include <linux/notifier.h>
#include <linux/fb.h>
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/of_platform.h>
#include <linux/proc_fs.h>
#include <linux/slab.h>
#include <linux/debugfs.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/interrupt.h>
#include <linux/arm-smccc.h>
#include <linux/soc/mediatek/mtk_sip_svc.h>
#include <linux/pm_runtime.h>
#include "m4u_secure.h"
#if defined(CONFIG_TRUSTONIC_TEE_SUPPORT)
#include "mobicore_driver_api.h"
#endif
static struct m4u_device *m4u_dev;
static unsigned long m4u_mm_base;
static unsigned int m4u_dev_irq[SECURE_BANK_NUM];
int M4U_L2_ENABLE = 1;
static unsigned long mtk_m4u_get_pt(void);
#ifdef M4U_TEE_SERVICE_ENABLE
static DEFINE_MUTEX(gM4u_sec_init);
bool m4u_tee_en;
static struct m4u_sec_gp_context m4u_gp_ta_ctx = {
.uuid = (struct TEEC_UUID)M4U_TA_UUID,
.ctx_lock = __MUTEX_INITIALIZER(m4u_gp_ta_ctx.ctx_lock),
.ctx_type = CTX_TYPE_TA,
};
struct m4u_sec_context m4u_ta_ctx;
void m4u_sec_set_context(void)
{
m4u_ta_ctx.name = "m4u_ta";
m4u_ta_ctx.imp = &m4u_gp_ta_ctx;
}
static int m4u_exec_session(struct m4u_sec_context *ctx)
{
int ret;
struct TEEC_Operation m4u_operation;
struct m4u_sec_gp_context *gp_ctx = ctx->imp;
if (!ctx->m4u_msg) {
pr_err("%s TCI/DCI error\n", __func__);
return -1;
}
pr_info("%s, Notify 0x%x\n", __func__, ctx->m4u_msg->cmd);
memset(&m4u_operation, 0, sizeof(struct TEEC_Operation));
m4u_operation.paramTypes = TEEC_PARAM_TYPES(
TEEC_MEMREF_PARTIAL_INPUT, TEEC_NONE, TEEC_NONE, TEEC_NONE);
m4u_operation.params[0].memref.parent = &gp_ctx->shared_mem;
m4u_operation.params[0].memref.offset = 0;
m4u_operation.params[0].memref.size = gp_ctx->shared_mem.size;
ret = TEEC_InvokeCommand(&gp_ctx->session,
ctx->m4u_msg->cmd, &m4u_operation, NULL);
if (ret != TEEC_SUCCESS) {
pr_err("tz_m4u Notify failed: %d\n", ret);
goto exit;
}
pr_info("%s, get_resp %x\n", __func__, ctx->m4u_msg->cmd);
exit:
return ret;
}
static int m4u_sec_gp_init(struct m4u_sec_context *ctx)
{
int ret;
struct m4u_sec_gp_context *gp_ctx = ctx->imp;
ret = TEEC_InitializeContext(TA_UUID, &gp_ctx->ctx);
if (ret != TEEC_SUCCESS) {
pr_err("teec_initialize_context failed: %x\n", ret);
return ret;
}
pr_info("%s, ta teec_initialize_context\n", __func__);
memset(&gp_ctx->shared_mem, 0, sizeof(struct TEEC_SharedMemory));
gp_ctx->shared_mem.size = sizeof(struct m4u_msg);
gp_ctx->shared_mem.flags = TEEC_MEM_INPUT;
ret = TEEC_AllocateSharedMemory(&gp_ctx->ctx, &gp_ctx->shared_mem);
if (ret == TEEC_SUCCESS) {
ctx->m4u_msg = (struct m4u_msg *)gp_ctx->shared_mem.buffer;
pr_info("teec_allocate_shared_memory buf: 0x%p\n",
gp_ctx->shared_mem.buffer);
} else {
pr_err("teec_allocate_shared_memory failed: %d\n", ret);
goto exit_finalize;
}
if (!ctx->m4u_msg) {
pr_err("m4u msg is invalid\n");
return -1;
}
if (!gp_ctx->init) {
ret = TEEC_OpenSession(&gp_ctx->ctx, &gp_ctx->session,
&gp_ctx->uuid, TEEC_LOGIN_PUBLIC, NULL, NULL, NULL);
if (ret != TEEC_SUCCESS) {
pr_err("teec_open_session failed: %x\n", ret);
goto exit_release;
}
gp_ctx->init = 1;
}
pr_info("%s, open TCI session success\n", __func__);
return ret;
exit_release:
TEEC_ReleaseSharedMemory(&gp_ctx->shared_mem);
exit_finalize:
TEEC_FinalizeContext(&gp_ctx->ctx);
return ret;
}
static int m4u_sec_gp_deinit(struct m4u_sec_context *ctx)
{
struct m4u_sec_gp_context *gp_ctx = ctx->imp;
TEEC_ReleaseSharedMemory(&gp_ctx->shared_mem);
TEEC_CloseSession(&gp_ctx->session);
TEEC_FinalizeContext(&gp_ctx->ctx);
gp_ctx->init = 0;
return 0;
}
static int m4u_sec_ta_open(void)
{
return m4u_sec_gp_init(&m4u_ta_ctx);
}
static int m4u_sec_ta_close(void)
{
return m4u_sec_gp_deinit(&m4u_ta_ctx);
}
int m4u_sec_context_init(void)
{
return m4u_sec_ta_open();
}
int m4u_sec_context_deinit(void)
{
return m4u_sec_ta_close();
}
struct m4u_sec_context *m4u_sec_ctx_get(unsigned int cmd)
{
struct m4u_sec_context *ctx = NULL;
struct m4u_sec_gp_context *gp_ctx;
ctx = &m4u_ta_ctx;
gp_ctx = ctx->imp;
if (!gp_ctx->init) {
pr_err("%s: before init\n", __func__);
return NULL;
}
mutex_lock(&gp_ctx->ctx_lock);
return ctx;
}
int m4u_sec_ctx_put(struct m4u_sec_context *ctx)
{
struct m4u_sec_gp_context *gp_ctx = ctx->imp;
mutex_unlock(&gp_ctx->ctx_lock);
return 0;
}
int m4u_exec_cmd(struct m4u_sec_context *ctx)
{
int ret;
if (ctx->m4u_msg == NULL) {
pr_err("%s TCI/DCI error\n", __func__);
return -1;
}
ret = m4u_exec_session(ctx);
if (ret < 0)
return -1;
return 0;
}
static int __m4u_sec_init(void)
{
int ret;
struct m4u_sec_context *ctx;
unsigned long pt_pa_nonsec = mtk_m4u_get_pt();
ctx = m4u_sec_ctx_get(CMD_M4UTL_INIT);
if (!ctx)
return -EFAULT;
pm_runtime_get_sync(m4u_dev->dev);
ctx->m4u_msg->cmd = CMD_M4UTL_INIT;
ctx->m4u_msg->init_param.nonsec_pt_pa = pt_pa_nonsec;
ctx->m4u_msg->init_param.l2_en = M4U_L2_ENABLE;
ctx->m4u_msg->init_param.sec_pt_pa = 0;
pr_info("%s call CMD_M4UTL_INIT, nonsec_pt_pa: 0x%lx\n",
__func__, pt_pa_nonsec);
ret = m4u_exec_cmd(ctx);
if (ret) {
pr_err("m4u exec command fail\n");
goto out;
}
ret = ctx->m4u_msg->rsp;
out:
pm_runtime_put_sync(m4u_dev->dev);
m4u_sec_ctx_put(ctx);
return ret;
}
int m4u_sec_init(void)
{
int ret;
pr_info("%s: start\n", __func__);
if (m4u_tee_en) {
pr_info("warning: re-initiation, %d\n", m4u_tee_en);
goto m4u_sec_reinit;
}
m4u_sec_set_context();
if (!m4u_tee_en) {
ret = m4u_sec_context_init();
if (ret)
return ret;
m4u_tee_en = 1;
} else {
pr_warn("[M4U] warning: reinit sec m4u en=%d\n", m4u_tee_en);
}
m4u_sec_reinit:
ret = __m4u_sec_init();
if (ret < 0) {
m4u_tee_en = 0;
m4u_sec_context_deinit();
pr_err("%s:init fail,ret=0x%x\n", __func__, ret);
return ret;
}
/* don't deinit ta because of multiple init operation */
pr_info("%s:normal init done\n", __func__);
return 0;
}
/*#ifdef TO_BE_IMPL*/
int m4u_larb_backup_sec(unsigned int larb_idx)
{
int ret;
struct m4u_sec_context *ctx;
ctx = m4u_sec_ctx_get(CMD_M4U_LARB_BACKUP);
if (!ctx)
return -EFAULT;
ctx->m4u_msg->cmd = CMD_M4U_LARB_BACKUP;
ctx->m4u_msg->larb_param.larb_idx = larb_idx;
ret = m4u_exec_cmd(ctx);
if (ret) {
pr_err("m4u exec command fail\n");
ret = -1;
goto out;
}
ret = ctx->m4u_msg->rsp;
out:
m4u_sec_ctx_put(ctx);
return ret;
}
int m4u_larb_restore_sec(unsigned int larb_idx)
{
int ret;
struct m4u_sec_context *ctx;
ctx = m4u_sec_ctx_get(CMD_M4U_LARB_RESTORE);
if (!ctx)
return -EFAULT;
ctx->m4u_msg->cmd = CMD_M4U_LARB_RESTORE;
ctx->m4u_msg->larb_param.larb_idx = larb_idx;
ret = m4u_exec_cmd(ctx);
if (ret) {
pr_err("m4u exec command fail\n");
ret = -1;
goto out;
}
ret = ctx->m4u_msg->rsp;
out:
m4u_sec_ctx_put(ctx);
return ret;
}
static int m4u_reg_backup_sec(void)
{
int ret;
struct m4u_sec_context *ctx;
ctx = m4u_sec_ctx_get(CMD_M4U_REG_BACKUP);
if (!ctx)
return -EFAULT;
ctx->m4u_msg->cmd = CMD_M4U_REG_BACKUP;
ret = m4u_exec_cmd(ctx);
if (ret) {
pr_err("m4u exec command fail\n");
ret = -1;
goto out;
}
ret = ctx->m4u_msg->rsp;
out:
m4u_sec_ctx_put(ctx);
return ret;
}
static int m4u_reg_restore_sec(void)
{
int ret;
struct m4u_sec_context *ctx;
ctx = m4u_sec_ctx_get(CMD_M4U_REG_RESTORE);
if (!ctx)
return -EFAULT;
ctx->m4u_msg->cmd = CMD_M4U_REG_RESTORE;
ret = m4u_exec_cmd(ctx);
if (ret) {
pr_err("m4u exec command fail\n");
ret = -1;
goto out;
}
ret = ctx->m4u_msg->rsp;
out:
m4u_sec_ctx_put(ctx);
return ret;
}
static void m4u_early_suspend(void)
{
pr_info("%s +, %d\n", __func__, m4u_tee_en);
if (m4u_tee_en)
m4u_reg_backup_sec();
pr_info("%s -\n", __func__);
}
static void m4u_late_resume(void)
{
pr_info("%s +, %d\n", __func__, m4u_tee_en);
if (m4u_tee_en)
m4u_reg_restore_sec();
pr_info("%s -\n", __func__);
}
static struct notifier_block m4u_fb_notifier;
static int m4u_fb_notifier_callback(
struct notifier_block *self, unsigned long event, void *data)
{
struct fb_event *evdata = data;
int blank;
pr_info("%s %ld, %d\n", __func__, event, FB_EVENT_BLANK);
if (event != FB_EVENT_BLANK)
return 0;
blank = *(int *)evdata->data;
switch (blank) {
case FB_BLANK_UNBLANK:
case FB_BLANK_NORMAL:
m4u_late_resume();
break;
case FB_BLANK_VSYNC_SUSPEND:
case FB_BLANK_HSYNC_SUSPEND:
break;
case FB_BLANK_POWERDOWN:
m4u_early_suspend();
break;
default:
return -EINVAL;
}
return 0;
}
#endif
irqreturn_t mtk_m4u_isr_sec(int irq, void *dev_id)
{
struct arm_smccc_res res;
unsigned long tf_en = 0;
unsigned long tf_port = 0;
unsigned long m4u_id = 0;
if (irq == m4u_dev_irq[0]) {
m4u_id = 0;
pr_info("This is secure MM_IOMMU domian\n");
} else if (irq == m4u_dev_irq[1]) {
m4u_id = 1;
pr_info("This is secure VPU_IOMMU domian\n");
} else {
pr_err("%s(), Invalid secure irq number %d\n", __func__, irq);
return -1;
}
pr_info("secure bank irq in normal world!\n");
arm_smccc_smc(MTK_M4U_DEBUG_DUMP, m4u_id, 0, 0,
0, 0, 0, 0, &res);
tf_en = res.a0;
tf_port = res.a1;
pr_warn("secure bank go back form secure world! en:%zu, fault_id:0x%lx\n",
tf_en, tf_port);
return IRQ_HANDLED;
}
static unsigned long mtk_m4u_get_pt(void)
{
unsigned long reval;
reval = readl_relaxed((void *)m4u_mm_base + REG_MMU_PT_BASE_ADDR);
if (reval & F_PGD_REG_BIT32)
reval |= BIT_ULL(32);
if (reval & F_PGD_REG_BIT33)
reval |= BIT_ULL(33);
pr_info("get pt: 0x%lx\n", reval);
return reval;
}
static int m4u_open(struct inode *inode, struct file *file)
{
pr_info("%s process : %s\n", __func__, current->comm);
return 0;
}
static int m4u_release(struct inode *inode, struct file *file)
{
pr_info("%s process : %s\n", __func__, current->comm);
return 0;
}
static long m4u_ioctl(struct file *filp,
unsigned int cmd, unsigned long arg)
{
int ret = 0;
switch (cmd) {
#ifdef M4U_TEE_SERVICE_ENABLE
case MTK_M4U_T_SEC_INIT:
{
pr_info("MTK M4U ioctl : MTK_M4U_T_SEC_INIT command!! 0x%x\n",
cmd);
mutex_lock(&gM4u_sec_init);
ret = m4u_sec_init();
mutex_unlock(&gM4u_sec_init);
}
break;
#endif
default:
pr_err("MTK M4U ioctl:No such command(0x%x)!!\n", cmd);
ret = -EINVAL;
break;
}
return ret;
}
#if IS_ENABLED(CONFIG_COMPAT)
static long m4u_compat_ioctl(struct file *filp,
unsigned int cmd, unsigned long arg)
{
long ret;
if (!filp->f_op || !filp->f_op->unlocked_ioctl)
return -ENOTTY;
switch (cmd) {
#ifdef M4U_TEE_SERVICE_ENABLE
case COMPAT_MTK_M4U_T_SEC_INIT:
{
pr_info("MTK_M4U_T_SEC_INIT command!! 0x%x\n",
cmd);
mutex_lock(&gM4u_sec_init);
ret = m4u_sec_init();
mutex_unlock(&gM4u_sec_init);
}
break;
#endif
default:
pr_err("compat ioctl:No such command(0x%x)!!\n", cmd);
ret = -ENOIOCTLCMD;
break;
}
return ret;
}
#else
#define m4u_compat_ioctl NULL
#endif
static const struct file_operations m4u_fops = {
.owner = THIS_MODULE,
.open = m4u_open,
.release = m4u_release,
.unlocked_ioctl = m4u_ioctl,
.compat_ioctl = m4u_compat_ioctl,
};
static int m4u_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct device_node *node = pdev->dev.of_node;
int i, ret;
struct device_node *mm_m4unode = NULL;
struct platform_device *plarbdev;
mm_m4unode = of_parse_phandle(dev->of_node, "mediatek,mm_m4u", 0);
if (!mm_m4unode) {
pr_err("not find mm m4u dts\n");
return -EINVAL;
}
plarbdev = of_find_device_by_node(mm_m4unode);
if (!plarbdev || !plarbdev->dev.driver) {
pr_info("mm m4u not ready!\n");
return -EPROBE_DEFER;
}
m4u_dev = kzalloc(sizeof(struct m4u_device), GFP_KERNEL);
if (!m4u_dev)
return -ENOMEM;
m4u_dev->dev = dev;
m4u_dev->m4u_dev_proc_entry = proc_create("m4u", 0644, NULL,
&m4u_fops);
if (!m4u_dev->m4u_dev_proc_entry) {
pr_err("proc m4u create error\n");
kfree(m4u_dev);
return -ENODEV;
}
m4u_mm_base = (unsigned long)of_iomap(mm_m4unode, 0);
dev_info(m4u_dev->dev, "mm m4u base:0x%lx\n", m4u_mm_base);
for (i = MM_SECURE_BANK; i < SECURE_BANK_NUM; i++) {
m4u_dev_irq[i] = irq_of_parse_and_map(node, i);
dev_info(m4u_dev->dev, "secure irq:%u, id:%d\n",
m4u_dev_irq[i], i);
if (request_irq(m4u_dev_irq[i], mtk_m4u_isr_sec,
IRQF_TRIGGER_NONE, "secure_m4u", NULL)) {
pr_err("request secure m4u%d IRQ line failed\n",
i);
return -ENODEV;
}
}
#ifdef M4U_TEE_SERVICE_ENABLE
m4u_fb_notifier.notifier_call = m4u_fb_notifier_callback;
ret = fb_register_client(&m4u_fb_notifier);
if (ret)
dev_err(m4u_dev->dev, "register fb_notifier failed!\n");
else
dev_info(m4u_dev->dev, "register fb_notifier OK!\n");
#endif
dev_info(m4u_dev->dev, "%s done\n", __func__);
return 0;
}
static int m4u_remove(struct platform_device *pdev)
{
if (m4u_dev->m4u_dev_proc_entry)
proc_remove(m4u_dev->m4u_dev_proc_entry);
return 0;
}
static const struct of_device_id mtk_m4u_sec_of_ids[] = {
{ .compatible = "mediatek,secure_m4u",},
{}
};
static struct platform_driver mtk_m4u_sec_driver = {
.probe = m4u_probe,
.remove = m4u_remove,
.driver = {
.name = M4U_DEVNAME,
.of_match_table = of_match_ptr(mtk_m4u_sec_of_ids),
.owner = THIS_MODULE,
}
};
module_platform_driver(mtk_m4u_sec_driver);