blob: 851bab8a475dc98f7edc7bc0cf426d86fcfe0ef7 [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#pragma once
#include <inttypes.h>
#include <fbl/array.h>
#include <fbl/unique_ptr.h>
#include <tee-client-api/tee-client-types.h>
#include <zircon/assert.h>
#include "optee-smc.h"
#include "shared-memory.h"
#include "util.h"
namespace optee {
// OP-TEE Messages
//
// The majority of data exchange with OP-TEE occurs via OP-TEE messages. These are used in
// conjunction with the OP-TEE SMC Call with Arg function. When that SMC function is invoked,
// OP-TEE will expect a physical pointer to an OP-TEE message to be passed in arguments a1 and a2.
//
// Each message is made up of a header and a variable number of parameters. The relevant fields of
// a message can depend on the command and the context, so these helper classes aim to reduce the
// possibilities of invariant access. For example, in some instances, a field might be an input and
// in others, it might be an output.
struct MessageHeader {
uint32_t command;
uint32_t app_function;
uint32_t session_id;
uint32_t cancel_id;
uint32_t unused;
uint32_t return_code;
uint32_t return_origin;
uint32_t num_params;
};
struct MessageParam {
enum AttributeType : uint64_t {
kAttributeTypeNone = 0x0,
kAttributeTypeValueInput = 0x1,
kAttributeTypeValueOutput = 0x2,
kAttributeTypeValueInOut = 0x3,
kAttributeTypeRegMemInput = 0x5,
kAttributeTypeRegMemOutput = 0x6,
kAttributeTypeRegMemInOut = 0x7,
kAttributeTypeTempMemInput = 0x9,
kAttributeTypeTempMemOutput = 0xa,
kAttributeTypeTempMemInOut = 0xb,
kAttributeTypeMeta = 0x100,
kAttributeTypeFragment = 0x200,
};
struct TemporaryMemory {
uint64_t buffer;
uint64_t size;
uint64_t shared_memory_reference;
};
struct RegisteredMemory {
uint64_t offset;
uint64_t size;
uint64_t shared_memory_reference;
};
union Value {
struct {
uint64_t a;
uint64_t b;
uint64_t c;
} generic;
TEEC_UUID uuid_big_endian;
struct {
SharedMemoryType memory_type;
uint64_t memory_size;
} allocate_memory_specs;
struct {
SharedMemoryType memory_type;
uint64_t memory_id;
} free_memory_specs;
};
uint64_t attribute;
union {
TemporaryMemory temporary_memory;
RegisteredMemory registered_memory;
Value value;
} payload;
};
// MessageParamList
//
// MessageParamList is a non-owning view of the parameters in a Message. It is only valid within
// the lifetime of the Message.
class MessageParamList {
public:
constexpr MessageParamList()
: params_(nullptr), count_(0U) {}
MessageParamList(MessageParam* params, size_t count)
: params_(params), count_(count) {}
size_t size() const { return count_; }
MessageParam* get() const { return params_; }
MessageParam& operator[](size_t i) const {
ZX_DEBUG_ASSERT(i < count_);
return params_[i];
}
MessageParam* begin() const {
return params_;
}
MessageParam* end() const {
return &params_[count_];
}
private:
MessageParam* params_;
size_t count_;
};
template <typename PtrType>
class MessageBase {
static_assert(fbl::is_same<PtrType, SharedMemory*>::value ||
fbl::is_same<PtrType, fbl::unique_ptr<SharedMemory>>::value,
"Template type of MessageBase must be a pointer (raw or smart) to SharedMemory!");
public:
using SharedMemoryPtr = PtrType;
enum Command : uint32_t {
kOpenSession = 0,
kInvokeCommand = 1,
kCloseSession = 2,
kCancel = 3,
kRegisterSharedMemory = 4,
kUnregisterSharedMemory = 5,
};
enum RpcCommand : uint32_t {
kLoadTa = 0,
kReplayMemoryBlock = 1,
kAccessFileSystem = 2,
kGetTime = 3,
kWaitQueue = 4,
kSuspend = 5,
kAllocateMemory = 6,
kFreeMemory = 7
};
explicit MessageBase(SharedMemoryPtr memory)
: memory_(fbl::move(memory)) {
ZX_DEBUG_ASSERT(memory_->size() >= sizeof(MessageHeader));
}
zx_paddr_t paddr() const { return memory_->paddr(); }
MessageHeader* header() const {
return reinterpret_cast<MessageHeader*>(memory_->vaddr());
}
// TODO(rjascani): Change this to return a reference to make ownership rules clearer
MessageParamList params() const {
return MessageParamList(reinterpret_cast<MessageParam*>(header() + 1),
header()->num_params);
}
protected:
static constexpr size_t CalculateSize(size_t num_params) {
return sizeof(MessageHeader) + (sizeof(MessageParam) * num_params);
}
SharedMemoryPtr memory_;
};
// UnmanagedMessage
//
// An OP-TEE message, where the lifetime of the underlying message memory is unmanaged.
// This is useful for cases where the driver has already tracked the underlying message memory and
// just needs to interpret the memory as a Message. This typically occurs when the secure world
// repurposes a chunk of previously allocated memory as a Message for tasks like RPC.
using UnmanagedMessage = MessageBase<SharedMemory*>;
// ManagedMessage
//
// An OP-TEE message, where the lifetime of the underlying message memory is owned and managed by
// a unique_ptr.
// This is useful for cases where the lifetime of the message memory should be coupled with the
// lifetime of the Message, which typically occurs when allocating memory for a new Message to pass
// into the secure world.
using ManagedMessage = MessageBase<fbl::unique_ptr<SharedMemory>>;
// OpenSessionMessage
//
// This OP-TEE message is used to start a session between a client app and trusted app.
class OpenSessionMessage : public ManagedMessage {
public:
static OpenSessionMessage Create(SharedMemoryManager::DriverMemoryPool* pool,
const UuidView& trusted_app,
const UuidView& client_app,
uint32_t client_login,
uint32_t cancel_id,
const fbl::Array<MessageParam>& params);
// Outputs
uint32_t session_id() const { return header()->session_id; }
uint32_t return_code() const { return header()->return_code; }
uint32_t return_origin() const { return header()->return_origin; }
protected:
using ManagedMessage::header; // make header() protected
using ManagedMessage::ManagedMessage; // inherit constructors
};
} // namespace optee