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