From 960a1416de3780e91855d9389c4534acf8c061df Mon Sep 17 00:00:00 2001 From: bunnei Date: Sat, 14 Oct 2017 22:18:42 -0400 Subject: [PATCH] hle: Initial implementation of NX service framework and IPC. --- src/common/logging/backend.cpp | 2 +- src/common/logging/log.h | 2 +- src/core/CMakeLists.txt | 6 +- src/core/hle/ipc.h | 189 +++++++++++++------- src/core/hle/ipc_helpers.h | 124 ++++++------- src/core/hle/kernel/hle_ipc.cpp | 154 ++++++++-------- src/core/hle/kernel/hle_ipc.h | 25 ++- src/core/hle/service/lm/lm.cpp | 43 +++++ src/core/hle/service/lm/lm.h | 25 +++ src/core/hle/service/service.cpp | 119 +++++-------- src/core/hle/service/service.h | 84 +-------- src/core/hle/service/sm/controller.cpp | 42 +++++ src/core/hle/service/sm/controller.h | 22 +++ src/core/hle/service/sm/sm.cpp | 91 +++++++++- src/core/hle/service/sm/sm.h | 20 ++- src/core/hle/service/sm/srv.cpp | 235 ------------------------- src/core/hle/service/sm/srv.h | 38 ---- src/core/hle/svc.cpp | 1 + src/tests/CMakeLists.txt | 1 - src/tests/core/hle/kernel/hle_ipc.cpp | 216 ----------------------- src/video_core/command_processor.cpp | 2 +- 21 files changed, 578 insertions(+), 863 deletions(-) create mode 100644 src/core/hle/service/lm/lm.cpp create mode 100644 src/core/hle/service/lm/lm.h create mode 100644 src/core/hle/service/sm/controller.cpp create mode 100644 src/core/hle/service/sm/controller.h delete mode 100644 src/core/hle/service/sm/srv.cpp delete mode 100644 src/core/hle/service/sm/srv.h delete mode 100644 src/tests/core/hle/kernel/hle_ipc.cpp diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp index 1361ccfba..dc10a7bc6 100644 --- a/src/common/logging/backend.cpp +++ b/src/common/logging/backend.cpp @@ -32,7 +32,7 @@ namespace Log { CLS(Kernel) \ SUB(Kernel, SVC) \ CLS(Service) \ - SUB(Service, SRV) \ + SUB(Service, SM) \ SUB(Service, FS) \ SUB(Service, GSP) \ SUB(Service, CFG) \ diff --git a/src/common/logging/log.h b/src/common/logging/log.h index 2ebd8911c..8e4601d6c 100644 --- a/src/common/logging/log.h +++ b/src/common/logging/log.h @@ -49,7 +49,7 @@ enum class Class : ClassType { Kernel_SVC, ///< Kernel system calls Service, ///< HLE implementation of system services. Each major service /// should have its own subclass. - Service_SRV, ///< The SRV (Service Directory) implementation + Service_SM, ///< The SRV (Service Directory) implementation Service_FS, ///< The FS (Filesystem) service implementation Service_GSP, ///< The GSP (GPU control) service Service_CFG, ///< The CFG (Configuration) service diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 8fa12bef3..40083607e 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -51,9 +51,10 @@ set(SRCS hle/service/dsp_dsp.cpp hle/service/gsp_gpu.cpp hle/service/hid/hid.cpp + hle/service/lm/lm.cpp hle/service/service.cpp + hle/service/sm/controller.cpp hle/service/sm/sm.cpp - hle/service/sm/srv.cpp hle/shared_page.cpp hle/svc.cpp hw/aes/arithmetic128.cpp @@ -139,9 +140,10 @@ set(HEADERS hle/service/dsp_dsp.h hle/service/gsp_gpu.h hle/service/hid/hid.h + hle/service/lm/lm.h hle/service/service.h + hle/service/sm/controller.h hle/service/sm/sm.h - hle/service/sm/srv.h hle/shared_page.h hle/svc.h hw/aes/arithmetic128.h diff --git a/src/core/hle/ipc.h b/src/core/hle/ipc.h index 87ed85df6..2f8a520ba 100644 --- a/src/core/hle/ipc.h +++ b/src/core/hle/ipc.h @@ -5,43 +5,10 @@ #pragma once #include "common/common_types.h" +#include "common/swap.h" #include "core/hle/kernel/errors.h" -#include "core/hle/kernel/thread.h" #include "core/memory.h" -namespace Kernel { - -/// Offset into command buffer of header -static const int kCommandHeaderOffset = 0x80; - -/** - * Returns a pointer to the command buffer in the current thread's TLS - * TODO(Subv): This is not entirely correct, the command buffer should be copied from - * the thread's TLS to an intermediate buffer in kernel memory, and then copied again to - * the service handler process' memory. - * @param offset Optional offset into command buffer - * @param offset Optional offset into command buffer (in bytes) - * @return Pointer to command buffer - */ -inline u32* GetCommandBuffer(const int offset = 0) { - return (u32*)Memory::GetPointer(GetCurrentThread()->GetTLSAddress() + kCommandHeaderOffset + - offset); -} - -/// Offset into static buffers, relative to command buffer header -static const int kStaticBuffersOffset = 0x100; - -/** - * Returns a pointer to the static buffers area in the current thread's TLS - * TODO(Subv): cf. GetCommandBuffer - * @param offset Optional offset into static buffers area (in bytes) - * @return Pointer to static buffers area - */ -inline u32* GetStaticBuffers(const int offset = 0) { - return GetCommandBuffer(kStaticBuffersOffset + offset); -} -} - namespace IPC { /// Size of the command buffer area, in 32-bit words. @@ -53,6 +20,130 @@ constexpr size_t COMMAND_BUFFER_LENGTH = 0x100 / sizeof(u32); using Kernel::ERR_INVALID_BUFFER_DESCRIPTOR; constexpr auto ERR_INVALID_HANDLE = Kernel::ERR_INVALID_HANDLE_OS; + +enum class ControlCommand : u32 { + ConvertSessionToDomain = 0, + ConvertDomainToSession = 1, + DuplicateSession = 2, + QueryPointerBufferSize = 3, + DuplicateSessionEx = 4, + Unspecified, +}; + +enum class CommandType : u32 { + Close = 2, + Request = 4, + Control = 5, + Unspecified, +}; + +struct CommandHeader { + union { + u32_le raw_low; + BitField<0, 16, CommandType> type; + BitField<16, 4, u32_le> num_buf_x_descriptors; + BitField<20, 4, u32_le> num_buf_a_descriptors; + BitField<24, 4, u32_le> num_buf_b_descriptors; + BitField<28, 4, u32_le> num_buf_w_descriptors; + }; + + enum class BufferDescriptorCFlag : u32 { + Disabled = 0, + NoDescriptor = 1, + TwoDesciptors = 2, + }; + + union { + u32_le raw_high; + BitField<0, 10, u32_le> data_size; + BitField<10, 4, BufferDescriptorCFlag> buf_c_descriptor_flags; + BitField<31, 1, u32_le> enable_handle_descriptor; + }; +}; +static_assert(sizeof(CommandHeader) == 8, "CommandHeader size is incorrect"); + +union HandleDescriptorHeader { + u32_le raw_high; + BitField<0, 1, u32_le> send_current_pid; + BitField<1, 4, u32_le> num_handles_to_copy; + BitField<5, 4, u32_le> num_handles_to_move; +}; +static_assert(sizeof(HandleDescriptorHeader) == 4, "HandleDescriptorHeader size is incorrect"); + +struct BufferDescriptorX { + union { + BitField<0, 6, u32_le> counter_bits_0_5; + BitField<6, 3, u32_le> address_bits_36_38; + BitField<9, 3, u32_le> counter_bits_9_11; + BitField<12, 4, u32_le> address_bits_32_35; + BitField<16, 16, u32_le> size; + }; + + u32_le address_bits_0_31; + + u32_le Counter() const { + u32_le counter{ counter_bits_0_5 }; + counter |= counter_bits_9_11 << 9; + return counter; + } + + VAddr Address() const { + VAddr address{ address_bits_0_31 }; + address |= static_cast(address_bits_32_35) << 32; + address |= static_cast(address_bits_36_38) << 36; + return address; + } +}; +static_assert(sizeof(BufferDescriptorX) == 8, "BufferDescriptorX size is incorrect"); + +struct BufferDescriptorABW { + u32_le size_bits_0_31; + u32_le address_bits_0_31; + + union { + BitField<0, 2, u32_le> flags; + BitField<2, 3, u32_le> address_bits_36_38; + BitField<24, 4, u32_le> size_bits_32_35; + BitField<28, 4, u32_le> address_bits_32_35; + }; + + VAddr Address() const { + VAddr address{ address_bits_0_31 }; + address |= static_cast(address_bits_32_35) << 32; + address |= static_cast(address_bits_36_38) << 36; + return address; + } + + u64 Size() const { + u64 size{ size_bits_0_31 }; + size |= static_cast(size_bits_32_35) << 32; + return size; + } +}; +static_assert(sizeof(BufferDescriptorABW) == 12, "BufferDescriptorABW size is incorrect"); + +struct BufferDescriptorC { + u32_le address_bits_0_31; + + union { + BitField<0, 16, u32_le> address_bits_32_47; + BitField<16, 16, u32_le> size; + }; + + VAddr Address() const { + VAddr address{ address_bits_0_31 }; + address |= static_cast(address_bits_32_47) << 32; + return address; + } +}; +static_assert(sizeof(BufferDescriptorC) == 8, "BufferDescriptorC size is incorrect"); + +struct DataPayloadHeader { + u32_le magic; + INSERT_PADDING_WORDS(1); +}; +static_assert(sizeof(DataPayloadHeader) == 8, "DataPayloadRequest size is incorrect"); + enum DescriptorType : u32 { // Buffer related desciptors types (mask : 0x0F) StaticBuffer = 0x02, @@ -65,36 +156,6 @@ enum DescriptorType : u32 { CallingPid = 0x20, }; -union Header { - u32 raw; - BitField<0, 6, u32> translate_params_size; - BitField<6, 6, u32> normal_params_size; - BitField<16, 16, u32> command_id; -}; - -/** - * @brief Creates a command header to be used for IPC - * @param command_id ID of the command to create a header for. - * @param normal_params_size Size of the normal parameters in words. Up to 63. - * @param translate_params_size Size of the translate parameters in words. Up to 63. - * @return The created IPC header. - * - * Normal parameters are sent directly to the process while the translate parameters might go - * through modifications and checks by the kernel. - * The translate parameters are described by headers generated with the IPC::*Desc functions. - * - * @note While @p normal_params_size is equivalent to the number of normal parameters, - * @p translate_params_size includes the size occupied by the translate parameters headers. - */ -inline u32 MakeHeader(u16 command_id, unsigned int normal_params_size, - unsigned int translate_params_size) { - Header header{}; - header.command_id.Assign(command_id); - header.normal_params_size.Assign(normal_params_size); - header.translate_params_size.Assign(translate_params_size); - return header.raw; -} - constexpr u32 MoveHandleDesc(u32 num_handles = 1) { return MoveHandle | ((num_handles - 1) << 26); } diff --git a/src/core/hle/ipc_helpers.h b/src/core/hle/ipc_helpers.h index 7cb95cbac..083d3a0d0 100644 --- a/src/core/hle/ipc_helpers.h +++ b/src/core/hle/ipc_helpers.h @@ -19,24 +19,17 @@ class RequestHelperBase { protected: Kernel::HLERequestContext* context = nullptr; u32* cmdbuf; - ptrdiff_t index = 1; - Header header; + ptrdiff_t index = 0; public: - RequestHelperBase(Kernel::HLERequestContext& context, Header desired_header) - : context(&context), cmdbuf(context.CommandBuffer()), header(desired_header) {} + RequestHelperBase(u32* command_buffer) : cmdbuf(command_buffer) {} - RequestHelperBase(u32* command_buffer, Header command_header) - : cmdbuf(command_buffer), header(command_header) {} - - /// Returns the total size of the request in words - size_t TotalSize() const { - return 1 /* command header */ + header.normal_params_size + header.translate_params_size; - } + RequestHelperBase(Kernel::HLERequestContext& context) + : context(&context), cmdbuf(context.CommandBuffer()) {} void ValidateHeader() { - DEBUG_ASSERT_MSG(index == TotalSize(), "Operations do not match the header (cmd 0x%x)", - header.raw); + // DEBUG_ASSERT_MSG(index == TotalSize(), "Operations do not match the header (cmd 0x%x)", + // header.raw); } void Skip(unsigned size_in_words, bool set_to_null) { @@ -46,46 +39,51 @@ public: } /** - * @brief Retrieves the address of a static buffer, used when a buffer is needed for output - * @param buffer_id The index of the static buffer - * @param data_size If non-null, will store the size of the buffer + * Aligns the current position forward to a 16-byte boundary, padding with zeros. Jumps forward + * by 16-bytes at a minimum. */ - VAddr PeekStaticBuffer(u8 buffer_id, size_t* data_size = nullptr) const { - u32* static_buffer = cmdbuf + Kernel::kStaticBuffersOffset / sizeof(u32) + buffer_id * 2; - if (data_size) - *data_size = StaticBufferDescInfo{static_buffer[0]}.size; - return static_buffer[1]; + void AlignWithPadding() { + Skip(4 - (index & 3), true); + } + + unsigned GetCurrentOffset() const { + return static_cast(index); } }; class RequestBuilder : public RequestHelperBase { public: - RequestBuilder(Kernel::HLERequestContext& context, Header command_header) - : RequestHelperBase(context, command_header) { - // From this point we will start overwriting the existing command buffer, so it's safe to - // release all previous incoming Object pointers since they won't be usable anymore. + RequestBuilder(u32* command_buffer) : RequestHelperBase(command_buffer) {} + + RequestBuilder(Kernel::HLERequestContext& context, unsigned normal_params_size, + u32 num_handles_to_copy = 0, u32 num_handles_to_move = 0) + : RequestHelperBase(context) { + memset(cmdbuf, 0, 64); + context.ClearIncomingObjects(); - cmdbuf[0] = header.raw; + + IPC::CommandHeader header{}; + header.data_size.Assign(normal_params_size * sizeof(u32)); + if (num_handles_to_copy || num_handles_to_move) { + header.enable_handle_descriptor.Assign(1); + } + PushRaw(header); + + if (header.enable_handle_descriptor) { + IPC::HandleDescriptorHeader handle_descriptor_header{}; + handle_descriptor_header.num_handles_to_copy.Assign(num_handles_to_copy); + handle_descriptor_header.num_handles_to_move.Assign(num_handles_to_move); + PushRaw(handle_descriptor_header); + Skip(num_handles_to_copy + num_handles_to_move, true); + } + + AlignWithPadding(); + + IPC::DataPayloadHeader data_payload_header{}; + data_payload_header.magic = 0x4f434653; + PushRaw(data_payload_header); } - RequestBuilder(Kernel::HLERequestContext& context, u16 command_id, unsigned normal_params_size, - unsigned translate_params_size) - : RequestBuilder( - context, Header{MakeHeader(command_id, normal_params_size, translate_params_size)}) {} - - RequestBuilder(u32* command_buffer, Header command_header) - : RequestHelperBase(command_buffer, command_header) { - cmdbuf[0] = header.raw; - } - - explicit RequestBuilder(u32* command_buffer, u32 command_header) - : RequestBuilder(command_buffer, Header{command_header}) {} - - RequestBuilder(u32* command_buffer, u16 command_id, unsigned normal_params_size, - unsigned translate_params_size) - : RequestBuilder(command_buffer, - MakeHeader(command_id, normal_params_size, translate_params_size)) {} - // Validate on destruction, as there shouldn't be any case where we don't want it ~RequestBuilder() { ValidateHeader(); @@ -131,7 +129,6 @@ inline void RequestBuilder::Push(u32 value) { template void RequestBuilder::PushRaw(const T& value) { - static_assert(std::is_trivially_copyable(), "Raw types should be trivially copyable"); std::memcpy(cmdbuf + index, &value, sizeof(T)); index += (sizeof(T) + 3) / 4; // round up to word length } @@ -203,36 +200,20 @@ inline void RequestBuilder::PushMappedBuffer(VAddr buffer_vaddr, size_t size, class RequestParser : public RequestHelperBase { public: - RequestParser(Kernel::HLERequestContext& context, Header desired_header) - : RequestHelperBase(context, desired_header) {} + RequestParser(u32* command_buffer) : RequestHelperBase(command_buffer) {} - RequestParser(Kernel::HLERequestContext& context, u16 command_id, unsigned normal_params_size, - unsigned translate_params_size) - : RequestParser(context, - Header{MakeHeader(command_id, normal_params_size, translate_params_size)}) { + RequestParser(Kernel::HLERequestContext& context) : RequestHelperBase(context) { + ASSERT_MSG(context.GetDataPayloadOffset(), "context is incomplete"); + Skip(context.GetDataPayloadOffset(), false); } - RequestParser(u32* command_buffer, Header command_header) - : RequestHelperBase(command_buffer, command_header) {} - - explicit RequestParser(u32* command_buffer, u32 command_header) - : RequestParser(command_buffer, Header{command_header}) {} - - RequestParser(u32* command_buffer, u16 command_id, unsigned normal_params_size, - unsigned translate_params_size) - : RequestParser(command_buffer, - MakeHeader(command_id, normal_params_size, translate_params_size)) {} - - RequestBuilder MakeBuilder(u32 normal_params_size, u32 translate_params_size, - bool validateHeader = true) { - if (validateHeader) + RequestBuilder MakeBuilder(u32 normal_params_size, u32 num_handles_to_copy, + u32 num_handles_to_move, bool validate_header = true) { + if (validate_header) { ValidateHeader(); - Header builderHeader{MakeHeader(static_cast(header.command_id), normal_params_size, - translate_params_size)}; - if (context != nullptr) - return {*context, builderHeader}; - else - return {cmdbuf, builderHeader}; + } + + return {*context, normal_params_size, num_handles_to_copy, num_handles_to_move}; } template @@ -342,7 +323,6 @@ inline u32 RequestParser::Pop() { template void RequestParser::PopRaw(T& value) { - static_assert(std::is_trivially_copyable(), "Raw types should be trivially copyable"); std::memcpy(&value, cmdbuf + index, sizeof(T)); index += (sizeof(T) + 3) / 4; // round up to word length } diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp index 6020e9764..b7070af03 100644 --- a/src/core/hle/kernel/hle_ipc.cpp +++ b/src/core/hle/kernel/hle_ipc.cpp @@ -5,6 +5,7 @@ #include #include "common/assert.h" #include "common/common_types.h" +#include "core/hle/ipc_helpers.h" #include "core/hle/kernel/handle_table.h" #include "core/hle/kernel/hle_ipc.h" #include "core/hle/kernel/kernel.h" @@ -44,88 +45,103 @@ void HLERequestContext::ClearIncomingObjects() { request_handles.clear(); } -ResultCode HLERequestContext::PopulateFromIncomingCommandBuffer(const u32_le* src_cmdbuf, - Process& src_process, - HandleTable& src_table) { - IPC::Header header{src_cmdbuf[0]}; +void HLERequestContext::ParseCommandBuffer(u32_le* src_cmdbuf) { + IPC::RequestParser rp(src_cmdbuf); + command_header = std::make_unique(rp.PopRaw()); - size_t untranslated_size = 1u + header.normal_params_size; - size_t command_size = untranslated_size + header.translate_params_size; - ASSERT(command_size <= IPC::COMMAND_BUFFER_LENGTH); // TODO(yuriks): Return error - - std::copy_n(src_cmdbuf, untranslated_size, cmd_buf.begin()); - - size_t i = untranslated_size; - while (i < command_size) { - u32 descriptor = cmd_buf[i] = src_cmdbuf[i]; - i += 1; - - switch (IPC::GetDescriptorType(descriptor)) { - case IPC::DescriptorType::CopyHandle: - case IPC::DescriptorType::MoveHandle: { - u32 num_handles = IPC::HandleNumberFromDesc(descriptor); - ASSERT(i + num_handles <= command_size); // TODO(yuriks): Return error - for (u32 j = 0; j < num_handles; ++j) { - Handle handle = src_cmdbuf[i]; - SharedPtr object = nullptr; - if (handle != 0) { - object = src_table.GetGeneric(handle); - ASSERT(object != nullptr); // TODO(yuriks): Return error - if (descriptor == IPC::DescriptorType::MoveHandle) { - src_table.Close(handle); - } - } - - cmd_buf[i++] = AddOutgoingHandle(std::move(object)); - } - break; - } - case IPC::DescriptorType::CallingPid: { - cmd_buf[i++] = src_process.process_id; - break; - } - default: - UNIMPLEMENTED_MSG("Unsupported handle translation: 0x%08X", descriptor); + // If handle descriptor is present, add size of it + if (command_header->enable_handle_descriptor) { + handle_descriptor_header = + std::make_unique(rp.PopRaw()); + if (handle_descriptor_header->send_current_pid) { + rp.Skip(2, false); } + rp.Skip(handle_descriptor_header->num_handles_to_copy, false); + rp.Skip(handle_descriptor_header->num_handles_to_move, false); } + // Padding to align to 16 bytes + rp.AlignWithPadding(); + + if (command_header->num_buf_x_descriptors) { + UNIMPLEMENTED(); + } + if (command_header->num_buf_a_descriptors) { + UNIMPLEMENTED(); + } + if (command_header->num_buf_b_descriptors) { + UNIMPLEMENTED(); + } + if (command_header->num_buf_w_descriptors) { + UNIMPLEMENTED(); + } + if (command_header->buf_c_descriptor_flags != + IPC::CommandHeader::BufferDescriptorCFlag::Disabled) { + UNIMPLEMENTED(); + } + + data_payload_header = + std::make_unique(rp.PopRaw()); + ASSERT(data_payload_header->magic == 0x49434653 || data_payload_header->magic == 0x4F434653); + + data_payload_offset = rp.GetCurrentOffset(); + command = rp.Pop(); +} + +ResultCode HLERequestContext::PopulateFromIncomingCommandBuffer(u32_le* src_cmdbuf, + Process& src_process, + HandleTable& src_table) { + ParseCommandBuffer(src_cmdbuf); + size_t untranslated_size = data_payload_offset + command_header->data_size; + std::copy_n(src_cmdbuf, untranslated_size, cmd_buf.begin()); + + if (command_header->enable_handle_descriptor) { + if (handle_descriptor_header->num_handles_to_copy || + handle_descriptor_header->num_handles_to_move) { + UNIMPLEMENTED(); + } + } return RESULT_SUCCESS; } ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(u32_le* dst_cmdbuf, Process& dst_process, - HandleTable& dst_table) const { - IPC::Header header{cmd_buf[0]}; - - size_t untranslated_size = 1u + header.normal_params_size; - size_t command_size = untranslated_size + header.translate_params_size; - ASSERT(command_size <= IPC::COMMAND_BUFFER_LENGTH); - + HandleTable& dst_table) { + ParseCommandBuffer(&cmd_buf[0]); + size_t untranslated_size = data_payload_offset + command_header->data_size; std::copy_n(cmd_buf.begin(), untranslated_size, dst_cmdbuf); - size_t i = untranslated_size; - while (i < command_size) { - u32 descriptor = dst_cmdbuf[i] = cmd_buf[i]; - i += 1; + if (command_header->enable_handle_descriptor) { + size_t command_size = untranslated_size + handle_descriptor_header->num_handles_to_copy + + handle_descriptor_header->num_handles_to_move; + ASSERT(command_size <= IPC::COMMAND_BUFFER_LENGTH); - switch (IPC::GetDescriptorType(descriptor)) { - case IPC::DescriptorType::CopyHandle: - case IPC::DescriptorType::MoveHandle: { - // HLE services don't use handles, so we treat both CopyHandle and MoveHandle equally - u32 num_handles = IPC::HandleNumberFromDesc(descriptor); - ASSERT(i + num_handles <= command_size); - for (u32 j = 0; j < num_handles; ++j) { - SharedPtr object = GetIncomingHandle(cmd_buf[i]); - Handle handle = 0; - if (object != nullptr) { - // TODO(yuriks): Figure out the proper error handling for if this fails - handle = dst_table.Create(object).Unwrap(); + size_t untranslated_index = untranslated_size; + size_t handle_write_offset = 3; + while (untranslated_index < command_size) { + u32 descriptor = cmd_buf[untranslated_index]; + untranslated_index += 1; + + switch (IPC::GetDescriptorType(descriptor)) { + case IPC::DescriptorType::CopyHandle: + case IPC::DescriptorType::MoveHandle: { + // HLE services don't use handles, so we treat both CopyHandle and MoveHandle + // equally + u32 num_handles = IPC::HandleNumberFromDesc(descriptor); + for (u32 j = 0; j < num_handles; ++j) { + SharedPtr object = GetIncomingHandle(cmd_buf[untranslated_index]); + Handle handle = 0; + if (object != nullptr) { + // TODO(yuriks): Figure out the proper error handling for if this fails + handle = dst_table.Create(object).Unwrap(); + } + dst_cmdbuf[handle_write_offset++] = handle; + untranslated_index++; } - dst_cmdbuf[i++] = handle; + break; + } + default: + UNIMPLEMENTED_MSG("Unsupported handle translation: 0x%08X", descriptor); } - break; - } - default: - UNIMPLEMENTED_MSG("Unsupported handle translation: 0x%08X", descriptor); } } diff --git a/src/core/hle/kernel/hle_ipc.h b/src/core/hle/kernel/hle_ipc.h index 35795fc1d..32a528968 100644 --- a/src/core/hle/kernel/hle_ipc.h +++ b/src/core/hle/kernel/hle_ipc.h @@ -119,18 +119,39 @@ public: */ void ClearIncomingObjects(); + void ParseCommandBuffer(u32_le* src_cmdbuf); + /// Populates this context with data from the requesting process/thread. - ResultCode PopulateFromIncomingCommandBuffer(const u32_le* src_cmdbuf, Process& src_process, + ResultCode PopulateFromIncomingCommandBuffer(u32_le* src_cmdbuf, Process& src_process, HandleTable& src_table); /// Writes data from this context back to the requesting process/thread. ResultCode WriteToOutgoingCommandBuffer(u32_le* dst_cmdbuf, Process& dst_process, - HandleTable& dst_table) const; + HandleTable& dst_table); + + u32_le GetCommand() const { + return command; + } + + IPC::CommandType GetCommandType() const { + return command_header->type; + } + + unsigned GetDataPayloadOffset() const { + return data_payload_offset; + } private: std::array cmd_buf; SharedPtr session; // TODO(yuriks): Check common usage of this and optimize size accordingly boost::container::small_vector, 8> request_handles; + + std::unique_ptr command_header; + std::unique_ptr handle_descriptor_header; + std::unique_ptr data_payload_header; + + unsigned data_payload_offset{}; + u32_le command{}; }; } // namespace Kernel diff --git a/src/core/hle/service/lm/lm.cpp b/src/core/hle/service/lm/lm.cpp new file mode 100644 index 000000000..7296b531b --- /dev/null +++ b/src/core/hle/service/lm/lm.cpp @@ -0,0 +1,43 @@ +// Copyright 2017 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "common/logging/log.h" +#include "core/hle/ipc_helpers.h" +#include "core/hle/service/lm/lm.h" + +namespace Service { +namespace LM { + +void InstallInterfaces(SM::ServiceManager& service_manager) { + std::make_shared()->InstallAsService(service_manager); +} + +/** + * SRV::Initialize service function + * Inputs: + * 0: 0x00000000 + * Outputs: + * 1: ResultCode + */ +void LM::Initialize(Kernel::HLERequestContext& ctx) { + IPC::RequestBuilder rb{ctx, 1}; + rb.Push(RESULT_SUCCESS); + LOG_WARNING(Service_SM, "(STUBBED) called"); +} + +LM::LM() : ServiceFramework("lm") { + static const FunctionInfo functions[] = { + {0x00000000, &LM::Initialize, "Initialize"}, + {0x00000001, nullptr, "Unknown2"}, + {0x00000002, nullptr, "Unknown3"}, + {0x00000003, nullptr, "Unknown4"}, + {0x00000004, nullptr, "Unknown5"}, + }; + RegisterHandlers(functions); +} + +LM::~LM() = default; + +} // namespace LM +} // namespace Service diff --git a/src/core/hle/service/lm/lm.h b/src/core/hle/service/lm/lm.h new file mode 100644 index 000000000..c497d82eb --- /dev/null +++ b/src/core/hle/service/lm/lm.h @@ -0,0 +1,25 @@ +// Copyright 2017 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/service.h" + +namespace Service { +namespace LM { + +class LM final : public ServiceFramework { +public: + explicit LM(); + ~LM(); + +private: + void Initialize(Kernel::HLERequestContext& ctx); +}; + +/// Registers all LM services with the specified service manager. +void InstallInterfaces(SM::ServiceManager& service_manager); + +} // namespace LM +} // namespace Service diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp index e1f22ca97..3141b71f5 100644 --- a/src/core/hle/service/service.cpp +++ b/src/core/hle/service/service.cpp @@ -8,17 +8,20 @@ #include "common/logging/log.h" #include "common/string_util.h" #include "core/hle/ipc.h" +#include "core/hle/ipc_helpers.h" #include "core/hle/kernel/client_port.h" #include "core/hle/kernel/process.h" #include "core/hle/kernel/server_port.h" #include "core/hle/kernel/server_session.h" +#include "core/hle/kernel/thread.h" #include "core/hle/kernel/handle_table.h" #include "core/hle/service/dsp_dsp.h" #include "core/hle/service/gsp_gpu.h" #include "core/hle/service/hid/hid.h" +#include "core/hle/service/lm/lm.h" #include "core/hle/service/service.h" +#include "core/hle/service/sm/controller.h" #include "core/hle/service/sm/sm.h" -#include "core/hle/service/sm/srv.h" using Kernel::ClientPort; using Kernel::ServerPort; @@ -46,42 +49,6 @@ static std::string MakeFunctionString(const char* name, const char* port_name, return function_string; } -Interface::Interface(u32 max_sessions) : max_sessions(max_sessions) {} -Interface::~Interface() = default; - -void Interface::HandleSyncRequest(SharedPtr server_session) { - // TODO(Subv): Make use of the server_session in the HLE service handlers to distinguish which - // session triggered each command. - - u32* cmd_buff = Kernel::GetCommandBuffer(); - auto itr = m_functions.find(cmd_buff[0]); - - if (itr == m_functions.end() || itr->second.func == nullptr) { - std::string function_name = (itr == m_functions.end()) - ? Common::StringFromFormat("0x%08X", cmd_buff[0]) - : itr->second.name; - LOG_ERROR( - Service, "unknown / unimplemented %s", - MakeFunctionString(function_name.c_str(), GetPortName().c_str(), cmd_buff).c_str()); - - // TODO(bunnei): Hack - ignore error - cmd_buff[1] = 0; - return; - } - LOG_TRACE(Service, "%s", - MakeFunctionString(itr->second.name, GetPortName().c_str(), cmd_buff).c_str()); - - itr->second.func(this); -} - -void Interface::Register(const FunctionInfo* functions, size_t n) { - m_functions.reserve(n); - for (size_t i = 0; i < n; ++i) { - // Usually this array is sorted by id already, so hint to instead at the end - m_functions.emplace_hint(m_functions.cend(), functions[i].id, functions[i]); - } -} - //////////////////////////////////////////////////////////////////////////////////////////////////// ServiceFrameworkBase::ServiceFrameworkBase(const char* service_name, u32 max_sessions, @@ -113,33 +80,38 @@ void ServiceFrameworkBase::RegisterHandlersBase(const FunctionInfoBase* function } } -void ServiceFrameworkBase::ReportUnimplementedFunction(u32* cmd_buf, const FunctionInfoBase* info) { - IPC::Header header{cmd_buf[0]}; - int num_params = header.normal_params_size + header.translate_params_size; +void ServiceFrameworkBase::ReportUnimplementedFunction(Kernel::HLERequestContext& ctx, const FunctionInfoBase* info) { + auto cmd_buf = ctx.CommandBuffer(); std::string function_name = info == nullptr ? fmt::format("{:#08x}", cmd_buf[0]) : info->name; fmt::MemoryWriter w; w.write("function '{}': port='{}' cmd_buf={{[0]={:#x}", function_name, service_name, cmd_buf[0]); - for (int i = 1; i <= num_params; ++i) { + for (int i = 1; i <= 8; ++i) { w.write(", [{}]={:#x}", i, cmd_buf[i]); } w << '}'; LOG_ERROR(Service, "unknown / unimplemented %s", w.c_str()); // TODO(bunnei): Hack - ignore error - cmd_buf[1] = 0; + IPC::RequestBuilder rb{ ctx, 1 }; + rb.Push(RESULT_SUCCESS); +} + +void ServiceFrameworkBase::InvokeRequest(Kernel::HLERequestContext& ctx) { + auto itr = handlers.find(ctx.GetCommand()); + const FunctionInfoBase* info = itr == handlers.end() ? nullptr : &itr->second; + if (info == nullptr || info->handler_callback == nullptr) { + return ReportUnimplementedFunction(ctx, info); + } + + LOG_TRACE(Service, "%s", + MakeFunctionString(info->name, GetServiceName().c_str(), ctx.CommandBuffer()).c_str()); + handler_invoker(this, info->handler_callback, ctx); } void ServiceFrameworkBase::HandleSyncRequest(SharedPtr server_session) { - u32* cmd_buf = Kernel::GetCommandBuffer(); - - u32 header_code = cmd_buf[0]; - auto itr = handlers.find(header_code); - const FunctionInfoBase* info = itr == handlers.end() ? nullptr : &itr->second; - if (info == nullptr || info->handler_callback == nullptr) { - return ReportUnimplementedFunction(cmd_buf, info); - } + u32* cmd_buf = (u32*)Memory::GetPointer(Kernel::GetCurrentThread()->GetTLSAddress());; // TODO(yuriks): The kernel should be the one handling this as part of translation after // everything else is migrated @@ -147,9 +119,27 @@ void ServiceFrameworkBase::HandleSyncRequest(SharedPtr server_ses context.PopulateFromIncomingCommandBuffer(cmd_buf, *Kernel::g_current_process, Kernel::g_handle_table); - LOG_TRACE(Service, "%s", - MakeFunctionString(info->name, GetServiceName().c_str(), cmd_buf).c_str()); - handler_invoker(this, info->handler_callback, context); + switch (context.GetCommandType()) { + case IPC::CommandType::Close: + { + IPC::RequestBuilder rb{context, 1}; + rb.Push(RESULT_SUCCESS); + break; + } + case IPC::CommandType::Control: + { + SM::g_service_manager->InvokeControlRequest(context); + break; + } + case IPC::CommandType::Request: + { + InvokeRequest(context); + break; + } + default: + UNIMPLEMENTED_MSG("command_type=%d", context.GetCommandType()); + } + context.WriteToOutgoingCommandBuffer(cmd_buf, *Kernel::g_current_process, Kernel::g_handle_table); } @@ -162,33 +152,14 @@ void AddNamedPort(std::string name, SharedPtr port) { g_kernel_named_ports.emplace(std::move(name), std::move(port)); } -static void AddNamedPort(Interface* interface_) { - SharedPtr server_port; - SharedPtr client_port; - std::tie(server_port, client_port) = - ServerPort::CreatePortPair(interface_->GetMaxSessions(), interface_->GetPortName()); - - server_port->SetHleHandler(std::shared_ptr(interface_)); - AddNamedPort(interface_->GetPortName(), std::move(client_port)); -} - -void AddService(Interface* interface_) { - auto server_port = - SM::g_service_manager - ->RegisterService(interface_->GetPortName(), interface_->GetMaxSessions()) - .Unwrap(); - server_port->SetHleHandler(std::shared_ptr(interface_)); -} - /// Initialize ServiceManager void Init() { SM::g_service_manager = std::make_shared(); SM::ServiceManager::InstallInterfaces(SM::g_service_manager); - HID::Init(); + LM::InstallInterfaces(*SM::g_service_manager); - AddService(new DSP_DSP::Interface); - AddService(new GSP::GSP_GPU); + HID::Init(); LOG_DEBUG(Service, "initialized OK"); } diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h index 281ff99bb..07bc8589a 100644 --- a/src/core/hle/service/service.h +++ b/src/core/hle/service/service.h @@ -20,6 +20,7 @@ namespace Kernel { class ClientPort; class ServerPort; class ServerSession; +class HLERequestContext; } namespace Service { @@ -32,83 +33,6 @@ static const int kMaxPortSize = 8; ///< Maximum size of a port name (8 character /// Arbitrary default number of maximum connections to an HLE service. static const u32 DefaultMaxSessions = 10; -/** - * Framework for implementing HLE service handlers which dispatch incoming SyncRequests based on a - * table mapping header ids to handler functions. - * - * @deprecated Use ServiceFramework for new services instead. It allows services to be stateful and - * is more extensible going forward. - */ -class Interface : public Kernel::SessionRequestHandler { -public: - /** - * Creates an HLE interface with the specified max sessions. - * @param max_sessions Maximum number of sessions that can be - * connected to this service at the same time. - */ - Interface(u32 max_sessions = DefaultMaxSessions); - - virtual ~Interface(); - - std::string GetName() const { - return GetPortName(); - } - - virtual void SetVersion(u32 raw_version) { - version.raw = raw_version; - } - - /** - * Gets the maximum allowed number of sessions that can be connected to this service - * at the same time. - * @returns The maximum number of connections allowed. - */ - u32 GetMaxSessions() const { - return max_sessions; - } - - typedef void (*Function)(Interface*); - - struct FunctionInfo { - u32 id; - Function func; - const char* name; - }; - - /** - * Gets the string name used by CTROS for a service - * @return Port name of service - */ - virtual std::string GetPortName() const { - return "[UNKNOWN SERVICE PORT]"; - } - -protected: - void HandleSyncRequest(Kernel::SharedPtr server_session) override; - - /** - * Registers the functions in the service - */ - template - inline void Register(const FunctionInfo (&functions)[N]) { - Register(functions, N); - } - - void Register(const FunctionInfo* functions, size_t n); - - union { - u32 raw; - BitField<0, 8, u32> major; - BitField<8, 8, u32> minor; - BitField<16, 8, u32> build; - BitField<24, 8, u32> revision; - } version = {}; - -private: - u32 max_sessions; ///< Maximum number of concurrent sessions that this service can handle. - boost::container::flat_map m_functions; -}; - /** * This is an non-templated base of ServiceFramework to reduce code bloat and compilation times, it * is not meant to be used directly. @@ -135,6 +59,8 @@ public: /// Creates a port pair and registers it on the kernel's global port registry. void InstallAsNamedPort(); + void InvokeRequest(Kernel::HLERequestContext& ctx); + void HandleSyncRequest(Kernel::SharedPtr server_session) override; protected: @@ -159,7 +85,7 @@ private: ~ServiceFrameworkBase(); void RegisterHandlersBase(const FunctionInfoBase* functions, size_t n); - void ReportUnimplementedFunction(u32* cmd_buf, const FunctionInfoBase* info); + void ReportUnimplementedFunction(Kernel::HLERequestContext& ctx, const FunctionInfoBase* info); /// Identifier string used to connect to the service. std::string service_name; @@ -260,7 +186,5 @@ extern std::unordered_map> g_ /// Adds a port to the named port table void AddNamedPort(std::string name, Kernel::SharedPtr port); -/// Adds a service to the services table -void AddService(Interface* interface_); } // namespace diff --git a/src/core/hle/service/sm/controller.cpp b/src/core/hle/service/sm/controller.cpp new file mode 100644 index 000000000..4b250d6ba --- /dev/null +++ b/src/core/hle/service/sm/controller.cpp @@ -0,0 +1,42 @@ +// Copyright 2017 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "common/logging/log.h" +#include "core/hle/ipc_helpers.h" +#include "core/hle/service/sm/controller.h" + +namespace Service { +namespace SM { + +/** + * Controller::QueryPointerBufferSize service function + * Inputs: + * 0: 0x00000003 + * Outputs: + * 1: ResultCode + * 3: Size of memory + */ +void Controller::QueryPointerBufferSize(Kernel::HLERequestContext& ctx) { + IPC::RequestBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + rb.Push(0x0U); + rb.Push(0x500U); + LOG_WARNING(Service, "(STUBBED) called"); +} + +Controller::Controller() : ServiceFramework("IpcController") { + static const FunctionInfo functions[] = { + {0x00000000, nullptr, "ConvertSessionToDomain"}, + {0x00000001, nullptr, "ConvertDomainToSession"}, + {0x00000002, nullptr, "DuplicateSession"}, + {0x00000003, &Controller::QueryPointerBufferSize, "QueryPointerBufferSize"}, + {0x00000004, nullptr, "DuplicateSessionEx"}, + }; + RegisterHandlers(functions); +} + +Controller::~Controller() = default; + +} // namespace SM +} // namespace Service diff --git a/src/core/hle/service/sm/controller.h b/src/core/hle/service/sm/controller.h new file mode 100644 index 000000000..c6aa6f502 --- /dev/null +++ b/src/core/hle/service/sm/controller.h @@ -0,0 +1,22 @@ +// Copyright 2017 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/service.h" + +namespace Service { +namespace SM { + +class Controller final : public ServiceFramework { +public: + explicit Controller(); + ~Controller(); + +private: + void QueryPointerBufferSize(Kernel::HLERequestContext& ctx); +}; + +} // namespace SM +} // namespace Service diff --git a/src/core/hle/service/sm/sm.cpp b/src/core/hle/service/sm/sm.cpp index 854ab9a05..2068471f2 100644 --- a/src/core/hle/service/sm/sm.cpp +++ b/src/core/hle/service/sm/sm.cpp @@ -4,16 +4,21 @@ #include #include "common/assert.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/server_port.h" #include "core/hle/result.h" +#include "core/hle/service/sm/controller.h" #include "core/hle/service/sm/sm.h" -#include "core/hle/service/sm/srv.h" namespace Service { namespace SM { +void ServiceManager::InvokeControlRequest(Kernel::HLERequestContext& context) { + controller_interface->InvokeRequest(context); +} + static ResultCode ValidateServiceName(const std::string& name) { if (name.size() <= 0 || name.size() > 8) { return ERR_INVALID_NAME_SIZE; @@ -25,11 +30,12 @@ static ResultCode ValidateServiceName(const std::string& name) { } void ServiceManager::InstallInterfaces(std::shared_ptr self) { - ASSERT(self->srv_interface.expired()); + ASSERT(self->sm_interface.expired()); - auto srv = std::make_shared(self); - srv->InstallAsNamedPort(); - self->srv_interface = srv; + auto sm = std::make_shared(self); + sm->InstallAsNamedPort(); + self->sm_interface = sm; + self->controller_interface = std::make_unique(); } ResultVal> ServiceManager::RegisterService( @@ -69,5 +75,80 @@ ResultVal> ServiceManager::ConnectToSer std::shared_ptr g_service_manager; +/** + * SM::Initialize service function + * Inputs: + * 0: 0x00000000 + * Outputs: + * 1: ResultCode + */ +void SM::Initialize(Kernel::HLERequestContext& ctx) { + IPC::RequestBuilder rb{ctx, 1}; + rb.Push(RESULT_SUCCESS); + LOG_DEBUG(Service_SM, "called"); +} + +/** + * SM::GetServiceHandle service function + * Inputs: + * 0: 0x00000001 + * 1: Unknown + * 2: Unknown + * 3-4: 8-byte UTF-8 service name + * Outputs: + * 1: ResultCode + * 3: Service handle + */ +void SM::GetService(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + u32 unk1 = rp.Pop(); + u32 unk2 = rp.Pop(); + auto name_buf = rp.PopRaw>(); + std::string name(name_buf.data()); + + // TODO(yuriks): Permission checks go here + + auto client_port = service_manager->GetServicePort(name); + if (client_port.Failed()) { + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0, 0); + rb.Push(client_port.Code()); + LOG_ERROR(Service_SM, "called service=%s -> error 0x%08X", name.c_str(), + client_port.Code().raw); + return; + } + + auto session = client_port.Unwrap()->Connect(); + if (session.Succeeded()) { + LOG_DEBUG(Service_SM, "called service=%s -> session=%u", name.c_str(), + (*session)->GetObjectId()); + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0, 1); + rb.Push(session.Code()); + rb.PushObjects(std::move(session).Unwrap()); + } else if (session.Code() == Kernel::ERR_MAX_CONNECTIONS_REACHED /*&& return_port_on_failure*/) { + LOG_WARNING(Service_SM, "called service=%s -> ERR_MAX_CONNECTIONS_REACHED, *port*=%u", + name.c_str(), (*client_port)->GetObjectId()); + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0, 1); + rb.Push(ERR_MAX_CONNECTIONS_REACHED); + rb.PushObjects(std::move(client_port).Unwrap()); + } else { + LOG_ERROR(Service_SM, "called service=%s -> error 0x%08X", name.c_str(), session.Code()); + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0, 0); + rb.Push(session.Code()); + } +} + +SM::SM(std::shared_ptr service_manager) + : ServiceFramework("sm:", 4), service_manager(std::move(service_manager)) { + static const FunctionInfo functions[] = { + {0x00000000, &SM::Initialize, "Initialize"}, + {0x00000001, &SM::GetService, "GetService"}, + {0x00000002, nullptr, "RegisterService"}, + {0x00000003, nullptr, "UnregisterService"}, + }; + RegisterHandlers(functions); +} + +SM::~SM() = default; + } // namespace SM } // namespace Service diff --git a/src/core/hle/service/sm/sm.h b/src/core/hle/service/sm/sm.h index 9f60a7965..eaae68ca1 100644 --- a/src/core/hle/service/sm/sm.h +++ b/src/core/hle/service/sm/sm.h @@ -20,7 +20,20 @@ class SessionRequestHandler; namespace Service { namespace SM { -class SRV; +/// Interface to "sm:" service +class SM final : public ServiceFramework { +public: + explicit SM(std::shared_ptr service_manager); + ~SM(); + +private: + void Initialize(Kernel::HLERequestContext& ctx); + void GetService(Kernel::HLERequestContext& ctx); + + std::shared_ptr service_manager; +}; + +class Controller; constexpr ResultCode ERR_SERVICE_NOT_REGISTERED(1, ErrorModule::SRV, ErrorSummary::WouldBlock, ErrorLevel::Temporary); // 0xD0406401 @@ -45,8 +58,11 @@ public: ResultVal> GetServicePort(const std::string& name); ResultVal> ConnectToService(const std::string& name); + void InvokeControlRequest(Kernel::HLERequestContext& context); + private: - std::weak_ptr srv_interface; + std::weak_ptr sm_interface; + std::unique_ptr controller_interface; /// Map of registered services, retrieved using GetServicePort or ConnectToService. std::unordered_map> registered_services; diff --git a/src/core/hle/service/sm/srv.cpp b/src/core/hle/service/sm/srv.cpp deleted file mode 100644 index fb873981c..000000000 --- a/src/core/hle/service/sm/srv.cpp +++ /dev/null @@ -1,235 +0,0 @@ -// Copyright 2016 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include - -#include "common/common_types.h" -#include "common/logging/log.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/errors.h" -#include "core/hle/kernel/hle_ipc.h" -#include "core/hle/kernel/semaphore.h" -#include "core/hle/kernel/server_port.h" -#include "core/hle/kernel/server_session.h" -#include "core/hle/service/sm/sm.h" -#include "core/hle/service/sm/srv.h" - -namespace Service { -namespace SM { - -constexpr int MAX_PENDING_NOTIFICATIONS = 16; - -/** - * SRV::RegisterClient service function - * Inputs: - * 0: 0x00010002 - * 1: ProcessId Header (must be 0x20) - * Outputs: - * 0: 0x00010040 - * 1: ResultCode - */ -void SRV::RegisterClient(Kernel::HLERequestContext& ctx) { - IPC::RequestParser rp(ctx, 0x1, 0, 2); - - u32 pid_descriptor = rp.Pop(); - if (pid_descriptor != IPC::CallingPidDesc()) { - IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); - rb.Push(IPC::ERR_INVALID_BUFFER_DESCRIPTOR); - return; - } - u32 caller_pid = rp.Pop(); - - IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); - rb.Push(RESULT_SUCCESS); - LOG_WARNING(Service_SRV, "(STUBBED) called"); -} - -/** - * SRV::EnableNotification service function - * Inputs: - * 0: 0x00020000 - * Outputs: - * 0: 0x00020042 - * 1: ResultCode - * 2: Translation descriptor: 0x20 - * 3: Handle to semaphore signaled on process notification - */ -void SRV::EnableNotification(Kernel::HLERequestContext& ctx) { - IPC::RequestParser rp(ctx, 0x2, 0, 0); - - notification_semaphore = - Kernel::Semaphore::Create(0, MAX_PENDING_NOTIFICATIONS, 0, "SRV:Notification").Unwrap(); - - IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); - rb.Push(RESULT_SUCCESS); - rb.PushObjects(notification_semaphore); - LOG_WARNING(Service_SRV, "(STUBBED) called"); -} - -/** - * SRV::GetServiceHandle service function - * Inputs: - * 0: 0x00050100 - * 1-2: 8-byte UTF-8 service name - * 3: Name length - * 4: Flags (bit0: if not set, return port-handle if session-handle unavailable) - * Outputs: - * 1: ResultCode - * 3: Service handle - */ -void SRV::GetServiceHandle(Kernel::HLERequestContext& ctx) { - IPC::RequestParser rp(ctx, 0x5, 4, 0); - auto name_buf = rp.PopRaw>(); - size_t name_len = rp.Pop(); - u32 flags = rp.Pop(); - - bool return_port_on_failure = (flags & 1) == 0; - - if (name_len > Service::kMaxPortSize) { - IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); - rb.Push(ERR_INVALID_NAME_SIZE); - LOG_ERROR(Service_SRV, "called name_len=0x%X -> ERR_INVALID_NAME_SIZE", name_len); - return; - } - std::string name(name_buf.data(), name_len); - - // TODO(yuriks): Permission checks go here - - auto client_port = service_manager->GetServicePort(name); - if (client_port.Failed()) { - IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); - rb.Push(client_port.Code()); - LOG_ERROR(Service_SRV, "called service=%s -> error 0x%08X", name.c_str(), - client_port.Code().raw); - return; - } - - auto session = client_port.Unwrap()->Connect(); - if (session.Succeeded()) { - LOG_DEBUG(Service_SRV, "called service=%s -> session=%u", name.c_str(), - (*session)->GetObjectId()); - IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); - rb.Push(session.Code()); - rb.PushObjects(std::move(session).Unwrap()); - } else if (session.Code() == Kernel::ERR_MAX_CONNECTIONS_REACHED && return_port_on_failure) { - LOG_WARNING(Service_SRV, "called service=%s -> ERR_MAX_CONNECTIONS_REACHED, *port*=%u", - name.c_str(), (*client_port)->GetObjectId()); - IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); - rb.Push(ERR_MAX_CONNECTIONS_REACHED); - rb.PushObjects(std::move(client_port).Unwrap()); - } else { - LOG_ERROR(Service_SRV, "called service=%s -> error 0x%08X", name.c_str(), session.Code()); - IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); - rb.Push(session.Code()); - } -} - -/** - * SRV::Subscribe service function - * Inputs: - * 0: 0x00090040 - * 1: Notification ID - * Outputs: - * 0: 0x00090040 - * 1: ResultCode - */ -void SRV::Subscribe(Kernel::HLERequestContext& ctx) { - IPC::RequestParser rp(ctx, 0x9, 1, 0); - u32 notification_id = rp.Pop(); - - IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); - rb.Push(RESULT_SUCCESS); - LOG_WARNING(Service_SRV, "(STUBBED) called, notification_id=0x%X", notification_id); -} - -/** - * SRV::Unsubscribe service function - * Inputs: - * 0: 0x000A0040 - * 1: Notification ID - * Outputs: - * 0: 0x000A0040 - * 1: ResultCode - */ -void SRV::Unsubscribe(Kernel::HLERequestContext& ctx) { - IPC::RequestParser rp(ctx, 0xA, 1, 0); - u32 notification_id = rp.Pop(); - - IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); - rb.Push(RESULT_SUCCESS); - LOG_WARNING(Service_SRV, "(STUBBED) called, notification_id=0x%X", notification_id); -} - -/** - * SRV::PublishToSubscriber service function - * Inputs: - * 0: 0x000C0080 - * 1: Notification ID - * 2: Flags (bit0: only fire if not fired, bit1: report errors) - * Outputs: - * 0: 0x000C0040 - * 1: ResultCode - */ -void SRV::PublishToSubscriber(Kernel::HLERequestContext& ctx) { - IPC::RequestParser rp(ctx, 0xC, 2, 0); - u32 notification_id = rp.Pop(); - u8 flags = rp.Pop(); - - IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); - rb.Push(RESULT_SUCCESS); - LOG_WARNING(Service_SRV, "(STUBBED) called, notification_id=0x%X, flags=%u", notification_id, - flags); -} - -void SRV::RegisterService(Kernel::HLERequestContext& ctx) { - IPC::RequestParser rp(ctx, 0x3, 4, 0); - - auto name_buf = rp.PopRaw>(); - size_t name_len = rp.Pop(); - u32 max_sessions = rp.Pop(); - - std::string name(name_buf.data(), std::min(name_len, name_buf.size())); - - auto port = service_manager->RegisterService(name, max_sessions); - - if (port.Failed()) { - IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); - rb.Push(port.Code()); - LOG_ERROR(Service_SRV, "called service=%s -> error 0x%08X", name.c_str(), port.Code().raw); - return; - } - - IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); - rb.Push(RESULT_SUCCESS); - rb.PushObjects(port.Unwrap()); -} - -SRV::SRV(std::shared_ptr service_manager) - : ServiceFramework("srv:", 4), service_manager(std::move(service_manager)) { - static const FunctionInfo functions[] = { - {0x00010002, &SRV::RegisterClient, "RegisterClient"}, - {0x00020000, &SRV::EnableNotification, "EnableNotification"}, - {0x00030100, &SRV::RegisterService, "RegisterService"}, - {0x000400C0, nullptr, "UnregisterService"}, - {0x00050100, &SRV::GetServiceHandle, "GetServiceHandle"}, - {0x000600C2, nullptr, "RegisterPort"}, - {0x000700C0, nullptr, "UnregisterPort"}, - {0x00080100, nullptr, "GetPort"}, - {0x00090040, &SRV::Subscribe, "Subscribe"}, - {0x000A0040, &SRV::Unsubscribe, "Unsubscribe"}, - {0x000B0000, nullptr, "ReceiveNotification"}, - {0x000C0080, &SRV::PublishToSubscriber, "PublishToSubscriber"}, - {0x000D0040, nullptr, "PublishAndGetSubscriber"}, - {0x000E00C0, nullptr, "IsServiceRegistered"}, - }; - RegisterHandlers(functions); -} - -SRV::~SRV() = default; - -} // namespace SM -} // namespace Service diff --git a/src/core/hle/service/sm/srv.h b/src/core/hle/service/sm/srv.h deleted file mode 100644 index aad839563..000000000 --- a/src/core/hle/service/sm/srv.h +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright 2014 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#pragma once - -#include "core/hle/kernel/kernel.h" -#include "core/hle/service/service.h" - -namespace Kernel { -class HLERequestContext; -class Semaphore; -} - -namespace Service { -namespace SM { - -/// Interface to "srv:" service -class SRV final : public ServiceFramework { -public: - explicit SRV(std::shared_ptr service_manager); - ~SRV(); - -private: - void RegisterClient(Kernel::HLERequestContext& ctx); - void EnableNotification(Kernel::HLERequestContext& ctx); - void GetServiceHandle(Kernel::HLERequestContext& ctx); - void Subscribe(Kernel::HLERequestContext& ctx); - void Unsubscribe(Kernel::HLERequestContext& ctx); - void PublishToSubscriber(Kernel::HLERequestContext& ctx); - void RegisterService(Kernel::HLERequestContext& ctx); - - std::shared_ptr service_manager; - Kernel::SharedPtr notification_semaphore; -}; - -} // namespace SM -} // namespace Service diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp index d72b32d2b..9da6c0adf 100644 --- a/src/core/hle/svc.cpp +++ b/src/core/hle/svc.cpp @@ -10,6 +10,7 @@ #include "core/hle/kernel/client_session.h" #include "core/hle/kernel/handle_table.h" #include "core/hle/kernel/process.h" +#include "core/hle/kernel/thread.h" #include "core/hle/lock.h" #include "core/hle/result.h" #include "core/hle/service/service.h" diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt index 1aac0daa2..9e048db3d 100644 --- a/src/tests/CMakeLists.txt +++ b/src/tests/CMakeLists.txt @@ -3,7 +3,6 @@ set(SRCS core/arm/arm_test_common.cpp core/arm/dyncom/arm_dyncom_vfp_tests.cpp core/file_sys/path_parser.cpp - core/hle/kernel/hle_ipc.cpp core/memory/memory.cpp glad.cpp tests.cpp diff --git a/src/tests/core/hle/kernel/hle_ipc.cpp b/src/tests/core/hle/kernel/hle_ipc.cpp deleted file mode 100644 index 4143a3ab8..000000000 --- a/src/tests/core/hle/kernel/hle_ipc.cpp +++ /dev/null @@ -1,216 +0,0 @@ -// Copyright 2017 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include -#include "core/hle/ipc.h" -#include "core/hle/kernel/client_port.h" -#include "core/hle/kernel/client_session.h" -#include "core/hle/kernel/event.h" -#include "core/hle/kernel/handle_table.h" -#include "core/hle/kernel/hle_ipc.h" -#include "core/hle/kernel/process.h" -#include "core/hle/kernel/server_session.h" - -namespace Kernel { - -static SharedPtr MakeObject() { - return Event::Create(ResetType::OneShot); -} - -TEST_CASE("HLERequestContext::PopulateFromIncomingCommandBuffer", "[core][kernel]") { - auto session = std::get>(ServerSession::CreateSessionPair()); - HLERequestContext context(std::move(session)); - - auto process = Process::Create(""); - HandleTable handle_table; - - SECTION("works with empty cmdbuf") { - const u32_le input[]{ - IPC::MakeHeader(0x1234, 0, 0), - }; - - context.PopulateFromIncomingCommandBuffer(input, *process, handle_table); - - REQUIRE(context.CommandBuffer()[0] == 0x12340000); - } - - SECTION("translates regular params") { - const u32_le input[]{ - IPC::MakeHeader(0, 3, 0), 0x12345678, 0x21122112, 0xAABBCCDD, - }; - - context.PopulateFromIncomingCommandBuffer(input, *process, handle_table); - - auto* output = context.CommandBuffer(); - REQUIRE(output[1] == 0x12345678); - REQUIRE(output[2] == 0x21122112); - REQUIRE(output[3] == 0xAABBCCDD); - } - - SECTION("translates move handles") { - auto a = MakeObject(); - Handle a_handle = handle_table.Create(a).Unwrap(); - const u32_le input[]{ - IPC::MakeHeader(0, 0, 2), IPC::MoveHandleDesc(1), a_handle, - }; - - context.PopulateFromIncomingCommandBuffer(input, *process, handle_table); - - auto* output = context.CommandBuffer(); - REQUIRE(context.GetIncomingHandle(output[2]) == a); - REQUIRE(handle_table.GetGeneric(a_handle) == nullptr); - } - - SECTION("translates copy handles") { - auto a = MakeObject(); - Handle a_handle = handle_table.Create(a).Unwrap(); - const u32_le input[]{ - IPC::MakeHeader(0, 0, 2), IPC::CopyHandleDesc(1), a_handle, - }; - - context.PopulateFromIncomingCommandBuffer(input, *process, handle_table); - - auto* output = context.CommandBuffer(); - REQUIRE(context.GetIncomingHandle(output[2]) == a); - REQUIRE(handle_table.GetGeneric(a_handle) == a); - } - - SECTION("translates multi-handle descriptors") { - auto a = MakeObject(); - auto b = MakeObject(); - auto c = MakeObject(); - const u32_le input[]{ - IPC::MakeHeader(0, 0, 5), IPC::MoveHandleDesc(2), - handle_table.Create(a).Unwrap(), handle_table.Create(b).Unwrap(), - IPC::MoveHandleDesc(1), handle_table.Create(c).Unwrap(), - }; - - context.PopulateFromIncomingCommandBuffer(input, *process, handle_table); - - auto* output = context.CommandBuffer(); - REQUIRE(context.GetIncomingHandle(output[2]) == a); - REQUIRE(context.GetIncomingHandle(output[3]) == b); - REQUIRE(context.GetIncomingHandle(output[5]) == c); - } - - SECTION("translates null handles") { - const u32_le input[]{ - IPC::MakeHeader(0, 0, 2), IPC::MoveHandleDesc(1), 0, - }; - - auto result = context.PopulateFromIncomingCommandBuffer(input, *process, handle_table); - - REQUIRE(result == RESULT_SUCCESS); - auto* output = context.CommandBuffer(); - REQUIRE(context.GetIncomingHandle(output[2]) == nullptr); - } - - SECTION("translates CallingPid descriptors") { - const u32_le input[]{ - IPC::MakeHeader(0, 0, 2), IPC::CallingPidDesc(), 0x98989898, - }; - - context.PopulateFromIncomingCommandBuffer(input, *process, handle_table); - - REQUIRE(context.CommandBuffer()[2] == process->process_id); - } - - SECTION("translates mixed params") { - auto a = MakeObject(); - const u32_le input[]{ - IPC::MakeHeader(0, 2, 4), - 0x12345678, - 0xABCDEF00, - IPC::MoveHandleDesc(1), - handle_table.Create(a).Unwrap(), - IPC::CallingPidDesc(), - 0, - }; - - 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); - } -} - -TEST_CASE("HLERequestContext::WriteToOutgoingCommandBuffer", "[core][kernel]") { - auto session = std::get>(ServerSession::CreateSessionPair()); - HLERequestContext context(std::move(session)); - - auto process = Process::Create(""); - HandleTable handle_table; - auto* input = context.CommandBuffer(); - u32_le output[IPC::COMMAND_BUFFER_LENGTH]; - - SECTION("works with empty cmdbuf") { - input[0] = IPC::MakeHeader(0x1234, 0, 0); - - context.WriteToOutgoingCommandBuffer(output, *process, handle_table); - - REQUIRE(output[0] == 0x12340000); - } - - SECTION("translates regular params") { - input[0] = IPC::MakeHeader(0, 3, 0); - input[1] = 0x12345678; - input[2] = 0x21122112; - input[3] = 0xAABBCCDD; - - context.WriteToOutgoingCommandBuffer(output, *process, handle_table); - - REQUIRE(output[1] == 0x12345678); - REQUIRE(output[2] == 0x21122112); - REQUIRE(output[3] == 0xAABBCCDD); - } - - SECTION("translates move/copy handles") { - auto a = MakeObject(); - auto b = MakeObject(); - input[0] = IPC::MakeHeader(0, 0, 4); - input[1] = IPC::MoveHandleDesc(1); - input[2] = context.AddOutgoingHandle(a); - input[3] = IPC::CopyHandleDesc(1); - input[4] = context.AddOutgoingHandle(b); - - context.WriteToOutgoingCommandBuffer(output, *process, handle_table); - - REQUIRE(handle_table.GetGeneric(output[2]) == a); - REQUIRE(handle_table.GetGeneric(output[4]) == b); - } - - SECTION("translates null handles") { - input[0] = IPC::MakeHeader(0, 0, 2); - input[1] = IPC::MoveHandleDesc(1); - input[2] = context.AddOutgoingHandle(nullptr); - - auto result = context.WriteToOutgoingCommandBuffer(output, *process, handle_table); - - REQUIRE(result == RESULT_SUCCESS); - REQUIRE(output[2] == 0); - } - - SECTION("translates multi-handle descriptors") { - auto a = MakeObject(); - auto b = MakeObject(); - auto c = MakeObject(); - input[0] = IPC::MakeHeader(0, 0, 5); - input[1] = IPC::MoveHandleDesc(2); - input[2] = context.AddOutgoingHandle(a); - input[3] = context.AddOutgoingHandle(b); - input[4] = IPC::CopyHandleDesc(1); - input[5] = context.AddOutgoingHandle(c); - - context.WriteToOutgoingCommandBuffer(output, *process, handle_table); - - REQUIRE(handle_table.GetGeneric(output[2]) == a); - REQUIRE(handle_table.GetGeneric(output[3]) == b); - REQUIRE(handle_table.GetGeneric(output[5]) == c); - } -} - -} // namespace Kernel diff --git a/src/video_core/command_processor.cpp b/src/video_core/command_processor.cpp index caf9f7a06..b6fbc5d80 100644 --- a/src/video_core/command_processor.cpp +++ b/src/video_core/command_processor.cpp @@ -366,7 +366,7 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) { switch (id) { // Trigger IRQ case PICA_REG_INDEX(trigger_irq): - Service::GSP::SignalInterrupt(Service::GSP::InterruptId::P3D); + //Service::GSP::SignalInterrupt(Service::GSP::InterruptId::P3D); break; case PICA_REG_INDEX(pipeline.triangle_topology):