mirror of
https://github.com/citra-mirror/citra.git
synced 2024-08-21 19:03:23 +02:00
Merge pull request #3077 from Subv/hle_static_buffer
Kernel/IPC: Implement StaticBuffer translation for HLE services that use the HLERequestContext architecture.
This commit is contained in:
commit
555c8ba7c1
@ -40,13 +40,16 @@ static const int kStaticBuffersOffset = 0x100;
|
||||
inline u32* GetStaticBuffers(const int offset = 0) {
|
||||
return GetCommandBuffer(kStaticBuffersOffset + offset);
|
||||
}
|
||||
}
|
||||
} // namespace Kernel
|
||||
|
||||
namespace IPC {
|
||||
|
||||
/// Size of the command buffer area, in 32-bit words.
|
||||
constexpr size_t COMMAND_BUFFER_LENGTH = 0x100 / sizeof(u32);
|
||||
|
||||
// Maximum number of static buffers per thread.
|
||||
constexpr size_t MAX_STATIC_BUFFERS = 16;
|
||||
|
||||
// These errors are commonly returned by invalid IPC translations, so alias them here for
|
||||
// convenience.
|
||||
// TODO(yuriks): These will probably go away once translation is implemented inside the kernel.
|
||||
|
@ -8,6 +8,7 @@
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include "core/hle/ipc.h"
|
||||
#include "core/hle/kernel/handle_table.h"
|
||||
#include "core/hle/kernel/hle_ipc.h"
|
||||
@ -117,7 +118,8 @@ public:
|
||||
|
||||
void PushCurrentPIDHandle();
|
||||
|
||||
void PushStaticBuffer(VAddr buffer_vaddr, size_t size, u8 buffer_id);
|
||||
[[deprecated]] void PushStaticBuffer(VAddr buffer_vaddr, size_t size, u8 buffer_id);
|
||||
void PushStaticBuffer(const std::vector<u8>& buffer, u8 buffer_id);
|
||||
|
||||
void PushMappedBuffer(VAddr buffer_vaddr, size_t size, MappedBufferPermissions perms);
|
||||
};
|
||||
@ -195,6 +197,16 @@ inline void RequestBuilder::PushStaticBuffer(VAddr buffer_vaddr, size_t size, u8
|
||||
Push(buffer_vaddr);
|
||||
}
|
||||
|
||||
inline void RequestBuilder::PushStaticBuffer(const std::vector<u8>& buffer, u8 buffer_id) {
|
||||
ASSERT_MSG(buffer_id < MAX_STATIC_BUFFERS, "Invalid static buffer id");
|
||||
|
||||
Push(StaticBufferDesc(buffer.size(), buffer_id));
|
||||
// This address will be replaced by the correct static buffer address during IPC translation.
|
||||
Push<VAddr>(0xDEADC0DE);
|
||||
|
||||
context->AddStaticBuffer(buffer_id, buffer);
|
||||
}
|
||||
|
||||
inline void RequestBuilder::PushMappedBuffer(VAddr buffer_vaddr, size_t size,
|
||||
MappedBufferPermissions perms) {
|
||||
Push(MappedBufferDesc(size, perms));
|
||||
@ -295,17 +307,23 @@ public:
|
||||
* @brief Pops the static buffer vaddr
|
||||
* @return The virtual address of the buffer
|
||||
* @param[out] data_size If non-null, the pointed value will be set to the size of the data
|
||||
* @param[out] useStaticBuffersToGetVaddr Indicates if we should read the vaddr from the static
|
||||
* buffers (which is the correct thing to do, but no service presently implement it) instead of
|
||||
* using the same value as the process who sent the request
|
||||
* given by the source process
|
||||
*
|
||||
* Static buffers must be set up before any IPC request using those is sent.
|
||||
* In real services, static buffers must be set up before any IPC request using those is sent.
|
||||
* It is the duty of the process (usually services) to allocate and set up the receiving static
|
||||
* buffer information
|
||||
* buffer information. Our HLE services do not need to set up the buffers beforehand.
|
||||
* Please note that the setup uses virtual addresses.
|
||||
*/
|
||||
VAddr PopStaticBuffer(size_t* data_size = nullptr, bool useStaticBuffersToGetVaddr = false);
|
||||
[[deprecated]] VAddr PopStaticBuffer(size_t* data_size);
|
||||
|
||||
/**
|
||||
* @brief Pops a static buffer from the IPC request buffer.
|
||||
* @return The buffer that was copied from the IPC request originator.
|
||||
*
|
||||
* In real services, static buffers must be set up before any IPC request using those is sent.
|
||||
* It is the duty of the process (usually services) to allocate and set up the receiving static
|
||||
* buffer information. Our HLE services do not need to set up the buffers beforehand.
|
||||
*/
|
||||
const std::vector<u8>& PopStaticBuffer();
|
||||
|
||||
/**
|
||||
* @brief Pops the mapped buffer vaddr
|
||||
@ -451,21 +469,21 @@ inline std::tuple<Kernel::SharedPtr<T>...> RequestParser::PopObjects() {
|
||||
std::index_sequence_for<T...>{});
|
||||
}
|
||||
|
||||
inline VAddr RequestParser::PopStaticBuffer(size_t* data_size, bool useStaticBuffersToGetVaddr) {
|
||||
inline VAddr RequestParser::PopStaticBuffer(size_t* data_size) {
|
||||
const u32 sbuffer_descriptor = Pop<u32>();
|
||||
StaticBufferDescInfo bufferInfo{sbuffer_descriptor};
|
||||
if (data_size != nullptr)
|
||||
*data_size = bufferInfo.size;
|
||||
if (!useStaticBuffersToGetVaddr)
|
||||
return Pop<VAddr>();
|
||||
else {
|
||||
ASSERT_MSG(0, "remove the assert if multiprocess/IPC translation are implemented.");
|
||||
// The buffer has already been copied to the static buffer by the kernel during
|
||||
// translation
|
||||
Pop<VAddr>(); // Pop the calling process buffer address
|
||||
// and get the vaddr from the static buffers
|
||||
return cmdbuf[(0x100 >> 2) + bufferInfo.buffer_id * 2 + 1];
|
||||
}
|
||||
return Pop<VAddr>();
|
||||
}
|
||||
|
||||
inline const std::vector<u8>& RequestParser::PopStaticBuffer() {
|
||||
const u32 sbuffer_descriptor = Pop<u32>();
|
||||
// Pop the address from the incoming request buffer
|
||||
Pop<VAddr>();
|
||||
|
||||
StaticBufferDescInfo buffer_info{sbuffer_descriptor};
|
||||
return context->GetStaticBuffer(buffer_info.buffer_id);
|
||||
}
|
||||
|
||||
inline VAddr RequestParser::PopMappedBuffer(size_t* data_size,
|
||||
|
@ -2,6 +2,7 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <vector>
|
||||
#include <boost/range/algorithm_ext/erase.hpp>
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
@ -44,6 +45,16 @@ void HLERequestContext::ClearIncomingObjects() {
|
||||
request_handles.clear();
|
||||
}
|
||||
|
||||
const std::vector<u8>& HLERequestContext::GetStaticBuffer(u8 buffer_id) const {
|
||||
ASSERT_MSG(!static_buffers[buffer_id].empty(), "Empty static buffer!");
|
||||
return static_buffers[buffer_id];
|
||||
}
|
||||
|
||||
void HLERequestContext::AddStaticBuffer(u8 buffer_id, std::vector<u8> data) {
|
||||
ASSERT(static_buffers[buffer_id].empty() && !data.empty());
|
||||
static_buffers[buffer_id] = std::move(data);
|
||||
}
|
||||
|
||||
ResultCode HLERequestContext::PopulateFromIncomingCommandBuffer(const u32_le* src_cmdbuf,
|
||||
Process& src_process,
|
||||
HandleTable& src_table) {
|
||||
@ -84,6 +95,18 @@ ResultCode HLERequestContext::PopulateFromIncomingCommandBuffer(const u32_le* sr
|
||||
cmd_buf[i++] = src_process.process_id;
|
||||
break;
|
||||
}
|
||||
case IPC::DescriptorType::StaticBuffer: {
|
||||
VAddr source_address = src_cmdbuf[i];
|
||||
IPC::StaticBufferDescInfo buffer_info{descriptor};
|
||||
|
||||
// Copy the input buffer into our own vector and store it.
|
||||
std::vector<u8> data(buffer_info.size);
|
||||
Memory::ReadBlock(src_process, source_address, data.data(), data.size());
|
||||
|
||||
AddStaticBuffer(buffer_info.buffer_id, std::move(data));
|
||||
cmd_buf[i++] = source_address;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
UNIMPLEMENTED_MSG("Unsupported handle translation: 0x%08X", descriptor);
|
||||
}
|
||||
@ -124,6 +147,25 @@ ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(u32_le* dst_cmdbuf, P
|
||||
}
|
||||
break;
|
||||
}
|
||||
case IPC::DescriptorType::StaticBuffer: {
|
||||
IPC::StaticBufferDescInfo buffer_info{descriptor};
|
||||
|
||||
const auto& data = GetStaticBuffer(buffer_info.buffer_id);
|
||||
|
||||
// Grab the address that the target thread set up to receive the response static buffer
|
||||
// and write our data there. The static buffers area is located right after the command
|
||||
// buffer area.
|
||||
size_t static_buffer_offset = IPC::COMMAND_BUFFER_LENGTH + 2 * buffer_info.buffer_id;
|
||||
IPC::StaticBufferDescInfo target_descriptor{dst_cmdbuf[static_buffer_offset]};
|
||||
VAddr target_address = dst_cmdbuf[static_buffer_offset + 1];
|
||||
|
||||
ASSERT_MSG(target_descriptor.size >= data.size(), "Static buffer data is too big");
|
||||
|
||||
Memory::WriteBlock(dst_process, target_address, data.data(), data.size());
|
||||
|
||||
dst_cmdbuf[i++] = target_address;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
UNIMPLEMENTED_MSG("Unsupported handle translation: 0x%08X", descriptor);
|
||||
}
|
||||
|
@ -119,6 +119,18 @@ public:
|
||||
*/
|
||||
void ClearIncomingObjects();
|
||||
|
||||
/**
|
||||
* Retrieves the static buffer identified by the input buffer_id. The static buffer *must* have
|
||||
* been created in PopulateFromIncomingCommandBuffer by way of an input StaticBuffer descriptor.
|
||||
*/
|
||||
const std::vector<u8>& GetStaticBuffer(u8 buffer_id) const;
|
||||
|
||||
/**
|
||||
* Sets up a static buffer that will be copied to the target process when the request is
|
||||
* translated.
|
||||
*/
|
||||
void AddStaticBuffer(u8 buffer_id, std::vector<u8> data);
|
||||
|
||||
/// Populates this context with data from the requesting process/thread.
|
||||
ResultCode PopulateFromIncomingCommandBuffer(const u32_le* src_cmdbuf, Process& src_process,
|
||||
HandleTable& src_table);
|
||||
@ -131,6 +143,8 @@ private:
|
||||
SharedPtr<ServerSession> session;
|
||||
// TODO(yuriks): Check common usage of this and optimize size accordingly
|
||||
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;
|
||||
};
|
||||
|
||||
} // namespace Kernel
|
||||
|
@ -2,6 +2,7 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <vector>
|
||||
#include "common/common_types.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/hle/ipc.h"
|
||||
@ -19,17 +20,12 @@ namespace AC {
|
||||
void Module::Interface::CreateDefaultConfig(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp(ctx, 0x1, 0, 0);
|
||||
|
||||
std::size_t desc_size;
|
||||
VAddr ac_config_addr = rp.PeekStaticBuffer(0, &desc_size);
|
||||
|
||||
ASSERT_MSG(desc_size >= sizeof(Module::ACConfig),
|
||||
"Output buffer size can't fit ACConfig structure");
|
||||
|
||||
Memory::WriteBlock(ac_config_addr, &ac->default_config, sizeof(ACConfig));
|
||||
std::vector<u8> buffer(sizeof(ACConfig));
|
||||
std::memcpy(buffer.data(), &ac->default_config, buffer.size());
|
||||
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 2);
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushStaticBuffer(ac_config_addr, sizeof(ACConfig), 0);
|
||||
rb.PushStaticBuffer(std::move(buffer), 0);
|
||||
|
||||
LOG_WARNING(Service_AC, "(STUBBED) called");
|
||||
}
|
||||
@ -106,7 +102,7 @@ void Module::Interface::GetWifiStatus(Kernel::HLERequestContext& ctx) {
|
||||
|
||||
void Module::Interface::GetInfraPriority(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp(ctx, 0x27, 0, 2);
|
||||
VAddr ac_config = rp.PopStaticBuffer();
|
||||
const std::vector<u8>& ac_config = rp.PopStaticBuffer();
|
||||
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
@ -121,13 +117,13 @@ void Module::Interface::SetRequestEulaVersion(Kernel::HLERequestContext& ctx) {
|
||||
u32 major = rp.Pop<u8>();
|
||||
u32 minor = rp.Pop<u8>();
|
||||
|
||||
VAddr ac_config = rp.PopStaticBuffer();
|
||||
const std::vector<u8>& ac_config = rp.PopStaticBuffer();
|
||||
|
||||
// TODO(Subv): Copy over the input ACConfig to the stored ACConfig.
|
||||
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 2);
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushStaticBuffer(ac_config, sizeof(ACConfig), 0);
|
||||
rb.PushStaticBuffer(std::move(ac_config), 0);
|
||||
|
||||
LOG_WARNING(Service_AC, "(STUBBED) called, major=%u, minor=%u", major, minor);
|
||||
}
|
||||
|
@ -720,7 +720,7 @@ void AppletUtility(Service::Interface* self) {
|
||||
u32 utility_command = rp.Pop<u32>();
|
||||
u32 input_size = rp.Pop<u32>();
|
||||
u32 output_size = rp.Pop<u32>();
|
||||
VAddr input_addr = rp.PopStaticBuffer();
|
||||
VAddr input_addr = rp.PopStaticBuffer(nullptr);
|
||||
|
||||
VAddr output_addr = rp.PeekStaticBuffer(0);
|
||||
|
||||
@ -823,7 +823,7 @@ void StartLibraryApplet(Service::Interface* self) {
|
||||
|
||||
size_t buffer_size = rp.Pop<u32>();
|
||||
Kernel::Handle handle = rp.PopHandle();
|
||||
VAddr buffer_addr = rp.PopStaticBuffer();
|
||||
VAddr buffer_addr = rp.PopStaticBuffer(nullptr);
|
||||
|
||||
LOG_DEBUG(Service_APT, "called applet_id=%08X", static_cast<u32>(applet_id));
|
||||
|
||||
|
@ -113,7 +113,7 @@ void UnscrambleLocalFriendCode(Service::Interface* self) {
|
||||
IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x1C, 1, 2);
|
||||
const u32 friend_code_count = rp.Pop<u32>();
|
||||
size_t in_buffer_size;
|
||||
const VAddr scrambled_friend_codes = rp.PopStaticBuffer(&in_buffer_size, false);
|
||||
const VAddr scrambled_friend_codes = rp.PopStaticBuffer(&in_buffer_size);
|
||||
ASSERT_MSG(in_buffer_size == (friend_code_count * scrambled_friend_code_size),
|
||||
"Wrong input buffer size");
|
||||
|
||||
|
@ -72,7 +72,7 @@ static void OpenFile(Service::Interface* self) {
|
||||
FileSys::Mode mode;
|
||||
mode.hex = rp.Pop<u32>();
|
||||
u32 attributes = rp.Pop<u32>(); // TODO(Link Mauve): do something with those attributes.
|
||||
VAddr filename_ptr = rp.PopStaticBuffer();
|
||||
VAddr filename_ptr = rp.PopStaticBuffer(nullptr);
|
||||
FileSys::Path file_path(filename_type, filename_size, filename_ptr);
|
||||
|
||||
LOG_DEBUG(Service_FS, "path=%s, mode=%u attrs=%u", file_path.DebugStr().c_str(), mode.hex,
|
||||
|
@ -433,7 +433,7 @@ static void FinalizeIrNop(Interface* self) {
|
||||
static void SendIrNop(Interface* self) {
|
||||
IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x0D, 1, 2);
|
||||
const u32 size = rp.Pop<u32>();
|
||||
const VAddr address = rp.PopStaticBuffer();
|
||||
const VAddr address = rp.PopStaticBuffer(nullptr);
|
||||
|
||||
std::vector<u8> buffer(size);
|
||||
Memory::ReadBlock(address, buffer.data(), size);
|
||||
|
@ -771,9 +771,9 @@ static void BeginHostingNetwork(Interface* self) {
|
||||
const u32 passphrase_size = rp.Pop<u32>();
|
||||
|
||||
size_t desc_size;
|
||||
const VAddr network_info_address = rp.PopStaticBuffer(&desc_size, false);
|
||||
const VAddr network_info_address = rp.PopStaticBuffer(&desc_size);
|
||||
ASSERT(desc_size == sizeof(NetworkInfo));
|
||||
const VAddr passphrase_address = rp.PopStaticBuffer(&desc_size, false);
|
||||
const VAddr passphrase_address = rp.PopStaticBuffer(&desc_size);
|
||||
ASSERT(desc_size == passphrase_size);
|
||||
|
||||
// TODO(Subv): Store the passphrase and verify it when attempting a connection.
|
||||
@ -907,7 +907,7 @@ static void SendTo(Interface* self) {
|
||||
u32 flags = rp.Pop<u32>();
|
||||
|
||||
size_t desc_size;
|
||||
const VAddr input_address = rp.PopStaticBuffer(&desc_size, false);
|
||||
const VAddr input_address = rp.PopStaticBuffer(&desc_size);
|
||||
ASSERT(desc_size >= data_size);
|
||||
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||
@ -1093,7 +1093,7 @@ static void SetApplicationData(Interface* self) {
|
||||
u32 size = rp.Pop<u32>();
|
||||
|
||||
size_t desc_size;
|
||||
const VAddr address = rp.PopStaticBuffer(&desc_size, false);
|
||||
const VAddr address = rp.PopStaticBuffer(&desc_size);
|
||||
ASSERT(desc_size == size);
|
||||
|
||||
LOG_DEBUG(Service_NWM, "called");
|
||||
|
@ -116,25 +116,58 @@ TEST_CASE("HLERequestContext::PopulateFromIncomingCommandBuffer", "[core][kernel
|
||||
REQUIRE(context.CommandBuffer()[2] == process->process_id);
|
||||
}
|
||||
|
||||
SECTION("translates StaticBuffer descriptors") {
|
||||
auto buffer = std::make_shared<std::vector<u8>>(Memory::PAGE_SIZE);
|
||||
std::fill(buffer->begin(), buffer->end(), 0xAB);
|
||||
|
||||
VAddr target_address = 0x10000000;
|
||||
auto result = process->vm_manager.MapMemoryBlock(target_address, buffer, 0, buffer->size(),
|
||||
MemoryState::Private);
|
||||
REQUIRE(result.Code() == RESULT_SUCCESS);
|
||||
|
||||
const u32_le input[]{
|
||||
IPC::MakeHeader(0, 0, 2), IPC::StaticBufferDesc(buffer->size(), 0), target_address,
|
||||
};
|
||||
|
||||
context.PopulateFromIncomingCommandBuffer(input, *process, handle_table);
|
||||
|
||||
CHECK(context.GetStaticBuffer(0) == *buffer);
|
||||
|
||||
REQUIRE(process->vm_manager.UnmapRange(target_address, buffer->size()) == RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
SECTION("translates mixed params") {
|
||||
auto buffer = std::make_shared<std::vector<u8>>(Memory::PAGE_SIZE);
|
||||
std::fill(buffer->begin(), buffer->end(), 0xCE);
|
||||
|
||||
VAddr target_address = 0x10000000;
|
||||
auto result = process->vm_manager.MapMemoryBlock(target_address, buffer, 0, buffer->size(),
|
||||
MemoryState::Private);
|
||||
REQUIRE(result.Code() == RESULT_SUCCESS);
|
||||
|
||||
auto a = MakeObject();
|
||||
const u32_le input[]{
|
||||
IPC::MakeHeader(0, 2, 4),
|
||||
IPC::MakeHeader(0, 2, 6),
|
||||
0x12345678,
|
||||
0xABCDEF00,
|
||||
IPC::MoveHandleDesc(1),
|
||||
handle_table.Create(a).Unwrap(),
|
||||
IPC::CallingPidDesc(),
|
||||
0,
|
||||
IPC::StaticBufferDesc(buffer->size(), 0),
|
||||
target_address,
|
||||
};
|
||||
|
||||
context.PopulateFromIncomingCommandBuffer(input, *process, handle_table);
|
||||
|
||||
auto* output = context.CommandBuffer();
|
||||
REQUIRE(output[1] == 0x12345678);
|
||||
REQUIRE(output[2] == 0xABCDEF00);
|
||||
REQUIRE(context.GetIncomingHandle(output[4]) == a);
|
||||
REQUIRE(output[6] == process->process_id);
|
||||
CHECK(output[1] == 0x12345678);
|
||||
CHECK(output[2] == 0xABCDEF00);
|
||||
CHECK(context.GetIncomingHandle(output[4]) == a);
|
||||
CHECK(output[6] == process->process_id);
|
||||
CHECK(context.GetStaticBuffer(0) == *buffer);
|
||||
|
||||
REQUIRE(process->vm_manager.UnmapRange(target_address, buffer->size()) == RESULT_SUCCESS);
|
||||
}
|
||||
}
|
||||
|
||||
@ -211,6 +244,37 @@ TEST_CASE("HLERequestContext::WriteToOutgoingCommandBuffer", "[core][kernel]") {
|
||||
REQUIRE(handle_table.GetGeneric(output[3]) == b);
|
||||
REQUIRE(handle_table.GetGeneric(output[5]) == c);
|
||||
}
|
||||
|
||||
SECTION("translates StaticBuffer descriptors") {
|
||||
std::vector<u8> input_buffer(Memory::PAGE_SIZE);
|
||||
std::fill(input_buffer.begin(), input_buffer.end(), 0xAB);
|
||||
|
||||
context.AddStaticBuffer(0, input_buffer);
|
||||
|
||||
auto output_buffer = std::make_shared<std::vector<u8>>(Memory::PAGE_SIZE);
|
||||
VAddr target_address = 0x10000000;
|
||||
auto result = process->vm_manager.MapMemoryBlock(
|
||||
target_address, output_buffer, 0, output_buffer->size(), MemoryState::Private);
|
||||
REQUIRE(result.Code() == RESULT_SUCCESS);
|
||||
|
||||
input[0] = IPC::MakeHeader(0, 0, 2);
|
||||
input[1] = IPC::StaticBufferDesc(input_buffer.size(), 0);
|
||||
input[2] = target_address;
|
||||
|
||||
// An entire command buffer plus enough space for one static buffer descriptor and its
|
||||
// target address
|
||||
std::array<u32_le, IPC::COMMAND_BUFFER_LENGTH + 2> output_cmdbuff;
|
||||
// Set up the output StaticBuffer
|
||||
output_cmdbuff[IPC::COMMAND_BUFFER_LENGTH] =
|
||||
IPC::StaticBufferDesc(output_buffer->size(), 0);
|
||||
output_cmdbuff[IPC::COMMAND_BUFFER_LENGTH + 1] = target_address;
|
||||
|
||||
context.WriteToOutgoingCommandBuffer(output_cmdbuff.data(), *process, handle_table);
|
||||
|
||||
CHECK(*output_buffer == input_buffer);
|
||||
REQUIRE(process->vm_manager.UnmapRange(target_address, output_buffer->size()) ==
|
||||
RESULT_SUCCESS);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Kernel
|
||||
|
Loading…
Reference in New Issue
Block a user