| // Copyright 2016 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 "magma_system_context.h" |
| #include "magma_system_connection.h" |
| #include "magma_system_device.h" |
| #include "magma_util/command_buffer.h" |
| #include "magma_util/macros.h" |
| |
| #include <memory> |
| #include <unordered_set> |
| #include <vector> |
| |
| class MagmaSystemCommandBuffer final : public magma::CommandBuffer { |
| public: |
| MagmaSystemCommandBuffer(std::unique_ptr<MagmaSystemBuffer> buffer) : buffer_(std::move(buffer)) |
| { |
| } |
| |
| magma::PlatformBuffer* platform_buffer() override { return buffer_->platform_buffer(); } |
| |
| MagmaSystemBuffer* system_buffer() { return buffer_.get(); } |
| |
| private: |
| std::unique_ptr<MagmaSystemBuffer> buffer_; |
| }; |
| |
| magma::Status |
| MagmaSystemContext::ExecuteCommandBuffer(std::unique_ptr<magma::PlatformBuffer> command_buffer) |
| { |
| |
| // copy command buffer before validating to avoid tampering after validating |
| // TODO(MA-111) use Copy On Write here if possible |
| auto command_buffer_copy = MagmaSystemBuffer::Create( |
| magma::PlatformBuffer::Create(command_buffer->size(), "command-buffer-copy")); |
| if (!command_buffer_copy) |
| return DRET_MSG(MAGMA_STATUS_MEMORY_ERROR, |
| "ExecuteCommandBuffer: failed to create command buffer copy"); |
| |
| void* cmd_buf_src; |
| if (!command_buffer->MapCpu(&cmd_buf_src)) |
| return DRET_MSG(MAGMA_STATUS_MEMORY_ERROR, |
| "ExecuteCommandBuffer: Failed to map command buffer for copying"); |
| |
| void* cmd_buf_dst; |
| if (!command_buffer_copy->platform_buffer()->MapCpu(&cmd_buf_dst)) |
| return DRET_MSG(MAGMA_STATUS_MEMORY_ERROR, |
| "ExecuteCommandBuffer: Failed to map command buffer copy for copying"); |
| |
| DASSERT(command_buffer->size() == command_buffer_copy->size()); |
| memcpy(cmd_buf_dst, cmd_buf_src, command_buffer->size()); |
| |
| if (!command_buffer->UnmapCpu()) |
| return DRET_MSG(MAGMA_STATUS_MEMORY_ERROR, |
| "ExecuteCommandBuffer: Failed to unmap command buffer after copying"); |
| |
| if (!command_buffer_copy->platform_buffer()->UnmapCpu()) |
| return DRET_MSG(MAGMA_STATUS_MEMORY_ERROR, |
| "ExecuteCommandBuffer: Failed to unmap command buffer copy after copying"); |
| |
| // we're done with our shared reference to the original buffer so we release it for good measure |
| command_buffer.reset(); |
| |
| auto cmd_buf = std::make_unique<MagmaSystemCommandBuffer>(std::move(command_buffer_copy)); |
| |
| if (!cmd_buf->Initialize()) |
| return DRET_MSG(MAGMA_STATUS_INTERNAL_ERROR, |
| "ExecuteCommandBuffer: Failed to initialize command buffer"); |
| |
| // used to validate that handles are not duplicated |
| std::unordered_set<uint32_t> id_set; |
| |
| // used to keep resources in scope until msd_context_execute_command_buffer returns |
| std::vector<std::shared_ptr<MagmaSystemBuffer>> system_resources; |
| system_resources.reserve(cmd_buf->num_resources()); |
| |
| // the resources to be sent to the MSD driver |
| auto msd_resources = std::vector<msd_buffer_t*>(); |
| msd_resources.reserve(cmd_buf->num_resources()); |
| |
| // validate batch buffer index |
| if (cmd_buf->batch_buffer_resource_index() >= cmd_buf->num_resources()) |
| return DRET_MSG(MAGMA_STATUS_INVALID_ARGS, |
| "ExecuteCommandBuffer: batch buffer resource index invalid"); |
| |
| // validate exec resources |
| for (uint32_t i = 0; i < cmd_buf->num_resources(); i++) { |
| uint64_t id = cmd_buf->resource(i).buffer_id(); |
| |
| auto buf = owner_->LookupBufferForContext(id); |
| if (!buf) |
| return DRET_MSG(MAGMA_STATUS_INVALID_ARGS, |
| "ExecuteCommandBuffer: exec resource has invalid buffer handle"); |
| |
| auto iter = id_set.find(id); |
| if (iter != id_set.end()) |
| return DRET_MSG(MAGMA_STATUS_INVALID_ARGS, |
| "ExecuteCommandBuffer: duplicate exec resource"); |
| |
| id_set.insert(id); |
| system_resources.push_back(buf); |
| msd_resources.push_back(buf->msd_buf()); |
| |
| if (i == cmd_buf->batch_buffer_resource_index()) { |
| // validate batch start |
| if (cmd_buf->batch_start_offset() >= buf->size()) |
| return DRET_MSG(MAGMA_STATUS_INVALID_ARGS, "invalid batch start offset 0x%x", |
| cmd_buf->batch_start_offset()); |
| } |
| } |
| |
| // validate relocations |
| for (uint32_t res_index = 0; res_index < cmd_buf->num_resources(); res_index++) { |
| auto resource = &cmd_buf->resource(res_index); |
| |
| for (uint32_t reloc_index = 0; reloc_index < resource->num_relocations(); reloc_index++) { |
| auto relocation = resource->relocation(reloc_index); |
| if (relocation->offset > system_resources[res_index]->size() - sizeof(uint32_t)) |
| return DRET_MSG(MAGMA_STATUS_INVALID_ARGS, |
| "ExecuteCommandBuffer: relocation offset invalid"); |
| |
| uint32_t target_index = relocation->target_resource_index; |
| |
| if (target_index >= cmd_buf->num_resources()) |
| return DRET_MSG(MAGMA_STATUS_INVALID_ARGS, |
| "ExecuteCommandBuffer: relocation target_resource_index invalid"); |
| |
| if (relocation->target_offset > |
| system_resources[target_index]->size() - sizeof(uint32_t)) |
| return DRET_MSG(MAGMA_STATUS_INVALID_ARGS, |
| "ExecuteCommandBuffer: relocation target_offset invalid"); |
| } |
| } |
| |
| // used to keep semaphores in scope until msd_context_execute_command_buffer returns |
| std::vector<msd_semaphore_t*> msd_wait_semaphores(cmd_buf->wait_semaphore_count()); |
| std::vector<msd_semaphore_t*> msd_signal_semaphores(cmd_buf->signal_semaphore_count()); |
| |
| // validate semaphores |
| for (uint32_t i = 0; i < cmd_buf->wait_semaphore_count(); i++) { |
| auto semaphore = owner_->LookupSemaphoreForContext(cmd_buf->wait_semaphore_id(i)); |
| if (!semaphore) |
| return DRET_MSG(MAGMA_STATUS_INVALID_ARGS, "wait semaphore id not found 0x%" PRIx64, |
| cmd_buf->wait_semaphore_id(i)); |
| msd_wait_semaphores[i] = semaphore->msd_semaphore(); |
| } |
| for (uint32_t i = 0; i < cmd_buf->signal_semaphore_count(); i++) { |
| auto semaphore = owner_->LookupSemaphoreForContext(cmd_buf->signal_semaphore_id(i)); |
| if (!semaphore) |
| return DRET_MSG(MAGMA_STATUS_INVALID_ARGS, "signal semaphore id not found 0x%" PRIx64, |
| cmd_buf->signal_semaphore_id(i)); |
| msd_signal_semaphores[i] = semaphore->msd_semaphore(); |
| } |
| |
| // submit command buffer to driver |
| magma_status_t result = msd_context_execute_command_buffer( |
| msd_ctx(), cmd_buf->system_buffer()->msd_buf(), msd_resources.data(), |
| msd_wait_semaphores.data(), msd_signal_semaphores.data()); |
| |
| return DRET_MSG(result, "ExecuteCommandBuffer: msd_context_execute_command_buffer failed: %d", |
| result); |
| } |
| |
| magma::Status MagmaSystemContext::ExecuteImmediateCommands(uint64_t commands_size, void* commands, |
| uint64_t semaphore_count, |
| uint64_t* semaphore_ids) |
| { |
| std::vector<msd_semaphore_t*> msd_semaphores(semaphore_count); |
| for (uint32_t i = 0; i < semaphore_count; i++) { |
| auto semaphore = owner_->LookupSemaphoreForContext(semaphore_ids[i]); |
| if (!semaphore) |
| return DRET_MSG(MAGMA_STATUS_INVALID_ARGS, "semaphore id not found 0x%" PRIx64, |
| semaphore_ids[i]); |
| msd_semaphores[i] = semaphore->msd_semaphore(); |
| } |
| magma_status_t result = msd_context_execute_immediate_commands( |
| msd_ctx(), commands_size, commands, semaphore_count, msd_semaphores.data()); |
| |
| return DRET_MSG(result, |
| "ExecuteImmediateCommands: msd_context_execute_immediate_commands failed: %d", |
| result); |
| } |