diff options
| author | GuEe-GUI <[email protected]> | 2026-06-14 11:42:01 +0800 |
|---|---|---|
| committer | Rbb666 <[email protected]> | 2026-07-27 17:53:20 +0800 |
| commit | a7b0182817059620738779b30f43eaeca10627ff (patch) | |
| tree | 585617e46cf93d1fd1e168c02bd7ba02b5b4f80c /documentation | |
| parent | b26ae7d8d9417c073999867187056f506086f256 (diff) | |
[dm][tee] support Trusted Execution Environment (TEE)
Signed-off-by: GuEe-GUI <[email protected]>
Diffstat (limited to 'documentation')
| -rw-r--r-- | documentation/6.components/device-driver/INDEX.md | 1 | ||||
| -rw-r--r-- | documentation/6.components/device-driver/tee/tee.md | 373 |
2 files changed, 374 insertions, 0 deletions
diff --git a/documentation/6.components/device-driver/INDEX.md b/documentation/6.components/device-driver/INDEX.md index 56f005df1a..19b2262210 100644 --- a/documentation/6.components/device-driver/INDEX.md +++ b/documentation/6.components/device-driver/INDEX.md @@ -18,6 +18,7 @@ - @subpage page_device_clock_time - @subpage page_device_hwtimer - @subpage page_device_ptp +- @subpage page_device_tee - @subpage page_device_watchdog - @subpage page_device_wlan - @subpage page_device_sensor diff --git a/documentation/6.components/device-driver/tee/tee.md b/documentation/6.components/device-driver/tee/tee.md new file mode 100644 index 0000000000..1a24837e4c --- /dev/null +++ b/documentation/6.components/device-driver/tee/tee.md @@ -0,0 +1,373 @@ +@page page_device_tee TEE subsystem + +# TEE subsystem (API) + +Header: **`components/drivers/include/drivers/tee.h`**. Core: **`components/drivers/tee/tee.c`**. + +The TEE layer provides a **GlobalPlatform-style** client API for talking to a **Trusted Execution Environment** from the normal world. Hardware or firmware backends implement **`rt_tee_ops`** (version query, session management, command invocation, shared memory registration). The core serializes access with a per-device mutex, tracks registered shared-memory objects, and registers a searchable device node (`tee0`, `tee1`, …). + +An **OP-TEE** backend lives under **`components/drivers/tee/optee/`** and is selected when **`RT_TEE_OPTEE`** is enabled. + +Requires **`RT_USING_TEE`** (Kconfig) and **`RT_USING_DM`**. + +--- + +## When to use this API + +| Use TEE when… | Prefer something else when… | +| --- | --- | +| The platform runs **OP-TEE** (or another GP-compliant TEE) and exposes **Trusted Applications (TAs)**. | Secrets can stay in normal-world software with adequate isolation — no TEE required. | +| You need **crypto, secure storage, or RNG** inside a TA with a defined UUID/command model. | Single-purpose on-chip crypto — a dedicated **hwcrypto** driver may suffice. | +| Firmware provides a DT node such as **`linaro,optee-tz`**. | No secure world firmware — enable and port a TEE OS first. | + +--- + +## Enable in menuconfig + +```text +RT-Thread Components → + Device Drivers → + [*] Using Trusted Execution Environment (TEE) support + [*] OP-TEE # ARCH_ARM_CORTEX_A || ARCH_ARMV8 + [*] Examples # FinSH command optee_example_rng +``` + +SoC-specific backends may be added under **`$(SOC_DM_TEE_DIR)/Kconfig`** when present. + +--- + +## Roles + +| Role | Responsibility | +| --- | --- | +| **TEE backend driver** | Embeds **`struct rt_tee_device`**, fills **`rt_tee_ops`**, calls **`rt_tee_device_register`**. Translates GP requests to SMC/HVC, mailbox, or other transport. | +| **Client / TA user** | Finds **`tee0`**, opens a session to a TA UUID, registers shared memory, invokes TA commands. Checks both **`rt_err_t`** (transport) and **`arg->ret`** (TEE status). | + +The core registers each device as **`RT_Device_Class_Char`** with **`RT_DEVICE_FLAG_DEACTIVATE`**. There is **no** generic `read`/`write`/`control` path in **`tee.c`** — use the **`rt_tee_*`** helpers. + +All public entry points take **`tdev->mutex`** except registration helpers. + +--- + +## Data structures + +### Device object + +```c +struct rt_tee_device { + struct rt_device parent; + rt_list_t shm_nodes; /* registered struct rt_tee_shm */ + const struct rt_tee_ops *ops; + struct rt_mutex mutex; +}; + +#define rt_device_to_tee_device(dev) rt_container_of(dev, struct rt_tee_device, parent) +``` + +### Version and capabilities + +```c +struct rt_tee_version { + rt_uint32_t impl_id; /* TEE_IMPL_ID_OPTEE, TEE_IMPL_ID_AMDTEE, … */ + rt_uint32_t impl_caps; /* e.g. TEE_OPTEE_CAP_TZ */ + rt_uint32_t gen_caps; /* TEE_GEN_CAP_GP, TEE_GEN_CAP_REG_MEM, … */ +}; +``` + +### Parameters (open session / invoke) + +Parameters follow GlobalPlatform attribute encoding: + +| `attr` | Meaning | +| --- | --- | +| `TEE_PARAM_ATTR_TYPE_NONE` | Unused slot | +| `TEE_PARAM_ATTR_TYPE_VALUE_IN/OUT/IN_OUT` | Three 64-bit values in **`value.a/b/c`** | +| `TEE_PARAM_ATTR_TYPE_MEMREF_IN/OUT/IN_OUT` | Shared memory reference via **`memref.shm`**, offset, size | + +Flexible arrays append parameters to the end of open/invoke argument blocks: + +```c +struct rt_tee_open_session_arg { + rt_uint8_t uuid[16]; + rt_uint8_t clnt_uuid[16]; + rt_uint32_t clnt_login; + rt_uint32_t cancel_id; + rt_uint32_t session; /* output: session id from TEE */ + rt_uint32_t ret; /* output: TEE result code */ + rt_uint32_t ret_origin; /* output: RT_TEE_ORIGIN_* */ + rt_uint32_t params_nr; + struct rt_tee_param params[]; +}; + +struct rt_tee_invoke_arg { + rt_uint32_t func; + rt_uint32_t session; + rt_uint32_t cancel_id; + rt_uint32_t ret; + rt_uint32_t ret_origin; + rt_uint32_t params_nr; + struct rt_tee_param params[]; +}; +``` + +### Shared memory + +```c +struct rt_tee_shm { + rt_list_t list; + struct rt_tee_device *tdev; + void *vaddr; + rt_ubase_t paddr; + rt_size_t size; + rt_size_t align; + rt_uint32_t flags; /* TEE_SHM_DRV | TEE_SHM_SYS | TEE_SHM_USER */ +}; +``` + +| Flag | Usage | +| --- | --- | +| **`TEE_SHM_SYS`** | Core allocates backing store in **`rt_tee_shm_register`** via **`rt_tee_shm_alloc_helper`** (page allocator or aligned malloc). Caller must **`rt_tee_shm_free_helper`** after unregister. | +| **`TEE_SHM_USER`** | Caller supplies **`vaddr`** (and optionally **`paddr`**); core resolves physical address when missing. | +| **`TEE_SHM_DRV`** | Driver-owned buffer; registration requires **`vaddr`** already set. | + +### Driver operations + +```c +struct rt_tee_ops { + rt_err_t (*get_version)(struct rt_tee_device *tdev, struct rt_tee_version *out_version); + rt_err_t (*open_session)(struct rt_tee_device *tdev, struct rt_tee_open_session_arg *arg); + rt_err_t (*close_session)(struct rt_tee_device *tdev, rt_uint32_t session); + rt_err_t (*invoke)(struct rt_tee_device *tdev, struct rt_tee_invoke_arg *arg); + rt_err_t (*cancel)(struct rt_tee_device *tdev, rt_uint32_t session, rt_uint32_t cancel_id); + rt_err_t (*shm_register)(struct rt_tee_device *tdev, struct rt_tee_shm *shm); + rt_err_t (*shm_unregister)(struct rt_tee_device *tdev, struct rt_tee_shm *shm); +}; +``` + +--- + +## TEE result codes + +Transport failures use **`rt_err_t`** (`-RT_EINVAL`, `-RT_ENOMEM`, `-RT_ENOSYS`, …). TEE-side status is returned in **`arg->ret`** with origin **`arg->ret_origin`**: + +| Code | Macro | +| --- | --- | +| Success | **`RT_TEE_SUCCESS`** (`0`) | +| Bad parameters | **`RT_TEE_ERROR_BAD_PARAMETERS`** | +| Not supported | **`RT_TEE_ERROR_NOT_SUPPORTED`** | +| Out of memory | **`RT_TEE_ERROR_OUT_OF_MEMORY`** | +| Communication error | **`RT_TEE_ERROR_COMMUNICATION`** | +| … | See **`tee.h`** for the full list | + +Origins: **`RT_TEE_ORIGIN_COMMS`**, **`RT_TEE_ORIGIN_TEE`**, **`RT_TEE_ORIGIN_TRUSTED_APP`**. + +Always check **both** the function return value and **`arg->ret`** on open/invoke. + +--- + +## Register and unregister + +```c +rt_err_t rt_tee_device_register(struct rt_tee_device *tdev); +rt_err_t rt_tee_device_unregister(struct rt_tee_device *tdev); +``` + +**`rt_tee_device_register`** requires **`tdev`** and **`tdev->ops`**. Device name is **`tee<N>`**. + +**`rt_tee_device_unregister`**: + +- Fails if **`ref_count != 0`** +- Warns and force-unregisters SHM entries if any remain on **`shm_nodes`** +- Detaches **`mutex`** after successful device unregister + +--- + +## Session and command API + +```c +rt_err_t rt_tee_get_version(struct rt_tee_device *tdev, struct rt_tee_version *out_version); + +rt_err_t rt_tee_open_session(struct rt_tee_device *tdev, struct rt_tee_open_session_arg *arg); +rt_err_t rt_tee_close_session(struct rt_tee_device *tdev, rt_uint32_t session); + +rt_err_t rt_tee_invoke(struct rt_tee_device *tdev, struct rt_tee_invoke_arg *arg); +rt_err_t rt_tee_cancel(struct rt_tee_device *tdev, rt_uint32_t session, rt_uint32_t cancel_id); +``` + +Typical flow: + +1. Allocate **`open_session_arg`** with room for trailing **`params[]`** +2. Fill **`uuid`** (TA) and optional client identity fields +3. **`rt_tee_open_session`** — on success read **`arg->session`**, verify **`arg->ret == RT_TEE_SUCCESS`** +4. Register SHM if the TA command needs buffers +5. Allocate **`invoke_arg`**, set **`func`**, **`session`**, parameters +6. **`rt_tee_invoke`** — verify **`arg->ret`** +7. **`rt_tee_close_session`**, unregister/free SHM, close device + +--- + +## Shared memory helpers + +```c +rt_err_t rt_tee_shm_alloc_helper(struct rt_tee_shm *shm); +rt_err_t rt_tee_shm_free_helper(struct rt_tee_shm *shm); +rt_err_t rt_tee_shm_register(struct rt_tee_device *tdev, struct rt_tee_shm *shm); +rt_err_t rt_tee_shm_unregister(struct rt_tee_device *tdev, struct rt_tee_shm *shm); +``` + +**`rt_tee_shm_register`**: + +- For **`TEE_SHM_SYS`** (without **`TEE_SHM_DRV`**): allocates memory, then calls **`ops->shm_register`** +- On backend failure after allocation, frees the buffer automatically +- On success, links **`shm`** into **`tdev->shm_nodes`** + +**`rt_tee_shm_unregister`** removes the node and calls **`ops->shm_unregister`**. Call **`rt_tee_shm_free_helper`** afterward when the core allocated the buffer. + +--- + +## OP-TEE backend + +**Files:** `components/drivers/tee/optee/optee.c`, `optee_msg.h`, `optee_smc.h` + +**Platform driver:** `optee-smc`, compatible **`linaro,optee-tz`** + +### Device tree + +| Property | Values | Notes | +| --- | --- | --- | +| **`compatible`** | `"linaro,optee-tz"` | Required | +| **`method`** | `"smc"` (default) or `"hvc"` | Selects **`arm_smccc_smc`** vs **`arm_smccc_hvc`** | + +### Probe requirements + +At probe time the driver: + +1. Selects SMC or HVC call path from **`method`** +2. Verifies OP-TEE UID via **`OPTEE_SMC_CALLS_UID`** +3. Requires **`OPTEE_SMC_SEC_CAP_DYNAMIC_SHM`** from capability exchange +4. Registers **`struct rt_tee_device`** with **`optee_ops`** + +### Normal-world supplicant (RPC) + +**`call_with_arg`** handles OP-TEE **RPC** returns while a secure-world call is in flight: + +| RPC function | Handled behavior | +| --- | --- | +| **`OPTEE_SMC_RPC_FUNC_ALLOC` / `FREE`** | Temporary RPC buffer alloc/free | +| **`OPTEE_SMC_RPC_FUNC_CMD`** | **`supplicant_cmd`**: SHM alloc/free, optional **I2C transfer** (`RT_USING_I2C`) | +| **`OPTEE_SMC_RPC_FUNC_FOREIGN_INTR`** | No-op placeholder | + +Shared memory passed to the secure world uses **non-contiguous page lists** built from physically contiguous normal-world pages. + +### FinSH example + +With **`RT_TEE_OPTEE_EXAMPLES`**, build OP-TEE for your platform with the **hwrng** TA (see [OP-TEE QEMU documentation](https://optee.readthedocs.io/en/latest/building/devices/qemu.html#qemu-v8)), then run: + +```text +optee_example_rng +``` + +The example opens **`tee0`**, opens a session to the hwrng TA UUID, registers a 32-byte output buffer, invokes command **`0x0` (GET_ENTROPY)**, and prints the result. + +Source: **`components/drivers/tee/optee/optee_examples.c`** + +--- + +## Example (client sketch) + +```c +#include <drivers/tee.h> + +static struct rt_tee_open_session_arg *open_arg_alloc(rt_uint32_t n) +{ + return rt_calloc(1, sizeof(struct rt_tee_open_session_arg) + + sizeof(struct rt_tee_param) * n); +} + +static void uuid_to_bytes(rt_uint8_t *d, struct rt_tee_uuid *s) +{ + d[0] = s->time_low >> 24; + d[1] = s->time_low >> 16; + d[2] = s->time_low >> 8; + d[3] = s->time_low; + d[4] = s->time_mid >> 8; + d[5] = s->time_mid; + d[6] = s->time_hi_and_version >> 8; + d[7] = s->time_hi_and_version; + rt_memcpy(d + 8, s->clock_seq_and_node, 8); +} + +static int ta_example(struct rt_tee_device *tdev) +{ + rt_err_t err; + struct rt_tee_shm shm = { .size = 64, .align = RT_MM_PAGE_SIZE, .flags = TEE_SHM_SYS }; + struct rt_tee_open_session_arg *open; + struct rt_tee_invoke_arg *invoke; + struct rt_tee_uuid ta_uuid = { /* TA UUID fields */ }; + + open = open_arg_alloc(0); + uuid_to_bytes(open->uuid, &ta_uuid); + + if ((err = rt_tee_open_session(tdev, open)) != RT_EOK || open->ret != RT_TEE_SUCCESS) + goto out; + + if ((err = rt_tee_shm_register(tdev, &shm)) != RT_EOK) + goto close; + + invoke = rt_calloc(1, sizeof(*invoke) + sizeof(struct rt_tee_param)); + invoke->func = 1; + invoke->session = open->session; + invoke->params_nr = 1; + invoke->params[0].attr = TEE_PARAM_ATTR_TYPE_MEMREF_OUT; + invoke->params[0].memref.shm = &shm; + invoke->params[0].memref.size = shm.size; + + err = rt_tee_invoke(tdev, invoke); + if (err == RT_EOK && invoke->ret == RT_TEE_SUCCESS) { + /* use shm.vaddr */ + } + + rt_free(invoke); + rt_tee_shm_unregister(tdev, &shm); + rt_tee_shm_free_helper(&shm); + +close: + rt_tee_close_session(tdev, open->session); +out: + rt_free(open); + return (int)err; +} +``` + +### Device lookup + +```c +struct rt_device *dev = rt_device_find("tee0"); +struct rt_tee_device *tdev; + +if (dev && rt_device_open(dev, 0) == RT_EOK) + tdev = rt_device_to_tee_device(dev); +``` + +--- + +## Pitfalls + +| Issue | Mitigation | +| --- | --- | +| **Ignoring `arg->ret`** | **`RT_EOK`** from **`rt_tee_invoke`** only means the message reached the backend; check **`invoke->ret`**. | +| **SHM leak** | Always **`rt_tee_shm_unregister`** then **`rt_tee_shm_free_helper`** for **`TEE_SHM_SYS`** buffers. | +| **Unregister with active refs** | **`rt_tee_device_unregister`** fails when **`ref_count != 0`** — close clients first. | +| **OP-TEE without dynamic SHM** | Probe fails if **`OPTEE_SMC_SEC_CAP_DYNAMIC_SHM`** is absent — use a matching OP-TEE build. | +| **Missing TA** | **`RT_TEE_ERROR_ITEM_NOT_FOUND`** / session open failure — deploy the TA binary to OP-TEE. | +| **I2C RPC** | OP-TEE I2C supplicant calls require **`RT_USING_I2C`** and a bus named **`i2c<N>`**. | +| **Cache coherency** | OP-TEE backend flushes/invalidates caches around shared message buffers; user SHM must remain in memory visible to both worlds. | + +--- + +## See also + +- [OP-TEE documentation](https://optee.readthedocs.io/) +- `components/drivers/tee/tee.c` +- `components/drivers/tee/optee/optee.c` +- `components/drivers/include/drivers/tee.h` |
