mirror of
https://github.com/citra-emu/citra.git
synced 2024-12-18 16:00:04 +00:00
Kernel/IPC: Implement MappedBuffer translation for HLE services that use the HLERequestContext architecture.
This commit is contained in:
parent
4071da5012
commit
3ecf650bf9
@ -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<u32>(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
|
||||
|
@ -63,6 +63,35 @@ protected:
|
||||
std::vector<SharedPtr<ServerSession>> 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<u8> 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<SharedPtr<Object>, 8> request_handles;
|
||||
// The static buffers will be created when the IPC request is translated.
|
||||
std::array<std::vector<u8>, IPC::MAX_STATIC_BUFFERS> static_buffers;
|
||||
// The mapped buffers will be created when the IPC request is translated
|
||||
boost::container::small_vector<MappedBuffer, 8> request_mapped_buffers;
|
||||
};
|
||||
|
||||
} // namespace Kernel
|
||||
|
Loading…
Reference in New Issue
Block a user