blob: 4869dfcd9ae460a89ce18775fa710e21649750e9 [file] [log] [blame]
// Copyright 2019 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.
#include "msd_img_connection.h"
#include "include/fuchsia_communication.h"
#include "msd_img_buffer.h"
extern "C"
{
#include "process_stats.h"
#include "pvrsrv.h"
#include "pvrsrv_error.h"
#include "srvcore.h"
#include "rgxdevice.h"
}
static thread_local MsdImgConnection *current_connection;
class ScopedSetConnection
{
public:
explicit ScopedSetConnection(MsdImgConnection *connection)
{
DASSERT(!current_connection);
current_connection = connection;
}
~ScopedSetConnection()
{
DASSERT(current_connection);
current_connection = nullptr;
}
};
MsdImgConnection::MsdImgConnection(Owner *owner, msd_client_id_t client_id) : owner_(owner), client_id_(client_id)
{
magic_ = kMagic;
snprintf(client_name_, sizeof(client_name_), "Client ID %d", client_id);
}
MsdImgConnection::~MsdImgConnection()
{
ScopedSetConnection set_connection(this);
if (connection_data_)
{
PVRSRVConnectionDisconnect(connection_data_);
}
magic_ = 0;
}
MsdImgConnection *
MsdImgConnection::GetCurrentConnection()
{
return current_connection;
}
bool
MsdImgConnection::Init()
{
ScopedSetConnection set_connection(this);
void *data;
PVRSRV_ERROR srv_err = PVRSRVConnectionConnect(&data, this);
if (srv_err != PVRSRV_OK)
{
return DRETF(false, "Failed to create PVRSRV connection: %d", srv_err);
}
connection_data_ = reinterpret_cast<CONNECTION_DATA *>(data);
return true;
}
magma_status_t
MsdImgConnection::ProcessCommandBuffer(msd_buffer_t *cmd_buf,
msd_buffer_t **exec_resources,
PVRSRV_BRIDGE_PACKAGE *package_out)
{
auto abi_command_buffer = MsdImgAbiBuffer::cast(cmd_buf);
if (!abi_command_buffer)
{
return DRET_MSG(MAGMA_STATUS_INVALID_ARGS, "No cmd_buf");
}
magma::PlatformBuffer *command_buffer = abi_command_buffer->base_ptr()->platform_buffer();
void *command_buffer_data;
if (!command_buffer->MapCpu(&command_buffer_data))
{
return DRET_MSG(MAGMA_STATUS_INVALID_ARGS, "Can't map cmd_buf");
}
auto sys_command_buffer = reinterpret_cast<magma_system_command_buffer *>(command_buffer_data);
uint32_t num_resources = sys_command_buffer->num_resources;
command_buffer->UnmapCpu();
// Array and payload buffers.
constexpr uint32_t kExpectedResourceCount = 1;
if (num_resources < kExpectedResourceCount)
{
return DRET_MSG(MAGMA_STATUS_INVALID_ARGS, "num_resources %d too small", num_resources);
}
// Assume the exec resources are in this order.
std::shared_ptr<MsdImgBuffer> payload_buffer = MsdImgAbiBuffer::cast(exec_resources[0])->base_ptr();
volatile FuchsiaImgCommandPayload *payload;
if (payload_buffer->platform_buffer()->size() < sizeof(*payload))
{
return DRET_MSG(MAGMA_STATUS_INVALID_ARGS, "Payload buffer too small for struct");
}
void *data;
if (!payload_buffer->GetPersistentCpuMap(&data))
{
return DRET_MSG(MAGMA_STATUS_INTERNAL_ERROR, "Unable to make persistent map");
}
payload = reinterpret_cast<volatile FuchsiaImgCommandPayload *>(data);
package_out->ui32BridgeID = payload->bridge_group;
package_out->ui32FunctionID = payload->function_id;
package_out->ui32Size = sizeof(*package_out);
package_out->ui32InBufferSize = payload->in_data_size;
package_out->ui32OutBufferSize = payload->out_data_size;
const uint64_t remaining_buffer_size = payload_buffer->platform_buffer()->size() - sizeof(*payload);
if (remaining_buffer_size < package_out->ui32InBufferSize)
{
return DRET_MSG(MAGMA_STATUS_INVALID_ARGS, "Payload buffer too small for in args");
}
if (remaining_buffer_size < package_out->ui32OutBufferSize)
{
return DRET_MSG(MAGMA_STATUS_INVALID_ARGS, "Payload buffer too small for out args");
}
// Volatile isn't necessary because the bridge code will memcpy everything
// before using it.
package_out->pvParamIn = reinterpret_cast<void *>(const_cast<FuchsiaImgCommandPayload *>(payload + 1));
package_out->pvParamOut = package_out->pvParamIn;
payload_buffer_ = payload_buffer;
current_client_thread_id_ = payload->thread_id;
if (num_resources > kExpectedResourceCount)
{
additional_buffer_ = MsdImgAbiBuffer::cast(exec_resources[1])->base_ptr();
}
return MAGMA_STATUS_OK;
}
magma_status_t
MsdImgConnection::ExecuteCommandBuffer(msd_buffer_t *cmd_buf, msd_buffer_t **exec_resources)
{
PVRSRV_BRIDGE_PACKAGE package;
magma_status_t status = ProcessCommandBuffer(cmd_buf, exec_resources, &package);
if (status != MAGMA_STATUS_OK)
return DRET(status);
ScopedSetConnection set_connection(this);
PVRSRV_ERROR eError = BridgedDispatchKM(connection_data_, &package);
payload_buffer_ = nullptr;
additional_buffer_.reset();
current_client_thread_id_ = 0;
if (eError != PVRSRV_OK)
return DRET_MSG(MAGMA_STATUS_INTERNAL_ERROR, "Bridge error: %d", eError);
return MAGMA_STATUS_OK;
}
std::shared_ptr<MsdImgBuffer>
MsdImgConnection::TakeAdditionalBuffer()
{
// Move should reset |additional_buffer_|.
return std::move(additional_buffer_);
}
bool
MsdImgConnection::CopyFromUser(void *dest, const void *src, uint32_t size)
{
DASSERT(payload_buffer_);
uint64_t src_offset = reinterpret_cast<uint64_t>(src);
if (src_offset + size > payload_buffer_->platform_buffer()->size())
{
return DRETF(false, "CopyFromUser out of bounds 0x%lx 0x%x 0x%lx", src_offset, size,
payload_buffer_->platform_buffer()->size());
}
return payload_buffer_->platform_buffer()->Read(dest, src_offset, size);
}
bool
MsdImgConnection::CopyToUser(void *dest, const void *src, uint32_t size)
{
DASSERT(payload_buffer_);
uint64_t dest_offset = reinterpret_cast<uint64_t>(dest);
if (dest_offset + size > payload_buffer_->platform_buffer()->size())
{
return DRETF(false, "CopyToUser out of bounds 0x%lx 0x%x 0x%lx", dest_offset, size,
payload_buffer_->platform_buffer()->size());
}
return payload_buffer_->platform_buffer()->Write(src, dest_offset, size);
}
PVRSRV_DEVICE_NODE *
OSGetDevData(CONNECTION_DATA *psConnection)
{
MsdImgConnection *connection = MsdImgConnection::Cast(psConnection->hOsPrivateData);
return connection->owner()->device_node();
}
PVRSRV_ERROR
OSConnectionPrivateDataInit(IMG_HANDLE *phOsPrivateData, void *pvOSData)
{
*phOsPrivateData = pvOSData;
return PVRSRV_OK;
}
PVRSRV_ERROR
OSConnectionPrivateDataDeInit(IMG_HANDLE hOsPrivateData)
{
// Data should be freed by connection close.
return PVRSRV_OK;
}