diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp index f906ccad0d..e51befeefb 100644 --- a/src/core/hle/kernel/hle_ipc.cpp +++ b/src/core/hle/kernel/hle_ipc.cpp @@ -107,6 +107,12 @@ ResultCode HLERequestContext::PopulateFromIncomingCommandBuffer(const u32_le* sr cmd_buf[i++] = source_address; break; } + case IPC::DescriptorType::MappedBuffer: { + u32 next_id = static_cast(request_mapped_buffers.size()); + request_mapped_buffers.emplace_back(src_process, descriptor, src_cmdbuf[i], next_id); + cmd_buf[i++] = next_id; + break; + } default: UNIMPLEMENTED_MSG("Unsupported handle translation: 0x%08X", descriptor); } @@ -166,6 +172,11 @@ ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(u32_le* dst_cmdbuf, P dst_cmdbuf[i++] = target_address; break; } + case IPC::DescriptorType::MappedBuffer: { + VAddr addr = request_mapped_buffers[cmd_buf[i]].address; + dst_cmdbuf[i++] = addr; + break; + } default: UNIMPLEMENTED_MSG("Unsupported handle translation: 0x%08X", descriptor); } @@ -174,4 +185,28 @@ ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(u32_le* dst_cmdbuf, P return RESULT_SUCCESS; } +MappedBuffer& HLERequestContext::GetMappedBuffer(u32 id_from_cmdbuf) { + ASSERT_MSG(id_from_cmdbuf < request_mapped_buffers.size(), "Mapped Buffer ID out of range!"); + return request_mapped_buffers[id_from_cmdbuf]; +} + +MappedBuffer::MappedBuffer(const Process& process, u32 descriptor, VAddr address, u32 id) + : process(&process), address(address), id(id) { + IPC::MappedBufferDescInfo desc{descriptor}; + size = desc.size; + perms = desc.perms; +} + +void MappedBuffer::Read(void* dest_buffer, size_t offset, size_t size) { + ASSERT(perms & IPC::R); + ASSERT(offset + size <= this->size); + Memory::ReadBlock(*process, address + offset, dest_buffer, size); +} + +void MappedBuffer::Write(const void* src_buffer, size_t offset, size_t size) { + ASSERT(perms & IPC::W); + ASSERT(offset + size <= this->size); + Memory::WriteBlock(*process, address + offset, src_buffer, size); +} + } // namespace Kernel diff --git a/src/core/hle/kernel/hle_ipc.h b/src/core/hle/kernel/hle_ipc.h index 03a7a4debb..2a246fa27c 100644 --- a/src/core/hle/kernel/hle_ipc.h +++ b/src/core/hle/kernel/hle_ipc.h @@ -63,6 +63,35 @@ protected: std::vector> connected_sessions; }; +class MappedBuffer { +public: + MappedBuffer(const Process& process, u32 descriptor, VAddr address, u32 id); + + // interface for service + void Read(void* dest_buffer, size_t offset, size_t size); + void Write(const void* src_buffer, size_t offset, size_t size); + size_t GetSize() const { + return size; + } + + // interface for ipc helper + u32 GenerateDescriptor() const { + return IPC::MappedBufferDesc(size, perms); + } + + u32 GetId() const { + return id; + } + +private: + friend class HLERequestContext; + u32 id; + VAddr address; + const Process* process; + size_t size; + IPC::MappedBufferPermissions perms; +}; + /** * Class containing information about an in-flight IPC request being handled by an HLE service * implementation. Services should avoid using old global APIs (e.g. Kernel::GetCommandBuffer()) and @@ -81,6 +110,17 @@ protected: * The end result is similar to just giving services their own real handle tables, but since these * ids are local to a specific context, it avoids requiring services to manage handles for objects * across multiple calls and ensuring that unneeded handles are cleaned up. + * + * HLE mapped buffer protocol + * ========================== + * + * HLE services don't have their own virtual memory space, a tweaked protocol is used to simulate + * memory mapping. The kernel will wrap the incoming buffers into a memory interface on which HLE + * services can operate, and insert a id in the buffer where the vaddr would normally be. The + * service then calls GetMappedBuffer with that id to get the memory interface. On response, like + * real services pushing back the mapped buffer address to unmap it, HLE services push back the + * id of the memory interface and let kernel convert it back to client vaddr. No real unmapping is + * needed in this case, though. */ class HLERequestContext { public: @@ -131,6 +171,12 @@ public: */ void AddStaticBuffer(u8 buffer_id, std::vector data); + /** + * Gets a memory interface by the id from the request command buffer. See the "HLE mapped buffer + * protocol" section in the class documentation for more details. + */ + MappedBuffer& GetMappedBuffer(u32 id_from_cmdbuf); + /// Populates this context with data from the requesting process/thread. ResultCode PopulateFromIncomingCommandBuffer(const u32_le* src_cmdbuf, Process& src_process, HandleTable& src_table); @@ -145,6 +191,8 @@ private: boost::container::small_vector, 8> request_handles; // The static buffers will be created when the IPC request is translated. std::array, IPC::MAX_STATIC_BUFFERS> static_buffers; + // The mapped buffers will be created when the IPC request is translated + boost::container::small_vector request_mapped_buffers; }; } // namespace Kernel