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 <fbl/vector.h> 12 #include <lib/zx/vmo.h> 13 #include <tee-client-api/tee-client-types.h> 14 #include <zircon/assert.h> 15 16 #include <utility> 17 18 #include "optee-smc.h" 19 #include "shared-memory.h" 20 #include "util.h" 21 22 namespace optee { 23 24 // OP-TEE Messages 25 // 26 // The majority of data exchange with OP-TEE occurs via OP-TEE messages. These are used in 27 // conjunction with the OP-TEE SMC Call with Arg function. When that SMC function is invoked, 28 // OP-TEE will expect a physical pointer to an OP-TEE message to be passed in arguments a1 and a2. 29 // 30 // Each message is made up of a header and a variable number of parameters. The relevant fields of 31 // a message can depend on the command and the context, so these helper classes aim to reduce the 32 // possibilities of invariant access. For example, in some instances, a field might be an input and 33 // in others, it might be an output. 34 35 struct MessageHeader { 36 uint32_t command; 37 uint32_t app_function; 38 uint32_t session_id; 39 uint32_t cancel_id; 40 41 uint32_t unused; 42 uint32_t return_code; 43 uint32_t return_origin; 44 uint32_t num_params; 45 }; 46 47 struct MessageParam { 48 enum AttributeType : uint64_t { 49 kAttributeTypeNone = 0x0, 50 kAttributeTypeValueInput = 0x1, 51 kAttributeTypeValueOutput = 0x2, 52 kAttributeTypeValueInOut = 0x3, 53 kAttributeTypeRegMemInput = 0x5, 54 kAttributeTypeRegMemOutput = 0x6, 55 kAttributeTypeRegMemInOut = 0x7, 56 kAttributeTypeTempMemInput = 0x9, 57 kAttributeTypeTempMemOutput = 0xa, 58 kAttributeTypeTempMemInOut = 0xb, 59 60 kAttributeTypeMeta = 0x100, 61 kAttributeTypeFragment = 0x200, 62 }; 63 64 struct TemporaryMemory { 65 uint64_t buffer; 66 uint64_t size; 67 uint64_t shared_memory_reference; 68 }; 69 70 struct RegisteredMemory { 71 uint64_t offset; 72 uint64_t size; 73 uint64_t shared_memory_reference; 74 }; 75 76 union Value { 77 struct { 78 uint64_t a; 79 uint64_t b; 80 uint64_t c; 81 } generic; 82 TEEC_UUID uuid_big_endian; 83 struct { 84 uint64_t memory_type; 85 uint64_t memory_size; 86 } allocate_memory_specs; 87 struct { 88 uint64_t memory_type; 89 uint64_t memory_id; 90 } free_memory_specs; 91 struct { 92 uint64_t command_number; 93 } file_system_command; 94 }; 95 96 uint64_t attribute; 97 union { 98 TemporaryMemory temporary_memory; 99 RegisteredMemory registered_memory; 100 Value value; 101 } payload; 102 }; 103 104 // MessageParamList 105 // 106 // MessageParamList is a non-owning view of the parameters in a Message. It is only valid within 107 // the lifetime of the Message. 108 class MessageParamList { 109 public: MessageParamList()110 constexpr MessageParamList() 111 : params_(nullptr), count_(0U) {} 112 MessageParamList(MessageParam * params,size_t count)113 MessageParamList(MessageParam* params, size_t count) 114 : params_(params), count_(count) {} 115 size()116 size_t size() const { return count_; } get()117 MessageParam* get() const { return params_; } 118 119 MessageParam& operator[](size_t i) const { 120 ZX_DEBUG_ASSERT(i < count_); 121 return params_[i]; 122 } 123 begin()124 MessageParam* begin() const { 125 return params_; 126 } end()127 MessageParam* end() const { 128 return ¶ms_[count_]; 129 } 130 131 private: 132 MessageParam* params_; 133 size_t count_; 134 }; 135 136 template <typename PtrType> 137 class MessageBase { 138 static_assert(fbl::is_same<PtrType, SharedMemory*>::value || 139 fbl::is_same<PtrType, fbl::unique_ptr<SharedMemory>>::value, 140 "Template type of MessageBase must be a pointer (raw or smart) to SharedMemory!"); 141 142 public: 143 using SharedMemoryPtr = PtrType; 144 paddr()145 zx_paddr_t paddr() const { 146 ZX_DEBUG_ASSERT_MSG(is_valid(), "Accessing uninitialized OP-TEE message"); 147 return memory_->paddr(); 148 } 149 150 // TODO(godtamit): Move this to protected once all usages of it outside are removed 151 // TODO(rjascani): Change this to return a reference to make ownership rules clearer params()152 MessageParamList params() const { 153 ZX_DEBUG_ASSERT_MSG(is_valid(), "Accessing uninitialized OP-TEE message"); 154 return MessageParamList(reinterpret_cast<MessageParam*>(header() + 1), 155 header()->num_params); 156 } 157 158 // Returns whether the message is valid. This must be true to access any class-specific field. is_valid()159 bool is_valid() const { return memory_ != nullptr; } 160 161 protected: CalculateSize(size_t num_params)162 static constexpr size_t CalculateSize(size_t num_params) { 163 return sizeof(MessageHeader) + (sizeof(MessageParam) * num_params); 164 } 165 166 // MessageBase 167 // 168 // Move constructor for MessageBase. MessageBase(MessageBase && msg)169 MessageBase(MessageBase&& msg) 170 : memory_(std::move(msg.memory_)) { 171 msg.memory_ = nullptr; 172 } 173 174 // Move-only, so explicitly delete copy constructor and copy assignment operator for clarity 175 MessageBase(const MessageBase&) = delete; 176 MessageBase& operator=(const MessageBase&) = delete; 177 MessageBase()178 explicit MessageBase() 179 : memory_(nullptr) {} 180 MessageBase(SharedMemoryPtr memory)181 explicit MessageBase(SharedMemoryPtr memory) 182 : memory_(std::move(memory)) {} 183 header()184 MessageHeader* header() const { 185 ZX_DEBUG_ASSERT_MSG(is_valid(), "Accessing uninitialized OP-TEE message"); 186 return reinterpret_cast<MessageHeader*>(memory_->vaddr()); 187 } 188 189 SharedMemoryPtr memory_; 190 }; 191 192 // Message 193 // 194 // A normal message from the rich world (REE). 195 class Message : public MessageBase<fbl::unique_ptr<SharedMemory>> { 196 public: 197 enum Command : uint32_t { 198 kOpenSession = 0, 199 kInvokeCommand = 1, 200 kCloseSession = 2, 201 kCancel = 3, 202 kRegisterSharedMemory = 4, 203 kUnregisterSharedMemory = 5, 204 }; 205 206 // Message 207 // 208 // Move constructor for Message. Uses the default implicit implementation. 209 Message(Message&&) = default; 210 211 // Move-only, so explicitly delete copy constructor and copy assignment operator for clarity 212 Message(const Message&) = delete; 213 Message& operator=(const Message&) = delete; 214 215 protected: 216 using MessageBase::MessageBase; // inherit constructors 217 218 bool TryInitializeParameters(size_t starting_param_index, 219 const fuchsia_hardware_tee_ParameterSet& parameter_set, 220 SharedMemoryManager::ClientMemoryPool* temp_memory_pool); 221 bool TryInitializeValue(const fuchsia_hardware_tee_Value& value, MessageParam* out_param); 222 bool TryInitializeBuffer(const fuchsia_hardware_tee_Buffer& buffer, 223 SharedMemoryManager::ClientMemoryPool* temp_memory_pool, 224 MessageParam* out_param); 225 226 zx_status_t CreateOutputParameterSet(size_t starting_param_index, 227 fuchsia_hardware_tee_ParameterSet* out_parameter_set); 228 229 private: 230 // This nested class is just a container for pairing a vmo with a chunk of shared memory. It 231 // can be used to synchronize the user provided buffers with the TEE shared memory. 232 class TemporarySharedMemory { 233 public: 234 explicit TemporarySharedMemory(zx::vmo vmo, uint64_t vmo_offset, size_t size, 235 fbl::unique_ptr<SharedMemory>); 236 237 TemporarySharedMemory(TemporarySharedMemory&&) = default; 238 TemporarySharedMemory& operator=(TemporarySharedMemory&&) = default; 239 vmo_offset()240 uint64_t vmo_offset() const { return vmo_offset_; } is_valid()241 bool is_valid() const { return vmo_.is_valid() && shared_memory_ != nullptr; } 242 243 zx_status_t SyncToSharedMemory(); 244 zx_status_t SyncToVmo(size_t actual_size); 245 246 zx_handle_t ReleaseVmo(); 247 248 private: 249 zx::vmo vmo_; 250 uint64_t vmo_offset_; 251 size_t size_; 252 fbl::unique_ptr<SharedMemory> shared_memory_; 253 }; 254 255 fuchsia_hardware_tee_Value CreateOutputValueParameter(const MessageParam& optee_param); 256 zx_status_t CreateOutputBufferParameter(const MessageParam& optee_param, 257 fuchsia_hardware_tee_Buffer* out_buffer); 258 259 fbl::Vector<TemporarySharedMemory> allocated_temp_memory_; 260 }; 261 262 // OpenSessionMessage 263 // 264 // This OP-TEE message is used to start a session between a client app and trusted app. 265 class OpenSessionMessage : public Message { 266 public: 267 explicit OpenSessionMessage(SharedMemoryManager::DriverMemoryPool* message_pool, 268 SharedMemoryManager::ClientMemoryPool* temp_memory_pool, 269 const Uuid& trusted_app, 270 const fuchsia_hardware_tee_ParameterSet& parameter_set); 271 272 // Outputs session_id()273 uint32_t session_id() const { return header()->session_id; } return_code()274 uint32_t return_code() const { return header()->return_code; } return_origin()275 uint32_t return_origin() const { return header()->return_origin; } 276 CreateOutputParameterSet(fuchsia_hardware_tee_ParameterSet * out_parameter_set)277 zx_status_t CreateOutputParameterSet(fuchsia_hardware_tee_ParameterSet* out_parameter_set) { 278 return Message::CreateOutputParameterSet(kNumFixedOpenSessionParams, out_parameter_set); 279 } 280 281 protected: 282 using Message::header; // make header() protected 283 284 static constexpr size_t kNumFixedOpenSessionParams = 2; 285 static constexpr size_t kTrustedAppParamIndex = 0; 286 static constexpr size_t kClientAppParamIndex = 1; 287 }; 288 289 // CloseSessionMessage 290 // 291 // This OP-TEE message is used to close an existing open session. 292 class CloseSessionMessage : public Message { 293 public: 294 explicit CloseSessionMessage(SharedMemoryManager::DriverMemoryPool* message_pool, 295 uint32_t session_id); 296 297 // Outputs return_code()298 uint32_t return_code() const { return header()->return_code; } return_origin()299 uint32_t return_origin() const { return header()->return_origin; } 300 301 protected: 302 using Message::header; // make header() protected 303 304 static constexpr size_t kNumParams = 0; 305 }; 306 307 // InvokeCommandMessage 308 // 309 // This OP-TEE message is used to invoke a command on a session between client app and trusted app. 310 class InvokeCommandMessage : public Message { 311 public: 312 explicit InvokeCommandMessage(SharedMemoryManager::DriverMemoryPool* message_pool, 313 SharedMemoryManager::ClientMemoryPool* temp_memory_pool, 314 uint32_t session_id, uint32_t command_id, 315 const fuchsia_hardware_tee_ParameterSet& parameter_set); 316 317 // Outputs return_code()318 uint32_t return_code() const { return header()->return_code; } return_origin()319 uint32_t return_origin() const { return header()->return_origin; } 320 CreateOutputParameterSet(fuchsia_hardware_tee_ParameterSet * out_parameter_set)321 zx_status_t CreateOutputParameterSet(fuchsia_hardware_tee_ParameterSet* out_parameter_set) { 322 return Message::CreateOutputParameterSet(0, out_parameter_set); 323 } 324 }; 325 326 // RpcMessage 327 // 328 // A message originating from the trusted world (TEE) specifying the details of a RPC request. 329 class RpcMessage : public MessageBase<SharedMemory*> { 330 public: 331 enum Command : uint32_t { 332 kLoadTa = 0, 333 kAccessReplayProtectedMemoryBlock = 1, 334 kAccessFileSystem = 2, 335 kGetTime = 3, 336 kWaitQueue = 4, 337 kSuspend = 5, 338 kAllocateMemory = 6, 339 kFreeMemory = 7, 340 kAccessSqlFileSystem = 8, 341 kLoadGprof = 9, 342 kPerformSocketIo = 10 343 }; 344 345 // RpcMessage 346 // 347 // Move constructor for RpcMessage. RpcMessage(RpcMessage && rpc_msg)348 RpcMessage(RpcMessage&& rpc_msg) 349 : MessageBase(std::move(rpc_msg)), 350 is_valid_(std::move(rpc_msg.is_valid_)) { 351 rpc_msg.is_valid_ = false; 352 } 353 354 // Move-only, so explicitly delete copy constructor and copy assignment operator for clarity 355 RpcMessage(const RpcMessage&) = delete; 356 RpcMessage& operator=(const RpcMessage&) = delete; 357 358 // RpcMessage 359 // 360 // Constructs an instance of an RpcMessage from a backing SharedMemory object. 361 // 362 // Parameters: 363 // * memory: A pointer to the SharedMemory object backing the RpcMessage. This pointer must 364 // be non-null and valid. RpcMessage(SharedMemory * memory)365 explicit RpcMessage(SharedMemory* memory) 366 : MessageBase(memory), is_valid_(TryInitializeMembers()) {} 367 command()368 uint32_t command() const { 369 ZX_DEBUG_ASSERT_MSG(is_valid(), "Accessing invalid OP-TEE RPC message"); 370 return header()->command; 371 } 372 set_return_origin(uint32_t return_origin)373 void set_return_origin(uint32_t return_origin) { 374 ZX_DEBUG_ASSERT_MSG(is_valid(), "Accessing invalid OP-TEE RPC message"); 375 header()->return_origin = return_origin; 376 } 377 set_return_code(uint32_t return_code)378 void set_return_code(uint32_t return_code) { 379 ZX_DEBUG_ASSERT_MSG(is_valid(), "Accessing invalid OP-TEE RPC message"); 380 header()->return_code = return_code; 381 } 382 383 // Returns whether the message is a valid RpcMessage. This must be true to access any 384 // class-specific field. is_valid()385 bool is_valid() const { return is_valid_; } 386 387 protected: 388 bool is_valid_; 389 390 private: 391 bool TryInitializeMembers(); 392 }; 393 394 // LoadTaRpcMessage 395 // 396 // A RpcMessage that should be interpreted with the command of loading a trusted application. 397 // A RpcMessage can be converted into a LoadTaRpcMessage via a constructor. 398 class LoadTaRpcMessage : public RpcMessage { 399 public: 400 // LoadTaRpcMessage 401 // 402 // Move constructor for LoadTaRpcMessage. Uses the default implicit implementation. 403 LoadTaRpcMessage(LoadTaRpcMessage&&) = default; 404 405 // LoadTaRpcMessage 406 // 407 // Constructs a LoadTaRpcMessage from a moved-in RpcMessage. LoadTaRpcMessage(RpcMessage && rpc_message)408 explicit LoadTaRpcMessage(RpcMessage&& rpc_message) 409 : RpcMessage(std::move(rpc_message)) { 410 ZX_DEBUG_ASSERT(is_valid()); // The RPC message passed in should've been valid 411 ZX_DEBUG_ASSERT(command() == RpcMessage::Command::kLoadTa); 412 413 is_valid_ = is_valid_ && TryInitializeMembers(); 414 } 415 ta_uuid()416 const TEEC_UUID& ta_uuid() const { 417 ZX_DEBUG_ASSERT_MSG(is_valid(), "Accessing invalid OP-TEE RPC message"); 418 return ta_uuid_; 419 } 420 memory_reference_id()421 uint64_t memory_reference_id() const { 422 ZX_DEBUG_ASSERT_MSG(is_valid(), "Accessing invalid OP-TEE RPC message"); 423 return mem_id_; 424 } 425 memory_reference_size()426 size_t memory_reference_size() const { 427 ZX_DEBUG_ASSERT_MSG(is_valid(), "Accessing invalid OP-TEE RPC message"); 428 return mem_size_; 429 } 430 memory_reference_offset()431 zx_off_t memory_reference_offset() const { 432 ZX_DEBUG_ASSERT_MSG(is_valid(), "Accessing invalid OP-TEE RPC message"); 433 return mem_offset_; 434 } 435 set_output_ta_size(size_t ta_size)436 void set_output_ta_size(size_t ta_size) { 437 ZX_DEBUG_ASSERT_MSG(is_valid(), "Accessing invalid OP-TEE RPC message"); 438 ZX_DEBUG_ASSERT(out_ta_size_ != nullptr); 439 *out_ta_size_ = static_cast<uint64_t>(ta_size); 440 } 441 442 protected: 443 static constexpr size_t kNumParams = 2; 444 static constexpr size_t kUuidParamIndex = 0; 445 static constexpr size_t kMemoryReferenceParamIndex = 1; 446 447 TEEC_UUID ta_uuid_; 448 uint64_t mem_id_; 449 size_t mem_size_; 450 zx_off_t mem_offset_; 451 uint64_t* out_ta_size_; 452 453 private: 454 bool TryInitializeMembers(); 455 }; 456 457 // AllocateMemoryRpcMessage 458 // 459 // A RpcMessage that should be interpreted with the command of allocating shared memory. 460 // A RpcMessage can be converted into a AllocateMemoryRpcMessage via a constructor. 461 class AllocateMemoryRpcMessage : public RpcMessage { 462 public: 463 // AllocateMemoryRpcMessage 464 // 465 // Move constructor for AllocateMemoryRpcMessage. Uses the default implicit implementation. 466 AllocateMemoryRpcMessage(AllocateMemoryRpcMessage&&) = default; 467 468 // AllocateMemoryRpcMessage 469 // 470 // Constructs a AllocateMemoryRpcMessage from a moved-in RpcMessage. AllocateMemoryRpcMessage(RpcMessage && rpc_message)471 explicit AllocateMemoryRpcMessage(RpcMessage&& rpc_message) 472 : RpcMessage(std::move(rpc_message)) { 473 ZX_DEBUG_ASSERT(is_valid()); // The RPC message passed in should've been valid 474 ZX_DEBUG_ASSERT(command() == RpcMessage::Command::kAllocateMemory); 475 476 is_valid_ = is_valid_ && TryInitializeMembers(); 477 } 478 memory_type()479 SharedMemoryType memory_type() const { 480 ZX_DEBUG_ASSERT_MSG(is_valid(), "Accessing invalid OP-TEE RPC message"); 481 return memory_type_; 482 } 483 memory_size()484 size_t memory_size() const { 485 ZX_DEBUG_ASSERT_MSG(is_valid(), "Accessing invalid OP-TEE RPC message"); 486 return static_cast<size_t>(memory_size_); 487 } 488 set_output_memory_size(size_t memory_size)489 void set_output_memory_size(size_t memory_size) { 490 ZX_DEBUG_ASSERT_MSG(is_valid(), "Accessing invalid OP-TEE RPC message"); 491 ZX_DEBUG_ASSERT(out_memory_size_ != nullptr); 492 *out_memory_size_ = static_cast<uint64_t>(memory_size); 493 } 494 set_output_buffer(zx_paddr_t buffer_paddr)495 void set_output_buffer(zx_paddr_t buffer_paddr) { 496 ZX_DEBUG_ASSERT_MSG(is_valid(), "Accessing invalid OP-TEE RPC message"); 497 ZX_DEBUG_ASSERT(out_memory_buffer_ != nullptr); 498 *out_memory_buffer_ = static_cast<uint64_t>(buffer_paddr); 499 } 500 set_output_memory_identifier(uint64_t id)501 void set_output_memory_identifier(uint64_t id) { 502 ZX_DEBUG_ASSERT_MSG(is_valid(), "Accessing invalid OP-TEE RPC message"); 503 ZX_DEBUG_ASSERT(out_memory_id_ != nullptr); 504 *out_memory_id_ = id; 505 } 506 507 protected: 508 static constexpr size_t kNumParams = 1; 509 static constexpr size_t kMemorySpecsParamIndex = 0; 510 static constexpr size_t kOutputTemporaryMemoryParamIndex = 0; 511 512 SharedMemoryType memory_type_; 513 size_t memory_size_; 514 uint64_t* out_memory_size_; 515 uint64_t* out_memory_buffer_; 516 uint64_t* out_memory_id_; 517 518 private: 519 bool TryInitializeMembers(); 520 }; 521 522 // FreeMemoryRpcMessage 523 // 524 // A RpcMessage that should be interpreted with the command of freeing shared memory. 525 // A RpcMessage can be converted into a FreeMemoryRpcMessage via a constructor. 526 class FreeMemoryRpcMessage : public RpcMessage { 527 public: 528 // FreeMemoryRpcMessage 529 // 530 // Move constructor for FreeMemoryRpcMessage. Uses the default implicit implementation. 531 FreeMemoryRpcMessage(FreeMemoryRpcMessage&&) = default; 532 533 // FreeMemoryRpcMessage 534 // 535 // Constructs a FreeMemoryRpcMessage from a moved-in RpcMessage. FreeMemoryRpcMessage(RpcMessage && rpc_message)536 explicit FreeMemoryRpcMessage(RpcMessage&& rpc_message) 537 : RpcMessage(std::move(rpc_message)) { 538 ZX_DEBUG_ASSERT(is_valid()); // The RPC message passed in should've been valid 539 ZX_DEBUG_ASSERT(command() == RpcMessage::Command::kFreeMemory); 540 541 is_valid_ = is_valid_ && TryInitializeMembers(); 542 } 543 memory_type()544 SharedMemoryType memory_type() const { 545 ZX_DEBUG_ASSERT_MSG(is_valid(), "Accessing invalid OP-TEE RPC message"); 546 return memory_type_; 547 } 548 memory_identifier()549 uint64_t memory_identifier() const { 550 ZX_DEBUG_ASSERT_MSG(is_valid(), "Accessing invalid OP-TEE RPC message"); 551 return memory_id_; 552 } 553 554 protected: 555 static constexpr size_t kNumParams = 1; 556 static constexpr size_t kMemorySpecsParamIndex = 0; 557 558 SharedMemoryType memory_type_; 559 uint64_t memory_id_; 560 561 private: 562 bool TryInitializeMembers(); 563 }; 564 565 // FileSystemRpcMessage 566 // 567 // A RpcMessage that should be interpreted with the command of accessing the file system. 568 // A RpcMessage can be converted into a FileSystemRpcMessage via a constructor. 569 class FileSystemRpcMessage : public RpcMessage { 570 public: 571 enum FileSystemCommand : uint64_t { 572 kOpenFile = 0, 573 kCreateFile = 1, 574 kCloseFile = 2, 575 kReadFile = 3, 576 kWriteFile = 4, 577 kTruncateFile = 5, 578 kRemoveFile = 6, 579 kRenameFile = 7, 580 kOpenDirectory = 8, 581 kCloseDirectory = 9, 582 kGetNextFileInDirectory = 10 583 }; 584 585 // FileSystemRpcMessage 586 // 587 // Move constructor for FileSystemRpcMessage. Uses the default implicit implementation. 588 FileSystemRpcMessage(FileSystemRpcMessage&&) = default; 589 590 // FileSystemRpcMessage 591 // 592 // Constructs a FileSystemRpcMessage from a moved-in RpcMessage. FileSystemRpcMessage(RpcMessage && rpc_message)593 explicit FileSystemRpcMessage(RpcMessage&& rpc_message) 594 : RpcMessage(std::move(rpc_message)) { 595 ZX_DEBUG_ASSERT(is_valid()); // The RPC message passed in should've been valid 596 ZX_DEBUG_ASSERT(command() == RpcMessage::Command::kAccessFileSystem); 597 598 is_valid_ = is_valid_ && TryInitializeMembers(); 599 } 600 file_system_command()601 FileSystemCommand file_system_command() const { 602 ZX_DEBUG_ASSERT_MSG(is_valid(), "Accessing invalid OP-TEE RPC message"); 603 return fs_command_; 604 } 605 606 protected: 607 static constexpr size_t kNumFileSystemCommands = 11; 608 static constexpr size_t kMinNumParams = 1; 609 static constexpr size_t kFileSystemCommandParamIndex = 0; 610 611 FileSystemCommand fs_command_; 612 613 private: 614 bool TryInitializeMembers(); 615 }; 616 617 } // namespace optee 618