⚠️ 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.cin 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()Get invoke function.
- E.g.
optee_smccc_smc()- P.S. SMCCC = SMC Calling Convention, defined by ARM.
- E.g.
optee_msg_api_uid_is_optee_api()- Check UUID:
- Call the invoke function, e.g.
optee_smccc_smc()with function ID:OPTEE_SMC_CALLS_UIDto retrieve the UUID.- OP-TEE will return UUID. Return
trueif the returned UUID isOPTEE_MSG_UID(384fb3e0-e7f8-11e3-af63-0002a5d5c51b),falseotherwise.
- OP-TEE will return UUID. Return
- Call the invoke function, e.g.
- Check UUID:
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_REVISIONto get OS version.- OP-TEE will return the OS revision.
- Call the invoke function, e.g.
- Get 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_REVISIONto retrieve the API revision.- OP-TEE will return the API revision. Return
trueif the returned API revision isOPTEE_MSG_REVISION(2.0),falseotherwise.
- OP-TEE will return the API revision. Return
- Call the invoke function, e.g.
- Check whether the API revision is compatible:
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_COUNTto get the OP-TEE thread count.
- Call the invoke function, e.g.
- Get OP-TEE thread count:
optee_msg_exchange_capabilities()- Exchange capabilities between Linux and OP-TEE:
- Linux sets:
- If UP,
OPTEE_SMC_NSEC_CAP_UNIPROCESSOR.
- If UP,
- Call the invoke function, e.g.
optee_smccc_smc()with function ID:OPTEE_SMC_EXCHANGE_CAPABILITIESto 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 reserved shared memory is enabled in secure world:
- Linux sets:
- Exchange capabilities between Linux and OP-TEE:
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_CONFIGto get the base address and the size of the static memory pool allocated by OP-TEE.- OP-TEE returns
default_nsec_shm_paddranddefault_nsec_shm_sizeindicating 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_SHMis defined.
- OP-TEE returns
- Call the invoke function, e.g.
Allocate
struct optee:optee->ops = &optee_ops
Call
tee_device_alloc()to allocate a newstruct tee_deviceinstance for TEE client device:optee-clntwithoptee_clnt_desc.struct opteeis passed asdriver_data.1 2 3 4 5 6 7 8 9 10 11 12 13 14// drivers/tee/optee/smc_abi.c static const struct tee_driver_ops optee_clnt_ops = { .get_version = optee_get_version, .open = optee_smc_open, .release = optee_release, .open_session = optee_open_session, .close_session = optee_close_session, .system_session = optee_system_session, .invoke_func = optee_invoke_func, .cancel_req = optee_cancel_req, .shm_register = optee_shm_register, .shm_unregister = optee_shm_unregister, };- The allocated TEE client device is assigned to
optee->teedev.
- The allocated TEE client device is assigned to
Call
tee_device_alloc()to allocate a newstruct tee_deviceinstance for TEE supplicant device:optee-suppwithoptee_supp_desc.struct opteeis passed asdriver_data.1 2 3 4 5 6 7 8 9 10 11// drivers/tee/optee/smc_abi.c static const struct tee_desc optee_supp_desc = { .name = DRIVER_NAME "-supp", .ops = &optee_supp_ops, .owner = THIS_MODULE, // If TEE_DESC_PRIVILEGED is set, the name of // tee_device will be: teeprivX. // Otherwise, the name of tee_device will be: teeX. .flags = TEE_DESC_PRIVILEGED, };1 2 3 4 5 6 7 8 9 10 11// drivers/tee/optee/smc_abi.c static const struct tee_driver_ops optee_supp_ops = { .get_version = optee_get_version, .open = optee_smc_open, .release = optee_release_supp, .supp_recv = optee_supp_recv, .supp_send = optee_supp_send, .shm_register = optee_shm_register_supp, .shm_unregister = optee_shm_unregister_supp, };- The allocated TEE supplicant device is assigned to
optee->supp_teedev.
- The allocated TEE supplicant device is assigned to
Call
tee_device_register()to registeroptee-clntdevice.Call
tee_device_register()to registeroptee-suppdevice.Call
platform_set_drvdata()to setopteedevice driver data to:struct optee.Open OP-TEE client device (
optee->teedev):tee_open()- This will call
optee_clnt_desc->open():-
optee_smc_open()
-
- This will call
If
OPTEE_SMC_SEC_CAP_ASYNC_NOTIFcapability 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()
- IRQ handler:
- Init work:
optee->smc.notif_pcpu_work:- Handler:
notif_pcpu_irq_work_fn()
- Handler:
- Create work queue:
optee->smc.notif_pcpu_wqoptee->smc.notif_pcpu_workis queued intooptee->scm.notif_pcpu_wqinnotif_pcpu_irq_handler()
- Otherwise:
init_irq()request_threaded_irq()- IRQ handler:
notif_irq_handler()irq_handler()
- Handler thread:
notif_irq_thread_fn()
- IRQ handler:
- If
- Call
optee_enumerate_devices()to enumerate PTA devices (PTA_CMD_GET_DEVICES).
- Call
optee_smccc_smc()- Call
sbi_mpxy_send_message_withresp()to message ID:OPTEED_MSG_COMMUNICATE(0x01).
- Call
optee_open()- Allocate
struct optee_context_data. - If
tee_deviceis a OP-TEE supplicant devices:- If
!optee->scan_bus_done:- Initialize
scan_bus_workwork queue. - Handler:
optee_bus_scan() - Schedule the work queue.
- Set
optee->scan_bus_donetotrue.
- Initialize
- If
- Assign the allocated
struct optee_context_datatotee_context->data.
- Allocate
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_paramtoOPTEE_MSGparameters (struct optee_msg_arg). Save the convertedOPTEE_MSGparameters to message arg.- E.g.
optee_to_msg_param()
- E.g.
Call
optee->ops->do_call_with_arg()with the command:OPTEE_MSG_CMD_OPEN_SESSIONto 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.
- E.g.
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_MSGparameters 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.
- Store the open session ID to
- If conversion fails, call
optee_close_session()to close the session.
- E.g.
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
shmandoffs.
- message arg is stored in the shared memory indicated by
- Call
optee->smc.invoke_fn()to enter OP-TEE in secure world with the message arg. - If SMC return is a RPC:
- Call
optee_handle_rpc()to handle the RPC from OP-TEE.
- Call
- Initialize RPC parameter (
optee_invoke_func()- Similar flow to
optee_open_session(). Except callingoptee->ops->do_call_with_arg()with the command:OPTEE_MSG_CMD_INVOKE_COMMANDto 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().
- E.g. If the opened session is registered for pseudo TA (OP-TEE:
- E.g.
- Similar flow to
optee_bus_scan()- Call
optee_enumerate_devices()to enumerate PTA supplicant devices (PTA_CMD_GET_DEVICES_SUPP).
- Call
optee_do_bottom_half()- Call
optee->ops->do_call_with_arg()with the command:OPTEE_MSG_CMD_DO_BOTTOM_HALFto schedule bottom half processing of a driver.
- Call
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-rngsdriver (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 itsbustotee_bus_type.
- OP-TEE driver is implemented as a module and is initialized through
- This will eventually call
get_devices()- Call
tee_client_invoke_func()to invoke the function (e.g.PTA_CMD_GET_DEVICESto get the pseudo TAs) to OP-TEE.
- Call
-
optee_handle_rpc()- If
optee_rpc_param->a0:OPTEE_SMC_RPC_FUNC_CMDfunction:- Get
arg(struct optee_msg_arg) from the shared memory. handle_rpc_func_cmd()witharg.
- Get
- …
- Set
param->a0toOPTEE_SMC_CALL_RETURN_FROM_RPCto indicate that we have successfully handled the RPC.
- If
-
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:
- If
-
optee_rpc_cmd()- If
arg->cmd:OPTEE_RPC_CMD_GET_TIME:handle_rpc_func_cmd_get_time()
- …
- Otherwise:
- If
-
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.
- This will unblock TEE supplicant: Otherwise, call
- 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.
- Insert the RPC request into the request list (
-
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.
- Check if we could pop the entry from the request list (
-
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.
- Set the return parameters (