| // SPDX-License-Identifier: (GPL-2.0+ OR MIT) |
| /* |
| * drivers/amlogic/tee/tee.c |
| * |
| * Copyright (C) 2017 Amlogic, Inc. 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 as published by |
| * the Free Software Foundation; either version 2 of the License, or |
| * (at your option) any later version. |
| * |
| * 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/kernel.h> |
| #include <linux/module.h> |
| #include <linux/arm-smccc.h> |
| #include <linux/platform_device.h> |
| |
| #include <linux/amlogic/tee.h> |
| #include <asm/cputype.h> |
| |
| #define DRIVER_NAME "tee_info" |
| #define DRIVER_DESC "Amlogic tee driver" |
| |
| #define TEE_MSG_UID_0 0x384fb3e0 |
| #define TEE_MSG_UID_1 0xe7f811e3 |
| #define TEE_MSG_UID_2 0xaf630002 |
| #define TEE_MSG_UID_3 0xa5d5c51b |
| static int disable_flag; |
| #define TEE_SMC_FUNCID_CALLS_REVISION 0xFF03 |
| #define TEE_SMC_CALLS_REVISION \ |
| ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, ARM_SMCCC_SMC_32, \ |
| ARM_SMCCC_OWNER_TRUSTED_OS_END, \ |
| TEE_SMC_FUNCID_CALLS_REVISION) |
| |
| #define TEE_SMC_FUNCID_CALLS_UID 0xFF01 |
| #define TEE_SMC_CALLS_UID \ |
| ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, ARM_SMCCC_SMC_32, \ |
| ARM_SMCCC_OWNER_TRUSTED_OS_END, \ |
| TEE_SMC_FUNCID_CALLS_UID) |
| |
| #define TEE_SMC_FAST_CALL_VAL(func_num) \ |
| ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, ARM_SMCCC_SMC_32, \ |
| ARM_SMCCC_OWNER_TRUSTED_OS, (func_num)) |
| |
| #define TEE_SMC_FUNCID_GET_OS_REVISION 0x0001 |
| #define TEE_SMC_CALL_GET_OS_REVISION \ |
| TEE_SMC_FAST_CALL_VAL(TEE_SMC_FUNCID_GET_OS_REVISION) |
| |
| #define TEE_SMC_FUNCID_CONFIG_DEVICE_SECURE 14 |
| #define TEE_SMC_CONFIG_DEVICE_SECURE \ |
| TEE_SMC_FAST_CALL_VAL(TEE_SMC_FUNCID_CONFIG_DEVICE_SECURE) |
| |
| #define TEE_SMC_FUNCID_LOAD_VIDEO_FW 15 |
| #define TEE_SMC_LOAD_VIDEO_FW \ |
| TEE_SMC_FAST_CALL_VAL(TEE_SMC_FUNCID_LOAD_VIDEO_FW) |
| |
| #define TEE_SMC_FUNCID_PROTECT_TVP_MEM 0xE020 |
| #define TEE_SMC_PROTECT_TVP_MEM \ |
| TEE_SMC_FAST_CALL_VAL(TEE_SMC_FUNCID_PROTECT_TVP_MEM) |
| |
| #define TEE_SMC_FUNCID_UNPROTECT_TVP_MEM 0xE021 |
| #define TEE_SMC_UNPROTECT_TVP_MEM \ |
| TEE_SMC_FAST_CALL_VAL(TEE_SMC_FUNCID_UNPROTECT_TVP_MEM) |
| |
| #define TEE_SMC_FUNCID_PROTECT_MEM_BY_TYPE 0xE023 |
| #define TEE_SMC_PROTECT_MEM_BY_TYPE \ |
| TEE_SMC_FAST_CALL_VAL(TEE_SMC_FUNCID_PROTECT_MEM_BY_TYPE) |
| |
| #define TEE_SMC_FUNCID_UNPROTECT_MEM 0xE024 |
| #define TEE_SMC_UNPROTECT_MEM \ |
| TEE_SMC_FAST_CALL_VAL(TEE_SMC_FUNCID_UNPROTECT_MEM) |
| |
| #define TEE_SMC_FUNCID_CHECK_IN_MEM 0xE025 |
| #define TEE_SMC_CHECK_IN_MEM \ |
| TEE_SMC_FAST_CALL_VAL(TEE_SMC_FUNCID_CHECK_IN_MEM) |
| |
| #define TEE_SMC_FUNCID_CHECK_OUT_MEM 0xE026 |
| #define TEE_SMC_CHECK_OUT_MEM \ |
| TEE_SMC_FAST_CALL_VAL(TEE_SMC_FUNCID_CHECK_OUT_MEM) |
| |
| #define TEE_SMC_FUNCID_DEMUX_CONFIG_PIPELINE 0xE050 |
| #define TEE_SMC_DEMUX_CONFIG_PIPELINE \ |
| TEE_SMC_FAST_CALL_VAL(TEE_SMC_FUNCID_DEMUX_CONFIG_PIPELINE) |
| |
| #define TEE_SMC_FUNCID_DEMUX_CONFIG_PAD 0xE051 |
| #define TEE_SMC_DEMUX_CONFIG_PAD \ |
| TEE_SMC_FAST_CALL_VAL(TEE_SMC_FUNCID_DEMUX_CONFIG_PAD) |
| |
| #define TEE_SMC_FUNCID_READ_REG 0xE052 |
| #define TEE_SMC_READ_REG \ |
| TEE_SMC_FAST_CALL_VAL(TEE_SMC_FUNCID_READ_REG) |
| |
| #define TEE_SMC_FUNCID_WRITE_REG 0xE053 |
| #define TEE_SMC_WRITE_REG \ |
| TEE_SMC_FAST_CALL_VAL(TEE_SMC_FUNCID_WRITE_REG) |
| |
| #define TEE_SMC_FUNCID_SYS_BOOT_COMPLETE 0xE060 |
| #define TEE_SMC_SYS_BOOT_COMPLETE \ |
| TEE_SMC_FAST_CALL_VAL(TEE_SMC_FUNCID_SYS_BOOT_COMPLETE) |
| |
| #define TEE_SMC_FUNCID_VP9_PROB_PROCESS 0xE070 |
| #define TEE_SMC_VP9_PROB_PROCESS \ |
| TEE_SMC_FAST_CALL_VAL(TEE_SMC_FUNCID_VP9_PROB_PROCESS) |
| |
| #define TEE_SMC_FUNCID_VP9_PROB_MALLOC 0xE071 |
| #define TEE_SMC_VP9_PROB_MALLOC \ |
| TEE_SMC_FAST_CALL_VAL(TEE_SMC_FUNCID_VP9_PROB_MALLOC) |
| |
| #define TEE_SMC_FUNCID_VP9_PROB_FREE 0xE072 |
| #define TEE_SMC_VP9_PROB_FREE \ |
| TEE_SMC_FAST_CALL_VAL(TEE_SMC_FUNCID_VP9_PROB_FREE) |
| |
| static struct class *tee_sys_class; |
| |
| struct tee_smc_calls_revision_result { |
| unsigned long major; |
| unsigned long minor; |
| unsigned long reserved0; |
| unsigned long reserved1; |
| }; |
| |
| static int tee_msg_os_revision(u32 *major, u32 *minor) |
| { |
| union { |
| struct arm_smccc_res smccc; |
| struct tee_smc_calls_revision_result result; |
| } res; |
| |
| arm_smccc_smc(TEE_SMC_CALL_GET_OS_REVISION, |
| 0, 0, 0, 0, 0, 0, 0, &res.smccc); |
| *major = res.result.major; |
| *minor = res.result.minor; |
| |
| return 0; |
| } |
| |
| static int tee_msg_api_revision(u32 *major, u32 *minor) |
| { |
| union { |
| struct arm_smccc_res smccc; |
| struct tee_smc_calls_revision_result result; |
| } res; |
| |
| arm_smccc_smc(TEE_SMC_CALLS_REVISION, |
| 0, 0, 0, 0, 0, 0, 0, &res.smccc); |
| *major = res.result.major; |
| *minor = res.result.minor; |
| |
| return 0; |
| } |
| |
| static int tee_set_sys_boot_complete(void) |
| { |
| struct arm_smccc_res res; |
| static int inited; |
| |
| res.a0 = 0; |
| |
| if (!inited) { |
| arm_smccc_smc(TEE_SMC_SYS_BOOT_COMPLETE, |
| 0, 0, 0, 0, 0, 0, 0, &res); |
| |
| if (!res.a0) |
| inited = 1; |
| } |
| |
| return res.a0; |
| } |
| |
| static ssize_t os_version_show(struct class *class, |
| struct class_attribute *attr, char *buf) |
| { |
| int ret; |
| u32 major, minor; |
| |
| ret = tee_msg_os_revision(&major, &minor); |
| if (ret) |
| return 0; |
| |
| ret = sprintf(buf, "os version: V%d.%d\n", major, minor); |
| |
| return ret; |
| } |
| |
| static ssize_t api_version_show(struct class *class, |
| struct class_attribute *attr, char *buf) |
| { |
| int ret; |
| u32 major, minor; |
| |
| ret = tee_msg_api_revision(&major, &minor); |
| if (ret) |
| return 0; |
| |
| ret = sprintf(buf, "api version: V%d.%d\n", major, minor); |
| |
| return ret; |
| } |
| |
| static ssize_t sys_boot_complete_store(struct class *class, |
| struct class_attribute *attr, |
| const char *buf, size_t count) |
| { |
| bool val; |
| int ret = 0; |
| |
| if (kstrtobool(buf, &val)) |
| return -EINVAL; |
| |
| if (val) |
| ret = tee_set_sys_boot_complete(); |
| |
| if (ret) |
| return -EINVAL; |
| |
| return count; |
| } |
| |
| static CLASS_ATTR_RO(os_version); |
| static CLASS_ATTR_RO(api_version); |
| static CLASS_ATTR_WO(sys_boot_complete); |
| |
| /* |
| * index: firmware index |
| * vdec: vdec type(0: compatible, 1: legency vdec, 2: HEVC vdec) |
| */ |
| static int tee_load_firmware(u32 index, u32 vdec, bool is_swap) |
| { |
| struct arm_smccc_res res; |
| |
| arm_smccc_smc(TEE_SMC_LOAD_VIDEO_FW, |
| index, vdec, is_swap, 0, 0, 0, 0, &res); |
| |
| return res.a0; |
| } |
| |
| int tee_load_video_fw(u32 index, u32 vdec) |
| { |
| return tee_load_firmware(index, vdec, false); |
| } |
| EXPORT_SYMBOL(tee_load_video_fw); |
| |
| int tee_load_video_fw_swap(u32 index, u32 vdec, bool is_swap) |
| { |
| return tee_load_firmware(index, vdec, is_swap); |
| } |
| EXPORT_SYMBOL(tee_load_video_fw_swap); |
| |
| bool tee_enabled(void) |
| { |
| struct arm_smccc_res res; |
| |
| if (disable_flag == 1) |
| return false; |
| /*return false;*/ /*disable tee load temporary*/ |
| |
| arm_smccc_smc(TEE_SMC_CALLS_UID, 0, 0, 0, 0, 0, 0, 0, &res); |
| |
| if (res.a0 == TEE_MSG_UID_0 && res.a1 == TEE_MSG_UID_1 && |
| res.a2 == TEE_MSG_UID_2 && res.a3 == TEE_MSG_UID_3) |
| return true; |
| return false; |
| } |
| EXPORT_SYMBOL(tee_enabled); |
| |
| u32 tee_protect_tvp_mem(u32 start, u32 size, u32 *handle) |
| { |
| struct arm_smccc_res res; |
| |
| if (!handle) |
| return 0xFFFF0006; |
| |
| arm_smccc_smc(TEE_SMC_PROTECT_TVP_MEM, |
| start, size, 0, 0, 0, 0, 0, &res); |
| |
| *handle = res.a1; |
| |
| return res.a0; |
| } |
| EXPORT_SYMBOL(tee_protect_tvp_mem); |
| |
| void tee_unprotect_tvp_mem(u32 handle) |
| { |
| struct arm_smccc_res res; |
| |
| arm_smccc_smc(TEE_SMC_UNPROTECT_TVP_MEM, |
| handle, 0, 0, 0, 0, 0, 0, &res); |
| } |
| EXPORT_SYMBOL(tee_unprotect_tvp_mem); |
| |
| u32 tee_protect_mem_by_type(u32 type, |
| u32 start, u32 size, |
| u32 *handle) |
| { |
| struct arm_smccc_res res; |
| |
| if (!handle) |
| return 0xFFFF0006; |
| |
| arm_smccc_smc(TEE_SMC_PROTECT_MEM_BY_TYPE, |
| type, start, size, 0, 0, 0, 0, &res); |
| |
| *handle = res.a1; |
| |
| return res.a0; |
| } |
| EXPORT_SYMBOL(tee_protect_mem_by_type); |
| |
| u32 tee_protect_mem(u32 type, u32 level, |
| u32 start, u32 size, u32 *handle) |
| { |
| struct arm_smccc_res res; |
| |
| if (!handle) |
| return 0xFFFF0006; |
| |
| arm_smccc_smc(TEE_SMC_PROTECT_MEM_BY_TYPE, |
| type, start, size, level, 0, 0, 0, &res); |
| |
| *handle = res.a1; |
| |
| return res.a0; |
| } |
| EXPORT_SYMBOL(tee_protect_mem); |
| |
| void tee_unprotect_mem(u32 handle) |
| { |
| struct arm_smccc_res res; |
| |
| arm_smccc_smc(TEE_SMC_UNPROTECT_MEM, |
| handle, 0, 0, 0, 0, 0, 0, &res); |
| } |
| EXPORT_SYMBOL(tee_unprotect_mem); |
| |
| int tee_check_in_mem(u32 pa, u32 size) |
| { |
| struct arm_smccc_res res; |
| |
| arm_smccc_smc(TEE_SMC_CHECK_IN_MEM, |
| pa, size, 0, 0, 0, 0, 0, &res); |
| |
| return res.a0; |
| } |
| EXPORT_SYMBOL(tee_check_in_mem); |
| |
| int tee_check_out_mem(u32 pa, u32 size) |
| { |
| struct arm_smccc_res res; |
| |
| arm_smccc_smc(TEE_SMC_CHECK_OUT_MEM, |
| pa, size, 0, 0, 0, 0, 0, &res); |
| |
| return res.a0; |
| } |
| EXPORT_SYMBOL(tee_check_out_mem); |
| |
| int tee_config_device_state(int dev_id, int secure) |
| { |
| struct arm_smccc_res res; |
| |
| arm_smccc_smc(TEE_SMC_CONFIG_DEVICE_SECURE, |
| dev_id, secure, 0, 0, 0, 0, 0, &res); |
| |
| return res.a0; |
| } |
| EXPORT_SYMBOL(tee_config_device_state); |
| |
| void tee_demux_config_pipeline(int tsn_in, int tsn_out) |
| { |
| struct arm_smccc_res res; |
| |
| arm_smccc_smc(TEE_SMC_DEMUX_CONFIG_PIPELINE, |
| tsn_in, tsn_out, 0, 0, 0, 0, 0, &res); |
| } |
| EXPORT_SYMBOL(tee_demux_config_pipeline); |
| |
| int tee_demux_config_pad(int reg, int val) |
| { |
| struct arm_smccc_res res; |
| |
| arm_smccc_smc(TEE_SMC_DEMUX_CONFIG_PAD, |
| reg, val, 0, 0, 0, 0, 0, &res); |
| |
| return res.a0; |
| } |
| EXPORT_SYMBOL(tee_demux_config_pad); |
| |
| int tee_read_reg_bits(u32 reg, u32 *val, u32 offset, u32 length) |
| { |
| struct arm_smccc_res res; |
| |
| arm_smccc_smc(TEE_SMC_READ_REG, |
| reg, 0, offset, length, 0, 0, 0, &res); |
| |
| *val = res.a2; |
| |
| return res.a0; |
| } |
| EXPORT_SYMBOL(tee_read_reg_bits); |
| |
| int tee_write_reg_bits(u32 reg, u32 val, u32 offset, u32 length) |
| { |
| struct arm_smccc_res res; |
| |
| arm_smccc_smc(TEE_SMC_WRITE_REG, |
| reg, val, offset, length, 0, 0, 0, &res); |
| |
| return res.a0; |
| } |
| EXPORT_SYMBOL(tee_write_reg_bits); |
| |
| int tee_vp9_prob_process(u32 cur_frame_type, u32 prev_frame_type, |
| u32 prob_status, u32 prob_addr) |
| { |
| struct arm_smccc_res res; |
| |
| arm_smccc_smc(TEE_SMC_VP9_PROB_PROCESS, |
| cur_frame_type, prev_frame_type, prob_status, |
| prob_addr, 0, 0, 0, &res); |
| |
| return res.a0; |
| } |
| EXPORT_SYMBOL(tee_vp9_prob_process); |
| |
| int tee_vp9_prob_malloc(u32 *prob_addr) |
| { |
| struct arm_smccc_res res; |
| |
| if (!prob_addr) |
| return 0xFFFF0006; |
| |
| arm_smccc_smc(TEE_SMC_VP9_PROB_MALLOC, |
| 0, 0, 0, 0, 0, 0, 0, &res); |
| |
| *prob_addr = res.a1; |
| |
| return res.a0; |
| } |
| EXPORT_SYMBOL(tee_vp9_prob_malloc); |
| |
| int tee_vp9_prob_free(u32 prob_addr) |
| { |
| struct arm_smccc_res res; |
| |
| arm_smccc_smc(TEE_SMC_VP9_PROB_FREE, |
| prob_addr, 0, 0, 0, 0, 0, 0, &res); |
| |
| return res.a0; |
| } |
| EXPORT_SYMBOL(tee_vp9_prob_free); |
| |
| int tee_create_sysfs(void) |
| { |
| int ret; |
| |
| tee_sys_class = class_create(THIS_MODULE, DRIVER_NAME); |
| ret = class_create_file(tee_sys_class, &class_attr_os_version); |
| if (ret != 0) { |
| pr_err("create class file os_version fail\n"); |
| return ret; |
| } |
| ret = class_create_file(tee_sys_class, &class_attr_api_version); |
| if (ret != 0) { |
| pr_err("create class file os_version fail\n"); |
| return ret; |
| } |
| |
| ret = class_create_file(tee_sys_class, &class_attr_sys_boot_complete); |
| if (ret != 0) { |
| pr_err("create class file sys_boot_complete fail\n"); |
| return ret; |
| } |
| |
| return ret; |
| } |
| |
| static int __init aml_tee_modinit(void) |
| { |
| return tee_create_sysfs(); |
| } |
| |
| arch_initcall(aml_tee_modinit); |
| |
| static void __exit aml_tee_modexit(void) |
| { |
| class_destroy(tee_sys_class); |
| } |
| module_param(disable_flag, uint, 0664); |
| MODULE_PARM_DESC(disable_flag, "\n tee firmload disable_flag flag\n"); |
| |
| module_exit(aml_tee_modexit); |
| |
| MODULE_AUTHOR("pengguang.zhu<pengguang.zhu@amlogic.com>"); |
| MODULE_DESCRIPTION("AMLOGIC tee driver"); |
| MODULE_LICENSE("GPL"); |