diff options
| author | 2017-10-14 22:18:42 -0400 | |
|---|---|---|
| committer | 2017-10-14 22:18:42 -0400 | |
| commit | 960a1416de3780e91855d9389c4534acf8c061df (patch) | |
| tree | 6b373fed639eb39098ba33e6247893919005a2c8 /src | |
| parent | nso: Add a log for loading submodules. (diff) | |
| download | yuzu-960a1416de3780e91855d9389c4534acf8c061df.tar.gz yuzu-960a1416de3780e91855d9389c4534acf8c061df.tar.xz yuzu-960a1416de3780e91855d9389c4534acf8c061df.zip | |
hle: Initial implementation of NX service framework and IPC.
Diffstat (limited to 'src')
| -rw-r--r-- | src/common/logging/backend.cpp | 2 | ||||
| -rw-r--r-- | src/common/logging/log.h | 2 | ||||
| -rw-r--r-- | src/core/CMakeLists.txt | 6 | ||||
| -rw-r--r-- | src/core/hle/ipc.h | 189 | ||||
| -rw-r--r-- | src/core/hle/ipc_helpers.h | 122 | ||||
| -rw-r--r-- | src/core/hle/kernel/hle_ipc.cpp | 152 | ||||
| -rw-r--r-- | src/core/hle/kernel/hle_ipc.h | 25 | ||||
| -rw-r--r-- | src/core/hle/service/lm/lm.cpp | 43 | ||||
| -rw-r--r-- | src/core/hle/service/lm/lm.h | 25 | ||||
| -rw-r--r-- | src/core/hle/service/service.cpp | 115 | ||||
| -rw-r--r-- | src/core/hle/service/service.h | 84 | ||||
| -rw-r--r-- | src/core/hle/service/sm/controller.cpp | 42 | ||||
| -rw-r--r-- | src/core/hle/service/sm/controller.h | 22 | ||||
| -rw-r--r-- | src/core/hle/service/sm/sm.cpp | 91 | ||||
| -rw-r--r-- | src/core/hle/service/sm/sm.h | 20 | ||||
| -rw-r--r-- | src/core/hle/service/sm/srv.cpp | 235 | ||||
| -rw-r--r-- | src/core/hle/service/sm/srv.h | 38 | ||||
| -rw-r--r-- | src/core/hle/svc.cpp | 1 | ||||
| -rw-r--r-- | src/tests/CMakeLists.txt | 1 | ||||
| -rw-r--r-- | src/tests/core/hle/kernel/hle_ipc.cpp | 216 | ||||
| -rw-r--r-- | src/video_core/command_processor.cpp | 2 |
21 files changed, 574 insertions, 859 deletions
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 { | |||
| 32 | CLS(Kernel) \ | 32 | CLS(Kernel) \ |
| 33 | SUB(Kernel, SVC) \ | 33 | SUB(Kernel, SVC) \ |
| 34 | CLS(Service) \ | 34 | CLS(Service) \ |
| 35 | SUB(Service, SRV) \ | 35 | SUB(Service, SM) \ |
| 36 | SUB(Service, FS) \ | 36 | SUB(Service, FS) \ |
| 37 | SUB(Service, GSP) \ | 37 | SUB(Service, GSP) \ |
| 38 | SUB(Service, CFG) \ | 38 | 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 { | |||
| 49 | Kernel_SVC, ///< Kernel system calls | 49 | Kernel_SVC, ///< Kernel system calls |
| 50 | Service, ///< HLE implementation of system services. Each major service | 50 | Service, ///< HLE implementation of system services. Each major service |
| 51 | /// should have its own subclass. | 51 | /// should have its own subclass. |
| 52 | Service_SRV, ///< The SRV (Service Directory) implementation | 52 | Service_SM, ///< The SRV (Service Directory) implementation |
| 53 | Service_FS, ///< The FS (Filesystem) service implementation | 53 | Service_FS, ///< The FS (Filesystem) service implementation |
| 54 | Service_GSP, ///< The GSP (GPU control) service | 54 | Service_GSP, ///< The GSP (GPU control) service |
| 55 | Service_CFG, ///< The CFG (Configuration) service | 55 | 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 | |||
| 51 | hle/service/dsp_dsp.cpp | 51 | hle/service/dsp_dsp.cpp |
| 52 | hle/service/gsp_gpu.cpp | 52 | hle/service/gsp_gpu.cpp |
| 53 | hle/service/hid/hid.cpp | 53 | hle/service/hid/hid.cpp |
| 54 | hle/service/lm/lm.cpp | ||
| 54 | hle/service/service.cpp | 55 | hle/service/service.cpp |
| 56 | hle/service/sm/controller.cpp | ||
| 55 | hle/service/sm/sm.cpp | 57 | hle/service/sm/sm.cpp |
| 56 | hle/service/sm/srv.cpp | ||
| 57 | hle/shared_page.cpp | 58 | hle/shared_page.cpp |
| 58 | hle/svc.cpp | 59 | hle/svc.cpp |
| 59 | hw/aes/arithmetic128.cpp | 60 | hw/aes/arithmetic128.cpp |
| @@ -139,9 +140,10 @@ set(HEADERS | |||
| 139 | hle/service/dsp_dsp.h | 140 | hle/service/dsp_dsp.h |
| 140 | hle/service/gsp_gpu.h | 141 | hle/service/gsp_gpu.h |
| 141 | hle/service/hid/hid.h | 142 | hle/service/hid/hid.h |
| 143 | hle/service/lm/lm.h | ||
| 142 | hle/service/service.h | 144 | hle/service/service.h |
| 145 | hle/service/sm/controller.h | ||
| 143 | hle/service/sm/sm.h | 146 | hle/service/sm/sm.h |
| 144 | hle/service/sm/srv.h | ||
| 145 | hle/shared_page.h | 147 | hle/shared_page.h |
| 146 | hle/svc.h | 148 | hle/svc.h |
| 147 | hw/aes/arithmetic128.h | 149 | 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 @@ | |||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include "common/common_types.h" | 7 | #include "common/common_types.h" |
| 8 | #include "common/swap.h" | ||
| 8 | #include "core/hle/kernel/errors.h" | 9 | #include "core/hle/kernel/errors.h" |
| 9 | #include "core/hle/kernel/thread.h" | ||
| 10 | #include "core/memory.h" | 10 | #include "core/memory.h" |
| 11 | 11 | ||
| 12 | namespace Kernel { | ||
| 13 | |||
| 14 | /// Offset into command buffer of header | ||
| 15 | static const int kCommandHeaderOffset = 0x80; | ||
| 16 | |||
| 17 | /** | ||
| 18 | * Returns a pointer to the command buffer in the current thread's TLS | ||
| 19 | * TODO(Subv): This is not entirely correct, the command buffer should be copied from | ||
| 20 | * the thread's TLS to an intermediate buffer in kernel memory, and then copied again to | ||
| 21 | * the service handler process' memory. | ||
| 22 | * @param offset Optional offset into command buffer | ||
| 23 | * @param offset Optional offset into command buffer (in bytes) | ||
| 24 | * @return Pointer to command buffer | ||
| 25 | */ | ||
| 26 | inline u32* GetCommandBuffer(const int offset = 0) { | ||
| 27 | return (u32*)Memory::GetPointer(GetCurrentThread()->GetTLSAddress() + kCommandHeaderOffset + | ||
| 28 | offset); | ||
| 29 | } | ||
| 30 | |||
| 31 | /// Offset into static buffers, relative to command buffer header | ||
| 32 | static const int kStaticBuffersOffset = 0x100; | ||
| 33 | |||
| 34 | /** | ||
| 35 | * Returns a pointer to the static buffers area in the current thread's TLS | ||
| 36 | * TODO(Subv): cf. GetCommandBuffer | ||
| 37 | * @param offset Optional offset into static buffers area (in bytes) | ||
| 38 | * @return Pointer to static buffers area | ||
| 39 | */ | ||
| 40 | inline u32* GetStaticBuffers(const int offset = 0) { | ||
| 41 | return GetCommandBuffer(kStaticBuffersOffset + offset); | ||
| 42 | } | ||
| 43 | } | ||
| 44 | |||
| 45 | namespace IPC { | 12 | namespace IPC { |
| 46 | 13 | ||
| 47 | /// Size of the command buffer area, in 32-bit words. | 14 | /// Size of the command buffer area, in 32-bit words. |
| @@ -53,6 +20,130 @@ constexpr size_t COMMAND_BUFFER_LENGTH = 0x100 / sizeof(u32); | |||
| 53 | using Kernel::ERR_INVALID_BUFFER_DESCRIPTOR; | 20 | using Kernel::ERR_INVALID_BUFFER_DESCRIPTOR; |
| 54 | constexpr auto ERR_INVALID_HANDLE = Kernel::ERR_INVALID_HANDLE_OS; | 21 | constexpr auto ERR_INVALID_HANDLE = Kernel::ERR_INVALID_HANDLE_OS; |
| 55 | 22 | ||
| 23 | |||
| 24 | enum class ControlCommand : u32 { | ||
| 25 | ConvertSessionToDomain = 0, | ||
| 26 | ConvertDomainToSession = 1, | ||
| 27 | DuplicateSession = 2, | ||
| 28 | QueryPointerBufferSize = 3, | ||
| 29 | DuplicateSessionEx = 4, | ||
| 30 | Unspecified, | ||
| 31 | }; | ||
| 32 | |||
| 33 | enum class CommandType : u32 { | ||
| 34 | Close = 2, | ||
| 35 | Request = 4, | ||
| 36 | Control = 5, | ||
| 37 | Unspecified, | ||
| 38 | }; | ||
| 39 | |||
| 40 | struct CommandHeader { | ||
| 41 | union { | ||
| 42 | u32_le raw_low; | ||
| 43 | BitField<0, 16, CommandType> type; | ||
| 44 | BitField<16, 4, u32_le> num_buf_x_descriptors; | ||
| 45 | BitField<20, 4, u32_le> num_buf_a_descriptors; | ||
| 46 | BitField<24, 4, u32_le> num_buf_b_descriptors; | ||
| 47 | BitField<28, 4, u32_le> num_buf_w_descriptors; | ||
| 48 | }; | ||
| 49 | |||
| 50 | enum class BufferDescriptorCFlag : u32 { | ||
| 51 | Disabled = 0, | ||
| 52 | NoDescriptor = 1, | ||
| 53 | TwoDesciptors = 2, | ||
| 54 | }; | ||
| 55 | |||
| 56 | union { | ||
| 57 | u32_le raw_high; | ||
| 58 | BitField<0, 10, u32_le> data_size; | ||
| 59 | BitField<10, 4, BufferDescriptorCFlag> buf_c_descriptor_flags; | ||
| 60 | BitField<31, 1, u32_le> enable_handle_descriptor; | ||
| 61 | }; | ||
| 62 | }; | ||
| 63 | static_assert(sizeof(CommandHeader) == 8, "CommandHeader size is incorrect"); | ||
| 64 | |||
| 65 | union HandleDescriptorHeader { | ||
| 66 | u32_le raw_high; | ||
| 67 | BitField<0, 1, u32_le> send_current_pid; | ||
| 68 | BitField<1, 4, u32_le> num_handles_to_copy; | ||
| 69 | BitField<5, 4, u32_le> num_handles_to_move; | ||
| 70 | }; | ||
| 71 | static_assert(sizeof(HandleDescriptorHeader) == 4, "HandleDescriptorHeader size is incorrect"); | ||
| 72 | |||
| 73 | struct BufferDescriptorX { | ||
| 74 | union { | ||
| 75 | BitField<0, 6, u32_le> counter_bits_0_5; | ||
| 76 | BitField<6, 3, u32_le> address_bits_36_38; | ||
| 77 | BitField<9, 3, u32_le> counter_bits_9_11; | ||
| 78 | BitField<12, 4, u32_le> address_bits_32_35; | ||
| 79 | BitField<16, 16, u32_le> size; | ||
| 80 | }; | ||
| 81 | |||
| 82 | u32_le address_bits_0_31; | ||
| 83 | |||
| 84 | u32_le Counter() const { | ||
| 85 | u32_le counter{ counter_bits_0_5 }; | ||
| 86 | counter |= counter_bits_9_11 << 9; | ||
| 87 | return counter; | ||
| 88 | } | ||
| 89 | |||
| 90 | VAddr Address() const { | ||
| 91 | VAddr address{ address_bits_0_31 }; | ||
| 92 | address |= static_cast<VAddr>(address_bits_32_35) << 32; | ||
| 93 | address |= static_cast<VAddr>(address_bits_36_38) << 36; | ||
| 94 | return address; | ||
| 95 | } | ||
| 96 | }; | ||
| 97 | static_assert(sizeof(BufferDescriptorX) == 8, "BufferDescriptorX size is incorrect"); | ||
| 98 | |||
| 99 | struct BufferDescriptorABW { | ||
| 100 | u32_le size_bits_0_31; | ||
| 101 | u32_le address_bits_0_31; | ||
| 102 | |||
| 103 | union { | ||
| 104 | BitField<0, 2, u32_le> flags; | ||
| 105 | BitField<2, 3, u32_le> address_bits_36_38; | ||
| 106 | BitField<24, 4, u32_le> size_bits_32_35; | ||
| 107 | BitField<28, 4, u32_le> address_bits_32_35; | ||
| 108 | }; | ||
| 109 | |||
| 110 | VAddr Address() const { | ||
| 111 | VAddr address{ address_bits_0_31 }; | ||
| 112 | address |= static_cast<VAddr>(address_bits_32_35) << 32; | ||
| 113 | address |= static_cast<VAddr>(address_bits_36_38) << 36; | ||
| 114 | return address; | ||
| 115 | } | ||
| 116 | |||
| 117 | u64 Size() const { | ||
| 118 | u64 size{ size_bits_0_31 }; | ||
| 119 | size |= static_cast<u64>(size_bits_32_35) << 32; | ||
| 120 | return size; | ||
| 121 | } | ||
| 122 | }; | ||
| 123 | static_assert(sizeof(BufferDescriptorABW) == 12, "BufferDescriptorABW size is incorrect"); | ||
| 124 | |||
| 125 | struct BufferDescriptorC { | ||
| 126 | u32_le address_bits_0_31; | ||
| 127 | |||
| 128 | union { | ||
| 129 | BitField<0, 16, u32_le> address_bits_32_47; | ||
| 130 | BitField<16, 16, u32_le> size; | ||
| 131 | }; | ||
| 132 | |||
| 133 | VAddr Address() const { | ||
| 134 | VAddr address{ address_bits_0_31 }; | ||
| 135 | address |= static_cast<VAddr>(address_bits_32_47) << 32; | ||
| 136 | return address; | ||
| 137 | } | ||
| 138 | }; | ||
| 139 | static_assert(sizeof(BufferDescriptorC) == 8, "BufferDescriptorC size is incorrect"); | ||
| 140 | |||
| 141 | struct DataPayloadHeader { | ||
| 142 | u32_le magic; | ||
| 143 | INSERT_PADDING_WORDS(1); | ||
| 144 | }; | ||
| 145 | static_assert(sizeof(DataPayloadHeader) == 8, "DataPayloadRequest size is incorrect"); | ||
| 146 | |||
| 56 | enum DescriptorType : u32 { | 147 | enum DescriptorType : u32 { |
| 57 | // Buffer related desciptors types (mask : 0x0F) | 148 | // Buffer related desciptors types (mask : 0x0F) |
| 58 | StaticBuffer = 0x02, | 149 | StaticBuffer = 0x02, |
| @@ -65,36 +156,6 @@ enum DescriptorType : u32 { | |||
| 65 | CallingPid = 0x20, | 156 | CallingPid = 0x20, |
| 66 | }; | 157 | }; |
| 67 | 158 | ||
| 68 | union Header { | ||
| 69 | u32 raw; | ||
| 70 | BitField<0, 6, u32> translate_params_size; | ||
| 71 | BitField<6, 6, u32> normal_params_size; | ||
| 72 | BitField<16, 16, u32> command_id; | ||
| 73 | }; | ||
| 74 | |||
| 75 | /** | ||
| 76 | * @brief Creates a command header to be used for IPC | ||
| 77 | * @param command_id ID of the command to create a header for. | ||
| 78 | * @param normal_params_size Size of the normal parameters in words. Up to 63. | ||
| 79 | * @param translate_params_size Size of the translate parameters in words. Up to 63. | ||
| 80 | * @return The created IPC header. | ||
| 81 | * | ||
| 82 | * Normal parameters are sent directly to the process while the translate parameters might go | ||
| 83 | * through modifications and checks by the kernel. | ||
| 84 | * The translate parameters are described by headers generated with the IPC::*Desc functions. | ||
| 85 | * | ||
| 86 | * @note While @p normal_params_size is equivalent to the number of normal parameters, | ||
| 87 | * @p translate_params_size includes the size occupied by the translate parameters headers. | ||
| 88 | */ | ||
| 89 | inline u32 MakeHeader(u16 command_id, unsigned int normal_params_size, | ||
| 90 | unsigned int translate_params_size) { | ||
| 91 | Header header{}; | ||
| 92 | header.command_id.Assign(command_id); | ||
| 93 | header.normal_params_size.Assign(normal_params_size); | ||
| 94 | header.translate_params_size.Assign(translate_params_size); | ||
| 95 | return header.raw; | ||
| 96 | } | ||
| 97 | |||
| 98 | constexpr u32 MoveHandleDesc(u32 num_handles = 1) { | 159 | constexpr u32 MoveHandleDesc(u32 num_handles = 1) { |
| 99 | return MoveHandle | ((num_handles - 1) << 26); | 160 | return MoveHandle | ((num_handles - 1) << 26); |
| 100 | } | 161 | } |
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 { | |||
| 19 | protected: | 19 | protected: |
| 20 | Kernel::HLERequestContext* context = nullptr; | 20 | Kernel::HLERequestContext* context = nullptr; |
| 21 | u32* cmdbuf; | 21 | u32* cmdbuf; |
| 22 | ptrdiff_t index = 1; | 22 | ptrdiff_t index = 0; |
| 23 | Header header; | ||
| 24 | 23 | ||
| 25 | public: | 24 | public: |
| 26 | RequestHelperBase(Kernel::HLERequestContext& context, Header desired_header) | 25 | RequestHelperBase(u32* command_buffer) : cmdbuf(command_buffer) {} |
| 27 | : context(&context), cmdbuf(context.CommandBuffer()), header(desired_header) {} | ||
| 28 | 26 | ||
| 29 | RequestHelperBase(u32* command_buffer, Header command_header) | 27 | RequestHelperBase(Kernel::HLERequestContext& context) |
| 30 | : cmdbuf(command_buffer), header(command_header) {} | 28 | : context(&context), cmdbuf(context.CommandBuffer()) {} |
| 31 | |||
| 32 | /// Returns the total size of the request in words | ||
| 33 | size_t TotalSize() const { | ||
| 34 | return 1 /* command header */ + header.normal_params_size + header.translate_params_size; | ||
| 35 | } | ||
| 36 | 29 | ||
| 37 | void ValidateHeader() { | 30 | void ValidateHeader() { |
| 38 | DEBUG_ASSERT_MSG(index == TotalSize(), "Operations do not match the header (cmd 0x%x)", | 31 | // DEBUG_ASSERT_MSG(index == TotalSize(), "Operations do not match the header (cmd 0x%x)", |
| 39 | header.raw); | 32 | // header.raw); |
| 40 | } | 33 | } |
| 41 | 34 | ||
| 42 | void Skip(unsigned size_in_words, bool set_to_null) { | 35 | void Skip(unsigned size_in_words, bool set_to_null) { |
| @@ -46,45 +39,50 @@ public: | |||
| 46 | } | 39 | } |
| 47 | 40 | ||
| 48 | /** | 41 | /** |
| 49 | * @brief Retrieves the address of a static buffer, used when a buffer is needed for output | 42 | * Aligns the current position forward to a 16-byte boundary, padding with zeros. Jumps forward |
| 50 | * @param buffer_id The index of the static buffer | 43 | * by 16-bytes at a minimum. |
| 51 | * @param data_size If non-null, will store the size of the buffer | ||
| 52 | */ | 44 | */ |
| 53 | VAddr PeekStaticBuffer(u8 buffer_id, size_t* data_size = nullptr) const { | 45 | void AlignWithPadding() { |
| 54 | u32* static_buffer = cmdbuf + Kernel::kStaticBuffersOffset / sizeof(u32) + buffer_id * 2; | 46 | Skip(4 - (index & 3), true); |
| 55 | if (data_size) | 47 | } |
| 56 | *data_size = StaticBufferDescInfo{static_buffer[0]}.size; | 48 | |
| 57 | return static_buffer[1]; | 49 | unsigned GetCurrentOffset() const { |
| 50 | return static_cast<unsigned>(index); | ||
| 58 | } | 51 | } |
| 59 | }; | 52 | }; |
| 60 | 53 | ||
| 61 | class RequestBuilder : public RequestHelperBase { | 54 | class RequestBuilder : public RequestHelperBase { |
| 62 | public: | 55 | public: |
| 63 | RequestBuilder(Kernel::HLERequestContext& context, Header command_header) | 56 | RequestBuilder(u32* command_buffer) : RequestHelperBase(command_buffer) {} |
| 64 | : RequestHelperBase(context, command_header) { | ||
| 65 | // From this point we will start overwriting the existing command buffer, so it's safe to | ||
| 66 | // release all previous incoming Object pointers since they won't be usable anymore. | ||
| 67 | context.ClearIncomingObjects(); | ||
| 68 | cmdbuf[0] = header.raw; | ||
| 69 | } | ||
| 70 | |||
| 71 | RequestBuilder(Kernel::HLERequestContext& context, u16 command_id, unsigned normal_params_size, | ||
| 72 | unsigned translate_params_size) | ||
| 73 | : RequestBuilder( | ||
| 74 | context, Header{MakeHeader(command_id, normal_params_size, translate_params_size)}) {} | ||
| 75 | 57 | ||
| 76 | RequestBuilder(u32* command_buffer, Header command_header) | 58 | RequestBuilder(Kernel::HLERequestContext& context, unsigned normal_params_size, |
| 77 | : RequestHelperBase(command_buffer, command_header) { | 59 | u32 num_handles_to_copy = 0, u32 num_handles_to_move = 0) |
| 78 | cmdbuf[0] = header.raw; | 60 | : RequestHelperBase(context) { |
| 79 | } | 61 | memset(cmdbuf, 0, 64); |
| 80 | 62 | ||
| 81 | explicit RequestBuilder(u32* command_buffer, u32 command_header) | 63 | context.ClearIncomingObjects(); |
| 82 | : RequestBuilder(command_buffer, Header{command_header}) {} | ||
| 83 | 64 | ||
| 84 | RequestBuilder(u32* command_buffer, u16 command_id, unsigned normal_params_size, | 65 | IPC::CommandHeader header{}; |
| 85 | unsigned translate_params_size) | 66 | header.data_size.Assign(normal_params_size * sizeof(u32)); |
| 86 | : RequestBuilder(command_buffer, | 67 | if (num_handles_to_copy || num_handles_to_move) { |
| 87 | MakeHeader(command_id, normal_params_size, translate_params_size)) {} | 68 | header.enable_handle_descriptor.Assign(1); |
| 69 | } | ||
| 70 | PushRaw(header); | ||
| 71 | |||
| 72 | if (header.enable_handle_descriptor) { | ||
| 73 | IPC::HandleDescriptorHeader handle_descriptor_header{}; | ||
| 74 | handle_descriptor_header.num_handles_to_copy.Assign(num_handles_to_copy); | ||
| 75 | handle_descriptor_header.num_handles_to_move.Assign(num_handles_to_move); | ||
| 76 | PushRaw(handle_descriptor_header); | ||
| 77 | Skip(num_handles_to_copy + num_handles_to_move, true); | ||
| 78 | } | ||
| 79 | |||
| 80 | AlignWithPadding(); | ||
| 81 | |||
| 82 | IPC::DataPayloadHeader data_payload_header{}; | ||
| 83 | data_payload_header.magic = 0x4f434653; | ||
| 84 | PushRaw(data_payload_header); | ||
| 85 | } | ||
| 88 | 86 | ||
| 89 | // Validate on destruction, as there shouldn't be any case where we don't want it | 87 | // Validate on destruction, as there shouldn't be any case where we don't want it |
| 90 | ~RequestBuilder() { | 88 | ~RequestBuilder() { |
| @@ -131,7 +129,6 @@ inline void RequestBuilder::Push(u32 value) { | |||
| 131 | 129 | ||
| 132 | template <typename T> | 130 | template <typename T> |
| 133 | void RequestBuilder::PushRaw(const T& value) { | 131 | void RequestBuilder::PushRaw(const T& value) { |
| 134 | static_assert(std::is_trivially_copyable<T>(), "Raw types should be trivially copyable"); | ||
| 135 | std::memcpy(cmdbuf + index, &value, sizeof(T)); | 132 | std::memcpy(cmdbuf + index, &value, sizeof(T)); |
| 136 | index += (sizeof(T) + 3) / 4; // round up to word length | 133 | index += (sizeof(T) + 3) / 4; // round up to word length |
| 137 | } | 134 | } |
| @@ -203,36 +200,20 @@ inline void RequestBuilder::PushMappedBuffer(VAddr buffer_vaddr, size_t size, | |||
| 203 | 200 | ||
| 204 | class RequestParser : public RequestHelperBase { | 201 | class RequestParser : public RequestHelperBase { |
| 205 | public: | 202 | public: |
| 206 | RequestParser(Kernel::HLERequestContext& context, Header desired_header) | 203 | RequestParser(u32* command_buffer) : RequestHelperBase(command_buffer) {} |
| 207 | : RequestHelperBase(context, desired_header) {} | ||
| 208 | 204 | ||
| 209 | RequestParser(Kernel::HLERequestContext& context, u16 command_id, unsigned normal_params_size, | 205 | RequestParser(Kernel::HLERequestContext& context) : RequestHelperBase(context) { |
| 210 | unsigned translate_params_size) | 206 | ASSERT_MSG(context.GetDataPayloadOffset(), "context is incomplete"); |
| 211 | : RequestParser(context, | 207 | Skip(context.GetDataPayloadOffset(), false); |
| 212 | Header{MakeHeader(command_id, normal_params_size, translate_params_size)}) { | ||
| 213 | } | 208 | } |
| 214 | 209 | ||
| 215 | RequestParser(u32* command_buffer, Header command_header) | 210 | RequestBuilder MakeBuilder(u32 normal_params_size, u32 num_handles_to_copy, |
| 216 | : RequestHelperBase(command_buffer, command_header) {} | 211 | u32 num_handles_to_move, bool validate_header = true) { |
| 217 | 212 | if (validate_header) { | |
| 218 | explicit RequestParser(u32* command_buffer, u32 command_header) | ||
| 219 | : RequestParser(command_buffer, Header{command_header}) {} | ||
| 220 | |||
| 221 | RequestParser(u32* command_buffer, u16 command_id, unsigned normal_params_size, | ||
| 222 | unsigned translate_params_size) | ||
| 223 | : RequestParser(command_buffer, | ||
| 224 | MakeHeader(command_id, normal_params_size, translate_params_size)) {} | ||
| 225 | |||
| 226 | RequestBuilder MakeBuilder(u32 normal_params_size, u32 translate_params_size, | ||
| 227 | bool validateHeader = true) { | ||
| 228 | if (validateHeader) | ||
| 229 | ValidateHeader(); | 213 | ValidateHeader(); |
| 230 | Header builderHeader{MakeHeader(static_cast<u16>(header.command_id), normal_params_size, | 214 | } |
| 231 | translate_params_size)}; | 215 | |
| 232 | if (context != nullptr) | 216 | return {*context, normal_params_size, num_handles_to_copy, num_handles_to_move}; |
| 233 | return {*context, builderHeader}; | ||
| 234 | else | ||
| 235 | return {cmdbuf, builderHeader}; | ||
| 236 | } | 217 | } |
| 237 | 218 | ||
| 238 | template <typename T> | 219 | template <typename T> |
| @@ -342,7 +323,6 @@ inline u32 RequestParser::Pop() { | |||
| 342 | 323 | ||
| 343 | template <typename T> | 324 | template <typename T> |
| 344 | void RequestParser::PopRaw(T& value) { | 325 | void RequestParser::PopRaw(T& value) { |
| 345 | static_assert(std::is_trivially_copyable<T>(), "Raw types should be trivially copyable"); | ||
| 346 | std::memcpy(&value, cmdbuf + index, sizeof(T)); | 326 | std::memcpy(&value, cmdbuf + index, sizeof(T)); |
| 347 | index += (sizeof(T) + 3) / 4; // round up to word length | 327 | index += (sizeof(T) + 3) / 4; // round up to word length |
| 348 | } | 328 | } |
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 @@ | |||
| 5 | #include <boost/range/algorithm_ext/erase.hpp> | 5 | #include <boost/range/algorithm_ext/erase.hpp> |
| 6 | #include "common/assert.h" | 6 | #include "common/assert.h" |
| 7 | #include "common/common_types.h" | 7 | #include "common/common_types.h" |
| 8 | #include "core/hle/ipc_helpers.h" | ||
| 8 | #include "core/hle/kernel/handle_table.h" | 9 | #include "core/hle/kernel/handle_table.h" |
| 9 | #include "core/hle/kernel/hle_ipc.h" | 10 | #include "core/hle/kernel/hle_ipc.h" |
| 10 | #include "core/hle/kernel/kernel.h" | 11 | #include "core/hle/kernel/kernel.h" |
| @@ -44,88 +45,103 @@ void HLERequestContext::ClearIncomingObjects() { | |||
| 44 | request_handles.clear(); | 45 | request_handles.clear(); |
| 45 | } | 46 | } |
| 46 | 47 | ||
| 47 | ResultCode HLERequestContext::PopulateFromIncomingCommandBuffer(const u32_le* src_cmdbuf, | 48 | void HLERequestContext::ParseCommandBuffer(u32_le* src_cmdbuf) { |
| 48 | Process& src_process, | 49 | IPC::RequestParser rp(src_cmdbuf); |
| 49 | HandleTable& src_table) { | 50 | command_header = std::make_unique<IPC::CommandHeader>(rp.PopRaw<IPC::CommandHeader>()); |
| 50 | IPC::Header header{src_cmdbuf[0]}; | ||
| 51 | 51 | ||
| 52 | size_t untranslated_size = 1u + header.normal_params_size; | 52 | // If handle descriptor is present, add size of it |
| 53 | size_t command_size = untranslated_size + header.translate_params_size; | 53 | if (command_header->enable_handle_descriptor) { |
| 54 | ASSERT(command_size <= IPC::COMMAND_BUFFER_LENGTH); // TODO(yuriks): Return error | 54 | handle_descriptor_header = |
| 55 | std::make_unique<IPC::HandleDescriptorHeader>(rp.PopRaw<IPC::HandleDescriptorHeader>()); | ||
| 56 | if (handle_descriptor_header->send_current_pid) { | ||
| 57 | rp.Skip(2, false); | ||
| 58 | } | ||
| 59 | rp.Skip(handle_descriptor_header->num_handles_to_copy, false); | ||
| 60 | rp.Skip(handle_descriptor_header->num_handles_to_move, false); | ||
| 61 | } | ||
| 55 | 62 | ||
| 56 | std::copy_n(src_cmdbuf, untranslated_size, cmd_buf.begin()); | 63 | // Padding to align to 16 bytes |
| 64 | rp.AlignWithPadding(); | ||
| 57 | 65 | ||
| 58 | size_t i = untranslated_size; | 66 | if (command_header->num_buf_x_descriptors) { |
| 59 | while (i < command_size) { | 67 | UNIMPLEMENTED(); |
| 60 | u32 descriptor = cmd_buf[i] = src_cmdbuf[i]; | 68 | } |
| 61 | i += 1; | 69 | if (command_header->num_buf_a_descriptors) { |
| 62 | 70 | UNIMPLEMENTED(); | |
| 63 | switch (IPC::GetDescriptorType(descriptor)) { | 71 | } |
| 64 | case IPC::DescriptorType::CopyHandle: | 72 | if (command_header->num_buf_b_descriptors) { |
| 65 | case IPC::DescriptorType::MoveHandle: { | 73 | UNIMPLEMENTED(); |
| 66 | u32 num_handles = IPC::HandleNumberFromDesc(descriptor); | 74 | } |
| 67 | ASSERT(i + num_handles <= command_size); // TODO(yuriks): Return error | 75 | if (command_header->num_buf_w_descriptors) { |
| 68 | for (u32 j = 0; j < num_handles; ++j) { | 76 | UNIMPLEMENTED(); |
| 69 | Handle handle = src_cmdbuf[i]; | 77 | } |
| 70 | SharedPtr<Object> object = nullptr; | 78 | if (command_header->buf_c_descriptor_flags != |
| 71 | if (handle != 0) { | 79 | IPC::CommandHeader::BufferDescriptorCFlag::Disabled) { |
| 72 | object = src_table.GetGeneric(handle); | 80 | UNIMPLEMENTED(); |
| 73 | ASSERT(object != nullptr); // TODO(yuriks): Return error | 81 | } |
| 74 | if (descriptor == IPC::DescriptorType::MoveHandle) { | ||
| 75 | src_table.Close(handle); | ||
| 76 | } | ||
| 77 | } | ||
| 78 | 82 | ||
| 79 | cmd_buf[i++] = AddOutgoingHandle(std::move(object)); | 83 | data_payload_header = |
| 80 | } | 84 | std::make_unique<IPC::DataPayloadHeader>(rp.PopRaw<IPC::DataPayloadHeader>()); |
| 81 | break; | 85 | ASSERT(data_payload_header->magic == 0x49434653 || data_payload_header->magic == 0x4F434653); |
| 82 | } | 86 | |
| 83 | case IPC::DescriptorType::CallingPid: { | 87 | data_payload_offset = rp.GetCurrentOffset(); |
| 84 | cmd_buf[i++] = src_process.process_id; | 88 | command = rp.Pop<u32_le>(); |
| 85 | break; | 89 | } |
| 86 | } | 90 | |
| 87 | default: | 91 | ResultCode HLERequestContext::PopulateFromIncomingCommandBuffer(u32_le* src_cmdbuf, |
| 88 | UNIMPLEMENTED_MSG("Unsupported handle translation: 0x%08X", descriptor); | 92 | Process& src_process, |
| 93 | HandleTable& src_table) { | ||
| 94 | ParseCommandBuffer(src_cmdbuf); | ||
| 95 | size_t untranslated_size = data_payload_offset + command_header->data_size; | ||
| 96 | std::copy_n(src_cmdbuf, untranslated_size, cmd_buf.begin()); | ||
| 97 | |||
| 98 | if (command_header->enable_handle_descriptor) { | ||
| 99 | if (handle_descriptor_header->num_handles_to_copy || | ||
| 100 | handle_descriptor_header->num_handles_to_move) { | ||
| 101 | UNIMPLEMENTED(); | ||
| 89 | } | 102 | } |
| 90 | } | 103 | } |
| 91 | |||
| 92 | return RESULT_SUCCESS; | 104 | return RESULT_SUCCESS; |
| 93 | } | 105 | } |
| 94 | 106 | ||
| 95 | ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(u32_le* dst_cmdbuf, Process& dst_process, | 107 | ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(u32_le* dst_cmdbuf, Process& dst_process, |
| 96 | HandleTable& dst_table) const { | 108 | HandleTable& dst_table) { |
| 97 | IPC::Header header{cmd_buf[0]}; | 109 | ParseCommandBuffer(&cmd_buf[0]); |
| 98 | 110 | size_t untranslated_size = data_payload_offset + command_header->data_size; | |
| 99 | size_t untranslated_size = 1u + header.normal_params_size; | ||
| 100 | size_t command_size = untranslated_size + header.translate_params_size; | ||
| 101 | ASSERT(command_size <= IPC::COMMAND_BUFFER_LENGTH); | ||
| 102 | |||
| 103 | std::copy_n(cmd_buf.begin(), untranslated_size, dst_cmdbuf); | 111 | std::copy_n(cmd_buf.begin(), untranslated_size, dst_cmdbuf); |
| 104 | 112 | ||
| 105 | size_t i = untranslated_size; | 113 | if (command_header->enable_handle_descriptor) { |
| 106 | while (i < command_size) { | 114 | size_t command_size = untranslated_size + handle_descriptor_header->num_handles_to_copy + |
| 107 | u32 descriptor = dst_cmdbuf[i] = cmd_buf[i]; | 115 | handle_descriptor_header->num_handles_to_move; |
| 108 | i += 1; | 116 | ASSERT(command_size <= IPC::COMMAND_BUFFER_LENGTH); |
| 109 | 117 | ||
| 110 | switch (IPC::GetDescriptorType(descriptor)) { | 118 | size_t untranslated_index = untranslated_size; |
| 111 | case IPC::DescriptorType::CopyHandle: | 119 | size_t handle_write_offset = 3; |
| 112 | case IPC::DescriptorType::MoveHandle: { | 120 | while (untranslated_index < command_size) { |
| 113 | // HLE services don't use handles, so we treat both CopyHandle and MoveHandle equally | 121 | u32 descriptor = cmd_buf[untranslated_index]; |
| 114 | u32 num_handles = IPC::HandleNumberFromDesc(descriptor); | 122 | untranslated_index += 1; |
| 115 | ASSERT(i + num_handles <= command_size); | 123 | |
| 116 | for (u32 j = 0; j < num_handles; ++j) { | 124 | switch (IPC::GetDescriptorType(descriptor)) { |
| 117 | SharedPtr<Object> object = GetIncomingHandle(cmd_buf[i]); | 125 | case IPC::DescriptorType::CopyHandle: |
| 118 | Handle handle = 0; | 126 | case IPC::DescriptorType::MoveHandle: { |
| 119 | if (object != nullptr) { | 127 | // HLE services don't use handles, so we treat both CopyHandle and MoveHandle |
| 120 | // TODO(yuriks): Figure out the proper error handling for if this fails | 128 | // equally |
| 121 | handle = dst_table.Create(object).Unwrap(); | 129 | u32 num_handles = IPC::HandleNumberFromDesc(descriptor); |
| 130 | for (u32 j = 0; j < num_handles; ++j) { | ||
| 131 | SharedPtr<Object> object = GetIncomingHandle(cmd_buf[untranslated_index]); | ||
| 132 | Handle handle = 0; | ||
| 133 | if (object != nullptr) { | ||
| 134 | // TODO(yuriks): Figure out the proper error handling for if this fails | ||
| 135 | handle = dst_table.Create(object).Unwrap(); | ||
| 136 | } | ||
| 137 | dst_cmdbuf[handle_write_offset++] = handle; | ||
| 138 | untranslated_index++; | ||
| 122 | } | 139 | } |
| 123 | dst_cmdbuf[i++] = handle; | 140 | break; |
| 141 | } | ||
| 142 | default: | ||
| 143 | UNIMPLEMENTED_MSG("Unsupported handle translation: 0x%08X", descriptor); | ||
| 124 | } | 144 | } |
| 125 | break; | ||
| 126 | } | ||
| 127 | default: | ||
| 128 | UNIMPLEMENTED_MSG("Unsupported handle translation: 0x%08X", descriptor); | ||
| 129 | } | 145 | } |
| 130 | } | 146 | } |
| 131 | 147 | ||
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: | |||
| 119 | */ | 119 | */ |
| 120 | void ClearIncomingObjects(); | 120 | void ClearIncomingObjects(); |
| 121 | 121 | ||
| 122 | void ParseCommandBuffer(u32_le* src_cmdbuf); | ||
| 123 | |||
| 122 | /// Populates this context with data from the requesting process/thread. | 124 | /// Populates this context with data from the requesting process/thread. |
| 123 | ResultCode PopulateFromIncomingCommandBuffer(const u32_le* src_cmdbuf, Process& src_process, | 125 | ResultCode PopulateFromIncomingCommandBuffer(u32_le* src_cmdbuf, Process& src_process, |
| 124 | HandleTable& src_table); | 126 | HandleTable& src_table); |
| 125 | /// Writes data from this context back to the requesting process/thread. | 127 | /// Writes data from this context back to the requesting process/thread. |
| 126 | ResultCode WriteToOutgoingCommandBuffer(u32_le* dst_cmdbuf, Process& dst_process, | 128 | ResultCode WriteToOutgoingCommandBuffer(u32_le* dst_cmdbuf, Process& dst_process, |
| 127 | HandleTable& dst_table) const; | 129 | HandleTable& dst_table); |
| 130 | |||
| 131 | u32_le GetCommand() const { | ||
| 132 | return command; | ||
| 133 | } | ||
| 134 | |||
| 135 | IPC::CommandType GetCommandType() const { | ||
| 136 | return command_header->type; | ||
| 137 | } | ||
| 138 | |||
| 139 | unsigned GetDataPayloadOffset() const { | ||
| 140 | return data_payload_offset; | ||
| 141 | } | ||
| 128 | 142 | ||
| 129 | private: | 143 | private: |
| 130 | std::array<u32, IPC::COMMAND_BUFFER_LENGTH> cmd_buf; | 144 | std::array<u32, IPC::COMMAND_BUFFER_LENGTH> cmd_buf; |
| 131 | SharedPtr<ServerSession> session; | 145 | SharedPtr<ServerSession> session; |
| 132 | // TODO(yuriks): Check common usage of this and optimize size accordingly | 146 | // TODO(yuriks): Check common usage of this and optimize size accordingly |
| 133 | boost::container::small_vector<SharedPtr<Object>, 8> request_handles; | 147 | boost::container::small_vector<SharedPtr<Object>, 8> request_handles; |
| 148 | |||
| 149 | std::unique_ptr<IPC::CommandHeader> command_header; | ||
| 150 | std::unique_ptr<IPC::HandleDescriptorHeader> handle_descriptor_header; | ||
| 151 | std::unique_ptr<IPC::DataPayloadHeader> data_payload_header; | ||
| 152 | |||
| 153 | unsigned data_payload_offset{}; | ||
| 154 | u32_le command{}; | ||
| 134 | }; | 155 | }; |
| 135 | 156 | ||
| 136 | } // namespace Kernel | 157 | } // 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 @@ | |||
| 1 | // Copyright 2017 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "common/logging/log.h" | ||
| 6 | #include "core/hle/ipc_helpers.h" | ||
| 7 | #include "core/hle/service/lm/lm.h" | ||
| 8 | |||
| 9 | namespace Service { | ||
| 10 | namespace LM { | ||
| 11 | |||
| 12 | void InstallInterfaces(SM::ServiceManager& service_manager) { | ||
| 13 | std::make_shared<LM>()->InstallAsService(service_manager); | ||
| 14 | } | ||
| 15 | |||
| 16 | /** | ||
| 17 | * SRV::Initialize service function | ||
| 18 | * Inputs: | ||
| 19 | * 0: 0x00000000 | ||
| 20 | * Outputs: | ||
| 21 | * 1: ResultCode | ||
| 22 | */ | ||
| 23 | void LM::Initialize(Kernel::HLERequestContext& ctx) { | ||
| 24 | IPC::RequestBuilder rb{ctx, 1}; | ||
| 25 | rb.Push(RESULT_SUCCESS); | ||
| 26 | LOG_WARNING(Service_SM, "(STUBBED) called"); | ||
| 27 | } | ||
| 28 | |||
| 29 | LM::LM() : ServiceFramework("lm") { | ||
| 30 | static const FunctionInfo functions[] = { | ||
| 31 | {0x00000000, &LM::Initialize, "Initialize"}, | ||
| 32 | {0x00000001, nullptr, "Unknown2"}, | ||
| 33 | {0x00000002, nullptr, "Unknown3"}, | ||
| 34 | {0x00000003, nullptr, "Unknown4"}, | ||
| 35 | {0x00000004, nullptr, "Unknown5"}, | ||
| 36 | }; | ||
| 37 | RegisterHandlers(functions); | ||
| 38 | } | ||
| 39 | |||
| 40 | LM::~LM() = default; | ||
| 41 | |||
| 42 | } // namespace LM | ||
| 43 | } // 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 @@ | |||
| 1 | // Copyright 2017 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include "core/hle/service/service.h" | ||
| 8 | |||
| 9 | namespace Service { | ||
| 10 | namespace LM { | ||
| 11 | |||
| 12 | class LM final : public ServiceFramework<LM> { | ||
| 13 | public: | ||
| 14 | explicit LM(); | ||
| 15 | ~LM(); | ||
| 16 | |||
| 17 | private: | ||
| 18 | void Initialize(Kernel::HLERequestContext& ctx); | ||
| 19 | }; | ||
| 20 | |||
| 21 | /// Registers all LM services with the specified service manager. | ||
| 22 | void InstallInterfaces(SM::ServiceManager& service_manager); | ||
| 23 | |||
| 24 | } // namespace LM | ||
| 25 | } // 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 @@ | |||
| 8 | #include "common/logging/log.h" | 8 | #include "common/logging/log.h" |
| 9 | #include "common/string_util.h" | 9 | #include "common/string_util.h" |
| 10 | #include "core/hle/ipc.h" | 10 | #include "core/hle/ipc.h" |
| 11 | #include "core/hle/ipc_helpers.h" | ||
| 11 | #include "core/hle/kernel/client_port.h" | 12 | #include "core/hle/kernel/client_port.h" |
| 12 | #include "core/hle/kernel/process.h" | 13 | #include "core/hle/kernel/process.h" |
| 13 | #include "core/hle/kernel/server_port.h" | 14 | #include "core/hle/kernel/server_port.h" |
| 14 | #include "core/hle/kernel/server_session.h" | 15 | #include "core/hle/kernel/server_session.h" |
| 16 | #include "core/hle/kernel/thread.h" | ||
| 15 | #include "core/hle/kernel/handle_table.h" | 17 | #include "core/hle/kernel/handle_table.h" |
| 16 | #include "core/hle/service/dsp_dsp.h" | 18 | #include "core/hle/service/dsp_dsp.h" |
| 17 | #include "core/hle/service/gsp_gpu.h" | 19 | #include "core/hle/service/gsp_gpu.h" |
| 18 | #include "core/hle/service/hid/hid.h" | 20 | #include "core/hle/service/hid/hid.h" |
| 21 | #include "core/hle/service/lm/lm.h" | ||
| 19 | #include "core/hle/service/service.h" | 22 | #include "core/hle/service/service.h" |
| 23 | #include "core/hle/service/sm/controller.h" | ||
| 20 | #include "core/hle/service/sm/sm.h" | 24 | #include "core/hle/service/sm/sm.h" |
| 21 | #include "core/hle/service/sm/srv.h" | ||
| 22 | 25 | ||
| 23 | using Kernel::ClientPort; | 26 | using Kernel::ClientPort; |
| 24 | using Kernel::ServerPort; | 27 | using Kernel::ServerPort; |
| @@ -46,42 +49,6 @@ static std::string MakeFunctionString(const char* name, const char* port_name, | |||
| 46 | return function_string; | 49 | return function_string; |
| 47 | } | 50 | } |
| 48 | 51 | ||
| 49 | Interface::Interface(u32 max_sessions) : max_sessions(max_sessions) {} | ||
| 50 | Interface::~Interface() = default; | ||
| 51 | |||
| 52 | void Interface::HandleSyncRequest(SharedPtr<ServerSession> server_session) { | ||
| 53 | // TODO(Subv): Make use of the server_session in the HLE service handlers to distinguish which | ||
| 54 | // session triggered each command. | ||
| 55 | |||
| 56 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 57 | auto itr = m_functions.find(cmd_buff[0]); | ||
| 58 | |||
| 59 | if (itr == m_functions.end() || itr->second.func == nullptr) { | ||
| 60 | std::string function_name = (itr == m_functions.end()) | ||
| 61 | ? Common::StringFromFormat("0x%08X", cmd_buff[0]) | ||
| 62 | : itr->second.name; | ||
| 63 | LOG_ERROR( | ||
| 64 | Service, "unknown / unimplemented %s", | ||
| 65 | MakeFunctionString(function_name.c_str(), GetPortName().c_str(), cmd_buff).c_str()); | ||
| 66 | |||
| 67 | // TODO(bunnei): Hack - ignore error | ||
| 68 | cmd_buff[1] = 0; | ||
| 69 | return; | ||
| 70 | } | ||
| 71 | LOG_TRACE(Service, "%s", | ||
| 72 | MakeFunctionString(itr->second.name, GetPortName().c_str(), cmd_buff).c_str()); | ||
| 73 | |||
| 74 | itr->second.func(this); | ||
| 75 | } | ||
| 76 | |||
| 77 | void Interface::Register(const FunctionInfo* functions, size_t n) { | ||
| 78 | m_functions.reserve(n); | ||
| 79 | for (size_t i = 0; i < n; ++i) { | ||
| 80 | // Usually this array is sorted by id already, so hint to instead at the end | ||
| 81 | m_functions.emplace_hint(m_functions.cend(), functions[i].id, functions[i]); | ||
| 82 | } | ||
| 83 | } | ||
| 84 | |||
| 85 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 52 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
| 86 | 53 | ||
| 87 | ServiceFrameworkBase::ServiceFrameworkBase(const char* service_name, u32 max_sessions, | 54 | ServiceFrameworkBase::ServiceFrameworkBase(const char* service_name, u32 max_sessions, |
| @@ -113,43 +80,66 @@ void ServiceFrameworkBase::RegisterHandlersBase(const FunctionInfoBase* function | |||
| 113 | } | 80 | } |
| 114 | } | 81 | } |
| 115 | 82 | ||
| 116 | void ServiceFrameworkBase::ReportUnimplementedFunction(u32* cmd_buf, const FunctionInfoBase* info) { | 83 | void ServiceFrameworkBase::ReportUnimplementedFunction(Kernel::HLERequestContext& ctx, const FunctionInfoBase* info) { |
| 117 | IPC::Header header{cmd_buf[0]}; | 84 | auto cmd_buf = ctx.CommandBuffer(); |
| 118 | int num_params = header.normal_params_size + header.translate_params_size; | ||
| 119 | std::string function_name = info == nullptr ? fmt::format("{:#08x}", cmd_buf[0]) : info->name; | 85 | std::string function_name = info == nullptr ? fmt::format("{:#08x}", cmd_buf[0]) : info->name; |
| 120 | 86 | ||
| 121 | fmt::MemoryWriter w; | 87 | fmt::MemoryWriter w; |
| 122 | w.write("function '{}': port='{}' cmd_buf={{[0]={:#x}", function_name, service_name, | 88 | w.write("function '{}': port='{}' cmd_buf={{[0]={:#x}", function_name, service_name, |
| 123 | cmd_buf[0]); | 89 | cmd_buf[0]); |
| 124 | for (int i = 1; i <= num_params; ++i) { | 90 | for (int i = 1; i <= 8; ++i) { |
| 125 | w.write(", [{}]={:#x}", i, cmd_buf[i]); | 91 | w.write(", [{}]={:#x}", i, cmd_buf[i]); |
| 126 | } | 92 | } |
| 127 | w << '}'; | 93 | w << '}'; |
| 128 | 94 | ||
| 129 | LOG_ERROR(Service, "unknown / unimplemented %s", w.c_str()); | 95 | LOG_ERROR(Service, "unknown / unimplemented %s", w.c_str()); |
| 130 | // TODO(bunnei): Hack - ignore error | 96 | // TODO(bunnei): Hack - ignore error |
| 131 | cmd_buf[1] = 0; | 97 | IPC::RequestBuilder rb{ ctx, 1 }; |
| 98 | rb.Push(RESULT_SUCCESS); | ||
| 132 | } | 99 | } |
| 133 | 100 | ||
| 134 | void ServiceFrameworkBase::HandleSyncRequest(SharedPtr<ServerSession> server_session) { | 101 | void ServiceFrameworkBase::InvokeRequest(Kernel::HLERequestContext& ctx) { |
| 135 | u32* cmd_buf = Kernel::GetCommandBuffer(); | 102 | auto itr = handlers.find(ctx.GetCommand()); |
| 136 | |||
| 137 | u32 header_code = cmd_buf[0]; | ||
| 138 | auto itr = handlers.find(header_code); | ||
| 139 | const FunctionInfoBase* info = itr == handlers.end() ? nullptr : &itr->second; | 103 | const FunctionInfoBase* info = itr == handlers.end() ? nullptr : &itr->second; |
| 140 | if (info == nullptr || info->handler_callback == nullptr) { | 104 | if (info == nullptr || info->handler_callback == nullptr) { |
| 141 | return ReportUnimplementedFunction(cmd_buf, info); | 105 | return ReportUnimplementedFunction(ctx, info); |
| 142 | } | 106 | } |
| 143 | 107 | ||
| 108 | LOG_TRACE(Service, "%s", | ||
| 109 | MakeFunctionString(info->name, GetServiceName().c_str(), ctx.CommandBuffer()).c_str()); | ||
| 110 | handler_invoker(this, info->handler_callback, ctx); | ||
| 111 | } | ||
| 112 | |||
| 113 | void ServiceFrameworkBase::HandleSyncRequest(SharedPtr<ServerSession> server_session) { | ||
| 114 | u32* cmd_buf = (u32*)Memory::GetPointer(Kernel::GetCurrentThread()->GetTLSAddress());; | ||
| 115 | |||
| 144 | // TODO(yuriks): The kernel should be the one handling this as part of translation after | 116 | // TODO(yuriks): The kernel should be the one handling this as part of translation after |
| 145 | // everything else is migrated | 117 | // everything else is migrated |
| 146 | Kernel::HLERequestContext context(std::move(server_session)); | 118 | Kernel::HLERequestContext context(std::move(server_session)); |
| 147 | context.PopulateFromIncomingCommandBuffer(cmd_buf, *Kernel::g_current_process, | 119 | context.PopulateFromIncomingCommandBuffer(cmd_buf, *Kernel::g_current_process, |
| 148 | Kernel::g_handle_table); | 120 | Kernel::g_handle_table); |
| 149 | 121 | ||
| 150 | LOG_TRACE(Service, "%s", | 122 | switch (context.GetCommandType()) { |
| 151 | MakeFunctionString(info->name, GetServiceName().c_str(), cmd_buf).c_str()); | 123 | case IPC::CommandType::Close: |
| 152 | handler_invoker(this, info->handler_callback, context); | 124 | { |
| 125 | IPC::RequestBuilder rb{context, 1}; | ||
| 126 | rb.Push(RESULT_SUCCESS); | ||
| 127 | break; | ||
| 128 | } | ||
| 129 | case IPC::CommandType::Control: | ||
| 130 | { | ||
| 131 | SM::g_service_manager->InvokeControlRequest(context); | ||
| 132 | break; | ||
| 133 | } | ||
| 134 | case IPC::CommandType::Request: | ||
| 135 | { | ||
| 136 | InvokeRequest(context); | ||
| 137 | break; | ||
| 138 | } | ||
| 139 | default: | ||
| 140 | UNIMPLEMENTED_MSG("command_type=%d", context.GetCommandType()); | ||
| 141 | } | ||
| 142 | |||
| 153 | context.WriteToOutgoingCommandBuffer(cmd_buf, *Kernel::g_current_process, | 143 | context.WriteToOutgoingCommandBuffer(cmd_buf, *Kernel::g_current_process, |
| 154 | Kernel::g_handle_table); | 144 | Kernel::g_handle_table); |
| 155 | } | 145 | } |
| @@ -162,33 +152,14 @@ void AddNamedPort(std::string name, SharedPtr<ClientPort> port) { | |||
| 162 | g_kernel_named_ports.emplace(std::move(name), std::move(port)); | 152 | g_kernel_named_ports.emplace(std::move(name), std::move(port)); |
| 163 | } | 153 | } |
| 164 | 154 | ||
| 165 | static void AddNamedPort(Interface* interface_) { | ||
| 166 | SharedPtr<ServerPort> server_port; | ||
| 167 | SharedPtr<ClientPort> client_port; | ||
| 168 | std::tie(server_port, client_port) = | ||
| 169 | ServerPort::CreatePortPair(interface_->GetMaxSessions(), interface_->GetPortName()); | ||
| 170 | |||
| 171 | server_port->SetHleHandler(std::shared_ptr<Interface>(interface_)); | ||
| 172 | AddNamedPort(interface_->GetPortName(), std::move(client_port)); | ||
| 173 | } | ||
| 174 | |||
| 175 | void AddService(Interface* interface_) { | ||
| 176 | auto server_port = | ||
| 177 | SM::g_service_manager | ||
| 178 | ->RegisterService(interface_->GetPortName(), interface_->GetMaxSessions()) | ||
| 179 | .Unwrap(); | ||
| 180 | server_port->SetHleHandler(std::shared_ptr<Interface>(interface_)); | ||
| 181 | } | ||
| 182 | |||
| 183 | /// Initialize ServiceManager | 155 | /// Initialize ServiceManager |
| 184 | void Init() { | 156 | void Init() { |
| 185 | SM::g_service_manager = std::make_shared<SM::ServiceManager>(); | 157 | SM::g_service_manager = std::make_shared<SM::ServiceManager>(); |
| 186 | SM::ServiceManager::InstallInterfaces(SM::g_service_manager); | 158 | SM::ServiceManager::InstallInterfaces(SM::g_service_manager); |
| 187 | 159 | ||
| 188 | HID::Init(); | 160 | LM::InstallInterfaces(*SM::g_service_manager); |
| 189 | 161 | ||
| 190 | AddService(new DSP_DSP::Interface); | 162 | HID::Init(); |
| 191 | AddService(new GSP::GSP_GPU); | ||
| 192 | 163 | ||
| 193 | LOG_DEBUG(Service, "initialized OK"); | 164 | LOG_DEBUG(Service, "initialized OK"); |
| 194 | } | 165 | } |
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 { | |||
| 20 | class ClientPort; | 20 | class ClientPort; |
| 21 | class ServerPort; | 21 | class ServerPort; |
| 22 | class ServerSession; | 22 | class ServerSession; |
| 23 | class HLERequestContext; | ||
| 23 | } | 24 | } |
| 24 | 25 | ||
| 25 | namespace Service { | 26 | namespace Service { |
| @@ -33,83 +34,6 @@ static const int kMaxPortSize = 8; ///< Maximum size of a port name (8 character | |||
| 33 | static const u32 DefaultMaxSessions = 10; | 34 | static const u32 DefaultMaxSessions = 10; |
| 34 | 35 | ||
| 35 | /** | 36 | /** |
| 36 | * Framework for implementing HLE service handlers which dispatch incoming SyncRequests based on a | ||
| 37 | * table mapping header ids to handler functions. | ||
| 38 | * | ||
| 39 | * @deprecated Use ServiceFramework for new services instead. It allows services to be stateful and | ||
| 40 | * is more extensible going forward. | ||
| 41 | */ | ||
| 42 | class Interface : public Kernel::SessionRequestHandler { | ||
| 43 | public: | ||
| 44 | /** | ||
| 45 | * Creates an HLE interface with the specified max sessions. | ||
| 46 | * @param max_sessions Maximum number of sessions that can be | ||
| 47 | * connected to this service at the same time. | ||
| 48 | */ | ||
| 49 | Interface(u32 max_sessions = DefaultMaxSessions); | ||
| 50 | |||
| 51 | virtual ~Interface(); | ||
| 52 | |||
| 53 | std::string GetName() const { | ||
| 54 | return GetPortName(); | ||
| 55 | } | ||
| 56 | |||
| 57 | virtual void SetVersion(u32 raw_version) { | ||
| 58 | version.raw = raw_version; | ||
| 59 | } | ||
| 60 | |||
| 61 | /** | ||
| 62 | * Gets the maximum allowed number of sessions that can be connected to this service | ||
| 63 | * at the same time. | ||
| 64 | * @returns The maximum number of connections allowed. | ||
| 65 | */ | ||
| 66 | u32 GetMaxSessions() const { | ||
| 67 | return max_sessions; | ||
| 68 | } | ||
| 69 | |||
| 70 | typedef void (*Function)(Interface*); | ||
| 71 | |||
| 72 | struct FunctionInfo { | ||
| 73 | u32 id; | ||
| 74 | Function func; | ||
| 75 | const char* name; | ||
| 76 | }; | ||
| 77 | |||
| 78 | /** | ||
| 79 | * Gets the string name used by CTROS for a service | ||
| 80 | * @return Port name of service | ||
| 81 | */ | ||
| 82 | virtual std::string GetPortName() const { | ||
| 83 | return "[UNKNOWN SERVICE PORT]"; | ||
| 84 | } | ||
| 85 | |||
| 86 | protected: | ||
| 87 | void HandleSyncRequest(Kernel::SharedPtr<Kernel::ServerSession> server_session) override; | ||
| 88 | |||
| 89 | /** | ||
| 90 | * Registers the functions in the service | ||
| 91 | */ | ||
| 92 | template <size_t N> | ||
| 93 | inline void Register(const FunctionInfo (&functions)[N]) { | ||
| 94 | Register(functions, N); | ||
| 95 | } | ||
| 96 | |||
| 97 | void Register(const FunctionInfo* functions, size_t n); | ||
| 98 | |||
| 99 | union { | ||
| 100 | u32 raw; | ||
| 101 | BitField<0, 8, u32> major; | ||
| 102 | BitField<8, 8, u32> minor; | ||
| 103 | BitField<16, 8, u32> build; | ||
| 104 | BitField<24, 8, u32> revision; | ||
| 105 | } version = {}; | ||
| 106 | |||
| 107 | private: | ||
| 108 | u32 max_sessions; ///< Maximum number of concurrent sessions that this service can handle. | ||
| 109 | boost::container::flat_map<u32, FunctionInfo> m_functions; | ||
| 110 | }; | ||
| 111 | |||
| 112 | /** | ||
| 113 | * This is an non-templated base of ServiceFramework to reduce code bloat and compilation times, it | 37 | * This is an non-templated base of ServiceFramework to reduce code bloat and compilation times, it |
| 114 | * is not meant to be used directly. | 38 | * is not meant to be used directly. |
| 115 | * | 39 | * |
| @@ -135,6 +59,8 @@ public: | |||
| 135 | /// Creates a port pair and registers it on the kernel's global port registry. | 59 | /// Creates a port pair and registers it on the kernel's global port registry. |
| 136 | void InstallAsNamedPort(); | 60 | void InstallAsNamedPort(); |
| 137 | 61 | ||
| 62 | void InvokeRequest(Kernel::HLERequestContext& ctx); | ||
| 63 | |||
| 138 | void HandleSyncRequest(Kernel::SharedPtr<Kernel::ServerSession> server_session) override; | 64 | void HandleSyncRequest(Kernel::SharedPtr<Kernel::ServerSession> server_session) override; |
| 139 | 65 | ||
| 140 | protected: | 66 | protected: |
| @@ -159,7 +85,7 @@ private: | |||
| 159 | ~ServiceFrameworkBase(); | 85 | ~ServiceFrameworkBase(); |
| 160 | 86 | ||
| 161 | void RegisterHandlersBase(const FunctionInfoBase* functions, size_t n); | 87 | void RegisterHandlersBase(const FunctionInfoBase* functions, size_t n); |
| 162 | void ReportUnimplementedFunction(u32* cmd_buf, const FunctionInfoBase* info); | 88 | void ReportUnimplementedFunction(Kernel::HLERequestContext& ctx, const FunctionInfoBase* info); |
| 163 | 89 | ||
| 164 | /// Identifier string used to connect to the service. | 90 | /// Identifier string used to connect to the service. |
| 165 | std::string service_name; | 91 | std::string service_name; |
| @@ -260,7 +186,5 @@ extern std::unordered_map<std::string, Kernel::SharedPtr<Kernel::ClientPort>> g_ | |||
| 260 | 186 | ||
| 261 | /// Adds a port to the named port table | 187 | /// Adds a port to the named port table |
| 262 | void AddNamedPort(std::string name, Kernel::SharedPtr<Kernel::ClientPort> port); | 188 | void AddNamedPort(std::string name, Kernel::SharedPtr<Kernel::ClientPort> port); |
| 263 | /// Adds a service to the services table | ||
| 264 | void AddService(Interface* interface_); | ||
| 265 | 189 | ||
| 266 | } // namespace | 190 | } // 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 @@ | |||
| 1 | // Copyright 2017 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "common/logging/log.h" | ||
| 6 | #include "core/hle/ipc_helpers.h" | ||
| 7 | #include "core/hle/service/sm/controller.h" | ||
| 8 | |||
| 9 | namespace Service { | ||
| 10 | namespace SM { | ||
| 11 | |||
| 12 | /** | ||
| 13 | * Controller::QueryPointerBufferSize service function | ||
| 14 | * Inputs: | ||
| 15 | * 0: 0x00000003 | ||
| 16 | * Outputs: | ||
| 17 | * 1: ResultCode | ||
| 18 | * 3: Size of memory | ||
| 19 | */ | ||
| 20 | void Controller::QueryPointerBufferSize(Kernel::HLERequestContext& ctx) { | ||
| 21 | IPC::RequestBuilder rb{ctx, 2}; | ||
| 22 | rb.Push(RESULT_SUCCESS); | ||
| 23 | rb.Push(0x0U); | ||
| 24 | rb.Push(0x500U); | ||
| 25 | LOG_WARNING(Service, "(STUBBED) called"); | ||
| 26 | } | ||
| 27 | |||
| 28 | Controller::Controller() : ServiceFramework("IpcController") { | ||
| 29 | static const FunctionInfo functions[] = { | ||
| 30 | {0x00000000, nullptr, "ConvertSessionToDomain"}, | ||
| 31 | {0x00000001, nullptr, "ConvertDomainToSession"}, | ||
| 32 | {0x00000002, nullptr, "DuplicateSession"}, | ||
| 33 | {0x00000003, &Controller::QueryPointerBufferSize, "QueryPointerBufferSize"}, | ||
| 34 | {0x00000004, nullptr, "DuplicateSessionEx"}, | ||
| 35 | }; | ||
| 36 | RegisterHandlers(functions); | ||
| 37 | } | ||
| 38 | |||
| 39 | Controller::~Controller() = default; | ||
| 40 | |||
| 41 | } // namespace SM | ||
| 42 | } // 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 @@ | |||
| 1 | // Copyright 2017 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include "core/hle/service/service.h" | ||
| 8 | |||
| 9 | namespace Service { | ||
| 10 | namespace SM { | ||
| 11 | |||
| 12 | class Controller final : public ServiceFramework<Controller> { | ||
| 13 | public: | ||
| 14 | explicit Controller(); | ||
| 15 | ~Controller(); | ||
| 16 | |||
| 17 | private: | ||
| 18 | void QueryPointerBufferSize(Kernel::HLERequestContext& ctx); | ||
| 19 | }; | ||
| 20 | |||
| 21 | } // namespace SM | ||
| 22 | } // 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 @@ | |||
| 4 | 4 | ||
| 5 | #include <tuple> | 5 | #include <tuple> |
| 6 | #include "common/assert.h" | 6 | #include "common/assert.h" |
| 7 | #include "core/hle/ipc_helpers.h" | ||
| 7 | #include "core/hle/kernel/client_port.h" | 8 | #include "core/hle/kernel/client_port.h" |
| 8 | #include "core/hle/kernel/client_session.h" | 9 | #include "core/hle/kernel/client_session.h" |
| 9 | #include "core/hle/kernel/server_port.h" | 10 | #include "core/hle/kernel/server_port.h" |
| 10 | #include "core/hle/result.h" | 11 | #include "core/hle/result.h" |
| 12 | #include "core/hle/service/sm/controller.h" | ||
| 11 | #include "core/hle/service/sm/sm.h" | 13 | #include "core/hle/service/sm/sm.h" |
| 12 | #include "core/hle/service/sm/srv.h" | ||
| 13 | 14 | ||
| 14 | namespace Service { | 15 | namespace Service { |
| 15 | namespace SM { | 16 | namespace SM { |
| 16 | 17 | ||
| 18 | void ServiceManager::InvokeControlRequest(Kernel::HLERequestContext& context) { | ||
| 19 | controller_interface->InvokeRequest(context); | ||
| 20 | } | ||
| 21 | |||
| 17 | static ResultCode ValidateServiceName(const std::string& name) { | 22 | static ResultCode ValidateServiceName(const std::string& name) { |
| 18 | if (name.size() <= 0 || name.size() > 8) { | 23 | if (name.size() <= 0 || name.size() > 8) { |
| 19 | return ERR_INVALID_NAME_SIZE; | 24 | return ERR_INVALID_NAME_SIZE; |
| @@ -25,11 +30,12 @@ static ResultCode ValidateServiceName(const std::string& name) { | |||
| 25 | } | 30 | } |
| 26 | 31 | ||
| 27 | void ServiceManager::InstallInterfaces(std::shared_ptr<ServiceManager> self) { | 32 | void ServiceManager::InstallInterfaces(std::shared_ptr<ServiceManager> self) { |
| 28 | ASSERT(self->srv_interface.expired()); | 33 | ASSERT(self->sm_interface.expired()); |
| 29 | 34 | ||
| 30 | auto srv = std::make_shared<SRV>(self); | 35 | auto sm = std::make_shared<SM>(self); |
| 31 | srv->InstallAsNamedPort(); | 36 | sm->InstallAsNamedPort(); |
| 32 | self->srv_interface = srv; | 37 | self->sm_interface = sm; |
| 38 | self->controller_interface = std::make_unique<Controller>(); | ||
| 33 | } | 39 | } |
| 34 | 40 | ||
| 35 | ResultVal<Kernel::SharedPtr<Kernel::ServerPort>> ServiceManager::RegisterService( | 41 | ResultVal<Kernel::SharedPtr<Kernel::ServerPort>> ServiceManager::RegisterService( |
| @@ -69,5 +75,80 @@ ResultVal<Kernel::SharedPtr<Kernel::ClientSession>> ServiceManager::ConnectToSer | |||
| 69 | 75 | ||
| 70 | std::shared_ptr<ServiceManager> g_service_manager; | 76 | std::shared_ptr<ServiceManager> g_service_manager; |
| 71 | 77 | ||
| 78 | /** | ||
| 79 | * SM::Initialize service function | ||
| 80 | * Inputs: | ||
| 81 | * 0: 0x00000000 | ||
| 82 | * Outputs: | ||
| 83 | * 1: ResultCode | ||
| 84 | */ | ||
| 85 | void SM::Initialize(Kernel::HLERequestContext& ctx) { | ||
| 86 | IPC::RequestBuilder rb{ctx, 1}; | ||
| 87 | rb.Push(RESULT_SUCCESS); | ||
| 88 | LOG_DEBUG(Service_SM, "called"); | ||
| 89 | } | ||
| 90 | |||
| 91 | /** | ||
| 92 | * SM::GetServiceHandle service function | ||
| 93 | * Inputs: | ||
| 94 | * 0: 0x00000001 | ||
| 95 | * 1: Unknown | ||
| 96 | * 2: Unknown | ||
| 97 | * 3-4: 8-byte UTF-8 service name | ||
| 98 | * Outputs: | ||
| 99 | * 1: ResultCode | ||
| 100 | * 3: Service handle | ||
| 101 | */ | ||
| 102 | void SM::GetService(Kernel::HLERequestContext& ctx) { | ||
| 103 | IPC::RequestParser rp{ctx}; | ||
| 104 | u32 unk1 = rp.Pop<u32>(); | ||
| 105 | u32 unk2 = rp.Pop<u32>(); | ||
| 106 | auto name_buf = rp.PopRaw<std::array<char, 6>>(); | ||
| 107 | std::string name(name_buf.data()); | ||
| 108 | |||
| 109 | // TODO(yuriks): Permission checks go here | ||
| 110 | |||
| 111 | auto client_port = service_manager->GetServicePort(name); | ||
| 112 | if (client_port.Failed()) { | ||
| 113 | IPC::RequestBuilder rb = rp.MakeBuilder(1, 0, 0); | ||
| 114 | rb.Push(client_port.Code()); | ||
| 115 | LOG_ERROR(Service_SM, "called service=%s -> error 0x%08X", name.c_str(), | ||
| 116 | client_port.Code().raw); | ||
| 117 | return; | ||
| 118 | } | ||
| 119 | |||
| 120 | auto session = client_port.Unwrap()->Connect(); | ||
| 121 | if (session.Succeeded()) { | ||
| 122 | LOG_DEBUG(Service_SM, "called service=%s -> session=%u", name.c_str(), | ||
| 123 | (*session)->GetObjectId()); | ||
| 124 | IPC::RequestBuilder rb = rp.MakeBuilder(1, 0, 1); | ||
| 125 | rb.Push(session.Code()); | ||
| 126 | rb.PushObjects(std::move(session).Unwrap()); | ||
| 127 | } else if (session.Code() == Kernel::ERR_MAX_CONNECTIONS_REACHED /*&& return_port_on_failure*/) { | ||
| 128 | LOG_WARNING(Service_SM, "called service=%s -> ERR_MAX_CONNECTIONS_REACHED, *port*=%u", | ||
| 129 | name.c_str(), (*client_port)->GetObjectId()); | ||
| 130 | IPC::RequestBuilder rb = rp.MakeBuilder(1, 0, 1); | ||
| 131 | rb.Push(ERR_MAX_CONNECTIONS_REACHED); | ||
| 132 | rb.PushObjects(std::move(client_port).Unwrap()); | ||
| 133 | } else { | ||
| 134 | LOG_ERROR(Service_SM, "called service=%s -> error 0x%08X", name.c_str(), session.Code()); | ||
| 135 | IPC::RequestBuilder rb = rp.MakeBuilder(1, 0, 0); | ||
| 136 | rb.Push(session.Code()); | ||
| 137 | } | ||
| 138 | } | ||
| 139 | |||
| 140 | SM::SM(std::shared_ptr<ServiceManager> service_manager) | ||
| 141 | : ServiceFramework("sm:", 4), service_manager(std::move(service_manager)) { | ||
| 142 | static const FunctionInfo functions[] = { | ||
| 143 | {0x00000000, &SM::Initialize, "Initialize"}, | ||
| 144 | {0x00000001, &SM::GetService, "GetService"}, | ||
| 145 | {0x00000002, nullptr, "RegisterService"}, | ||
| 146 | {0x00000003, nullptr, "UnregisterService"}, | ||
| 147 | }; | ||
| 148 | RegisterHandlers(functions); | ||
| 149 | } | ||
| 150 | |||
| 151 | SM::~SM() = default; | ||
| 152 | |||
| 72 | } // namespace SM | 153 | } // namespace SM |
| 73 | } // namespace Service | 154 | } // 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; | |||
| 20 | namespace Service { | 20 | namespace Service { |
| 21 | namespace SM { | 21 | namespace SM { |
| 22 | 22 | ||
| 23 | class SRV; | 23 | /// Interface to "sm:" service |
| 24 | class SM final : public ServiceFramework<SM> { | ||
| 25 | public: | ||
| 26 | explicit SM(std::shared_ptr<ServiceManager> service_manager); | ||
| 27 | ~SM(); | ||
| 28 | |||
| 29 | private: | ||
| 30 | void Initialize(Kernel::HLERequestContext& ctx); | ||
| 31 | void GetService(Kernel::HLERequestContext& ctx); | ||
| 32 | |||
| 33 | std::shared_ptr<ServiceManager> service_manager; | ||
| 34 | }; | ||
| 35 | |||
| 36 | class Controller; | ||
| 24 | 37 | ||
| 25 | constexpr ResultCode ERR_SERVICE_NOT_REGISTERED(1, ErrorModule::SRV, ErrorSummary::WouldBlock, | 38 | constexpr ResultCode ERR_SERVICE_NOT_REGISTERED(1, ErrorModule::SRV, ErrorSummary::WouldBlock, |
| 26 | ErrorLevel::Temporary); // 0xD0406401 | 39 | ErrorLevel::Temporary); // 0xD0406401 |
| @@ -45,8 +58,11 @@ public: | |||
| 45 | ResultVal<Kernel::SharedPtr<Kernel::ClientPort>> GetServicePort(const std::string& name); | 58 | ResultVal<Kernel::SharedPtr<Kernel::ClientPort>> GetServicePort(const std::string& name); |
| 46 | ResultVal<Kernel::SharedPtr<Kernel::ClientSession>> ConnectToService(const std::string& name); | 59 | ResultVal<Kernel::SharedPtr<Kernel::ClientSession>> ConnectToService(const std::string& name); |
| 47 | 60 | ||
| 61 | void InvokeControlRequest(Kernel::HLERequestContext& context); | ||
| 62 | |||
| 48 | private: | 63 | private: |
| 49 | std::weak_ptr<SRV> srv_interface; | 64 | std::weak_ptr<SM> sm_interface; |
| 65 | std::unique_ptr<Controller> controller_interface; | ||
| 50 | 66 | ||
| 51 | /// Map of registered services, retrieved using GetServicePort or ConnectToService. | 67 | /// Map of registered services, retrieved using GetServicePort or ConnectToService. |
| 52 | std::unordered_map<std::string, Kernel::SharedPtr<Kernel::ClientPort>> registered_services; | 68 | std::unordered_map<std::string, Kernel::SharedPtr<Kernel::ClientPort>> 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 @@ | |||
| 1 | // Copyright 2016 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <tuple> | ||
| 6 | |||
| 7 | #include "common/common_types.h" | ||
| 8 | #include "common/logging/log.h" | ||
| 9 | #include "core/hle/ipc.h" | ||
| 10 | #include "core/hle/ipc_helpers.h" | ||
| 11 | #include "core/hle/kernel/client_port.h" | ||
| 12 | #include "core/hle/kernel/client_session.h" | ||
| 13 | #include "core/hle/kernel/errors.h" | ||
| 14 | #include "core/hle/kernel/hle_ipc.h" | ||
| 15 | #include "core/hle/kernel/semaphore.h" | ||
| 16 | #include "core/hle/kernel/server_port.h" | ||
| 17 | #include "core/hle/kernel/server_session.h" | ||
| 18 | #include "core/hle/service/sm/sm.h" | ||
| 19 | #include "core/hle/service/sm/srv.h" | ||
| 20 | |||
| 21 | namespace Service { | ||
| 22 | namespace SM { | ||
| 23 | |||
| 24 | constexpr int MAX_PENDING_NOTIFICATIONS = 16; | ||
| 25 | |||
| 26 | /** | ||
| 27 | * SRV::RegisterClient service function | ||
| 28 | * Inputs: | ||
| 29 | * 0: 0x00010002 | ||
| 30 | * 1: ProcessId Header (must be 0x20) | ||
| 31 | * Outputs: | ||
| 32 | * 0: 0x00010040 | ||
| 33 | * 1: ResultCode | ||
| 34 | */ | ||
| 35 | void SRV::RegisterClient(Kernel::HLERequestContext& ctx) { | ||
| 36 | IPC::RequestParser rp(ctx, 0x1, 0, 2); | ||
| 37 | |||
| 38 | u32 pid_descriptor = rp.Pop<u32>(); | ||
| 39 | if (pid_descriptor != IPC::CallingPidDesc()) { | ||
| 40 | IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||
| 41 | rb.Push(IPC::ERR_INVALID_BUFFER_DESCRIPTOR); | ||
| 42 | return; | ||
| 43 | } | ||
| 44 | u32 caller_pid = rp.Pop<u32>(); | ||
| 45 | |||
| 46 | IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||
| 47 | rb.Push(RESULT_SUCCESS); | ||
| 48 | LOG_WARNING(Service_SRV, "(STUBBED) called"); | ||
| 49 | } | ||
| 50 | |||
| 51 | /** | ||
| 52 | * SRV::EnableNotification service function | ||
| 53 | * Inputs: | ||
| 54 | * 0: 0x00020000 | ||
| 55 | * Outputs: | ||
| 56 | * 0: 0x00020042 | ||
| 57 | * 1: ResultCode | ||
| 58 | * 2: Translation descriptor: 0x20 | ||
| 59 | * 3: Handle to semaphore signaled on process notification | ||
| 60 | */ | ||
| 61 | void SRV::EnableNotification(Kernel::HLERequestContext& ctx) { | ||
| 62 | IPC::RequestParser rp(ctx, 0x2, 0, 0); | ||
| 63 | |||
| 64 | notification_semaphore = | ||
| 65 | Kernel::Semaphore::Create(0, MAX_PENDING_NOTIFICATIONS, 0, "SRV:Notification").Unwrap(); | ||
| 66 | |||
| 67 | IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); | ||
| 68 | rb.Push(RESULT_SUCCESS); | ||
| 69 | rb.PushObjects(notification_semaphore); | ||
| 70 | LOG_WARNING(Service_SRV, "(STUBBED) called"); | ||
| 71 | } | ||
| 72 | |||
| 73 | /** | ||
| 74 | * SRV::GetServiceHandle service function | ||
| 75 | * Inputs: | ||
| 76 | * 0: 0x00050100 | ||
| 77 | * 1-2: 8-byte UTF-8 service name | ||
| 78 | * 3: Name length | ||
| 79 | * 4: Flags (bit0: if not set, return port-handle if session-handle unavailable) | ||
| 80 | * Outputs: | ||
| 81 | * 1: ResultCode | ||
| 82 | * 3: Service handle | ||
| 83 | */ | ||
| 84 | void SRV::GetServiceHandle(Kernel::HLERequestContext& ctx) { | ||
| 85 | IPC::RequestParser rp(ctx, 0x5, 4, 0); | ||
| 86 | auto name_buf = rp.PopRaw<std::array<char, 8>>(); | ||
| 87 | size_t name_len = rp.Pop<u32>(); | ||
| 88 | u32 flags = rp.Pop<u32>(); | ||
| 89 | |||
| 90 | bool return_port_on_failure = (flags & 1) == 0; | ||
| 91 | |||
| 92 | if (name_len > Service::kMaxPortSize) { | ||
| 93 | IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||
| 94 | rb.Push(ERR_INVALID_NAME_SIZE); | ||
| 95 | LOG_ERROR(Service_SRV, "called name_len=0x%X -> ERR_INVALID_NAME_SIZE", name_len); | ||
| 96 | return; | ||
| 97 | } | ||
| 98 | std::string name(name_buf.data(), name_len); | ||
| 99 | |||
| 100 | // TODO(yuriks): Permission checks go here | ||
| 101 | |||
| 102 | auto client_port = service_manager->GetServicePort(name); | ||
| 103 | if (client_port.Failed()) { | ||
| 104 | IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||
| 105 | rb.Push(client_port.Code()); | ||
| 106 | LOG_ERROR(Service_SRV, "called service=%s -> error 0x%08X", name.c_str(), | ||
| 107 | client_port.Code().raw); | ||
| 108 | return; | ||
| 109 | } | ||
| 110 | |||
| 111 | auto session = client_port.Unwrap()->Connect(); | ||
| 112 | if (session.Succeeded()) { | ||
| 113 | LOG_DEBUG(Service_SRV, "called service=%s -> session=%u", name.c_str(), | ||
| 114 | (*session)->GetObjectId()); | ||
| 115 | IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); | ||
| 116 | rb.Push(session.Code()); | ||
| 117 | rb.PushObjects(std::move(session).Unwrap()); | ||
| 118 | } else if (session.Code() == Kernel::ERR_MAX_CONNECTIONS_REACHED && return_port_on_failure) { | ||
| 119 | LOG_WARNING(Service_SRV, "called service=%s -> ERR_MAX_CONNECTIONS_REACHED, *port*=%u", | ||
| 120 | name.c_str(), (*client_port)->GetObjectId()); | ||
| 121 | IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); | ||
| 122 | rb.Push(ERR_MAX_CONNECTIONS_REACHED); | ||
| 123 | rb.PushObjects(std::move(client_port).Unwrap()); | ||
| 124 | } else { | ||
| 125 | LOG_ERROR(Service_SRV, "called service=%s -> error 0x%08X", name.c_str(), session.Code()); | ||
| 126 | IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||
| 127 | rb.Push(session.Code()); | ||
| 128 | } | ||
| 129 | } | ||
| 130 | |||
| 131 | /** | ||
| 132 | * SRV::Subscribe service function | ||
| 133 | * Inputs: | ||
| 134 | * 0: 0x00090040 | ||
| 135 | * 1: Notification ID | ||
| 136 | * Outputs: | ||
| 137 | * 0: 0x00090040 | ||
| 138 | * 1: ResultCode | ||
| 139 | */ | ||
| 140 | void SRV::Subscribe(Kernel::HLERequestContext& ctx) { | ||
| 141 | IPC::RequestParser rp(ctx, 0x9, 1, 0); | ||
| 142 | u32 notification_id = rp.Pop<u32>(); | ||
| 143 | |||
| 144 | IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||
| 145 | rb.Push(RESULT_SUCCESS); | ||
| 146 | LOG_WARNING(Service_SRV, "(STUBBED) called, notification_id=0x%X", notification_id); | ||
| 147 | } | ||
| 148 | |||
| 149 | /** | ||
| 150 | * SRV::Unsubscribe service function | ||
| 151 | * Inputs: | ||
| 152 | * 0: 0x000A0040 | ||
| 153 | * 1: Notification ID | ||
| 154 | * Outputs: | ||
| 155 | * 0: 0x000A0040 | ||
| 156 | * 1: ResultCode | ||
| 157 | */ | ||
| 158 | void SRV::Unsubscribe(Kernel::HLERequestContext& ctx) { | ||
| 159 | IPC::RequestParser rp(ctx, 0xA, 1, 0); | ||
| 160 | u32 notification_id = rp.Pop<u32>(); | ||
| 161 | |||
| 162 | IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||
| 163 | rb.Push(RESULT_SUCCESS); | ||
| 164 | LOG_WARNING(Service_SRV, "(STUBBED) called, notification_id=0x%X", notification_id); | ||
| 165 | } | ||
| 166 | |||
| 167 | /** | ||
| 168 | * SRV::PublishToSubscriber service function | ||
| 169 | * Inputs: | ||
| 170 | * 0: 0x000C0080 | ||
| 171 | * 1: Notification ID | ||
| 172 | * 2: Flags (bit0: only fire if not fired, bit1: report errors) | ||
| 173 | * Outputs: | ||
| 174 | * 0: 0x000C0040 | ||
| 175 | * 1: ResultCode | ||
| 176 | */ | ||
| 177 | void SRV::PublishToSubscriber(Kernel::HLERequestContext& ctx) { | ||
| 178 | IPC::RequestParser rp(ctx, 0xC, 2, 0); | ||
| 179 | u32 notification_id = rp.Pop<u32>(); | ||
| 180 | u8 flags = rp.Pop<u8>(); | ||
| 181 | |||
| 182 | IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||
| 183 | rb.Push(RESULT_SUCCESS); | ||
| 184 | LOG_WARNING(Service_SRV, "(STUBBED) called, notification_id=0x%X, flags=%u", notification_id, | ||
| 185 | flags); | ||
| 186 | } | ||
| 187 | |||
| 188 | void SRV::RegisterService(Kernel::HLERequestContext& ctx) { | ||
| 189 | IPC::RequestParser rp(ctx, 0x3, 4, 0); | ||
| 190 | |||
| 191 | auto name_buf = rp.PopRaw<std::array<char, 8>>(); | ||
| 192 | size_t name_len = rp.Pop<u32>(); | ||
| 193 | u32 max_sessions = rp.Pop<u32>(); | ||
| 194 | |||
| 195 | std::string name(name_buf.data(), std::min(name_len, name_buf.size())); | ||
| 196 | |||
| 197 | auto port = service_manager->RegisterService(name, max_sessions); | ||
| 198 | |||
| 199 | if (port.Failed()) { | ||
| 200 | IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||
| 201 | rb.Push(port.Code()); | ||
| 202 | LOG_ERROR(Service_SRV, "called service=%s -> error 0x%08X", name.c_str(), port.Code().raw); | ||
| 203 | return; | ||
| 204 | } | ||
| 205 | |||
| 206 | IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); | ||
| 207 | rb.Push(RESULT_SUCCESS); | ||
| 208 | rb.PushObjects(port.Unwrap()); | ||
| 209 | } | ||
| 210 | |||
| 211 | SRV::SRV(std::shared_ptr<ServiceManager> service_manager) | ||
| 212 | : ServiceFramework("srv:", 4), service_manager(std::move(service_manager)) { | ||
| 213 | static const FunctionInfo functions[] = { | ||
| 214 | {0x00010002, &SRV::RegisterClient, "RegisterClient"}, | ||
| 215 | {0x00020000, &SRV::EnableNotification, "EnableNotification"}, | ||
| 216 | {0x00030100, &SRV::RegisterService, "RegisterService"}, | ||
| 217 | {0x000400C0, nullptr, "UnregisterService"}, | ||
| 218 | {0x00050100, &SRV::GetServiceHandle, "GetServiceHandle"}, | ||
| 219 | {0x000600C2, nullptr, "RegisterPort"}, | ||
| 220 | {0x000700C0, nullptr, "UnregisterPort"}, | ||
| 221 | {0x00080100, nullptr, "GetPort"}, | ||
| 222 | {0x00090040, &SRV::Subscribe, "Subscribe"}, | ||
| 223 | {0x000A0040, &SRV::Unsubscribe, "Unsubscribe"}, | ||
| 224 | {0x000B0000, nullptr, "ReceiveNotification"}, | ||
| 225 | {0x000C0080, &SRV::PublishToSubscriber, "PublishToSubscriber"}, | ||
| 226 | {0x000D0040, nullptr, "PublishAndGetSubscriber"}, | ||
| 227 | {0x000E00C0, nullptr, "IsServiceRegistered"}, | ||
| 228 | }; | ||
| 229 | RegisterHandlers(functions); | ||
| 230 | } | ||
| 231 | |||
| 232 | SRV::~SRV() = default; | ||
| 233 | |||
| 234 | } // namespace SM | ||
| 235 | } // 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 @@ | |||
| 1 | // Copyright 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include "core/hle/kernel/kernel.h" | ||
| 8 | #include "core/hle/service/service.h" | ||
| 9 | |||
| 10 | namespace Kernel { | ||
| 11 | class HLERequestContext; | ||
| 12 | class Semaphore; | ||
| 13 | } | ||
| 14 | |||
| 15 | namespace Service { | ||
| 16 | namespace SM { | ||
| 17 | |||
| 18 | /// Interface to "srv:" service | ||
| 19 | class SRV final : public ServiceFramework<SRV> { | ||
| 20 | public: | ||
| 21 | explicit SRV(std::shared_ptr<ServiceManager> service_manager); | ||
| 22 | ~SRV(); | ||
| 23 | |||
| 24 | private: | ||
| 25 | void RegisterClient(Kernel::HLERequestContext& ctx); | ||
| 26 | void EnableNotification(Kernel::HLERequestContext& ctx); | ||
| 27 | void GetServiceHandle(Kernel::HLERequestContext& ctx); | ||
| 28 | void Subscribe(Kernel::HLERequestContext& ctx); | ||
| 29 | void Unsubscribe(Kernel::HLERequestContext& ctx); | ||
| 30 | void PublishToSubscriber(Kernel::HLERequestContext& ctx); | ||
| 31 | void RegisterService(Kernel::HLERequestContext& ctx); | ||
| 32 | |||
| 33 | std::shared_ptr<ServiceManager> service_manager; | ||
| 34 | Kernel::SharedPtr<Kernel::Semaphore> notification_semaphore; | ||
| 35 | }; | ||
| 36 | |||
| 37 | } // namespace SM | ||
| 38 | } // 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 @@ | |||
| 10 | #include "core/hle/kernel/client_session.h" | 10 | #include "core/hle/kernel/client_session.h" |
| 11 | #include "core/hle/kernel/handle_table.h" | 11 | #include "core/hle/kernel/handle_table.h" |
| 12 | #include "core/hle/kernel/process.h" | 12 | #include "core/hle/kernel/process.h" |
| 13 | #include "core/hle/kernel/thread.h" | ||
| 13 | #include "core/hle/lock.h" | 14 | #include "core/hle/lock.h" |
| 14 | #include "core/hle/result.h" | 15 | #include "core/hle/result.h" |
| 15 | #include "core/hle/service/service.h" | 16 | #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 | |||
| 3 | core/arm/arm_test_common.cpp | 3 | core/arm/arm_test_common.cpp |
| 4 | core/arm/dyncom/arm_dyncom_vfp_tests.cpp | 4 | core/arm/dyncom/arm_dyncom_vfp_tests.cpp |
| 5 | core/file_sys/path_parser.cpp | 5 | core/file_sys/path_parser.cpp |
| 6 | core/hle/kernel/hle_ipc.cpp | ||
| 7 | core/memory/memory.cpp | 6 | core/memory/memory.cpp |
| 8 | glad.cpp | 7 | glad.cpp |
| 9 | tests.cpp | 8 | 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 @@ | |||
| 1 | // Copyright 2017 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <catch.hpp> | ||
| 6 | #include "core/hle/ipc.h" | ||
| 7 | #include "core/hle/kernel/client_port.h" | ||
| 8 | #include "core/hle/kernel/client_session.h" | ||
| 9 | #include "core/hle/kernel/event.h" | ||
| 10 | #include "core/hle/kernel/handle_table.h" | ||
| 11 | #include "core/hle/kernel/hle_ipc.h" | ||
| 12 | #include "core/hle/kernel/process.h" | ||
| 13 | #include "core/hle/kernel/server_session.h" | ||
| 14 | |||
| 15 | namespace Kernel { | ||
| 16 | |||
| 17 | static SharedPtr<Object> MakeObject() { | ||
| 18 | return Event::Create(ResetType::OneShot); | ||
| 19 | } | ||
| 20 | |||
| 21 | TEST_CASE("HLERequestContext::PopulateFromIncomingCommandBuffer", "[core][kernel]") { | ||
| 22 | auto session = std::get<SharedPtr<ServerSession>>(ServerSession::CreateSessionPair()); | ||
| 23 | HLERequestContext context(std::move(session)); | ||
| 24 | |||
| 25 | auto process = Process::Create(""); | ||
| 26 | HandleTable handle_table; | ||
| 27 | |||
| 28 | SECTION("works with empty cmdbuf") { | ||
| 29 | const u32_le input[]{ | ||
| 30 | IPC::MakeHeader(0x1234, 0, 0), | ||
| 31 | }; | ||
| 32 | |||
| 33 | context.PopulateFromIncomingCommandBuffer(input, *process, handle_table); | ||
| 34 | |||
| 35 | REQUIRE(context.CommandBuffer()[0] == 0x12340000); | ||
| 36 | } | ||
| 37 | |||
| 38 | SECTION("translates regular params") { | ||
| 39 | const u32_le input[]{ | ||
| 40 | IPC::MakeHeader(0, 3, 0), 0x12345678, 0x21122112, 0xAABBCCDD, | ||
| 41 | }; | ||
| 42 | |||
| 43 | context.PopulateFromIncomingCommandBuffer(input, *process, handle_table); | ||
| 44 | |||
| 45 | auto* output = context.CommandBuffer(); | ||
| 46 | REQUIRE(output[1] == 0x12345678); | ||
| 47 | REQUIRE(output[2] == 0x21122112); | ||
| 48 | REQUIRE(output[3] == 0xAABBCCDD); | ||
| 49 | } | ||
| 50 | |||
| 51 | SECTION("translates move handles") { | ||
| 52 | auto a = MakeObject(); | ||
| 53 | Handle a_handle = handle_table.Create(a).Unwrap(); | ||
| 54 | const u32_le input[]{ | ||
| 55 | IPC::MakeHeader(0, 0, 2), IPC::MoveHandleDesc(1), a_handle, | ||
| 56 | }; | ||
| 57 | |||
| 58 | context.PopulateFromIncomingCommandBuffer(input, *process, handle_table); | ||
| 59 | |||
| 60 | auto* output = context.CommandBuffer(); | ||
| 61 | REQUIRE(context.GetIncomingHandle(output[2]) == a); | ||
| 62 | REQUIRE(handle_table.GetGeneric(a_handle) == nullptr); | ||
| 63 | } | ||
| 64 | |||
| 65 | SECTION("translates copy handles") { | ||
| 66 | auto a = MakeObject(); | ||
| 67 | Handle a_handle = handle_table.Create(a).Unwrap(); | ||
| 68 | const u32_le input[]{ | ||
| 69 | IPC::MakeHeader(0, 0, 2), IPC::CopyHandleDesc(1), a_handle, | ||
| 70 | }; | ||
| 71 | |||
| 72 | context.PopulateFromIncomingCommandBuffer(input, *process, handle_table); | ||
| 73 | |||
| 74 | auto* output = context.CommandBuffer(); | ||
| 75 | REQUIRE(context.GetIncomingHandle(output[2]) == a); | ||
| 76 | REQUIRE(handle_table.GetGeneric(a_handle) == a); | ||
| 77 | } | ||
| 78 | |||
| 79 | SECTION("translates multi-handle descriptors") { | ||
| 80 | auto a = MakeObject(); | ||
| 81 | auto b = MakeObject(); | ||
| 82 | auto c = MakeObject(); | ||
| 83 | const u32_le input[]{ | ||
| 84 | IPC::MakeHeader(0, 0, 5), IPC::MoveHandleDesc(2), | ||
| 85 | handle_table.Create(a).Unwrap(), handle_table.Create(b).Unwrap(), | ||
| 86 | IPC::MoveHandleDesc(1), handle_table.Create(c).Unwrap(), | ||
| 87 | }; | ||
| 88 | |||
| 89 | context.PopulateFromIncomingCommandBuffer(input, *process, handle_table); | ||
| 90 | |||
| 91 | auto* output = context.CommandBuffer(); | ||
| 92 | REQUIRE(context.GetIncomingHandle(output[2]) == a); | ||
| 93 | REQUIRE(context.GetIncomingHandle(output[3]) == b); | ||
| 94 | REQUIRE(context.GetIncomingHandle(output[5]) == c); | ||
| 95 | } | ||
| 96 | |||
| 97 | SECTION("translates null handles") { | ||
| 98 | const u32_le input[]{ | ||
| 99 | IPC::MakeHeader(0, 0, 2), IPC::MoveHandleDesc(1), 0, | ||
| 100 | }; | ||
| 101 | |||
| 102 | auto result = context.PopulateFromIncomingCommandBuffer(input, *process, handle_table); | ||
| 103 | |||
| 104 | REQUIRE(result == RESULT_SUCCESS); | ||
| 105 | auto* output = context.CommandBuffer(); | ||
| 106 | REQUIRE(context.GetIncomingHandle(output[2]) == nullptr); | ||
| 107 | } | ||
| 108 | |||
| 109 | SECTION("translates CallingPid descriptors") { | ||
| 110 | const u32_le input[]{ | ||
| 111 | IPC::MakeHeader(0, 0, 2), IPC::CallingPidDesc(), 0x98989898, | ||
| 112 | }; | ||
| 113 | |||
| 114 | context.PopulateFromIncomingCommandBuffer(input, *process, handle_table); | ||
| 115 | |||
| 116 | REQUIRE(context.CommandBuffer()[2] == process->process_id); | ||
| 117 | } | ||
| 118 | |||
| 119 | SECTION("translates mixed params") { | ||
| 120 | auto a = MakeObject(); | ||
| 121 | const u32_le input[]{ | ||
| 122 | IPC::MakeHeader(0, 2, 4), | ||
| 123 | 0x12345678, | ||
| 124 | 0xABCDEF00, | ||
| 125 | IPC::MoveHandleDesc(1), | ||
| 126 | handle_table.Create(a).Unwrap(), | ||
| 127 | IPC::CallingPidDesc(), | ||
| 128 | 0, | ||
| 129 | }; | ||
| 130 | |||
| 131 | context.PopulateFromIncomingCommandBuffer(input, *process, handle_table); | ||
| 132 | |||
| 133 | auto* output = context.CommandBuffer(); | ||
| 134 | REQUIRE(output[1] == 0x12345678); | ||
| 135 | REQUIRE(output[2] == 0xABCDEF00); | ||
| 136 | REQUIRE(context.GetIncomingHandle(output[4]) == a); | ||
| 137 | REQUIRE(output[6] == process->process_id); | ||
| 138 | } | ||
| 139 | } | ||
| 140 | |||
| 141 | TEST_CASE("HLERequestContext::WriteToOutgoingCommandBuffer", "[core][kernel]") { | ||
| 142 | auto session = std::get<SharedPtr<ServerSession>>(ServerSession::CreateSessionPair()); | ||
| 143 | HLERequestContext context(std::move(session)); | ||
| 144 | |||
| 145 | auto process = Process::Create(""); | ||
| 146 | HandleTable handle_table; | ||
| 147 | auto* input = context.CommandBuffer(); | ||
| 148 | u32_le output[IPC::COMMAND_BUFFER_LENGTH]; | ||
| 149 | |||
| 150 | SECTION("works with empty cmdbuf") { | ||
| 151 | input[0] = IPC::MakeHeader(0x1234, 0, 0); | ||
| 152 | |||
| 153 | context.WriteToOutgoingCommandBuffer(output, *process, handle_table); | ||
| 154 | |||
| 155 | REQUIRE(output[0] == 0x12340000); | ||
| 156 | } | ||
| 157 | |||
| 158 | SECTION("translates regular params") { | ||
| 159 | input[0] = IPC::MakeHeader(0, 3, 0); | ||
| 160 | input[1] = 0x12345678; | ||
| 161 | input[2] = 0x21122112; | ||
| 162 | input[3] = 0xAABBCCDD; | ||
| 163 | |||
| 164 | context.WriteToOutgoingCommandBuffer(output, *process, handle_table); | ||
| 165 | |||
| 166 | REQUIRE(output[1] == 0x12345678); | ||
| 167 | REQUIRE(output[2] == 0x21122112); | ||
| 168 | REQUIRE(output[3] == 0xAABBCCDD); | ||
| 169 | } | ||
| 170 | |||
| 171 | SECTION("translates move/copy handles") { | ||
| 172 | auto a = MakeObject(); | ||
| 173 | auto b = MakeObject(); | ||
| 174 | input[0] = IPC::MakeHeader(0, 0, 4); | ||
| 175 | input[1] = IPC::MoveHandleDesc(1); | ||
| 176 | input[2] = context.AddOutgoingHandle(a); | ||
| 177 | input[3] = IPC::CopyHandleDesc(1); | ||
| 178 | input[4] = context.AddOutgoingHandle(b); | ||
| 179 | |||
| 180 | context.WriteToOutgoingCommandBuffer(output, *process, handle_table); | ||
| 181 | |||
| 182 | REQUIRE(handle_table.GetGeneric(output[2]) == a); | ||
| 183 | REQUIRE(handle_table.GetGeneric(output[4]) == b); | ||
| 184 | } | ||
| 185 | |||
| 186 | SECTION("translates null handles") { | ||
| 187 | input[0] = IPC::MakeHeader(0, 0, 2); | ||
| 188 | input[1] = IPC::MoveHandleDesc(1); | ||
| 189 | input[2] = context.AddOutgoingHandle(nullptr); | ||
| 190 | |||
| 191 | auto result = context.WriteToOutgoingCommandBuffer(output, *process, handle_table); | ||
| 192 | |||
| 193 | REQUIRE(result == RESULT_SUCCESS); | ||
| 194 | REQUIRE(output[2] == 0); | ||
| 195 | } | ||
| 196 | |||
| 197 | SECTION("translates multi-handle descriptors") { | ||
| 198 | auto a = MakeObject(); | ||
| 199 | auto b = MakeObject(); | ||
| 200 | auto c = MakeObject(); | ||
| 201 | input[0] = IPC::MakeHeader(0, 0, 5); | ||
| 202 | input[1] = IPC::MoveHandleDesc(2); | ||
| 203 | input[2] = context.AddOutgoingHandle(a); | ||
| 204 | input[3] = context.AddOutgoingHandle(b); | ||
| 205 | input[4] = IPC::CopyHandleDesc(1); | ||
| 206 | input[5] = context.AddOutgoingHandle(c); | ||
| 207 | |||
| 208 | context.WriteToOutgoingCommandBuffer(output, *process, handle_table); | ||
| 209 | |||
| 210 | REQUIRE(handle_table.GetGeneric(output[2]) == a); | ||
| 211 | REQUIRE(handle_table.GetGeneric(output[3]) == b); | ||
| 212 | REQUIRE(handle_table.GetGeneric(output[5]) == c); | ||
| 213 | } | ||
| 214 | } | ||
| 215 | |||
| 216 | } // 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) { | |||
| 366 | switch (id) { | 366 | switch (id) { |
| 367 | // Trigger IRQ | 367 | // Trigger IRQ |
| 368 | case PICA_REG_INDEX(trigger_irq): | 368 | case PICA_REG_INDEX(trigger_irq): |
| 369 | Service::GSP::SignalInterrupt(Service::GSP::InterruptId::P3D); | 369 | //Service::GSP::SignalInterrupt(Service::GSP::InterruptId::P3D); |
| 370 | break; | 370 | break; |
| 371 | 371 | ||
| 372 | case PICA_REG_INDEX(pipeline.triangle_topology): | 372 | case PICA_REG_INDEX(pipeline.triangle_topology): |