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 <catch2/catch.hpp>
6 #include "common/archives.h"
7 #include "core/core.h"
8 #include "core/core_timing.h"
9 #include "core/hle/ipc.h"
10 #include "core/hle/kernel/client_port.h"
11 #include "core/hle/kernel/client_session.h"
12 #include "core/hle/kernel/event.h"
13 #include "core/hle/kernel/handle_table.h"
14 #include "core/hle/kernel/hle_ipc.h"
15 #include "core/hle/kernel/process.h"
16 #include "core/hle/kernel/server_session.h"
17
18 namespace Kernel {
19
MakeObject(Kernel::KernelSystem & kernel)20 static std::shared_ptr<Object> MakeObject(Kernel::KernelSystem& kernel) {
21 return kernel.CreateEvent(ResetType::OneShot);
22 }
23
24 TEST_CASE("HLERequestContext::PopulateFromIncomingCommandBuffer", "[core][kernel]") {
25 Core::Timing timing(1, 100);
26 Memory::MemorySystem memory;
27 Kernel::KernelSystem kernel(
__anon0b7b08a60102null28 memory, timing, [] {}, 0, 1, 0);
29 auto [server, client] = kernel.CreateSessionPair();
30 HLERequestContext context(kernel, std::move(server), nullptr);
31
32 auto process = kernel.CreateProcess(kernel.CreateCodeSet("", 0));
33
34 SECTION("works with empty cmdbuf") {
35 const u32_le input[]{
36 IPC::MakeHeader(0x1234, 0, 0),
37 };
38
39 context.PopulateFromIncomingCommandBuffer(input, process);
40
41 REQUIRE(context.CommandBuffer()[0] == 0x12340000);
42 }
43
44 SECTION("translates regular params") {
45 const u32_le input[]{
46 IPC::MakeHeader(0, 3, 0),
47 0x12345678,
48 0x21122112,
49 0xAABBCCDD,
50 };
51
52 context.PopulateFromIncomingCommandBuffer(input, process);
53
54 auto* output = context.CommandBuffer();
55 REQUIRE(output[1] == 0x12345678);
56 REQUIRE(output[2] == 0x21122112);
57 REQUIRE(output[3] == 0xAABBCCDD);
58 }
59
60 SECTION("translates move handles") {
61 auto a = MakeObject(kernel);
62 Handle a_handle = process->handle_table.Create(a).Unwrap();
63 const u32_le input[]{
64 IPC::MakeHeader(0, 0, 2),
65 IPC::MoveHandleDesc(1),
66 a_handle,
67 };
68
69 context.PopulateFromIncomingCommandBuffer(input, process);
70
71 auto* output = context.CommandBuffer();
72 REQUIRE(context.GetIncomingHandle(output[2]) == a);
73 REQUIRE(process->handle_table.GetGeneric(a_handle) == nullptr);
74 }
75
76 SECTION("translates copy handles") {
77 auto a = MakeObject(kernel);
78 Handle a_handle = process->handle_table.Create(a).Unwrap();
79 const u32_le input[]{
80 IPC::MakeHeader(0, 0, 2),
81 IPC::CopyHandleDesc(1),
82 a_handle,
83 };
84
85 context.PopulateFromIncomingCommandBuffer(input, process);
86
87 auto* output = context.CommandBuffer();
88 REQUIRE(context.GetIncomingHandle(output[2]) == a);
89 REQUIRE(process->handle_table.GetGeneric(a_handle) == a);
90 }
91
92 SECTION("translates multi-handle descriptors") {
93 auto a = MakeObject(kernel);
94 auto b = MakeObject(kernel);
95 auto c = MakeObject(kernel);
96 const u32_le input[]{
97 IPC::MakeHeader(0, 0, 5),
98 IPC::MoveHandleDesc(2),
99 process->handle_table.Create(a).Unwrap(),
100 process->handle_table.Create(b).Unwrap(),
101 IPC::MoveHandleDesc(1),
102 process->handle_table.Create(c).Unwrap(),
103 };
104
105 context.PopulateFromIncomingCommandBuffer(input, process);
106
107 auto* output = context.CommandBuffer();
108 REQUIRE(context.GetIncomingHandle(output[2]) == a);
109 REQUIRE(context.GetIncomingHandle(output[3]) == b);
110 REQUIRE(context.GetIncomingHandle(output[5]) == c);
111 }
112
113 SECTION("translates null handles") {
114 const u32_le input[]{
115 IPC::MakeHeader(0, 0, 2),
116 IPC::MoveHandleDesc(1),
117 0,
118 };
119
120 auto result = context.PopulateFromIncomingCommandBuffer(input, process);
121
122 REQUIRE(result == RESULT_SUCCESS);
123 auto* output = context.CommandBuffer();
124 REQUIRE(context.GetIncomingHandle(output[2]) == nullptr);
125 }
126
127 SECTION("translates CallingPid descriptors") {
128 const u32_le input[]{
129 IPC::MakeHeader(0, 0, 2),
130 IPC::CallingPidDesc(),
131 0x98989898,
132 };
133
134 context.PopulateFromIncomingCommandBuffer(input, process);
135
136 REQUIRE(context.CommandBuffer()[2] == process->process_id);
137 }
138
139 SECTION("translates StaticBuffer descriptors") {
140 auto mem = std::make_shared<BufferMem>(Memory::PAGE_SIZE);
141 MemoryRef buffer{mem};
142 std::fill(buffer.GetPtr(), buffer.GetPtr() + buffer.GetSize(), 0xAB);
143
144 VAddr target_address = 0x10000000;
145 auto result = process->vm_manager.MapBackingMemory(target_address, buffer, buffer.GetSize(),
146 MemoryState::Private);
147 REQUIRE(result.Code() == RESULT_SUCCESS);
148
149 const u32_le input[]{
150 IPC::MakeHeader(0, 0, 2),
151 IPC::StaticBufferDesc(buffer.GetSize(), 0),
152 target_address,
153 };
154
155 context.PopulateFromIncomingCommandBuffer(input, process);
156
157 CHECK(context.GetStaticBuffer(0) == mem->Vector());
158
159 REQUIRE(process->vm_manager.UnmapRange(target_address, buffer.GetSize()) == RESULT_SUCCESS);
160 }
161
162 SECTION("translates MappedBuffer descriptors") {
163 auto mem = std::make_shared<BufferMem>(Memory::PAGE_SIZE);
164 MemoryRef buffer{mem};
165 std::fill(buffer.GetPtr(), buffer.GetPtr() + buffer.GetSize(), 0xCD);
166
167 VAddr target_address = 0x10000000;
168 auto result = process->vm_manager.MapBackingMemory(target_address, buffer, buffer.GetSize(),
169 MemoryState::Private);
170
171 const u32_le input[]{
172 IPC::MakeHeader(0, 0, 2),
173 IPC::MappedBufferDesc(buffer.GetSize(), IPC::R),
174 target_address,
175 };
176
177 context.PopulateFromIncomingCommandBuffer(input, process);
178
179 std::vector<u8> other_buffer(buffer.GetSize());
180 context.GetMappedBuffer(0).Read(other_buffer.data(), 0, buffer.GetSize());
181
182 CHECK(other_buffer == mem->Vector());
183
184 REQUIRE(process->vm_manager.UnmapRange(target_address, buffer.GetSize()) == RESULT_SUCCESS);
185 }
186
187 SECTION("translates mixed params") {
188 auto mem_static = std::make_shared<BufferMem>(Memory::PAGE_SIZE);
189 MemoryRef buffer_static{mem_static};
190 std::fill(buffer_static.GetPtr(), buffer_static.GetPtr() + buffer_static.GetSize(), 0xCE);
191
192 auto mem_mapped = std::make_shared<BufferMem>(Memory::PAGE_SIZE);
193 MemoryRef buffer_mapped{mem_mapped};
194 std::fill(buffer_mapped.GetPtr(), buffer_mapped.GetPtr() + buffer_mapped.GetSize(), 0xDF);
195
196 VAddr target_address_static = 0x10000000;
197 auto result = process->vm_manager.MapBackingMemory(
198 target_address_static, buffer_static, buffer_static.GetSize(), MemoryState::Private);
199 REQUIRE(result.Code() == RESULT_SUCCESS);
200
201 VAddr target_address_mapped = 0x20000000;
202 result = process->vm_manager.MapBackingMemory(
203 target_address_mapped, buffer_mapped, buffer_mapped.GetSize(), MemoryState::Private);
204 REQUIRE(result.Code() == RESULT_SUCCESS);
205
206 auto a = MakeObject(kernel);
207 const u32_le input[]{
208 IPC::MakeHeader(0, 2, 8),
209 0x12345678,
210 0xABCDEF00,
211 IPC::MoveHandleDesc(1),
212 process->handle_table.Create(a).Unwrap(),
213 IPC::CallingPidDesc(),
214 0,
215 IPC::StaticBufferDesc(buffer_static.GetSize(), 0),
216 target_address_static,
217 IPC::MappedBufferDesc(buffer_mapped.GetSize(), IPC::R),
218 target_address_mapped,
219 };
220
221 context.PopulateFromIncomingCommandBuffer(input, process);
222
223 auto* output = context.CommandBuffer();
224 CHECK(output[1] == 0x12345678);
225 CHECK(output[2] == 0xABCDEF00);
226 CHECK(context.GetIncomingHandle(output[4]) == a);
227 CHECK(output[6] == process->process_id);
228 CHECK(context.GetStaticBuffer(0) == mem_static->Vector());
229 std::vector<u8> other_buffer(buffer_mapped.GetSize());
230 context.GetMappedBuffer(0).Read(other_buffer.data(), 0, buffer_mapped.GetSize());
231 CHECK(other_buffer == mem_mapped->Vector());
232
233 REQUIRE(process->vm_manager.UnmapRange(target_address_static, buffer_static.GetSize()) ==
234 RESULT_SUCCESS);
235 REQUIRE(process->vm_manager.UnmapRange(target_address_mapped, buffer_mapped.GetSize()) ==
236 RESULT_SUCCESS);
237 }
238 }
239
240 TEST_CASE("HLERequestContext::WriteToOutgoingCommandBuffer", "[core][kernel]") {
241 Core::Timing timing(1, 100);
242 Memory::MemorySystem memory;
243 Kernel::KernelSystem kernel(
__anon0b7b08a60202null244 memory, timing, [] {}, 0, 1, 0);
245 auto [server, client] = kernel.CreateSessionPair();
246 HLERequestContext context(kernel, std::move(server), nullptr);
247
248 auto process = kernel.CreateProcess(kernel.CreateCodeSet("", 0));
249 auto* input = context.CommandBuffer();
250 u32_le output[IPC::COMMAND_BUFFER_LENGTH];
251
252 SECTION("works with empty cmdbuf") {
253 input[0] = IPC::MakeHeader(0x1234, 0, 0);
254
255 context.WriteToOutgoingCommandBuffer(output, *process);
256
257 REQUIRE(output[0] == 0x12340000);
258 }
259
260 SECTION("translates regular params") {
261 input[0] = IPC::MakeHeader(0, 3, 0);
262 input[1] = 0x12345678;
263 input[2] = 0x21122112;
264 input[3] = 0xAABBCCDD;
265
266 context.WriteToOutgoingCommandBuffer(output, *process);
267
268 REQUIRE(output[1] == 0x12345678);
269 REQUIRE(output[2] == 0x21122112);
270 REQUIRE(output[3] == 0xAABBCCDD);
271 }
272
273 SECTION("translates move/copy handles") {
274 auto a = MakeObject(kernel);
275 auto b = MakeObject(kernel);
276 input[0] = IPC::MakeHeader(0, 0, 4);
277 input[1] = IPC::MoveHandleDesc(1);
278 input[2] = context.AddOutgoingHandle(a);
279 input[3] = IPC::CopyHandleDesc(1);
280 input[4] = context.AddOutgoingHandle(b);
281
282 context.WriteToOutgoingCommandBuffer(output, *process);
283
284 REQUIRE(process->handle_table.GetGeneric(output[2]) == a);
285 REQUIRE(process->handle_table.GetGeneric(output[4]) == b);
286 }
287
288 SECTION("translates null handles") {
289 input[0] = IPC::MakeHeader(0, 0, 2);
290 input[1] = IPC::MoveHandleDesc(1);
291 input[2] = context.AddOutgoingHandle(nullptr);
292
293 auto result = context.WriteToOutgoingCommandBuffer(output, *process);
294
295 REQUIRE(result == RESULT_SUCCESS);
296 REQUIRE(output[2] == 0);
297 }
298
299 SECTION("translates multi-handle descriptors") {
300 auto a = MakeObject(kernel);
301 auto b = MakeObject(kernel);
302 auto c = MakeObject(kernel);
303 input[0] = IPC::MakeHeader(0, 0, 5);
304 input[1] = IPC::MoveHandleDesc(2);
305 input[2] = context.AddOutgoingHandle(a);
306 input[3] = context.AddOutgoingHandle(b);
307 input[4] = IPC::CopyHandleDesc(1);
308 input[5] = context.AddOutgoingHandle(c);
309
310 context.WriteToOutgoingCommandBuffer(output, *process);
311
312 REQUIRE(process->handle_table.GetGeneric(output[2]) == a);
313 REQUIRE(process->handle_table.GetGeneric(output[3]) == b);
314 REQUIRE(process->handle_table.GetGeneric(output[5]) == c);
315 }
316
317 SECTION("translates StaticBuffer descriptors") {
318 std::vector<u8> input_buffer(Memory::PAGE_SIZE);
319 std::fill(input_buffer.begin(), input_buffer.end(), 0xAB);
320
321 context.AddStaticBuffer(0, input_buffer);
322
323 auto output_mem = std::make_shared<BufferMem>(Memory::PAGE_SIZE);
324 MemoryRef output_buffer{output_mem};
325
326 VAddr target_address = 0x10000000;
327 auto result = process->vm_manager.MapBackingMemory(
328 target_address, output_buffer, output_buffer.GetSize(), MemoryState::Private);
329 REQUIRE(result.Code() == RESULT_SUCCESS);
330
331 input[0] = IPC::MakeHeader(0, 0, 2);
332 input[1] = IPC::StaticBufferDesc(input_buffer.size(), 0);
333 input[2] = target_address;
334
335 // An entire command buffer plus enough space for one static buffer descriptor and its
336 // target address
337 std::array<u32_le, IPC::COMMAND_BUFFER_LENGTH + 2> output_cmdbuff;
338 // Set up the output StaticBuffer
339 output_cmdbuff[IPC::COMMAND_BUFFER_LENGTH] =
340 IPC::StaticBufferDesc(output_buffer.GetSize(), 0);
341 output_cmdbuff[IPC::COMMAND_BUFFER_LENGTH + 1] = target_address;
342
343 context.WriteToOutgoingCommandBuffer(output_cmdbuff.data(), *process);
344
345 CHECK(output_mem->Vector() == input_buffer);
346 REQUIRE(process->vm_manager.UnmapRange(target_address, output_buffer.GetSize()) ==
347 RESULT_SUCCESS);
348 }
349
350 SECTION("translates StaticBuffer descriptors") {
351 std::vector<u8> input_buffer(Memory::PAGE_SIZE);
352 std::fill(input_buffer.begin(), input_buffer.end(), 0xAB);
353
354 auto output_mem = std::make_shared<BufferMem>(Memory::PAGE_SIZE);
355 MemoryRef output_buffer{output_mem};
356
357 VAddr target_address = 0x10000000;
358 auto result = process->vm_manager.MapBackingMemory(
359 target_address, output_buffer, output_buffer.GetSize(), MemoryState::Private);
360 REQUIRE(result.Code() == RESULT_SUCCESS);
361
362 const u32_le input_cmdbuff[]{
363 IPC::MakeHeader(0, 0, 2),
364 IPC::MappedBufferDesc(output_buffer.GetSize(), IPC::W),
365 target_address,
366 };
367
368 context.PopulateFromIncomingCommandBuffer(input_cmdbuff, process);
369
370 context.GetMappedBuffer(0).Write(input_buffer.data(), 0, input_buffer.size());
371
372 input[0] = IPC::MakeHeader(0, 0, 2);
373 input[1] = IPC::MappedBufferDesc(output_buffer.GetSize(), IPC::W);
374 input[2] = 0;
375
376 context.WriteToOutgoingCommandBuffer(output, *process);
377
378 CHECK(output[1] == IPC::MappedBufferDesc(output_buffer.GetSize(), IPC::W));
379 CHECK(output[2] == target_address);
380 CHECK(output_mem->Vector() == input_buffer);
381 REQUIRE(process->vm_manager.UnmapRange(target_address, output_buffer.GetSize()) ==
382 RESULT_SUCCESS);
383 }
384 }
385
386 } // namespace Kernel
387