diff --git a/src/core/hle/ipc_helpers.h b/src/core/hle/ipc_helpers.h index 61573b153..d7127e899 100644 --- a/src/core/hle/ipc_helpers.h +++ b/src/core/hle/ipc_helpers.h @@ -121,7 +121,11 @@ public: [[deprecated]] void PushStaticBuffer(VAddr buffer_vaddr, size_t size, u8 buffer_id); void PushStaticBuffer(const std::vector& buffer, u8 buffer_id); - void PushMappedBuffer(VAddr buffer_vaddr, size_t size, MappedBufferPermissions perms); + [[deprecated]] void PushMappedBuffer(VAddr buffer_vaddr, size_t size, + MappedBufferPermissions perms); + + /// Pushes an HLE MappedBuffer interface back to unmapped the buffer. + void PushMappedBuffer(const Kernel::MappedBuffer& mapped_buffer); }; /// Push /// @@ -213,6 +217,11 @@ inline void RequestBuilder::PushMappedBuffer(VAddr buffer_vaddr, size_t size, Push(buffer_vaddr); } +inline void RequestBuilder::PushMappedBuffer(const Kernel::MappedBuffer& mapped_buffer) { + Push(mapped_buffer.GenerateDescriptor()); + Push(mapped_buffer.GetId()); +} + class RequestParser : public RequestHelperBase { public: RequestParser(Kernel::HLERequestContext& context, Header desired_header) @@ -333,8 +342,11 @@ public: * @param[out] buffer_perms If non-null, the pointed value will be set to the permissions of the * buffer */ - VAddr PopMappedBuffer(size_t* data_size = nullptr, - MappedBufferPermissions* buffer_perms = nullptr); + [[deprecated]] VAddr PopMappedBuffer(size_t* data_size, + MappedBufferPermissions* buffer_perms = nullptr); + + /// Pops a mapped buffer descriptor with its vaddr and resolves it to an HLE interface + Kernel::MappedBuffer& PopMappedBuffer(); /** * @brief Reads the next normal parameters as a struct, by copying it @@ -497,4 +509,11 @@ inline VAddr RequestParser::PopMappedBuffer(size_t* data_size, return Pop(); } +inline Kernel::MappedBuffer& RequestParser::PopMappedBuffer() { + u32 mapped_buffer_descriptor = Pop(); + ASSERT_MSG(GetDescriptorType(mapped_buffer_descriptor) == MappedBuffer, + "Tried to pop mapped buffer but the descriptor is not a mapped buffer descriptor"); + return context->GetMappedBuffer(Pop()); +} + } // namespace IPC diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp index 0653dd690..303b898f1 100644 --- a/src/core/hle/kernel/hle_ipc.cpp +++ b/src/core/hle/kernel/hle_ipc.cpp @@ -106,6 +106,12 @@ ResultCode HLERequestContext::PopulateFromIncomingCommandBuffer(const u32_le* sr cmd_buf[i++] = source_address; break; } + case IPC::DescriptorType::MappedBuffer: { + u32 next_id = static_cast(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); } @@ -165,6 +171,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); } @@ -173,4 +184,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 diff --git a/src/core/hle/kernel/hle_ipc.h b/src/core/hle/kernel/hle_ipc.h index 03a7a4deb..2a246fa27 100644 --- a/src/core/hle/kernel/hle_ipc.h +++ b/src/core/hle/kernel/hle_ipc.h @@ -63,6 +63,35 @@ protected: std::vector> 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 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, 8> request_handles; // The static buffers will be created when the IPC request is translated. std::array, IPC::MAX_STATIC_BUFFERS> static_buffers; + // The mapped buffers will be created when the IPC request is translated + boost::container::small_vector request_mapped_buffers; }; } // namespace Kernel diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index 208ea1ada..9b87bb6da 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp @@ -606,7 +606,7 @@ void DeleteContents(Service::Interface* self) { u8 media_type = rp.Pop(); u64 title_id = rp.Pop(); u32 content_count = rp.Pop(); - VAddr content_ids_in = rp.PopMappedBuffer(); + VAddr content_ids_in = rp.PopMappedBuffer(nullptr); IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); rb.Push(RESULT_SUCCESS); @@ -620,7 +620,7 @@ void GetProgramList(Service::Interface* self) { u32 count = rp.Pop(); u8 media_type = rp.Pop(); - VAddr title_ids_output_pointer = rp.PopMappedBuffer(); + VAddr title_ids_output_pointer = rp.PopMappedBuffer(nullptr); if (!Memory::IsValidVirtualAddress(title_ids_output_pointer) || media_type > 2) { IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); @@ -765,7 +765,7 @@ void ListDataTitleTicketInfos(Service::Interface* self) { u32 ticket_count = rp.Pop(); u64 title_id = rp.Pop(); u32 start_index = rp.Pop(); - VAddr ticket_info_out = rp.PopMappedBuffer(); + VAddr ticket_info_out = rp.PopMappedBuffer(nullptr); VAddr ticket_info_write = ticket_info_out; for (u32 i = 0; i < ticket_count; i++) { @@ -840,7 +840,7 @@ void GetTicketList(Service::Interface* self) { IPC::RequestParser rp(Kernel::GetCommandBuffer(), 9, 2, 2); // 0x00090082 u32 ticket_list_count = rp.Pop(); u32 ticket_index = rp.Pop(); - VAddr ticket_tids_out = rp.PopMappedBuffer(); + VAddr ticket_tids_out = rp.PopMappedBuffer(nullptr); IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); rb.Push(RESULT_SUCCESS); diff --git a/src/core/hle/service/fs/archive.cpp b/src/core/hle/service/fs/archive.cpp index ec1a65223..855a63a5c 100644 --- a/src/core/hle/service/fs/archive.cpp +++ b/src/core/hle/service/fs/archive.cpp @@ -27,6 +27,7 @@ #include "core/file_sys/errors.h" #include "core/file_sys/file_backend.h" #include "core/hle/ipc.h" +#include "core/hle/ipc_helpers.h" #include "core/hle/kernel/client_port.h" #include "core/hle/kernel/client_session.h" #include "core/hle/kernel/handle_table.h" @@ -84,122 +85,132 @@ enum class DirectoryCommand : u32 { }; File::File(std::unique_ptr&& backend, const FileSys::Path& path) - : path(path), priority(0), backend(std::move(backend)) {} + : path(path), priority(0), backend(std::move(backend)), ServiceFramework("", 1) { + static const FunctionInfo functions[] = { + {0x080200C2, &File::Read, "Read"}, + {0x08030102, &File::Write, "Write"}, + {0x08040000, &File::GetSize, "GetSize"}, + {0x08050080, &File::SetSize, "SetSize"}, + {0x08080000, &File::Close, "Close"}, + {0x08090000, &File::Flush, "Flush"}, + {0x080A0040, &File::SetPriority, "SetPriority"}, + {0x080B0000, &File::GetPriority, "GetPriority"}, + {0x080C0000, &File::OpenLinkFile, "OpenLinkFile"}, + }; + RegisterHandlers(functions); +} -File::~File() {} +void File::Read(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx, 0x0802, 3, 2); + u64 offset = rp.Pop(); + u32 length = rp.Pop(); + auto& buffer = rp.PopMappedBuffer(); + LOG_TRACE(Service_FS, "Read %s: offset=0x%" PRIx64 " length=0x%08X", GetName().c_str(), offset, + length); -void File::HandleSyncRequest(Kernel::SharedPtr server_session) { + if (offset + length > backend->GetSize()) { + LOG_ERROR(Service_FS, "Reading from out of bounds offset=0x%" PRIx64 + " length=0x%08X file_size=0x%" PRIx64, + offset, length, backend->GetSize()); + } + + IPC::RequestBuilder rb = rp.MakeBuilder(2, 2); + + std::vector data(length); + ResultVal read = backend->Read(offset, data.size(), data.data()); + if (read.Failed()) { + rb.Push(read.Code()); + rb.Push(0); + } else { + buffer.Write(data.data(), 0, *read); + rb.Push(RESULT_SUCCESS); + rb.Push(*read); + } + rb.PushMappedBuffer(buffer); +} + +void File::Write(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx, 0x0803, 3, 2); + u64 offset = rp.Pop(); + u32 length = rp.Pop(); + u32 flush = rp.Pop(); + auto& buffer = rp.PopMappedBuffer(); + LOG_TRACE(Service_FS, "Write %s: offset=0x%llx length=%d, flush=0x%x", GetName().c_str(), + offset, length, flush); + + IPC::RequestBuilder rb = rp.MakeBuilder(2, 2); + + std::vector data(length); + buffer.Read(data.data(), 0, data.size()); + ResultVal written = backend->Write(offset, data.size(), flush != 0, data.data()); + if (written.Failed()) { + rb.Push(written.Code()); + rb.Push(0); + } else { + rb.Push(RESULT_SUCCESS); + rb.Push(*written); + } + rb.PushMappedBuffer(buffer); +} + +void File::GetSize(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx, 0x0804, 0, 0); + IPC::RequestBuilder rb = rp.MakeBuilder(3, 0); + rb.Push(RESULT_SUCCESS); + rb.Push(backend->GetSize()); +} + +void File::SetSize(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx, 0x0805, 2, 0); + backend->SetSize(rp.Pop()); + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(RESULT_SUCCESS); +} + +void File::Close(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx, 0x0808, 0, 0); + backend->Close(); + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(RESULT_SUCCESS); +} + +void File::Flush(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx, 0x0809, 0, 0); + backend->Flush(); + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(RESULT_SUCCESS); +} + +void File::SetPriority(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx, 0x080A, 1, 0); + priority = rp.Pop(); + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(RESULT_SUCCESS); +} + +void File::GetPriority(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx, 0x080B, 0, 0); + IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); + rb.Push(RESULT_SUCCESS); + rb.Push(priority); +} + +void File::OpenLinkFile(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_FS, "(STUBBED) File command OpenLinkFile %s", GetName().c_str()); using Kernel::ClientSession; using Kernel::ServerSession; using Kernel::SharedPtr; + IPC::RequestParser rp(ctx, 0x080C, 0, 0); + IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); + auto sessions = ServerSession::CreateSessionPair(GetName()); + ClientConnected(std::get>(sessions)); - u32* cmd_buff = Kernel::GetCommandBuffer(); - FileCommand cmd = static_cast(cmd_buff[0]); - switch (cmd) { - - // Read from file... - case FileCommand::Read: { - u64 offset = cmd_buff[1] | ((u64)cmd_buff[2]) << 32; - u32 length = cmd_buff[3]; - u32 address = cmd_buff[5]; - LOG_TRACE(Service_FS, "Read %s: offset=0x%llx length=%d address=0x%x", GetName().c_str(), - offset, length, address); - - if (offset + length > backend->GetSize()) { - LOG_ERROR(Service_FS, "Reading from out of bounds offset=0x%" PRIx64 - " length=0x%08X file_size=0x%" PRIx64, - offset, length, backend->GetSize()); - } - - std::vector data(length); - ResultVal read = backend->Read(offset, data.size(), data.data()); - if (read.Failed()) { - cmd_buff[1] = read.Code().raw; - return; - } - Memory::WriteBlock(address, data.data(), *read); - cmd_buff[2] = static_cast(*read); - break; - } - - // Write to file... - case FileCommand::Write: { - u64 offset = cmd_buff[1] | ((u64)cmd_buff[2]) << 32; - u32 length = cmd_buff[3]; - u32 flush = cmd_buff[4]; - u32 address = cmd_buff[6]; - LOG_TRACE(Service_FS, "Write %s: offset=0x%llx length=%d address=0x%x, flush=0x%x", - GetName().c_str(), offset, length, address, flush); - - std::vector data(length); - Memory::ReadBlock(address, data.data(), data.size()); - ResultVal written = backend->Write(offset, data.size(), flush != 0, data.data()); - if (written.Failed()) { - cmd_buff[1] = written.Code().raw; - return; - } - cmd_buff[2] = static_cast(*written); - break; - } - - case FileCommand::GetSize: { - LOG_TRACE(Service_FS, "GetSize %s", GetName().c_str()); - u64 size = backend->GetSize(); - cmd_buff[2] = (u32)size; - cmd_buff[3] = size >> 32; - break; - } - - case FileCommand::SetSize: { - u64 size = cmd_buff[1] | ((u64)cmd_buff[2] << 32); - LOG_TRACE(Service_FS, "SetSize %s size=%llu", GetName().c_str(), size); - backend->SetSize(size); - break; - } - - case FileCommand::Close: { - LOG_TRACE(Service_FS, "Close %s", GetName().c_str()); - backend->Close(); - break; - } - - case FileCommand::Flush: { - LOG_TRACE(Service_FS, "Flush"); - backend->Flush(); - break; - } - - case FileCommand::OpenLinkFile: { - LOG_WARNING(Service_FS, "(STUBBED) File command OpenLinkFile %s", GetName().c_str()); - auto sessions = ServerSession::CreateSessionPair(GetName()); - ClientConnected(std::get>(sessions)); - cmd_buff[3] = Kernel::g_handle_table.Create(std::get>(sessions)) - .ValueOr(INVALID_HANDLE); - break; - } - - case FileCommand::SetPriority: { - priority = cmd_buff[1]; - LOG_TRACE(Service_FS, "SetPriority %u", priority); - break; - } - - case FileCommand::GetPriority: { - cmd_buff[2] = priority; - LOG_TRACE(Service_FS, "GetPriority"); - break; - } - - // Unknown command... - default: - LOG_ERROR(Service_FS, "Unknown command=0x%08X!", static_cast(cmd)); - ResultCode error = UnimplementedFunction(ErrorModule::FS); - cmd_buff[1] = error.raw; // TODO(Link Mauve): use the correct error code for that. - return; - } - cmd_buff[1] = RESULT_SUCCESS.raw; // No error + rb.Push(RESULT_SUCCESS); + rb.PushObjects(std::get>(sessions)); } +File::~File() {} + Directory::Directory(std::unique_ptr&& backend, const FileSys::Path& path) : path(path), backend(std::move(backend)) {} diff --git a/src/core/hle/service/fs/archive.h b/src/core/hle/service/fs/archive.h index e3c8fc2ef..b9a9637e1 100644 --- a/src/core/hle/service/fs/archive.h +++ b/src/core/hle/service/fs/archive.h @@ -10,6 +10,7 @@ #include "core/file_sys/archive_backend.h" #include "core/hle/kernel/hle_ipc.h" #include "core/hle/result.h" +#include "core/hle/service/service.h" namespace FileSys { class DirectoryBackend; @@ -47,7 +48,9 @@ enum class MediaType : u32 { NAND = 0, SDMC = 1, GameCard = 2 }; typedef u64 ArchiveHandle; -class File final : public Kernel::SessionRequestHandler { +// TODO: File is not a real service, but it can still utilize ServiceFramework::RegisterHandlers. +// Consider splitting ServiceFramework interface. +class File final : public ServiceFramework { public: File(std::unique_ptr&& backend, const FileSys::Path& path); ~File(); @@ -60,8 +63,16 @@ public: u32 priority; ///< Priority of the file. TODO(Subv): Find out what this means std::unique_ptr backend; ///< File backend interface -protected: - void HandleSyncRequest(Kernel::SharedPtr server_session) override; +private: + void Read(Kernel::HLERequestContext& ctx); + void Write(Kernel::HLERequestContext& ctx); + void GetSize(Kernel::HLERequestContext& ctx); + void SetSize(Kernel::HLERequestContext& ctx); + void Close(Kernel::HLERequestContext& ctx); + void Flush(Kernel::HLERequestContext& ctx); + void SetPriority(Kernel::HLERequestContext& ctx); + void GetPriority(Kernel::HLERequestContext& ctx); + void OpenLinkFile(Kernel::HLERequestContext& ctx); }; class Directory final : public Kernel::SessionRequestHandler { diff --git a/src/tests/core/hle/kernel/hle_ipc.cpp b/src/tests/core/hle/kernel/hle_ipc.cpp index 64b06cc8b..9e51666e8 100644 --- a/src/tests/core/hle/kernel/hle_ipc.cpp +++ b/src/tests/core/hle/kernel/hle_ipc.cpp @@ -136,26 +136,58 @@ TEST_CASE("HLERequestContext::PopulateFromIncomingCommandBuffer", "[core][kernel REQUIRE(process->vm_manager.UnmapRange(target_address, buffer->size()) == RESULT_SUCCESS); } - SECTION("translates mixed params") { + SECTION("translates MappedBuffer descriptors") { auto buffer = std::make_shared>(Memory::PAGE_SIZE); - std::fill(buffer->begin(), buffer->end(), 0xCE); + std::fill(buffer->begin(), buffer->end(), 0xCD); VAddr target_address = 0x10000000; auto result = process->vm_manager.MapMemoryBlock(target_address, buffer, 0, buffer->size(), MemoryState::Private); + + const u32_le input[]{ + IPC::MakeHeader(0, 0, 2), IPC::MappedBufferDesc(buffer->size(), IPC::R), target_address, + }; + + context.PopulateFromIncomingCommandBuffer(input, *process, handle_table); + + std::vector other_buffer(buffer->size()); + context.GetMappedBuffer(0).Read(other_buffer.data(), 0, buffer->size()); + + CHECK(other_buffer == *buffer); + + REQUIRE(process->vm_manager.UnmapRange(target_address, buffer->size()) == RESULT_SUCCESS); + } + + SECTION("translates mixed params") { + auto buffer_static = std::make_shared>(Memory::PAGE_SIZE); + std::fill(buffer_static->begin(), buffer_static->end(), 0xCE); + + auto buffer_mapped = std::make_shared>(Memory::PAGE_SIZE); + std::fill(buffer_mapped->begin(), buffer_mapped->end(), 0xDF); + + VAddr target_address_static = 0x10000000; + auto result = process->vm_manager.MapMemoryBlock( + target_address_static, buffer_static, 0, buffer_static->size(), MemoryState::Private); + REQUIRE(result.Code() == RESULT_SUCCESS); + + VAddr target_address_mapped = 0x20000000; + result = process->vm_manager.MapMemoryBlock(target_address_mapped, buffer_mapped, 0, + buffer_mapped->size(), MemoryState::Private); REQUIRE(result.Code() == RESULT_SUCCESS); auto a = MakeObject(); const u32_le input[]{ - IPC::MakeHeader(0, 2, 6), + IPC::MakeHeader(0, 2, 8), 0x12345678, 0xABCDEF00, IPC::MoveHandleDesc(1), handle_table.Create(a).Unwrap(), IPC::CallingPidDesc(), 0, - IPC::StaticBufferDesc(buffer->size(), 0), - target_address, + IPC::StaticBufferDesc(buffer_static->size(), 0), + target_address_static, + IPC::MappedBufferDesc(buffer_mapped->size(), IPC::R), + target_address_mapped, }; context.PopulateFromIncomingCommandBuffer(input, *process, handle_table); @@ -165,9 +197,15 @@ TEST_CASE("HLERequestContext::PopulateFromIncomingCommandBuffer", "[core][kernel CHECK(output[2] == 0xABCDEF00); CHECK(context.GetIncomingHandle(output[4]) == a); CHECK(output[6] == process->process_id); - CHECK(context.GetStaticBuffer(0) == *buffer); + CHECK(context.GetStaticBuffer(0) == *buffer_static); + std::vector other_buffer(buffer_mapped->size()); + context.GetMappedBuffer(0).Read(other_buffer.data(), 0, buffer_mapped->size()); + CHECK(other_buffer == *buffer_mapped); - REQUIRE(process->vm_manager.UnmapRange(target_address, buffer->size()) == RESULT_SUCCESS); + REQUIRE(process->vm_manager.UnmapRange(target_address_static, buffer_static->size()) == + RESULT_SUCCESS); + REQUIRE(process->vm_manager.UnmapRange(target_address_mapped, buffer_mapped->size()) == + RESULT_SUCCESS); } } @@ -275,6 +313,38 @@ TEST_CASE("HLERequestContext::WriteToOutgoingCommandBuffer", "[core][kernel]") { REQUIRE(process->vm_manager.UnmapRange(target_address, output_buffer->size()) == RESULT_SUCCESS); } + + SECTION("translates StaticBuffer descriptors") { + std::vector input_buffer(Memory::PAGE_SIZE); + std::fill(input_buffer.begin(), input_buffer.end(), 0xAB); + + auto output_buffer = std::make_shared>(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); + + const u32_le input_cmdbuff[]{ + IPC::MakeHeader(0, 0, 2), IPC::MappedBufferDesc(output_buffer->size(), IPC::W), + target_address, + }; + + context.PopulateFromIncomingCommandBuffer(input_cmdbuff, *process, handle_table); + + context.GetMappedBuffer(0).Write(input_buffer.data(), 0, input_buffer.size()); + + input[0] = IPC::MakeHeader(0, 0, 2); + input[1] = IPC::MappedBufferDesc(output_buffer->size(), IPC::W); + input[2] = 0; + + context.WriteToOutgoingCommandBuffer(output, *process, handle_table); + + CHECK(output[1] == IPC::MappedBufferDesc(output_buffer->size(), IPC::W)); + CHECK(output[2] == target_address); + CHECK(*output_buffer == input_buffer); + REQUIRE(process->vm_manager.UnmapRange(target_address, output_buffer->size()) == + RESULT_SUCCESS); + } } } // namespace Kernel