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:
Weiyi Wang 2018-11-08 13:53:20 -05:00
parent 7a564b904b
commit 0b8d2ecabe
4 changed files with 59 additions and 82 deletions

View File

@ -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:

View File

@ -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

View File

@ -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;

View File

@ -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.