type
status
date
slug
summary
tags
category
icon
password
The code is based on: https://gitlab.com/riseproject/riscv-optee/linux/-/tree/dev-optee-mpxy
Commit ID:
df5dc01764820f113312f7a39f221b49985bbd7a
- OP-TEE provides a pseudo Trusted Application (PTA):
drivers/tee/optee/device.c
in order to support device enumeration. In other words, OP-TEE driver invokes this application to retrieve a list of Trusted Applications which can be registered as devices on the TEE bus.
do_initcalls()
optee_core_init()
optee_smc_abi_register()
optee_ffa_abi_register()
- Not supported in RISC-V.
optee_smc_abi_register()
- Register
optee_driver
.
optee_probe()
sbi_mpxy_tee_probe()
- Get invoke function.
- E.g.
optee_smccc_smc()
- P.S. SMCCC = SMC Calling Convention, defined by ARM.
optee_msg_api_uid_is_optee_api()
- Check UUID:
- Call the invoke function, e.g.
optee_smccc_smc()
with function ID:OPTEE_SMC_CALLS_UID
to retrieve the UUID. - OP-TEE will return UUID. Return
true
if the returned UUID isOPTEE_MSG_UID
(384fb3e0-e7f8-11e3-af63-0002a5d5c51b
),false
otherwise. optee_msg_get_os_revision()
- Get OS revision
- Call the invoke function, e.g.
optee_smccc_smc()
with function ID:OPTEE_SMC_CALL_GET_OS_REVISION
to get OS version. - OP-TEE will return the OS revision.
optee_msg_api_revision_is_compatible()
- Check whether the API revision is compatible:
- Call the invoke function, e.g.
optee_smccc_smc()
with function ID:OPTEE_SMC_CALLS_REVISION
to retrieve the API revision. - OP-TEE will return the API revision. Return
true
if the returned API revision isOPTEE_MSG_REVISION
(2.0
),false
otherwise. optee_msg_get_thread_count()
- Get OP-TEE thread count:
- Call the invoke function, e.g.
optee_smccc_smc()
with function ID:OPTEE_SMC_GET_THREAD_COUNT
to get the OP-TEE thread count. optee_msg_exchange_capabilities()
- Exchange capabilities between Linux and OP-TEE:
- Linux sets:
- If UP,
OPTEE_SMC_NSEC_CAP_UNIPROCESSOR
. - Call the invoke function, e.g.
optee_smccc_smc()
with function ID:OPTEE_SMC_EXCHANGE_CAPABILITIES
to exchange the capabilities between Linux and OP-TEE. - OP-TEE sets:
- If reserved shared memory is enabled in secure world:
OPTEE_ABI_SEC_CAP_HAVE_RESERVED_SHM
. - If dynamic shared memory is enabled in secure world:
OPTEE_ABI_SEC_CAP_DYNAMIC_SHM
. - If virtualization is enabled in secure world:
OPTEE_ABI_SEC_CAP_VIRTUALIZATION
. - Shared memory with a NULL reference is supported in secure world:
OPTEE_ABI_SEC_CAP_MEMREF_NULL
. - If asynchronous notification of normal world is supported in secure world:
OPTEE_ABI_SEC_CAP_ASYNC_NOTIF
. - Pre-allocating RPC arg struct is supported in secure world:
OPTEE_ABI_SEC_CAP_RPC_ARG
. - If the capabilities has
OPTEE_ABI_SEC_CAP_DYNAMIC_SHM
, create page-based allocator pool based onalloc_pages()
. - If the capabilities has
OPTEE_ABI_SEC_CAP_HAVE_RESERVED_SHM
, use the static memory pool. - Call the invoke function, e.g.
optee_smccc_smc()
with function ID:OPTEE_SMC_GET_SHM_CONFIG
to get the base address and the size of the static memory pool allocated by OP-TEE. - OP-TEE returns
default_nsec_shm_paddr
anddefault_nsec_shm_size
indicating the physical base address and the size of the non-secure shared memory initialized inteecore_init_pub_ram()
. - Non-secured shared memory is declared statically in OP-TEE:
teecore_init_pub_ram()
is declared withearly_init()
whenCFG_CORE_RESERVED_SHM
is defined.- Allocate
struct optee
: optee->ops = &optee_ops
- Call
tee_device_alloc()
to allocate a newstruct tee_device
instance for TEE client device:optee-clnt
withoptee_clnt_desc
.struct optee
is passed asdriver_data
. - The allocated TEE client device is assigned to
optee->teedev
. - Call
tee_device_alloc()
to allocate a newstruct tee_device
instance for TEE supplicant device:optee-supp
withoptee_supp_desc
.struct optee
is passed asdriver_data
. - The allocated TEE supplicant device is assigned to
optee->supp_teedev
. - Call
tee_device_register()
to registeroptee-clnt
device. - Call
tee_device_register()
to registeroptee-supp
device. - Call
platform_set_drvdata()
to setoptee
device driver data to:struct optee
. - Open OP-TEE client device (
optee->teedev
): tee_open()
- This will call
optee_clnt_desc->open()
: optee_smc_open()
- If
OPTEE_SMC_SEC_CAP_ASYNC_NOTIF
capability is supported: - Call
platform_get_irq()
to get (non-secure) IRQ 0. - Call
optee_smc_notif_init_irq()
to request IRQ. - If
irq_is_percpu_devid()
: init_pcpu_irq()
request_percpu_irq()
- IRQ handler:
notif_pcpu_irq_handler()
irq_handler()
- Init work:
optee->smc.notif_pcpu_work
: - Handler:
notif_pcpu_irq_work_fn()
- Create work queue:
optee->smc.notif_pcpu_wq
optee->smc.notif_pcpu_work
is queued intooptee->scm.notif_pcpu_wq
innotif_pcpu_irq_handler()
- Otherwise:
init_irq()
request_threaded_irq()
- IRQ handler:
notif_irq_handler()
irq_handler()
- Handler thread:
notif_irq_thread_fn()
- Call
optee_enumerate_devices()
to enumerate PTA devices (PTA_CMD_GET_DEVICES
).
optee_smccc_smc()
- Call
sbi_mpxy_send_message_withresp()
to message ID:OPTEED_MSG_COMMUNICATE
(0x01
).
optee_open()
- Allocate
struct optee_context_data
. - If
tee_device
is a OP-TEE supplicant devices: - If
!optee->scan_bus_done
: - Initialize
scan_bus_work
work queue. - Handler:
optee_bus_scan()
- Schedule the work queue.
- Set
optee->scan_bus_done
totrue
. - Assign the allocated
struct optee_context_data
totee_context->data
.
optee_open_session()
- Call
optee_get_msg_arg()
to allocate the shared memory for the message arg (struct optee_msg_arg
). - Call
tee_session_calc_client_uuid()
to create Linux environment client UUID from the session arg. - Initialize message arg and add the meta parameters needed when opening a session with the session arg and Linux environment client UUID.
- Call
optee->ops->to_msg_param()
to convert the passed-instruct tee_param
toOPTEE_MSG
parameters (struct optee_msg_arg
). Save the convertedOPTEE_MSG
parameters to message arg. - E.g.
optee_to_msg_param()
- Call
optee->ops->do_call_with_arg()
with the command:OPTEE_MSG_CMD_OPEN_SESSION
to enter OP-TEE in secure world with the message arg. - E.g.
optee_smc_do_call_with_arg()
- OP-TEE will call
entry_open_session()
to open the session based on the UUID (tee_ioctl_open_session_arg->uuid
, e.g. PTA UUID) passed from Linux. The UUID is used to look up or initialize the TA with the same UUID. The opened session ID is saved to the message arg and passed back to Linux. - If
optee_smc_do_call_with_arg()
returns success, add the session to the sessions list (ctxdata->sess_list
) - Call
optee->ops->from_msg_param()
to convert the returned sessionOPTEE_MSG
parameters tostruct tee_param
. Update the results to session arg. - E.g.
optee_from_msg_param()
- Store the open session ID to
struct tee_ioctl_open_session_arg->session
. - If conversion fails, call
optee_close_session()
to close the session. - Call
optee_free_msg_arg()
to free the allocated message arg.
optee_smc_do_call_with_arg()
- Initialize RPC parameter (
struct optee_rpc_param
) from the message arg. - message arg is stored in the shared memory indicated by
shm
andoffs
. - Call
optee->smc.invoke_fn()
to enter OP-TEE in secure world with the message arg. - E.g.
optee_smccc_smc()
- P.S. SMCCC = SMC Calling Convention, defined by ARM.
- If SMC return is a RPC:
- Call
optee_handle_rpc()
to handle the RPC from OP-TEE.
optee_invoke_func()
- Similar flow to
optee_open_session()
. Except callingoptee->ops->do_call_with_arg()
with the command:OPTEE_MSG_CMD_INVOKE_COMMAND
to enter OP-TEE in secure world with the message arg. - E.g.
optee_smc_do_call_with_arg()
- OP-TEE will call
entry_invoke_command()
to invoke the command based on the passed-in function (e.g.PTA_CMD_GET_DEVICES
) in the opened session. - E.g. If the opened session is registered for pseudo TA (OP-TEE:
core/pta/pseudo_ta.c
). Theenter_invoke_cmd()
callback in OP-TEE is:pseudo_ta_enter_invoke_cmd()
.
optee_bus_scan()
- Call
optee_enumerate_devices()
to enumerate PTA supplicant devices (PTA_CMD_GET_DEVICES_SUPP
).
optee_do_bottom_half()
- Call
optee->ops->do_call_with_arg()
with the command:OPTEE_MSG_CMD_DO_BOTTOM_HALF
to schedule bottom half processing of a driver.
optee_enumerate_devices()
- Enumerate the TA devices.
__optee_enumerate_devices()
- PTA UUID:
- Call
tee_client_open_context()
to open TEE devices with OP-TEE driver. - Prepare session arg (
struct tee_ioctl_open_session_arg
). - Call
tee_client_open_session()
to open session with device enumeration pseudo Trusted Application with the session arg using PTA UUID. - Call
get_devices()
to get the required shared memory size for UUIDs of pseudo TAs. - Shared memory size = sizeof(UUID) * Number of pseudo TAs
- After retrieving the shared memory size, call
tee_shm_alloc_kernel_buf()
to allocate the shared memory buffer for UUIDs of pseudo TAs. - Call
get_devices()
again to get the UUIDs of pseudo TAs. The UUIDs of psuedo TA are stored in the allocated shared memory by OP-TEE. - For each pseudo TA, call
optee_register_device()
to register the device to TEE bus (tee_bus_type
). - This will eventually call
tee_client_device_match()
to match the driver for the TA device based on UUID. Driverโsprobe()
callback will be called if matches. - For example:
optee-rngs
driver (drivers/char/hw_random/optee-rngs.c
). - OP-TEE driver is implemented as a module and is initialized through
module_init()
. OP-TEE driver sets itsbus
totee_bus_type
.
get_devices()
- Call
tee_client_invoke_func()
to invoke the function (e.g.PTA_CMD_GET_DEVICES
to get the pseudo TAs) to OP-TEE.
optee_handle_rpc()
- If
optee_rpc_param->a0
: OPTEE_SMC_RPC_FUNC_CMD
function:- Get
arg
(struct optee_msg_arg) from the shared memory. handle_rpc_func_cmd()
witharg
.- โฆ
- Set
param->a0
toOPTEE_SMC_CALL_RETURN_FROM_RPC
to indicate that we have successfully handled the RPC.
handle_rpc_func_cmd()
- If
arg->cmd
: OPTEE_RPC_CMD_SHM_ALLOC
:handle_rpc_func_cmd_shm_alloc()
OPTEE_RPC_CMD_SHM_FREE
:handle_rpc_func_cmd_shm_free()
- Otherwise:
optee_rpc_cmd()
- If
arg->cmd
: OPTEE_RPC_CMD_GET_TIME
:handle_rpc_func_cmd_get_time()
- โฆ
- Otherwise:
handle_rpc_supp_cmd()
optee_supp_thrd_req()
- Insert the RPC request into the request list (
supp->reqs
). - Call
complete(
&supp->reqs_c
)
to tell an eventual waiter there's a new request. - This will unblock TEE supplicant: Otherwise, call
wait_for_completion_interruptible(
&supp->reqs_c
)
to wait for the new request from OP-TEE. - Call
wait_for_completion_interruptible(
&req->c
)
to wait for TEE supplicant to process. - This will be blocked until TEE supplicant handles the RPC.
- Return the result.
optee_supp_recv()
- TEE supplicant waits for OP-TEEโs request- Check if we could pop the entry from the request list (
supp->reqs
). If yes, extract the RPC parameters. - Otherwise, call
wait_for_completion_interruptible(
&supp->reqs_c
)
to wait for the new request from OP-TEE.
optee_supp_send()
- TEE supplicant handles the RPC and sends the response to OP-TEE.- Set the return parameters (
struct tee_param
). - Call
complete(
&req->c
)
to unblock Callwait_for_completion_interruptible(
&req->c
)
to wait for TEE supplicant to process.