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 &params_[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