blob: 351ebc9b5429f8b2607b419baa74f4e59baa07d2 [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 <atomic>
#include "include/fuchsia_communication.h"
#include "msd_img_buffer.h"
#include "msd_img_semaphore.h"
#include "platform_trace.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(magma_system_command_buffer *command_buffer,
magma_system_exec_resource *exec_resources,
msd_buffer_t **buffers,
msd_semaphore_t **signal_semaphores,
PVRSRV_BRIDGE_PACKAGE *package_out,
volatile FuchsiaImgCommandPayload **payload_out,
std::vector<std::shared_ptr<MsdImgSemaphore>> *semaphores_out)
{
uint32_t num_resources = command_buffer->num_resources;
uint32_t num_semaphores = command_buffer->signal_semaphore_count;
// 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);
}
if (num_semaphores < kSemaphoreMax)
{
return DRET_MSG(MAGMA_STATUS_INVALID_ARGS, "num_semaphores %d too small", num_semaphores);
}
// Assume the exec resources are in this order.
std::shared_ptr<MsdImgBuffer> payload_buffer = MsdImgAbiBuffer::cast(buffers[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;
*payload_out = payload;
semaphores_out->push_back(MsdImgAbiSemaphore::cast(signal_semaphores[0])->base_ptr());
if (num_semaphores > 1)
{
additional_semaphore_ = MsdImgAbiSemaphore::cast(signal_semaphores[1])->base_ptr();
}
if (num_resources > kExpectedResourceCount)
{
additional_buffer_ = MsdImgAbiBuffer::cast(buffers[1])->base_ptr();
}
return MAGMA_STATUS_OK;
}
void
MsdImgConnection::CleanupAfterCommand(volatile FuchsiaImgCommandPayload *payload,
const std::vector<std::shared_ptr<MsdImgSemaphore>> &semaphores,
bool success)
{
payload->success = success;
semaphores[kSemaphoreCompletion]->platform_semaphore()->Signal();
// The semaphore signal should happen before |finished_command| is set
// as otherwise the client may reuse this payload buffer too quickly and
// think the next command put in the buffer was completed too early.
std::atomic_thread_fence(std::memory_order_release);
payload->finished_command = 1;
payload_buffer_ = nullptr;
additional_buffer_.reset();
additional_semaphore_.reset();
current_client_thread_id_ = 0;
}
magma_status_t
MsdImgConnection::ExecuteCommandBuffer(magma_system_command_buffer *command_buffer,
magma_system_exec_resource *exec_resources,
msd_buffer_t **buffers,
msd_semaphore_t **signal_semaphores)
{
PVRSRV_BRIDGE_PACKAGE package;
volatile FuchsiaImgCommandPayload *payload;
std::vector<std::shared_ptr<MsdImgSemaphore>> semaphores;
magma_status_t status = ProcessCommandBuffer(command_buffer, exec_resources, buffers, signal_semaphores, &package,
&payload, &semaphores);
if (status != MAGMA_STATUS_OK)
return DRET(status);
PVRSRV_ERROR eError;
{
TRACE_DURATION("gfx", "MsdImgConnection::ExecuteCommandBuffer", "bridge group", package.ui32BridgeID,
"function", package.ui32FunctionID);
ScopedSetConnection set_connection(this);
eError = BridgedDispatchKM(connection_data_, &package);
}
CleanupAfterCommand(payload, semaphores, eError == PVRSRV_OK);
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_);
}
std::shared_ptr<MsdImgSemaphore>
MsdImgConnection::TakeAdditionalSemaphore()
{
// Move should reset |additional_semaphore_|.
return std::move(additional_semaphore_);
}
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;
}