| // SPDX-License-Identifier: BSD-2-Clause |
| /* |
| * Copyright (c) 2015, Linaro Limited |
| * Copyright (c) 2014, STMicroelectronics International N.V. |
| */ |
| |
| #include <tee/entry_fast.h> |
| #include <optee_msg.h> |
| #include <sm/optee_smc.h> |
| #include <kernel/generic_boot.h> |
| #include <kernel/tee_l2cc_mutex.h> |
| #include <kernel/virtualization.h> |
| #include <kernel/misc.h> |
| #include <mm/core_mmu.h> |
| |
| #ifdef CFG_CORE_RESERVED_SHM |
| static void tee_entry_get_shm_config(struct thread_smc_args *args) |
| { |
| args->a0 = OPTEE_SMC_RETURN_OK; |
| args->a1 = default_nsec_shm_paddr; |
| args->a2 = default_nsec_shm_size; |
| /* Should this be TEESMC cache attributes instead? */ |
| args->a3 = core_mmu_is_shm_cached(); |
| } |
| #endif |
| |
| static void tee_entry_fastcall_l2cc_mutex(struct thread_smc_args *args) |
| { |
| TEE_Result ret; |
| #ifdef ARM32 |
| paddr_t pa = 0; |
| |
| switch (args->a1) { |
| case OPTEE_SMC_L2CC_MUTEX_GET_ADDR: |
| ret = tee_get_l2cc_mutex(&pa); |
| reg_pair_from_64(pa, &args->a2, &args->a3); |
| break; |
| case OPTEE_SMC_L2CC_MUTEX_SET_ADDR: |
| pa = reg_pair_to_64(args->a2, args->a3); |
| ret = tee_set_l2cc_mutex(&pa); |
| break; |
| case OPTEE_SMC_L2CC_MUTEX_ENABLE: |
| ret = tee_enable_l2cc_mutex(); |
| break; |
| case OPTEE_SMC_L2CC_MUTEX_DISABLE: |
| ret = tee_disable_l2cc_mutex(); |
| break; |
| default: |
| args->a0 = OPTEE_SMC_RETURN_EBADCMD; |
| return; |
| } |
| #else |
| ret = TEE_ERROR_NOT_SUPPORTED; |
| #endif |
| if (ret == TEE_ERROR_NOT_SUPPORTED) |
| args->a0 = OPTEE_SMC_RETURN_UNKNOWN_FUNCTION; |
| else if (ret) |
| args->a0 = OPTEE_SMC_RETURN_EBADADDR; |
| else |
| args->a0 = OPTEE_SMC_RETURN_OK; |
| } |
| |
| static void tee_entry_exchange_capabilities(struct thread_smc_args *args) |
| { |
| bool dyn_shm_en = false; |
| |
| /* |
| * Currently we ignore OPTEE_SMC_NSEC_CAP_UNIPROCESSOR. |
| * |
| * The memory mapping of shared memory is defined as normal |
| * shared memory for SMP systems and normal memory for UP |
| * systems. Currently we map all memory as shared in secure |
| * world. |
| * |
| * When translation tables are created with shared bit cleared for |
| * uniprocessor systems we'll need to check |
| * OPTEE_SMC_NSEC_CAP_UNIPROCESSOR. |
| */ |
| |
| if (args->a1 & ~OPTEE_SMC_NSEC_CAP_UNIPROCESSOR) { |
| /* Unknown capability. */ |
| args->a0 = OPTEE_SMC_RETURN_ENOTAVAIL; |
| return; |
| } |
| |
| args->a0 = OPTEE_SMC_RETURN_OK; |
| args->a1 = 0; |
| #ifdef CFG_CORE_RESERVED_SHM |
| args->a1 |= OPTEE_SMC_SEC_CAP_HAVE_RESERVED_SHM; |
| #endif |
| #ifdef CFG_VIRTUALIZATION |
| args->a1 |= OPTEE_SMC_SEC_CAP_VIRTUALIZATION; |
| #endif |
| |
| #if defined(CFG_CORE_DYN_SHM) |
| dyn_shm_en = core_mmu_nsec_ddr_is_defined(); |
| if (dyn_shm_en) |
| args->a1 |= OPTEE_SMC_SEC_CAP_DYNAMIC_SHM; |
| #endif |
| |
| DMSG("Dynamic shared memory is %sabled", dyn_shm_en ? "en" : "dis"); |
| } |
| |
| static void tee_entry_disable_shm_cache(struct thread_smc_args *args) |
| { |
| uint64_t cookie; |
| |
| if (!thread_disable_prealloc_rpc_cache(&cookie)) { |
| args->a0 = OPTEE_SMC_RETURN_EBUSY; |
| return; |
| } |
| |
| if (!cookie) { |
| args->a0 = OPTEE_SMC_RETURN_ENOTAVAIL; |
| return; |
| } |
| |
| args->a0 = OPTEE_SMC_RETURN_OK; |
| args->a1 = cookie >> 32; |
| args->a2 = cookie; |
| } |
| |
| static void tee_entry_enable_shm_cache(struct thread_smc_args *args) |
| { |
| if (thread_enable_prealloc_rpc_cache()) |
| args->a0 = OPTEE_SMC_RETURN_OK; |
| else |
| args->a0 = OPTEE_SMC_RETURN_EBUSY; |
| } |
| |
| static void tee_entry_boot_secondary(struct thread_smc_args *args) |
| { |
| #if defined(CFG_BOOT_SECONDARY_REQUEST) |
| if (!generic_boot_core_release(args->a1, (paddr_t)(args->a3))) |
| args->a0 = OPTEE_SMC_RETURN_OK; |
| else |
| args->a0 = OPTEE_SMC_RETURN_EBADCMD; |
| #else |
| args->a0 = OPTEE_SMC_RETURN_ENOTAVAIL; |
| #endif |
| } |
| |
| static void tee_entry_get_thread_count(struct thread_smc_args *args) |
| { |
| args->a0 = OPTEE_SMC_RETURN_OK; |
| args->a1 = CFG_NUM_THREADS; |
| } |
| |
| #if defined(CFG_VIRTUALIZATION) |
| static void tee_entry_vm_created(struct thread_smc_args *args) |
| { |
| uint16_t guest_id = args->a1; |
| |
| /* Only hypervisor can issue this request */ |
| if (args->a7 != HYP_CLNT_ID) { |
| args->a0 = OPTEE_SMC_RETURN_ENOTAVAIL; |
| return; |
| } |
| |
| args->a0 = virt_guest_created(guest_id); |
| } |
| |
| static void tee_entry_vm_destroyed(struct thread_smc_args *args) |
| { |
| uint16_t guest_id = args->a1; |
| |
| /* Only hypervisor can issue this request */ |
| if (args->a7 != HYP_CLNT_ID) { |
| args->a0 = OPTEE_SMC_RETURN_ENOTAVAIL; |
| return; |
| } |
| |
| args->a0 = virt_guest_destroyed(guest_id); |
| } |
| #endif |
| |
| /* Note: this function is weak to let platforms add special handling */ |
| void __weak tee_entry_fast(struct thread_smc_args *args) |
| { |
| __tee_entry_fast(args); |
| } |
| |
| /* |
| * If tee_entry_fast() is overridden, it's still supposed to call this |
| * function. |
| */ |
| void __tee_entry_fast(struct thread_smc_args *args) |
| { |
| switch (args->a0) { |
| |
| /* Generic functions */ |
| case OPTEE_SMC_CALLS_COUNT: |
| tee_entry_get_api_call_count(args); |
| break; |
| case OPTEE_SMC_CALLS_UID: |
| tee_entry_get_api_uuid(args); |
| break; |
| case OPTEE_SMC_CALLS_REVISION: |
| tee_entry_get_api_revision(args); |
| break; |
| case OPTEE_SMC_CALL_GET_OS_UUID: |
| tee_entry_get_os_uuid(args); |
| break; |
| case OPTEE_SMC_CALL_GET_OS_REVISION: |
| tee_entry_get_os_revision(args); |
| break; |
| |
| /* OP-TEE specific SMC functions */ |
| #ifdef CFG_CORE_RESERVED_SHM |
| case OPTEE_SMC_GET_SHM_CONFIG: |
| tee_entry_get_shm_config(args); |
| break; |
| #endif |
| case OPTEE_SMC_L2CC_MUTEX: |
| tee_entry_fastcall_l2cc_mutex(args); |
| break; |
| case OPTEE_SMC_EXCHANGE_CAPABILITIES: |
| tee_entry_exchange_capabilities(args); |
| break; |
| case OPTEE_SMC_DISABLE_SHM_CACHE: |
| tee_entry_disable_shm_cache(args); |
| break; |
| case OPTEE_SMC_ENABLE_SHM_CACHE: |
| tee_entry_enable_shm_cache(args); |
| break; |
| case OPTEE_SMC_BOOT_SECONDARY: |
| tee_entry_boot_secondary(args); |
| break; |
| case OPTEE_SMC_GET_THREAD_COUNT: |
| tee_entry_get_thread_count(args); |
| break; |
| |
| #if defined(CFG_VIRTUALIZATION) |
| case OPTEE_SMC_VM_CREATED: |
| tee_entry_vm_created(args); |
| break; |
| case OPTEE_SMC_VM_DESTROYED: |
| tee_entry_vm_destroyed(args); |
| break; |
| #endif |
| |
| default: |
| args->a0 = OPTEE_SMC_RETURN_UNKNOWN_FUNCTION; |
| break; |
| } |
| } |
| |
| size_t tee_entry_generic_get_api_call_count(void) |
| { |
| /* |
| * All the different calls handled in this file. If the specific |
| * target has additional calls it will call this function and |
| * add the number of calls the target has added. |
| */ |
| size_t ret = 12; |
| |
| #if defined(CFG_VIRTUALIZATION) |
| ret += 2; |
| #endif |
| |
| return ret; |
| } |
| |
| void __weak tee_entry_get_api_call_count(struct thread_smc_args *args) |
| { |
| args->a0 = tee_entry_generic_get_api_call_count(); |
| } |
| |
| void __weak tee_entry_get_api_uuid(struct thread_smc_args *args) |
| { |
| args->a0 = OPTEE_MSG_UID_0; |
| args->a1 = OPTEE_MSG_UID_1; |
| args->a2 = OPTEE_MSG_UID_2; |
| args->a3 = OPTEE_MSG_UID_3; |
| } |
| |
| void __weak tee_entry_get_api_revision(struct thread_smc_args *args) |
| { |
| args->a0 = OPTEE_MSG_REVISION_MAJOR; |
| args->a1 = OPTEE_MSG_REVISION_MINOR; |
| } |
| |
| void __weak tee_entry_get_os_uuid(struct thread_smc_args *args) |
| { |
| args->a0 = OPTEE_MSG_OS_OPTEE_UUID_0; |
| args->a1 = OPTEE_MSG_OS_OPTEE_UUID_1; |
| args->a2 = OPTEE_MSG_OS_OPTEE_UUID_2; |
| args->a3 = OPTEE_MSG_OS_OPTEE_UUID_3; |
| } |
| |
| void __weak tee_entry_get_os_revision(struct thread_smc_args *args) |
| { |
| args->a0 = CFG_OPTEE_REVISION_MAJOR; |
| args->a1 = CFG_OPTEE_REVISION_MINOR; |
| args->a2 = TEE_IMPL_GIT_SHA1; |
| } |