Hi Amir,
On Tue, Oct 6, 2026 at 2:40 AM Amirreza Zarrabi amirreza.zarrabi@oss.qualcomm.com wrote:
Define the OP-TEE service protocol carried by RPMI TEE_CALL payloads. Add operations for version and capability queries, shared-memory unregistration, asynchronous notification enablement, and yielding-call start and resume. Use fixed-width little-endian control fields and RPMI error codes for control responses.
Add a parcel memory-reference layout to the common message parameter union. Identify shared memory by its parcel ID and nonce, with a 64-bit byte offset and size, without changing the message parameter size. Encode NULL references with a zero parcel ID and nonce, leaving parcel ID zero usable with a nonzero nonce.
Document the wire layouts and ownership rules for matching Linux and OP-TEE implementations.
Signed-off-by: Amirreza Zarrabi amirreza.zarrabi@oss.qualcomm.com
drivers/tee/optee/optee_msg.h | 38 +++++-- drivers/tee/optee/optee_rpmi.h | 234 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 264 insertions(+), 8 deletions(-)
diff --git a/drivers/tee/optee/optee_msg.h b/drivers/tee/optee/optee_msg.h index 6c3043f8da33..028f8cd95fdd 100644 --- a/drivers/tee/optee/optee_msg.h +++ b/drivers/tee/optee/optee_msg.h @@ -31,6 +31,9 @@ #define OPTEE_MSG_ATTR_TYPE_FMEM_INPUT OPTEE_MSG_ATTR_TYPE_RMEM_INPUT #define OPTEE_MSG_ATTR_TYPE_FMEM_OUTPUT OPTEE_MSG_ATTR_TYPE_RMEM_OUTPUT #define OPTEE_MSG_ATTR_TYPE_FMEM_INOUT OPTEE_MSG_ATTR_TYPE_RMEM_INOUT +#define OPTEE_MSG_ATTR_TYPE_PMEM_INPUT OPTEE_MSG_ATTR_TYPE_RMEM_INPUT +#define OPTEE_MSG_ATTR_TYPE_PMEM_OUTPUT OPTEE_MSG_ATTR_TYPE_RMEM_OUTPUT +#define OPTEE_MSG_ATTR_TYPE_PMEM_INOUT OPTEE_MSG_ATTR_TYPE_RMEM_INOUT #define OPTEE_MSG_ATTR_TYPE_TMEM_INPUT 0x9 #define OPTEE_MSG_ATTR_TYPE_TMEM_OUTPUT 0xa #define OPTEE_MSG_ATTR_TYPE_TMEM_INOUT 0xb @@ -149,6 +152,23 @@ struct optee_msg_param_fmem { u64 global_id; };
+/**
- struct optee_msg_param_pmem - RPMI parcel memory reference
- @offs: full-width byte offset from the parcel's first byte
- @size: logical reference size, or required size for a short-buffer response
- @parcel_id: firmware-assigned parcel identifier
- @nonce: nonzero REE nonce supplied when sharing the parcel
- A zero parcel ID and nonce encode a NULL reference. Its offset must be zero;
- its size is preserved. Parcel ID zero remains valid with a nonzero nonce.
- */
+struct optee_msg_param_pmem {
Without an internal offset like in struct optee_msg_param_fmem, an explicit register SHM call into OP-TEE is needed before it can be used. You'd still need the TEE_MEMORY_PARCEL_CREATE, but we can save one round-trip into the secure world.
u64 offs;u64 size;u32 parcel_id;u32 nonce;
I wonder if parcel_id and nonce wouldn't be better combined into a single field. They need to be separate when preparing arguments for the TEE_MEMORY_* calls. Everywhere else, it's only an opaque memory handle, and one handle is easier to keep track of than two.
+};
/**
- struct optee_msg_param_value - opaque value parameter
- @a: first opaque value
@@ -166,18 +186,19 @@ struct optee_msg_param_value { /**
- struct optee_msg_param - parameter used together with struct optee_msg_arg
- @attr: attributes
- @tmem: parameter by temporary memory reference
- @rmem: parameter by registered memory reference
- @fmem: parameter by FF-A registered memory reference
- @value: parameter by opaque value
- @octets: parameter by octet string
- @u.tmem: parameter by temporary memory reference
- @u.rmem: parameter by registered memory reference
- @u.fmem: parameter by FF-A registered memory reference
- @u.pmem: parameter by RPMI parcel memory reference
- @u.value: parameter by opaque value
- @u.octets: parameter by octet string
- @u: union holding OP-TEE msg parameter
- @attr & OPTEE_MSG_ATTR_TYPE_MASK indicates if tmem, rmem or value is used in
- the union. OPTEE_MSG_ATTR_TYPE_VALUE_* indicates value or octets,
- OPTEE_MSG_ATTR_TYPE_TMEM_* indicates @tmem and
- OPTEE_MSG_ATTR_TYPE_RMEM_* or the alias PTEE_MSG_ATTR_TYPE_FMEM_* indicates
- @rmem or @fmem depending on the conduit.
- OPTEE_MSG_ATTR_TYPE_TMEM_* indicates @u.tmem. OPTEE_MSG_ATTR_TYPE_RMEM_*
- and its FMEM/PMEM aliases indicate @u.rmem, @u.fmem or @u.pmem depending
*/
- on the conduit.
- OPTEE_MSG_ATTR_TYPE_NONE indicates that none of the members are used.
struct optee_msg_param { @@ -186,6 +207,7 @@ struct optee_msg_param { struct optee_msg_param_tmem tmem; struct optee_msg_param_rmem rmem; struct optee_msg_param_fmem fmem;
struct optee_msg_param_pmem pmem; struct optee_msg_param_value value; u8 octets[24]; } u;diff --git a/drivers/tee/optee/optee_rpmi.h b/drivers/tee/optee/optee_rpmi.h new file mode 100644 index 000000000000..252216aad09b --- /dev/null +++ b/drivers/tee/optee/optee_rpmi.h @@ -0,0 +1,234 @@ +/* SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) */ +/*
- Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
- */
+#ifndef OPTEE_RPMI_H +#define OPTEE_RPMI_H
+#include <linux/bitops.h> +#include <linux/types.h> +#include <linux/uuid.h>
+/*
- OP-TEE service ABI over RPMI TEE_CALL.
- Requests and responses are carried in the TEE_CALL service payload.
- Control fields are little-endian. Response status fields contain signed
- RPMI error codes, distinct from the outer TEE_CALL status and the GP
- command result in optee_msg_arg.ret.
- */
+#define OPTEE_RPMI_SERVICE_UUID \
UUID_INIT(0x486178e0, 0xe7f8, 0x11e3, \0xbc, 0x5e, 0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b)+#define OPTEE_RPMI_VERSION_MAJOR 1 +#define OPTEE_RPMI_VERSION_MINOR 0
+/**
- struct optee_rpmi_probe_req - request without operation-specific arguments
- @op: GET_API_VERSION, GET_OS_VERSION or EXCHANGE_CAPABILITIES
- */
+struct optee_rpmi_probe_req {
__le32 op;+} __packed;
+/**
- struct optee_rpmi_status_resp - response carrying only an RPMI status
- @status: signed RPMI error code
- */
+struct optee_rpmi_status_resp {
__le32 status;+} __packed;
+/*
- Return the service API version.
- Request: struct optee_rpmi_probe_req
- Response: struct optee_rpmi_api_resp
- */
+#define OPTEE_RPMI_GET_API_VERSION 0
+/**
- struct optee_rpmi_api_resp - GET_API_VERSION response
- @status: signed RPMI error code
- @major: incompatible protocol revision
- @minor: compatible protocol revision
- */
+struct optee_rpmi_api_resp {
__le32 status;__le32 major;__le32 minor;+} __packed;
Why do these communication structs have to be packed? With careful design of the layout, padding, alignment, etc, it shouldn't be an issue.
+/*
- Return the trusted OS revision, not the service API revision.
- Request: struct optee_rpmi_probe_req
- Response: struct optee_rpmi_os_resp
- */
+#define OPTEE_RPMI_GET_OS_VERSION 1
+/**
- struct optee_rpmi_os_resp - GET_OS_VERSION response
- @status: signed RPMI error code
- @major: trusted OS major revision
- @minor: trusted OS minor revision
- @reserved: must be zero
- @build_id: trusted OS build identifier, zero if unspecified
- */
+struct optee_rpmi_os_resp {
__le32 status;__le32 major;__le32 minor;__le32 reserved;__le64 build_id;+} __packed;
+/*
- Query secure-world capabilities and limits.
- Request: struct optee_rpmi_probe_req
- Response: struct optee_rpmi_caps_resp
- */
+#define OPTEE_RPMI_EXCHANGE_CAPABILITIES 2
+/**
- struct optee_rpmi_caps_resp - EXCHANGE_CAPABILITIES response
- @status: signed RPMI error code
- @secure_caps: reserved for future optional features; zero in version 1
- @rpc_param_count: nonzero parameter capacity of each RPC argument buffer
- @notification_count: nonzero logical key count, including synchronous keys
Keys range from zero through notification_count - 1.
- Version 1 defines no capability bits. Unknown bits are ignored by Linux
- for compatibility with future extensions.
Note that we expect the ABI version to stay at 1.0 for a foreseeable future. Extensions to the ABI are primarily negotiated using capabilities.
Cheers, Jens
- */
+struct optee_rpmi_caps_resp {
__le32 status;__le32 secure_caps;__le32 rpc_param_count;__le32 notification_count;+} __packed;
+/*
- Unregister a shared parcel from OP-TEE.
- Request: struct optee_rpmi_unregister_req
- Response: struct optee_rpmi_status_resp
- */
+#define OPTEE_RPMI_UNREGISTER_SHM 3
+/**
- struct optee_rpmi_unregister_req - retire a shared parcel in OP-TEE
- @op: OPTEE_RPMI_UNREGISTER_SHM
- @parcel_id: parcel to retire; zero is valid with a nonzero nonce
- @nonce: nonzero nonce associated with the parcel
- Success means OP-TEE stopped using the mapping and released its receiver
- interest.
- */
+struct optee_rpmi_unregister_req {
__le32 op;__le32 parcel_id;__le32 nonce;+} __packed;
+/*
- Enable asynchronous notification delivery.
- Request: struct optee_rpmi_enable_notif_req
- Response: struct optee_rpmi_status_resp
- */
+#define OPTEE_RPMI_ENABLE_ASYNC_NOTIF 4
+/**
- struct optee_rpmi_enable_notif_req - bind an incoming RPMI doorbell
- @op: OPTEE_RPMI_ENABLE_ASYNC_NOTIF
- @signal_id: allocated TEE-to-REE signal, not a logical notification key
- Success activates delivery and raises the doorbell for pending work.
- Raising the signal requests OPTEE_MSG_CMD_DO_BOTTOM_HALF.
- OPTEE_MSG_CMD_STOP_ASYNC_NOTIF stops future doorbell generation, but does
- not drain already-raised signals or release the signal ID.
- */
+struct optee_rpmi_enable_notif_req {
__le32 op;__le32 signal_id;+} __packed;
+/*
- Start a yielding command using shared command and RPC arguments.
- Request: struct optee_rpmi_call_req
- Response: struct optee_rpmi_call_resp
- */
+#define OPTEE_RPMI_YIELDING_CALL_WITH_ARG 5
+#define OPTEE_RPMI_YIELDING_CALL_RETURN_DONE 0 +#define OPTEE_RPMI_YIELDING_CALL_RETURN_RPC_CMD 1 +#define OPTEE_RPMI_YIELDING_CALL_RETURN_INTERRUPT 2
+/**
- struct optee_rpmi_call_req - start a yielding command
- @op: OPTEE_RPMI_YIELDING_CALL_WITH_ARG
- @parcel_id: argument parcel identity
- @nonce: nonce associated with the parcel
- @flags: zero in version 1
- @arg_offset: command argument byte offset from the parcel's first byte
- @rpc_offset: RPC argument byte offset from the parcel's first byte
- @arg_size: command argument extent in bytes
- @rpc_size: RPC argument capacity in bytes
- Firmware validates ownership, RW access, alignment and disjoint ranges.
- RPMI_ERR_BUSY rejects an initial request without acquiring call ownership.
- */
+struct optee_rpmi_call_req {
__le32 op;__le32 parcel_id;__le32 nonce;__le32 flags;__le64 arg_offset;__le64 rpc_offset;__le32 arg_size;__le32 rpc_size;+} __packed;
+/**
- struct optee_rpmi_call_resp - yielding command response
- @status: signed RPMI error code, distinct from the command's GP result
- @result: OPTEE_RPMI_YIELDING_CALL_RETURN_* value when status is success
- @resume_token: zero for DONE, nonzero opaque token for a suspended command
- DONE releases all access to the call's argument and RPC ranges. RPC_CMD
- requests RPC command handling; INTERRUPT requests resumption without an
- RPC command. Tokens belong to one accepted call, service and caller.
- */
+struct optee_rpmi_call_resp {
__le32 status;__le32 result;__le64 resume_token;+} __packed;
+/*
- Resume a suspended yielding command.
- Request: struct optee_rpmi_resume_req
- Response: struct optee_rpmi_call_resp
- */
+#define OPTEE_RPMI_YIELDING_CALL_RESUME 6
+/**
- struct optee_rpmi_resume_req - resume a suspended command
- @op: OPTEE_RPMI_YIELDING_CALL_RESUME
- @reserved: must be zero
- @resume_token: token from the preceding response for this call
- Resume must not return RPMI_ERR_BUSY.
- */
+struct optee_rpmi_resume_req {
__le32 op;__le32 reserved;__le64 resume_token;+} __packed;
+#endif /* OPTEE_RPMI_H */
-- 2.34.1