Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Provision TEE threads for system invocations #5789

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions core/arch/arm/include/kernel/thread_private_arch.h
Original file line number Diff line number Diff line change
Expand Up @@ -185,8 +185,8 @@ uint32_t thread_get_usr_lr(void);
void thread_set_usr_lr(uint32_t usr_lr);
#endif /*ARM32*/

void thread_alloc_and_run(uint32_t a0, uint32_t a1, uint32_t a2, uint32_t a3,
uint32_t a4, uint32_t a5);
void thread_alloc_and_run(bool sys_thread, uint32_t a0, uint32_t a1,
uint32_t a2, uint32_t a3, uint32_t a4, uint32_t a5);
void thread_resume_from_rpc(uint32_t thread_id, uint32_t a0, uint32_t a1,
uint32_t a2, uint32_t a3);

Expand Down Expand Up @@ -239,5 +239,10 @@ uint32_t thread_handle_std_smc(uint32_t a0, uint32_t a1, uint32_t a2,
void thread_scall_handler(struct thread_scall_regs *regs);

void thread_spmc_register_secondary_ep(vaddr_t ep);

/* Reservation of system thread context */
TEE_Result thread_reserve_sys_ctx(void);
TEE_Result thread_unreserve_sys_ctx(void);

#endif /*__ASSEMBLER__*/
#endif /*__KERNEL_THREAD_PRIVATE_ARCH_H*/
70 changes: 66 additions & 4 deletions core/arch/arm/include/sm/optee_smc.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/*
* Copyright (c) 2015-2021, Linaro Limited
* Copyright (c) 2015-2023, Linaro Limited
*/
#ifndef OPTEE_SMC_H
#define OPTEE_SMC_H
Expand Down Expand Up @@ -136,11 +136,15 @@
* Call with struct optee_msg_arg as argument
*
* When called with OPTEE_SMC_CALL_WITH_RPC_ARG or
* OPTEE_SMC_CALL_WITH_REGD_ARG in a0 there is one RPC struct optee_msg_arg
* OPTEE_SMC_CALL_WITH_REGD_ARG or OPTEE_SMC_CALL_SYSTEM_WITH_REGD_ARG
* in a0 there is one RPC struct optee_msg_arg
* following after the first struct optee_msg_arg. The RPC struct
* optee_msg_arg has reserved space for the number of RPC parameters as
* returned by OPTEE_SMC_EXCHANGE_CAPABILITIES.
*
* When called with OPTEE_SMC_CALL_SYSTEM_WITH_REGD_ARG in a0 secure world
* will use provisioned system resource for the call execution.
*
* When calling these functions normal world has a few responsibilities:
* 1. It must be able to handle eventual RPCs
* 2. Non-secure interrupts should not be masked
Expand All @@ -158,8 +162,10 @@
* a4-6 Not used
* a7 Hypervisor Client ID register
*
* Call register usage, OPTEE_SMC_CALL_WITH_REGD_ARG:
* a0 SMC Function ID, OPTEE_SMC_CALL_WITH_REGD_ARG
* Call register usage, OPTEE_SMC_CALL_WITH_REGD_ARG and
* OPTEE_SMC_CALL_SYSTEM_WITH_REGD_ARG:
* a0 SMC Function ID, OPTEE_SMC_CALL_WITH_REGD_ARG or
* OPTEE_SMC_CALL_SYSTEM_WITH_REGD_ARG
* a1 Upper 32 bits of a 64-bit shared memory cookie
* a2 Lower 32 bits of a 64-bit shared memory cookie
* a3 Offset of the struct optee_msg_arg in the shared memory with the
Expand Down Expand Up @@ -203,6 +209,8 @@
OPTEE_SMC_STD_CALL_VAL(OPTEE_SMC_FUNCID_CALL_WITH_RPC_ARG)
#define OPTEE_SMC_CALL_WITH_REGD_ARG \
OPTEE_SMC_STD_CALL_VAL(OPTEE_SMC_FUNCID_CALL_WITH_REGD_ARG)
#define OPTEE_SMC_CALL_SYSTEM_WITH_REGD_ARG \
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sumit's suggestion with a bit to indicate if it's a system request made a lot of sense. That way we don't add arbitrary limitations.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see where to store that bit. ABI for OPTEE_SMC_CALL_WITH*_ARG leave no room for an extra bit, we need new funcIDs, reversing an ABI register to carry that information (e.g. bit0 of input a4 argument).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sumit suggested BIT(15) in the SMC ID if I remember correctly.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That would define few new funcIDs, from SMCCC view.
Using existing funcIDs and considering bit15 would be quite easy in Linux and OP-TEE impelmentation, but the description of this specific bit would look a bit hacky, no?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't address this comment yet.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about we start calling top 4 bits for funcIDs as flags in OP-TEE SMC ABI? Since they are already zeros and would give us enough flexibility to add features like system threads etc. Otherwise the function IDs list will keep on increasing given all the combinatorial involved and hard to keep track off. Also, still there will be 12 bits for real funcIDs which allows that list to go upto value: 8191.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd like to have both, combinations explicitly defined so:

#define OPTEE_SMC_CALL_SYSTEM_THREAD_FLAG BIT(15)
#define OPTEE_SMC_CALL_SYSTEM_WITH_ARG \
        OPTEE_SMC_STD_CALL_VAL(OPTEE_SMC_FUNCID_CALL_WITH_ARG | \
                               OPTEE_SMC_CALL_SYSTEM_THREAD_FLAG)
#define OPTEE_SMC_CALL_SYSTEM_WITH_RPC_ARG \
        OPTEE_SMC_STD_CALL_VAL(OPTEE_SMC_FUNCID_CALL_WITH_RPC_ARG | \
                               OPTEE_SMC_CALL_SYSTEM_THREAD_FLAG)
#define OPTEE_SMC_CALL_SYSTEM_WITH_REGD_ARG \
        OPTEE_SMC_STD_CALL_VAL(OPTEE_SMC_FUNCID_CALL_WITH_REGD_ARG | \
                               OPTEE_SMC_CALL_SYSTEM_THREAD_FLAG)

The OPTEE_SMC_CALL_SYSTEM_THREAD_FLAG doesn't make sense for all SMCs so we should still define all SMCs, but with the bit defined there's still some structure in how an SMC is constructed so masking, etc can still be done where that's convenient.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay that's fine with me.

OPTEE_SMC_STD_CALL_VAL(OPTEE_SMC_FUNCID_CALL_SYSTEM_WITH_REGD_ARG)

/*
* Get Shared Memory Config
Expand Down Expand Up @@ -316,6 +324,11 @@
#define OPTEE_SMC_SEC_CAP_ASYNC_NOTIF BIT(5)
/* Secure world supports pre-allocating RPC arg struct */
#define OPTEE_SMC_SEC_CAP_RPC_ARG BIT(6)
/*
* Secure world provisions resources for system calls using SMC Function ID
* OPTEE_SMC_CALL_SYSTEM_WITH_REGD_ARG.
*/
#define OPTEE_SMC_SEC_CAP_SYSTEM_THREAD BIT(7)

#define OPTEE_SMC_FUNCID_EXCHANGE_CAPABILITIES U(9)
#define OPTEE_SMC_EXCHANGE_CAPABILITIES \
Expand Down Expand Up @@ -559,6 +572,55 @@
/* See OPTEE_SMC_CALL_WITH_REGD_ARG above */
#define OPTEE_SMC_FUNCID_CALL_WITH_REGD_ARG U(19)

/* See OPTEE_SMC_CALL_SYSTEM_WITH_REGD_ARG above */
#define OPTEE_SMC_FUNCID_CALL_SYSTEM_WITH_REGD_ARG U(20)

/*
* Request reservation of a system invocation thread context in OP-TEE
*
* Call register usage:
* a0 SMC Function ID: OPTEE_SMC_RESERVE_SYS_THREAD
* a1-6 Not used
* a7 Hypervisor Client ID register
*
* Normal return register usage:
* a0 Return value, OPTEE_SMC_RETURN_*
* a1-3 Not used
* a4-7 Preserved
*
* Possible return values:
* OPTEE_SMC_RETURN_UNKNOWN_FUNCTION Trusted OS does not recognize this
* function.
* OPTEE_SMC_RETURN_OK Call successfully completed.
* OPTEE_SMC_RETURN_ETHREAD_LIMIT Number of Trusted OS threads exceeded
* for the request.
*/
#define OPTEE_SMC_FUNCID_RESERVE_SYS_THREAD U(21)
#define OPTEE_SMC_RESERVE_SYS_THREAD \
OPTEE_SMC_FAST_CALL_VAL(OPTEE_SMC_FUNCID_RESERVE_SYS_THREAD)

/*
* Unregister reservation of a system invocation thread context in OP-TEE
*
* Call register usage:
* a0 SMC Function ID: OPTEE_SMC_UNRESERVE_SYS_THREAD
* a1-6 Not used
* a7 Hypervisor Client ID register
*
* Normal return register usage:
* a0 Return value, OPTEE_SMC_RETURN_*
* a1-3 Not used
* a4-7 Preserved
*
* Possible return values:
* OPTEE_SMC_RETURN_UNKNOWN_FUNCTION Trusted OS does not recognize this
* function.
* OPTEE_SMC_RETURN_OK Call successfully completed.
*/
#define OPTEE_SMC_FUNCID_UNRESERVE_SYS_THREAD U(22)
#define OPTEE_SMC_UNRESERVE_SYS_THREAD \
OPTEE_SMC_FAST_CALL_VAL(OPTEE_SMC_FUNCID_UNRESERVE_SYS_THREAD)

/*
* Resume from RPC (for example after processing a foreign interrupt)
*
Expand Down
96 changes: 72 additions & 24 deletions core/arch/arm/kernel/thread.c
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ long thread_user_kcode_offset __nex_bss;
static size_t thread_user_kcode_size __nex_bss;
#endif

static size_t __maybe_unused reserved_sys_thread;

#if defined(CFG_CORE_UNMAP_CORE_AT_EL0) && \
defined(CFG_CORE_WORKAROUND_SPECTRE_BP_SEC) && defined(ARM64)
long thread_user_kdata_sp_offset __nex_bss;
Expand Down Expand Up @@ -215,65 +217,111 @@ static void init_regs(struct thread_ctx *thread, uint32_t a0, uint32_t a1,
}
#endif /*ARM64*/

static void __thread_alloc_and_run(uint32_t a0, uint32_t a1, uint32_t a2,
uint32_t a3, uint32_t a4, uint32_t a5,
uint32_t a6, uint32_t a7,
TEE_Result thread_reserve_sys_ctx(void)
{
TEE_Result res = TEE_ERROR_GENERIC;

thread_lock_global();

/* Reserved a context if at least 1 generic context remains */
if (IS_ENABLED(CFG_RESERVED_SYSTEM_THREAD) &&
(CFG_NUM_THREADS - reserved_sys_thread) > 1) {
reserved_sys_thread++;
res = TEE_SUCCESS;
}

thread_unlock_global();

return res;
}

TEE_Result thread_unreserve_sys_ctx(void)
{
TEE_Result res = TEE_ERROR_GENERIC;

thread_lock_global();

if (IS_ENABLED(CFG_RESERVED_SYSTEM_THREAD) && reserved_sys_thread) {
reserved_sys_thread--;
res = TEE_SUCCESS;
}

thread_unlock_global();

return res;
}

static int find_free_thread(size_t start_idx, size_t count)
{
size_t n = 0;

for (n = start_idx; n < start_idx + count; n++)
if (threads[n].state == THREAD_STATE_FREE)
return n;

return -1;
}

static void __thread_alloc_and_run(bool sys_thread, uint32_t a0, uint32_t a1,
uint32_t a2, uint32_t a3, uint32_t a4,
uint32_t a5, uint32_t a6, uint32_t a7,
void *pc)
{
struct thread_core_local *l = thread_get_core_local();
bool found_thread = false;
size_t n = 0;
int i = -1;

assert(l->curr_thread == THREAD_ID_INVALID);

thread_lock_global();

for (n = 0; n < CFG_NUM_THREADS; n++) {
if (threads[n].state == THREAD_STATE_FREE) {
threads[n].state = THREAD_STATE_ACTIVE;
found_thread = true;
break;
}
}
if (sys_thread)
i = find_free_thread(CFG_NUM_THREADS - reserved_sys_thread,
reserved_sys_thread);

if (i < 0)
i = find_free_thread(0, CFG_NUM_THREADS - reserved_sys_thread);

if (i >= 0)
threads[i].state = THREAD_STATE_ACTIVE;

thread_unlock_global();

if (!found_thread)
if (i < 0)
return;

l->curr_thread = n;
l->curr_thread = i;

threads[n].flags = 0;
init_regs(threads + n, a0, a1, a2, a3, a4, a5, a6, a7, pc);
threads[i].flags = 0;
init_regs(threads + i, a0, a1, a2, a3, a4, a5, a6, a7, pc);
#ifdef CFG_CORE_PAUTH
/*
* Copy the APIA key into the registers to be restored with
* thread_resume().
*/
threads[n].regs.apiakey_hi = threads[n].keys.apia_hi;
threads[n].regs.apiakey_lo = threads[n].keys.apia_lo;
threads[i].regs.apiakey_hi = threads[i].keys.apia_hi;
threads[i].regs.apiakey_lo = threads[i].keys.apia_lo;
#endif

thread_lazy_save_ns_vfp();

l->flags &= ~THREAD_CLF_TMP;
thread_resume(&threads[n].regs);
thread_resume(&threads[i].regs);
/*NOTREACHED*/
panic();
}

void thread_alloc_and_run(uint32_t a0, uint32_t a1, uint32_t a2, uint32_t a3,
uint32_t a4, uint32_t a5)
void thread_alloc_and_run(bool sys_thread, uint32_t a0, uint32_t a1,
uint32_t a2, uint32_t a3, uint32_t a4, uint32_t a5)
{
__thread_alloc_and_run(a0, a1, a2, a3, a4, a5, 0, 0,
__thread_alloc_and_run(sys_thread, a0, a1, a2, a3, a4, a5, 0, 0,
thread_std_smc_entry);
}

#ifdef CFG_SECURE_PARTITION
void thread_sp_alloc_and_run(struct thread_smc_args *args __maybe_unused)
{
__thread_alloc_and_run(args->a0, args->a1, args->a2, args->a3, args->a4,
args->a5, args->a6, args->a7,
__thread_alloc_and_run(false, args->a0, args->a1, args->a2, args->a3,
args->a4, args->a5, args->a6, args->a7,
spmc_sp_thread_entry);
}
#endif
Expand Down
5 changes: 4 additions & 1 deletion core/arch/arm/kernel/thread_optee_smc.c
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,9 @@ uint32_t thread_handle_std_smc(uint32_t a0, uint32_t a1, uint32_t a2,
thread_resume_from_rpc(a3, a1, a2, a4, a5);
rv = OPTEE_SMC_RETURN_ERESUME;
} else {
thread_alloc_and_run(a0, a1, a2, a3, 0, 0);
bool sys_thread = (a0 == OPTEE_SMC_CALL_SYSTEM_WITH_REGD_ARG);

thread_alloc_and_run(sys_thread, a0, a1, a2, a3, 0, 0);
rv = OPTEE_SMC_RETURN_ETHREAD_LIMIT;
}

Expand Down Expand Up @@ -279,6 +281,7 @@ static uint32_t std_smc_entry(uint32_t a0, uint32_t a1, uint32_t a2,
return std_entry_with_parg(reg_pair_to_64(a1, a2),
with_rpc_arg);
case OPTEE_SMC_CALL_WITH_REGD_ARG:
case OPTEE_SMC_CALL_SYSTEM_WITH_REGD_ARG:
return std_entry_with_regd_arg(reg_pair_to_64(a1, a2), a3);
default:
EMSG("Unknown SMC 0x%"PRIx32, a0);
Expand Down
4 changes: 2 additions & 2 deletions core/arch/arm/kernel/thread_spmc.c
Original file line number Diff line number Diff line change
Expand Up @@ -451,8 +451,8 @@ static void handle_yielding_call(struct thread_smc_args *args)
0);
res = TEE_ERROR_BAD_PARAMETERS;
} else {
thread_alloc_and_run(args->a1, args->a3, args->a4, args->a5,
args->a6, args->a7);
thread_alloc_and_run(false, args->a1, args->a3, args->a4,
args->a5, args->a6, args->a7);
res = TEE_ERROR_BUSY;
}
spmc_set_args(args, FFA_MSG_SEND_DIRECT_RESP_32,
Expand Down
1 change: 1 addition & 0 deletions core/arch/arm/plat-stm32mp1/conf.mk
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,7 @@ endif

# Provision enough threads to pass xtest
ifneq (,$(filter y,$(CFG_SCMI_PTA) $(CFG_STM32MP1_SCMI_SIP)))
CFG_RESERVED_SYSTEM_THREAD ?= y
ifeq ($(CFG_WITH_PAGER),y)
CFG_NUM_THREADS ?= 3
else
Expand Down
37 changes: 37 additions & 0 deletions core/arch/arm/tee/entry_fast.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include <kernel/misc.h>
#include <kernel/notif.h>
#include <kernel/tee_l2cc_mutex.h>
#include <kernel/thread_private_arch.h>
#include <kernel/virtualization.h>
#include <mm/core_mmu.h>
#include <optee_msg.h>
Expand Down Expand Up @@ -116,6 +117,9 @@ static void tee_entry_exchange_capabilities(struct thread_smc_args *args)

args->a1 |= OPTEE_SMC_SEC_CAP_RPC_ARG;
args->a3 = THREAD_RPC_MAX_NUM_PARAMS;

if (IS_ENABLED(CFG_RESERVED_SYSTEM_THREAD))
args->a1 |= OPTEE_SMC_SEC_CAP_SYSTEM_THREAD;
}

static void tee_entry_disable_shm_cache(struct thread_smc_args *args)
Expand Down Expand Up @@ -217,6 +221,32 @@ static void get_async_notif_value(struct thread_smc_args *args)
args->a2 |= OPTEE_SMC_ASYNC_NOTIF_PENDING;
}

static void request_system_thread_context(struct thread_smc_args *args)
{
if (IS_ENABLED(CFG_RESERVED_SYSTEM_THREAD)) {
if (thread_reserve_sys_ctx())
args->a0 = OPTEE_SMC_RETURN_ETHREAD_LIMIT;
else
args->a0 = OPTEE_SMC_RETURN_OK;

} else {
args->a0 = OPTEE_SMC_RETURN_EBADCMD;
}
}

static void release_system_thread_context(struct thread_smc_args *args)
{
if (IS_ENABLED(CFG_RESERVED_SYSTEM_THREAD)) {
if (thread_unreserve_sys_ctx())
args->a0 = OPTEE_SMC_RETURN_ETHREAD_LIMIT;
else
args->a0 = OPTEE_SMC_RETURN_OK;

} else {
args->a0 = OPTEE_SMC_RETURN_EBADCMD;
}
}

/*
* If tee_entry_fast() is overridden, it's still supposed to call this
* function.
Expand Down Expand Up @@ -291,6 +321,13 @@ void __tee_entry_fast(struct thread_smc_args *args)
args->a0 = OPTEE_SMC_RETURN_UNKNOWN_FUNCTION;
break;

case OPTEE_SMC_RESERVE_SYS_THREAD:
request_system_thread_context(args);
break;
case OPTEE_SMC_UNRESERVE_SYS_THREAD:
release_system_thread_context(args);
break;

default:
args->a0 = OPTEE_SMC_RETURN_UNKNOWN_FUNCTION;
break;
Expand Down
5 changes: 5 additions & 0 deletions mk/config.mk
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,11 @@ CFG_WITH_SOFTWARE_PRNG ?= y
# Number of threads
CFG_NUM_THREADS ?= 2

# CFG_RESERVED_SYSTEM_THREAD when enabled, allows normal world to reservation
# system thread contexts reached with a specific invocation, as needed by
# OP-TEE SCMI services.
CFG_RESERVED_SYSTEM_THREAD ?= n

# API implementation version
CFG_TEE_API_VERSION ?= GPD-1.1-dev

Expand Down