mirror of
https://github.com/citra-emu/citra.git
synced 2024-11-15 07:30:06 +00:00
IPC: store mapped buffer info in session context
So that it doesn't have to scan over the request buffer again on reply. This also allow us to store additional info like memory mapping
This commit is contained in:
parent
7a564b904b
commit
0b8d2ecabe
@ -2,6 +2,7 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
#include "common/alignment.h"
|
||||
#include "core/hle/ipc.h"
|
||||
#include "core/hle/kernel/handle_table.h"
|
||||
@ -14,70 +15,10 @@
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
void ScanForAndUnmapBuffer(std::array<u32, IPC::COMMAND_BUFFER_LENGTH>& dst_cmd_buf,
|
||||
const std::size_t dst_command_size, std::size_t& target_index,
|
||||
SharedPtr<Process> src_process, SharedPtr<Process> dst_process,
|
||||
const VAddr source_address, const VAddr page_start, const u32 num_pages,
|
||||
const u32 size, const IPC::MappedBufferPermissions permissions) {
|
||||
while (target_index < dst_command_size) {
|
||||
u32 desc = dst_cmd_buf[target_index++];
|
||||
|
||||
if (IPC::GetDescriptorType(desc) == IPC::DescriptorType::CopyHandle ||
|
||||
IPC::GetDescriptorType(desc) == IPC::DescriptorType::MoveHandle) {
|
||||
u32 num_handles = IPC::HandleNumberFromDesc(desc);
|
||||
for (u32 j = 0; j < num_handles; ++j) {
|
||||
target_index += 1;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (IPC::GetDescriptorType(desc) == IPC::DescriptorType::CallingPid ||
|
||||
IPC::GetDescriptorType(desc) == IPC::DescriptorType::StaticBuffer) {
|
||||
target_index += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (IPC::GetDescriptorType(desc) == IPC::DescriptorType::MappedBuffer) {
|
||||
VAddr dest_address = dst_cmd_buf[target_index];
|
||||
IPC::MappedBufferDescInfo dest_descInfo{desc};
|
||||
u32 dest_size = static_cast<u32>(dest_descInfo.size);
|
||||
IPC::MappedBufferPermissions dest_permissions = dest_descInfo.perms;
|
||||
|
||||
if (dest_size == 0) {
|
||||
target_index += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
ASSERT(permissions == dest_permissions && size == dest_size);
|
||||
// Readonly buffers do not need to be copied over to the target
|
||||
// process again because they were (presumably) not modified. This
|
||||
// behavior is consistent with the real kernel.
|
||||
if (permissions != IPC::MappedBufferPermissions::R) {
|
||||
// Copy the modified buffer back into the target process
|
||||
Memory::CopyBlock(*src_process, *dst_process, source_address, dest_address, size);
|
||||
}
|
||||
|
||||
VAddr prev_reserve = page_start - Memory::PAGE_SIZE;
|
||||
VAddr next_reserve = page_start + num_pages * Memory::PAGE_SIZE;
|
||||
|
||||
auto& prev_vma = src_process->vm_manager.FindVMA(prev_reserve)->second;
|
||||
auto& next_vma = src_process->vm_manager.FindVMA(next_reserve)->second;
|
||||
ASSERT(prev_vma.meminfo_state == MemoryState::Reserved &&
|
||||
next_vma.meminfo_state == MemoryState::Reserved);
|
||||
|
||||
// Unmap the buffer and guard pages from the source process
|
||||
ResultCode result = src_process->vm_manager.UnmapRange(
|
||||
page_start - Memory::PAGE_SIZE, (num_pages + 2) * Memory::PAGE_SIZE);
|
||||
ASSERT(result == RESULT_SUCCESS);
|
||||
|
||||
target_index += 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ResultCode TranslateCommandBuffer(SharedPtr<Thread> src_thread, SharedPtr<Thread> dst_thread,
|
||||
VAddr src_address, VAddr dst_address, bool reply) {
|
||||
VAddr src_address, VAddr dst_address,
|
||||
std::vector<MappedBufferContext>& mapped_buffer_context,
|
||||
bool reply) {
|
||||
|
||||
auto& src_process = src_thread->owner_process;
|
||||
auto& dst_process = dst_thread->owner_process;
|
||||
@ -95,18 +36,6 @@ ResultCode TranslateCommandBuffer(SharedPtr<Thread> src_thread, SharedPtr<Thread
|
||||
std::array<u32, IPC::COMMAND_BUFFER_LENGTH> cmd_buf;
|
||||
Memory::ReadBlock(*src_process, src_address, cmd_buf.data(), command_size * sizeof(u32));
|
||||
|
||||
// Create a copy of the target's command buffer
|
||||
IPC::Header dst_header;
|
||||
Memory::ReadBlock(*dst_process, dst_address, &dst_header.raw, sizeof(dst_header.raw));
|
||||
|
||||
std::size_t dst_untranslated_size = 1u + dst_header.normal_params_size;
|
||||
std::size_t dst_command_size = dst_untranslated_size + dst_header.translate_params_size;
|
||||
std::size_t target_index = dst_untranslated_size;
|
||||
|
||||
std::array<u32, IPC::COMMAND_BUFFER_LENGTH> dst_cmd_buf;
|
||||
Memory::ReadBlock(*dst_process, dst_address, dst_cmd_buf.data(),
|
||||
dst_command_size * sizeof(u32));
|
||||
|
||||
std::size_t i = untranslated_size;
|
||||
while (i < command_size) {
|
||||
u32 descriptor = cmd_buf[i];
|
||||
@ -212,9 +141,36 @@ ResultCode TranslateCommandBuffer(SharedPtr<Thread> src_thread, SharedPtr<Thread
|
||||
if (reply) {
|
||||
// Scan the target's command buffer for the matching mapped buffer.
|
||||
// The real kernel panics if you try to reply with an unsolicited MappedBuffer.
|
||||
ScanForAndUnmapBuffer(dst_cmd_buf, dst_command_size, target_index, src_process,
|
||||
dst_process, source_address, page_start, num_pages, size,
|
||||
permissions);
|
||||
auto found = std::find_if(
|
||||
mapped_buffer_context.begin(), mapped_buffer_context.end(),
|
||||
[permissions, size, source_address](const MappedBufferContext& context) {
|
||||
// Note: reply's source_address is request's target_address
|
||||
return context.permissions == permissions && context.size == size &&
|
||||
context.target_address == source_address;
|
||||
});
|
||||
|
||||
ASSERT(found != mapped_buffer_context.end());
|
||||
|
||||
if (permissions != IPC::MappedBufferPermissions::R) {
|
||||
// Copy the modified buffer back into the target process
|
||||
Memory::CopyBlock(*src_process, *dst_process, found->target_address,
|
||||
found->source_address, size);
|
||||
}
|
||||
|
||||
VAddr prev_reserve = page_start - Memory::PAGE_SIZE;
|
||||
VAddr next_reserve = page_start + num_pages * Memory::PAGE_SIZE;
|
||||
|
||||
auto& prev_vma = src_process->vm_manager.FindVMA(prev_reserve)->second;
|
||||
auto& next_vma = src_process->vm_manager.FindVMA(next_reserve)->second;
|
||||
ASSERT(prev_vma.meminfo_state == MemoryState::Reserved &&
|
||||
next_vma.meminfo_state == MemoryState::Reserved);
|
||||
|
||||
// Unmap the buffer and guard pages from the source process
|
||||
ResultCode result = src_process->vm_manager.UnmapRange(
|
||||
page_start - Memory::PAGE_SIZE, (num_pages + 2) * Memory::PAGE_SIZE);
|
||||
ASSERT(result == RESULT_SUCCESS);
|
||||
|
||||
mapped_buffer_context.erase(found);
|
||||
|
||||
i += 1;
|
||||
break;
|
||||
@ -246,6 +202,9 @@ ResultCode TranslateCommandBuffer(SharedPtr<Thread> src_thread, SharedPtr<Thread
|
||||
dst_process->vm_manager.MapMemoryBlockToBase(
|
||||
Memory::IPC_MAPPING_VADDR, Memory::IPC_MAPPING_SIZE, reserve_buffer, 0,
|
||||
static_cast<u32>(reserve_buffer->size()), Kernel::MemoryState::Reserved);
|
||||
|
||||
mapped_buffer_context.push_back({permissions, size, source_address, target_address});
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
|
@ -4,11 +4,23 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/ipc.h"
|
||||
#include "core/hle/kernel/thread.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
struct MappedBufferContext {
|
||||
IPC::MappedBufferPermissions permissions;
|
||||
u32 size;
|
||||
VAddr source_address;
|
||||
VAddr target_address;
|
||||
};
|
||||
|
||||
/// Performs IPC command buffer translation from one process to another.
|
||||
ResultCode TranslateCommandBuffer(SharedPtr<Thread> src_thread, SharedPtr<Thread> dst_thread,
|
||||
VAddr src_address, VAddr dst_address, bool reply);
|
||||
VAddr src_address, VAddr dst_address,
|
||||
std::vector<MappedBufferContext>& mapped_buffer_context,
|
||||
bool reply);
|
||||
} // namespace Kernel
|
||||
|
@ -8,6 +8,7 @@
|
||||
#include <string>
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/kernel/ipc.h"
|
||||
#include "core/hle/kernel/object.h"
|
||||
#include "core/hle/kernel/wait_object.h"
|
||||
#include "core/hle/result.h"
|
||||
@ -83,6 +84,9 @@ public:
|
||||
/// TODO(Subv): Find a better name for this.
|
||||
SharedPtr<Thread> currently_handling;
|
||||
|
||||
/// A temporary list holding mapped buffer info from IPC request, used for during IPC reply
|
||||
std::vector<MappedBufferContext> mapped_buffer_context;
|
||||
|
||||
private:
|
||||
explicit ServerSession(KernelSystem& kernel);
|
||||
~ServerSession() override;
|
||||
|
@ -600,8 +600,9 @@ static ResultCode ReceiveIPCRequest(SharedPtr<ServerSession> server_session,
|
||||
VAddr target_address = thread->GetCommandBufferAddress();
|
||||
VAddr source_address = server_session->currently_handling->GetCommandBufferAddress();
|
||||
|
||||
ResultCode translation_result = TranslateCommandBuffer(
|
||||
server_session->currently_handling, thread, source_address, target_address, false);
|
||||
ResultCode translation_result =
|
||||
TranslateCommandBuffer(server_session->currently_handling, thread, source_address,
|
||||
target_address, server_session->mapped_buffer_context, false);
|
||||
|
||||
// If a translation error occurred, immediately resume the client thread.
|
||||
if (translation_result.IsError()) {
|
||||
@ -667,7 +668,8 @@ ResultCode SVC::ReplyAndReceive(s32* index, VAddr handles_address, s32 handle_co
|
||||
VAddr target_address = request_thread->GetCommandBufferAddress();
|
||||
|
||||
ResultCode translation_result =
|
||||
TranslateCommandBuffer(thread, request_thread, source_address, target_address, true);
|
||||
TranslateCommandBuffer(thread, request_thread, source_address, target_address,
|
||||
session->mapped_buffer_context, true);
|
||||
|
||||
// Note: The real kernel seems to always panic if the Server->Client buffer translation
|
||||
// fails for whatever reason.
|
||||
|
Loading…
Reference in New Issue
Block a user