blob: 690ee8dbfd498cea28b4c99fc61ffff27a110e3a [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 <ddktl/device.h>
#include <ddktl/protocol/empty-protocol.h>
#include <fbl/intrusive_double_list.h>
#include <fbl/intrusive_hash_table.h>
#include <fuchsia/hardware/tee/c/fidl.h>
#include "optee-controller.h"
namespace optee {
class OpteeClient;
using OpteeClientBase = ddk::Device<OpteeClient, ddk::Closable, ddk::Messageable>;
using OpteeClientProtocol = ddk::EmptyProtocol<ZX_PROTOCOL_TEE>;
// The Optee driver allows for simultaneous access from different processes. The OpteeClient object
// is a distinct device instance for each client connection. This allows for per-instance state to
// be managed together. For example, if a client closes the device, OpteeClient can free all of the
// allocated shared memory buffers and sessions that were created by that client without interfering
// with other active clients.
class OpteeClient : public OpteeClientBase,
public OpteeClientProtocol,
public fbl::DoublyLinkedListable<OpteeClient*> {
public:
explicit OpteeClient(OpteeController* controller)
: OpteeClientBase(controller->zxdev()), controller_(controller) {}
OpteeClient(const OpteeClient&) = delete;
OpteeClient& operator=(const OpteeClient&) = delete;
zx_status_t DdkClose(uint32_t flags);
void DdkRelease();
zx_status_t DdkMessage(fidl_msg_t* msg, fidl_txn_t* txn);
// If the Controller is unbound, we need to notify all clients that the device is no longer
// available. The Controller will invoke this function so that any subsequent calls on the
// client will notify the caller that the peer has closed.
void MarkForClosing() { needs_to_close_ = true; }
// FIDL Handlers
zx_status_t GetOsInfo(fidl_txn_t* txn) const;
zx_status_t OpenSession(const fuchsia_hardware_tee_Uuid* trusted_app,
const fuchsia_hardware_tee_ParameterSet* parameter_set,
fidl_txn_t* txn);
zx_status_t InvokeCommand(uint32_t session_id, uint32_t command_id,
const fuchsia_hardware_tee_ParameterSet* parameter_set,
fidl_txn_t* txn);
zx_status_t CloseSession(uint32_t session_id, fidl_txn_t* txn);
private:
using SharedMemoryList = fbl::DoublyLinkedList<fbl::unique_ptr<SharedMemory>>;
struct OpteeSession : fbl::SinglyLinkedListable<fbl::unique_ptr<OpteeSession>> {
OpteeSession(uint32_t id)
: id(id) {}
uint32_t GetKey() const { return id; }
static size_t GetHash(uint32_t key) { return static_cast<size_t>(key); }
uint32_t id;
};
using OpenSessionsTable = fbl::HashTable<uint32_t, fbl::unique_ptr<OpteeSession>>;
zx_status_t CloseSession(uint32_t session_id);
// Attempts to allocate a block of SharedMemory from a designated memory pool.
//
// On success:
// * Tracks the allocated memory block in the allocated_shared_memory_ list.
// * Gives the physical address of the memory block in out_phys_addr
// * Gives an identifier for the memory block in out_mem_id. This identifier will later be
// used to free the memory block.
//
// On failure:
// * Sets the physical address of the memory block to 0.
// * Sets the identifier of the memory block to 0.
template <typename SharedMemoryPoolTraits>
zx_status_t AllocateSharedMemory(size_t size,
SharedMemoryPool<SharedMemoryPoolTraits>* memory_pool,
zx_paddr_t* out_phys_addr,
uint64_t* out_mem_id);
// Frees a block of SharedMemory that was previously allocated by the driver.
//
// Parameters:
// * mem_id: The identifier for the memory block to free, given at allocation time.
//
// Returns:
// * ZX_OK: Successfully freed the memory.
// * ZX_ERR_NOT_FOUND: Could not find a block corresponding to the identifier given.
zx_status_t FreeSharedMemory(uint64_t mem_id);
// Attempts to find a previously allocated block of memory.
//
// Returns:
// * If the block was found, an iterator object pointing to the SharedMemory block.
// * Otherwise, an iterator object pointing to the end of allocated_shared_memory_.
SharedMemoryList::iterator FindSharedMemory(uint64_t mem_id);
// Gets a pointer to the memory referenced by a SharedMemoryList iterator.
//
// Parameters:
// * mem_iter: The SharedMemoryList iterator object.
// * min_size: The minimum amount of usable memory expected. This is compared against the size
// of the SharedMemory block minus the offset.
// * offset: The offset from the beginning of the SharedMemory to return.
//
// Returns:
// * If the iterator points to a valid memory reference of proper size, a uint8_t pointer to
// the beginning of the memory plus the requested offset.
// * Otherwise, a nullptr.
void* GetSharedMemoryPointer(SharedMemoryList::iterator mem_iter,
size_t min_size,
zx_off_t offset);
//
// OP-TEE RPC Function Handlers
//
// The section below outlines the functions that are used to parse and fulfill RPC commands from
// the OP-TEE secure world.
//
// There are two main "types" of functions defined and can be identified by their naming
// convention:
// * "HandleRpc" functions handle the first layer of commands. These are basic, fundamental
// commands used for critical tasks like setting up shared memory, notifying the normal world
// of interrupts, and accessing the second layer of commands.
// * "HandleRpcCommand" functions handle the second layer of commands. These are more advanced
// commands, like loading trusted applications and accessing the file system. These make up
// the bulk of RPC commands once a session is open.
// * HandleRpcCommand is actually a specific command in the first layer that can be invoked
// once initial shared memory is set up for the command message.
//
// Because these RPCs are the primary channel through which the normal and secure worlds mediate
// shared resources, it is important that handlers in the normal world are resilient to errors
// from the trusted world. While we don't expect that the trusted world is actively malicious in
// any way, we do want handlers to be cautious against buggy or unexpected behaviors, as we do
// not want errors propagating into the normal world (especially with resources like memory).
// Identifies and dispatches the first layer of RPC command requests.
zx_status_t HandleRpc(const RpcFunctionArgs& args, RpcFunctionResult* out_result);
zx_status_t HandleRpcAllocateMemory(const RpcFunctionAllocateMemoryArgs& args,
RpcFunctionAllocateMemoryResult* out_result);
zx_status_t HandleRpcFreeMemory(const RpcFunctionFreeMemoryArgs& args,
RpcFunctionFreeMemoryResult* out_result);
// Identifies and dispatches the second layer of RPC command requests.
//
// This dispatcher is actually a specific command in the first layer of RPC requests.
zx_status_t HandleRpcCommand(const RpcFunctionExecuteCommandsArgs& args,
RpcFunctionExecuteCommandsResult* out_result);
zx_status_t HandleRpcCommandLoadTa(LoadTaRpcMessage* message);
zx_status_t HandleRpcCommandAllocateMemory(AllocateMemoryRpcMessage* message);
zx_status_t HandleRpcCommandFreeMemory(FreeMemoryRpcMessage* message);
zx_status_t HandleRpcCommandFileSystem(FileSystemRpcMessage* message);
static fuchsia_hardware_tee_Device_ops_t kFidlOps;
OpteeController* controller_;
bool needs_to_close_ = false;
SharedMemoryList allocated_shared_memory_;
OpenSessionsTable open_sessions_;
};
} // namespace optee