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#include <ddk/debug.h>
6#include <fbl/string_buffer.h>
7#include <fbl/string_piece.h>
8#include <fbl/type_support.h>
9#include <lib/zx/vmo.h>
10#include <tee-client-api/tee-client-types.h>
11
12#include "optee-client.h"
13#include "optee-smc.h"
14
15namespace {
16// RFC 4122 specification dictates a UUID is of the form xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
17constexpr const char* kUuidNameFormat = "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x";
18constexpr size_t kUuidNameLength = 36;
19
20constexpr const char kFirmwarePathPrefix[] = "/boot/lib/firmware/";
21constexpr const char kTaFileExtension[] = ".ta";
22
23// The length of a path to a trusted app consists of the path prefix, the UUID, and file extension
24// Subtracting 1 from sizeof(char[])s to account for the terminating null character.
25constexpr size_t kTaPathLength = (sizeof(kFirmwarePathPrefix) - 1u) +
26                                 kUuidNameLength +
27                                 (sizeof(kTaFileExtension) - 1u);
28
29template <typename SRC_T, typename DST_T>
30static constexpr typename fbl::enable_if<
31    fbl::is_unsigned_integer<SRC_T>::value &&
32    fbl::is_unsigned_integer<DST_T>::value>::type
33SplitInto32BitParts(SRC_T src, DST_T* dst_hi, DST_T* dst_lo) {
34    static_assert(sizeof(SRC_T) == 8, "Type SRC_T should be 64 bits!");
35    static_assert(sizeof(DST_T) >= 4, "Type DST_T should be at least 32 bits!");
36    ZX_DEBUG_ASSERT(dst_hi != nullptr);
37    ZX_DEBUG_ASSERT(dst_lo != nullptr);
38    *dst_hi = static_cast<DST_T>(src >> 32);
39    *dst_lo = static_cast<DST_T>(static_cast<uint32_t>(src));
40}
41
42template <typename SRC_T, typename DST_T>
43static constexpr typename fbl::enable_if<
44    fbl::is_unsigned_integer<SRC_T>::value &&
45    fbl::is_unsigned_integer<DST_T>::value>::type
46JoinFrom32BitParts(SRC_T src_hi, SRC_T src_lo, DST_T* dst) {
47    static_assert(sizeof(SRC_T) >= 4, "Type SRC_T should be at least 32 bits!");
48    static_assert(sizeof(DST_T) >= 8, "Type DST_T should be at least 64-bits!");
49    ZX_DEBUG_ASSERT(dst != nullptr);
50    *dst = (static_cast<DST_T>(src_hi) << 32) | static_cast<DST_T>(static_cast<uint32_t>(src_lo));
51}
52
53// Builds a UUID string from a TEEC_UUID, formatting as per the RFC 4122 specification.
54static fbl::StringBuffer<kUuidNameLength> BuildUuidString(const TEEC_UUID& ta_uuid) {
55    fbl::StringBuffer<kUuidNameLength> buf;
56
57    buf.AppendPrintf(kUuidNameFormat,
58                     ta_uuid.timeLow,
59                     ta_uuid.timeMid,
60                     ta_uuid.timeHiAndVersion,
61                     ta_uuid.clockSeqAndNode[0],
62                     ta_uuid.clockSeqAndNode[1],
63                     ta_uuid.clockSeqAndNode[2],
64                     ta_uuid.clockSeqAndNode[3],
65                     ta_uuid.clockSeqAndNode[4],
66                     ta_uuid.clockSeqAndNode[5],
67                     ta_uuid.clockSeqAndNode[6],
68                     ta_uuid.clockSeqAndNode[7]);
69    return buf;
70}
71
72// Builds the expected path to a trusted application, given its UUID string.
73static fbl::StringBuffer<kTaPathLength> BuildTaPath(const fbl::StringPiece& uuid_str) {
74    fbl::StringBuffer<kTaPathLength> buf;
75
76    buf.Append(kFirmwarePathPrefix);
77    buf.Append(uuid_str);
78    buf.Append(kTaFileExtension);
79
80    return buf;
81}
82}; // namespace
83
84namespace optee {
85zx_status_t OpteeClient::DdkClose(uint32_t flags) {
86    controller_->RemoveClient(this);
87    return ZX_OK;
88}
89
90void OpteeClient::DdkRelease() {
91    // devmgr has given up ownership, so we must clean ourself up.
92    delete this;
93}
94
95zx_status_t OpteeClient::DdkIoctl(uint32_t op, const void* in_buf, size_t in_len, void* out_buf,
96                                  size_t out_len, size_t* out_actual) {
97    if (needs_to_close_) {
98        return ZX_ERR_PEER_CLOSED;
99    }
100
101    switch (op) {
102    case IOCTL_TEE_GET_DESCRIPTION: {
103        if ((out_buf == nullptr) || (out_len != sizeof(tee_ioctl_description_t)) ||
104            (out_actual == nullptr)) {
105            return ZX_ERR_INVALID_ARGS;
106        }
107
108        return controller_->GetDescription(reinterpret_cast<tee_ioctl_description_t*>(out_buf),
109                                           out_actual);
110    }
111    case IOCTL_TEE_OPEN_SESSION: {
112        if ((in_buf == nullptr) || (in_len != sizeof(tee_ioctl_session_request_t)) ||
113            (out_buf == nullptr) || (out_len != sizeof(tee_ioctl_session_t)) ||
114            (out_actual == nullptr)) {
115            return ZX_ERR_INVALID_ARGS;
116        }
117
118        return OpenSession(reinterpret_cast<const tee_ioctl_session_request_t*>(in_buf),
119                           reinterpret_cast<tee_ioctl_session_t*>(out_buf),
120                           out_actual);
121    }
122    }
123
124    return ZX_ERR_NOT_SUPPORTED;
125}
126
127zx_status_t OpteeClient::OpenSession(const tee_ioctl_session_request_t* session_request,
128                                     tee_ioctl_session_t* out_session,
129                                     size_t* out_actual) {
130    ZX_DEBUG_ASSERT(session_request != nullptr);
131    ZX_DEBUG_ASSERT(out_session != nullptr);
132    ZX_DEBUG_ASSERT(out_actual != nullptr);
133    *out_actual = 0;
134
135    UuidView trusted_app{session_request->trusted_app, TEE_IOCTL_UUID_SIZE};
136    UuidView client_app{session_request->client_app, TEE_IOCTL_UUID_SIZE};
137
138    fbl::Array<MessageParam> params;
139    zx_status_t status = ConvertIoctlParamsToOpteeParams(session_request->params,
140                                                         session_request->num_params,
141                                                         &params);
142    if (status != ZX_OK) {
143        zxlogf(ERROR, "optee: invalid ioctl parameters\n");
144        out_session->return_code = TEEC_ERROR_BAD_PARAMETERS;
145        out_session->return_origin = TEEC_ORIGIN_COMMS;
146        return status;
147    }
148
149    OpenSessionMessage message(controller_->driver_pool(),
150                               trusted_app,
151                               client_app,
152                               session_request->client_login,
153                               session_request->cancel_id,
154                               params);
155
156    *out_actual = sizeof(*out_session);
157    uint32_t call_code =
158        controller_->CallWithMessage(message, fbl::BindMember(this, &OpteeClient::HandleRpc));
159    if (call_code != kReturnOk) {
160        out_session->return_code = TEEC_ERROR_COMMUNICATION;
161        out_session->return_origin = TEEC_ORIGIN_COMMS;
162        return status;
163    }
164
165    // TODO(rjascani): Create session object from session id
166    out_session->session_id = message.session_id();
167    out_session->return_code = message.return_code();
168    out_session->return_origin = message.return_origin();
169    // TODO(godtamit): Remove this when all of RPC is implemented
170    zxlogf(INFO,
171           "session ID is 0x%x, return code is 0x%x, return origin is 0x%x\n",
172           out_session->session_id,
173           out_session->return_code,
174           out_session->return_origin);
175
176    return ZX_OK;
177}
178
179zx_status_t OpteeClient::ConvertIoctlParamsToOpteeParams(
180    const tee_ioctl_param_t* params,
181    size_t num_params,
182    fbl::Array<MessageParam>* out_optee_params) {
183    ZX_DEBUG_ASSERT(params != nullptr);
184    ZX_DEBUG_ASSERT(out_optee_params != nullptr);
185
186    fbl::Array<MessageParam> optee_params(new MessageParam[num_params], num_params);
187
188    for (size_t i = 0; i < num_params; ++i) {
189        const tee_ioctl_param_t& ioctl_param = params[i];
190        MessageParam& optee_param = optee_params[i];
191
192        switch (ioctl_param.type) {
193        case TEE_PARAM_TYPE_NONE:
194            optee_param.attribute = MessageParam::kAttributeTypeNone;
195            optee_param.payload.value.generic.a = 0;
196            optee_param.payload.value.generic.b = 0;
197            optee_param.payload.value.generic.c = 0;
198            break;
199        case TEE_PARAM_TYPE_VALUE_INPUT:
200            optee_param.attribute = MessageParam::kAttributeTypeValueInput;
201            optee_param.payload.value.generic.a = ioctl_param.a;
202            optee_param.payload.value.generic.b = ioctl_param.b;
203            optee_param.payload.value.generic.c = ioctl_param.c;
204            break;
205        case TEE_PARAM_TYPE_VALUE_OUTPUT:
206            optee_param.attribute = MessageParam::kAttributeTypeValueOutput;
207            optee_param.payload.value.generic.a = ioctl_param.a;
208            optee_param.payload.value.generic.b = ioctl_param.b;
209            optee_param.payload.value.generic.c = ioctl_param.c;
210            break;
211        case TEE_PARAM_TYPE_VALUE_INOUT:
212            optee_param.attribute = MessageParam::kAttributeTypeValueInOut;
213            optee_param.payload.value.generic.a = ioctl_param.a;
214            optee_param.payload.value.generic.b = ioctl_param.b;
215            optee_param.payload.value.generic.c = ioctl_param.c;
216            break;
217        case TEE_PARAM_TYPE_MEMREF_INPUT:
218        case TEE_PARAM_TYPE_MEMREF_OUTPUT:
219        case TEE_PARAM_TYPE_MEMREF_INOUT:
220            // TODO(rjascani): Add support for memory references
221            return ZX_ERR_NOT_SUPPORTED;
222            break;
223        default:
224            return ZX_ERR_INVALID_ARGS;
225        }
226    }
227
228    *out_optee_params = fbl::move(optee_params);
229    return ZX_OK;
230}
231
232template <typename SharedMemoryPoolTraits>
233zx_status_t OpteeClient::AllocateSharedMemory(size_t size,
234                                              SharedMemoryPool<SharedMemoryPoolTraits>* memory_pool,
235                                              zx_paddr_t* out_phys_addr,
236                                              uint64_t* out_mem_id) {
237    ZX_DEBUG_ASSERT(memory_pool != nullptr);
238    ZX_DEBUG_ASSERT(out_phys_addr != nullptr);
239    ZX_DEBUG_ASSERT(out_mem_id != nullptr);
240
241    // Set these to 0 and overwrite, if necessary, on success path
242    *out_phys_addr = 0;
243    *out_mem_id = 0;
244
245    if (size == 0) {
246        return ZX_ERR_INVALID_ARGS;
247    }
248
249    fbl::unique_ptr<SharedMemory> sh_mem;
250    zx_status_t status = memory_pool->Allocate(size, &sh_mem);
251    if (status != ZX_OK) {
252        return status;
253    }
254
255    *out_phys_addr = sh_mem->paddr();
256
257    // Track the new piece of allocated SharedMemory in the list
258    allocated_shared_memory_.push_back(fbl::move(sh_mem));
259
260    // TODO(godtamit): Move away from memory addresses as memory identifiers
261    //
262    // Make the memory identifier the address of the SharedMemory object
263    auto sh_mem_addr = reinterpret_cast<uintptr_t>(&allocated_shared_memory_.back());
264    *out_mem_id = static_cast<uint64_t>(sh_mem_addr);
265
266    // TODO(godtamit): Remove when all RPC is done
267    zxlogf(INFO,
268           "optee: allocated shared memory at physical addr 0x%" PRIuPTR
269           " with id 0x%" PRIu64 "\n",
270           *out_phys_addr,
271           *out_mem_id);
272
273    return status;
274}
275
276zx_status_t OpteeClient::FreeSharedMemory(uint64_t mem_id) {
277    // Check if client owns memory that matches the memory id
278    SharedMemoryList::iterator mem_iter = FindSharedMemory(mem_id);
279    if (mem_iter == allocated_shared_memory_.end()) {
280        return ZX_ERR_NOT_FOUND;
281    }
282
283    // Destructor of SharedMemory will automatically free block back into pool
284    //
285    // TODO(godtamit): Remove mem_to_free and logging when all of RPC is implemented
286    __UNUSED auto mem_to_free = allocated_shared_memory_.erase(mem_iter);
287    zxlogf(INFO,
288           "optee: successfully freed shared memory at phys 0x%" PRIuPTR "\n",
289           mem_to_free->paddr());
290
291    return ZX_OK;
292}
293
294OpteeClient::SharedMemoryList::iterator OpteeClient::FindSharedMemory(uint64_t mem_id) {
295    // TODO(godtamit): Move away from memory addresses as memory identifiers
296    auto mem_id_ptr_val = static_cast<uintptr_t>(mem_id);
297    return allocated_shared_memory_.find_if(
298        [mem_id_ptr_val](auto& item) {
299            return mem_id_ptr_val == reinterpret_cast<uintptr_t>(&item);
300        });
301}
302
303zx_status_t OpteeClient::HandleRpc(const RpcFunctionArgs& args, RpcFunctionResult* out_result) {
304    zx_status_t status;
305    uint32_t func_code = GetRpcFunctionCode(args.generic.status);
306
307    switch (func_code) {
308    case kRpcFunctionIdAllocateMemory:
309        status = HandleRpcAllocateMemory(args.allocate_memory, &out_result->allocate_memory);
310        break;
311    case kRpcFunctionIdFreeMemory:
312        status = HandleRpcFreeMemory(args.free_memory, &out_result->free_memory);
313        break;
314    case kRpcFunctionIdDeliverIrq:
315        // TODO(godtamit): Remove when all of RPC is implemented
316        zxlogf(INFO, "optee: delivering IRQ\n");
317        // Foreign interrupt detected while in the secure world
318        // Zircon handles this so just mark the RPC as handled
319        status = ZX_OK;
320        break;
321    case kRpcFunctionIdExecuteCommand:
322        status = HandleRpcCommand(args.execute_command, &out_result->execute_command);
323        break;
324    default:
325        status = ZX_ERR_NOT_SUPPORTED;
326        break;
327    }
328
329    // Set the function to return from RPC
330    out_result->generic.func_id = optee::kReturnFromRpcFuncId;
331
332    return status;
333}
334
335zx_status_t OpteeClient::HandleRpcAllocateMemory(const RpcFunctionAllocateMemoryArgs& args,
336                                                 RpcFunctionAllocateMemoryResult* out_result) {
337    ZX_DEBUG_ASSERT(out_result != nullptr);
338
339    zx_paddr_t paddr;
340    uint64_t mem_id;
341
342    zx_status_t status = AllocateSharedMemory(static_cast<size_t>(args.size),
343                                              controller_->driver_pool(),
344                                              &paddr,
345                                              &mem_id);
346    // If allocation failed, AllocateSharedMemory sets paddr and mem_id to 0. Continue with packing
347    // those values into the result regardless.
348
349    // Put the physical address of allocated memory in the args
350    SplitInto32BitParts(paddr, &out_result->phys_addr_upper32, &out_result->phys_addr_lower32);
351
352    // Pack the memory identifier in the args
353    SplitInto32BitParts(mem_id, &out_result->mem_id_upper32, &out_result->mem_id_lower32);
354
355    return status;
356}
357
358zx_status_t OpteeClient::HandleRpcFreeMemory(const RpcFunctionFreeMemoryArgs& args,
359                                             RpcFunctionFreeMemoryResult* out_result) {
360    ZX_DEBUG_ASSERT(out_result != nullptr);
361
362    uint64_t mem_id;
363    JoinFrom32BitParts(args.mem_id_upper32, args.mem_id_lower32, &mem_id);
364
365    return FreeSharedMemory(mem_id);
366}
367
368zx_status_t OpteeClient::HandleRpcCommand(const RpcFunctionExecuteCommandsArgs& args,
369                                          RpcFunctionExecuteCommandsResult* out_result) {
370    uint64_t mem_id;
371    JoinFrom32BitParts(args.msg_mem_id_upper32, args.msg_mem_id_lower32, &mem_id);
372
373    // Make sure memory where message is stored is valid
374    // This dispatcher method only checks that the memory needed for the header is valid. Commands
375    // that require more memory than just the header will need to do further memory checks.
376    SharedMemoryList::iterator mem_iter = FindSharedMemory(mem_id);
377    if (mem_iter == allocated_shared_memory_.end()) {
378        zxlogf(ERROR, "optee: invalid shared memory region passed into RPC command!\n");
379        return ZX_ERR_INVALID_ARGS;
380    } else if (mem_iter->size() < sizeof(MessageHeader)) {
381        zxlogf(ERROR,
382               "optee: shared memory region passed into RPC command is too small\n");
383        return ZX_ERR_INVALID_ARGS;
384    }
385
386    // Read message header from shared memory
387    SharedMemory& msg_mem = *mem_iter;
388    RpcMessage message(&msg_mem);
389    if (!message.is_valid()) {
390        return ZX_ERR_INVALID_ARGS;
391    }
392
393    switch (message.command()) {
394    case RpcMessage::Command::kLoadTa: {
395        LoadTaRpcMessage load_ta_msg(fbl::move(message));
396        if (!load_ta_msg.is_valid()) {
397            return ZX_ERR_INVALID_ARGS;
398        }
399        return HandleRpcCommandLoadTa(&load_ta_msg);
400    }
401    case RpcMessage::Command::kAccessFileSystem:
402        zxlogf(ERROR, "optee: RPC command to access file system recognized but not implemented\n");
403        return ZX_ERR_NOT_SUPPORTED;
404    case RpcMessage::Command::kGetTime:
405        zxlogf(ERROR, "optee: RPC command to access file system recognized but not implemented\n");
406        return ZX_ERR_NOT_SUPPORTED;
407    case RpcMessage::Command::kWaitQueue:
408        zxlogf(ERROR, "optee: RPC command wait queue recognized but not implemented\n");
409        return ZX_ERR_NOT_SUPPORTED;
410    case RpcMessage::Command::kSuspend:
411        zxlogf(ERROR, "optee: RPC command to suspend recognized but not implemented\n");
412        return ZX_ERR_NOT_SUPPORTED;
413    case RpcMessage::Command::kAllocateMemory:
414        return HandleRpcCommandAllocateMemory(&message);
415    case RpcMessage::Command::kFreeMemory:
416        return HandleRpcCommandFreeMemory(&message);
417    case RpcMessage::Command::kPerformSocketIo:
418        zxlogf(ERROR, "optee: RPC command to perform socket IO recognized but not implemented\n");
419        message.set_return_origin(TEEC_ORIGIN_COMMS);
420        message.set_return_code(TEEC_ERROR_NOT_SUPPORTED);
421        return ZX_OK;
422    case RpcMessage::Command::kAccessReplayProtectedMemoryBlock:
423    case RpcMessage::Command::kAccessSqlFileSystem:
424    case RpcMessage::Command::kLoadGprof:
425        zxlogf(INFO, "optee: received unsupported RPC command\n");
426        message.set_return_origin(TEEC_ORIGIN_COMMS);
427        message.set_return_code(TEEC_ERROR_NOT_SUPPORTED);
428        return ZX_OK;
429    default:
430        zxlogf(ERROR,
431               "optee: unrecognized command passed to RPC 0x%" PRIu32 "\n",
432               message.command());
433        message.set_return_origin(TEEC_ORIGIN_COMMS);
434        message.set_return_code(TEEC_ERROR_NOT_SUPPORTED);
435        return ZX_ERR_NOT_SUPPORTED;
436    }
437}
438
439zx_status_t OpteeClient::HandleRpcCommandLoadTa(LoadTaRpcMessage* message) {
440    ZX_DEBUG_ASSERT(message->is_valid());
441
442    // Mark that the return code will originate from driver
443    message->set_return_origin(TEEC_ORIGIN_COMMS);
444
445    if (message->memory_reference_offset() >= message->memory_reference_size() &&
446        message->memory_reference_offset() > 0) {
447        zxlogf(ERROR, "optee: RPC command received a memory offset out of bounds!\n");
448        message->set_return_code(TEEC_ERROR_BAD_PARAMETERS);
449        return ZX_ERR_INVALID_ARGS;
450    }
451
452    // The amount of memory available for loading the TA
453    uint64_t mem_usable_size = message->memory_reference_size() -
454                               message->memory_reference_offset();
455
456    // Try to find the SharedMemory based on the memory id
457    uint8_t* out_ta_mem; // Where to write the TA in memory
458
459    if (message->memory_reference_id() != 0) {
460        SharedMemoryList::iterator out_mem_iter = FindSharedMemory(message->memory_reference_id());
461        if (out_mem_iter == allocated_shared_memory_.end()) {
462            // Valid memory reference could not be found and TEE is not querying size
463            zxlogf(ERROR,
464                   "optee: received invalid memory reference from TEE command to load TA!\n");
465            message->set_return_code(TEEC_ERROR_BAD_PARAMETERS);
466            return ZX_ERR_INVALID_ARGS;
467        } else if (mem_usable_size > out_mem_iter->size()) {
468            // The TEE is claiming the memory reference given is larger than it actually is
469            // We want to catch this in case TEE is buggy
470            zxlogf(ERROR,
471                   "optee: TEE claimed a memory reference's size is larger than the real memory"
472                   "size!\n");
473            message->set_return_code(TEEC_ERROR_BAD_PARAMETERS);
474            return ZX_ERR_INVALID_ARGS;
475        }
476
477        out_ta_mem = reinterpret_cast<uint8_t*>(out_mem_iter->vaddr() +
478                                                message->memory_reference_offset());
479    } else {
480        // TEE is just querying size of TA, so it sent a memory identifier of 0
481        ZX_DEBUG_ASSERT(message->memory_reference_offset() == 0);
482        ZX_DEBUG_ASSERT(message->memory_reference_size() == 0);
483
484        out_ta_mem = nullptr;
485    }
486
487    auto ta_name = BuildUuidString(message->ta_uuid());
488    auto ta_path = BuildTaPath(ta_name.ToStringPiece());
489
490    // Load the trusted app into a VMO
491    size_t ta_size;
492    zx::vmo ta_vmo;
493    zx_status_t status = load_firmware(controller_->zxdev(),
494                                       ta_path.data(),
495                                       ta_vmo.reset_and_get_address(),
496                                       &ta_size);
497
498    if (status != ZX_OK) {
499        if (status == ZX_ERR_NOT_FOUND) {
500            zxlogf(ERROR, "optee: could not find trusted app %s!\n", ta_path.data());
501            message->set_return_code(TEEC_ERROR_ITEM_NOT_FOUND);
502        } else {
503            zxlogf(ERROR, "optee: error loading trusted app %s!\n", ta_path.data());
504            message->set_return_code(TEEC_ERROR_GENERIC);
505        }
506
507        return status;
508    } else if (ta_size == 0) {
509        zxlogf(ERROR, "optee: loaded trusted app %s with unexpected size!\n", ta_path.data());
510        message->set_return_code(TEEC_ERROR_GENERIC);
511        return status;
512    }
513
514    message->set_output_ta_size(static_cast<uint64_t>(ta_size));
515
516    if (out_ta_mem == nullptr) {
517        // TEE is querying the size of the TA
518        message->set_return_code(TEEC_SUCCESS);
519        return ZX_OK;
520    } else if (ta_size > mem_usable_size) {
521        // TEE provided too small of a memory region to write TA into
522        message->set_return_code(TEEC_ERROR_SHORT_BUFFER);
523        return ZX_OK;
524    }
525
526    // TODO(godtamit): in the future, we may want to register the memory as shared and use its VMO,
527    // so we don't have to do a copy of the TA
528    status = ta_vmo.read(out_ta_mem, 0, ta_size);
529    if (status != ZX_OK) {
530        zxlogf(ERROR, "optee: failed to copy trusted app from VMO to shared memory!\n");
531        message->set_return_code(TEEC_ERROR_GENERIC);
532        return status;
533    }
534
535    if (ta_size < mem_usable_size) {
536        // Clear out the rest of the memory after the TA
537        uint8_t* ta_end = out_ta_mem + ta_size;
538        memset(ta_end, 0, mem_usable_size - ta_size);
539    }
540
541    message->set_return_code(TEEC_SUCCESS);
542    return ZX_OK;
543}
544
545zx_status_t OpteeClient::HandleRpcCommandAllocateMemory(RpcMessage* message) {
546    // Mark that the return code will originate from driver
547    message->set_return_origin(TEEC_ORIGIN_COMMS);
548
549    MessageParamList params = message->params();
550    if (params.size() != 1) {
551        zxlogf(ERROR,
552               "optee: RPC command to allocate shared memory received a bad number of parameters!"
553               "\n");
554        message->set_return_code(TEEC_ERROR_BAD_PARAMETERS);
555        return ZX_ERR_INVALID_ARGS;
556    }
557
558    // The first parameter outlines the specifications of the memory to be allocated
559    const MessageParam& memory_specs_param = params[0];
560    if (memory_specs_param.attribute != MessageParam::AttributeType::kAttributeTypeValueInput) {
561        zxlogf(ERROR,
562               "optee: RPC command to allocate shared memory received an unexpected parameter type!"
563               "\n");
564        message->set_return_code(TEEC_ERROR_BAD_PARAMETERS);
565        return ZX_ERR_INVALID_ARGS;
566    }
567
568    // The first parameter in the Message specifies what kind of memory to allocate and how much
569    auto& mem_specs = memory_specs_param.payload.value.allocate_memory_specs;
570    switch (mem_specs.memory_type) {
571    case SharedMemoryType::kApplication:
572    case SharedMemoryType::kKernel:
573        break;
574    case SharedMemoryType::kGlobal:
575        zxlogf(ERROR, "optee: implementation currently does not support global shared memory!\n");
576        return ZX_ERR_NOT_SUPPORTED;
577    default:
578        zxlogf(ERROR,
579               "optee: cannot allocate unknown memory type %" PRIu64 "\n", mem_specs.memory_type);
580        message->set_return_code(TEEC_ERROR_BAD_PARAMETERS);
581        return ZX_ERR_INVALID_ARGS;
582    }
583
584    zx_paddr_t paddr;
585    uint64_t mem_id;
586    zx_status_t status = AllocateSharedMemory(static_cast<size_t>(mem_specs.memory_size),
587                                              controller_->client_pool(),
588                                              &paddr,
589                                              &mem_id);
590    if (status != ZX_OK) {
591        if (status == ZX_ERR_NO_MEMORY) {
592            message->set_return_code(TEEC_ERROR_OUT_OF_MEMORY);
593        } else {
594            message->set_return_code(TEEC_ERROR_GENERIC);
595        }
596
597        return status;
598    }
599
600    // The first parameter in the Message gets reused to output the result of the allocated memory
601    MessageParam& out_memory_result_param = params[0];
602    out_memory_result_param.attribute = MessageParam::AttributeType::kAttributeTypeTempMemOutput;
603
604    MessageParam::TemporaryMemory& out_temp_mem = out_memory_result_param.payload.temporary_memory;
605    out_temp_mem.size = mem_specs.memory_size;
606    out_temp_mem.buffer = static_cast<uint64_t>(paddr);
607    out_temp_mem.shared_memory_reference = mem_id;
608
609    message->set_return_code(TEEC_SUCCESS);
610
611    return status;
612}
613
614zx_status_t OpteeClient::HandleRpcCommandFreeMemory(RpcMessage* message) {
615    // Mark that the return code will originate from driver
616    message->set_return_origin(TEEC_ORIGIN_COMMS);
617
618    MessageParamList params = message->params();
619    if (params.size() != 1) {
620        zxlogf(ERROR,
621               "optee: RPC command to free shared memory received a bad number of parameters!\n");
622        message->set_return_code(TEEC_ERROR_BAD_PARAMETERS);
623        return ZX_ERR_INVALID_ARGS;
624    }
625
626    // The first parameter outlines the specifications of the memory to be freed
627    const MessageParam& memory_specs_param = params[0];
628    if (memory_specs_param.attribute != MessageParam::AttributeType::kAttributeTypeValueInput) {
629        zxlogf(ERROR,
630               "optee: RPC command to free shared memory received an unexpected parameter type!\n");
631        message->set_return_code(TEEC_ERROR_BAD_PARAMETERS);
632        return ZX_ERR_INVALID_ARGS;
633    }
634
635    auto& mem_specs = memory_specs_param.payload.value.free_memory_specs;
636    switch (mem_specs.memory_type) {
637    case SharedMemoryType::kApplication:
638    case SharedMemoryType::kKernel:
639        break;
640    case SharedMemoryType::kGlobal:
641        zxlogf(ERROR, "optee: implementation currently does not support global shared memory!\n");
642        return ZX_ERR_NOT_SUPPORTED;
643    default:
644        zxlogf(ERROR,
645               "optee: cannot free unknown memory type %" PRIu64 "\n", mem_specs.memory_type);
646        message->set_return_code(TEEC_ERROR_BAD_PARAMETERS);
647        return ZX_ERR_INVALID_ARGS;
648    }
649
650    zx_status_t status = FreeSharedMemory(mem_specs.memory_id);
651    if (status != ZX_OK) {
652        if (status == ZX_ERR_NOT_FOUND) {
653            message->set_return_code(TEEC_ERROR_ITEM_NOT_FOUND);
654        } else {
655            message->set_return_code(TEEC_ERROR_GENERIC);
656        }
657
658        return status;
659    }
660
661    message->set_return_code(TEEC_SUCCESS);
662    return status;
663}
664
665} // namespace optee
666