diff options
Diffstat (limited to 'plat/mediatek/common/mtk_smc_handlers.c')
-rw-r--r-- | plat/mediatek/common/mtk_smc_handlers.c | 259 |
1 files changed, 259 insertions, 0 deletions
diff --git a/plat/mediatek/common/mtk_smc_handlers.c b/plat/mediatek/common/mtk_smc_handlers.c new file mode 100644 index 0000000..5a3ad1f --- /dev/null +++ b/plat/mediatek/common/mtk_smc_handlers.c @@ -0,0 +1,259 @@ +/* + * Copyright (c) 2022-2023, MediaTek Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <assert.h> +#include <errno.h> +#if MTK_SIP_KERNEL_BOOT_ENABLE +#include <cold_boot.h> +#endif +#include <common/debug.h> +#include <common/runtime_svc.h> +#include <lib/mtk_init/mtk_init.h> +#include <mtk_sip_svc.h> + +#define SMC_HANDLER_DEBUG(...) VERBOSE(__VA_ARGS__) +#define SMC_HANDLER_DEBUG_NOT_IMP_MSG "%s[0x%x] smc handler not implemented\n" +#define SMC_HANDLER_DEBUG_START_MSG "%s[0x%x] smc handler start, smc desc. index:%d\n" +#define SMC_HANDLER_DEBUG_END_MSG "%s[0x%x] smc handler end\n" + +/* + * These macros below are used to identify SIP calls from Kernel, + * Hypervisor, or 2ndBootloader + */ +#define SIP_FID_ORI_MASK (0xc000) +#define SIP_FID_ORI_SHIFT (14) +#define SIP_FID_KERNEL (0x0) +#define SIP_FID_KERNEL_VIA_GZ (0x1) +#define SIP_FID_GZ (0x2) + +#define GET_SMC_ORI(_fid) (((_fid) & SIP_FID_ORI_MASK) >> SIP_FID_ORI_SHIFT) +#define GET_SMC_ORI_NUM(_fid) ((_fid) & ~(SIP_FID_ORI_MASK)) + +#define is_from_nsel2(_ori) (_ori == SIP_FID_GZ) +#define is_from_bl33(_ori) \ + ((_ori != SIP_FID_GZ) && (is_el1_2nd_bootloader() == 1)) +#define is_from_nsel1(_ori) \ + (((_ori == SIP_FID_KERNEL) || \ + (_ori == SIP_FID_KERNEL_VIA_GZ)) && \ + (is_el1_2nd_bootloader() == 0)) + +#define is_smc_forbidden(_ori) (_ori == SIP_FID_KERNEL_VIA_GZ) + +#define MASK_32_BIT (0xffffffffU) +#define SMC_ID_EXPAND_AS_SMC_OPERATION(_smc_id, _smc_num) \ + case _smc_id##_AARCH32: \ + { \ + x1 = x1 & MASK_32_BIT; \ + x2 = x2 & MASK_32_BIT; \ + x3 = x3 & MASK_32_BIT; \ + x4 = x4 & MASK_32_BIT; \ + } \ + /* fallthrough */ \ + case _smc_id##_AARCH64: \ + { \ + if (_smc_id##_descriptor_index < 0) { \ + SMC_HANDLER_DEBUG(SMC_HANDLER_DEBUG_NOT_IMP_MSG, #_smc_id, smc_id); \ + break; \ + } \ + if (_smc_id##_descriptor_index >= smc_id_descriptor_max) { \ + SMC_HANDLER_DEBUG("smc descriptor index[%d] exceed max[%d]\n", \ + _smc_id##_descriptor_index, smc_id_descriptor_max); \ + break; \ + } \ + SMC_HANDLER_DEBUG(SMC_HANDLER_DEBUG_START_MSG, #_smc_id, smc_id, \ + _smc_id##_descriptor_index); \ + ret = smc_handler_pool[_smc_id##_descriptor_index].smc_handler(x1,\ + x2, x3, x4, handle, &smc_ret); \ + SMC_HANDLER_DEBUG(SMC_HANDLER_DEBUG_END_MSG, #_smc_id, smc_id); \ + break; \ + } + +#define SMC_ID_EXPAND_AS_DESCRIPTOR_INDEX(_smc_id, _smc_num) \ + short _smc_id##_descriptor_index __section(".mtk_plat_ro") = -1; + +MTK_SIP_SMC_FROM_BL33_TABLE(SMC_ID_EXPAND_AS_DESCRIPTOR_INDEX); +MTK_SIP_SMC_FROM_NS_EL1_TABLE(SMC_ID_EXPAND_AS_DESCRIPTOR_INDEX); +MTK_SIP_SMC_FROM_S_EL1_TABLE(SMC_ID_EXPAND_AS_DESCRIPTOR_INDEX); + +IMPORT_SYM(uintptr_t, __MTK_SMC_POOL_START__, MTK_SMC_POOL_START); +IMPORT_SYM(uintptr_t, __MTK_SMC_POOL_END_UNALIGNED__, MTK_SMC_POOL_END_UNALIGNED); + +static const struct smc_descriptor *smc_handler_pool; +static short smc_id_descriptor_max; + +#if !MTK_SIP_KERNEL_BOOT_ENABLE +/* + * If there is no SMC request needs to be served in 2nd bootloader, + * disable the service path inherently. + */ +bool is_el1_2nd_bootloader(void) +{ + return false; +} +#endif + +static void print_smc_descriptor(const struct smc_descriptor pool[]) +{ + const struct smc_descriptor *p_smc_desc; + + INFO("print smc descriptor pool\n"); + for (p_smc_desc = &pool[0]; + (char *)p_smc_desc < (char *)MTK_SMC_POOL_END_UNALIGNED; + p_smc_desc++) { + INFO("descriptor name:%s\n", p_smc_desc->smc_name); + INFO("descriptor index:%d\n", *p_smc_desc->smc_descriptor_index); + INFO("smc id 32:0x%x, smc id 64:0x%x\n", + p_smc_desc->smc_id_aarch32, p_smc_desc->smc_id_aarch64); + } +} + +static int mtk_smc_handler_init(void) +{ + const struct smc_descriptor *iter; + short index_cnt; + int ret = 0; + + smc_handler_pool = (const struct smc_descriptor *)MTK_SMC_POOL_START; + /* Designate descriptor index point to smc_handler_pool */ + for (index_cnt = 0, iter = &smc_handler_pool[0]; + (char *)iter < (char *)MTK_SMC_POOL_END_UNALIGNED; + iter++, index_cnt++) { + if (index_cnt < 0) { + SMC_HANDLER_DEBUG("smc handler pool index overflow!\n"); + ret = -EPERM; + assert(0); + break; + } + *(iter->smc_descriptor_index) = index_cnt; + } + smc_id_descriptor_max = index_cnt; + print_smc_descriptor(smc_handler_pool); + return ret; +} +MTK_EARLY_PLAT_INIT(mtk_smc_handler_init); + +/* This function handles Mediatek defined SiP Calls from Secure world */ +static u_register_t mtk_smc_handler_sel1(uint32_t smc_id, + u_register_t x1, + u_register_t x2, + u_register_t x3, + u_register_t x4, + void *cookie, + void *handle, + u_register_t flags) +{ + u_register_t ret = MTK_SIP_E_SUCCESS; + struct smccc_res smc_ret = {0}; + + switch (smc_id) { + MTK_SIP_SMC_FROM_S_EL1_TABLE(SMC_ID_EXPAND_AS_SMC_OPERATION); + default: + INFO("SEL1 SMC ID:0x%x not support\n", smc_id); + ret = SMC_UNK; + } + SMC_RET4(handle, ret, smc_ret.a1, smc_ret.a2, smc_ret.a3); +} + +/* This function handles Mediatek defined SiP Calls from Bootloader */ +static uintptr_t mtk_smc_handler_bl33(uint32_t smc_id, + u_register_t x1, + u_register_t x2, + u_register_t x3, + u_register_t x4, + void *cookie, + void *handle, + u_register_t flags) +{ + uintptr_t ret = MTK_SIP_E_SUCCESS; + struct smccc_res smc_ret = {0}; + + switch (smc_id) { + MTK_SIP_SMC_FROM_BL33_TABLE(SMC_ID_EXPAND_AS_SMC_OPERATION); + default: + INFO("BL33 SMC ID:0x%x not supported\n", smc_id); + ret = SMC_UNK; + break; + } + SMC_RET4(handle, ret, smc_ret.a1, smc_ret.a2, smc_ret.a3); +} + +/* This function handles Mediatek defined SiP Calls from Kernel */ +static uintptr_t mtk_smc_handler_nsel1(uint32_t smc_id, + u_register_t x1, + u_register_t x2, + u_register_t x3, + u_register_t x4, + void *cookie, + void *handle, + u_register_t flags) +{ + uintptr_t ret = MTK_SIP_E_SUCCESS; + struct smccc_res smc_ret = {0}; + + switch (smc_id) { + MTK_SIP_SMC_FROM_NS_EL1_TABLE(SMC_ID_EXPAND_AS_SMC_OPERATION); + default: + INFO("NSEL1 SMC ID:0x%x not supported\n", smc_id); + ret = SMC_UNK; + break; + } + SMC_RET4(handle, ret, smc_ret.a1, smc_ret.a2, smc_ret.a3); +} + +static uintptr_t mtk_smc_handler(uint32_t smc_id, + u_register_t x1, + u_register_t x2, + u_register_t x3, + u_register_t x4, + void *cookie, + void *handle, + u_register_t flags) +{ + uintptr_t ret = SMC_UNK; + uint32_t ns; + uint32_t smc_ori; + uint32_t smc_num; + + /* Get SMC Originator bit 14.15 */ + smc_ori = GET_SMC_ORI(smc_id); + /* Get SMC Number. Clean bit 14.15 */ + smc_num = GET_SMC_ORI_NUM(smc_id); + + /* Determine which security state this SMC originated from */ + ns = is_caller_non_secure(flags); + + if (ns && is_smc_forbidden(smc_ori)) { + ERROR("%s: Forbidden SMC call (0x%x)\n", __func__, smc_id); + SMC_RET1(handle, ret); + } + + if (!ns) { + /* SiP SMC service secure world's call */ + return mtk_smc_handler_sel1(smc_num, x1, x2, x3, x4, + cookie, handle, flags); + } + if (is_from_bl33(smc_ori)) { + /* SiP SMC service secure bootloader's call */ + return mtk_smc_handler_bl33(smc_num, x1, x2, x3, x4, + cookie, handle, flags); + } else if (is_from_nsel1(smc_ori)) { + /* SiP SMC service kernel's call */ + return mtk_smc_handler_nsel1(smc_num, x1, x2, x3, x4, + cookie, handle, flags); + } + INFO("SMC ID:0x%x not supported\n", smc_id); + SMC_RET1(handle, ret); +} + +/* Define a runtime service descriptor for fast SMC calls */ +DECLARE_RT_SVC( + mtk_smc_handler, + OEN_SIP_START, + OEN_SIP_END, + SMC_TYPE_FAST, + NULL, + mtk_smc_handler +); |