Re: [PATCH RFC v2 2/8] tee: optee: define the RPMI control and parcel-reference ABI

From: Jens Wiklander

Date: Fri Oct 09 2026 - 10:00:43 EST


Hi Amir,

On Fri, Oct 9, 2026 at 12:03 AM Amirreza Zarrabi
<amirreza.zarrabi@xxxxxxxxxxxxxxxx> wrote:
>
> Hi Jens,
>
> On 10/8/2026 5:51 PM, Jens Wiklander wrote:
> > Hi Amir,
> >
> > On Tue, Oct 6, 2026 at 2:40 AM Amirreza Zarrabi
> > <amirreza.zarrabi@xxxxxxxxxxxxxxxx> 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@xxxxxxxxxxxxxxxx>
> >> ---
> >> 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.
> >
>
> The original intenation was to use parcel-relative offsets, with the
> secure-side memory object covers the entire parcel. OP-TEE can retrieve it
> lazily and apply the supplied offset directly, so this design does not require
> an explicit SHM registration call or an additional round trip.
>
> Since we are implementing the RPMI parcel memory-object cache independently of
> FF-A's cache in OP-TEE, this seems simpler: it avoids a separate
> initial-offset property, preserves a full-width offset reference, while
> keeping OP-TEE ignorant from the SHM concept which is a Linux side concept.
>
> That said, I can separate the offsets to matching FF-A's memory-object
> model. But I am not sure what we achive?

I think it's needed, but I don't mind being proven wrong. :-)

IIRC, this stems from TEE_IOC_SHM_REGISTER. In hindsight, I regret
that offset. We should have kept it in userspace, but that's too late
now.

>
> >> + 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.
>
> I thought about that before. This would push the conversion to the
> firmware boundary. I was not sure if it is acceptable. I'll do that :).

Sounds good to me.

>
> >
> >> +};
> >> +
> >> /**
> >> * 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.
>
> Agreed. The structures already have naturally aligned fields and
> explicit reserved fields where needed, so `__packed` is unnecessary for
> their current layouts. I'll remove it and keep the wire layout explicitly
> defined, retaining unaligned access helpers where transport-buffer alignment
> is not guaranteed.

Good.

Cheers,
Jens