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(
__anon5a84ac7f0102null28         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(
__anon5a84ac7f0202null244         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