| // 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; |
| } |