Julian Hall | e76ade8 | 2020-11-25 03:07:21 +0100 | [diff] [blame] | 1 | Service Access Protocols |
| 2 | ======================== |
| 3 | |
| 4 | A trusted service is accessed by calling service-specific methods via an RPC mechanism. The set of callable methods forms the |
| 5 | public interface exposed by a service. This section is concerned with interface conventions and protocols used for serializing |
| 6 | method parameters and return values. It is anticipated that there will be a need to support different parameter serialization |
| 7 | schemes to suite different needs. The project accommodates this with the following: |
| 8 | |
| 9 | - The Protocols directory structure allows for different protocol definitions for the same service. |
| 10 | - Message serialization code is decoupled from service provider code using an abstract 'serializer' interface. Alternative |
| 11 | concrete serializers may provide implementations of the interface. |
| 12 | |
Julian Hall | e76ade8 | 2020-11-25 03:07:21 +0100 | [diff] [blame] | 13 | A deployment independent interface for locating services and establishing RPC sessions is described here: :ref:`Service Locator` |
| 14 | |
Imre Kis | 9df392c | 2023-09-14 13:29:09 +0200 | [diff] [blame] | 15 | Trusted Services protocol layers |
| 16 | -------------------------------- |
| 17 | |
| 18 | .. image:: image/TSProtocolLayers.svg |
| 19 | |
| 20 | * Service client interface: This component provides the interface to a given service for a user application, i.e. the PSA |
| 21 | Crypto, Internal Trusted Storage, etc. interface. |
| 22 | |
| 23 | * Service layer: This layer is responsible for serializing and deserializing the service specific parameters and it provides |
| 24 | a transparent interface between the caller and endpoint side. |
| 25 | |
| 26 | * RPC layer: |
| 27 | |
| 28 | * RPC caller session: This component provides a session-like object for the service layer. After opening the session, it is |
| 29 | tied to the opened service interface of the endpoint. Each service call can use the simple begin/invoke/end interface for |
| 30 | requesting a buffer for the call parameters, for invoking the call and for releasing the response buffer. |
| 31 | |
| 32 | The RPC caller session manages the lifetime of the shared memory. Currently it has two options. It either creates the |
| 33 | memory on session open and keeps it while the session is open, or it creates the shared buffer for each call and releases |
| 34 | it when the call ends (``end()``). |
| 35 | |
| 36 | * RPC caller implementations (`ts_rpc_caller_linux`, `ts_rpc_caller_sp`, etc.): The RPC caller session is built on the |
| 37 | primitives of the RPC callers. These primitives allow the caller session finding the remote endpoint, creating and |
| 38 | releasing shared memories and doing the actual call. |
| 39 | |
| 40 | The main RPC implementation is the TS RPC which is a TS specific RPC solution over FF-A. The project contains caller |
| 41 | implementations for Linux and for S-EL0 SPs. The Linux implementation is split between the user space and a kernel driver. |
| 42 | |
| 43 | There are other RPC caller implementations (dummy, direct) which are used for testing purposes. |
| 44 | |
| 45 | * RPC endpoint (`ts_rpc_endpoint_sp`): This component provides the RPC endpoint which can host multiple services. Once it |
| 46 | receives the call from the client, it finds the matching service and forwards the serialized call parameters to it. |
| 47 | |
| 48 | * FF-A layer: It is the transport layer of the protocol stack, and it provides interfaces for sending messages and sharing |
| 49 | memory between normal world and secure world components. |
| 50 | |
| 51 | TS RPC implementation |
| 52 | --------------------- |
| 53 | |
| 54 | Generic concepts |
| 55 | '''''''''''''''' |
| 56 | |
| 57 | * The requests are always sent by the caller and the endpoint sends a response. |
| 58 | * The protocol version describes the ABI, the allowed values of the message fields and the behavior of the calls. |
| 59 | * Service endpoints are provided by FF-A secure partitions. |
| 60 | * Each endpoint can implement multiple services. The services are identified by their service UUID (**not** FF-A UUID). To |
| 61 | avoid including the UUID in each service call, a short interface ID is assigned to each service. The mapping of service |
| 62 | UUIDs and interface IDs can be queried by an RPC call. The lifetime of the interface ID is the same as the lifetime of the |
| 63 | service. The `0xff` interface ID is used for the management interface. |
| 64 | * The service calls use shared memory to forward the call payload. It has to be shared via FF-A and then retrieved by the |
| 65 | endpoint. The shared memories are tied to an endpoint not to a service. |
| 66 | * The errors which happen in the RPC layer will result in a RPC status code which indicates an error. |
| 67 | * The errors which happen in the service handler will result in a service status code which indicates an error. In this case |
| 68 | the RPC status code will be `RPC_SUCCESS` as the RPC layer was able to forward the call between the service caller and the |
| 69 | service handler. |
| 70 | |
| 71 | ABI |
| 72 | ''' |
| 73 | |
| 74 | The ABI of the TS RPC protocol uses the 32 bit variants of ``FFA_MSG_SEND_DIRECT_REQ`` and ``FFA_MSG_SEND_DIRECT_RESP`` |
| 75 | interfaces of the FF-A specification. The use of the implementation specific arguments is listed in the table below. |
| 76 | |
| 77 | .. list-table:: TS RPC ABI |
| 78 | :header-rows: 1 |
| 79 | |
| 80 | * - Message name |
| 81 | - Short message ID |
| 82 | - W3[31:30] - SAP |
| 83 | - W3[29:24] - Flags |
| 84 | - W3[23:16] - Interface ID |
| 85 | - W3[15:0] - Opcode |
| 86 | - W4 - Arg1 |
| 87 | - W5 - Arg2 |
| 88 | - W6 - Arg3 |
| 89 | - W7 - Arg4 |
| 90 | * - RPC protocol version get request |
| 91 | - ``VERSION_GET`` |
| 92 | - ``0b00`` |
| 93 | - ``0b000000`` |
| 94 | - ``0xff`` |
| 95 | - ``0x0000`` |
| 96 | - Reserved (MBZ) |
| 97 | - Reserved (MBZ) |
| 98 | - Reserved (MBZ) |
| 99 | - Reserved (MBZ) |
| 100 | * - RPC protocol version get response |
| 101 | - ``VERSION_GET`` |
| 102 | - ``0b00`` |
| 103 | - ``0b000000`` |
| 104 | - ``0xff`` |
| 105 | - ``0x0000`` |
| 106 | - Version, starting from ``0x00000001`` |
| 107 | - Reserved (MBZ) |
| 108 | - Reserved (MBZ) |
| 109 | - Reserved (MBZ) |
| 110 | * - Memory retrieve request |
| 111 | - ``MEM_RETRIEVE`` |
| 112 | - ``0b00`` |
| 113 | - ``0b000000`` |
| 114 | - ``0xff`` |
| 115 | - ``0x0001`` |
| 116 | - FF-A memory handle LSW |
| 117 | - FF-A memory handle MSW |
| 118 | - FF-A memory tag LSW |
| 119 | - FF-A memory tag MSW |
| 120 | * - Memory relinquish request |
| 121 | - ``MEM_RELINQUISH`` |
| 122 | - ``0b00`` |
| 123 | - ``0b000000`` |
| 124 | - ``0xff`` |
| 125 | - ``0x0002`` |
| 126 | - FF-A memory handle LSW |
| 127 | - FF-A memory handle MSW |
| 128 | - Reserved (MBZ) |
| 129 | - Reserved (MBZ) |
| 130 | * - Memory retrieve/relinquish response |
| 131 | - ``MEM_RETRIEVE``/``MEM_RELINQUISH`` |
| 132 | - ``0b00`` |
| 133 | - ``0b000000`` |
| 134 | - ``0xff`` |
| 135 | - ``0x0001``/``0x0002`` |
| 136 | - TS RPC status |
| 137 | - Reserved (MBZ) |
| 138 | - Reserved (MBZ) |
| 139 | - Reserved (MBZ) |
| 140 | * - Service info get request |
| 141 | - ``SERVICE_INFO_GET`` |
| 142 | - ``0b00`` |
| 143 | - ``0b000000`` |
| 144 | - ``0xff`` |
| 145 | - ``0x0003`` |
| 146 | - Service UUID |
| 147 | - Service UUID |
| 148 | - Service UUID |
| 149 | - Service UUID |
| 150 | * - Service info get response |
| 151 | - ``SERVICE_INFO_GET`` |
| 152 | - ``0b00`` |
| 153 | - ``0b000000`` |
| 154 | - ``0xff`` |
| 155 | - ``0x0003`` |
| 156 | - TS RPC status |
| 157 | - ``[31:8]`` Reserved |
| 158 | |
| 159 | ``[7:0]`` Queried service interface ID |
| 160 | - Reserved (MBZ) |
| 161 | - Reserved (MBZ) |
| 162 | * - Service call request |
| 163 | - |
| 164 | - ``0b00`` |
| 165 | - ``0b000000`` |
| 166 | - Service interface ID |
| 167 | - Service opcode |
| 168 | - FF-A memory handle LSW |
| 169 | - FF-A memory handle MSW |
| 170 | - Request length |
| 171 | - Client ID |
| 172 | * - Service call response |
| 173 | - |
| 174 | - ``0b00`` |
| 175 | - ``0b000000`` |
| 176 | - Service interface ID |
| 177 | - Service opcode |
| 178 | - TS RPC status |
| 179 | - Service status |
| 180 | - Response length |
| 181 | - Reserved |
| 182 | |
| 183 | * **RPC protocol version get** |
| 184 | |
| 185 | Queries the RPC protocol version. This message must be available and backwards compatible for all protocol versions. |
| 186 | |
| 187 | * **Memory retrieve** |
| 188 | |
| 189 | Requests the endpoint to do an ``FFA_MEM_RETRIEVE_REQ`` call using the forwarded FF-A memory handle and tag. |
| 190 | |
| 191 | * **Memory relinquish** |
| 192 | |
| 193 | Requests the endpoint to do an ``FFA_MEM_RELINQUISH`` call using the forwarded FF-A memory handle. |
| 194 | |
| 195 | * **Service info get** |
| 196 | |
| 197 | Query service information from the endpoint by the UUID of the service. The UUID is transmitted as defined in SMCCC section |
| 198 | 5.3 but in registers W4-W7. The returned service interface ID should be used in the service calls. Multiple endpoints can |
| 199 | implement the same service but one endpoint can implement a service only once. |
| 200 | |
| 201 | * **Service call** |
| 202 | |
| 203 | After creating a shared memory and querying the interface ID for a given service UUID the caller can make a service call. The |
| 204 | service opcode and the contents of the shared memory is service specific. The request and response length fields indicate the |
| 205 | used area of the shared memory. |
| 206 | |
| 207 | It is allowed to do a limited service call without shared memory, i.e. doorbell call. In this case the FF-A memory ID has to |
| 208 | be the invalid handle value ``0xffffffffffffffff``. |
| 209 | |
| 210 | Service discovery |
| 211 | ''''''''''''''''' |
| 212 | |
| 213 | * Query all TS SPs via ``FFA_PARTITION_INFO_GET`` call made to the SPMC. All TS SPs have the same FF-A UUID: |
| 214 | ``bdcd76d7-825e-4751-963b-86d4f84943ac`` If the system setup has fixed SP endpoint IDs, this step can be skipped. |
| 215 | * Iterate thought the TS SPs and make a "Service info get request" RPC call to the SPs, containing the service UUID. If the |
| 216 | RPC status in the "Service info get response" is `RPC_SUCCESS`, the SP implements the service and its interface ID is returned |
| 217 | in the response. |
| 218 | * If there are multiple instances of a service, the selection between these should be done in a service specific way (i.e. |
| 219 | query service version, capabilities, etc.). |
| 220 | |
| 221 | .. image:: image/TSServiceDiscovery.svg |
| 222 | |
| 223 | RPC status code values |
| 224 | ''''''''''''''''''''''' |
| 225 | |
| 226 | The status codes for the RPC layer are defined in `components/rpc/common/interface/rpc_status.h`. Currently the following values |
| 227 | are defined: |
| 228 | |
| 229 | .. literalinclude:: ../../components/rpc/common/interface/rpc_status.h |
| 230 | :lines: 20-32 |
| 231 | :language: C |
| 232 | |
| 233 | Example TS RPC call |
| 234 | ''''''''''''''''''' |
| 235 | |
| 236 | This example shows the full sequence of a service call by opening the RPC session, doing the call (begin/invoke/end) and then |
| 237 | closing the session. In this case the RPC session it set to create individual shared memory for each call. |
| 238 | |
| 239 | .. uml:: uml/TSRPCCall.puml |
| 240 | |
| 241 | .. note:: |
| 242 | Although the TS RPC layer messages use ``FFA_MSG_SEND_DIRECT_REQ``/``FFA_MSG_SEND_DIRECT_RESP`` interface and go through the |
| 243 | SPMC their destination is not the SPMC but the RPC endpoint. For simplifying the diagram, these calls are showed as direct |
| 244 | calls between the TS RPC caller and the TS RPC endpoint. |
| 245 | |
Julian Hall | e76ade8 | 2020-11-25 03:07:21 +0100 | [diff] [blame] | 246 | Status Codes |
| 247 | ------------ |
| 248 | |
Imre Kis | 9df392c | 2023-09-14 13:29:09 +0200 | [diff] [blame] | 249 | On returning from a request to invoke a service method, two status codes are returned: |
Julian Hall | e76ade8 | 2020-11-25 03:07:21 +0100 | [diff] [blame] | 250 | |
| 251 | - *RPC status* - A generic status code that corresponds to the RPC call transaction. RPC status codes are standardized across |
Imre Kis | 9df392c | 2023-09-14 13:29:09 +0200 | [diff] [blame] | 252 | all services. (See: `RPC status code values`_) |
| 253 | - *Service status* - a service specific status code. (See: `Service Status Codes`_ ) |
Julian Hall | e76ade8 | 2020-11-25 03:07:21 +0100 | [diff] [blame] | 254 | |
| 255 | Separation of status codes by layer allows service specific status codes to be accommodated while keeping RPC status codes |
| 256 | common. |
| 257 | |
Imre Kis | 9df392c | 2023-09-14 13:29:09 +0200 | [diff] [blame] | 258 | A client should only check the returned service status if the returned RPC status value is `RPC_SUCCESS`. All other RPC |
| 259 | status values indicate that an error occurred in delivering the RPC request. An RPC status of `RPC_SUCCESS` does not |
| 260 | indicate that the service was successful. It merely indicates that the request was delivered, a suitable handler was |
Julian Hall | e76ade8 | 2020-11-25 03:07:21 +0100 | [diff] [blame] | 261 | identified and the request parameters were understood. |
| 262 | |
| 263 | Service Access Protocol Definition Conventions |
| 264 | ---------------------------------------------- |
| 265 | |
| 266 | A service access protocol defines the following: |
| 267 | |
| 268 | - Opcodes used for identifying service methods. |
| 269 | - Request parameters for each method. |
| 270 | - Response parameters for method return values. |
| 271 | - Operation status code. |
| 272 | |
| 273 | Details of how public interface definition files for trusted services are organized, see: :ref:`Project Structure` |
| 274 | |
| 275 | It is possible that for certain deployments, it will be necessary to customize which parameter encoding scheme is used. Many |
| 276 | schemes are possible such as Protocol Buffers, CBOR, JSON, TLV, TPM commands or packed C structures. To make scheme |
Imre Kis | 9df392c | 2023-09-14 13:29:09 +0200 | [diff] [blame] | 277 | customization straight forward, serialize/deserialize operations should be encapsulated behind a common interface to decouple |
Julian Hall | e76ade8 | 2020-11-25 03:07:21 +0100 | [diff] [blame] | 278 | service provider code from any particular serialization scheme. A section below describes a pattern for achieving this. |
| 279 | |
| 280 | Service Namespace |
| 281 | ''''''''''''''''' |
| 282 | |
| 283 | Definitions that form a service access protocol should live within a namespace that is unique for the particular service. Using |
| 284 | a namespace for service definitions avoids possible clashes between similarly named definitions that belong to different |
| 285 | services. How the namespace is implemented depends on how the access protocol is defined. For example, the Protocol Buffers |
| 286 | definitions for the crypto service all live within the ts_crypto package. The recommended convention for forming a trusted |
| 287 | service namespace is as follows:: |
| 288 | |
| 289 | ts_<service_name> |
| 290 | |
| 291 | e.g. |
| 292 | ts_crypto |
| 293 | ts_secure_storage |
| 294 | |
| 295 | Language Independent Protocol Definitions |
| 296 | ''''''''''''''''''''''''''''''''''''''''' |
| 297 | |
| 298 | By defining service access protocols using an interface description language (IDL) with good support for different programming |
| 299 | languages, it should be straight forward to access trusted services from clients written in a range of languages. On Arm |
| 300 | Cortex-A deployments, it is common for user applications to be implemented using a range of languages such as Go, Python or |
| 301 | Java. Rather than relying on a binding to a C client library, native client code may be generated from the formal protocol |
| 302 | definition files. Initial protocol definitions use Google Protocol Buffers as the IDL. The project structure allows for use of |
| 303 | alternative definition schemes and serializations. |
| 304 | |
| 305 | Opcode Definition |
| 306 | ````````````````` |
| 307 | |
| 308 | Opcodes are integer values that identify methods implemented by a service endpoint. Opcodes only need to be unique within the |
| 309 | scope of a particular service. The mapping of opcode to method is an important part of a service interface definition and |
| 310 | should be readily available to clients written in a variety of programming languages. For a Protocol Buffers based definition, |
| 311 | opcodes are defined in a file called:: |
| 312 | |
| 313 | opcodes.proto |
| 314 | |
| 315 | For example, for the Crypto trusted service, the Protocol Buffers opcode definitions are in:: |
| 316 | |
| 317 | protocols/service/crypto/protobuf/opcodes.proto |
| 318 | |
| 319 | Alternative definitions for light-weight C clients using the packed-c scheme are in:: |
| 320 | |
| 321 | protocols/service/crypto/packed-c/opcodes.h |
| 322 | |
| 323 | Parameter Definition |
| 324 | ```````````````````` |
| 325 | |
| 326 | The convention used for serializing method parameters and return values may be specific to a particular service. The definition |
| 327 | file will include message definitions for both request and response parameters. Common objects that are used for multiple |
| 328 | methods should be defined in separate files. When using Protobufs, the following naming convention for method parameter files |
| 329 | should be used:: |
| 330 | |
| 331 | <method_name>.proto |
| 332 | |
| 333 | For example, the Crypto export_public_key method is defined in a file called:: |
| 334 | |
| 335 | protocols/service/crypto/protobuf/export_public_key.proto |
| 336 | |
Julian Hall | e76ade8 | 2020-11-25 03:07:21 +0100 | [diff] [blame] | 337 | Service Status Codes |
| 338 | ```````````````````` |
| 339 | |
| 340 | Service specific status code definitions using different definition schemes are defined here (using crypto service as an |
| 341 | example):: |
| 342 | |
| 343 | protocols/service/crypto/protobuf/status.proto |
| 344 | protocols/service/crypto/packed-c/status.h |
| 345 | |
| 346 | Status code definitions may also be shared between services. For example, services that conform to PSA API conventions will use |
| 347 | standardized PSA status codes, defined here:: |
| 348 | |
| 349 | protocols/service/psa/protobuf/status.proto |
| 350 | protocols/service/psa/packed-c/status.h |
| 351 | |
| 352 | Use of Protocol Buffers |
| 353 | ----------------------- |
| 354 | |
| 355 | When Protocol Buffers is used for protocol definition and parameter serialization, the following conventions have been adopted. |
| 356 | |
| 357 | .proto File Style Guide |
| 358 | ''''''''''''''''''''''' |
| 359 | |
| 360 | The style of the .proto files should follow Google's Protocol Buffers Style Guide. |
| 361 | |
| 362 | Protocol Buffer Library for Trusted Services |
| 363 | '''''''''''''''''''''''''''''''''''''''''''' |
| 364 | |
| 365 | Protocol Buffers standardizes how service interfaces are defined and the on-wire encoding for messages. Because of this, service |
| 366 | clients and service providers are free to use any conformant implementation. However for trusted services that may be deployed |
| 367 | across a range of environments, some of which may be resource constrained, a lightweight library should be used for C/C++ code |
| 368 | that implement or use trusted services. For this purpose, Nanobp (https://github.com/nanopb/nanopb) should be used. |
| 369 | |
| 370 | Serialization Protocol Flexibility |
| 371 | ---------------------------------- |
| 372 | |
| 373 | Many different serialization protocols exist for encoding and decoding message parameters. Hard-wiring a particular protocol |
| 374 | into a trusted service provider implementation isn't desirable for the following reasons: |
| 375 | |
| 376 | - Depending on the complexity of serialization operations, mixing serialization logic with protocol-independent code makes |
| 377 | trusted service provider code bigger and more difficult to maintain. |
| 378 | - Different protocols may be needed for different deployments. It should be possible to make a build-time or even a |
| 379 | run-time selection of which protocol to use. |
| 380 | - The number of supported serializations protocols is likely to grow. Adding a new protocol shouldn't require you to make |
| 381 | extensive code changes and definitely shouldn't break support for existing protocols. |
| 382 | |
| 383 | These problems can be avoided by implementing protocol specific operations behind a common interface. Serialize/deserialize |
| 384 | operations will have the following pattern:: |
| 385 | |
| 386 | int serialize_for_method(msg_buffer *buf, in args...); |
| 387 | int deserialize_for_method(const msg_buffer *buf, out args...); |
| 388 | |
Imre Kis | 9df392c | 2023-09-14 13:29:09 +0200 | [diff] [blame] | 389 | Encoding types are represented as dedicated service interfaces in the RPC protocol and as such are identified by a uniq |
| 390 | service UUID. |
Julian Hall | e76ade8 | 2020-11-25 03:07:21 +0100 | [diff] [blame] | 391 | |
| 392 | -------------- |
| 393 | |
Imre Kis | 9df392c | 2023-09-14 13:29:09 +0200 | [diff] [blame] | 394 | *Copyright (c) 2020-2023, Arm Limited and Contributors. All rights reserved.* |
Julian Hall | e76ade8 | 2020-11-25 03:07:21 +0100 | [diff] [blame] | 395 | |
| 396 | SPDX-License-Identifier: BSD-3-Clause |