| // 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(msd_buffer_t *cmd_buf, |
| msd_buffer_t **exec_resources, |
| msd_semaphore_t **signal_semaphores, |
| PVRSRV_BRIDGE_PACKAGE *package_out, |
| volatile FuchsiaImgCommandPayload **payload_out, |
| std::vector<std::shared_ptr<MsdImgSemaphore>> *semaphores_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; |
| uint32_t num_semaphores = sys_command_buffer->signal_semaphore_count; |
| 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); |
| } |
| 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(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; |
| *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(exec_resources[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(msd_buffer_t *cmd_buf, |
| msd_buffer_t **exec_resources, |
| msd_semaphore_t **signal_semaphores) |
| { |
| PVRSRV_BRIDGE_PACKAGE package; |
| volatile FuchsiaImgCommandPayload *payload; |
| std::vector<std::shared_ptr<MsdImgSemaphore>> semaphores; |
| magma_status_t status = |
| ProcessCommandBuffer(cmd_buf, exec_resources, 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; |
| } |