summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGravatar Lectem2016-12-26 13:39:45 +0100
committerGravatar Lectem2016-12-26 14:07:29 +0100
commit8f2b642415a488064e214a1a6428fec422dd8374 (patch)
tree08274248fa268366cbf8f29282942f504d9b57df /src
parentMerge pull request #2369 from MerryMage/core-frontend (diff)
downloadyuzu-8f2b642415a488064e214a1a6428fec422dd8374.tar.gz
yuzu-8f2b642415a488064e214a1a6428fec422dd8374.tar.xz
yuzu-8f2b642415a488064e214a1a6428fec422dd8374.zip
IPC helpers
Diffstat (limited to 'src')
-rw-r--r--src/core/CMakeLists.txt1
-rw-r--r--src/core/hle/ipc.h111
-rw-r--r--src/core/hle/ipc_helpers.h259
3 files changed, 323 insertions, 48 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 3621449b3..c33555bd5 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -206,6 +206,7 @@ set(HEADERS
206 hle/config_mem.h 206 hle/config_mem.h
207 hle/function_wrappers.h 207 hle/function_wrappers.h
208 hle/ipc.h 208 hle/ipc.h
209 hle/ipc_helpers.h
209 hle/applets/applet.h 210 hle/applets/applet.h
210 hle/applets/erreula.h 211 hle/applets/erreula.h
211 hle/applets/mii_selector.h 212 hle/applets/mii_selector.h
diff --git a/src/core/hle/ipc.h b/src/core/hle/ipc.h
index 4e094faa7..4f7e5d050 100644
--- a/src/core/hle/ipc.h
+++ b/src/core/hle/ipc.h
@@ -18,12 +18,26 @@ static const int kCommandHeaderOffset = 0x80; ///< Offset into command buffer of
18 * the thread's TLS to an intermediate buffer in kernel memory, and then copied again to 18 * the thread's TLS to an intermediate buffer in kernel memory, and then copied again to
19 * the service handler process' memory. 19 * the service handler process' memory.
20 * @param offset Optional offset into command buffer 20 * @param offset Optional offset into command buffer
21 * @param offset Optional offset into command buffer (in bytes)
21 * @return Pointer to command buffer 22 * @return Pointer to command buffer
22 */ 23 */
23inline u32* GetCommandBuffer(const int offset = 0) { 24inline u32* GetCommandBuffer(const int offset = 0) {
24 return (u32*)Memory::GetPointer(GetCurrentThread()->GetTLSAddress() + kCommandHeaderOffset + 25 return (u32*)Memory::GetPointer(GetCurrentThread()->GetTLSAddress() + kCommandHeaderOffset +
25 offset); 26 offset);
26} 27}
28
29static const int kStaticBuffersOffset =
30 0x100; ///< Offset into static buffers, relative to command buffer header
31
32/**
33 * Returns a pointer to the static buffers area in the current thread's TLS
34 * TODO(Subv): cf. GetCommandBuffer
35 * @param offset Optional offset into static buffers area (in bytes)
36 * @return Pointer to static buffers area
37 */
38inline u32* GetStaticBuffers(const int offset = 0) {
39 return GetCommandBuffer(kStaticBuffersOffset + offset);
40}
27} 41}
28 42
29namespace IPC { 43namespace IPC {
@@ -40,35 +54,34 @@ enum DescriptorType : u32 {
40 CallingPid = 0x20, 54 CallingPid = 0x20,
41}; 55};
42 56
43/**
44 * @brief Creates a command header to be used for IPC
45 * @param command_id ID of the command to create a header for.
46 * @param normal_params Size of the normal parameters in words. Up to 63.
47 * @param translate_params_size Size of the translate parameters in words. Up to 63.
48 * @return The created IPC header.
49 *
50 * Normal parameters are sent directly to the process while the translate parameters might go
51 * through modifications and checks by the kernel.
52 * The translate parameters are described by headers generated with the IPC::*Desc functions.
53 *
54 * @note While #normal_params is equivalent to the number of normal parameters,
55 * #translate_params_size includes the size occupied by the translate parameters headers.
56 */
57constexpr u32 MakeHeader(u16 command_id, unsigned int normal_params,
58 unsigned int translate_params_size) {
59 return (u32(command_id) << 16) | ((u32(normal_params) & 0x3F) << 6) |
60 (u32(translate_params_size) & 0x3F);
61}
62
63union Header { 57union Header {
64 u32 raw; 58 u32 raw;
65 BitField<0, 6, u32> translate_params_size; 59 BitField<0, 6, u32> translate_params_size;
66 BitField<6, 6, u32> normal_params; 60 BitField<6, 6, u32> normal_params_size;
67 BitField<16, 16, u32> command_id; 61 BitField<16, 16, u32> command_id;
68}; 62};
69 63
70inline Header ParseHeader(u32 header) { 64/**
71 return {header}; 65* @brief Creates a command header to be used for IPC
66* @param command_id ID of the command to create a header for.
67* @param normal_params_size Size of the normal parameters in words. Up to 63.
68* @param translate_params_size Size of the translate parameters in words. Up to 63.
69* @return The created IPC header.
70*
71* Normal parameters are sent directly to the process while the translate parameters might go
72* through modifications and checks by the kernel.
73* The translate parameters are described by headers generated with the IPC::*Desc functions.
74*
75* @note While #normal_params_size is equivalent to the number of normal parameters,
76* #translate_params_size includes the size occupied by the translate parameters headers.
77*/
78inline u32 MakeHeader(u16 command_id, unsigned int normal_params_size,
79 unsigned int translate_params_size) {
80 Header header;
81 header.command_id.Assign(command_id);
82 header.normal_params_size.Assign(normal_params_size);
83 header.translate_params_size.Assign(translate_params_size);
84 return header.raw;
72} 85}
73 86
74constexpr u32 MoveHandleDesc(u32 num_handles = 1) { 87constexpr u32 MoveHandleDesc(u32 num_handles = 1) {
@@ -83,7 +96,7 @@ constexpr u32 CallingPidDesc() {
83 return CallingPid; 96 return CallingPid;
84} 97}
85 98
86constexpr bool isHandleDescriptor(u32 descriptor) { 99constexpr bool IsHandleDescriptor(u32 descriptor) {
87 return (descriptor & 0xF) == 0x0; 100 return (descriptor & 0xF) == 0x0;
88} 101}
89 102
@@ -91,30 +104,31 @@ constexpr u32 HandleNumberFromDesc(u32 handle_descriptor) {
91 return (handle_descriptor >> 26) + 1; 104 return (handle_descriptor >> 26) + 1;
92} 105}
93 106
94constexpr u32 StaticBufferDesc(u32 size, u8 buffer_id) {
95 return StaticBuffer | (size << 14) | ((buffer_id & 0xF) << 10);
96}
97
98union StaticBufferDescInfo { 107union StaticBufferDescInfo {
99 u32 raw; 108 u32 raw;
109 BitField<0, 4, u32> descriptor_type;
100 BitField<10, 4, u32> buffer_id; 110 BitField<10, 4, u32> buffer_id;
101 BitField<14, 18, u32> size; 111 BitField<14, 18, u32> size;
102}; 112};
103 113
104inline StaticBufferDescInfo ParseStaticBufferDesc(const u32 desc) { 114inline u32 StaticBufferDesc(u32 size, u8 buffer_id) {
105 return {desc}; 115 StaticBufferDescInfo info;
116 info.descriptor_type.Assign(StaticBuffer);
117 info.buffer_id.Assign(buffer_id);
118 info.size.Assign(size);
119 return info.raw;
106} 120}
107 121
108/** 122/**
109 * @brief Creates a header describing a buffer to be sent over PXI. 123* @brief Creates a header describing a buffer to be sent over PXI.
110 * @param size Size of the buffer. Max 0x00FFFFFF. 124* @param size Size of the buffer. Max 0x00FFFFFF.
111 * @param buffer_id The Id of the buffer. Max 0xF. 125* @param buffer_id The Id of the buffer. Max 0xF.
112 * @param is_read_only true if the buffer is read-only. If false, the buffer is considered to have 126* @param is_read_only true if the buffer is read-only. If false, the buffer is considered to have
113 * read-write access. 127* read-write access.
114 * @return The created PXI buffer header. 128* @return The created PXI buffer header.
115 * 129*
116 * The next value is a phys-address of a table located in the BASE memregion. 130* The next value is a phys-address of a table located in the BASE memregion.
117 */ 131*/
118inline u32 PXIBufferDesc(u32 size, unsigned buffer_id, bool is_read_only) { 132inline u32 PXIBufferDesc(u32 size, unsigned buffer_id, bool is_read_only) {
119 u32 type = PXIBuffer; 133 u32 type = PXIBuffer;
120 if (is_read_only) 134 if (is_read_only)
@@ -122,29 +136,30 @@ inline u32 PXIBufferDesc(u32 size, unsigned buffer_id, bool is_read_only) {
122 return type | (size << 8) | ((buffer_id & 0xF) << 4); 136 return type | (size << 8) | ((buffer_id & 0xF) << 4);
123} 137}
124 138
125enum MappedBufferPermissions { 139enum MappedBufferPermissions : u32 {
126 R = 1, 140 R = 1,
127 W = 2, 141 W = 2,
128 RW = R | W, 142 RW = R | W,
129}; 143};
130 144
131constexpr u32 MappedBufferDesc(u32 size, MappedBufferPermissions perms) {
132 return MappedBuffer | (size << 4) | (u32(perms) << 1);
133}
134
135union MappedBufferDescInfo { 145union MappedBufferDescInfo {
136 u32 raw; 146 u32 raw;
137 BitField<4, 28, u32> size; 147 BitField<0, 4, u32> flags;
138 BitField<1, 2, MappedBufferPermissions> perms; 148 BitField<1, 2, MappedBufferPermissions> perms;
149 BitField<4, 28, u32> size;
139}; 150};
140 151
141inline MappedBufferDescInfo ParseMappedBufferDesc(const u32 desc) { 152inline u32 MappedBufferDesc(u32 size, MappedBufferPermissions perms) {
142 return {desc}; 153 MappedBufferDescInfo info;
154 info.flags.Assign(MappedBuffer);
155 info.perms.Assign(perms);
156 info.size.Assign(size);
157 return info.raw;
143} 158}
144 159
145inline DescriptorType GetDescriptorType(u32 descriptor) { 160inline DescriptorType GetDescriptorType(u32 descriptor) {
146 // Note: Those checks must be done in this order 161 // Note: Those checks must be done in this order
147 if (isHandleDescriptor(descriptor)) 162 if (IsHandleDescriptor(descriptor))
148 return (DescriptorType)(descriptor & 0x30); 163 return (DescriptorType)(descriptor & 0x30);
149 164
150 // handle the fact that the following descriptors can have rights 165 // handle the fact that the following descriptors can have rights
diff --git a/src/core/hle/ipc_helpers.h b/src/core/hle/ipc_helpers.h
new file mode 100644
index 000000000..6089c39c7
--- /dev/null
+++ b/src/core/hle/ipc_helpers.h
@@ -0,0 +1,259 @@
1// Copyright 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6#include "core/hle/ipc.h"
7#include "core/hle/kernel/kernel.h"
8
9namespace IPC {
10
11class RequestHelperBase {
12protected:
13 u32* cmdbuf;
14 ptrdiff_t index = 1;
15 Header header;
16
17public:
18 RequestHelperBase(u32* command_buffer, Header command_header)
19 : cmdbuf(command_buffer), header(command_header) {}
20
21 /// Returns the total size of the request in words
22 size_t TotalSize() const {
23 return 1 /* command header */ + header.normal_params_size + header.translate_params_size;
24 }
25
26 void ValidateHeader() {
27 DEBUG_ASSERT_MSG(index == TotalSize(), "Operations do not match the header (cmd 0x%x)",
28 header.raw);
29 }
30};
31
32class RequestBuilder : public RequestHelperBase {
33public:
34 RequestBuilder(u32* command_buffer, Header command_header)
35 : RequestHelperBase(command_buffer, command_header) {
36 cmdbuf[0] = header.raw;
37 }
38 explicit RequestBuilder(u32* command_buffer, u32 command_header)
39 : RequestBuilder(command_buffer, Header{command_header}) {}
40 RequestBuilder(u32* command_buffer, u16 command_id, unsigned normal_params_size,
41 unsigned translate_params_size)
42 : RequestBuilder(command_buffer,
43 MakeHeader(command_id, normal_params_size, translate_params_size)) {}
44
45 // Validate on destruction, as there shouldn't be any case where we don't want it
46 ~RequestBuilder() {
47 ValidateHeader();
48 }
49
50 template <typename T>
51 void Push(T value);
52
53 template <>
54 void Push(u32);
55
56 template <typename First, class... Other>
57 void Push(First first_value, const Other&... other_values) {
58 Push(first_value);
59 Push(other_values...);
60 }
61
62 /**
63 * @brief Copies the content of the given trivially copyable class to the buffer as a normal
64 * param
65 * @note: The input class must be correctly packed/padded to fit hardware layout.
66 */
67 template <typename T>
68 void PushRaw(const T& value) {
69 static_assert(std::is_trivially_copyable<T>(), "Raw types should be trivially copyable");
70 std::memcpy(cmdbuf + index, &value, sizeof(T));
71 index += (sizeof(T) + 3) / 4; // round up to word length
72 }
73
74 // TODO : ensure that translate params are added after all regular params
75 template <typename... H>
76 void PushCopyHandles(H... handles) {
77 Push(CopyHandleDesc(sizeof...(H)));
78 Push(static_cast<Kernel::Handle>(handles)...);
79 }
80
81 template <typename... H>
82 void PushMoveHandles(H... handles) {
83 Push(MoveHandleDesc(sizeof...(H)));
84 Push(static_cast<Kernel::Handle>(handles)...);
85 }
86
87 void PushCurrentPIDHandle() {
88 Push(CallingPidDesc());
89 Push<u32>(0);
90 }
91
92 void PushStaticBuffer(VAddr buffer_vaddr, u32 size, u8 buffer_id) {
93 Push(StaticBufferDesc(size, buffer_id));
94 Push(buffer_vaddr);
95 }
96
97 void PushMappedBuffer(VAddr buffer_vaddr, u32 size, MappedBufferPermissions perms) {
98 Push(MappedBufferDesc(size, perms));
99 Push(buffer_vaddr);
100 }
101};
102
103class RequestParser : public RequestHelperBase {
104public:
105 RequestParser(u32* command_buffer, Header command_header)
106 : RequestHelperBase(command_buffer, command_header) {}
107 explicit RequestParser(u32* command_buffer, u32 command_header)
108 : RequestParser(command_buffer, Header{command_header}) {}
109 RequestParser(u32* command_buffer, u16 command_id, unsigned normal_params_size,
110 unsigned translate_params_size)
111 : RequestParser(command_buffer,
112 MakeHeader(command_id, normal_params_size, translate_params_size)) {}
113
114 RequestBuilder MakeBuilder(u32 normal_params_size, u32 translate_params_size,
115 bool validateHeader = true) {
116 if (validateHeader)
117 ValidateHeader();
118 Header builderHeader{
119 MakeHeader(header.command_id, normal_params_size, translate_params_size)};
120 return {cmdbuf, builderHeader};
121 }
122
123 template <typename T>
124 T Pop();
125
126 template <>
127 u32 Pop<u32>();
128
129 template <typename T>
130 void Pop(T& value) {
131 value = Pop<T>();
132 }
133
134 template <typename First, class... Other>
135 void Pop(First& first_value, Other&... other_values) {
136 first_value = Pop<First>();
137 Pop(other_values...);
138 }
139
140 Kernel::Handle PopHandle() {
141 const u32 handle_descriptor = Pop<u32>();
142 DEBUG_ASSERT_MSG(IsHandleDescriptor(handle_descriptor),
143 "Tried to pop handle(s) but the descriptor is not a handle descriptor");
144 DEBUG_ASSERT_MSG(HandleNumberFromDesc(handle_descriptor) == 1,
145 "Descriptor indicates that there isn't exactly one handle");
146 return Pop<Kernel::Handle>();
147 }
148
149 template <typename... H>
150 void PopHandles(H&... handles) {
151 const u32 handle_descriptor = Pop<u32>();
152 const int handles_number = sizeof...(H);
153 DEBUG_ASSERT_MSG(IsHandleDescriptor(handle_descriptor),
154 "Tried to pop handle(s) but the descriptor is not a handle descriptor");
155 DEBUG_ASSERT_MSG(handles_number == HandleNumberFromDesc(handle_descriptor),
156 "Number of handles doesn't match the descriptor");
157 Pop(static_cast<Kernel::Handle&>(handles)...);
158 }
159
160 /**
161 * @brief Pops the static buffer vaddr
162 * @return The virtual address of the buffer
163 * @param[out] data_size If non-null, the pointed value will be set to the size of the data
164 * @param[out] useStaticBuffersToGetVaddr Indicates if we should read the vaddr from the static
165 * buffers (which is the correct thing to do, but no service presently implement it) instead of
166 * using the same value as the process who sent the request
167 * given by the source process
168 *
169 * Static buffers must be set up before any IPC request using those is sent.
170 * It is the duty of the process (usually services) to allocate and set up the receiving static
171 * buffer information
172 * Please note that the setup uses virtual addresses.
173 */
174 VAddr PopStaticBuffer(size_t* data_size = nullptr, bool useStaticBuffersToGetVaddr = false) {
175 const u32 sbuffer_descriptor = Pop<u32>();
176 StaticBufferDescInfo bufferInfo{sbuffer_descriptor};
177 if (data_size != nullptr)
178 *data_size = bufferInfo.size;
179 if (!useStaticBuffersToGetVaddr)
180 return Pop<VAddr>();
181 else {
182 ASSERT_MSG(0, "remove the assert if multiprocess/IPC translation are implemented.");
183 // The buffer has already been copied to the static buffer by the kernel during
184 // translation
185 Pop<VAddr>(); // Pop the calling process buffer address
186 // and get the vaddr from the static buffers
187 return cmdbuf[(0x100 >> 2) + bufferInfo.buffer_id * 2 + 1];
188 }
189 }
190
191 /**
192 * @brief Pops the mapped buffer vaddr
193 * @return The virtual address of the buffer
194 * @param[out] data_size If non-null, the pointed value will be set to the size of the data
195 * given by the source process
196 * @param[out] buffer_perms If non-null, the pointed value will be set to the permissions of the
197 * buffer
198 */
199 VAddr PopMappedBuffer(size_t* data_size = nullptr,
200 MappedBufferPermissions* buffer_perms = nullptr) {
201 const u32 sbuffer_descriptor = Pop<u32>();
202 MappedBufferDescInfo bufferInfo{sbuffer_descriptor};
203 if (data_size != nullptr)
204 *data_size = bufferInfo.size;
205 if (buffer_perms != nullptr)
206 *buffer_perms = bufferInfo.perms;
207 return Pop<VAddr>();
208 }
209
210 /**
211 * @brief Reads the next normal parameters as a struct, by copying it
212 * @note: The output class must be correctly packed/padded to fit hardware layout.
213 */
214 template <typename T>
215 void PopRaw(T& value) {
216 static_assert(std::is_trivially_copyable<T>(), "Raw types should be trivially copyable");
217 std::memcpy(&value, cmdbuf + index, sizeof(T));
218 index += (sizeof(T) + 3) / 4; // round up to word length
219 }
220};
221
222/// Pop ///
223
224template <>
225inline u32 RequestParser::Pop<u32>() {
226 return cmdbuf[index++];
227}
228
229template <>
230inline u64 RequestParser::Pop<u64>() {
231 const u64 lsw = Pop<u32>();
232 const u64 msw = Pop<u32>();
233 return msw << 32 | lsw;
234}
235
236template <>
237inline ResultCode RequestParser::Pop<ResultCode>() {
238 return ResultCode{Pop<u32>()};
239}
240
241/// Push ///
242
243template <>
244inline void RequestBuilder::Push<u32>(u32 value) {
245 cmdbuf[index++] = value;
246}
247
248template <>
249inline void RequestBuilder::Push<u64>(u64 value) {
250 Push(static_cast<u32>(value));
251 Push(static_cast<u32>(value >> 32));
252}
253
254template <>
255inline void RequestBuilder::Push<ResultCode>(ResultCode value) {
256 Push(value.raw);
257}
258
259} // namespace IPC