blob: 41391445fcf7086a0ceb789f4951414bda39c4ed [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 <atomic>
#include <filesystem>
#include <optional>
#include <string_view>
#include <ddktl/device.h>
#include <ddktl/protocol/empty-protocol.h>
#include <fbl/intrusive_double_list.h>
#include <fbl/intrusive_hash_table.h>
#include <fuchsia/tee/c/fidl.h>
#include <lib/zx/channel.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, zx::channel service_provider_channel)
: OpteeClientBase(controller->zxdev()), controller_(controller),
service_provider_channel_(std::move(service_provider_channel)) {}
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_tee_Uuid* trusted_app,
const fuchsia_tee_ParameterSet* parameter_set,
fidl_txn_t* txn);
zx_status_t InvokeCommand(uint32_t session_id, uint32_t command_id,
const fuchsia_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>>;
// TODO(godtamit): Move to `std::unordered_map` when BLD-413 is complete
struct FileSystemObject : fbl::SinglyLinkedListable<std::unique_ptr<FileSystemObject>> {
FileSystemObject(uint64_t id, zx::channel&& channel)
: id(id), channel(std::move(channel)) {}
uint64_t GetKey() const { return id; }
static size_t GetHash(uint64_t key) { return static_cast<size_t>(key); }
uint64_t id;
zx::channel channel;
};
using FileSystemObjectTable = fbl::HashTable<uint64_t, std::unique_ptr<FileSystemObject>>;
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);
// Attempts to get a slice of `SharedMemory` representing an OP-TEE memory reference.
//
// Parameters:
// * mem_iter: The `SharedMemoryList::iterator` object pointing to the `SharedMemory`.
// This may point to the end of `allocated_shared_memory_`.
// * base_paddr: The starting base physical address of the slice.
// * size: The size of the slice.
//
// Returns:
// * If `mem_iter` is valid and the slice bounds are valid, an initialized `std::optional` with
// the `SharedMemoryView`.
// * Otherwise, an uninitialized `std::optional`.
std::optional<SharedMemoryView> GetMemoryReference(SharedMemoryList::iterator mem_iter,
zx_paddr_t base_paddr,
size_t size);
// Requests the root storage channel from the `ServiceProvider` and caches it in
// `root_storage_channel_`.
//
// Subsequent calls to the function will return the cached channel.
//
// Returns:
// * ZX_OK: The operation was successful.
// * ZX_ERR_UNAVAILABLE: The current client does not have access to a `ServiceProvider`.
// * `zx_status_t` codes from `zx::channel::create` or requesting the `ServiceProvider` over
// FIDL.
zx_status_t GetRootStorageChannel(zx::unowned_channel* out_root_channel);
// Requests a connection to the storage directory pointed to by the path.
//
// Parameters:
// * path: The path of the directory, relative to the root storage directory.
// * create: Flag specifying whether to create directories if they don't exist.
// * out_storage_channel: Where to output the `fuchsia.io.Directory` client end channel.
zx_status_t GetStorageDirectory(std::filesystem::path path, bool create,
zx::channel* out_storage_channel);
// Tracks a new file system object associated with the current client.
//
// This occurs when the trusted world creates or opens a file system object.
//
// Parameters:
// * io_node_channel: The channel to the `fuchsia.io.Node` file system object.
//
// Returns:
// * The identifier for the trusted world to refer to the object.
__WARN_UNUSED_RESULT uint64_t TrackFileSystemObject(zx::channel io_node_channel);
// Gets the channel to the file system object associated with the given identifier.
//
// Parameters:
// * identifier: The identifier to find the file system object by.
//
// Returns:
// * A `std::optional` containing a reference to the `zx::channel`, if it was found.
std::optional<zx::unowned_channel>
GetFileSystemObjectChannel(uint64_t identifier);
// Untracks a file system object associated with the current client.
//
// This occurs when the trusted world closes a previously open file system object.
//
// Parameters:
// * identifier: The identifier to refer to the object.
//
// Returns:
// * Whether a file system object associated with the identifier was untracked.
bool UntrackFileSystemObject(uint64_t identifier);
//
// 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 HandleRpcCommandGetTime(GetTimeRpcMessage* message);
zx_status_t HandleRpcCommandAllocateMemory(AllocateMemoryRpcMessage* message);
zx_status_t HandleRpcCommandFreeMemory(FreeMemoryRpcMessage* message);
// Move in the FileSystemRpcMessage since it'll be moved into a sub-type in this function.
zx_status_t HandleRpcCommandFileSystem(FileSystemRpcMessage&& message);
zx_status_t HandleRpcCommandFileSystemOpenFile(OpenFileFileSystemRpcMessage* message);
zx_status_t HandleRpcCommandFileSystemCreateFile(CreateFileFileSystemRpcMessage* message);
zx_status_t HandleRpcCommandFileSystemCloseFile(CloseFileFileSystemRpcMessage* message);
zx_status_t HandleRpcCommandFileSystemReadFile(ReadFileFileSystemRpcMessage* message);
zx_status_t HandleRpcCommandFileSystemWriteFile(WriteFileFileSystemRpcMessage* message);
zx_status_t HandleRpcCommandFileSystemTruncateFile(TruncateFileFileSystemRpcMessage* message);
zx_status_t HandleRpcCommandFileSystemRemoveFile(RemoveFileFileSystemRpcMessage* message);
zx_status_t HandleRpcCommandFileSystemRenameFile(RenameFileFileSystemRpcMessage* message);
static fuchsia_tee_Device_ops_t kFidlOps;
OpteeController* controller_;
bool needs_to_close_ = false;
SharedMemoryList allocated_shared_memory_;
OpenSessionsTable open_sessions_;
std::atomic<uint64_t> next_file_system_object_id_{1};
// TODO(godtamit): Move to `std::unordered_map` when BLD-413 is complete
FileSystemObjectTable open_file_system_objects_;
// The client end of a channel to the `fuchsia.tee.manager.ServiceProvider` protocol.
// This may be an invalid channel, which indicates the client has no service provider support.
zx::channel service_provider_channel_;
// A lazily-initialized, cached channel to the root storage channel.
// This may be an invalid channel, which indicates it has not been initialized yet.
zx::channel root_storage_channel_;
};
} // namespace optee