diff --git a/xen/arch/arm/tee/optee.c b/xen/arch/arm/tee/optee.c index e9b69bd2d2..6c51caa41a 100644 --- a/xen/arch/arm/tee/optee.c +++ b/xen/arch/arm/tee/optee.c @@ -32,9 +32,17 @@ #include #include +/* Number of SMCs known to the mediator */ +#define OPTEE_MEDIATOR_SMC_COUNT 11 + /* Client ID 0 is reserved for the hypervisor itself */ #define OPTEE_CLIENT_ID(domain) ((domain)->domain_id + 1) +#define OPTEE_KNOWN_NSEC_CAPS OPTEE_SMC_NSEC_CAP_UNIPROCESSOR +#define OPTEE_KNOWN_SEC_CAPS (OPTEE_SMC_SEC_CAP_HAVE_RESERVED_SHM | \ + OPTEE_SMC_SEC_CAP_UNREGISTERED_SHM | \ + OPTEE_SMC_SEC_CAP_DYNAMIC_SHM) + /* Domain context */ struct optee_domain { }; @@ -120,22 +128,111 @@ static int optee_relinquish_resources(struct domain *d) return 0; } +static void handle_exchange_capabilities(struct cpu_user_regs *regs) +{ + struct arm_smccc_res resp; + uint32_t caps; + + /* Filter out unknown guest caps */ + caps = get_user_reg(regs, 1); + caps &= OPTEE_KNOWN_NSEC_CAPS; + + arm_smccc_smc(OPTEE_SMC_EXCHANGE_CAPABILITIES, caps, 0, 0, 0, 0, 0, + OPTEE_CLIENT_ID(current->domain), &resp); + if ( resp.a0 != OPTEE_SMC_RETURN_OK ) { + set_user_reg(regs, 0, resp.a0); + return; + } + + caps = resp.a1; + + /* Filter out unknown OP-TEE caps */ + caps &= OPTEE_KNOWN_SEC_CAPS; + + /* Drop static SHM_RPC cap */ + caps &= ~OPTEE_SMC_SEC_CAP_HAVE_RESERVED_SHM; + + /* Don't allow guests to work without dynamic SHM */ + if ( !(caps & OPTEE_SMC_SEC_CAP_DYNAMIC_SHM) ) + { + set_user_reg(regs, 0, OPTEE_SMC_RETURN_ENOTAVAIL); + return; + } + + set_user_reg(regs, 0, OPTEE_SMC_RETURN_OK); + set_user_reg(regs, 1, caps); +} + static bool optee_handle_call(struct cpu_user_regs *regs) { + struct arm_smccc_res resp; + if ( !current->domain->arch.tee ) return false; switch ( get_user_reg(regs, 0) ) { case OPTEE_SMC_CALLS_COUNT: + set_user_reg(regs, 0, OPTEE_MEDIATOR_SMC_COUNT); + return true; + case OPTEE_SMC_CALLS_UID: + arm_smccc_smc(OPTEE_SMC_CALLS_UID, 0, 0, 0, 0, 0, 0, + OPTEE_CLIENT_ID(current->domain), &resp); + set_user_reg(regs, 0, resp.a0); + set_user_reg(regs, 1, resp.a1); + set_user_reg(regs, 2, resp.a2); + set_user_reg(regs, 3, resp.a3); + return true; + case OPTEE_SMC_CALLS_REVISION: + arm_smccc_smc(OPTEE_SMC_CALLS_REVISION, 0, 0, 0, 0, 0, 0, + OPTEE_CLIENT_ID(current->domain), &resp); + set_user_reg(regs, 0, resp.a0); + set_user_reg(regs, 1, resp.a1); + return true; + case OPTEE_SMC_CALL_GET_OS_UUID: + arm_smccc_smc(OPTEE_SMC_CALL_GET_OS_UUID, 0, 0, 0, 0, 0, 0, + OPTEE_CLIENT_ID(current->domain),&resp); + set_user_reg(regs, 0, resp.a0); + set_user_reg(regs, 1, resp.a1); + set_user_reg(regs, 2, resp.a2); + set_user_reg(regs, 3, resp.a3); + return true; + case OPTEE_SMC_CALL_GET_OS_REVISION: + arm_smccc_smc(OPTEE_SMC_CALL_GET_OS_REVISION, 0, 0, 0, 0, 0, 0, + OPTEE_CLIENT_ID(current->domain), &resp); + set_user_reg(regs, 0, resp.a0); + set_user_reg(regs, 1, resp.a1); + return true; + case OPTEE_SMC_ENABLE_SHM_CACHE: + arm_smccc_smc(OPTEE_SMC_ENABLE_SHM_CACHE, 0, 0, 0, 0, 0, 0, + OPTEE_CLIENT_ID(current->domain), &resp); + set_user_reg(regs, 0, resp.a0); + return true; + case OPTEE_SMC_DISABLE_SHM_CACHE: + arm_smccc_smc(OPTEE_SMC_ENABLE_SHM_CACHE, 0, 0, 0, 0, 0, 0, + OPTEE_CLIENT_ID(current->domain), &resp); + set_user_reg(regs, 0, resp.a0); + if ( resp.a0 == OPTEE_SMC_RETURN_OK ) { + set_user_reg(regs, 1, resp.a1); + set_user_reg(regs, 2, resp.a2); + } + return true; + case OPTEE_SMC_GET_SHM_CONFIG: + /* No static SHM available for guests */ + set_user_reg(regs, 0, OPTEE_SMC_RETURN_ENOTAVAIL); + return true; + case OPTEE_SMC_EXCHANGE_CAPABILITIES: + handle_exchange_capabilities(regs); + return true; + case OPTEE_SMC_CALL_WITH_ARG: case OPTEE_SMC_CALL_RETURN_FROM_RPC: set_user_reg(regs, 0, OPTEE_SMC_RETURN_ENOTAVAIL);