Linux Kernel: OP-TEE
2024-10-19
| 2025-1-12
ๆœฌๆ–‡ๅญ—ๆ•ธย 1718้–ฑ่ฎ€ๆ™‚้•ทโ‰ˆย 5ย ๅˆ†้˜
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.



  • optee_smc_abi_register()
  • optee_probe()
    • sbi_mpxy_tee_probe()
    • Get invoke function.
    • 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 is OPTEE_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 is OPTEE_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 on alloc_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 and default_nsec_shm_size indicating the physical base address and the size of the non-secure shared memory initialized in teecore_init_pub_ram().
          • Non-secured shared memory is declared statically in OP-TEE:
            • teecore_init_pub_ram() is declared with early_init() when CFG_CORE_RESERVED_SHM is defined.
      • Allocate struct optee:
        • optee->ops = &optee_ops
      • Call tee_device_alloc() to allocate a new struct tee_device instance for TEE client device: optee-clnt with optee_clnt_desc. struct optee is passed as driver_data.
        • The allocated TEE client device is assigned to optee->teedev.
      • Call tee_device_alloc() to allocate a new struct tee_device instance for TEE supplicant device: optee-supp with optee_supp_desc. struct optee is passed as driver_data.
        • The allocated TEE supplicant device is assigned to optee->supp_teedev.
      • Call tee_device_register() to register optee-clnt device.
      • Call tee_device_register() to register optee-supp device.
      • Call platform_set_drvdata() to set optee device driver data to: struct optee.
      • Open OP-TEE client device (optee->teedev):
      • 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:
              • Create work queue: optee->smc.notif_pcpu_wq
                • optee->smc.notif_pcpu_work is queued into optee->scm.notif_pcpu_wq in notif_pcpu_irq_handler()
          • Otherwise:
            • init_irq()
              • request_threaded_irq()
        • Call optee_enumerate_devices() to enumerate PTA devices (PTA_CMD_GET_DEVICES).
    • 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.
          • Schedule the work queue.
          • Set optee->scan_bus_done to true.
      • Assign the allocated struct optee_context_data to tee_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-in struct tee_param to OPTEE_MSG parameters (struct optee_msg_arg). Save the converted OPTEE_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 session OPTEE_MSG parameters to struct 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 and offs.
      • Call optee->smc.invoke_fn() to enter OP-TEE in secure world with the message arg.
      • If SMC return is a RPC:
    • optee_invoke_func()
      • Similar flow to optee_open_session(). Except calling optee->ops->do_call_with_arg() with the command: OPTEE_MSG_CMD_INVOKE_COMMAND to enter OP-TEE in secure world with the message arg.
    • 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โ€™s probe() 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 its bus to tee_bus_type.
      • get_devices()

      • optee_handle_rpc()
        • If optee_rpc_param->a0:
          • OPTEE_SMC_RPC_FUNC_CMD function:
          • โ€ฆ
        • Set param->a0 to OPTEE_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_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.
    • Linux Kernel
    • OP-TEE
    • Linux Kernel: OP-TEE SupplicantLinux Kernel: TEE
      Loading...
      ็›ฎ้Œ„