1// Copyright 2018 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#pragma once
6
7#include <inttypes.h>
8
9#include <fbl/array.h>
10#include <fbl/unique_ptr.h>
11#include <tee-client-api/tee-client-types.h>
12#include <zircon/assert.h>
13
14#include "optee-smc.h"
15#include "shared-memory.h"
16#include "util.h"
17
18namespace optee {
19
20// OP-TEE Messages
21//
22// The majority of data exchange with OP-TEE occurs via OP-TEE messages. These are used in
23// conjunction with the OP-TEE SMC Call with Arg function. When that SMC function is invoked,
24// OP-TEE will expect a physical pointer to an OP-TEE message to be passed in arguments a1 and a2.
25//
26// Each message is made up of a header and a variable number of parameters. The relevant fields of
27// a message can depend on the command and the context, so these helper classes aim to reduce the
28// possibilities of invariant access. For example, in some instances, a field might be an input and
29// in others, it might be an output.
30
31struct MessageHeader {
32    uint32_t command;
33    uint32_t app_function;
34    uint32_t session_id;
35    uint32_t cancel_id;
36
37    uint32_t unused;
38    uint32_t return_code;
39    uint32_t return_origin;
40    uint32_t num_params;
41};
42
43struct MessageParam {
44    enum AttributeType : uint64_t {
45        kAttributeTypeNone = 0x0,
46        kAttributeTypeValueInput = 0x1,
47        kAttributeTypeValueOutput = 0x2,
48        kAttributeTypeValueInOut = 0x3,
49        kAttributeTypeRegMemInput = 0x5,
50        kAttributeTypeRegMemOutput = 0x6,
51        kAttributeTypeRegMemInOut = 0x7,
52        kAttributeTypeTempMemInput = 0x9,
53        kAttributeTypeTempMemOutput = 0xa,
54        kAttributeTypeTempMemInOut = 0xb,
55
56        kAttributeTypeMeta = 0x100,
57        kAttributeTypeFragment = 0x200,
58    };
59
60    struct TemporaryMemory {
61        uint64_t buffer;
62        uint64_t size;
63        uint64_t shared_memory_reference;
64    };
65
66    struct RegisteredMemory {
67        uint64_t offset;
68        uint64_t size;
69        uint64_t shared_memory_reference;
70    };
71
72    union Value {
73        struct {
74            uint64_t a;
75            uint64_t b;
76            uint64_t c;
77        } generic;
78        TEEC_UUID uuid_big_endian;
79        struct {
80            SharedMemoryType memory_type;
81            uint64_t memory_size;
82        } allocate_memory_specs;
83        struct {
84            SharedMemoryType memory_type;
85            uint64_t memory_id;
86        } free_memory_specs;
87    };
88
89    uint64_t attribute;
90    union {
91        TemporaryMemory temporary_memory;
92        RegisteredMemory registered_memory;
93        Value value;
94    } payload;
95};
96
97// MessageParamList
98//
99// MessageParamList is a non-owning view of the parameters in a Message. It is only valid within
100// the lifetime of the Message.
101class MessageParamList {
102public:
103    constexpr MessageParamList()
104        : params_(nullptr), count_(0U) {}
105
106    MessageParamList(MessageParam* params, size_t count)
107        : params_(params), count_(count) {}
108
109    size_t size() const { return count_; }
110    MessageParam* get() const { return params_; }
111
112    MessageParam& operator[](size_t i) const {
113        ZX_DEBUG_ASSERT(i < count_);
114        return params_[i];
115    }
116
117    MessageParam* begin() const {
118        return params_;
119    }
120    MessageParam* end() const {
121        return &params_[count_];
122    }
123
124private:
125    MessageParam* params_;
126    size_t count_;
127};
128
129template <typename PtrType>
130class MessageBase {
131    static_assert(fbl::is_same<PtrType, SharedMemory*>::value ||
132                      fbl::is_same<PtrType, fbl::unique_ptr<SharedMemory>>::value,
133                  "Template type of MessageBase must be a pointer (raw or smart) to SharedMemory!");
134
135public:
136    using SharedMemoryPtr = PtrType;
137
138    zx_paddr_t paddr() const {
139        ZX_DEBUG_ASSERT_MSG(is_valid(), "Accessing uninitialized OP-TEE message");
140        return memory_->paddr();
141    }
142
143    // TODO(godtamit): Move this to protected once all usages of it outside are removed
144    // TODO(rjascani): Change this to return a reference to make ownership rules clearer
145    MessageParamList params() const {
146        ZX_DEBUG_ASSERT_MSG(is_valid(), "Accessing uninitialized OP-TEE message");
147        return MessageParamList(reinterpret_cast<MessageParam*>(header() + 1),
148                                header()->num_params);
149    }
150
151    // Returns whether the message is valid. This must be true to access any class-specific field.
152    bool is_valid() const { return memory_ != nullptr; }
153
154protected:
155    static constexpr size_t CalculateSize(size_t num_params) {
156        return sizeof(MessageHeader) + (sizeof(MessageParam) * num_params);
157    }
158
159    // MessageBase
160    //
161    // Move constructor for MessageBase.
162    MessageBase(MessageBase&& msg)
163        : memory_(fbl::move(msg.memory_)) {
164        msg.memory_ = nullptr;
165    }
166
167    // Move-only, so explicitly delete copy constructor and copy assignment operator for clarity
168    MessageBase(const MessageBase&) = delete;
169    MessageBase& operator=(const MessageBase&) = delete;
170
171    explicit MessageBase()
172        : memory_(nullptr) {}
173
174    explicit MessageBase(SharedMemoryPtr memory)
175        : memory_(fbl::move(memory)) {}
176
177    MessageHeader* header() const {
178        ZX_DEBUG_ASSERT_MSG(is_valid(), "Accessing uninitialized OP-TEE message");
179        return reinterpret_cast<MessageHeader*>(memory_->vaddr());
180    }
181
182    SharedMemoryPtr memory_;
183};
184
185// Message
186//
187// A normal message from the rich world (REE).
188class Message : public MessageBase<fbl::unique_ptr<SharedMemory>> {
189public:
190    enum Command : uint32_t {
191        kOpenSession = 0,
192        kInvokeCommand = 1,
193        kCloseSession = 2,
194        kCancel = 3,
195        kRegisterSharedMemory = 4,
196        kUnregisterSharedMemory = 5,
197    };
198
199    // Message
200    //
201    // Move constructor for Message. Uses the default implicit implementation.
202    Message(Message&&) = default;
203
204    // Move-only, so explicitly delete copy constructor and copy assignment operator for clarity
205    Message(const Message&) = delete;
206    Message& operator=(const Message&) = delete;
207
208protected:
209    using MessageBase::MessageBase; // inherit constructors
210};
211
212// OpenSessionMessage
213//
214// This OP-TEE message is used to start a session between a client app and trusted app.
215class OpenSessionMessage : public Message {
216public:
217    explicit OpenSessionMessage(SharedMemoryManager::DriverMemoryPool* pool,
218                                const UuidView& trusted_app,
219                                const UuidView& client_app,
220                                uint32_t client_login,
221                                uint32_t cancel_id,
222                                const fbl::Array<MessageParam>& params);
223
224    // Outputs
225    uint32_t session_id() const { return header()->session_id; }
226    uint32_t return_code() const { return header()->return_code; }
227    uint32_t return_origin() const { return header()->return_origin; }
228
229protected:
230    using Message::header; // make header() protected
231
232    static constexpr size_t kNumFixedOpenSessionParams = 2;
233};
234
235// RpcMessage
236//
237// A message originating from the trusted world (TEE) specifying the details of a RPC request.
238class RpcMessage : public MessageBase<SharedMemory*> {
239public:
240    enum Command : uint32_t {
241        kLoadTa = 0,
242        kAccessReplayProtectedMemoryBlock = 1,
243        kAccessFileSystem = 2,
244        kGetTime = 3,
245        kWaitQueue = 4,
246        kSuspend = 5,
247        kAllocateMemory = 6,
248        kFreeMemory = 7,
249        kAccessSqlFileSystem = 8,
250        kLoadGprof = 9,
251        kPerformSocketIo = 10
252    };
253
254    // RpcMessage
255    //
256    // Move constructor for RpcMessage.
257    RpcMessage(RpcMessage&& rpc_msg)
258        : MessageBase(fbl::move(rpc_msg)),
259          is_valid_(fbl::move(rpc_msg.is_valid_)) {
260        rpc_msg.is_valid_ = false;
261    }
262
263    // Move-only, so explicitly delete copy constructor and copy assignment operator for clarity
264    RpcMessage(const RpcMessage&) = delete;
265    RpcMessage& operator=(const RpcMessage&) = delete;
266
267    // RpcMessage
268    //
269    // Constructs an instance of an RpcMessage from a backing SharedMemory object.
270    //
271    // Parameters:
272    //  * memory:   A pointer to the SharedMemory object backing the RpcMessage. This pointer must
273    //              be non-null and valid.
274    explicit RpcMessage(SharedMemory* memory)
275        : MessageBase(memory), is_valid_(TryInitializeMembers()) {}
276
277    uint32_t command() const {
278        ZX_DEBUG_ASSERT_MSG(is_valid(), "Accessing invalid OP-TEE RPC message");
279        return header()->command;
280    }
281
282    void set_return_origin(uint32_t return_origin) {
283        ZX_DEBUG_ASSERT_MSG(is_valid(), "Accessing invalid OP-TEE RPC message");
284        header()->return_origin = return_origin;
285    }
286
287    void set_return_code(uint32_t return_code) {
288        ZX_DEBUG_ASSERT_MSG(is_valid(), "Accessing invalid OP-TEE RPC message");
289        header()->return_code = return_code;
290    }
291
292    // Returns whether the message is a valid RpcMessage. This must be true to access any
293    // class-specific field.
294    bool is_valid() const { return is_valid_; }
295
296protected:
297    bool is_valid_;
298
299private:
300    bool TryInitializeMembers();
301};
302
303// LoadTaRpcMessage
304//
305// A RpcMessage that should be interpreted with the command of loading a trusted application.
306// A RpcMessage can be converted into a LoadTaRpcMessage via a constructor.
307class LoadTaRpcMessage : public RpcMessage {
308public:
309    // LoadTaRpcMessage
310    //
311    // Move constructor for LoadTaRpcMessage. Uses the default implicit implementation.
312    LoadTaRpcMessage(LoadTaRpcMessage&& load_ta_msg) = default;
313
314    // LoadTaRpcMessage
315    //
316    // Constructs a LoadTaRpcMessage from a moved-in RpcMessage.
317    explicit LoadTaRpcMessage(RpcMessage&& rpc_message)
318        : RpcMessage(fbl::move(rpc_message)) {
319        ZX_DEBUG_ASSERT(is_valid()); // The RPC message passed in should've been valid
320        ZX_DEBUG_ASSERT(command() == RpcMessage::Command::kLoadTa);
321
322        is_valid_ = is_valid_ && TryInitializeMembers();
323    }
324
325    const TEEC_UUID& ta_uuid() const {
326        ZX_DEBUG_ASSERT_MSG(is_valid(), "Accessing invalid OP-TEE RPC message");
327        return ta_uuid_;
328    }
329
330    uint64_t memory_reference_id() const {
331        ZX_DEBUG_ASSERT_MSG(is_valid(), "Accessing invalid OP-TEE RPC message");
332        return mem_id_;
333    }
334
335    uint64_t memory_reference_size() const {
336        ZX_DEBUG_ASSERT_MSG(is_valid(), "Accessing invalid OP-TEE RPC message");
337        return mem_size_;
338    }
339
340    uint64_t memory_reference_offset() const {
341        ZX_DEBUG_ASSERT_MSG(is_valid(), "Accessing invalid OP-TEE RPC message");
342        return mem_offset_;
343    }
344
345    void set_output_ta_size(uint64_t ta_size) {
346        ZX_DEBUG_ASSERT_MSG(is_valid(), "Accessing invalid OP-TEE RPC message");
347        *out_ta_size_ = ta_size;
348    }
349
350protected:
351    static constexpr size_t kNumParams = 2;
352    static constexpr size_t kUuidParamIndex = 0;
353    static constexpr size_t kMemoryReferenceParamIndex = 1;
354
355    TEEC_UUID ta_uuid_;
356    uint64_t mem_id_;
357    uint64_t mem_size_;
358    size_t mem_offset_;
359    uint64_t* out_ta_size_;
360
361private:
362    bool TryInitializeMembers();
363};
364
365} // namespace optee
366