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 ¶ms_[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