summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/core/CMakeLists.txt37
-rw-r--r--src/core/hle/kernel/svc.cpp2686
-rw-r--r--src/core/hle/kernel/svc.h156
-rw-r--r--src/core/hle/kernel/svc/svc_activity.cpp44
-rw-r--r--src/core/hle/kernel/svc/svc_address_arbiter.cpp113
-rw-r--r--src/core/hle/kernel/svc/svc_address_translation.cpp6
-rw-r--r--src/core/hle/kernel/svc/svc_cache.cpp31
-rw-r--r--src/core/hle/kernel/svc/svc_code_memory.cpp154
-rw-r--r--src/core/hle/kernel/svc/svc_condition_variable.cpp69
-rw-r--r--src/core/hle/kernel/svc/svc_debug.cpp6
-rw-r--r--src/core/hle/kernel/svc/svc_debug_string.cpp25
-rw-r--r--src/core/hle/kernel/svc/svc_device_address_space.cpp6
-rw-r--r--src/core/hle/kernel/svc/svc_event.cpp111
-rw-r--r--src/core/hle/kernel/svc/svc_exception.cpp121
-rw-r--r--src/core/hle/kernel/svc/svc_info.cpp282
-rw-r--r--src/core/hle/kernel/svc/svc_interrupt_event.cpp6
-rw-r--r--src/core/hle/kernel/svc/svc_io_pool.cpp6
-rw-r--r--src/core/hle/kernel/svc/svc_ipc.cpp89
-rw-r--r--src/core/hle/kernel/svc/svc_kernel_debug.cpp19
-rw-r--r--src/core/hle/kernel/svc/svc_light_ipc.cpp6
-rw-r--r--src/core/hle/kernel/svc/svc_lock.cpp57
-rw-r--r--src/core/hle/kernel/svc/svc_memory.cpp189
-rw-r--r--src/core/hle/kernel/svc/svc_physical_memory.cpp137
-rw-r--r--src/core/hle/kernel/svc/svc_port.cpp71
-rw-r--r--src/core/hle/kernel/svc/svc_power_management.cpp6
-rw-r--r--src/core/hle/kernel/svc/svc_process.cpp124
-rw-r--r--src/core/hle/kernel/svc/svc_process_memory.cpp274
-rw-r--r--src/core/hle/kernel/svc/svc_processor.cpp21
-rw-r--r--src/core/hle/kernel/svc/svc_query_memory.cpp55
-rw-r--r--src/core/hle/kernel/svc/svc_register.cpp6
-rw-r--r--src/core/hle/kernel/svc/svc_resource_limit.cpp95
-rw-r--r--src/core/hle/kernel/svc/svc_secure_monitor_call.cpp6
-rw-r--r--src/core/hle/kernel/svc/svc_session.cpp103
-rw-r--r--src/core/hle/kernel/svc/svc_shared_memory.cpp106
-rw-r--r--src/core/hle/kernel/svc/svc_synchronization.cpp139
-rw-r--r--src/core/hle/kernel/svc/svc_thread.cpp396
-rw-r--r--src/core/hle/kernel/svc/svc_thread_profiler.cpp6
-rw-r--r--src/core/hle/kernel/svc/svc_tick.cpp33
-rw-r--r--src/core/hle/kernel/svc/svc_transfer_memory.cpp79
-rw-r--r--src/core/hle/kernel/svc_wrap.h8
40 files changed, 3196 insertions, 2688 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 112c61b80..f16072e6c 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -298,7 +298,42 @@ add_library(core STATIC
298 hle/kernel/svc.h 298 hle/kernel/svc.h
299 hle/kernel/svc_common.h 299 hle/kernel/svc_common.h
300 hle/kernel/svc_types.h 300 hle/kernel/svc_types.h
301 hle/kernel/svc_wrap.h 301 hle/kernel/svc/svc_activity.cpp
302 hle/kernel/svc/svc_address_arbiter.cpp
303 hle/kernel/svc/svc_address_translation.cpp
304 hle/kernel/svc/svc_cache.cpp
305 hle/kernel/svc/svc_code_memory.cpp
306 hle/kernel/svc/svc_condition_variable.cpp
307 hle/kernel/svc/svc_debug.cpp
308 hle/kernel/svc/svc_debug_string.cpp
309 hle/kernel/svc/svc_device_address_space.cpp
310 hle/kernel/svc/svc_event.cpp
311 hle/kernel/svc/svc_exception.cpp
312 hle/kernel/svc/svc_info.cpp
313 hle/kernel/svc/svc_interrupt_event.cpp
314 hle/kernel/svc/svc_io_pool.cpp
315 hle/kernel/svc/svc_ipc.cpp
316 hle/kernel/svc/svc_kernel_debug.cpp
317 hle/kernel/svc/svc_light_ipc.cpp
318 hle/kernel/svc/svc_lock.cpp
319 hle/kernel/svc/svc_memory.cpp
320 hle/kernel/svc/svc_physical_memory.cpp
321 hle/kernel/svc/svc_port.cpp
322 hle/kernel/svc/svc_power_management.cpp
323 hle/kernel/svc/svc_process.cpp
324 hle/kernel/svc/svc_process_memory.cpp
325 hle/kernel/svc/svc_processor.cpp
326 hle/kernel/svc/svc_query_memory.cpp
327 hle/kernel/svc/svc_register.cpp
328 hle/kernel/svc/svc_resource_limit.cpp
329 hle/kernel/svc/svc_secure_monitor_call.cpp
330 hle/kernel/svc/svc_session.cpp
331 hle/kernel/svc/svc_shared_memory.cpp
332 hle/kernel/svc/svc_synchronization.cpp
333 hle/kernel/svc/svc_thread.cpp
334 hle/kernel/svc/svc_thread_profiler.cpp
335 hle/kernel/svc/svc_tick.cpp
336 hle/kernel/svc/svc_transfer_memory.cpp
302 hle/result.h 337 hle/result.h
303 hle/service/acc/acc.cpp 338 hle/service/acc/acc.cpp
304 hle/service/acc/acc.h 339 hle/service/acc/acc.h
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp
index 67fa5d71c..4cb6f40a0 100644
--- a/src/core/hle/kernel/svc.cpp
+++ b/src/core/hle/kernel/svc.cpp
@@ -1,2697 +1,16 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include <algorithm> 4#include "common/common_types.h"
5#include <cinttypes>
6#include <iterator>
7#include <mutex>
8#include <vector>
9
10#include "common/alignment.h"
11#include "common/assert.h"
12#include "common/common_funcs.h"
13#include "common/fiber.h"
14#include "common/logging/log.h"
15#include "common/scope_exit.h"
16#include "core/core.h"
17#include "core/core_timing.h"
18#include "core/debugger/debugger.h"
19#include "core/hle/kernel/k_client_port.h"
20#include "core/hle/kernel/k_client_session.h"
21#include "core/hle/kernel/k_code_memory.h"
22#include "core/hle/kernel/k_event.h"
23#include "core/hle/kernel/k_handle_table.h"
24#include "core/hle/kernel/k_memory_block.h"
25#include "core/hle/kernel/k_memory_layout.h"
26#include "core/hle/kernel/k_page_table.h"
27#include "core/hle/kernel/k_port.h"
28#include "core/hle/kernel/k_process.h" 5#include "core/hle/kernel/k_process.h"
29#include "core/hle/kernel/k_readable_event.h"
30#include "core/hle/kernel/k_resource_limit.h"
31#include "core/hle/kernel/k_scheduler.h"
32#include "core/hle/kernel/k_scoped_resource_reservation.h"
33#include "core/hle/kernel/k_session.h"
34#include "core/hle/kernel/k_shared_memory.h"
35#include "core/hle/kernel/k_synchronization_object.h"
36#include "core/hle/kernel/k_thread.h" 6#include "core/hle/kernel/k_thread.h"
37#include "core/hle/kernel/k_thread_queue.h"
38#include "core/hle/kernel/k_transfer_memory.h"
39#include "core/hle/kernel/kernel.h"
40#include "core/hle/kernel/physical_core.h"
41#include "core/hle/kernel/svc.h" 7#include "core/hle/kernel/svc.h"
42#include "core/hle/kernel/svc_results.h"
43#include "core/hle/kernel/svc_types.h"
44#include "core/hle/kernel/svc_wrap.h" 8#include "core/hle/kernel/svc_wrap.h"
45#include "core/hle/result.h" 9#include "core/hle/result.h"
46#include "core/memory.h"
47#include "core/reporter.h"
48 10
49namespace Kernel::Svc { 11namespace Kernel::Svc {
50namespace { 12namespace {
51 13
52// Checks if address + size is greater than the given address
53// This can return false if the size causes an overflow of a 64-bit type
54// or if the given size is zero.
55constexpr bool IsValidAddressRange(VAddr address, u64 size) {
56 return address + size > address;
57}
58
59// Helper function that performs the common sanity checks for svcMapMemory
60// and svcUnmapMemory. This is doable, as both functions perform their sanitizing
61// in the same order.
62Result MapUnmapMemorySanityChecks(const KPageTable& manager, VAddr dst_addr, VAddr src_addr,
63 u64 size) {
64 if (!Common::Is4KBAligned(dst_addr)) {
65 LOG_ERROR(Kernel_SVC, "Destination address is not aligned to 4KB, 0x{:016X}", dst_addr);
66 return ResultInvalidAddress;
67 }
68
69 if (!Common::Is4KBAligned(src_addr)) {
70 LOG_ERROR(Kernel_SVC, "Source address is not aligned to 4KB, 0x{:016X}", src_addr);
71 return ResultInvalidSize;
72 }
73
74 if (size == 0) {
75 LOG_ERROR(Kernel_SVC, "Size is 0");
76 return ResultInvalidSize;
77 }
78
79 if (!Common::Is4KBAligned(size)) {
80 LOG_ERROR(Kernel_SVC, "Size is not aligned to 4KB, 0x{:016X}", size);
81 return ResultInvalidSize;
82 }
83
84 if (!IsValidAddressRange(dst_addr, size)) {
85 LOG_ERROR(Kernel_SVC,
86 "Destination is not a valid address range, addr=0x{:016X}, size=0x{:016X}",
87 dst_addr, size);
88 return ResultInvalidCurrentMemory;
89 }
90
91 if (!IsValidAddressRange(src_addr, size)) {
92 LOG_ERROR(Kernel_SVC, "Source is not a valid address range, addr=0x{:016X}, size=0x{:016X}",
93 src_addr, size);
94 return ResultInvalidCurrentMemory;
95 }
96
97 if (!manager.IsInsideAddressSpace(src_addr, size)) {
98 LOG_ERROR(Kernel_SVC,
99 "Source is not within the address space, addr=0x{:016X}, size=0x{:016X}",
100 src_addr, size);
101 return ResultInvalidCurrentMemory;
102 }
103
104 if (manager.IsOutsideStackRegion(dst_addr, size)) {
105 LOG_ERROR(Kernel_SVC,
106 "Destination is not within the stack region, addr=0x{:016X}, size=0x{:016X}",
107 dst_addr, size);
108 return ResultInvalidMemoryRegion;
109 }
110
111 if (manager.IsInsideHeapRegion(dst_addr, size)) {
112 LOG_ERROR(Kernel_SVC,
113 "Destination does not fit within the heap region, addr=0x{:016X}, "
114 "size=0x{:016X}",
115 dst_addr, size);
116 return ResultInvalidMemoryRegion;
117 }
118
119 if (manager.IsInsideAliasRegion(dst_addr, size)) {
120 LOG_ERROR(Kernel_SVC,
121 "Destination does not fit within the map region, addr=0x{:016X}, "
122 "size=0x{:016X}",
123 dst_addr, size);
124 return ResultInvalidMemoryRegion;
125 }
126
127 return ResultSuccess;
128}
129
130enum class ResourceLimitValueType {
131 CurrentValue,
132 LimitValue,
133 PeakValue,
134};
135
136} // Anonymous namespace
137
138/// Set the process heap to a given Size. It can both extend and shrink the heap.
139static Result SetHeapSize(Core::System& system, VAddr* out_address, u64 size) {
140 LOG_TRACE(Kernel_SVC, "called, heap_size=0x{:X}", size);
141
142 // Validate size.
143 R_UNLESS(Common::IsAligned(size, HeapSizeAlignment), ResultInvalidSize);
144 R_UNLESS(size < MainMemorySizeMax, ResultInvalidSize);
145
146 // Set the heap size.
147 R_TRY(system.Kernel().CurrentProcess()->PageTable().SetHeapSize(out_address, size));
148
149 return ResultSuccess;
150}
151
152static Result SetHeapSize32(Core::System& system, u32* heap_addr, u32 heap_size) {
153 VAddr temp_heap_addr{};
154 const Result result{SetHeapSize(system, &temp_heap_addr, heap_size)};
155 *heap_addr = static_cast<u32>(temp_heap_addr);
156 return result;
157}
158
159constexpr bool IsValidSetMemoryPermission(MemoryPermission perm) {
160 switch (perm) {
161 case MemoryPermission::None:
162 case MemoryPermission::Read:
163 case MemoryPermission::ReadWrite:
164 return true;
165 default:
166 return false;
167 }
168}
169
170static Result SetMemoryPermission(Core::System& system, VAddr address, u64 size,
171 MemoryPermission perm) {
172 LOG_DEBUG(Kernel_SVC, "called, address=0x{:016X}, size=0x{:X}, perm=0x{:08X", address, size,
173 perm);
174
175 // Validate address / size.
176 R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress);
177 R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize);
178 R_UNLESS(size > 0, ResultInvalidSize);
179 R_UNLESS((address < address + size), ResultInvalidCurrentMemory);
180
181 // Validate the permission.
182 R_UNLESS(IsValidSetMemoryPermission(perm), ResultInvalidNewMemoryPermission);
183
184 // Validate that the region is in range for the current process.
185 auto& page_table = system.Kernel().CurrentProcess()->PageTable();
186 R_UNLESS(page_table.Contains(address, size), ResultInvalidCurrentMemory);
187
188 // Set the memory attribute.
189 return page_table.SetMemoryPermission(address, size, perm);
190}
191
192static Result SetMemoryAttribute(Core::System& system, VAddr address, u64 size, u32 mask,
193 u32 attr) {
194 LOG_DEBUG(Kernel_SVC,
195 "called, address=0x{:016X}, size=0x{:X}, mask=0x{:08X}, attribute=0x{:08X}", address,
196 size, mask, attr);
197
198 // Validate address / size.
199 R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress);
200 R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize);
201 R_UNLESS(size > 0, ResultInvalidSize);
202 R_UNLESS((address < address + size), ResultInvalidCurrentMemory);
203
204 // Validate the attribute and mask.
205 constexpr u32 SupportedMask = static_cast<u32>(MemoryAttribute::Uncached);
206 R_UNLESS((mask | attr) == mask, ResultInvalidCombination);
207 R_UNLESS((mask | attr | SupportedMask) == SupportedMask, ResultInvalidCombination);
208
209 // Validate that the region is in range for the current process.
210 auto& page_table{system.Kernel().CurrentProcess()->PageTable()};
211 R_UNLESS(page_table.Contains(address, size), ResultInvalidCurrentMemory);
212
213 // Set the memory attribute.
214 return page_table.SetMemoryAttribute(address, size, mask, attr);
215}
216
217static Result SetMemoryAttribute32(Core::System& system, u32 address, u32 size, u32 mask,
218 u32 attr) {
219 return SetMemoryAttribute(system, address, size, mask, attr);
220}
221
222/// Maps a memory range into a different range.
223static Result MapMemory(Core::System& system, VAddr dst_addr, VAddr src_addr, u64 size) {
224 LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr,
225 src_addr, size);
226
227 auto& page_table{system.Kernel().CurrentProcess()->PageTable()};
228
229 if (const Result result{MapUnmapMemorySanityChecks(page_table, dst_addr, src_addr, size)};
230 result.IsError()) {
231 return result;
232 }
233
234 return page_table.MapMemory(dst_addr, src_addr, size);
235}
236
237static Result MapMemory32(Core::System& system, u32 dst_addr, u32 src_addr, u32 size) {
238 return MapMemory(system, dst_addr, src_addr, size);
239}
240
241/// Unmaps a region that was previously mapped with svcMapMemory
242static Result UnmapMemory(Core::System& system, VAddr dst_addr, VAddr src_addr, u64 size) {
243 LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr,
244 src_addr, size);
245
246 auto& page_table{system.Kernel().CurrentProcess()->PageTable()};
247
248 if (const Result result{MapUnmapMemorySanityChecks(page_table, dst_addr, src_addr, size)};
249 result.IsError()) {
250 return result;
251 }
252
253 return page_table.UnmapMemory(dst_addr, src_addr, size);
254}
255
256static Result UnmapMemory32(Core::System& system, u32 dst_addr, u32 src_addr, u32 size) {
257 return UnmapMemory(system, dst_addr, src_addr, size);
258}
259
260template <typename T>
261Result CreateSession(Core::System& system, Handle* out_server, Handle* out_client, u64 name) {
262 auto& process = *system.CurrentProcess();
263 auto& handle_table = process.GetHandleTable();
264
265 // Declare the session we're going to allocate.
266 T* session;
267
268 // Reserve a new session from the process resource limit.
269 // FIXME: LimitableResource_SessionCountMax
270 KScopedResourceReservation session_reservation(&process, LimitableResource::SessionCountMax);
271 if (session_reservation.Succeeded()) {
272 session = T::Create(system.Kernel());
273 } else {
274 return ResultLimitReached;
275
276 // // We couldn't reserve a session. Check that we support dynamically expanding the
277 // // resource limit.
278 // R_UNLESS(process.GetResourceLimit() ==
279 // &system.Kernel().GetSystemResourceLimit(), ResultLimitReached);
280 // R_UNLESS(KTargetSystem::IsDynamicResourceLimitsEnabled(), ResultLimitReached());
281
282 // // Try to allocate a session from unused slab memory.
283 // session = T::CreateFromUnusedSlabMemory();
284 // R_UNLESS(session != nullptr, ResultLimitReached);
285 // ON_RESULT_FAILURE { session->Close(); };
286
287 // // If we're creating a KSession, we want to add two KSessionRequests to the heap, to
288 // // prevent request exhaustion.
289 // // NOTE: Nintendo checks if session->DynamicCast<KSession *>() != nullptr, but there's
290 // // no reason to not do this statically.
291 // if constexpr (std::same_as<T, KSession>) {
292 // for (size_t i = 0; i < 2; i++) {
293 // KSessionRequest* request = KSessionRequest::CreateFromUnusedSlabMemory();
294 // R_UNLESS(request != nullptr, ResultLimitReached);
295 // request->Close();
296 // }
297 // }
298
299 // We successfully allocated a session, so add the object we allocated to the resource
300 // limit.
301 // system.Kernel().GetSystemResourceLimit().Reserve(LimitableResource::SessionCountMax, 1);
302 }
303
304 // Check that we successfully created a session.
305 R_UNLESS(session != nullptr, ResultOutOfResource);
306
307 // Initialize the session.
308 session->Initialize(nullptr, fmt::format("{}", name));
309
310 // Commit the session reservation.
311 session_reservation.Commit();
312
313 // Ensure that we clean up the session (and its only references are handle table) on function
314 // end.
315 SCOPE_EXIT({
316 session->GetClientSession().Close();
317 session->GetServerSession().Close();
318 });
319
320 // Register the session.
321 T::Register(system.Kernel(), session);
322
323 // Add the server session to the handle table.
324 R_TRY(handle_table.Add(out_server, &session->GetServerSession()));
325
326 // Add the client session to the handle table.
327 const auto result = handle_table.Add(out_client, &session->GetClientSession());
328
329 if (!R_SUCCEEDED(result)) {
330 // Ensure that we maintaing a clean handle state on exit.
331 handle_table.Remove(*out_server);
332 }
333
334 return result;
335}
336
337static Result CreateSession(Core::System& system, Handle* out_server, Handle* out_client,
338 u32 is_light, u64 name) {
339 if (is_light) {
340 // return CreateSession<KLightSession>(system, out_server, out_client, name);
341 return ResultUnknown;
342 } else {
343 return CreateSession<KSession>(system, out_server, out_client, name);
344 }
345}
346
347/// Connect to an OS service given the port name, returns the handle to the port to out
348static Result ConnectToNamedPort(Core::System& system, Handle* out, VAddr port_name_address) {
349 auto& memory = system.Memory();
350 if (!memory.IsValidVirtualAddress(port_name_address)) {
351 LOG_ERROR(Kernel_SVC,
352 "Port Name Address is not a valid virtual address, port_name_address=0x{:016X}",
353 port_name_address);
354 return ResultNotFound;
355 }
356
357 static constexpr std::size_t PortNameMaxLength = 11;
358 // Read 1 char beyond the max allowed port name to detect names that are too long.
359 const std::string port_name = memory.ReadCString(port_name_address, PortNameMaxLength + 1);
360 if (port_name.size() > PortNameMaxLength) {
361 LOG_ERROR(Kernel_SVC, "Port name is too long, expected {} but got {}", PortNameMaxLength,
362 port_name.size());
363 return ResultOutOfRange;
364 }
365
366 LOG_TRACE(Kernel_SVC, "called port_name={}", port_name);
367
368 // Get the current handle table.
369 auto& kernel = system.Kernel();
370 auto& handle_table = kernel.CurrentProcess()->GetHandleTable();
371
372 // Find the client port.
373 auto port = kernel.CreateNamedServicePort(port_name);
374 if (!port) {
375 LOG_ERROR(Kernel_SVC, "tried to connect to unknown port: {}", port_name);
376 return ResultNotFound;
377 }
378
379 // Reserve a handle for the port.
380 // NOTE: Nintendo really does write directly to the output handle here.
381 R_TRY(handle_table.Reserve(out));
382 auto handle_guard = SCOPE_GUARD({ handle_table.Unreserve(*out); });
383
384 // Create a session.
385 KClientSession* session{};
386 R_TRY(port->CreateSession(std::addressof(session)));
387
388 kernel.RegisterNamedServiceHandler(port_name, &port->GetParent()->GetServerPort());
389
390 // Register the session in the table, close the extra reference.
391 handle_table.Register(*out, session);
392 session->Close();
393
394 // We succeeded.
395 handle_guard.Cancel();
396 return ResultSuccess;
397}
398
399static Result ConnectToNamedPort32(Core::System& system, Handle* out_handle,
400 u32 port_name_address) {
401
402 return ConnectToNamedPort(system, out_handle, port_name_address);
403}
404
405/// Makes a blocking IPC call to a service.
406static Result SendSyncRequest(Core::System& system, Handle handle) {
407 auto& kernel = system.Kernel();
408
409 // Create the wait queue.
410 KThreadQueue wait_queue(kernel);
411
412 // Get the client session from its handle.
413 KScopedAutoObject session =
414 kernel.CurrentProcess()->GetHandleTable().GetObject<KClientSession>(handle);
415 R_UNLESS(session.IsNotNull(), ResultInvalidHandle);
416
417 LOG_TRACE(Kernel_SVC, "called handle=0x{:08X}({})", handle, session->GetName());
418
419 return session->SendSyncRequest();
420}
421
422static Result SendSyncRequest32(Core::System& system, Handle handle) {
423 return SendSyncRequest(system, handle);
424}
425
426static Result ReplyAndReceive(Core::System& system, s32* out_index, Handle* handles,
427 s32 num_handles, Handle reply_target, s64 timeout_ns) {
428 auto& kernel = system.Kernel();
429 auto& handle_table = GetCurrentThread(kernel).GetOwnerProcess()->GetHandleTable();
430
431 // Convert handle list to object table.
432 std::vector<KSynchronizationObject*> objs(num_handles);
433 R_UNLESS(
434 handle_table.GetMultipleObjects<KSynchronizationObject>(objs.data(), handles, num_handles),
435 ResultInvalidHandle);
436
437 // Ensure handles are closed when we're done.
438 SCOPE_EXIT({
439 for (auto i = 0; i < num_handles; ++i) {
440 objs[i]->Close();
441 }
442 });
443
444 // Reply to the target, if one is specified.
445 if (reply_target != InvalidHandle) {
446 KScopedAutoObject session = handle_table.GetObject<KServerSession>(reply_target);
447 R_UNLESS(session.IsNotNull(), ResultInvalidHandle);
448
449 // If we fail to reply, we want to set the output index to -1.
450 // ON_RESULT_FAILURE { *out_index = -1; };
451
452 // Send the reply.
453 // R_TRY(session->SendReply());
454
455 Result rc = session->SendReply();
456 if (!R_SUCCEEDED(rc)) {
457 *out_index = -1;
458 return rc;
459 }
460 }
461
462 // Wait for a message.
463 while (true) {
464 // Wait for an object.
465 s32 index;
466 Result result = KSynchronizationObject::Wait(kernel, &index, objs.data(),
467 static_cast<s32>(objs.size()), timeout_ns);
468 if (result == ResultTimedOut) {
469 return result;
470 }
471
472 // Receive the request.
473 if (R_SUCCEEDED(result)) {
474 KServerSession* session = objs[index]->DynamicCast<KServerSession*>();
475 if (session != nullptr) {
476 result = session->ReceiveRequest();
477 if (result == ResultNotFound) {
478 continue;
479 }
480 }
481 }
482
483 *out_index = index;
484 return result;
485 }
486}
487
488/// Get the ID for the specified thread.
489static Result GetThreadId(Core::System& system, u64* out_thread_id, Handle thread_handle) {
490 // Get the thread from its handle.
491 KScopedAutoObject thread =
492 system.Kernel().CurrentProcess()->GetHandleTable().GetObject<KThread>(thread_handle);
493 R_UNLESS(thread.IsNotNull(), ResultInvalidHandle);
494
495 // Get the thread's id.
496 *out_thread_id = thread->GetId();
497 return ResultSuccess;
498}
499
500static Result GetThreadId32(Core::System& system, u32* out_thread_id_low, u32* out_thread_id_high,
501 Handle thread_handle) {
502 u64 out_thread_id{};
503 const Result result{GetThreadId(system, &out_thread_id, thread_handle)};
504
505 *out_thread_id_low = static_cast<u32>(out_thread_id >> 32);
506 *out_thread_id_high = static_cast<u32>(out_thread_id & std::numeric_limits<u32>::max());
507
508 return result;
509}
510
511/// Gets the ID of the specified process or a specified thread's owning process.
512static Result GetProcessId(Core::System& system, u64* out_process_id, Handle handle) {
513 LOG_DEBUG(Kernel_SVC, "called handle=0x{:08X}", handle);
514
515 // Get the object from the handle table.
516 KScopedAutoObject obj =
517 system.Kernel().CurrentProcess()->GetHandleTable().GetObject<KAutoObject>(
518 static_cast<Handle>(handle));
519 R_UNLESS(obj.IsNotNull(), ResultInvalidHandle);
520
521 // Get the process from the object.
522 KProcess* process = nullptr;
523 if (KProcess* p = obj->DynamicCast<KProcess*>(); p != nullptr) {
524 // The object is a process, so we can use it directly.
525 process = p;
526 } else if (KThread* t = obj->DynamicCast<KThread*>(); t != nullptr) {
527 // The object is a thread, so we want to use its parent.
528 process = reinterpret_cast<KThread*>(obj.GetPointerUnsafe())->GetOwnerProcess();
529 } else {
530 // TODO(bunnei): This should also handle debug objects before returning.
531 UNIMPLEMENTED_MSG("Debug objects not implemented");
532 }
533
534 // Make sure the target process exists.
535 R_UNLESS(process != nullptr, ResultInvalidHandle);
536
537 // Get the process id.
538 *out_process_id = process->GetId();
539
540 return ResultSuccess;
541}
542
543static Result GetProcessId32(Core::System& system, u32* out_process_id_low,
544 u32* out_process_id_high, Handle handle) {
545 u64 out_process_id{};
546 const auto result = GetProcessId(system, &out_process_id, handle);
547 *out_process_id_low = static_cast<u32>(out_process_id);
548 *out_process_id_high = static_cast<u32>(out_process_id >> 32);
549 return result;
550}
551
552/// Wait for the given handles to synchronize, timeout after the specified nanoseconds
553static Result WaitSynchronization(Core::System& system, s32* index, VAddr handles_address,
554 s32 num_handles, s64 nano_seconds) {
555 LOG_TRACE(Kernel_SVC, "called handles_address=0x{:X}, num_handles={}, nano_seconds={}",
556 handles_address, num_handles, nano_seconds);
557
558 // Ensure number of handles is valid.
559 R_UNLESS(0 <= num_handles && num_handles <= ArgumentHandleCountMax, ResultOutOfRange);
560
561 auto& kernel = system.Kernel();
562 std::vector<KSynchronizationObject*> objs(num_handles);
563 const auto& handle_table = kernel.CurrentProcess()->GetHandleTable();
564 Handle* handles = system.Memory().GetPointer<Handle>(handles_address);
565
566 // Copy user handles.
567 if (num_handles > 0) {
568 // Convert the handles to objects.
569 R_UNLESS(handle_table.GetMultipleObjects<KSynchronizationObject>(objs.data(), handles,
570 num_handles),
571 ResultInvalidHandle);
572 for (const auto& obj : objs) {
573 kernel.RegisterInUseObject(obj);
574 }
575 }
576
577 // Ensure handles are closed when we're done.
578 SCOPE_EXIT({
579 for (s32 i = 0; i < num_handles; ++i) {
580 kernel.UnregisterInUseObject(objs[i]);
581 objs[i]->Close();
582 }
583 });
584
585 return KSynchronizationObject::Wait(kernel, index, objs.data(), static_cast<s32>(objs.size()),
586 nano_seconds);
587}
588
589static Result WaitSynchronization32(Core::System& system, u32 timeout_low, u32 handles_address,
590 s32 num_handles, u32 timeout_high, s32* index) {
591 const s64 nano_seconds{(static_cast<s64>(timeout_high) << 32) | static_cast<s64>(timeout_low)};
592 return WaitSynchronization(system, index, handles_address, num_handles, nano_seconds);
593}
594
595/// Resumes a thread waiting on WaitSynchronization
596static Result CancelSynchronization(Core::System& system, Handle handle) {
597 LOG_TRACE(Kernel_SVC, "called handle=0x{:X}", handle);
598
599 // Get the thread from its handle.
600 KScopedAutoObject thread =
601 system.Kernel().CurrentProcess()->GetHandleTable().GetObject<KThread>(handle);
602 R_UNLESS(thread.IsNotNull(), ResultInvalidHandle);
603
604 // Cancel the thread's wait.
605 thread->WaitCancel();
606 return ResultSuccess;
607}
608
609static Result CancelSynchronization32(Core::System& system, Handle handle) {
610 return CancelSynchronization(system, handle);
611}
612
613/// Attempts to locks a mutex
614static Result ArbitrateLock(Core::System& system, Handle thread_handle, VAddr address, u32 tag) {
615 LOG_TRACE(Kernel_SVC, "called thread_handle=0x{:08X}, address=0x{:X}, tag=0x{:08X}",
616 thread_handle, address, tag);
617
618 // Validate the input address.
619 if (IsKernelAddress(address)) {
620 LOG_ERROR(Kernel_SVC, "Attempting to arbitrate a lock on a kernel address (address={:08X})",
621 address);
622 return ResultInvalidCurrentMemory;
623 }
624 if (!Common::IsAligned(address, sizeof(u32))) {
625 LOG_ERROR(Kernel_SVC, "Input address must be 4 byte aligned (address: {:08X})", address);
626 return ResultInvalidAddress;
627 }
628
629 return system.Kernel().CurrentProcess()->WaitForAddress(thread_handle, address, tag);
630}
631
632static Result ArbitrateLock32(Core::System& system, Handle thread_handle, u32 address, u32 tag) {
633 return ArbitrateLock(system, thread_handle, address, tag);
634}
635
636/// Unlock a mutex
637static Result ArbitrateUnlock(Core::System& system, VAddr address) {
638 LOG_TRACE(Kernel_SVC, "called address=0x{:X}", address);
639
640 // Validate the input address.
641 if (IsKernelAddress(address)) {
642 LOG_ERROR(Kernel_SVC,
643 "Attempting to arbitrate an unlock on a kernel address (address={:08X})",
644 address);
645 return ResultInvalidCurrentMemory;
646 }
647 if (!Common::IsAligned(address, sizeof(u32))) {
648 LOG_ERROR(Kernel_SVC, "Input address must be 4 byte aligned (address: {:08X})", address);
649 return ResultInvalidAddress;
650 }
651
652 return system.Kernel().CurrentProcess()->SignalToAddress(address);
653}
654
655static Result ArbitrateUnlock32(Core::System& system, u32 address) {
656 return ArbitrateUnlock(system, address);
657}
658
659/// Break program execution
660static void Break(Core::System& system, u32 reason, u64 info1, u64 info2) {
661 BreakReason break_reason =
662 static_cast<BreakReason>(reason & ~static_cast<u32>(BreakReason::NotificationOnlyFlag));
663 bool notification_only = (reason & static_cast<u32>(BreakReason::NotificationOnlyFlag)) != 0;
664
665 bool has_dumped_buffer{};
666 std::vector<u8> debug_buffer;
667
668 const auto handle_debug_buffer = [&](VAddr addr, u64 sz) {
669 if (sz == 0 || addr == 0 || has_dumped_buffer) {
670 return;
671 }
672
673 auto& memory = system.Memory();
674
675 // This typically is an error code so we're going to assume this is the case
676 if (sz == sizeof(u32)) {
677 LOG_CRITICAL(Debug_Emulated, "debug_buffer_err_code={:X}", memory.Read32(addr));
678 } else {
679 // We don't know what's in here so we'll hexdump it
680 debug_buffer.resize(sz);
681 memory.ReadBlock(addr, debug_buffer.data(), sz);
682 std::string hexdump;
683 for (std::size_t i = 0; i < debug_buffer.size(); i++) {
684 hexdump += fmt::format("{:02X} ", debug_buffer[i]);
685 if (i != 0 && i % 16 == 0) {
686 hexdump += '\n';
687 }
688 }
689 LOG_CRITICAL(Debug_Emulated, "debug_buffer=\n{}", hexdump);
690 }
691 has_dumped_buffer = true;
692 };
693 switch (break_reason) {
694 case BreakReason::Panic:
695 LOG_CRITICAL(Debug_Emulated, "Userspace PANIC! info1=0x{:016X}, info2=0x{:016X}", info1,
696 info2);
697 handle_debug_buffer(info1, info2);
698 break;
699 case BreakReason::Assert:
700 LOG_CRITICAL(Debug_Emulated, "Userspace Assertion failed! info1=0x{:016X}, info2=0x{:016X}",
701 info1, info2);
702 handle_debug_buffer(info1, info2);
703 break;
704 case BreakReason::User:
705 LOG_WARNING(Debug_Emulated, "Userspace Break! 0x{:016X} with size 0x{:016X}", info1, info2);
706 handle_debug_buffer(info1, info2);
707 break;
708 case BreakReason::PreLoadDll:
709 LOG_INFO(Debug_Emulated,
710 "Userspace Attempting to load an NRO at 0x{:016X} with size 0x{:016X}", info1,
711 info2);
712 break;
713 case BreakReason::PostLoadDll:
714 LOG_INFO(Debug_Emulated, "Userspace Loaded an NRO at 0x{:016X} with size 0x{:016X}", info1,
715 info2);
716 break;
717 case BreakReason::PreUnloadDll:
718 LOG_INFO(Debug_Emulated,
719 "Userspace Attempting to unload an NRO at 0x{:016X} with size 0x{:016X}", info1,
720 info2);
721 break;
722 case BreakReason::PostUnloadDll:
723 LOG_INFO(Debug_Emulated, "Userspace Unloaded an NRO at 0x{:016X} with size 0x{:016X}",
724 info1, info2);
725 break;
726 case BreakReason::CppException:
727 LOG_CRITICAL(Debug_Emulated, "Signalling debugger. Uncaught C++ exception encountered.");
728 break;
729 default:
730 LOG_WARNING(
731 Debug_Emulated,
732 "Signalling debugger, Unknown break reason {:#X}, info1=0x{:016X}, info2=0x{:016X}",
733 reason, info1, info2);
734 handle_debug_buffer(info1, info2);
735 break;
736 }
737
738 system.GetReporter().SaveSvcBreakReport(reason, notification_only, info1, info2,
739 has_dumped_buffer ? std::make_optional(debug_buffer)
740 : std::nullopt);
741
742 if (!notification_only) {
743 LOG_CRITICAL(
744 Debug_Emulated,
745 "Emulated program broke execution! reason=0x{:016X}, info1=0x{:016X}, info2=0x{:016X}",
746 reason, info1, info2);
747
748 handle_debug_buffer(info1, info2);
749
750 auto* const current_thread = GetCurrentThreadPointer(system.Kernel());
751 const auto thread_processor_id = current_thread->GetActiveCore();
752 system.ArmInterface(static_cast<std::size_t>(thread_processor_id)).LogBacktrace();
753 }
754
755 if (system.DebuggerEnabled()) {
756 auto* thread = system.Kernel().GetCurrentEmuThread();
757 system.GetDebugger().NotifyThreadStopped(thread);
758 thread->RequestSuspend(Kernel::SuspendType::Debug);
759 }
760}
761
762static void Break32(Core::System& system, u32 reason, u32 info1, u32 info2) {
763 Break(system, reason, info1, info2);
764}
765
766/// Used to output a message on a debug hardware unit - does nothing on a retail unit
767static void OutputDebugString(Core::System& system, VAddr address, u64 len) {
768 if (len == 0) {
769 return;
770 }
771
772 std::string str(len, '\0');
773 system.Memory().ReadBlock(address, str.data(), str.size());
774 LOG_DEBUG(Debug_Emulated, "{}", str);
775}
776
777static void OutputDebugString32(Core::System& system, u32 address, u32 len) {
778 OutputDebugString(system, address, len);
779}
780
781/// Gets system/memory information for the current process
782static Result GetInfo(Core::System& system, u64* result, u64 info_id, Handle handle,
783 u64 info_sub_id) {
784 LOG_TRACE(Kernel_SVC, "called info_id=0x{:X}, info_sub_id=0x{:X}, handle=0x{:08X}", info_id,
785 info_sub_id, handle);
786
787 const auto info_id_type = static_cast<InfoType>(info_id);
788
789 switch (info_id_type) {
790 case InfoType::CoreMask:
791 case InfoType::PriorityMask:
792 case InfoType::AliasRegionAddress:
793 case InfoType::AliasRegionSize:
794 case InfoType::HeapRegionAddress:
795 case InfoType::HeapRegionSize:
796 case InfoType::AslrRegionAddress:
797 case InfoType::AslrRegionSize:
798 case InfoType::StackRegionAddress:
799 case InfoType::StackRegionSize:
800 case InfoType::TotalMemorySize:
801 case InfoType::UsedMemorySize:
802 case InfoType::SystemResourceSizeTotal:
803 case InfoType::SystemResourceSizeUsed:
804 case InfoType::ProgramId:
805 case InfoType::UserExceptionContextAddress:
806 case InfoType::TotalNonSystemMemorySize:
807 case InfoType::UsedNonSystemMemorySize:
808 case InfoType::IsApplication:
809 case InfoType::FreeThreadCount: {
810 if (info_sub_id != 0) {
811 LOG_ERROR(Kernel_SVC, "Info sub id is non zero! info_id={}, info_sub_id={}", info_id,
812 info_sub_id);
813 return ResultInvalidEnumValue;
814 }
815
816 const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
817 KScopedAutoObject process = handle_table.GetObject<KProcess>(handle);
818 if (process.IsNull()) {
819 LOG_ERROR(Kernel_SVC, "Process is not valid! info_id={}, info_sub_id={}, handle={:08X}",
820 info_id, info_sub_id, handle);
821 return ResultInvalidHandle;
822 }
823
824 switch (info_id_type) {
825 case InfoType::CoreMask:
826 *result = process->GetCoreMask();
827 return ResultSuccess;
828
829 case InfoType::PriorityMask:
830 *result = process->GetPriorityMask();
831 return ResultSuccess;
832
833 case InfoType::AliasRegionAddress:
834 *result = process->PageTable().GetAliasRegionStart();
835 return ResultSuccess;
836
837 case InfoType::AliasRegionSize:
838 *result = process->PageTable().GetAliasRegionSize();
839 return ResultSuccess;
840
841 case InfoType::HeapRegionAddress:
842 *result = process->PageTable().GetHeapRegionStart();
843 return ResultSuccess;
844
845 case InfoType::HeapRegionSize:
846 *result = process->PageTable().GetHeapRegionSize();
847 return ResultSuccess;
848
849 case InfoType::AslrRegionAddress:
850 *result = process->PageTable().GetAliasCodeRegionStart();
851 return ResultSuccess;
852
853 case InfoType::AslrRegionSize:
854 *result = process->PageTable().GetAliasCodeRegionSize();
855 return ResultSuccess;
856
857 case InfoType::StackRegionAddress:
858 *result = process->PageTable().GetStackRegionStart();
859 return ResultSuccess;
860
861 case InfoType::StackRegionSize:
862 *result = process->PageTable().GetStackRegionSize();
863 return ResultSuccess;
864
865 case InfoType::TotalMemorySize:
866 *result = process->GetTotalPhysicalMemoryAvailable();
867 return ResultSuccess;
868
869 case InfoType::UsedMemorySize:
870 *result = process->GetTotalPhysicalMemoryUsed();
871 return ResultSuccess;
872
873 case InfoType::SystemResourceSizeTotal:
874 *result = process->GetSystemResourceSize();
875 return ResultSuccess;
876
877 case InfoType::SystemResourceSizeUsed:
878 LOG_WARNING(Kernel_SVC, "(STUBBED) Attempted to query system resource usage");
879 *result = process->GetSystemResourceUsage();
880 return ResultSuccess;
881
882 case InfoType::ProgramId:
883 *result = process->GetProgramID();
884 return ResultSuccess;
885
886 case InfoType::UserExceptionContextAddress:
887 *result = process->GetProcessLocalRegionAddress();
888 return ResultSuccess;
889
890 case InfoType::TotalNonSystemMemorySize:
891 *result = process->GetTotalPhysicalMemoryAvailableWithoutSystemResource();
892 return ResultSuccess;
893
894 case InfoType::UsedNonSystemMemorySize:
895 *result = process->GetTotalPhysicalMemoryUsedWithoutSystemResource();
896 return ResultSuccess;
897
898 case InfoType::FreeThreadCount:
899 *result = process->GetFreeThreadCount();
900 return ResultSuccess;
901
902 default:
903 break;
904 }
905
906 LOG_ERROR(Kernel_SVC, "Unimplemented svcGetInfo id=0x{:016X}", info_id);
907 return ResultInvalidEnumValue;
908 }
909
910 case InfoType::DebuggerAttached:
911 *result = 0;
912 return ResultSuccess;
913
914 case InfoType::ResourceLimit: {
915 if (handle != 0) {
916 LOG_ERROR(Kernel, "Handle is non zero! handle={:08X}", handle);
917 return ResultInvalidHandle;
918 }
919
920 if (info_sub_id != 0) {
921 LOG_ERROR(Kernel, "Info sub id is non zero! info_id={}, info_sub_id={}", info_id,
922 info_sub_id);
923 return ResultInvalidCombination;
924 }
925
926 KProcess* const current_process = system.Kernel().CurrentProcess();
927 KHandleTable& handle_table = current_process->GetHandleTable();
928 const auto resource_limit = current_process->GetResourceLimit();
929 if (!resource_limit) {
930 *result = Svc::InvalidHandle;
931 // Yes, the kernel considers this a successful operation.
932 return ResultSuccess;
933 }
934
935 Handle resource_handle{};
936 R_TRY(handle_table.Add(&resource_handle, resource_limit));
937
938 *result = resource_handle;
939 return ResultSuccess;
940 }
941
942 case InfoType::RandomEntropy:
943 if (handle != 0) {
944 LOG_ERROR(Kernel_SVC, "Process Handle is non zero, expected 0 result but got {:016X}",
945 handle);
946 return ResultInvalidHandle;
947 }
948
949 if (info_sub_id >= KProcess::RANDOM_ENTROPY_SIZE) {
950 LOG_ERROR(Kernel_SVC, "Entropy size is out of range, expected {} but got {}",
951 KProcess::RANDOM_ENTROPY_SIZE, info_sub_id);
952 return ResultInvalidCombination;
953 }
954
955 *result = system.Kernel().CurrentProcess()->GetRandomEntropy(info_sub_id);
956 return ResultSuccess;
957
958 case InfoType::InitialProcessIdRange:
959 LOG_WARNING(Kernel_SVC,
960 "(STUBBED) Attempted to query privileged process id bounds, returned 0");
961 *result = 0;
962 return ResultSuccess;
963
964 case InfoType::ThreadTickCount: {
965 constexpr u64 num_cpus = 4;
966 if (info_sub_id != 0xFFFFFFFFFFFFFFFF && info_sub_id >= num_cpus) {
967 LOG_ERROR(Kernel_SVC, "Core count is out of range, expected {} but got {}", num_cpus,
968 info_sub_id);
969 return ResultInvalidCombination;
970 }
971
972 KScopedAutoObject thread =
973 system.Kernel().CurrentProcess()->GetHandleTable().GetObject<KThread>(
974 static_cast<Handle>(handle));
975 if (thread.IsNull()) {
976 LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle=0x{:08X}",
977 static_cast<Handle>(handle));
978 return ResultInvalidHandle;
979 }
980
981 const auto& core_timing = system.CoreTiming();
982 const auto& scheduler = *system.Kernel().CurrentScheduler();
983 const auto* const current_thread = GetCurrentThreadPointer(system.Kernel());
984 const bool same_thread = current_thread == thread.GetPointerUnsafe();
985
986 const u64 prev_ctx_ticks = scheduler.GetLastContextSwitchTime();
987 u64 out_ticks = 0;
988 if (same_thread && info_sub_id == 0xFFFFFFFFFFFFFFFF) {
989 const u64 thread_ticks = current_thread->GetCpuTime();
990
991 out_ticks = thread_ticks + (core_timing.GetCPUTicks() - prev_ctx_ticks);
992 } else if (same_thread && info_sub_id == system.Kernel().CurrentPhysicalCoreIndex()) {
993 out_ticks = core_timing.GetCPUTicks() - prev_ctx_ticks;
994 }
995
996 *result = out_ticks;
997 return ResultSuccess;
998 }
999 case InfoType::IdleTickCount: {
1000 // Verify the input handle is invalid.
1001 R_UNLESS(handle == InvalidHandle, ResultInvalidHandle);
1002
1003 // Verify the requested core is valid.
1004 const bool core_valid =
1005 (info_sub_id == 0xFFFFFFFFFFFFFFFF) ||
1006 (info_sub_id == static_cast<u64>(system.Kernel().CurrentPhysicalCoreIndex()));
1007 R_UNLESS(core_valid, ResultInvalidCombination);
1008
1009 // Get the idle tick count.
1010 *result = system.Kernel().CurrentScheduler()->GetIdleThread()->GetCpuTime();
1011 return ResultSuccess;
1012 }
1013 case InfoType::MesosphereCurrentProcess: {
1014 // Verify the input handle is invalid.
1015 R_UNLESS(handle == InvalidHandle, ResultInvalidHandle);
1016
1017 // Verify the sub-type is valid.
1018 R_UNLESS(info_sub_id == 0, ResultInvalidCombination);
1019
1020 // Get the handle table.
1021 KProcess* current_process = system.Kernel().CurrentProcess();
1022 KHandleTable& handle_table = current_process->GetHandleTable();
1023
1024 // Get a new handle for the current process.
1025 Handle tmp;
1026 R_TRY(handle_table.Add(&tmp, current_process));
1027
1028 // Set the output.
1029 *result = tmp;
1030
1031 // We succeeded.
1032 return ResultSuccess;
1033 }
1034 default:
1035 LOG_ERROR(Kernel_SVC, "Unimplemented svcGetInfo id=0x{:016X}", info_id);
1036 return ResultInvalidEnumValue;
1037 }
1038}
1039
1040static Result GetInfo32(Core::System& system, u32* result_low, u32* result_high, u32 sub_id_low,
1041 u32 info_id, u32 handle, u32 sub_id_high) {
1042 const u64 sub_id{u64{sub_id_low} | (u64{sub_id_high} << 32)};
1043 u64 res_value{};
1044
1045 const Result result{GetInfo(system, &res_value, info_id, handle, sub_id)};
1046 *result_high = static_cast<u32>(res_value >> 32);
1047 *result_low = static_cast<u32>(res_value & std::numeric_limits<u32>::max());
1048
1049 return result;
1050}
1051
1052/// Maps memory at a desired address
1053static Result MapPhysicalMemory(Core::System& system, VAddr addr, u64 size) {
1054 LOG_DEBUG(Kernel_SVC, "called, addr=0x{:016X}, size=0x{:X}", addr, size);
1055
1056 if (!Common::Is4KBAligned(addr)) {
1057 LOG_ERROR(Kernel_SVC, "Address is not aligned to 4KB, 0x{:016X}", addr);
1058 return ResultInvalidAddress;
1059 }
1060
1061 if (!Common::Is4KBAligned(size)) {
1062 LOG_ERROR(Kernel_SVC, "Size is not aligned to 4KB, 0x{:X}", size);
1063 return ResultInvalidSize;
1064 }
1065
1066 if (size == 0) {
1067 LOG_ERROR(Kernel_SVC, "Size is zero");
1068 return ResultInvalidSize;
1069 }
1070
1071 if (!(addr < addr + size)) {
1072 LOG_ERROR(Kernel_SVC, "Size causes 64-bit overflow of address");
1073 return ResultInvalidMemoryRegion;
1074 }
1075
1076 KProcess* const current_process{system.Kernel().CurrentProcess()};
1077 auto& page_table{current_process->PageTable()};
1078
1079 if (current_process->GetSystemResourceSize() == 0) {
1080 LOG_ERROR(Kernel_SVC, "System Resource Size is zero");
1081 return ResultInvalidState;
1082 }
1083
1084 if (!page_table.IsInsideAddressSpace(addr, size)) {
1085 LOG_ERROR(Kernel_SVC,
1086 "Address is not within the address space, addr=0x{:016X}, size=0x{:016X}", addr,
1087 size);
1088 return ResultInvalidMemoryRegion;
1089 }
1090
1091 if (page_table.IsOutsideAliasRegion(addr, size)) {
1092 LOG_ERROR(Kernel_SVC,
1093 "Address is not within the alias region, addr=0x{:016X}, size=0x{:016X}", addr,
1094 size);
1095 return ResultInvalidMemoryRegion;
1096 }
1097
1098 return page_table.MapPhysicalMemory(addr, size);
1099}
1100
1101static Result MapPhysicalMemory32(Core::System& system, u32 addr, u32 size) {
1102 return MapPhysicalMemory(system, addr, size);
1103}
1104
1105/// Unmaps memory previously mapped via MapPhysicalMemory
1106static Result UnmapPhysicalMemory(Core::System& system, VAddr addr, u64 size) {
1107 LOG_DEBUG(Kernel_SVC, "called, addr=0x{:016X}, size=0x{:X}", addr, size);
1108
1109 if (!Common::Is4KBAligned(addr)) {
1110 LOG_ERROR(Kernel_SVC, "Address is not aligned to 4KB, 0x{:016X}", addr);
1111 return ResultInvalidAddress;
1112 }
1113
1114 if (!Common::Is4KBAligned(size)) {
1115 LOG_ERROR(Kernel_SVC, "Size is not aligned to 4KB, 0x{:X}", size);
1116 return ResultInvalidSize;
1117 }
1118
1119 if (size == 0) {
1120 LOG_ERROR(Kernel_SVC, "Size is zero");
1121 return ResultInvalidSize;
1122 }
1123
1124 if (!(addr < addr + size)) {
1125 LOG_ERROR(Kernel_SVC, "Size causes 64-bit overflow of address");
1126 return ResultInvalidMemoryRegion;
1127 }
1128
1129 KProcess* const current_process{system.Kernel().CurrentProcess()};
1130 auto& page_table{current_process->PageTable()};
1131
1132 if (current_process->GetSystemResourceSize() == 0) {
1133 LOG_ERROR(Kernel_SVC, "System Resource Size is zero");
1134 return ResultInvalidState;
1135 }
1136
1137 if (!page_table.IsInsideAddressSpace(addr, size)) {
1138 LOG_ERROR(Kernel_SVC,
1139 "Address is not within the address space, addr=0x{:016X}, size=0x{:016X}", addr,
1140 size);
1141 return ResultInvalidMemoryRegion;
1142 }
1143
1144 if (page_table.IsOutsideAliasRegion(addr, size)) {
1145 LOG_ERROR(Kernel_SVC,
1146 "Address is not within the alias region, addr=0x{:016X}, size=0x{:016X}", addr,
1147 size);
1148 return ResultInvalidMemoryRegion;
1149 }
1150
1151 return page_table.UnmapPhysicalMemory(addr, size);
1152}
1153
1154static Result UnmapPhysicalMemory32(Core::System& system, u32 addr, u32 size) {
1155 return UnmapPhysicalMemory(system, addr, size);
1156}
1157
1158/// Sets the thread activity
1159static Result SetThreadActivity(Core::System& system, Handle thread_handle,
1160 ThreadActivity thread_activity) {
1161 LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, activity=0x{:08X}", thread_handle,
1162 thread_activity);
1163
1164 // Validate the activity.
1165 constexpr auto IsValidThreadActivity = [](ThreadActivity activity) {
1166 return activity == ThreadActivity::Runnable || activity == ThreadActivity::Paused;
1167 };
1168 R_UNLESS(IsValidThreadActivity(thread_activity), ResultInvalidEnumValue);
1169
1170 // Get the thread from its handle.
1171 KScopedAutoObject thread =
1172 system.Kernel().CurrentProcess()->GetHandleTable().GetObject<KThread>(thread_handle);
1173 R_UNLESS(thread.IsNotNull(), ResultInvalidHandle);
1174
1175 // Check that the activity is being set on a non-current thread for the current process.
1176 R_UNLESS(thread->GetOwnerProcess() == system.Kernel().CurrentProcess(), ResultInvalidHandle);
1177 R_UNLESS(thread.GetPointerUnsafe() != GetCurrentThreadPointer(system.Kernel()), ResultBusy);
1178
1179 // Set the activity.
1180 R_TRY(thread->SetActivity(thread_activity));
1181
1182 return ResultSuccess;
1183}
1184
1185static Result SetThreadActivity32(Core::System& system, Handle thread_handle,
1186 Svc::ThreadActivity thread_activity) {
1187 return SetThreadActivity(system, thread_handle, thread_activity);
1188}
1189
1190/// Gets the thread context
1191static Result GetThreadContext(Core::System& system, VAddr out_context, Handle thread_handle) {
1192 LOG_DEBUG(Kernel_SVC, "called, out_context=0x{:08X}, thread_handle=0x{:X}", out_context,
1193 thread_handle);
1194
1195 auto& kernel = system.Kernel();
1196
1197 // Get the thread from its handle.
1198 KScopedAutoObject thread =
1199 kernel.CurrentProcess()->GetHandleTable().GetObject<KThread>(thread_handle);
1200 R_UNLESS(thread.IsNotNull(), ResultInvalidHandle);
1201
1202 // Require the handle be to a non-current thread in the current process.
1203 const auto* current_process = kernel.CurrentProcess();
1204 R_UNLESS(current_process == thread->GetOwnerProcess(), ResultInvalidId);
1205
1206 // Verify that the thread isn't terminated.
1207 R_UNLESS(thread->GetState() != ThreadState::Terminated, ResultTerminationRequested);
1208
1209 /// Check that the thread is not the current one.
1210 /// NOTE: Nintendo does not check this, and thus the following loop will deadlock.
1211 R_UNLESS(thread.GetPointerUnsafe() != GetCurrentThreadPointer(kernel), ResultInvalidId);
1212
1213 // Try to get the thread context until the thread isn't current on any core.
1214 while (true) {
1215 KScopedSchedulerLock sl{kernel};
1216
1217 // TODO(bunnei): Enforce that thread is suspended for debug here.
1218
1219 // If the thread's raw state isn't runnable, check if it's current on some core.
1220 if (thread->GetRawState() != ThreadState::Runnable) {
1221 bool current = false;
1222 for (auto i = 0; i < static_cast<s32>(Core::Hardware::NUM_CPU_CORES); ++i) {
1223 if (thread.GetPointerUnsafe() == kernel.Scheduler(i).GetSchedulerCurrentThread()) {
1224 current = true;
1225 break;
1226 }
1227 }
1228
1229 // If the thread is current, retry until it isn't.
1230 if (current) {
1231 continue;
1232 }
1233 }
1234
1235 // Get the thread context.
1236 std::vector<u8> context;
1237 R_TRY(thread->GetThreadContext3(context));
1238
1239 // Copy the thread context to user space.
1240 system.Memory().WriteBlock(out_context, context.data(), context.size());
1241
1242 return ResultSuccess;
1243 }
1244
1245 return ResultSuccess;
1246}
1247
1248static Result GetThreadContext32(Core::System& system, u32 out_context, Handle thread_handle) {
1249 return GetThreadContext(system, out_context, thread_handle);
1250}
1251
1252/// Gets the priority for the specified thread
1253static Result GetThreadPriority(Core::System& system, u32* out_priority, Handle handle) {
1254 LOG_TRACE(Kernel_SVC, "called");
1255
1256 // Get the thread from its handle.
1257 KScopedAutoObject thread =
1258 system.Kernel().CurrentProcess()->GetHandleTable().GetObject<KThread>(handle);
1259 R_UNLESS(thread.IsNotNull(), ResultInvalidHandle);
1260
1261 // Get the thread's priority.
1262 *out_priority = thread->GetPriority();
1263 return ResultSuccess;
1264}
1265
1266static Result GetThreadPriority32(Core::System& system, u32* out_priority, Handle handle) {
1267 return GetThreadPriority(system, out_priority, handle);
1268}
1269
1270/// Sets the priority for the specified thread
1271static Result SetThreadPriority(Core::System& system, Handle thread_handle, u32 priority) {
1272 // Get the current process.
1273 KProcess& process = *system.Kernel().CurrentProcess();
1274
1275 // Validate the priority.
1276 R_UNLESS(HighestThreadPriority <= priority && priority <= LowestThreadPriority,
1277 ResultInvalidPriority);
1278 R_UNLESS(process.CheckThreadPriority(priority), ResultInvalidPriority);
1279
1280 // Get the thread from its handle.
1281 KScopedAutoObject thread = process.GetHandleTable().GetObject<KThread>(thread_handle);
1282 R_UNLESS(thread.IsNotNull(), ResultInvalidHandle);
1283
1284 // Set the thread priority.
1285 thread->SetBasePriority(priority);
1286 return ResultSuccess;
1287}
1288
1289static Result SetThreadPriority32(Core::System& system, Handle thread_handle, u32 priority) {
1290 return SetThreadPriority(system, thread_handle, priority);
1291}
1292
1293/// Get which CPU core is executing the current thread
1294static u32 GetCurrentProcessorNumber(Core::System& system) {
1295 LOG_TRACE(Kernel_SVC, "called");
1296 return static_cast<u32>(system.CurrentPhysicalCore().CoreIndex());
1297}
1298
1299static u32 GetCurrentProcessorNumber32(Core::System& system) {
1300 return GetCurrentProcessorNumber(system);
1301}
1302
1303namespace {
1304
1305constexpr bool IsValidSharedMemoryPermission(Svc::MemoryPermission perm) {
1306 switch (perm) {
1307 case Svc::MemoryPermission::Read:
1308 case Svc::MemoryPermission::ReadWrite:
1309 return true;
1310 default:
1311 return false;
1312 }
1313}
1314
1315[[maybe_unused]] constexpr bool IsValidRemoteSharedMemoryPermission(Svc::MemoryPermission perm) {
1316 return IsValidSharedMemoryPermission(perm) || perm == Svc::MemoryPermission::DontCare;
1317}
1318
1319constexpr bool IsValidProcessMemoryPermission(Svc::MemoryPermission perm) {
1320 switch (perm) {
1321 case Svc::MemoryPermission::None:
1322 case Svc::MemoryPermission::Read:
1323 case Svc::MemoryPermission::ReadWrite:
1324 case Svc::MemoryPermission::ReadExecute:
1325 return true;
1326 default:
1327 return false;
1328 }
1329}
1330
1331constexpr bool IsValidMapCodeMemoryPermission(Svc::MemoryPermission perm) {
1332 return perm == Svc::MemoryPermission::ReadWrite;
1333}
1334
1335constexpr bool IsValidMapToOwnerCodeMemoryPermission(Svc::MemoryPermission perm) {
1336 return perm == Svc::MemoryPermission::Read || perm == Svc::MemoryPermission::ReadExecute;
1337}
1338
1339constexpr bool IsValidUnmapCodeMemoryPermission(Svc::MemoryPermission perm) {
1340 return perm == Svc::MemoryPermission::None;
1341}
1342
1343constexpr bool IsValidUnmapFromOwnerCodeMemoryPermission(Svc::MemoryPermission perm) {
1344 return perm == Svc::MemoryPermission::None;
1345}
1346
1347} // Anonymous namespace
1348
1349static Result MapSharedMemory(Core::System& system, Handle shmem_handle, VAddr address, u64 size,
1350 Svc::MemoryPermission map_perm) {
1351 LOG_TRACE(Kernel_SVC,
1352 "called, shared_memory_handle=0x{:X}, addr=0x{:X}, size=0x{:X}, permissions=0x{:08X}",
1353 shmem_handle, address, size, map_perm);
1354
1355 // Validate the address/size.
1356 R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress);
1357 R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize);
1358 R_UNLESS(size > 0, ResultInvalidSize);
1359 R_UNLESS((address < address + size), ResultInvalidCurrentMemory);
1360
1361 // Validate the permission.
1362 R_UNLESS(IsValidSharedMemoryPermission(map_perm), ResultInvalidNewMemoryPermission);
1363
1364 // Get the current process.
1365 auto& process = *system.Kernel().CurrentProcess();
1366 auto& page_table = process.PageTable();
1367
1368 // Get the shared memory.
1369 KScopedAutoObject shmem = process.GetHandleTable().GetObject<KSharedMemory>(shmem_handle);
1370 R_UNLESS(shmem.IsNotNull(), ResultInvalidHandle);
1371
1372 // Verify that the mapping is in range.
1373 R_UNLESS(page_table.CanContain(address, size, KMemoryState::Shared), ResultInvalidMemoryRegion);
1374
1375 // Add the shared memory to the process.
1376 R_TRY(process.AddSharedMemory(shmem.GetPointerUnsafe(), address, size));
1377
1378 // Ensure that we clean up the shared memory if we fail to map it.
1379 auto guard =
1380 SCOPE_GUARD({ process.RemoveSharedMemory(shmem.GetPointerUnsafe(), address, size); });
1381
1382 // Map the shared memory.
1383 R_TRY(shmem->Map(process, address, size, map_perm));
1384
1385 // We succeeded.
1386 guard.Cancel();
1387 return ResultSuccess;
1388}
1389
1390static Result MapSharedMemory32(Core::System& system, Handle shmem_handle, u32 address, u32 size,
1391 Svc::MemoryPermission map_perm) {
1392 return MapSharedMemory(system, shmem_handle, address, size, map_perm);
1393}
1394
1395static Result UnmapSharedMemory(Core::System& system, Handle shmem_handle, VAddr address,
1396 u64 size) {
1397 // Validate the address/size.
1398 R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress);
1399 R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize);
1400 R_UNLESS(size > 0, ResultInvalidSize);
1401 R_UNLESS((address < address + size), ResultInvalidCurrentMemory);
1402
1403 // Get the current process.
1404 auto& process = *system.Kernel().CurrentProcess();
1405 auto& page_table = process.PageTable();
1406
1407 // Get the shared memory.
1408 KScopedAutoObject shmem = process.GetHandleTable().GetObject<KSharedMemory>(shmem_handle);
1409 R_UNLESS(shmem.IsNotNull(), ResultInvalidHandle);
1410
1411 // Verify that the mapping is in range.
1412 R_UNLESS(page_table.CanContain(address, size, KMemoryState::Shared), ResultInvalidMemoryRegion);
1413
1414 // Unmap the shared memory.
1415 R_TRY(shmem->Unmap(process, address, size));
1416
1417 // Remove the shared memory from the process.
1418 process.RemoveSharedMemory(shmem.GetPointerUnsafe(), address, size);
1419
1420 return ResultSuccess;
1421}
1422
1423static Result UnmapSharedMemory32(Core::System& system, Handle shmem_handle, u32 address,
1424 u32 size) {
1425 return UnmapSharedMemory(system, shmem_handle, address, size);
1426}
1427
1428static Result SetProcessMemoryPermission(Core::System& system, Handle process_handle, VAddr address,
1429 u64 size, Svc::MemoryPermission perm) {
1430 LOG_TRACE(Kernel_SVC,
1431 "called, process_handle=0x{:X}, addr=0x{:X}, size=0x{:X}, permissions=0x{:08X}",
1432 process_handle, address, size, perm);
1433
1434 // Validate the address/size.
1435 R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress);
1436 R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize);
1437 R_UNLESS(size > 0, ResultInvalidSize);
1438 R_UNLESS((address < address + size), ResultInvalidCurrentMemory);
1439 R_UNLESS(address == static_cast<uintptr_t>(address), ResultInvalidCurrentMemory);
1440 R_UNLESS(size == static_cast<size_t>(size), ResultInvalidCurrentMemory);
1441
1442 // Validate the memory permission.
1443 R_UNLESS(IsValidProcessMemoryPermission(perm), ResultInvalidNewMemoryPermission);
1444
1445 // Get the process from its handle.
1446 KScopedAutoObject process =
1447 system.CurrentProcess()->GetHandleTable().GetObject<KProcess>(process_handle);
1448 R_UNLESS(process.IsNotNull(), ResultInvalidHandle);
1449
1450 // Validate that the address is in range.
1451 auto& page_table = process->PageTable();
1452 R_UNLESS(page_table.Contains(address, size), ResultInvalidCurrentMemory);
1453
1454 // Set the memory permission.
1455 return page_table.SetProcessMemoryPermission(address, size, perm);
1456}
1457
1458static Result MapProcessMemory(Core::System& system, VAddr dst_address, Handle process_handle,
1459 VAddr src_address, u64 size) {
1460 LOG_TRACE(Kernel_SVC,
1461 "called, dst_address=0x{:X}, process_handle=0x{:X}, src_address=0x{:X}, size=0x{:X}",
1462 dst_address, process_handle, src_address, size);
1463
1464 // Validate the address/size.
1465 R_UNLESS(Common::IsAligned(dst_address, PageSize), ResultInvalidAddress);
1466 R_UNLESS(Common::IsAligned(src_address, PageSize), ResultInvalidAddress);
1467 R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize);
1468 R_UNLESS(size > 0, ResultInvalidSize);
1469 R_UNLESS((dst_address < dst_address + size), ResultInvalidCurrentMemory);
1470 R_UNLESS((src_address < src_address + size), ResultInvalidCurrentMemory);
1471
1472 // Get the processes.
1473 KProcess* dst_process = system.CurrentProcess();
1474 KScopedAutoObject src_process =
1475 dst_process->GetHandleTable().GetObjectWithoutPseudoHandle<KProcess>(process_handle);
1476 R_UNLESS(src_process.IsNotNull(), ResultInvalidHandle);
1477
1478 // Get the page tables.
1479 auto& dst_pt = dst_process->PageTable();
1480 auto& src_pt = src_process->PageTable();
1481
1482 // Validate that the mapping is in range.
1483 R_UNLESS(src_pt.Contains(src_address, size), ResultInvalidCurrentMemory);
1484 R_UNLESS(dst_pt.CanContain(dst_address, size, KMemoryState::SharedCode),
1485 ResultInvalidMemoryRegion);
1486
1487 // Create a new page group.
1488 KPageGroup pg{system.Kernel(), dst_pt.GetBlockInfoManager()};
1489 R_TRY(src_pt.MakeAndOpenPageGroup(
1490 std::addressof(pg), src_address, size / PageSize, KMemoryState::FlagCanMapProcess,
1491 KMemoryState::FlagCanMapProcess, KMemoryPermission::None, KMemoryPermission::None,
1492 KMemoryAttribute::All, KMemoryAttribute::None));
1493
1494 // Map the group.
1495 R_TRY(dst_pt.MapPageGroup(dst_address, pg, KMemoryState::SharedCode,
1496 KMemoryPermission::UserReadWrite));
1497
1498 return ResultSuccess;
1499}
1500
1501static Result UnmapProcessMemory(Core::System& system, VAddr dst_address, Handle process_handle,
1502 VAddr src_address, u64 size) {
1503 LOG_TRACE(Kernel_SVC,
1504 "called, dst_address=0x{:X}, process_handle=0x{:X}, src_address=0x{:X}, size=0x{:X}",
1505 dst_address, process_handle, src_address, size);
1506
1507 // Validate the address/size.
1508 R_UNLESS(Common::IsAligned(dst_address, PageSize), ResultInvalidAddress);
1509 R_UNLESS(Common::IsAligned(src_address, PageSize), ResultInvalidAddress);
1510 R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize);
1511 R_UNLESS(size > 0, ResultInvalidSize);
1512 R_UNLESS((dst_address < dst_address + size), ResultInvalidCurrentMemory);
1513 R_UNLESS((src_address < src_address + size), ResultInvalidCurrentMemory);
1514
1515 // Get the processes.
1516 KProcess* dst_process = system.CurrentProcess();
1517 KScopedAutoObject src_process =
1518 dst_process->GetHandleTable().GetObjectWithoutPseudoHandle<KProcess>(process_handle);
1519 R_UNLESS(src_process.IsNotNull(), ResultInvalidHandle);
1520
1521 // Get the page tables.
1522 auto& dst_pt = dst_process->PageTable();
1523 auto& src_pt = src_process->PageTable();
1524
1525 // Validate that the mapping is in range.
1526 R_UNLESS(src_pt.Contains(src_address, size), ResultInvalidCurrentMemory);
1527 R_UNLESS(dst_pt.CanContain(dst_address, size, KMemoryState::SharedCode),
1528 ResultInvalidMemoryRegion);
1529
1530 // Unmap the memory.
1531 R_TRY(dst_pt.UnmapProcessMemory(dst_address, size, src_pt, src_address));
1532
1533 return ResultSuccess;
1534}
1535
1536static Result CreateCodeMemory(Core::System& system, Handle* out, VAddr address, size_t size) {
1537 LOG_TRACE(Kernel_SVC, "called, address=0x{:X}, size=0x{:X}", address, size);
1538
1539 // Get kernel instance.
1540 auto& kernel = system.Kernel();
1541
1542 // Validate address / size.
1543 R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress);
1544 R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize);
1545 R_UNLESS(size > 0, ResultInvalidSize);
1546 R_UNLESS((address < address + size), ResultInvalidCurrentMemory);
1547
1548 // Create the code memory.
1549
1550 KCodeMemory* code_mem = KCodeMemory::Create(kernel);
1551 R_UNLESS(code_mem != nullptr, ResultOutOfResource);
1552
1553 // Verify that the region is in range.
1554 R_UNLESS(system.CurrentProcess()->PageTable().Contains(address, size),
1555 ResultInvalidCurrentMemory);
1556
1557 // Initialize the code memory.
1558 R_TRY(code_mem->Initialize(system.DeviceMemory(), address, size));
1559
1560 // Register the code memory.
1561 KCodeMemory::Register(kernel, code_mem);
1562
1563 // Add the code memory to the handle table.
1564 R_TRY(system.CurrentProcess()->GetHandleTable().Add(out, code_mem));
1565
1566 code_mem->Close();
1567
1568 return ResultSuccess;
1569}
1570
1571static Result CreateCodeMemory32(Core::System& system, Handle* out, u32 address, u32 size) {
1572 return CreateCodeMemory(system, out, address, size);
1573}
1574
1575static Result ControlCodeMemory(Core::System& system, Handle code_memory_handle, u32 operation,
1576 VAddr address, size_t size, Svc::MemoryPermission perm) {
1577
1578 LOG_TRACE(Kernel_SVC,
1579 "called, code_memory_handle=0x{:X}, operation=0x{:X}, address=0x{:X}, size=0x{:X}, "
1580 "permission=0x{:X}",
1581 code_memory_handle, operation, address, size, perm);
1582
1583 // Validate the address / size.
1584 R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress);
1585 R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize);
1586 R_UNLESS(size > 0, ResultInvalidSize);
1587 R_UNLESS((address < address + size), ResultInvalidCurrentMemory);
1588
1589 // Get the code memory from its handle.
1590 KScopedAutoObject code_mem =
1591 system.CurrentProcess()->GetHandleTable().GetObject<KCodeMemory>(code_memory_handle);
1592 R_UNLESS(code_mem.IsNotNull(), ResultInvalidHandle);
1593
1594 // NOTE: Here, Atmosphere extends the SVC to allow code memory operations on one's own process.
1595 // This enables homebrew usage of these SVCs for JIT.
1596
1597 // Perform the operation.
1598 switch (static_cast<CodeMemoryOperation>(operation)) {
1599 case CodeMemoryOperation::Map: {
1600 // Check that the region is in range.
1601 R_UNLESS(
1602 system.CurrentProcess()->PageTable().CanContain(address, size, KMemoryState::CodeOut),
1603 ResultInvalidMemoryRegion);
1604
1605 // Check the memory permission.
1606 R_UNLESS(IsValidMapCodeMemoryPermission(perm), ResultInvalidNewMemoryPermission);
1607
1608 // Map the memory.
1609 R_TRY(code_mem->Map(address, size));
1610 } break;
1611 case CodeMemoryOperation::Unmap: {
1612 // Check that the region is in range.
1613 R_UNLESS(
1614 system.CurrentProcess()->PageTable().CanContain(address, size, KMemoryState::CodeOut),
1615 ResultInvalidMemoryRegion);
1616
1617 // Check the memory permission.
1618 R_UNLESS(IsValidUnmapCodeMemoryPermission(perm), ResultInvalidNewMemoryPermission);
1619
1620 // Unmap the memory.
1621 R_TRY(code_mem->Unmap(address, size));
1622 } break;
1623 case CodeMemoryOperation::MapToOwner: {
1624 // Check that the region is in range.
1625 R_UNLESS(code_mem->GetOwner()->PageTable().CanContain(address, size,
1626 KMemoryState::GeneratedCode),
1627 ResultInvalidMemoryRegion);
1628
1629 // Check the memory permission.
1630 R_UNLESS(IsValidMapToOwnerCodeMemoryPermission(perm), ResultInvalidNewMemoryPermission);
1631
1632 // Map the memory to its owner.
1633 R_TRY(code_mem->MapToOwner(address, size, perm));
1634 } break;
1635 case CodeMemoryOperation::UnmapFromOwner: {
1636 // Check that the region is in range.
1637 R_UNLESS(code_mem->GetOwner()->PageTable().CanContain(address, size,
1638 KMemoryState::GeneratedCode),
1639 ResultInvalidMemoryRegion);
1640
1641 // Check the memory permission.
1642 R_UNLESS(IsValidUnmapFromOwnerCodeMemoryPermission(perm), ResultInvalidNewMemoryPermission);
1643
1644 // Unmap the memory from its owner.
1645 R_TRY(code_mem->UnmapFromOwner(address, size));
1646 } break;
1647 default:
1648 return ResultInvalidEnumValue;
1649 }
1650
1651 return ResultSuccess;
1652}
1653
1654static Result ControlCodeMemory32(Core::System& system, Handle code_memory_handle, u32 operation,
1655 u64 address, u64 size, Svc::MemoryPermission perm) {
1656 return ControlCodeMemory(system, code_memory_handle, operation, address, size, perm);
1657}
1658
1659static Result QueryProcessMemory(Core::System& system, VAddr memory_info_address,
1660 VAddr page_info_address, Handle process_handle, VAddr address) {
1661 LOG_TRACE(Kernel_SVC, "called process=0x{:08X} address={:X}", process_handle, address);
1662 const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
1663 KScopedAutoObject process = handle_table.GetObject<KProcess>(process_handle);
1664 if (process.IsNull()) {
1665 LOG_ERROR(Kernel_SVC, "Process handle does not exist, process_handle=0x{:08X}",
1666 process_handle);
1667 return ResultInvalidHandle;
1668 }
1669
1670 auto& memory{system.Memory()};
1671 const auto memory_info{process->PageTable().QueryInfo(address).GetSvcMemoryInfo()};
1672
1673 memory.Write64(memory_info_address + 0x00, memory_info.base_address);
1674 memory.Write64(memory_info_address + 0x08, memory_info.size);
1675 memory.Write32(memory_info_address + 0x10, static_cast<u32>(memory_info.state) & 0xff);
1676 memory.Write32(memory_info_address + 0x14, static_cast<u32>(memory_info.attribute));
1677 memory.Write32(memory_info_address + 0x18, static_cast<u32>(memory_info.permission));
1678 memory.Write32(memory_info_address + 0x1c, memory_info.ipc_count);
1679 memory.Write32(memory_info_address + 0x20, memory_info.device_count);
1680 memory.Write32(memory_info_address + 0x24, 0);
1681
1682 // Page info appears to be currently unused by the kernel and is always set to zero.
1683 memory.Write32(page_info_address, 0);
1684
1685 return ResultSuccess;
1686}
1687
1688static Result QueryMemory(Core::System& system, VAddr memory_info_address, VAddr page_info_address,
1689 VAddr query_address) {
1690 LOG_TRACE(Kernel_SVC,
1691 "called, memory_info_address=0x{:016X}, page_info_address=0x{:016X}, "
1692 "query_address=0x{:016X}",
1693 memory_info_address, page_info_address, query_address);
1694
1695 return QueryProcessMemory(system, memory_info_address, page_info_address, CurrentProcess,
1696 query_address);
1697}
1698
1699static Result QueryMemory32(Core::System& system, u32 memory_info_address, u32 page_info_address,
1700 u32 query_address) {
1701 return QueryMemory(system, memory_info_address, page_info_address, query_address);
1702}
1703
1704static Result MapProcessCodeMemory(Core::System& system, Handle process_handle, u64 dst_address,
1705 u64 src_address, u64 size) {
1706 LOG_DEBUG(Kernel_SVC,
1707 "called. process_handle=0x{:08X}, dst_address=0x{:016X}, "
1708 "src_address=0x{:016X}, size=0x{:016X}",
1709 process_handle, dst_address, src_address, size);
1710
1711 if (!Common::Is4KBAligned(src_address)) {
1712 LOG_ERROR(Kernel_SVC, "src_address is not page-aligned (src_address=0x{:016X}).",
1713 src_address);
1714 return ResultInvalidAddress;
1715 }
1716
1717 if (!Common::Is4KBAligned(dst_address)) {
1718 LOG_ERROR(Kernel_SVC, "dst_address is not page-aligned (dst_address=0x{:016X}).",
1719 dst_address);
1720 return ResultInvalidAddress;
1721 }
1722
1723 if (size == 0 || !Common::Is4KBAligned(size)) {
1724 LOG_ERROR(Kernel_SVC, "Size is zero or not page-aligned (size=0x{:016X})", size);
1725 return ResultInvalidSize;
1726 }
1727
1728 if (!IsValidAddressRange(dst_address, size)) {
1729 LOG_ERROR(Kernel_SVC,
1730 "Destination address range overflows the address space (dst_address=0x{:016X}, "
1731 "size=0x{:016X}).",
1732 dst_address, size);
1733 return ResultInvalidCurrentMemory;
1734 }
1735
1736 if (!IsValidAddressRange(src_address, size)) {
1737 LOG_ERROR(Kernel_SVC,
1738 "Source address range overflows the address space (src_address=0x{:016X}, "
1739 "size=0x{:016X}).",
1740 src_address, size);
1741 return ResultInvalidCurrentMemory;
1742 }
1743
1744 const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
1745 KScopedAutoObject process = handle_table.GetObject<KProcess>(process_handle);
1746 if (process.IsNull()) {
1747 LOG_ERROR(Kernel_SVC, "Invalid process handle specified (handle=0x{:08X}).",
1748 process_handle);
1749 return ResultInvalidHandle;
1750 }
1751
1752 auto& page_table = process->PageTable();
1753 if (!page_table.IsInsideAddressSpace(src_address, size)) {
1754 LOG_ERROR(Kernel_SVC,
1755 "Source address range is not within the address space (src_address=0x{:016X}, "
1756 "size=0x{:016X}).",
1757 src_address, size);
1758 return ResultInvalidCurrentMemory;
1759 }
1760
1761 if (!page_table.IsInsideASLRRegion(dst_address, size)) {
1762 LOG_ERROR(Kernel_SVC,
1763 "Destination address range is not within the ASLR region (dst_address=0x{:016X}, "
1764 "size=0x{:016X}).",
1765 dst_address, size);
1766 return ResultInvalidMemoryRegion;
1767 }
1768
1769 return page_table.MapCodeMemory(dst_address, src_address, size);
1770}
1771
1772static Result UnmapProcessCodeMemory(Core::System& system, Handle process_handle, u64 dst_address,
1773 u64 src_address, u64 size) {
1774 LOG_DEBUG(Kernel_SVC,
1775 "called. process_handle=0x{:08X}, dst_address=0x{:016X}, src_address=0x{:016X}, "
1776 "size=0x{:016X}",
1777 process_handle, dst_address, src_address, size);
1778
1779 if (!Common::Is4KBAligned(dst_address)) {
1780 LOG_ERROR(Kernel_SVC, "dst_address is not page-aligned (dst_address=0x{:016X}).",
1781 dst_address);
1782 return ResultInvalidAddress;
1783 }
1784
1785 if (!Common::Is4KBAligned(src_address)) {
1786 LOG_ERROR(Kernel_SVC, "src_address is not page-aligned (src_address=0x{:016X}).",
1787 src_address);
1788 return ResultInvalidAddress;
1789 }
1790
1791 if (size == 0 || !Common::Is4KBAligned(size)) {
1792 LOG_ERROR(Kernel_SVC, "Size is zero or not page-aligned (size=0x{:016X}).", size);
1793 return ResultInvalidSize;
1794 }
1795
1796 if (!IsValidAddressRange(dst_address, size)) {
1797 LOG_ERROR(Kernel_SVC,
1798 "Destination address range overflows the address space (dst_address=0x{:016X}, "
1799 "size=0x{:016X}).",
1800 dst_address, size);
1801 return ResultInvalidCurrentMemory;
1802 }
1803
1804 if (!IsValidAddressRange(src_address, size)) {
1805 LOG_ERROR(Kernel_SVC,
1806 "Source address range overflows the address space (src_address=0x{:016X}, "
1807 "size=0x{:016X}).",
1808 src_address, size);
1809 return ResultInvalidCurrentMemory;
1810 }
1811
1812 const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
1813 KScopedAutoObject process = handle_table.GetObject<KProcess>(process_handle);
1814 if (process.IsNull()) {
1815 LOG_ERROR(Kernel_SVC, "Invalid process handle specified (handle=0x{:08X}).",
1816 process_handle);
1817 return ResultInvalidHandle;
1818 }
1819
1820 auto& page_table = process->PageTable();
1821 if (!page_table.IsInsideAddressSpace(src_address, size)) {
1822 LOG_ERROR(Kernel_SVC,
1823 "Source address range is not within the address space (src_address=0x{:016X}, "
1824 "size=0x{:016X}).",
1825 src_address, size);
1826 return ResultInvalidCurrentMemory;
1827 }
1828
1829 if (!page_table.IsInsideASLRRegion(dst_address, size)) {
1830 LOG_ERROR(Kernel_SVC,
1831 "Destination address range is not within the ASLR region (dst_address=0x{:016X}, "
1832 "size=0x{:016X}).",
1833 dst_address, size);
1834 return ResultInvalidMemoryRegion;
1835 }
1836
1837 return page_table.UnmapCodeMemory(dst_address, src_address, size,
1838 KPageTable::ICacheInvalidationStrategy::InvalidateAll);
1839}
1840
1841/// Exits the current process
1842static void ExitProcess(Core::System& system) {
1843 auto* current_process = system.Kernel().CurrentProcess();
1844
1845 LOG_INFO(Kernel_SVC, "Process {} exiting", current_process->GetProcessID());
1846 ASSERT_MSG(current_process->GetState() == KProcess::State::Running,
1847 "Process has already exited");
1848
1849 system.Exit();
1850}
1851
1852static void ExitProcess32(Core::System& system) {
1853 ExitProcess(system);
1854}
1855
1856namespace {
1857
1858constexpr bool IsValidVirtualCoreId(int32_t core_id) {
1859 return (0 <= core_id && core_id < static_cast<int32_t>(Core::Hardware::NUM_CPU_CORES));
1860}
1861
1862} // Anonymous namespace
1863
1864/// Creates a new thread
1865static Result CreateThread(Core::System& system, Handle* out_handle, VAddr entry_point, u64 arg,
1866 VAddr stack_bottom, u32 priority, s32 core_id) {
1867 LOG_DEBUG(Kernel_SVC,
1868 "called entry_point=0x{:08X}, arg=0x{:08X}, stack_bottom=0x{:08X}, "
1869 "priority=0x{:08X}, core_id=0x{:08X}",
1870 entry_point, arg, stack_bottom, priority, core_id);
1871
1872 // Adjust core id, if it's the default magic.
1873 auto& kernel = system.Kernel();
1874 auto& process = *kernel.CurrentProcess();
1875 if (core_id == IdealCoreUseProcessValue) {
1876 core_id = process.GetIdealCoreId();
1877 }
1878
1879 // Validate arguments.
1880 if (!IsValidVirtualCoreId(core_id)) {
1881 LOG_ERROR(Kernel_SVC, "Invalid Core ID specified (id={})", core_id);
1882 return ResultInvalidCoreId;
1883 }
1884 if (((1ULL << core_id) & process.GetCoreMask()) == 0) {
1885 LOG_ERROR(Kernel_SVC, "Core ID doesn't fall within allowable cores (id={})", core_id);
1886 return ResultInvalidCoreId;
1887 }
1888
1889 if (HighestThreadPriority > priority || priority > LowestThreadPriority) {
1890 LOG_ERROR(Kernel_SVC, "Invalid priority specified (priority={})", priority);
1891 return ResultInvalidPriority;
1892 }
1893 if (!process.CheckThreadPriority(priority)) {
1894 LOG_ERROR(Kernel_SVC, "Invalid allowable thread priority (priority={})", priority);
1895 return ResultInvalidPriority;
1896 }
1897
1898 // Reserve a new thread from the process resource limit (waiting up to 100ms).
1899 KScopedResourceReservation thread_reservation(
1900 kernel.CurrentProcess(), LimitableResource::ThreadCountMax, 1,
1901 system.CoreTiming().GetGlobalTimeNs().count() + 100000000);
1902 if (!thread_reservation.Succeeded()) {
1903 LOG_ERROR(Kernel_SVC, "Could not reserve a new thread");
1904 return ResultLimitReached;
1905 }
1906
1907 // Create the thread.
1908 KThread* thread = KThread::Create(kernel);
1909 if (!thread) {
1910 LOG_ERROR(Kernel_SVC, "Unable to create new threads. Thread creation limit reached.");
1911 return ResultOutOfResource;
1912 }
1913 SCOPE_EXIT({ thread->Close(); });
1914
1915 // Initialize the thread.
1916 {
1917 KScopedLightLock lk{process.GetStateLock()};
1918 R_TRY(KThread::InitializeUserThread(system, thread, entry_point, arg, stack_bottom,
1919 priority, core_id, &process));
1920 }
1921
1922 // Set the thread name for debugging purposes.
1923 thread->SetName(fmt::format("thread[entry_point={:X}, handle={:X}]", entry_point, *out_handle));
1924
1925 // Commit the thread reservation.
1926 thread_reservation.Commit();
1927
1928 // Register the new thread.
1929 KThread::Register(kernel, thread);
1930
1931 // Add the thread to the handle table.
1932 R_TRY(process.GetHandleTable().Add(out_handle, thread));
1933
1934 return ResultSuccess;
1935}
1936
1937static Result CreateThread32(Core::System& system, Handle* out_handle, u32 priority,
1938 u32 entry_point, u32 arg, u32 stack_top, s32 processor_id) {
1939 return CreateThread(system, out_handle, entry_point, arg, stack_top, priority, processor_id);
1940}
1941
1942/// Starts the thread for the provided handle
1943static Result StartThread(Core::System& system, Handle thread_handle) {
1944 LOG_DEBUG(Kernel_SVC, "called thread=0x{:08X}", thread_handle);
1945
1946 // Get the thread from its handle.
1947 KScopedAutoObject thread =
1948 system.Kernel().CurrentProcess()->GetHandleTable().GetObject<KThread>(thread_handle);
1949 R_UNLESS(thread.IsNotNull(), ResultInvalidHandle);
1950
1951 // Try to start the thread.
1952 R_TRY(thread->Run());
1953
1954 // If we succeeded, persist a reference to the thread.
1955 thread->Open();
1956 system.Kernel().RegisterInUseObject(thread.GetPointerUnsafe());
1957
1958 return ResultSuccess;
1959}
1960
1961static Result StartThread32(Core::System& system, Handle thread_handle) {
1962 return StartThread(system, thread_handle);
1963}
1964
1965/// Called when a thread exits
1966static void ExitThread(Core::System& system) {
1967 LOG_DEBUG(Kernel_SVC, "called, pc=0x{:08X}", system.CurrentArmInterface().GetPC());
1968
1969 auto* const current_thread = GetCurrentThreadPointer(system.Kernel());
1970 system.GlobalSchedulerContext().RemoveThread(current_thread);
1971 current_thread->Exit();
1972 system.Kernel().UnregisterInUseObject(current_thread);
1973}
1974
1975static void ExitThread32(Core::System& system) {
1976 ExitThread(system);
1977}
1978
1979/// Sleep the current thread
1980static void SleepThread(Core::System& system, s64 nanoseconds) {
1981 auto& kernel = system.Kernel();
1982 const auto yield_type = static_cast<Svc::YieldType>(nanoseconds);
1983
1984 LOG_TRACE(Kernel_SVC, "called nanoseconds={}", nanoseconds);
1985
1986 // When the input tick is positive, sleep.
1987 if (nanoseconds > 0) {
1988 // Convert the timeout from nanoseconds to ticks.
1989 // NOTE: Nintendo does not use this conversion logic in WaitSynchronization...
1990
1991 // Sleep.
1992 // NOTE: Nintendo does not check the result of this sleep.
1993 static_cast<void>(GetCurrentThread(kernel).Sleep(nanoseconds));
1994 } else if (yield_type == Svc::YieldType::WithoutCoreMigration) {
1995 KScheduler::YieldWithoutCoreMigration(kernel);
1996 } else if (yield_type == Svc::YieldType::WithCoreMigration) {
1997 KScheduler::YieldWithCoreMigration(kernel);
1998 } else if (yield_type == Svc::YieldType::ToAnyThread) {
1999 KScheduler::YieldToAnyThread(kernel);
2000 } else {
2001 // Nintendo does nothing at all if an otherwise invalid value is passed.
2002 ASSERT_MSG(false, "Unimplemented sleep yield type '{:016X}'!", nanoseconds);
2003 }
2004}
2005
2006static void SleepThread32(Core::System& system, u32 nanoseconds_low, u32 nanoseconds_high) {
2007 const auto nanoseconds = static_cast<s64>(u64{nanoseconds_low} | (u64{nanoseconds_high} << 32));
2008 SleepThread(system, nanoseconds);
2009}
2010
2011/// Wait process wide key atomic
2012static Result WaitProcessWideKeyAtomic(Core::System& system, VAddr address, VAddr cv_key, u32 tag,
2013 s64 timeout_ns) {
2014 LOG_TRACE(Kernel_SVC, "called address={:X}, cv_key={:X}, tag=0x{:08X}, timeout_ns={}", address,
2015 cv_key, tag, timeout_ns);
2016
2017 // Validate input.
2018 if (IsKernelAddress(address)) {
2019 LOG_ERROR(Kernel_SVC, "Attempted to wait on kernel address (address={:08X})", address);
2020 return ResultInvalidCurrentMemory;
2021 }
2022 if (!Common::IsAligned(address, sizeof(s32))) {
2023 LOG_ERROR(Kernel_SVC, "Address must be 4 byte aligned (address={:08X})", address);
2024 return ResultInvalidAddress;
2025 }
2026
2027 // Convert timeout from nanoseconds to ticks.
2028 s64 timeout{};
2029 if (timeout_ns > 0) {
2030 const s64 offset_tick(timeout_ns);
2031 if (offset_tick > 0) {
2032 timeout = offset_tick + 2;
2033 if (timeout <= 0) {
2034 timeout = std::numeric_limits<s64>::max();
2035 }
2036 } else {
2037 timeout = std::numeric_limits<s64>::max();
2038 }
2039 } else {
2040 timeout = timeout_ns;
2041 }
2042
2043 // Wait on the condition variable.
2044 return system.Kernel().CurrentProcess()->WaitConditionVariable(
2045 address, Common::AlignDown(cv_key, sizeof(u32)), tag, timeout);
2046}
2047
2048static Result WaitProcessWideKeyAtomic32(Core::System& system, u32 address, u32 cv_key, u32 tag,
2049 u32 timeout_ns_low, u32 timeout_ns_high) {
2050 const auto timeout_ns = static_cast<s64>(timeout_ns_low | (u64{timeout_ns_high} << 32));
2051 return WaitProcessWideKeyAtomic(system, address, cv_key, tag, timeout_ns);
2052}
2053
2054/// Signal process wide key
2055static void SignalProcessWideKey(Core::System& system, VAddr cv_key, s32 count) {
2056 LOG_TRACE(Kernel_SVC, "called, cv_key=0x{:X}, count=0x{:08X}", cv_key, count);
2057
2058 // Signal the condition variable.
2059 return system.Kernel().CurrentProcess()->SignalConditionVariable(
2060 Common::AlignDown(cv_key, sizeof(u32)), count);
2061}
2062
2063static void SignalProcessWideKey32(Core::System& system, u32 cv_key, s32 count) {
2064 SignalProcessWideKey(system, cv_key, count);
2065}
2066
2067namespace {
2068
2069constexpr bool IsValidSignalType(Svc::SignalType type) {
2070 switch (type) {
2071 case Svc::SignalType::Signal:
2072 case Svc::SignalType::SignalAndIncrementIfEqual:
2073 case Svc::SignalType::SignalAndModifyByWaitingCountIfEqual:
2074 return true;
2075 default:
2076 return false;
2077 }
2078}
2079
2080constexpr bool IsValidArbitrationType(Svc::ArbitrationType type) {
2081 switch (type) {
2082 case Svc::ArbitrationType::WaitIfLessThan:
2083 case Svc::ArbitrationType::DecrementAndWaitIfLessThan:
2084 case Svc::ArbitrationType::WaitIfEqual:
2085 return true;
2086 default:
2087 return false;
2088 }
2089}
2090
2091} // namespace
2092
2093// Wait for an address (via Address Arbiter)
2094static Result WaitForAddress(Core::System& system, VAddr address, Svc::ArbitrationType arb_type,
2095 s32 value, s64 timeout_ns) {
2096 LOG_TRACE(Kernel_SVC, "called, address=0x{:X}, arb_type=0x{:X}, value=0x{:X}, timeout_ns={}",
2097 address, arb_type, value, timeout_ns);
2098
2099 // Validate input.
2100 if (IsKernelAddress(address)) {
2101 LOG_ERROR(Kernel_SVC, "Attempting to wait on kernel address (address={:08X})", address);
2102 return ResultInvalidCurrentMemory;
2103 }
2104 if (!Common::IsAligned(address, sizeof(s32))) {
2105 LOG_ERROR(Kernel_SVC, "Wait address must be 4 byte aligned (address={:08X})", address);
2106 return ResultInvalidAddress;
2107 }
2108 if (!IsValidArbitrationType(arb_type)) {
2109 LOG_ERROR(Kernel_SVC, "Invalid arbitration type specified (type={})", arb_type);
2110 return ResultInvalidEnumValue;
2111 }
2112
2113 // Convert timeout from nanoseconds to ticks.
2114 s64 timeout{};
2115 if (timeout_ns > 0) {
2116 const s64 offset_tick(timeout_ns);
2117 if (offset_tick > 0) {
2118 timeout = offset_tick + 2;
2119 if (timeout <= 0) {
2120 timeout = std::numeric_limits<s64>::max();
2121 }
2122 } else {
2123 timeout = std::numeric_limits<s64>::max();
2124 }
2125 } else {
2126 timeout = timeout_ns;
2127 }
2128
2129 return system.Kernel().CurrentProcess()->WaitAddressArbiter(address, arb_type, value, timeout);
2130}
2131
2132static Result WaitForAddress32(Core::System& system, u32 address, Svc::ArbitrationType arb_type,
2133 s32 value, u32 timeout_ns_low, u32 timeout_ns_high) {
2134 const auto timeout = static_cast<s64>(timeout_ns_low | (u64{timeout_ns_high} << 32));
2135 return WaitForAddress(system, address, arb_type, value, timeout);
2136}
2137
2138// Signals to an address (via Address Arbiter)
2139static Result SignalToAddress(Core::System& system, VAddr address, Svc::SignalType signal_type,
2140 s32 value, s32 count) {
2141 LOG_TRACE(Kernel_SVC, "called, address=0x{:X}, signal_type=0x{:X}, value=0x{:X}, count=0x{:X}",
2142 address, signal_type, value, count);
2143
2144 // Validate input.
2145 if (IsKernelAddress(address)) {
2146 LOG_ERROR(Kernel_SVC, "Attempting to signal to a kernel address (address={:08X})", address);
2147 return ResultInvalidCurrentMemory;
2148 }
2149 if (!Common::IsAligned(address, sizeof(s32))) {
2150 LOG_ERROR(Kernel_SVC, "Signaled address must be 4 byte aligned (address={:08X})", address);
2151 return ResultInvalidAddress;
2152 }
2153 if (!IsValidSignalType(signal_type)) {
2154 LOG_ERROR(Kernel_SVC, "Invalid signal type specified (type={})", signal_type);
2155 return ResultInvalidEnumValue;
2156 }
2157
2158 return system.Kernel().CurrentProcess()->SignalAddressArbiter(address, signal_type, value,
2159 count);
2160}
2161
2162static void SynchronizePreemptionState(Core::System& system) {
2163 auto& kernel = system.Kernel();
2164
2165 // Lock the scheduler.
2166 KScopedSchedulerLock sl{kernel};
2167
2168 // If the current thread is pinned, unpin it.
2169 KProcess* cur_process = system.Kernel().CurrentProcess();
2170 const auto core_id = GetCurrentCoreId(kernel);
2171
2172 if (cur_process->GetPinnedThread(core_id) == GetCurrentThreadPointer(kernel)) {
2173 // Clear the current thread's interrupt flag.
2174 GetCurrentThread(kernel).ClearInterruptFlag();
2175
2176 // Unpin the current thread.
2177 cur_process->UnpinCurrentThread(core_id);
2178 }
2179}
2180
2181static Result SignalToAddress32(Core::System& system, u32 address, Svc::SignalType signal_type,
2182 s32 value, s32 count) {
2183 return SignalToAddress(system, address, signal_type, value, count);
2184}
2185
2186static void KernelDebug([[maybe_unused]] Core::System& system,
2187 [[maybe_unused]] u32 kernel_debug_type, [[maybe_unused]] u64 param1,
2188 [[maybe_unused]] u64 param2, [[maybe_unused]] u64 param3) {
2189 // Intentionally do nothing, as this does nothing in released kernel binaries.
2190}
2191
2192static void ChangeKernelTraceState([[maybe_unused]] Core::System& system,
2193 [[maybe_unused]] u32 trace_state) {
2194 // Intentionally do nothing, as this does nothing in released kernel binaries.
2195}
2196
2197/// This returns the total CPU ticks elapsed since the CPU was powered-on
2198static u64 GetSystemTick(Core::System& system) {
2199 LOG_TRACE(Kernel_SVC, "called");
2200
2201 auto& core_timing = system.CoreTiming();
2202
2203 // Returns the value of cntpct_el0 (https://switchbrew.org/wiki/SVC#svcGetSystemTick)
2204 const u64 result{core_timing.GetClockTicks()};
2205
2206 if (!system.Kernel().IsMulticore()) {
2207 core_timing.AddTicks(400U);
2208 }
2209
2210 return result;
2211}
2212
2213static void GetSystemTick32(Core::System& system, u32* time_low, u32* time_high) {
2214 const auto time = GetSystemTick(system);
2215 *time_low = static_cast<u32>(time);
2216 *time_high = static_cast<u32>(time >> 32);
2217}
2218
2219/// Close a handle
2220static Result CloseHandle(Core::System& system, Handle handle) {
2221 LOG_TRACE(Kernel_SVC, "Closing handle 0x{:08X}", handle);
2222
2223 // Remove the handle.
2224 R_UNLESS(system.Kernel().CurrentProcess()->GetHandleTable().Remove(handle),
2225 ResultInvalidHandle);
2226
2227 return ResultSuccess;
2228}
2229
2230static Result CloseHandle32(Core::System& system, Handle handle) {
2231 return CloseHandle(system, handle);
2232}
2233
2234/// Clears the signaled state of an event or process.
2235static Result ResetSignal(Core::System& system, Handle handle) {
2236 LOG_DEBUG(Kernel_SVC, "called handle 0x{:08X}", handle);
2237
2238 // Get the current handle table.
2239 const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
2240
2241 // Try to reset as readable event.
2242 {
2243 KScopedAutoObject readable_event = handle_table.GetObject<KReadableEvent>(handle);
2244 if (readable_event.IsNotNull()) {
2245 return readable_event->Reset();
2246 }
2247 }
2248
2249 // Try to reset as process.
2250 {
2251 KScopedAutoObject process = handle_table.GetObject<KProcess>(handle);
2252 if (process.IsNotNull()) {
2253 return process->Reset();
2254 }
2255 }
2256
2257 LOG_ERROR(Kernel_SVC, "invalid handle (0x{:08X})", handle);
2258
2259 return ResultInvalidHandle;
2260}
2261
2262static Result ResetSignal32(Core::System& system, Handle handle) {
2263 return ResetSignal(system, handle);
2264}
2265
2266namespace {
2267
2268constexpr bool IsValidTransferMemoryPermission(MemoryPermission perm) {
2269 switch (perm) {
2270 case MemoryPermission::None:
2271 case MemoryPermission::Read:
2272 case MemoryPermission::ReadWrite:
2273 return true;
2274 default:
2275 return false;
2276 }
2277}
2278
2279} // Anonymous namespace
2280
2281/// Creates a TransferMemory object
2282static Result CreateTransferMemory(Core::System& system, Handle* out, VAddr address, u64 size,
2283 MemoryPermission map_perm) {
2284 auto& kernel = system.Kernel();
2285
2286 // Validate the size.
2287 R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress);
2288 R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize);
2289 R_UNLESS(size > 0, ResultInvalidSize);
2290 R_UNLESS((address < address + size), ResultInvalidCurrentMemory);
2291
2292 // Validate the permissions.
2293 R_UNLESS(IsValidTransferMemoryPermission(map_perm), ResultInvalidNewMemoryPermission);
2294
2295 // Get the current process and handle table.
2296 auto& process = *kernel.CurrentProcess();
2297 auto& handle_table = process.GetHandleTable();
2298
2299 // Reserve a new transfer memory from the process resource limit.
2300 KScopedResourceReservation trmem_reservation(kernel.CurrentProcess(),
2301 LimitableResource::TransferMemoryCountMax);
2302 R_UNLESS(trmem_reservation.Succeeded(), ResultLimitReached);
2303
2304 // Create the transfer memory.
2305 KTransferMemory* trmem = KTransferMemory::Create(kernel);
2306 R_UNLESS(trmem != nullptr, ResultOutOfResource);
2307
2308 // Ensure the only reference is in the handle table when we're done.
2309 SCOPE_EXIT({ trmem->Close(); });
2310
2311 // Ensure that the region is in range.
2312 R_UNLESS(process.PageTable().Contains(address, size), ResultInvalidCurrentMemory);
2313
2314 // Initialize the transfer memory.
2315 R_TRY(trmem->Initialize(address, size, map_perm));
2316
2317 // Commit the reservation.
2318 trmem_reservation.Commit();
2319
2320 // Register the transfer memory.
2321 KTransferMemory::Register(kernel, trmem);
2322
2323 // Add the transfer memory to the handle table.
2324 R_TRY(handle_table.Add(out, trmem));
2325
2326 return ResultSuccess;
2327}
2328
2329static Result CreateTransferMemory32(Core::System& system, Handle* out, u32 address, u32 size,
2330 MemoryPermission map_perm) {
2331 return CreateTransferMemory(system, out, address, size, map_perm);
2332}
2333
2334static Result GetThreadCoreMask(Core::System& system, Handle thread_handle, s32* out_core_id,
2335 u64* out_affinity_mask) {
2336 LOG_TRACE(Kernel_SVC, "called, handle=0x{:08X}", thread_handle);
2337
2338 // Get the thread from its handle.
2339 KScopedAutoObject thread =
2340 system.Kernel().CurrentProcess()->GetHandleTable().GetObject<KThread>(thread_handle);
2341 R_UNLESS(thread.IsNotNull(), ResultInvalidHandle);
2342
2343 // Get the core mask.
2344 R_TRY(thread->GetCoreMask(out_core_id, out_affinity_mask));
2345
2346 return ResultSuccess;
2347}
2348
2349static Result GetThreadCoreMask32(Core::System& system, Handle thread_handle, s32* out_core_id,
2350 u32* out_affinity_mask_low, u32* out_affinity_mask_high) {
2351 u64 out_affinity_mask{};
2352 const auto result = GetThreadCoreMask(system, thread_handle, out_core_id, &out_affinity_mask);
2353 *out_affinity_mask_high = static_cast<u32>(out_affinity_mask >> 32);
2354 *out_affinity_mask_low = static_cast<u32>(out_affinity_mask);
2355 return result;
2356}
2357
2358static Result SetThreadCoreMask(Core::System& system, Handle thread_handle, s32 core_id,
2359 u64 affinity_mask) {
2360 // Determine the core id/affinity mask.
2361 if (core_id == IdealCoreUseProcessValue) {
2362 core_id = system.Kernel().CurrentProcess()->GetIdealCoreId();
2363 affinity_mask = (1ULL << core_id);
2364 } else {
2365 // Validate the affinity mask.
2366 const u64 process_core_mask = system.Kernel().CurrentProcess()->GetCoreMask();
2367 R_UNLESS((affinity_mask | process_core_mask) == process_core_mask, ResultInvalidCoreId);
2368 R_UNLESS(affinity_mask != 0, ResultInvalidCombination);
2369
2370 // Validate the core id.
2371 if (IsValidVirtualCoreId(core_id)) {
2372 R_UNLESS(((1ULL << core_id) & affinity_mask) != 0, ResultInvalidCombination);
2373 } else {
2374 R_UNLESS(core_id == IdealCoreNoUpdate || core_id == IdealCoreDontCare,
2375 ResultInvalidCoreId);
2376 }
2377 }
2378
2379 // Get the thread from its handle.
2380 KScopedAutoObject thread =
2381 system.Kernel().CurrentProcess()->GetHandleTable().GetObject<KThread>(thread_handle);
2382 R_UNLESS(thread.IsNotNull(), ResultInvalidHandle);
2383
2384 // Set the core mask.
2385 R_TRY(thread->SetCoreMask(core_id, affinity_mask));
2386
2387 return ResultSuccess;
2388}
2389
2390static Result SetThreadCoreMask32(Core::System& system, Handle thread_handle, s32 core_id,
2391 u32 affinity_mask_low, u32 affinity_mask_high) {
2392 const auto affinity_mask = u64{affinity_mask_low} | (u64{affinity_mask_high} << 32);
2393 return SetThreadCoreMask(system, thread_handle, core_id, affinity_mask);
2394}
2395
2396static Result SignalEvent(Core::System& system, Handle event_handle) {
2397 LOG_DEBUG(Kernel_SVC, "called, event_handle=0x{:08X}", event_handle);
2398
2399 // Get the current handle table.
2400 const KHandleTable& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
2401
2402 // Get the event.
2403 KScopedAutoObject event = handle_table.GetObject<KEvent>(event_handle);
2404 R_UNLESS(event.IsNotNull(), ResultInvalidHandle);
2405
2406 return event->Signal();
2407}
2408
2409static Result SignalEvent32(Core::System& system, Handle event_handle) {
2410 return SignalEvent(system, event_handle);
2411}
2412
2413static Result ClearEvent(Core::System& system, Handle event_handle) {
2414 LOG_TRACE(Kernel_SVC, "called, event_handle=0x{:08X}", event_handle);
2415
2416 // Get the current handle table.
2417 const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
2418
2419 // Try to clear the writable event.
2420 {
2421 KScopedAutoObject event = handle_table.GetObject<KEvent>(event_handle);
2422 if (event.IsNotNull()) {
2423 return event->Clear();
2424 }
2425 }
2426
2427 // Try to clear the readable event.
2428 {
2429 KScopedAutoObject readable_event = handle_table.GetObject<KReadableEvent>(event_handle);
2430 if (readable_event.IsNotNull()) {
2431 return readable_event->Clear();
2432 }
2433 }
2434
2435 LOG_ERROR(Kernel_SVC, "Event handle does not exist, event_handle=0x{:08X}", event_handle);
2436
2437 return ResultInvalidHandle;
2438}
2439
2440static Result ClearEvent32(Core::System& system, Handle event_handle) {
2441 return ClearEvent(system, event_handle);
2442}
2443
2444static Result CreateEvent(Core::System& system, Handle* out_write, Handle* out_read) {
2445 LOG_DEBUG(Kernel_SVC, "called");
2446
2447 // Get the kernel reference and handle table.
2448 auto& kernel = system.Kernel();
2449 auto& handle_table = kernel.CurrentProcess()->GetHandleTable();
2450
2451 // Reserve a new event from the process resource limit
2452 KScopedResourceReservation event_reservation(kernel.CurrentProcess(),
2453 LimitableResource::EventCountMax);
2454 R_UNLESS(event_reservation.Succeeded(), ResultLimitReached);
2455
2456 // Create a new event.
2457 KEvent* event = KEvent::Create(kernel);
2458 R_UNLESS(event != nullptr, ResultOutOfResource);
2459
2460 // Initialize the event.
2461 event->Initialize(kernel.CurrentProcess());
2462
2463 // Commit the thread reservation.
2464 event_reservation.Commit();
2465
2466 // Ensure that we clean up the event (and its only references are handle table) on function end.
2467 SCOPE_EXIT({
2468 event->GetReadableEvent().Close();
2469 event->Close();
2470 });
2471
2472 // Register the event.
2473 KEvent::Register(kernel, event);
2474
2475 // Add the event to the handle table.
2476 R_TRY(handle_table.Add(out_write, event));
2477
2478 // Ensure that we maintaing a clean handle state on exit.
2479 auto handle_guard = SCOPE_GUARD({ handle_table.Remove(*out_write); });
2480
2481 // Add the readable event to the handle table.
2482 R_TRY(handle_table.Add(out_read, std::addressof(event->GetReadableEvent())));
2483
2484 // We succeeded.
2485 handle_guard.Cancel();
2486 return ResultSuccess;
2487}
2488
2489static Result CreateEvent32(Core::System& system, Handle* out_write, Handle* out_read) {
2490 return CreateEvent(system, out_write, out_read);
2491}
2492
2493static Result GetProcessInfo(Core::System& system, u64* out, Handle process_handle, u32 type) {
2494 LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, type=0x{:X}", process_handle, type);
2495
2496 const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
2497 KScopedAutoObject process = handle_table.GetObject<KProcess>(process_handle);
2498 if (process.IsNull()) {
2499 LOG_ERROR(Kernel_SVC, "Process handle does not exist, process_handle=0x{:08X}",
2500 process_handle);
2501 return ResultInvalidHandle;
2502 }
2503
2504 const auto info_type = static_cast<ProcessInfoType>(type);
2505 if (info_type != ProcessInfoType::ProcessState) {
2506 LOG_ERROR(Kernel_SVC, "Expected info_type to be ProcessState but got {} instead", type);
2507 return ResultInvalidEnumValue;
2508 }
2509
2510 *out = static_cast<u64>(process->GetState());
2511 return ResultSuccess;
2512}
2513
2514static Result CreateResourceLimit(Core::System& system, Handle* out_handle) {
2515 LOG_DEBUG(Kernel_SVC, "called");
2516
2517 // Create a new resource limit.
2518 auto& kernel = system.Kernel();
2519 KResourceLimit* resource_limit = KResourceLimit::Create(kernel);
2520 R_UNLESS(resource_limit != nullptr, ResultOutOfResource);
2521
2522 // Ensure we don't leak a reference to the limit.
2523 SCOPE_EXIT({ resource_limit->Close(); });
2524
2525 // Initialize the resource limit.
2526 resource_limit->Initialize(&system.CoreTiming());
2527
2528 // Register the limit.
2529 KResourceLimit::Register(kernel, resource_limit);
2530
2531 // Add the limit to the handle table.
2532 R_TRY(kernel.CurrentProcess()->GetHandleTable().Add(out_handle, resource_limit));
2533
2534 return ResultSuccess;
2535}
2536
2537static Result GetResourceLimitLimitValue(Core::System& system, u64* out_limit_value,
2538 Handle resource_limit_handle, LimitableResource which) {
2539 LOG_DEBUG(Kernel_SVC, "called, resource_limit_handle={:08X}, which={}", resource_limit_handle,
2540 which);
2541
2542 // Validate the resource.
2543 R_UNLESS(IsValidResourceType(which), ResultInvalidEnumValue);
2544
2545 // Get the resource limit.
2546 auto& kernel = system.Kernel();
2547 KScopedAutoObject resource_limit =
2548 kernel.CurrentProcess()->GetHandleTable().GetObject<KResourceLimit>(resource_limit_handle);
2549 R_UNLESS(resource_limit.IsNotNull(), ResultInvalidHandle);
2550
2551 // Get the limit value.
2552 *out_limit_value = resource_limit->GetLimitValue(which);
2553
2554 return ResultSuccess;
2555}
2556
2557static Result GetResourceLimitCurrentValue(Core::System& system, u64* out_current_value,
2558 Handle resource_limit_handle, LimitableResource which) {
2559 LOG_DEBUG(Kernel_SVC, "called, resource_limit_handle={:08X}, which={}", resource_limit_handle,
2560 which);
2561
2562 // Validate the resource.
2563 R_UNLESS(IsValidResourceType(which), ResultInvalidEnumValue);
2564
2565 // Get the resource limit.
2566 auto& kernel = system.Kernel();
2567 KScopedAutoObject resource_limit =
2568 kernel.CurrentProcess()->GetHandleTable().GetObject<KResourceLimit>(resource_limit_handle);
2569 R_UNLESS(resource_limit.IsNotNull(), ResultInvalidHandle);
2570
2571 // Get the current value.
2572 *out_current_value = resource_limit->GetCurrentValue(which);
2573
2574 return ResultSuccess;
2575}
2576
2577static Result SetResourceLimitLimitValue(Core::System& system, Handle resource_limit_handle,
2578 LimitableResource which, u64 limit_value) {
2579 LOG_DEBUG(Kernel_SVC, "called, resource_limit_handle={:08X}, which={}, limit_value={}",
2580 resource_limit_handle, which, limit_value);
2581
2582 // Validate the resource.
2583 R_UNLESS(IsValidResourceType(which), ResultInvalidEnumValue);
2584
2585 // Get the resource limit.
2586 auto& kernel = system.Kernel();
2587 KScopedAutoObject resource_limit =
2588 kernel.CurrentProcess()->GetHandleTable().GetObject<KResourceLimit>(resource_limit_handle);
2589 R_UNLESS(resource_limit.IsNotNull(), ResultInvalidHandle);
2590
2591 // Set the limit value.
2592 R_TRY(resource_limit->SetLimitValue(which, limit_value));
2593
2594 return ResultSuccess;
2595}
2596
2597static Result GetProcessList(Core::System& system, u32* out_num_processes, VAddr out_process_ids,
2598 u32 out_process_ids_size) {
2599 LOG_DEBUG(Kernel_SVC, "called. out_process_ids=0x{:016X}, out_process_ids_size={}",
2600 out_process_ids, out_process_ids_size);
2601
2602 // If the supplied size is negative or greater than INT32_MAX / sizeof(u64), bail.
2603 if ((out_process_ids_size & 0xF0000000) != 0) {
2604 LOG_ERROR(Kernel_SVC,
2605 "Supplied size outside [0, 0x0FFFFFFF] range. out_process_ids_size={}",
2606 out_process_ids_size);
2607 return ResultOutOfRange;
2608 }
2609
2610 const auto& kernel = system.Kernel();
2611 const auto total_copy_size = out_process_ids_size * sizeof(u64);
2612
2613 if (out_process_ids_size > 0 && !kernel.CurrentProcess()->PageTable().IsInsideAddressSpace(
2614 out_process_ids, total_copy_size)) {
2615 LOG_ERROR(Kernel_SVC, "Address range outside address space. begin=0x{:016X}, end=0x{:016X}",
2616 out_process_ids, out_process_ids + total_copy_size);
2617 return ResultInvalidCurrentMemory;
2618 }
2619
2620 auto& memory = system.Memory();
2621 const auto& process_list = kernel.GetProcessList();
2622 const auto num_processes = process_list.size();
2623 const auto copy_amount = std::min(std::size_t{out_process_ids_size}, num_processes);
2624
2625 for (std::size_t i = 0; i < copy_amount; ++i) {
2626 memory.Write64(out_process_ids, process_list[i]->GetProcessID());
2627 out_process_ids += sizeof(u64);
2628 }
2629
2630 *out_num_processes = static_cast<u32>(num_processes);
2631 return ResultSuccess;
2632}
2633
2634static Result GetThreadList(Core::System& system, u32* out_num_threads, VAddr out_thread_ids,
2635 u32 out_thread_ids_size, Handle debug_handle) {
2636 // TODO: Handle this case when debug events are supported.
2637 UNIMPLEMENTED_IF(debug_handle != InvalidHandle);
2638
2639 LOG_DEBUG(Kernel_SVC, "called. out_thread_ids=0x{:016X}, out_thread_ids_size={}",
2640 out_thread_ids, out_thread_ids_size);
2641
2642 // If the size is negative or larger than INT32_MAX / sizeof(u64)
2643 if ((out_thread_ids_size & 0xF0000000) != 0) {
2644 LOG_ERROR(Kernel_SVC, "Supplied size outside [0, 0x0FFFFFFF] range. size={}",
2645 out_thread_ids_size);
2646 return ResultOutOfRange;
2647 }
2648
2649 auto* const current_process = system.Kernel().CurrentProcess();
2650 const auto total_copy_size = out_thread_ids_size * sizeof(u64);
2651
2652 if (out_thread_ids_size > 0 &&
2653 !current_process->PageTable().IsInsideAddressSpace(out_thread_ids, total_copy_size)) {
2654 LOG_ERROR(Kernel_SVC, "Address range outside address space. begin=0x{:016X}, end=0x{:016X}",
2655 out_thread_ids, out_thread_ids + total_copy_size);
2656 return ResultInvalidCurrentMemory;
2657 }
2658
2659 auto& memory = system.Memory();
2660 const auto& thread_list = current_process->GetThreadList();
2661 const auto num_threads = thread_list.size();
2662 const auto copy_amount = std::min(std::size_t{out_thread_ids_size}, num_threads);
2663
2664 auto list_iter = thread_list.cbegin();
2665 for (std::size_t i = 0; i < copy_amount; ++i, ++list_iter) {
2666 memory.Write64(out_thread_ids, (*list_iter)->GetThreadID());
2667 out_thread_ids += sizeof(u64);
2668 }
2669
2670 *out_num_threads = static_cast<u32>(num_threads);
2671 return ResultSuccess;
2672}
2673
2674static Result FlushProcessDataCache32(Core::System& system, Handle process_handle, u64 address,
2675 u64 size) {
2676 // Validate address/size.
2677 R_UNLESS(size > 0, ResultInvalidSize);
2678 R_UNLESS(address == static_cast<uintptr_t>(address), ResultInvalidCurrentMemory);
2679 R_UNLESS(size == static_cast<size_t>(size), ResultInvalidCurrentMemory);
2680
2681 // Get the process from its handle.
2682 KScopedAutoObject process =
2683 system.Kernel().CurrentProcess()->GetHandleTable().GetObject<KProcess>(process_handle);
2684 R_UNLESS(process.IsNotNull(), ResultInvalidHandle);
2685
2686 // Verify the region is within range.
2687 auto& page_table = process->PageTable();
2688 R_UNLESS(page_table.Contains(address, size), ResultInvalidCurrentMemory);
2689
2690 // Perform the operation.
2691 R_RETURN(system.Memory().FlushDataCache(*process, address, size));
2692}
2693
2694namespace {
2695struct FunctionDef { 14struct FunctionDef {
2696 using Func = void(Core::System&); 15 using Func = void(Core::System&);
2697 16
@@ -2699,6 +18,7 @@ struct FunctionDef {
2699 Func* func; 18 Func* func;
2700 const char* name; 19 const char* name;
2701}; 20};
21
2702} // namespace 22} // namespace
2703 23
2704static const FunctionDef SVC_Table_32[] = { 24static const FunctionDef SVC_Table_32[] = {
diff --git a/src/core/hle/kernel/svc.h b/src/core/hle/kernel/svc.h
index 13f061b83..b599f9a3d 100644
--- a/src/core/hle/kernel/svc.h
+++ b/src/core/hle/kernel/svc.h
@@ -4,6 +4,8 @@
4#pragma once 4#pragma once
5 5
6#include "common/common_types.h" 6#include "common/common_types.h"
7#include "core/hle/kernel/svc_types.h"
8#include "core/hle/result.h"
7 9
8namespace Core { 10namespace Core {
9class System; 11class System;
@@ -13,4 +15,158 @@ namespace Kernel::Svc {
13 15
14void Call(Core::System& system, u32 immediate); 16void Call(Core::System& system, u32 immediate);
15 17
18Result SetHeapSize(Core::System& system, VAddr* out_address, u64 size);
19Result SetMemoryPermission(Core::System& system, VAddr address, u64 size, MemoryPermission perm);
20Result SetMemoryAttribute(Core::System& system, VAddr address, u64 size, u32 mask, u32 attr);
21Result MapMemory(Core::System& system, VAddr dst_addr, VAddr src_addr, u64 size);
22Result UnmapMemory(Core::System& system, VAddr dst_addr, VAddr src_addr, u64 size);
23Result QueryMemory(Core::System& system, VAddr memory_info_address, VAddr page_info_address,
24 VAddr query_address);
25void ExitProcess(Core::System& system);
26Result CreateThread(Core::System& system, Handle* out_handle, VAddr entry_point, u64 arg,
27 VAddr stack_bottom, u32 priority, s32 core_id);
28Result StartThread(Core::System& system, Handle thread_handle);
29void ExitThread(Core::System& system);
30void SleepThread(Core::System& system, s64 nanoseconds);
31Result GetThreadPriority(Core::System& system, u32* out_priority, Handle handle);
32Result SetThreadPriority(Core::System& system, Handle thread_handle, u32 priority);
33Result GetThreadCoreMask(Core::System& system, Handle thread_handle, s32* out_core_id,
34 u64* out_affinity_mask);
35Result SetThreadCoreMask(Core::System& system, Handle thread_handle, s32 core_id,
36 u64 affinity_mask);
37u32 GetCurrentProcessorNumber(Core::System& system);
38Result SignalEvent(Core::System& system, Handle event_handle);
39Result ClearEvent(Core::System& system, Handle event_handle);
40Result MapSharedMemory(Core::System& system, Handle shmem_handle, VAddr address, u64 size,
41 MemoryPermission map_perm);
42Result UnmapSharedMemory(Core::System& system, Handle shmem_handle, VAddr address, u64 size);
43Result CreateTransferMemory(Core::System& system, Handle* out, VAddr address, u64 size,
44 MemoryPermission map_perm);
45Result CloseHandle(Core::System& system, Handle handle);
46Result ResetSignal(Core::System& system, Handle handle);
47Result WaitSynchronization(Core::System& system, s32* index, VAddr handles_address, s32 num_handles,
48 s64 nano_seconds);
49Result CancelSynchronization(Core::System& system, Handle handle);
50Result ArbitrateLock(Core::System& system, Handle thread_handle, VAddr address, u32 tag);
51Result ArbitrateUnlock(Core::System& system, VAddr address);
52Result WaitProcessWideKeyAtomic(Core::System& system, VAddr address, VAddr cv_key, u32 tag,
53 s64 timeout_ns);
54void SignalProcessWideKey(Core::System& system, VAddr cv_key, s32 count);
55u64 GetSystemTick(Core::System& system);
56Result ConnectToNamedPort(Core::System& system, Handle* out, VAddr port_name_address);
57Result SendSyncRequest(Core::System& system, Handle handle);
58Result GetProcessId(Core::System& system, u64* out_process_id, Handle handle);
59Result GetThreadId(Core::System& system, u64* out_thread_id, Handle thread_handle);
60void Break(Core::System& system, u32 reason, u64 info1, u64 info2);
61void OutputDebugString(Core::System& system, VAddr address, u64 len);
62Result GetInfo(Core::System& system, u64* result, u64 info_id, Handle handle, u64 info_sub_id);
63Result MapPhysicalMemory(Core::System& system, VAddr addr, u64 size);
64Result UnmapPhysicalMemory(Core::System& system, VAddr addr, u64 size);
65Result GetResourceLimitLimitValue(Core::System& system, u64* out_limit_value,
66 Handle resource_limit_handle, LimitableResource which);
67Result GetResourceLimitCurrentValue(Core::System& system, u64* out_current_value,
68 Handle resource_limit_handle, LimitableResource which);
69Result SetThreadActivity(Core::System& system, Handle thread_handle,
70 ThreadActivity thread_activity);
71Result GetThreadContext(Core::System& system, VAddr out_context, Handle thread_handle);
72Result WaitForAddress(Core::System& system, VAddr address, ArbitrationType arb_type, s32 value,
73 s64 timeout_ns);
74Result SignalToAddress(Core::System& system, VAddr address, SignalType signal_type, s32 value,
75 s32 count);
76void SynchronizePreemptionState(Core::System& system);
77void KernelDebug(Core::System& system, u32 kernel_debug_type, u64 param1, u64 param2, u64 param3);
78void ChangeKernelTraceState(Core::System& system, u32 trace_state);
79Result CreateSession(Core::System& system, Handle* out_server, Handle* out_client, u32 is_light,
80 u64 name);
81Result ReplyAndReceive(Core::System& system, s32* out_index, Handle* handles, s32 num_handles,
82 Handle reply_target, s64 timeout_ns);
83Result CreateEvent(Core::System& system, Handle* out_write, Handle* out_read);
84Result CreateCodeMemory(Core::System& system, Handle* out, VAddr address, size_t size);
85Result ControlCodeMemory(Core::System& system, Handle code_memory_handle, u32 operation,
86 VAddr address, size_t size, MemoryPermission perm);
87Result GetProcessList(Core::System& system, u32* out_num_processes, VAddr out_process_ids,
88 u32 out_process_ids_size);
89Result GetThreadList(Core::System& system, u32* out_num_threads, VAddr out_thread_ids,
90 u32 out_thread_ids_size, Handle debug_handle);
91Result SetProcessMemoryPermission(Core::System& system, Handle process_handle, VAddr address,
92 u64 size, MemoryPermission perm);
93Result MapProcessMemory(Core::System& system, VAddr dst_address, Handle process_handle,
94 VAddr src_address, u64 size);
95Result UnmapProcessMemory(Core::System& system, VAddr dst_address, Handle process_handle,
96 VAddr src_address, u64 size);
97Result QueryProcessMemory(Core::System& system, VAddr memory_info_address, VAddr page_info_address,
98 Handle process_handle, VAddr address);
99Result MapProcessCodeMemory(Core::System& system, Handle process_handle, u64 dst_address,
100 u64 src_address, u64 size);
101Result UnmapProcessCodeMemory(Core::System& system, Handle process_handle, u64 dst_address,
102 u64 src_address, u64 size);
103Result GetProcessInfo(Core::System& system, u64* out, Handle process_handle, u32 type);
104Result CreateResourceLimit(Core::System& system, Handle* out_handle);
105Result SetResourceLimitLimitValue(Core::System& system, Handle resource_limit_handle,
106 LimitableResource which, u64 limit_value);
107
108//
109
110Result SetHeapSize32(Core::System& system, u32* heap_addr, u32 heap_size);
111Result SetMemoryAttribute32(Core::System& system, u32 address, u32 size, u32 mask, u32 attr);
112Result MapMemory32(Core::System& system, u32 dst_addr, u32 src_addr, u32 size);
113Result UnmapMemory32(Core::System& system, u32 dst_addr, u32 src_addr, u32 size);
114Result QueryMemory32(Core::System& system, u32 memory_info_address, u32 page_info_address,
115 u32 query_address);
116void ExitProcess32(Core::System& system);
117Result CreateThread32(Core::System& system, Handle* out_handle, u32 priority, u32 entry_point,
118 u32 arg, u32 stack_top, s32 processor_id);
119Result StartThread32(Core::System& system, Handle thread_handle);
120void ExitThread32(Core::System& system);
121void SleepThread32(Core::System& system, u32 nanoseconds_low, u32 nanoseconds_high);
122Result GetThreadPriority32(Core::System& system, u32* out_priority, Handle handle);
123Result SetThreadPriority32(Core::System& system, Handle thread_handle, u32 priority);
124Result GetThreadCoreMask32(Core::System& system, Handle thread_handle, s32* out_core_id,
125 u32* out_affinity_mask_low, u32* out_affinity_mask_high);
126Result SetThreadCoreMask32(Core::System& system, Handle thread_handle, s32 core_id,
127 u32 affinity_mask_low, u32 affinity_mask_high);
128u32 GetCurrentProcessorNumber32(Core::System& system);
129Result SignalEvent32(Core::System& system, Handle event_handle);
130Result ClearEvent32(Core::System& system, Handle event_handle);
131Result MapSharedMemory32(Core::System& system, Handle shmem_handle, u32 address, u32 size,
132 MemoryPermission map_perm);
133Result UnmapSharedMemory32(Core::System& system, Handle shmem_handle, u32 address, u32 size);
134Result CreateTransferMemory32(Core::System& system, Handle* out, u32 address, u32 size,
135 MemoryPermission map_perm);
136Result CloseHandle32(Core::System& system, Handle handle);
137Result ResetSignal32(Core::System& system, Handle handle);
138Result WaitSynchronization32(Core::System& system, u32 timeout_low, u32 handles_address,
139 s32 num_handles, u32 timeout_high, s32* index);
140Result CancelSynchronization32(Core::System& system, Handle handle);
141Result ArbitrateLock32(Core::System& system, Handle thread_handle, u32 address, u32 tag);
142Result ArbitrateUnlock32(Core::System& system, u32 address);
143Result WaitProcessWideKeyAtomic32(Core::System& system, u32 address, u32 cv_key, u32 tag,
144 u32 timeout_ns_low, u32 timeout_ns_high);
145void SignalProcessWideKey32(Core::System& system, u32 cv_key, s32 count);
146void GetSystemTick32(Core::System& system, u32* time_low, u32* time_high);
147Result ConnectToNamedPort32(Core::System& system, Handle* out_handle, u32 port_name_address);
148Result SendSyncRequest32(Core::System& system, Handle handle);
149Result GetProcessId32(Core::System& system, u32* out_process_id_low, u32* out_process_id_high,
150 Handle handle);
151Result GetThreadId32(Core::System& system, u32* out_thread_id_low, u32* out_thread_id_high,
152 Handle thread_handle);
153void Break32(Core::System& system, u32 reason, u32 info1, u32 info2);
154void OutputDebugString32(Core::System& system, u32 address, u32 len);
155Result GetInfo32(Core::System& system, u32* result_low, u32* result_high, u32 sub_id_low,
156 u32 info_id, u32 handle, u32 sub_id_high);
157Result MapPhysicalMemory32(Core::System& system, u32 addr, u32 size);
158Result UnmapPhysicalMemory32(Core::System& system, u32 addr, u32 size);
159Result SetThreadActivity32(Core::System& system, Handle thread_handle,
160 ThreadActivity thread_activity);
161Result GetThreadContext32(Core::System& system, u32 out_context, Handle thread_handle);
162Result WaitForAddress32(Core::System& system, u32 address, ArbitrationType arb_type, s32 value,
163 u32 timeout_ns_low, u32 timeout_ns_high);
164Result SignalToAddress32(Core::System& system, u32 address, SignalType signal_type, s32 value,
165 s32 count);
166Result CreateEvent32(Core::System& system, Handle* out_write, Handle* out_read);
167Result CreateCodeMemory32(Core::System& system, Handle* out, u32 address, u32 size);
168Result ControlCodeMemory32(Core::System& system, Handle code_memory_handle, u32 operation,
169 u64 address, u64 size, MemoryPermission perm);
170Result FlushProcessDataCache32(Core::System& system, Handle process_handle, u64 address, u64 size);
171
16} // namespace Kernel::Svc 172} // namespace Kernel::Svc
diff --git a/src/core/hle/kernel/svc/svc_activity.cpp b/src/core/hle/kernel/svc/svc_activity.cpp
new file mode 100644
index 000000000..8774a5c98
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_activity.cpp
@@ -0,0 +1,44 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/core.h"
5#include "core/hle/kernel/k_process.h"
6#include "core/hle/kernel/k_thread.h"
7#include "core/hle/kernel/svc.h"
8#include "core/hle/kernel/svc_results.h"
9
10namespace Kernel::Svc {
11
12/// Sets the thread activity
13Result SetThreadActivity(Core::System& system, Handle thread_handle,
14 ThreadActivity thread_activity) {
15 LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, activity=0x{:08X}", thread_handle,
16 thread_activity);
17
18 // Validate the activity.
19 constexpr auto IsValidThreadActivity = [](ThreadActivity activity) {
20 return activity == ThreadActivity::Runnable || activity == ThreadActivity::Paused;
21 };
22 R_UNLESS(IsValidThreadActivity(thread_activity), ResultInvalidEnumValue);
23
24 // Get the thread from its handle.
25 KScopedAutoObject thread =
26 system.Kernel().CurrentProcess()->GetHandleTable().GetObject<KThread>(thread_handle);
27 R_UNLESS(thread.IsNotNull(), ResultInvalidHandle);
28
29 // Check that the activity is being set on a non-current thread for the current process.
30 R_UNLESS(thread->GetOwnerProcess() == system.Kernel().CurrentProcess(), ResultInvalidHandle);
31 R_UNLESS(thread.GetPointerUnsafe() != GetCurrentThreadPointer(system.Kernel()), ResultBusy);
32
33 // Set the activity.
34 R_TRY(thread->SetActivity(thread_activity));
35
36 return ResultSuccess;
37}
38
39Result SetThreadActivity32(Core::System& system, Handle thread_handle,
40 ThreadActivity thread_activity) {
41 return SetThreadActivity(system, thread_handle, thread_activity);
42}
43
44} // namespace Kernel::Svc
diff --git a/src/core/hle/kernel/svc/svc_address_arbiter.cpp b/src/core/hle/kernel/svc/svc_address_arbiter.cpp
new file mode 100644
index 000000000..842107726
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_address_arbiter.cpp
@@ -0,0 +1,113 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/core.h"
5#include "core/hle/kernel/k_memory_layout.h"
6#include "core/hle/kernel/k_process.h"
7#include "core/hle/kernel/kernel.h"
8#include "core/hle/kernel/svc.h"
9#include "core/hle/kernel/svc_results.h"
10#include "core/hle/kernel/svc_types.h"
11
12namespace Kernel::Svc {
13namespace {
14
15constexpr bool IsValidSignalType(Svc::SignalType type) {
16 switch (type) {
17 case Svc::SignalType::Signal:
18 case Svc::SignalType::SignalAndIncrementIfEqual:
19 case Svc::SignalType::SignalAndModifyByWaitingCountIfEqual:
20 return true;
21 default:
22 return false;
23 }
24}
25
26constexpr bool IsValidArbitrationType(Svc::ArbitrationType type) {
27 switch (type) {
28 case Svc::ArbitrationType::WaitIfLessThan:
29 case Svc::ArbitrationType::DecrementAndWaitIfLessThan:
30 case Svc::ArbitrationType::WaitIfEqual:
31 return true;
32 default:
33 return false;
34 }
35}
36
37} // namespace
38
39// Wait for an address (via Address Arbiter)
40Result WaitForAddress(Core::System& system, VAddr address, ArbitrationType arb_type, s32 value,
41 s64 timeout_ns) {
42 LOG_TRACE(Kernel_SVC, "called, address=0x{:X}, arb_type=0x{:X}, value=0x{:X}, timeout_ns={}",
43 address, arb_type, value, timeout_ns);
44
45 // Validate input.
46 if (IsKernelAddress(address)) {
47 LOG_ERROR(Kernel_SVC, "Attempting to wait on kernel address (address={:08X})", address);
48 return ResultInvalidCurrentMemory;
49 }
50 if (!Common::IsAligned(address, sizeof(s32))) {
51 LOG_ERROR(Kernel_SVC, "Wait address must be 4 byte aligned (address={:08X})", address);
52 return ResultInvalidAddress;
53 }
54 if (!IsValidArbitrationType(arb_type)) {
55 LOG_ERROR(Kernel_SVC, "Invalid arbitration type specified (type={})", arb_type);
56 return ResultInvalidEnumValue;
57 }
58
59 // Convert timeout from nanoseconds to ticks.
60 s64 timeout{};
61 if (timeout_ns > 0) {
62 const s64 offset_tick(timeout_ns);
63 if (offset_tick > 0) {
64 timeout = offset_tick + 2;
65 if (timeout <= 0) {
66 timeout = std::numeric_limits<s64>::max();
67 }
68 } else {
69 timeout = std::numeric_limits<s64>::max();
70 }
71 } else {
72 timeout = timeout_ns;
73 }
74
75 return system.Kernel().CurrentProcess()->WaitAddressArbiter(address, arb_type, value, timeout);
76}
77
78Result WaitForAddress32(Core::System& system, u32 address, ArbitrationType arb_type, s32 value,
79 u32 timeout_ns_low, u32 timeout_ns_high) {
80 const auto timeout = static_cast<s64>(timeout_ns_low | (u64{timeout_ns_high} << 32));
81 return WaitForAddress(system, address, arb_type, value, timeout);
82}
83
84// Signals to an address (via Address Arbiter)
85Result SignalToAddress(Core::System& system, VAddr address, SignalType signal_type, s32 value,
86 s32 count) {
87 LOG_TRACE(Kernel_SVC, "called, address=0x{:X}, signal_type=0x{:X}, value=0x{:X}, count=0x{:X}",
88 address, signal_type, value, count);
89
90 // Validate input.
91 if (IsKernelAddress(address)) {
92 LOG_ERROR(Kernel_SVC, "Attempting to signal to a kernel address (address={:08X})", address);
93 return ResultInvalidCurrentMemory;
94 }
95 if (!Common::IsAligned(address, sizeof(s32))) {
96 LOG_ERROR(Kernel_SVC, "Signaled address must be 4 byte aligned (address={:08X})", address);
97 return ResultInvalidAddress;
98 }
99 if (!IsValidSignalType(signal_type)) {
100 LOG_ERROR(Kernel_SVC, "Invalid signal type specified (type={})", signal_type);
101 return ResultInvalidEnumValue;
102 }
103
104 return system.Kernel().CurrentProcess()->SignalAddressArbiter(address, signal_type, value,
105 count);
106}
107
108Result SignalToAddress32(Core::System& system, u32 address, SignalType signal_type, s32 value,
109 s32 count) {
110 return SignalToAddress(system, address, signal_type, value, count);
111}
112
113} // namespace Kernel::Svc
diff --git a/src/core/hle/kernel/svc/svc_address_translation.cpp b/src/core/hle/kernel/svc/svc_address_translation.cpp
new file mode 100644
index 000000000..299e22ae6
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_address_translation.cpp
@@ -0,0 +1,6 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/hle/kernel/svc.h"
5
6namespace Kernel::Svc {} // namespace Kernel::Svc
diff --git a/src/core/hle/kernel/svc/svc_cache.cpp b/src/core/hle/kernel/svc/svc_cache.cpp
new file mode 100644
index 000000000..42167d35b
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_cache.cpp
@@ -0,0 +1,31 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/core.h"
5#include "core/hle/kernel/k_process.h"
6#include "core/hle/kernel/svc.h"
7#include "core/hle/kernel/svc_results.h"
8#include "core/hle/kernel/svc_types.h"
9
10namespace Kernel::Svc {
11
12Result FlushProcessDataCache32(Core::System& system, Handle process_handle, u64 address, u64 size) {
13 // Validate address/size.
14 R_UNLESS(size > 0, ResultInvalidSize);
15 R_UNLESS(address == static_cast<uintptr_t>(address), ResultInvalidCurrentMemory);
16 R_UNLESS(size == static_cast<size_t>(size), ResultInvalidCurrentMemory);
17
18 // Get the process from its handle.
19 KScopedAutoObject process =
20 system.Kernel().CurrentProcess()->GetHandleTable().GetObject<KProcess>(process_handle);
21 R_UNLESS(process.IsNotNull(), ResultInvalidHandle);
22
23 // Verify the region is within range.
24 auto& page_table = process->PageTable();
25 R_UNLESS(page_table.Contains(address, size), ResultInvalidCurrentMemory);
26
27 // Perform the operation.
28 R_RETURN(system.Memory().FlushDataCache(*process, address, size));
29}
30
31} // namespace Kernel::Svc
diff --git a/src/core/hle/kernel/svc/svc_code_memory.cpp b/src/core/hle/kernel/svc/svc_code_memory.cpp
new file mode 100644
index 000000000..4cb21e101
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_code_memory.cpp
@@ -0,0 +1,154 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/core.h"
5#include "core/hle/kernel/k_code_memory.h"
6#include "core/hle/kernel/k_process.h"
7#include "core/hle/kernel/kernel.h"
8#include "core/hle/kernel/svc.h"
9
10namespace Kernel::Svc {
11namespace {
12
13constexpr bool IsValidMapCodeMemoryPermission(MemoryPermission perm) {
14 return perm == MemoryPermission::ReadWrite;
15}
16
17constexpr bool IsValidMapToOwnerCodeMemoryPermission(MemoryPermission perm) {
18 return perm == MemoryPermission::Read || perm == MemoryPermission::ReadExecute;
19}
20
21constexpr bool IsValidUnmapCodeMemoryPermission(MemoryPermission perm) {
22 return perm == MemoryPermission::None;
23}
24
25constexpr bool IsValidUnmapFromOwnerCodeMemoryPermission(MemoryPermission perm) {
26 return perm == MemoryPermission::None;
27}
28
29} // namespace
30
31Result CreateCodeMemory(Core::System& system, Handle* out, VAddr address, size_t size) {
32 LOG_TRACE(Kernel_SVC, "called, address=0x{:X}, size=0x{:X}", address, size);
33
34 // Get kernel instance.
35 auto& kernel = system.Kernel();
36
37 // Validate address / size.
38 R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress);
39 R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize);
40 R_UNLESS(size > 0, ResultInvalidSize);
41 R_UNLESS((address < address + size), ResultInvalidCurrentMemory);
42
43 // Create the code memory.
44
45 KCodeMemory* code_mem = KCodeMemory::Create(kernel);
46 R_UNLESS(code_mem != nullptr, ResultOutOfResource);
47
48 // Verify that the region is in range.
49 R_UNLESS(system.CurrentProcess()->PageTable().Contains(address, size),
50 ResultInvalidCurrentMemory);
51
52 // Initialize the code memory.
53 R_TRY(code_mem->Initialize(system.DeviceMemory(), address, size));
54
55 // Register the code memory.
56 KCodeMemory::Register(kernel, code_mem);
57
58 // Add the code memory to the handle table.
59 R_TRY(system.CurrentProcess()->GetHandleTable().Add(out, code_mem));
60
61 code_mem->Close();
62
63 return ResultSuccess;
64}
65
66Result CreateCodeMemory32(Core::System& system, Handle* out, u32 address, u32 size) {
67 return CreateCodeMemory(system, out, address, size);
68}
69
70Result ControlCodeMemory(Core::System& system, Handle code_memory_handle, u32 operation,
71 VAddr address, size_t size, MemoryPermission perm) {
72
73 LOG_TRACE(Kernel_SVC,
74 "called, code_memory_handle=0x{:X}, operation=0x{:X}, address=0x{:X}, size=0x{:X}, "
75 "permission=0x{:X}",
76 code_memory_handle, operation, address, size, perm);
77
78 // Validate the address / size.
79 R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress);
80 R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize);
81 R_UNLESS(size > 0, ResultInvalidSize);
82 R_UNLESS((address < address + size), ResultInvalidCurrentMemory);
83
84 // Get the code memory from its handle.
85 KScopedAutoObject code_mem =
86 system.CurrentProcess()->GetHandleTable().GetObject<KCodeMemory>(code_memory_handle);
87 R_UNLESS(code_mem.IsNotNull(), ResultInvalidHandle);
88
89 // NOTE: Here, Atmosphere extends the SVC to allow code memory operations on one's own process.
90 // This enables homebrew usage of these SVCs for JIT.
91
92 // Perform the operation.
93 switch (static_cast<CodeMemoryOperation>(operation)) {
94 case CodeMemoryOperation::Map: {
95 // Check that the region is in range.
96 R_UNLESS(
97 system.CurrentProcess()->PageTable().CanContain(address, size, KMemoryState::CodeOut),
98 ResultInvalidMemoryRegion);
99
100 // Check the memory permission.
101 R_UNLESS(IsValidMapCodeMemoryPermission(perm), ResultInvalidNewMemoryPermission);
102
103 // Map the memory.
104 R_TRY(code_mem->Map(address, size));
105 } break;
106 case CodeMemoryOperation::Unmap: {
107 // Check that the region is in range.
108 R_UNLESS(
109 system.CurrentProcess()->PageTable().CanContain(address, size, KMemoryState::CodeOut),
110 ResultInvalidMemoryRegion);
111
112 // Check the memory permission.
113 R_UNLESS(IsValidUnmapCodeMemoryPermission(perm), ResultInvalidNewMemoryPermission);
114
115 // Unmap the memory.
116 R_TRY(code_mem->Unmap(address, size));
117 } break;
118 case CodeMemoryOperation::MapToOwner: {
119 // Check that the region is in range.
120 R_UNLESS(code_mem->GetOwner()->PageTable().CanContain(address, size,
121 KMemoryState::GeneratedCode),
122 ResultInvalidMemoryRegion);
123
124 // Check the memory permission.
125 R_UNLESS(IsValidMapToOwnerCodeMemoryPermission(perm), ResultInvalidNewMemoryPermission);
126
127 // Map the memory to its owner.
128 R_TRY(code_mem->MapToOwner(address, size, perm));
129 } break;
130 case CodeMemoryOperation::UnmapFromOwner: {
131 // Check that the region is in range.
132 R_UNLESS(code_mem->GetOwner()->PageTable().CanContain(address, size,
133 KMemoryState::GeneratedCode),
134 ResultInvalidMemoryRegion);
135
136 // Check the memory permission.
137 R_UNLESS(IsValidUnmapFromOwnerCodeMemoryPermission(perm), ResultInvalidNewMemoryPermission);
138
139 // Unmap the memory from its owner.
140 R_TRY(code_mem->UnmapFromOwner(address, size));
141 } break;
142 default:
143 return ResultInvalidEnumValue;
144 }
145
146 return ResultSuccess;
147}
148
149Result ControlCodeMemory32(Core::System& system, Handle code_memory_handle, u32 operation,
150 u64 address, u64 size, MemoryPermission perm) {
151 return ControlCodeMemory(system, code_memory_handle, operation, address, size, perm);
152}
153
154} // namespace Kernel::Svc
diff --git a/src/core/hle/kernel/svc/svc_condition_variable.cpp b/src/core/hle/kernel/svc/svc_condition_variable.cpp
new file mode 100644
index 000000000..d6cfc87c5
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_condition_variable.cpp
@@ -0,0 +1,69 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/core.h"
5#include "core/hle/kernel/k_memory_layout.h"
6#include "core/hle/kernel/k_process.h"
7#include "core/hle/kernel/kernel.h"
8#include "core/hle/kernel/svc.h"
9#include "core/hle/kernel/svc_results.h"
10
11namespace Kernel::Svc {
12
13/// Wait process wide key atomic
14Result WaitProcessWideKeyAtomic(Core::System& system, VAddr address, VAddr cv_key, u32 tag,
15 s64 timeout_ns) {
16 LOG_TRACE(Kernel_SVC, "called address={:X}, cv_key={:X}, tag=0x{:08X}, timeout_ns={}", address,
17 cv_key, tag, timeout_ns);
18
19 // Validate input.
20 if (IsKernelAddress(address)) {
21 LOG_ERROR(Kernel_SVC, "Attempted to wait on kernel address (address={:08X})", address);
22 return ResultInvalidCurrentMemory;
23 }
24 if (!Common::IsAligned(address, sizeof(s32))) {
25 LOG_ERROR(Kernel_SVC, "Address must be 4 byte aligned (address={:08X})", address);
26 return ResultInvalidAddress;
27 }
28
29 // Convert timeout from nanoseconds to ticks.
30 s64 timeout{};
31 if (timeout_ns > 0) {
32 const s64 offset_tick(timeout_ns);
33 if (offset_tick > 0) {
34 timeout = offset_tick + 2;
35 if (timeout <= 0) {
36 timeout = std::numeric_limits<s64>::max();
37 }
38 } else {
39 timeout = std::numeric_limits<s64>::max();
40 }
41 } else {
42 timeout = timeout_ns;
43 }
44
45 // Wait on the condition variable.
46 return system.Kernel().CurrentProcess()->WaitConditionVariable(
47 address, Common::AlignDown(cv_key, sizeof(u32)), tag, timeout);
48}
49
50Result WaitProcessWideKeyAtomic32(Core::System& system, u32 address, u32 cv_key, u32 tag,
51 u32 timeout_ns_low, u32 timeout_ns_high) {
52 const auto timeout_ns = static_cast<s64>(timeout_ns_low | (u64{timeout_ns_high} << 32));
53 return WaitProcessWideKeyAtomic(system, address, cv_key, tag, timeout_ns);
54}
55
56/// Signal process wide key
57void SignalProcessWideKey(Core::System& system, VAddr cv_key, s32 count) {
58 LOG_TRACE(Kernel_SVC, "called, cv_key=0x{:X}, count=0x{:08X}", cv_key, count);
59
60 // Signal the condition variable.
61 return system.Kernel().CurrentProcess()->SignalConditionVariable(
62 Common::AlignDown(cv_key, sizeof(u32)), count);
63}
64
65void SignalProcessWideKey32(Core::System& system, u32 cv_key, s32 count) {
66 SignalProcessWideKey(system, cv_key, count);
67}
68
69} // namespace Kernel::Svc
diff --git a/src/core/hle/kernel/svc/svc_debug.cpp b/src/core/hle/kernel/svc/svc_debug.cpp
new file mode 100644
index 000000000..299e22ae6
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_debug.cpp
@@ -0,0 +1,6 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/hle/kernel/svc.h"
5
6namespace Kernel::Svc {} // namespace Kernel::Svc
diff --git a/src/core/hle/kernel/svc/svc_debug_string.cpp b/src/core/hle/kernel/svc/svc_debug_string.cpp
new file mode 100644
index 000000000..486e62cc4
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_debug_string.cpp
@@ -0,0 +1,25 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/core.h"
5#include "core/hle/kernel/svc.h"
6#include "core/memory.h"
7
8namespace Kernel::Svc {
9
10/// Used to output a message on a debug hardware unit - does nothing on a retail unit
11void OutputDebugString(Core::System& system, VAddr address, u64 len) {
12 if (len == 0) {
13 return;
14 }
15
16 std::string str(len, '\0');
17 system.Memory().ReadBlock(address, str.data(), str.size());
18 LOG_DEBUG(Debug_Emulated, "{}", str);
19}
20
21void OutputDebugString32(Core::System& system, u32 address, u32 len) {
22 OutputDebugString(system, address, len);
23}
24
25} // namespace Kernel::Svc
diff --git a/src/core/hle/kernel/svc/svc_device_address_space.cpp b/src/core/hle/kernel/svc/svc_device_address_space.cpp
new file mode 100644
index 000000000..299e22ae6
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_device_address_space.cpp
@@ -0,0 +1,6 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/hle/kernel/svc.h"
5
6namespace Kernel::Svc {} // namespace Kernel::Svc
diff --git a/src/core/hle/kernel/svc/svc_event.cpp b/src/core/hle/kernel/svc/svc_event.cpp
new file mode 100644
index 000000000..885f02f50
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_event.cpp
@@ -0,0 +1,111 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "common/scope_exit.h"
5#include "core/core.h"
6#include "core/hle/kernel/k_event.h"
7#include "core/hle/kernel/k_process.h"
8#include "core/hle/kernel/k_scoped_resource_reservation.h"
9#include "core/hle/kernel/kernel.h"
10#include "core/hle/kernel/svc.h"
11
12namespace Kernel::Svc {
13
14Result SignalEvent(Core::System& system, Handle event_handle) {
15 LOG_DEBUG(Kernel_SVC, "called, event_handle=0x{:08X}", event_handle);
16
17 // Get the current handle table.
18 const KHandleTable& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
19
20 // Get the event.
21 KScopedAutoObject event = handle_table.GetObject<KEvent>(event_handle);
22 R_UNLESS(event.IsNotNull(), ResultInvalidHandle);
23
24 return event->Signal();
25}
26
27Result SignalEvent32(Core::System& system, Handle event_handle) {
28 return SignalEvent(system, event_handle);
29}
30
31Result ClearEvent(Core::System& system, Handle event_handle) {
32 LOG_TRACE(Kernel_SVC, "called, event_handle=0x{:08X}", event_handle);
33
34 // Get the current handle table.
35 const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
36
37 // Try to clear the writable event.
38 {
39 KScopedAutoObject event = handle_table.GetObject<KEvent>(event_handle);
40 if (event.IsNotNull()) {
41 return event->Clear();
42 }
43 }
44
45 // Try to clear the readable event.
46 {
47 KScopedAutoObject readable_event = handle_table.GetObject<KReadableEvent>(event_handle);
48 if (readable_event.IsNotNull()) {
49 return readable_event->Clear();
50 }
51 }
52
53 LOG_ERROR(Kernel_SVC, "Event handle does not exist, event_handle=0x{:08X}", event_handle);
54
55 return ResultInvalidHandle;
56}
57
58Result ClearEvent32(Core::System& system, Handle event_handle) {
59 return ClearEvent(system, event_handle);
60}
61
62Result CreateEvent(Core::System& system, Handle* out_write, Handle* out_read) {
63 LOG_DEBUG(Kernel_SVC, "called");
64
65 // Get the kernel reference and handle table.
66 auto& kernel = system.Kernel();
67 auto& handle_table = kernel.CurrentProcess()->GetHandleTable();
68
69 // Reserve a new event from the process resource limit
70 KScopedResourceReservation event_reservation(kernel.CurrentProcess(),
71 LimitableResource::EventCountMax);
72 R_UNLESS(event_reservation.Succeeded(), ResultLimitReached);
73
74 // Create a new event.
75 KEvent* event = KEvent::Create(kernel);
76 R_UNLESS(event != nullptr, ResultOutOfResource);
77
78 // Initialize the event.
79 event->Initialize(kernel.CurrentProcess());
80
81 // Commit the thread reservation.
82 event_reservation.Commit();
83
84 // Ensure that we clean up the event (and its only references are handle table) on function end.
85 SCOPE_EXIT({
86 event->GetReadableEvent().Close();
87 event->Close();
88 });
89
90 // Register the event.
91 KEvent::Register(kernel, event);
92
93 // Add the event to the handle table.
94 R_TRY(handle_table.Add(out_write, event));
95
96 // Ensure that we maintaing a clean handle state on exit.
97 auto handle_guard = SCOPE_GUARD({ handle_table.Remove(*out_write); });
98
99 // Add the readable event to the handle table.
100 R_TRY(handle_table.Add(out_read, std::addressof(event->GetReadableEvent())));
101
102 // We succeeded.
103 handle_guard.Cancel();
104 return ResultSuccess;
105}
106
107Result CreateEvent32(Core::System& system, Handle* out_write, Handle* out_read) {
108 return CreateEvent(system, out_write, out_read);
109}
110
111} // namespace Kernel::Svc
diff --git a/src/core/hle/kernel/svc/svc_exception.cpp b/src/core/hle/kernel/svc/svc_exception.cpp
new file mode 100644
index 000000000..fb9f133c1
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_exception.cpp
@@ -0,0 +1,121 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/core.h"
5#include "core/debugger/debugger.h"
6#include "core/hle/kernel/k_thread.h"
7#include "core/hle/kernel/svc.h"
8#include "core/hle/kernel/svc_types.h"
9#include "core/memory.h"
10#include "core/reporter.h"
11
12namespace Kernel::Svc {
13
14/// Break program execution
15void Break(Core::System& system, u32 reason, u64 info1, u64 info2) {
16 BreakReason break_reason =
17 static_cast<BreakReason>(reason & ~static_cast<u32>(BreakReason::NotificationOnlyFlag));
18 bool notification_only = (reason & static_cast<u32>(BreakReason::NotificationOnlyFlag)) != 0;
19
20 bool has_dumped_buffer{};
21 std::vector<u8> debug_buffer;
22
23 const auto handle_debug_buffer = [&](VAddr addr, u64 sz) {
24 if (sz == 0 || addr == 0 || has_dumped_buffer) {
25 return;
26 }
27
28 auto& memory = system.Memory();
29
30 // This typically is an error code so we're going to assume this is the case
31 if (sz == sizeof(u32)) {
32 LOG_CRITICAL(Debug_Emulated, "debug_buffer_err_code={:X}", memory.Read32(addr));
33 } else {
34 // We don't know what's in here so we'll hexdump it
35 debug_buffer.resize(sz);
36 memory.ReadBlock(addr, debug_buffer.data(), sz);
37 std::string hexdump;
38 for (std::size_t i = 0; i < debug_buffer.size(); i++) {
39 hexdump += fmt::format("{:02X} ", debug_buffer[i]);
40 if (i != 0 && i % 16 == 0) {
41 hexdump += '\n';
42 }
43 }
44 LOG_CRITICAL(Debug_Emulated, "debug_buffer=\n{}", hexdump);
45 }
46 has_dumped_buffer = true;
47 };
48 switch (break_reason) {
49 case BreakReason::Panic:
50 LOG_CRITICAL(Debug_Emulated, "Userspace PANIC! info1=0x{:016X}, info2=0x{:016X}", info1,
51 info2);
52 handle_debug_buffer(info1, info2);
53 break;
54 case BreakReason::Assert:
55 LOG_CRITICAL(Debug_Emulated, "Userspace Assertion failed! info1=0x{:016X}, info2=0x{:016X}",
56 info1, info2);
57 handle_debug_buffer(info1, info2);
58 break;
59 case BreakReason::User:
60 LOG_WARNING(Debug_Emulated, "Userspace Break! 0x{:016X} with size 0x{:016X}", info1, info2);
61 handle_debug_buffer(info1, info2);
62 break;
63 case BreakReason::PreLoadDll:
64 LOG_INFO(Debug_Emulated,
65 "Userspace Attempting to load an NRO at 0x{:016X} with size 0x{:016X}", info1,
66 info2);
67 break;
68 case BreakReason::PostLoadDll:
69 LOG_INFO(Debug_Emulated, "Userspace Loaded an NRO at 0x{:016X} with size 0x{:016X}", info1,
70 info2);
71 break;
72 case BreakReason::PreUnloadDll:
73 LOG_INFO(Debug_Emulated,
74 "Userspace Attempting to unload an NRO at 0x{:016X} with size 0x{:016X}", info1,
75 info2);
76 break;
77 case BreakReason::PostUnloadDll:
78 LOG_INFO(Debug_Emulated, "Userspace Unloaded an NRO at 0x{:016X} with size 0x{:016X}",
79 info1, info2);
80 break;
81 case BreakReason::CppException:
82 LOG_CRITICAL(Debug_Emulated, "Signalling debugger. Uncaught C++ exception encountered.");
83 break;
84 default:
85 LOG_WARNING(
86 Debug_Emulated,
87 "Signalling debugger, Unknown break reason {:#X}, info1=0x{:016X}, info2=0x{:016X}",
88 reason, info1, info2);
89 handle_debug_buffer(info1, info2);
90 break;
91 }
92
93 system.GetReporter().SaveSvcBreakReport(reason, notification_only, info1, info2,
94 has_dumped_buffer ? std::make_optional(debug_buffer)
95 : std::nullopt);
96
97 if (!notification_only) {
98 LOG_CRITICAL(
99 Debug_Emulated,
100 "Emulated program broke execution! reason=0x{:016X}, info1=0x{:016X}, info2=0x{:016X}",
101 reason, info1, info2);
102
103 handle_debug_buffer(info1, info2);
104
105 auto* const current_thread = GetCurrentThreadPointer(system.Kernel());
106 const auto thread_processor_id = current_thread->GetActiveCore();
107 system.ArmInterface(static_cast<std::size_t>(thread_processor_id)).LogBacktrace();
108 }
109
110 if (system.DebuggerEnabled()) {
111 auto* thread = system.Kernel().GetCurrentEmuThread();
112 system.GetDebugger().NotifyThreadStopped(thread);
113 thread->RequestSuspend(Kernel::SuspendType::Debug);
114 }
115}
116
117void Break32(Core::System& system, u32 reason, u32 info1, u32 info2) {
118 Break(system, reason, info1, info2);
119}
120
121} // namespace Kernel::Svc
diff --git a/src/core/hle/kernel/svc/svc_info.cpp b/src/core/hle/kernel/svc/svc_info.cpp
new file mode 100644
index 000000000..df5dd85a4
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_info.cpp
@@ -0,0 +1,282 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/core.h"
5#include "core/core_timing.h"
6#include "core/hle/kernel/k_process.h"
7#include "core/hle/kernel/k_resource_limit.h"
8#include "core/hle/kernel/svc.h"
9
10namespace Kernel::Svc {
11
12/// Gets system/memory information for the current process
13Result GetInfo(Core::System& system, u64* result, u64 info_id, Handle handle, u64 info_sub_id) {
14 LOG_TRACE(Kernel_SVC, "called info_id=0x{:X}, info_sub_id=0x{:X}, handle=0x{:08X}", info_id,
15 info_sub_id, handle);
16
17 const auto info_id_type = static_cast<InfoType>(info_id);
18
19 switch (info_id_type) {
20 case InfoType::CoreMask:
21 case InfoType::PriorityMask:
22 case InfoType::AliasRegionAddress:
23 case InfoType::AliasRegionSize:
24 case InfoType::HeapRegionAddress:
25 case InfoType::HeapRegionSize:
26 case InfoType::AslrRegionAddress:
27 case InfoType::AslrRegionSize:
28 case InfoType::StackRegionAddress:
29 case InfoType::StackRegionSize:
30 case InfoType::TotalMemorySize:
31 case InfoType::UsedMemorySize:
32 case InfoType::SystemResourceSizeTotal:
33 case InfoType::SystemResourceSizeUsed:
34 case InfoType::ProgramId:
35 case InfoType::UserExceptionContextAddress:
36 case InfoType::TotalNonSystemMemorySize:
37 case InfoType::UsedNonSystemMemorySize:
38 case InfoType::IsApplication:
39 case InfoType::FreeThreadCount: {
40 if (info_sub_id != 0) {
41 LOG_ERROR(Kernel_SVC, "Info sub id is non zero! info_id={}, info_sub_id={}", info_id,
42 info_sub_id);
43 return ResultInvalidEnumValue;
44 }
45
46 const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
47 KScopedAutoObject process = handle_table.GetObject<KProcess>(handle);
48 if (process.IsNull()) {
49 LOG_ERROR(Kernel_SVC, "Process is not valid! info_id={}, info_sub_id={}, handle={:08X}",
50 info_id, info_sub_id, handle);
51 return ResultInvalidHandle;
52 }
53
54 switch (info_id_type) {
55 case InfoType::CoreMask:
56 *result = process->GetCoreMask();
57 return ResultSuccess;
58
59 case InfoType::PriorityMask:
60 *result = process->GetPriorityMask();
61 return ResultSuccess;
62
63 case InfoType::AliasRegionAddress:
64 *result = process->PageTable().GetAliasRegionStart();
65 return ResultSuccess;
66
67 case InfoType::AliasRegionSize:
68 *result = process->PageTable().GetAliasRegionSize();
69 return ResultSuccess;
70
71 case InfoType::HeapRegionAddress:
72 *result = process->PageTable().GetHeapRegionStart();
73 return ResultSuccess;
74
75 case InfoType::HeapRegionSize:
76 *result = process->PageTable().GetHeapRegionSize();
77 return ResultSuccess;
78
79 case InfoType::AslrRegionAddress:
80 *result = process->PageTable().GetAliasCodeRegionStart();
81 return ResultSuccess;
82
83 case InfoType::AslrRegionSize:
84 *result = process->PageTable().GetAliasCodeRegionSize();
85 return ResultSuccess;
86
87 case InfoType::StackRegionAddress:
88 *result = process->PageTable().GetStackRegionStart();
89 return ResultSuccess;
90
91 case InfoType::StackRegionSize:
92 *result = process->PageTable().GetStackRegionSize();
93 return ResultSuccess;
94
95 case InfoType::TotalMemorySize:
96 *result = process->GetTotalPhysicalMemoryAvailable();
97 return ResultSuccess;
98
99 case InfoType::UsedMemorySize:
100 *result = process->GetTotalPhysicalMemoryUsed();
101 return ResultSuccess;
102
103 case InfoType::SystemResourceSizeTotal:
104 *result = process->GetSystemResourceSize();
105 return ResultSuccess;
106
107 case InfoType::SystemResourceSizeUsed:
108 LOG_WARNING(Kernel_SVC, "(STUBBED) Attempted to query system resource usage");
109 *result = process->GetSystemResourceUsage();
110 return ResultSuccess;
111
112 case InfoType::ProgramId:
113 *result = process->GetProgramID();
114 return ResultSuccess;
115
116 case InfoType::UserExceptionContextAddress:
117 *result = process->GetProcessLocalRegionAddress();
118 return ResultSuccess;
119
120 case InfoType::TotalNonSystemMemorySize:
121 *result = process->GetTotalPhysicalMemoryAvailableWithoutSystemResource();
122 return ResultSuccess;
123
124 case InfoType::UsedNonSystemMemorySize:
125 *result = process->GetTotalPhysicalMemoryUsedWithoutSystemResource();
126 return ResultSuccess;
127
128 case InfoType::FreeThreadCount:
129 *result = process->GetFreeThreadCount();
130 return ResultSuccess;
131
132 default:
133 break;
134 }
135
136 LOG_ERROR(Kernel_SVC, "Unimplemented svcGetInfo id=0x{:016X}", info_id);
137 return ResultInvalidEnumValue;
138 }
139
140 case InfoType::DebuggerAttached:
141 *result = 0;
142 return ResultSuccess;
143
144 case InfoType::ResourceLimit: {
145 if (handle != 0) {
146 LOG_ERROR(Kernel, "Handle is non zero! handle={:08X}", handle);
147 return ResultInvalidHandle;
148 }
149
150 if (info_sub_id != 0) {
151 LOG_ERROR(Kernel, "Info sub id is non zero! info_id={}, info_sub_id={}", info_id,
152 info_sub_id);
153 return ResultInvalidCombination;
154 }
155
156 KProcess* const current_process = system.Kernel().CurrentProcess();
157 KHandleTable& handle_table = current_process->GetHandleTable();
158 const auto resource_limit = current_process->GetResourceLimit();
159 if (!resource_limit) {
160 *result = Svc::InvalidHandle;
161 // Yes, the kernel considers this a successful operation.
162 return ResultSuccess;
163 }
164
165 Handle resource_handle{};
166 R_TRY(handle_table.Add(&resource_handle, resource_limit));
167
168 *result = resource_handle;
169 return ResultSuccess;
170 }
171
172 case InfoType::RandomEntropy:
173 if (handle != 0) {
174 LOG_ERROR(Kernel_SVC, "Process Handle is non zero, expected 0 result but got {:016X}",
175 handle);
176 return ResultInvalidHandle;
177 }
178
179 if (info_sub_id >= KProcess::RANDOM_ENTROPY_SIZE) {
180 LOG_ERROR(Kernel_SVC, "Entropy size is out of range, expected {} but got {}",
181 KProcess::RANDOM_ENTROPY_SIZE, info_sub_id);
182 return ResultInvalidCombination;
183 }
184
185 *result = system.Kernel().CurrentProcess()->GetRandomEntropy(info_sub_id);
186 return ResultSuccess;
187
188 case InfoType::InitialProcessIdRange:
189 LOG_WARNING(Kernel_SVC,
190 "(STUBBED) Attempted to query privileged process id bounds, returned 0");
191 *result = 0;
192 return ResultSuccess;
193
194 case InfoType::ThreadTickCount: {
195 constexpr u64 num_cpus = 4;
196 if (info_sub_id != 0xFFFFFFFFFFFFFFFF && info_sub_id >= num_cpus) {
197 LOG_ERROR(Kernel_SVC, "Core count is out of range, expected {} but got {}", num_cpus,
198 info_sub_id);
199 return ResultInvalidCombination;
200 }
201
202 KScopedAutoObject thread =
203 system.Kernel().CurrentProcess()->GetHandleTable().GetObject<KThread>(
204 static_cast<Handle>(handle));
205 if (thread.IsNull()) {
206 LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle=0x{:08X}",
207 static_cast<Handle>(handle));
208 return ResultInvalidHandle;
209 }
210
211 const auto& core_timing = system.CoreTiming();
212 const auto& scheduler = *system.Kernel().CurrentScheduler();
213 const auto* const current_thread = GetCurrentThreadPointer(system.Kernel());
214 const bool same_thread = current_thread == thread.GetPointerUnsafe();
215
216 const u64 prev_ctx_ticks = scheduler.GetLastContextSwitchTime();
217 u64 out_ticks = 0;
218 if (same_thread && info_sub_id == 0xFFFFFFFFFFFFFFFF) {
219 const u64 thread_ticks = current_thread->GetCpuTime();
220
221 out_ticks = thread_ticks + (core_timing.GetCPUTicks() - prev_ctx_ticks);
222 } else if (same_thread && info_sub_id == system.Kernel().CurrentPhysicalCoreIndex()) {
223 out_ticks = core_timing.GetCPUTicks() - prev_ctx_ticks;
224 }
225
226 *result = out_ticks;
227 return ResultSuccess;
228 }
229 case InfoType::IdleTickCount: {
230 // Verify the input handle is invalid.
231 R_UNLESS(handle == InvalidHandle, ResultInvalidHandle);
232
233 // Verify the requested core is valid.
234 const bool core_valid =
235 (info_sub_id == 0xFFFFFFFFFFFFFFFF) ||
236 (info_sub_id == static_cast<u64>(system.Kernel().CurrentPhysicalCoreIndex()));
237 R_UNLESS(core_valid, ResultInvalidCombination);
238
239 // Get the idle tick count.
240 *result = system.Kernel().CurrentScheduler()->GetIdleThread()->GetCpuTime();
241 return ResultSuccess;
242 }
243 case InfoType::MesosphereCurrentProcess: {
244 // Verify the input handle is invalid.
245 R_UNLESS(handle == InvalidHandle, ResultInvalidHandle);
246
247 // Verify the sub-type is valid.
248 R_UNLESS(info_sub_id == 0, ResultInvalidCombination);
249
250 // Get the handle table.
251 KProcess* current_process = system.Kernel().CurrentProcess();
252 KHandleTable& handle_table = current_process->GetHandleTable();
253
254 // Get a new handle for the current process.
255 Handle tmp;
256 R_TRY(handle_table.Add(&tmp, current_process));
257
258 // Set the output.
259 *result = tmp;
260
261 // We succeeded.
262 return ResultSuccess;
263 }
264 default:
265 LOG_ERROR(Kernel_SVC, "Unimplemented svcGetInfo id=0x{:016X}", info_id);
266 return ResultInvalidEnumValue;
267 }
268}
269
270Result GetInfo32(Core::System& system, u32* result_low, u32* result_high, u32 sub_id_low,
271 u32 info_id, u32 handle, u32 sub_id_high) {
272 const u64 sub_id{u64{sub_id_low} | (u64{sub_id_high} << 32)};
273 u64 res_value{};
274
275 const Result result{GetInfo(system, &res_value, info_id, handle, sub_id)};
276 *result_high = static_cast<u32>(res_value >> 32);
277 *result_low = static_cast<u32>(res_value & std::numeric_limits<u32>::max());
278
279 return result;
280}
281
282} // namespace Kernel::Svc
diff --git a/src/core/hle/kernel/svc/svc_interrupt_event.cpp b/src/core/hle/kernel/svc/svc_interrupt_event.cpp
new file mode 100644
index 000000000..299e22ae6
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_interrupt_event.cpp
@@ -0,0 +1,6 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/hle/kernel/svc.h"
5
6namespace Kernel::Svc {} // namespace Kernel::Svc
diff --git a/src/core/hle/kernel/svc/svc_io_pool.cpp b/src/core/hle/kernel/svc/svc_io_pool.cpp
new file mode 100644
index 000000000..299e22ae6
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_io_pool.cpp
@@ -0,0 +1,6 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/hle/kernel/svc.h"
5
6namespace Kernel::Svc {} // namespace Kernel::Svc
diff --git a/src/core/hle/kernel/svc/svc_ipc.cpp b/src/core/hle/kernel/svc/svc_ipc.cpp
new file mode 100644
index 000000000..dbb68e89a
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_ipc.cpp
@@ -0,0 +1,89 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "common/scope_exit.h"
5#include "core/core.h"
6#include "core/hle/kernel/k_client_session.h"
7#include "core/hle/kernel/k_process.h"
8#include "core/hle/kernel/k_server_session.h"
9#include "core/hle/kernel/svc.h"
10
11namespace Kernel::Svc {
12
13/// Makes a blocking IPC call to a service.
14Result SendSyncRequest(Core::System& system, Handle handle) {
15 auto& kernel = system.Kernel();
16
17 // Get the client session from its handle.
18 KScopedAutoObject session =
19 kernel.CurrentProcess()->GetHandleTable().GetObject<KClientSession>(handle);
20 R_UNLESS(session.IsNotNull(), ResultInvalidHandle);
21
22 LOG_TRACE(Kernel_SVC, "called handle=0x{:08X}({})", handle, session->GetName());
23
24 return session->SendSyncRequest();
25}
26
27Result SendSyncRequest32(Core::System& system, Handle handle) {
28 return SendSyncRequest(system, handle);
29}
30
31Result ReplyAndReceive(Core::System& system, s32* out_index, Handle* handles, s32 num_handles,
32 Handle reply_target, s64 timeout_ns) {
33 auto& kernel = system.Kernel();
34 auto& handle_table = GetCurrentThread(kernel).GetOwnerProcess()->GetHandleTable();
35
36 // Convert handle list to object table.
37 std::vector<KSynchronizationObject*> objs(num_handles);
38 R_UNLESS(
39 handle_table.GetMultipleObjects<KSynchronizationObject>(objs.data(), handles, num_handles),
40 ResultInvalidHandle);
41
42 // Ensure handles are closed when we're done.
43 SCOPE_EXIT({
44 for (auto i = 0; i < num_handles; ++i) {
45 objs[i]->Close();
46 }
47 });
48
49 // Reply to the target, if one is specified.
50 if (reply_target != InvalidHandle) {
51 KScopedAutoObject session = handle_table.GetObject<KServerSession>(reply_target);
52 R_UNLESS(session.IsNotNull(), ResultInvalidHandle);
53
54 // If we fail to reply, we want to set the output index to -1.
55 ON_RESULT_FAILURE {
56 *out_index = -1;
57 };
58
59 // Send the reply.
60 R_TRY(session->SendReply());
61 }
62
63 // Wait for a message.
64 while (true) {
65 // Wait for an object.
66 s32 index;
67 Result result = KSynchronizationObject::Wait(kernel, &index, objs.data(),
68 static_cast<s32>(objs.size()), timeout_ns);
69 if (result == ResultTimedOut) {
70 return result;
71 }
72
73 // Receive the request.
74 if (R_SUCCEEDED(result)) {
75 KServerSession* session = objs[index]->DynamicCast<KServerSession*>();
76 if (session != nullptr) {
77 result = session->ReceiveRequest();
78 if (result == ResultNotFound) {
79 continue;
80 }
81 }
82 }
83
84 *out_index = index;
85 return result;
86 }
87}
88
89} // namespace Kernel::Svc
diff --git a/src/core/hle/kernel/svc/svc_kernel_debug.cpp b/src/core/hle/kernel/svc/svc_kernel_debug.cpp
new file mode 100644
index 000000000..454255e7a
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_kernel_debug.cpp
@@ -0,0 +1,19 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/hle/kernel/svc.h"
5
6namespace Kernel::Svc {
7
8void KernelDebug([[maybe_unused]] Core::System& system, [[maybe_unused]] u32 kernel_debug_type,
9 [[maybe_unused]] u64 param1, [[maybe_unused]] u64 param2,
10 [[maybe_unused]] u64 param3) {
11 // Intentionally do nothing, as this does nothing in released kernel binaries.
12}
13
14void ChangeKernelTraceState([[maybe_unused]] Core::System& system,
15 [[maybe_unused]] u32 trace_state) {
16 // Intentionally do nothing, as this does nothing in released kernel binaries.
17}
18
19} // namespace Kernel::Svc
diff --git a/src/core/hle/kernel/svc/svc_light_ipc.cpp b/src/core/hle/kernel/svc/svc_light_ipc.cpp
new file mode 100644
index 000000000..299e22ae6
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_light_ipc.cpp
@@ -0,0 +1,6 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/hle/kernel/svc.h"
5
6namespace Kernel::Svc {} // namespace Kernel::Svc
diff --git a/src/core/hle/kernel/svc/svc_lock.cpp b/src/core/hle/kernel/svc/svc_lock.cpp
new file mode 100644
index 000000000..45f2a6553
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_lock.cpp
@@ -0,0 +1,57 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/core.h"
5#include "core/hle/kernel/k_memory_layout.h"
6#include "core/hle/kernel/k_process.h"
7#include "core/hle/kernel/svc.h"
8
9namespace Kernel::Svc {
10
11/// Attempts to locks a mutex
12Result ArbitrateLock(Core::System& system, Handle thread_handle, VAddr address, u32 tag) {
13 LOG_TRACE(Kernel_SVC, "called thread_handle=0x{:08X}, address=0x{:X}, tag=0x{:08X}",
14 thread_handle, address, tag);
15
16 // Validate the input address.
17 if (IsKernelAddress(address)) {
18 LOG_ERROR(Kernel_SVC, "Attempting to arbitrate a lock on a kernel address (address={:08X})",
19 address);
20 return ResultInvalidCurrentMemory;
21 }
22 if (!Common::IsAligned(address, sizeof(u32))) {
23 LOG_ERROR(Kernel_SVC, "Input address must be 4 byte aligned (address: {:08X})", address);
24 return ResultInvalidAddress;
25 }
26
27 return system.Kernel().CurrentProcess()->WaitForAddress(thread_handle, address, tag);
28}
29
30Result ArbitrateLock32(Core::System& system, Handle thread_handle, u32 address, u32 tag) {
31 return ArbitrateLock(system, thread_handle, address, tag);
32}
33
34/// Unlock a mutex
35Result ArbitrateUnlock(Core::System& system, VAddr address) {
36 LOG_TRACE(Kernel_SVC, "called address=0x{:X}", address);
37
38 // Validate the input address.
39 if (IsKernelAddress(address)) {
40 LOG_ERROR(Kernel_SVC,
41 "Attempting to arbitrate an unlock on a kernel address (address={:08X})",
42 address);
43 return ResultInvalidCurrentMemory;
44 }
45 if (!Common::IsAligned(address, sizeof(u32))) {
46 LOG_ERROR(Kernel_SVC, "Input address must be 4 byte aligned (address: {:08X})", address);
47 return ResultInvalidAddress;
48 }
49
50 return system.Kernel().CurrentProcess()->SignalToAddress(address);
51}
52
53Result ArbitrateUnlock32(Core::System& system, u32 address) {
54 return ArbitrateUnlock(system, address);
55}
56
57} // namespace Kernel::Svc
diff --git a/src/core/hle/kernel/svc/svc_memory.cpp b/src/core/hle/kernel/svc/svc_memory.cpp
new file mode 100644
index 000000000..f78b1239b
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_memory.cpp
@@ -0,0 +1,189 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/core.h"
5#include "core/hle/kernel/k_process.h"
6#include "core/hle/kernel/svc.h"
7
8namespace Kernel::Svc {
9namespace {
10
11constexpr bool IsValidSetMemoryPermission(MemoryPermission perm) {
12 switch (perm) {
13 case MemoryPermission::None:
14 case MemoryPermission::Read:
15 case MemoryPermission::ReadWrite:
16 return true;
17 default:
18 return false;
19 }
20}
21
22// Checks if address + size is greater than the given address
23// This can return false if the size causes an overflow of a 64-bit type
24// or if the given size is zero.
25constexpr bool IsValidAddressRange(VAddr address, u64 size) {
26 return address + size > address;
27}
28
29// Helper function that performs the common sanity checks for svcMapMemory
30// and svcUnmapMemory. This is doable, as both functions perform their sanitizing
31// in the same order.
32Result MapUnmapMemorySanityChecks(const KPageTable& manager, VAddr dst_addr, VAddr src_addr,
33 u64 size) {
34 if (!Common::Is4KBAligned(dst_addr)) {
35 LOG_ERROR(Kernel_SVC, "Destination address is not aligned to 4KB, 0x{:016X}", dst_addr);
36 return ResultInvalidAddress;
37 }
38
39 if (!Common::Is4KBAligned(src_addr)) {
40 LOG_ERROR(Kernel_SVC, "Source address is not aligned to 4KB, 0x{:016X}", src_addr);
41 return ResultInvalidSize;
42 }
43
44 if (size == 0) {
45 LOG_ERROR(Kernel_SVC, "Size is 0");
46 return ResultInvalidSize;
47 }
48
49 if (!Common::Is4KBAligned(size)) {
50 LOG_ERROR(Kernel_SVC, "Size is not aligned to 4KB, 0x{:016X}", size);
51 return ResultInvalidSize;
52 }
53
54 if (!IsValidAddressRange(dst_addr, size)) {
55 LOG_ERROR(Kernel_SVC,
56 "Destination is not a valid address range, addr=0x{:016X}, size=0x{:016X}",
57 dst_addr, size);
58 return ResultInvalidCurrentMemory;
59 }
60
61 if (!IsValidAddressRange(src_addr, size)) {
62 LOG_ERROR(Kernel_SVC, "Source is not a valid address range, addr=0x{:016X}, size=0x{:016X}",
63 src_addr, size);
64 return ResultInvalidCurrentMemory;
65 }
66
67 if (!manager.IsInsideAddressSpace(src_addr, size)) {
68 LOG_ERROR(Kernel_SVC,
69 "Source is not within the address space, addr=0x{:016X}, size=0x{:016X}",
70 src_addr, size);
71 return ResultInvalidCurrentMemory;
72 }
73
74 if (manager.IsOutsideStackRegion(dst_addr, size)) {
75 LOG_ERROR(Kernel_SVC,
76 "Destination is not within the stack region, addr=0x{:016X}, size=0x{:016X}",
77 dst_addr, size);
78 return ResultInvalidMemoryRegion;
79 }
80
81 if (manager.IsInsideHeapRegion(dst_addr, size)) {
82 LOG_ERROR(Kernel_SVC,
83 "Destination does not fit within the heap region, addr=0x{:016X}, "
84 "size=0x{:016X}",
85 dst_addr, size);
86 return ResultInvalidMemoryRegion;
87 }
88
89 if (manager.IsInsideAliasRegion(dst_addr, size)) {
90 LOG_ERROR(Kernel_SVC,
91 "Destination does not fit within the map region, addr=0x{:016X}, "
92 "size=0x{:016X}",
93 dst_addr, size);
94 return ResultInvalidMemoryRegion;
95 }
96
97 return ResultSuccess;
98}
99
100} // namespace
101
102Result SetMemoryPermission(Core::System& system, VAddr address, u64 size, MemoryPermission perm) {
103 LOG_DEBUG(Kernel_SVC, "called, address=0x{:016X}, size=0x{:X}, perm=0x{:08X", address, size,
104 perm);
105
106 // Validate address / size.
107 R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress);
108 R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize);
109 R_UNLESS(size > 0, ResultInvalidSize);
110 R_UNLESS((address < address + size), ResultInvalidCurrentMemory);
111
112 // Validate the permission.
113 R_UNLESS(IsValidSetMemoryPermission(perm), ResultInvalidNewMemoryPermission);
114
115 // Validate that the region is in range for the current process.
116 auto& page_table = system.Kernel().CurrentProcess()->PageTable();
117 R_UNLESS(page_table.Contains(address, size), ResultInvalidCurrentMemory);
118
119 // Set the memory attribute.
120 return page_table.SetMemoryPermission(address, size, perm);
121}
122
123Result SetMemoryAttribute(Core::System& system, VAddr address, u64 size, u32 mask, u32 attr) {
124 LOG_DEBUG(Kernel_SVC,
125 "called, address=0x{:016X}, size=0x{:X}, mask=0x{:08X}, attribute=0x{:08X}", address,
126 size, mask, attr);
127
128 // Validate address / size.
129 R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress);
130 R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize);
131 R_UNLESS(size > 0, ResultInvalidSize);
132 R_UNLESS((address < address + size), ResultInvalidCurrentMemory);
133
134 // Validate the attribute and mask.
135 constexpr u32 SupportedMask = static_cast<u32>(MemoryAttribute::Uncached);
136 R_UNLESS((mask | attr) == mask, ResultInvalidCombination);
137 R_UNLESS((mask | attr | SupportedMask) == SupportedMask, ResultInvalidCombination);
138
139 // Validate that the region is in range for the current process.
140 auto& page_table{system.Kernel().CurrentProcess()->PageTable()};
141 R_UNLESS(page_table.Contains(address, size), ResultInvalidCurrentMemory);
142
143 // Set the memory attribute.
144 return page_table.SetMemoryAttribute(address, size, mask, attr);
145}
146
147Result SetMemoryAttribute32(Core::System& system, u32 address, u32 size, u32 mask, u32 attr) {
148 return SetMemoryAttribute(system, address, size, mask, attr);
149}
150
151/// Maps a memory range into a different range.
152Result MapMemory(Core::System& system, VAddr dst_addr, VAddr src_addr, u64 size) {
153 LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr,
154 src_addr, size);
155
156 auto& page_table{system.Kernel().CurrentProcess()->PageTable()};
157
158 if (const Result result{MapUnmapMemorySanityChecks(page_table, dst_addr, src_addr, size)};
159 result.IsError()) {
160 return result;
161 }
162
163 return page_table.MapMemory(dst_addr, src_addr, size);
164}
165
166Result MapMemory32(Core::System& system, u32 dst_addr, u32 src_addr, u32 size) {
167 return MapMemory(system, dst_addr, src_addr, size);
168}
169
170/// Unmaps a region that was previously mapped with svcMapMemory
171Result UnmapMemory(Core::System& system, VAddr dst_addr, VAddr src_addr, u64 size) {
172 LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr,
173 src_addr, size);
174
175 auto& page_table{system.Kernel().CurrentProcess()->PageTable()};
176
177 if (const Result result{MapUnmapMemorySanityChecks(page_table, dst_addr, src_addr, size)};
178 result.IsError()) {
179 return result;
180 }
181
182 return page_table.UnmapMemory(dst_addr, src_addr, size);
183}
184
185Result UnmapMemory32(Core::System& system, u32 dst_addr, u32 src_addr, u32 size) {
186 return UnmapMemory(system, dst_addr, src_addr, size);
187}
188
189} // namespace Kernel::Svc
diff --git a/src/core/hle/kernel/svc/svc_physical_memory.cpp b/src/core/hle/kernel/svc/svc_physical_memory.cpp
new file mode 100644
index 000000000..0fc262203
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_physical_memory.cpp
@@ -0,0 +1,137 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/core.h"
5#include "core/hle/kernel/k_process.h"
6#include "core/hle/kernel/svc.h"
7
8namespace Kernel::Svc {
9
10/// Set the process heap to a given Size. It can both extend and shrink the heap.
11Result SetHeapSize(Core::System& system, VAddr* out_address, u64 size) {
12 LOG_TRACE(Kernel_SVC, "called, heap_size=0x{:X}", size);
13
14 // Validate size.
15 R_UNLESS(Common::IsAligned(size, HeapSizeAlignment), ResultInvalidSize);
16 R_UNLESS(size < MainMemorySizeMax, ResultInvalidSize);
17
18 // Set the heap size.
19 R_TRY(system.Kernel().CurrentProcess()->PageTable().SetHeapSize(out_address, size));
20
21 return ResultSuccess;
22}
23
24Result SetHeapSize32(Core::System& system, u32* heap_addr, u32 heap_size) {
25 VAddr temp_heap_addr{};
26 const Result result{SetHeapSize(system, &temp_heap_addr, heap_size)};
27 *heap_addr = static_cast<u32>(temp_heap_addr);
28 return result;
29}
30
31/// Maps memory at a desired address
32Result MapPhysicalMemory(Core::System& system, VAddr addr, u64 size) {
33 LOG_DEBUG(Kernel_SVC, "called, addr=0x{:016X}, size=0x{:X}", addr, size);
34
35 if (!Common::Is4KBAligned(addr)) {
36 LOG_ERROR(Kernel_SVC, "Address is not aligned to 4KB, 0x{:016X}", addr);
37 return ResultInvalidAddress;
38 }
39
40 if (!Common::Is4KBAligned(size)) {
41 LOG_ERROR(Kernel_SVC, "Size is not aligned to 4KB, 0x{:X}", size);
42 return ResultInvalidSize;
43 }
44
45 if (size == 0) {
46 LOG_ERROR(Kernel_SVC, "Size is zero");
47 return ResultInvalidSize;
48 }
49
50 if (!(addr < addr + size)) {
51 LOG_ERROR(Kernel_SVC, "Size causes 64-bit overflow of address");
52 return ResultInvalidMemoryRegion;
53 }
54
55 KProcess* const current_process{system.Kernel().CurrentProcess()};
56 auto& page_table{current_process->PageTable()};
57
58 if (current_process->GetSystemResourceSize() == 0) {
59 LOG_ERROR(Kernel_SVC, "System Resource Size is zero");
60 return ResultInvalidState;
61 }
62
63 if (!page_table.IsInsideAddressSpace(addr, size)) {
64 LOG_ERROR(Kernel_SVC,
65 "Address is not within the address space, addr=0x{:016X}, size=0x{:016X}", addr,
66 size);
67 return ResultInvalidMemoryRegion;
68 }
69
70 if (page_table.IsOutsideAliasRegion(addr, size)) {
71 LOG_ERROR(Kernel_SVC,
72 "Address is not within the alias region, addr=0x{:016X}, size=0x{:016X}", addr,
73 size);
74 return ResultInvalidMemoryRegion;
75 }
76
77 return page_table.MapPhysicalMemory(addr, size);
78}
79
80Result MapPhysicalMemory32(Core::System& system, u32 addr, u32 size) {
81 return MapPhysicalMemory(system, addr, size);
82}
83
84/// Unmaps memory previously mapped via MapPhysicalMemory
85Result UnmapPhysicalMemory(Core::System& system, VAddr addr, u64 size) {
86 LOG_DEBUG(Kernel_SVC, "called, addr=0x{:016X}, size=0x{:X}", addr, size);
87
88 if (!Common::Is4KBAligned(addr)) {
89 LOG_ERROR(Kernel_SVC, "Address is not aligned to 4KB, 0x{:016X}", addr);
90 return ResultInvalidAddress;
91 }
92
93 if (!Common::Is4KBAligned(size)) {
94 LOG_ERROR(Kernel_SVC, "Size is not aligned to 4KB, 0x{:X}", size);
95 return ResultInvalidSize;
96 }
97
98 if (size == 0) {
99 LOG_ERROR(Kernel_SVC, "Size is zero");
100 return ResultInvalidSize;
101 }
102
103 if (!(addr < addr + size)) {
104 LOG_ERROR(Kernel_SVC, "Size causes 64-bit overflow of address");
105 return ResultInvalidMemoryRegion;
106 }
107
108 KProcess* const current_process{system.Kernel().CurrentProcess()};
109 auto& page_table{current_process->PageTable()};
110
111 if (current_process->GetSystemResourceSize() == 0) {
112 LOG_ERROR(Kernel_SVC, "System Resource Size is zero");
113 return ResultInvalidState;
114 }
115
116 if (!page_table.IsInsideAddressSpace(addr, size)) {
117 LOG_ERROR(Kernel_SVC,
118 "Address is not within the address space, addr=0x{:016X}, size=0x{:016X}", addr,
119 size);
120 return ResultInvalidMemoryRegion;
121 }
122
123 if (page_table.IsOutsideAliasRegion(addr, size)) {
124 LOG_ERROR(Kernel_SVC,
125 "Address is not within the alias region, addr=0x{:016X}, size=0x{:016X}", addr,
126 size);
127 return ResultInvalidMemoryRegion;
128 }
129
130 return page_table.UnmapPhysicalMemory(addr, size);
131}
132
133Result UnmapPhysicalMemory32(Core::System& system, u32 addr, u32 size) {
134 return UnmapPhysicalMemory(system, addr, size);
135}
136
137} // namespace Kernel::Svc
diff --git a/src/core/hle/kernel/svc/svc_port.cpp b/src/core/hle/kernel/svc/svc_port.cpp
new file mode 100644
index 000000000..cdfe0dd16
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_port.cpp
@@ -0,0 +1,71 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "common/scope_exit.h"
5#include "core/core.h"
6#include "core/hle/kernel/k_client_port.h"
7#include "core/hle/kernel/k_client_session.h"
8#include "core/hle/kernel/k_port.h"
9#include "core/hle/kernel/k_process.h"
10#include "core/hle/kernel/svc.h"
11
12namespace Kernel::Svc {
13
14/// Connect to an OS service given the port name, returns the handle to the port to out
15Result ConnectToNamedPort(Core::System& system, Handle* out, VAddr port_name_address) {
16 auto& memory = system.Memory();
17 if (!memory.IsValidVirtualAddress(port_name_address)) {
18 LOG_ERROR(Kernel_SVC,
19 "Port Name Address is not a valid virtual address, port_name_address=0x{:016X}",
20 port_name_address);
21 return ResultNotFound;
22 }
23
24 static constexpr std::size_t PortNameMaxLength = 11;
25 // Read 1 char beyond the max allowed port name to detect names that are too long.
26 const std::string port_name = memory.ReadCString(port_name_address, PortNameMaxLength + 1);
27 if (port_name.size() > PortNameMaxLength) {
28 LOG_ERROR(Kernel_SVC, "Port name is too long, expected {} but got {}", PortNameMaxLength,
29 port_name.size());
30 return ResultOutOfRange;
31 }
32
33 LOG_TRACE(Kernel_SVC, "called port_name={}", port_name);
34
35 // Get the current handle table.
36 auto& kernel = system.Kernel();
37 auto& handle_table = kernel.CurrentProcess()->GetHandleTable();
38
39 // Find the client port.
40 auto port = kernel.CreateNamedServicePort(port_name);
41 if (!port) {
42 LOG_ERROR(Kernel_SVC, "tried to connect to unknown port: {}", port_name);
43 return ResultNotFound;
44 }
45
46 // Reserve a handle for the port.
47 // NOTE: Nintendo really does write directly to the output handle here.
48 R_TRY(handle_table.Reserve(out));
49 auto handle_guard = SCOPE_GUARD({ handle_table.Unreserve(*out); });
50
51 // Create a session.
52 KClientSession* session{};
53 R_TRY(port->CreateSession(std::addressof(session)));
54
55 kernel.RegisterNamedServiceHandler(port_name, &port->GetParent()->GetServerPort());
56
57 // Register the session in the table, close the extra reference.
58 handle_table.Register(*out, session);
59 session->Close();
60
61 // We succeeded.
62 handle_guard.Cancel();
63 return ResultSuccess;
64}
65
66Result ConnectToNamedPort32(Core::System& system, Handle* out_handle, u32 port_name_address) {
67
68 return ConnectToNamedPort(system, out_handle, port_name_address);
69}
70
71} // namespace Kernel::Svc
diff --git a/src/core/hle/kernel/svc/svc_power_management.cpp b/src/core/hle/kernel/svc/svc_power_management.cpp
new file mode 100644
index 000000000..299e22ae6
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_power_management.cpp
@@ -0,0 +1,6 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/hle/kernel/svc.h"
5
6namespace Kernel::Svc {} // namespace Kernel::Svc
diff --git a/src/core/hle/kernel/svc/svc_process.cpp b/src/core/hle/kernel/svc/svc_process.cpp
new file mode 100644
index 000000000..d6c8b4561
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_process.cpp
@@ -0,0 +1,124 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/core.h"
5#include "core/hle/kernel/k_process.h"
6#include "core/hle/kernel/svc.h"
7
8namespace Kernel::Svc {
9
10/// Exits the current process
11void ExitProcess(Core::System& system) {
12 auto* current_process = system.Kernel().CurrentProcess();
13
14 LOG_INFO(Kernel_SVC, "Process {} exiting", current_process->GetProcessID());
15 ASSERT_MSG(current_process->GetState() == KProcess::State::Running,
16 "Process has already exited");
17
18 system.Exit();
19}
20
21void ExitProcess32(Core::System& system) {
22 ExitProcess(system);
23}
24
25/// Gets the ID of the specified process or a specified thread's owning process.
26Result GetProcessId(Core::System& system, u64* out_process_id, Handle handle) {
27 LOG_DEBUG(Kernel_SVC, "called handle=0x{:08X}", handle);
28
29 // Get the object from the handle table.
30 KScopedAutoObject obj =
31 system.Kernel().CurrentProcess()->GetHandleTable().GetObject<KAutoObject>(
32 static_cast<Handle>(handle));
33 R_UNLESS(obj.IsNotNull(), ResultInvalidHandle);
34
35 // Get the process from the object.
36 KProcess* process = nullptr;
37 if (KProcess* p = obj->DynamicCast<KProcess*>(); p != nullptr) {
38 // The object is a process, so we can use it directly.
39 process = p;
40 } else if (KThread* t = obj->DynamicCast<KThread*>(); t != nullptr) {
41 // The object is a thread, so we want to use its parent.
42 process = reinterpret_cast<KThread*>(obj.GetPointerUnsafe())->GetOwnerProcess();
43 } else {
44 // TODO(bunnei): This should also handle debug objects before returning.
45 UNIMPLEMENTED_MSG("Debug objects not implemented");
46 }
47
48 // Make sure the target process exists.
49 R_UNLESS(process != nullptr, ResultInvalidHandle);
50
51 // Get the process id.
52 *out_process_id = process->GetId();
53
54 return ResultSuccess;
55}
56
57Result GetProcessId32(Core::System& system, u32* out_process_id_low, u32* out_process_id_high,
58 Handle handle) {
59 u64 out_process_id{};
60 const auto result = GetProcessId(system, &out_process_id, handle);
61 *out_process_id_low = static_cast<u32>(out_process_id);
62 *out_process_id_high = static_cast<u32>(out_process_id >> 32);
63 return result;
64}
65
66Result GetProcessList(Core::System& system, u32* out_num_processes, VAddr out_process_ids,
67 u32 out_process_ids_size) {
68 LOG_DEBUG(Kernel_SVC, "called. out_process_ids=0x{:016X}, out_process_ids_size={}",
69 out_process_ids, out_process_ids_size);
70
71 // If the supplied size is negative or greater than INT32_MAX / sizeof(u64), bail.
72 if ((out_process_ids_size & 0xF0000000) != 0) {
73 LOG_ERROR(Kernel_SVC,
74 "Supplied size outside [0, 0x0FFFFFFF] range. out_process_ids_size={}",
75 out_process_ids_size);
76 return ResultOutOfRange;
77 }
78
79 const auto& kernel = system.Kernel();
80 const auto total_copy_size = out_process_ids_size * sizeof(u64);
81
82 if (out_process_ids_size > 0 && !kernel.CurrentProcess()->PageTable().IsInsideAddressSpace(
83 out_process_ids, total_copy_size)) {
84 LOG_ERROR(Kernel_SVC, "Address range outside address space. begin=0x{:016X}, end=0x{:016X}",
85 out_process_ids, out_process_ids + total_copy_size);
86 return ResultInvalidCurrentMemory;
87 }
88
89 auto& memory = system.Memory();
90 const auto& process_list = kernel.GetProcessList();
91 const auto num_processes = process_list.size();
92 const auto copy_amount = std::min(std::size_t{out_process_ids_size}, num_processes);
93
94 for (std::size_t i = 0; i < copy_amount; ++i) {
95 memory.Write64(out_process_ids, process_list[i]->GetProcessID());
96 out_process_ids += sizeof(u64);
97 }
98
99 *out_num_processes = static_cast<u32>(num_processes);
100 return ResultSuccess;
101}
102
103Result GetProcessInfo(Core::System& system, u64* out, Handle process_handle, u32 type) {
104 LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, type=0x{:X}", process_handle, type);
105
106 const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
107 KScopedAutoObject process = handle_table.GetObject<KProcess>(process_handle);
108 if (process.IsNull()) {
109 LOG_ERROR(Kernel_SVC, "Process handle does not exist, process_handle=0x{:08X}",
110 process_handle);
111 return ResultInvalidHandle;
112 }
113
114 const auto info_type = static_cast<ProcessInfoType>(type);
115 if (info_type != ProcessInfoType::ProcessState) {
116 LOG_ERROR(Kernel_SVC, "Expected info_type to be ProcessState but got {} instead", type);
117 return ResultInvalidEnumValue;
118 }
119
120 *out = static_cast<u64>(process->GetState());
121 return ResultSuccess;
122}
123
124} // namespace Kernel::Svc
diff --git a/src/core/hle/kernel/svc/svc_process_memory.cpp b/src/core/hle/kernel/svc/svc_process_memory.cpp
new file mode 100644
index 000000000..b6ac43af2
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_process_memory.cpp
@@ -0,0 +1,274 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/core.h"
5#include "core/hle/kernel/k_process.h"
6#include "core/hle/kernel/svc.h"
7
8namespace Kernel::Svc {
9namespace {
10
11constexpr bool IsValidAddressRange(VAddr address, u64 size) {
12 return address + size > address;
13}
14
15constexpr bool IsValidProcessMemoryPermission(Svc::MemoryPermission perm) {
16 switch (perm) {
17 case Svc::MemoryPermission::None:
18 case Svc::MemoryPermission::Read:
19 case Svc::MemoryPermission::ReadWrite:
20 case Svc::MemoryPermission::ReadExecute:
21 return true;
22 default:
23 return false;
24 }
25}
26
27} // namespace
28
29Result SetProcessMemoryPermission(Core::System& system, Handle process_handle, VAddr address,
30 u64 size, Svc::MemoryPermission perm) {
31 LOG_TRACE(Kernel_SVC,
32 "called, process_handle=0x{:X}, addr=0x{:X}, size=0x{:X}, permissions=0x{:08X}",
33 process_handle, address, size, perm);
34
35 // Validate the address/size.
36 R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress);
37 R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize);
38 R_UNLESS(size > 0, ResultInvalidSize);
39 R_UNLESS((address < address + size), ResultInvalidCurrentMemory);
40 R_UNLESS(address == static_cast<uintptr_t>(address), ResultInvalidCurrentMemory);
41 R_UNLESS(size == static_cast<size_t>(size), ResultInvalidCurrentMemory);
42
43 // Validate the memory permission.
44 R_UNLESS(IsValidProcessMemoryPermission(perm), ResultInvalidNewMemoryPermission);
45
46 // Get the process from its handle.
47 KScopedAutoObject process =
48 system.CurrentProcess()->GetHandleTable().GetObject<KProcess>(process_handle);
49 R_UNLESS(process.IsNotNull(), ResultInvalidHandle);
50
51 // Validate that the address is in range.
52 auto& page_table = process->PageTable();
53 R_UNLESS(page_table.Contains(address, size), ResultInvalidCurrentMemory);
54
55 // Set the memory permission.
56 return page_table.SetProcessMemoryPermission(address, size, perm);
57}
58
59Result MapProcessMemory(Core::System& system, VAddr dst_address, Handle process_handle,
60 VAddr src_address, u64 size) {
61 LOG_TRACE(Kernel_SVC,
62 "called, dst_address=0x{:X}, process_handle=0x{:X}, src_address=0x{:X}, size=0x{:X}",
63 dst_address, process_handle, src_address, size);
64
65 // Validate the address/size.
66 R_UNLESS(Common::IsAligned(dst_address, PageSize), ResultInvalidAddress);
67 R_UNLESS(Common::IsAligned(src_address, PageSize), ResultInvalidAddress);
68 R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize);
69 R_UNLESS(size > 0, ResultInvalidSize);
70 R_UNLESS((dst_address < dst_address + size), ResultInvalidCurrentMemory);
71 R_UNLESS((src_address < src_address + size), ResultInvalidCurrentMemory);
72
73 // Get the processes.
74 KProcess* dst_process = system.CurrentProcess();
75 KScopedAutoObject src_process =
76 dst_process->GetHandleTable().GetObjectWithoutPseudoHandle<KProcess>(process_handle);
77 R_UNLESS(src_process.IsNotNull(), ResultInvalidHandle);
78
79 // Get the page tables.
80 auto& dst_pt = dst_process->PageTable();
81 auto& src_pt = src_process->PageTable();
82
83 // Validate that the mapping is in range.
84 R_UNLESS(src_pt.Contains(src_address, size), ResultInvalidCurrentMemory);
85 R_UNLESS(dst_pt.CanContain(dst_address, size, KMemoryState::SharedCode),
86 ResultInvalidMemoryRegion);
87
88 // Create a new page group.
89 KPageGroup pg{system.Kernel(), dst_pt.GetBlockInfoManager()};
90 R_TRY(src_pt.MakeAndOpenPageGroup(
91 std::addressof(pg), src_address, size / PageSize, KMemoryState::FlagCanMapProcess,
92 KMemoryState::FlagCanMapProcess, KMemoryPermission::None, KMemoryPermission::None,
93 KMemoryAttribute::All, KMemoryAttribute::None));
94
95 // Map the group.
96 R_TRY(dst_pt.MapPageGroup(dst_address, pg, KMemoryState::SharedCode,
97 KMemoryPermission::UserReadWrite));
98
99 return ResultSuccess;
100}
101
102Result UnmapProcessMemory(Core::System& system, VAddr dst_address, Handle process_handle,
103 VAddr src_address, u64 size) {
104 LOG_TRACE(Kernel_SVC,
105 "called, dst_address=0x{:X}, process_handle=0x{:X}, src_address=0x{:X}, size=0x{:X}",
106 dst_address, process_handle, src_address, size);
107
108 // Validate the address/size.
109 R_UNLESS(Common::IsAligned(dst_address, PageSize), ResultInvalidAddress);
110 R_UNLESS(Common::IsAligned(src_address, PageSize), ResultInvalidAddress);
111 R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize);
112 R_UNLESS(size > 0, ResultInvalidSize);
113 R_UNLESS((dst_address < dst_address + size), ResultInvalidCurrentMemory);
114 R_UNLESS((src_address < src_address + size), ResultInvalidCurrentMemory);
115
116 // Get the processes.
117 KProcess* dst_process = system.CurrentProcess();
118 KScopedAutoObject src_process =
119 dst_process->GetHandleTable().GetObjectWithoutPseudoHandle<KProcess>(process_handle);
120 R_UNLESS(src_process.IsNotNull(), ResultInvalidHandle);
121
122 // Get the page tables.
123 auto& dst_pt = dst_process->PageTable();
124 auto& src_pt = src_process->PageTable();
125
126 // Validate that the mapping is in range.
127 R_UNLESS(src_pt.Contains(src_address, size), ResultInvalidCurrentMemory);
128 R_UNLESS(dst_pt.CanContain(dst_address, size, KMemoryState::SharedCode),
129 ResultInvalidMemoryRegion);
130
131 // Unmap the memory.
132 R_TRY(dst_pt.UnmapProcessMemory(dst_address, size, src_pt, src_address));
133
134 return ResultSuccess;
135}
136
137Result MapProcessCodeMemory(Core::System& system, Handle process_handle, u64 dst_address,
138 u64 src_address, u64 size) {
139 LOG_DEBUG(Kernel_SVC,
140 "called. process_handle=0x{:08X}, dst_address=0x{:016X}, "
141 "src_address=0x{:016X}, size=0x{:016X}",
142 process_handle, dst_address, src_address, size);
143
144 if (!Common::Is4KBAligned(src_address)) {
145 LOG_ERROR(Kernel_SVC, "src_address is not page-aligned (src_address=0x{:016X}).",
146 src_address);
147 return ResultInvalidAddress;
148 }
149
150 if (!Common::Is4KBAligned(dst_address)) {
151 LOG_ERROR(Kernel_SVC, "dst_address is not page-aligned (dst_address=0x{:016X}).",
152 dst_address);
153 return ResultInvalidAddress;
154 }
155
156 if (size == 0 || !Common::Is4KBAligned(size)) {
157 LOG_ERROR(Kernel_SVC, "Size is zero or not page-aligned (size=0x{:016X})", size);
158 return ResultInvalidSize;
159 }
160
161 if (!IsValidAddressRange(dst_address, size)) {
162 LOG_ERROR(Kernel_SVC,
163 "Destination address range overflows the address space (dst_address=0x{:016X}, "
164 "size=0x{:016X}).",
165 dst_address, size);
166 return ResultInvalidCurrentMemory;
167 }
168
169 if (!IsValidAddressRange(src_address, size)) {
170 LOG_ERROR(Kernel_SVC,
171 "Source address range overflows the address space (src_address=0x{:016X}, "
172 "size=0x{:016X}).",
173 src_address, size);
174 return ResultInvalidCurrentMemory;
175 }
176
177 const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
178 KScopedAutoObject process = handle_table.GetObject<KProcess>(process_handle);
179 if (process.IsNull()) {
180 LOG_ERROR(Kernel_SVC, "Invalid process handle specified (handle=0x{:08X}).",
181 process_handle);
182 return ResultInvalidHandle;
183 }
184
185 auto& page_table = process->PageTable();
186 if (!page_table.IsInsideAddressSpace(src_address, size)) {
187 LOG_ERROR(Kernel_SVC,
188 "Source address range is not within the address space (src_address=0x{:016X}, "
189 "size=0x{:016X}).",
190 src_address, size);
191 return ResultInvalidCurrentMemory;
192 }
193
194 if (!page_table.IsInsideASLRRegion(dst_address, size)) {
195 LOG_ERROR(Kernel_SVC,
196 "Destination address range is not within the ASLR region (dst_address=0x{:016X}, "
197 "size=0x{:016X}).",
198 dst_address, size);
199 return ResultInvalidMemoryRegion;
200 }
201
202 return page_table.MapCodeMemory(dst_address, src_address, size);
203}
204
205Result UnmapProcessCodeMemory(Core::System& system, Handle process_handle, u64 dst_address,
206 u64 src_address, u64 size) {
207 LOG_DEBUG(Kernel_SVC,
208 "called. process_handle=0x{:08X}, dst_address=0x{:016X}, src_address=0x{:016X}, "
209 "size=0x{:016X}",
210 process_handle, dst_address, src_address, size);
211
212 if (!Common::Is4KBAligned(dst_address)) {
213 LOG_ERROR(Kernel_SVC, "dst_address is not page-aligned (dst_address=0x{:016X}).",
214 dst_address);
215 return ResultInvalidAddress;
216 }
217
218 if (!Common::Is4KBAligned(src_address)) {
219 LOG_ERROR(Kernel_SVC, "src_address is not page-aligned (src_address=0x{:016X}).",
220 src_address);
221 return ResultInvalidAddress;
222 }
223
224 if (size == 0 || !Common::Is4KBAligned(size)) {
225 LOG_ERROR(Kernel_SVC, "Size is zero or not page-aligned (size=0x{:016X}).", size);
226 return ResultInvalidSize;
227 }
228
229 if (!IsValidAddressRange(dst_address, size)) {
230 LOG_ERROR(Kernel_SVC,
231 "Destination address range overflows the address space (dst_address=0x{:016X}, "
232 "size=0x{:016X}).",
233 dst_address, size);
234 return ResultInvalidCurrentMemory;
235 }
236
237 if (!IsValidAddressRange(src_address, size)) {
238 LOG_ERROR(Kernel_SVC,
239 "Source address range overflows the address space (src_address=0x{:016X}, "
240 "size=0x{:016X}).",
241 src_address, size);
242 return ResultInvalidCurrentMemory;
243 }
244
245 const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
246 KScopedAutoObject process = handle_table.GetObject<KProcess>(process_handle);
247 if (process.IsNull()) {
248 LOG_ERROR(Kernel_SVC, "Invalid process handle specified (handle=0x{:08X}).",
249 process_handle);
250 return ResultInvalidHandle;
251 }
252
253 auto& page_table = process->PageTable();
254 if (!page_table.IsInsideAddressSpace(src_address, size)) {
255 LOG_ERROR(Kernel_SVC,
256 "Source address range is not within the address space (src_address=0x{:016X}, "
257 "size=0x{:016X}).",
258 src_address, size);
259 return ResultInvalidCurrentMemory;
260 }
261
262 if (!page_table.IsInsideASLRRegion(dst_address, size)) {
263 LOG_ERROR(Kernel_SVC,
264 "Destination address range is not within the ASLR region (dst_address=0x{:016X}, "
265 "size=0x{:016X}).",
266 dst_address, size);
267 return ResultInvalidMemoryRegion;
268 }
269
270 return page_table.UnmapCodeMemory(dst_address, src_address, size,
271 KPageTable::ICacheInvalidationStrategy::InvalidateAll);
272}
273
274} // namespace Kernel::Svc
diff --git a/src/core/hle/kernel/svc/svc_processor.cpp b/src/core/hle/kernel/svc/svc_processor.cpp
new file mode 100644
index 000000000..8561cf74f
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_processor.cpp
@@ -0,0 +1,21 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "common/logging/log.h"
5#include "core/core.h"
6#include "core/hle/kernel/physical_core.h"
7#include "core/hle/kernel/svc.h"
8
9namespace Kernel::Svc {
10
11/// Get which CPU core is executing the current thread
12u32 GetCurrentProcessorNumber(Core::System& system) {
13 LOG_TRACE(Kernel_SVC, "called");
14 return static_cast<u32>(system.CurrentPhysicalCore().CoreIndex());
15}
16
17u32 GetCurrentProcessorNumber32(Core::System& system) {
18 return GetCurrentProcessorNumber(system);
19}
20
21} // namespace Kernel::Svc
diff --git a/src/core/hle/kernel/svc/svc_query_memory.cpp b/src/core/hle/kernel/svc/svc_query_memory.cpp
new file mode 100644
index 000000000..aac3b2eca
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_query_memory.cpp
@@ -0,0 +1,55 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/core.h"
5#include "core/hle/kernel/k_process.h"
6#include "core/hle/kernel/svc.h"
7
8namespace Kernel::Svc {
9
10Result QueryMemory(Core::System& system, VAddr memory_info_address, VAddr page_info_address,
11 VAddr query_address) {
12 LOG_TRACE(Kernel_SVC,
13 "called, memory_info_address=0x{:016X}, page_info_address=0x{:016X}, "
14 "query_address=0x{:016X}",
15 memory_info_address, page_info_address, query_address);
16
17 return QueryProcessMemory(system, memory_info_address, page_info_address, CurrentProcess,
18 query_address);
19}
20
21Result QueryMemory32(Core::System& system, u32 memory_info_address, u32 page_info_address,
22 u32 query_address) {
23 return QueryMemory(system, memory_info_address, page_info_address, query_address);
24}
25
26Result QueryProcessMemory(Core::System& system, VAddr memory_info_address, VAddr page_info_address,
27 Handle process_handle, VAddr address) {
28 LOG_TRACE(Kernel_SVC, "called process=0x{:08X} address={:X}", process_handle, address);
29 const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
30 KScopedAutoObject process = handle_table.GetObject<KProcess>(process_handle);
31 if (process.IsNull()) {
32 LOG_ERROR(Kernel_SVC, "Process handle does not exist, process_handle=0x{:08X}",
33 process_handle);
34 return ResultInvalidHandle;
35 }
36
37 auto& memory{system.Memory()};
38 const auto memory_info{process->PageTable().QueryInfo(address).GetSvcMemoryInfo()};
39
40 memory.Write64(memory_info_address + 0x00, memory_info.base_address);
41 memory.Write64(memory_info_address + 0x08, memory_info.size);
42 memory.Write32(memory_info_address + 0x10, static_cast<u32>(memory_info.state) & 0xff);
43 memory.Write32(memory_info_address + 0x14, static_cast<u32>(memory_info.attribute));
44 memory.Write32(memory_info_address + 0x18, static_cast<u32>(memory_info.permission));
45 memory.Write32(memory_info_address + 0x1c, memory_info.ipc_count);
46 memory.Write32(memory_info_address + 0x20, memory_info.device_count);
47 memory.Write32(memory_info_address + 0x24, 0);
48
49 // Page info appears to be currently unused by the kernel and is always set to zero.
50 memory.Write32(page_info_address, 0);
51
52 return ResultSuccess;
53}
54
55} // namespace Kernel::Svc
diff --git a/src/core/hle/kernel/svc/svc_register.cpp b/src/core/hle/kernel/svc/svc_register.cpp
new file mode 100644
index 000000000..299e22ae6
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_register.cpp
@@ -0,0 +1,6 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/hle/kernel/svc.h"
5
6namespace Kernel::Svc {} // namespace Kernel::Svc
diff --git a/src/core/hle/kernel/svc/svc_resource_limit.cpp b/src/core/hle/kernel/svc/svc_resource_limit.cpp
new file mode 100644
index 000000000..679ba10fa
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_resource_limit.cpp
@@ -0,0 +1,95 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "common/scope_exit.h"
5#include "core/core.h"
6#include "core/hle/kernel/k_process.h"
7#include "core/hle/kernel/k_resource_limit.h"
8#include "core/hle/kernel/svc.h"
9
10namespace Kernel::Svc {
11
12Result CreateResourceLimit(Core::System& system, Handle* out_handle) {
13 LOG_DEBUG(Kernel_SVC, "called");
14
15 // Create a new resource limit.
16 auto& kernel = system.Kernel();
17 KResourceLimit* resource_limit = KResourceLimit::Create(kernel);
18 R_UNLESS(resource_limit != nullptr, ResultOutOfResource);
19
20 // Ensure we don't leak a reference to the limit.
21 SCOPE_EXIT({ resource_limit->Close(); });
22
23 // Initialize the resource limit.
24 resource_limit->Initialize(&system.CoreTiming());
25
26 // Register the limit.
27 KResourceLimit::Register(kernel, resource_limit);
28
29 // Add the limit to the handle table.
30 R_TRY(kernel.CurrentProcess()->GetHandleTable().Add(out_handle, resource_limit));
31
32 return ResultSuccess;
33}
34
35Result GetResourceLimitLimitValue(Core::System& system, u64* out_limit_value,
36 Handle resource_limit_handle, LimitableResource which) {
37 LOG_DEBUG(Kernel_SVC, "called, resource_limit_handle={:08X}, which={}", resource_limit_handle,
38 which);
39
40 // Validate the resource.
41 R_UNLESS(IsValidResourceType(which), ResultInvalidEnumValue);
42
43 // Get the resource limit.
44 auto& kernel = system.Kernel();
45 KScopedAutoObject resource_limit =
46 kernel.CurrentProcess()->GetHandleTable().GetObject<KResourceLimit>(resource_limit_handle);
47 R_UNLESS(resource_limit.IsNotNull(), ResultInvalidHandle);
48
49 // Get the limit value.
50 *out_limit_value = resource_limit->GetLimitValue(which);
51
52 return ResultSuccess;
53}
54
55Result GetResourceLimitCurrentValue(Core::System& system, u64* out_current_value,
56 Handle resource_limit_handle, LimitableResource which) {
57 LOG_DEBUG(Kernel_SVC, "called, resource_limit_handle={:08X}, which={}", resource_limit_handle,
58 which);
59
60 // Validate the resource.
61 R_UNLESS(IsValidResourceType(which), ResultInvalidEnumValue);
62
63 // Get the resource limit.
64 auto& kernel = system.Kernel();
65 KScopedAutoObject resource_limit =
66 kernel.CurrentProcess()->GetHandleTable().GetObject<KResourceLimit>(resource_limit_handle);
67 R_UNLESS(resource_limit.IsNotNull(), ResultInvalidHandle);
68
69 // Get the current value.
70 *out_current_value = resource_limit->GetCurrentValue(which);
71
72 return ResultSuccess;
73}
74
75Result SetResourceLimitLimitValue(Core::System& system, Handle resource_limit_handle,
76 LimitableResource which, u64 limit_value) {
77 LOG_DEBUG(Kernel_SVC, "called, resource_limit_handle={:08X}, which={}, limit_value={}",
78 resource_limit_handle, which, limit_value);
79
80 // Validate the resource.
81 R_UNLESS(IsValidResourceType(which), ResultInvalidEnumValue);
82
83 // Get the resource limit.
84 auto& kernel = system.Kernel();
85 KScopedAutoObject resource_limit =
86 kernel.CurrentProcess()->GetHandleTable().GetObject<KResourceLimit>(resource_limit_handle);
87 R_UNLESS(resource_limit.IsNotNull(), ResultInvalidHandle);
88
89 // Set the limit value.
90 R_TRY(resource_limit->SetLimitValue(which, limit_value));
91
92 return ResultSuccess;
93}
94
95} // namespace Kernel::Svc
diff --git a/src/core/hle/kernel/svc/svc_secure_monitor_call.cpp b/src/core/hle/kernel/svc/svc_secure_monitor_call.cpp
new file mode 100644
index 000000000..299e22ae6
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_secure_monitor_call.cpp
@@ -0,0 +1,6 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/hle/kernel/svc.h"
5
6namespace Kernel::Svc {} // namespace Kernel::Svc
diff --git a/src/core/hle/kernel/svc/svc_session.cpp b/src/core/hle/kernel/svc/svc_session.cpp
new file mode 100644
index 000000000..dac8ce33c
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_session.cpp
@@ -0,0 +1,103 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "common/scope_exit.h"
5#include "core/core.h"
6#include "core/hle/kernel/k_process.h"
7#include "core/hle/kernel/k_scoped_resource_reservation.h"
8#include "core/hle/kernel/k_session.h"
9#include "core/hle/kernel/svc.h"
10
11namespace Kernel::Svc {
12namespace {
13
14template <typename T>
15Result CreateSession(Core::System& system, Handle* out_server, Handle* out_client, u64 name) {
16 auto& process = *system.CurrentProcess();
17 auto& handle_table = process.GetHandleTable();
18
19 // Declare the session we're going to allocate.
20 T* session;
21
22 // Reserve a new session from the process resource limit.
23 // FIXME: LimitableResource_SessionCountMax
24 KScopedResourceReservation session_reservation(&process, LimitableResource::SessionCountMax);
25 if (session_reservation.Succeeded()) {
26 session = T::Create(system.Kernel());
27 } else {
28 return ResultLimitReached;
29
30 // // We couldn't reserve a session. Check that we support dynamically expanding the
31 // // resource limit.
32 // R_UNLESS(process.GetResourceLimit() ==
33 // &system.Kernel().GetSystemResourceLimit(), ResultLimitReached);
34 // R_UNLESS(KTargetSystem::IsDynamicResourceLimitsEnabled(), ResultLimitReached());
35
36 // // Try to allocate a session from unused slab memory.
37 // session = T::CreateFromUnusedSlabMemory();
38 // R_UNLESS(session != nullptr, ResultLimitReached);
39 // ON_RESULT_FAILURE { session->Close(); };
40
41 // // If we're creating a KSession, we want to add two KSessionRequests to the heap, to
42 // // prevent request exhaustion.
43 // // NOTE: Nintendo checks if session->DynamicCast<KSession *>() != nullptr, but there's
44 // // no reason to not do this statically.
45 // if constexpr (std::same_as<T, KSession>) {
46 // for (size_t i = 0; i < 2; i++) {
47 // KSessionRequest* request = KSessionRequest::CreateFromUnusedSlabMemory();
48 // R_UNLESS(request != nullptr, ResultLimitReached);
49 // request->Close();
50 // }
51 // }
52
53 // We successfully allocated a session, so add the object we allocated to the resource
54 // limit.
55 // system.Kernel().GetSystemResourceLimit().Reserve(LimitableResource::SessionCountMax, 1);
56 }
57
58 // Check that we successfully created a session.
59 R_UNLESS(session != nullptr, ResultOutOfResource);
60
61 // Initialize the session.
62 session->Initialize(nullptr, fmt::format("{}", name));
63
64 // Commit the session reservation.
65 session_reservation.Commit();
66
67 // Ensure that we clean up the session (and its only references are handle table) on function
68 // end.
69 SCOPE_EXIT({
70 session->GetClientSession().Close();
71 session->GetServerSession().Close();
72 });
73
74 // Register the session.
75 T::Register(system.Kernel(), session);
76
77 // Add the server session to the handle table.
78 R_TRY(handle_table.Add(out_server, &session->GetServerSession()));
79
80 // Add the client session to the handle table.
81 const auto result = handle_table.Add(out_client, &session->GetClientSession());
82
83 if (!R_SUCCEEDED(result)) {
84 // Ensure that we maintaing a clean handle state on exit.
85 handle_table.Remove(*out_server);
86 }
87
88 return result;
89}
90
91} // namespace
92
93Result CreateSession(Core::System& system, Handle* out_server, Handle* out_client, u32 is_light,
94 u64 name) {
95 if (is_light) {
96 // return CreateSession<KLightSession>(system, out_server, out_client, name);
97 return ResultUnknown;
98 } else {
99 return CreateSession<KSession>(system, out_server, out_client, name);
100 }
101}
102
103} // namespace Kernel::Svc
diff --git a/src/core/hle/kernel/svc/svc_shared_memory.cpp b/src/core/hle/kernel/svc/svc_shared_memory.cpp
new file mode 100644
index 000000000..d465bcbe7
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_shared_memory.cpp
@@ -0,0 +1,106 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "common/scope_exit.h"
5#include "core/core.h"
6#include "core/hle/kernel/k_process.h"
7#include "core/hle/kernel/k_shared_memory.h"
8#include "core/hle/kernel/svc.h"
9
10namespace Kernel::Svc {
11namespace {
12
13constexpr bool IsValidSharedMemoryPermission(MemoryPermission perm) {
14 switch (perm) {
15 case MemoryPermission::Read:
16 case MemoryPermission::ReadWrite:
17 return true;
18 default:
19 return false;
20 }
21}
22
23[[maybe_unused]] constexpr bool IsValidRemoteSharedMemoryPermission(MemoryPermission perm) {
24 return IsValidSharedMemoryPermission(perm) || perm == MemoryPermission::DontCare;
25}
26
27} // namespace
28
29Result MapSharedMemory(Core::System& system, Handle shmem_handle, VAddr address, u64 size,
30 Svc::MemoryPermission map_perm) {
31 LOG_TRACE(Kernel_SVC,
32 "called, shared_memory_handle=0x{:X}, addr=0x{:X}, size=0x{:X}, permissions=0x{:08X}",
33 shmem_handle, address, size, map_perm);
34
35 // Validate the address/size.
36 R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress);
37 R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize);
38 R_UNLESS(size > 0, ResultInvalidSize);
39 R_UNLESS((address < address + size), ResultInvalidCurrentMemory);
40
41 // Validate the permission.
42 R_UNLESS(IsValidSharedMemoryPermission(map_perm), ResultInvalidNewMemoryPermission);
43
44 // Get the current process.
45 auto& process = *system.Kernel().CurrentProcess();
46 auto& page_table = process.PageTable();
47
48 // Get the shared memory.
49 KScopedAutoObject shmem = process.GetHandleTable().GetObject<KSharedMemory>(shmem_handle);
50 R_UNLESS(shmem.IsNotNull(), ResultInvalidHandle);
51
52 // Verify that the mapping is in range.
53 R_UNLESS(page_table.CanContain(address, size, KMemoryState::Shared), ResultInvalidMemoryRegion);
54
55 // Add the shared memory to the process.
56 R_TRY(process.AddSharedMemory(shmem.GetPointerUnsafe(), address, size));
57
58 // Ensure that we clean up the shared memory if we fail to map it.
59 auto guard =
60 SCOPE_GUARD({ process.RemoveSharedMemory(shmem.GetPointerUnsafe(), address, size); });
61
62 // Map the shared memory.
63 R_TRY(shmem->Map(process, address, size, map_perm));
64
65 // We succeeded.
66 guard.Cancel();
67 return ResultSuccess;
68}
69
70Result MapSharedMemory32(Core::System& system, Handle shmem_handle, u32 address, u32 size,
71 Svc::MemoryPermission map_perm) {
72 return MapSharedMemory(system, shmem_handle, address, size, map_perm);
73}
74
75Result UnmapSharedMemory(Core::System& system, Handle shmem_handle, VAddr address, u64 size) {
76 // Validate the address/size.
77 R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress);
78 R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize);
79 R_UNLESS(size > 0, ResultInvalidSize);
80 R_UNLESS((address < address + size), ResultInvalidCurrentMemory);
81
82 // Get the current process.
83 auto& process = *system.Kernel().CurrentProcess();
84 auto& page_table = process.PageTable();
85
86 // Get the shared memory.
87 KScopedAutoObject shmem = process.GetHandleTable().GetObject<KSharedMemory>(shmem_handle);
88 R_UNLESS(shmem.IsNotNull(), ResultInvalidHandle);
89
90 // Verify that the mapping is in range.
91 R_UNLESS(page_table.CanContain(address, size, KMemoryState::Shared), ResultInvalidMemoryRegion);
92
93 // Unmap the shared memory.
94 R_TRY(shmem->Unmap(process, address, size));
95
96 // Remove the shared memory from the process.
97 process.RemoveSharedMemory(shmem.GetPointerUnsafe(), address, size);
98
99 return ResultSuccess;
100}
101
102Result UnmapSharedMemory32(Core::System& system, Handle shmem_handle, u32 address, u32 size) {
103 return UnmapSharedMemory(system, shmem_handle, address, size);
104}
105
106} // namespace Kernel::Svc
diff --git a/src/core/hle/kernel/svc/svc_synchronization.cpp b/src/core/hle/kernel/svc/svc_synchronization.cpp
new file mode 100644
index 000000000..1bf6a612a
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_synchronization.cpp
@@ -0,0 +1,139 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "common/scope_exit.h"
5#include "core/core.h"
6#include "core/hle/kernel/k_process.h"
7#include "core/hle/kernel/k_readable_event.h"
8#include "core/hle/kernel/svc.h"
9
10namespace Kernel::Svc {
11
12/// Close a handle
13Result CloseHandle(Core::System& system, Handle handle) {
14 LOG_TRACE(Kernel_SVC, "Closing handle 0x{:08X}", handle);
15
16 // Remove the handle.
17 R_UNLESS(system.Kernel().CurrentProcess()->GetHandleTable().Remove(handle),
18 ResultInvalidHandle);
19
20 return ResultSuccess;
21}
22
23Result CloseHandle32(Core::System& system, Handle handle) {
24 return CloseHandle(system, handle);
25}
26
27/// Clears the signaled state of an event or process.
28Result ResetSignal(Core::System& system, Handle handle) {
29 LOG_DEBUG(Kernel_SVC, "called handle 0x{:08X}", handle);
30
31 // Get the current handle table.
32 const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
33
34 // Try to reset as readable event.
35 {
36 KScopedAutoObject readable_event = handle_table.GetObject<KReadableEvent>(handle);
37 if (readable_event.IsNotNull()) {
38 return readable_event->Reset();
39 }
40 }
41
42 // Try to reset as process.
43 {
44 KScopedAutoObject process = handle_table.GetObject<KProcess>(handle);
45 if (process.IsNotNull()) {
46 return process->Reset();
47 }
48 }
49
50 LOG_ERROR(Kernel_SVC, "invalid handle (0x{:08X})", handle);
51
52 return ResultInvalidHandle;
53}
54
55Result ResetSignal32(Core::System& system, Handle handle) {
56 return ResetSignal(system, handle);
57}
58
59/// Wait for the given handles to synchronize, timeout after the specified nanoseconds
60Result WaitSynchronization(Core::System& system, s32* index, VAddr handles_address, s32 num_handles,
61 s64 nano_seconds) {
62 LOG_TRACE(Kernel_SVC, "called handles_address=0x{:X}, num_handles={}, nano_seconds={}",
63 handles_address, num_handles, nano_seconds);
64
65 // Ensure number of handles is valid.
66 R_UNLESS(0 <= num_handles && num_handles <= ArgumentHandleCountMax, ResultOutOfRange);
67
68 auto& kernel = system.Kernel();
69 std::vector<KSynchronizationObject*> objs(num_handles);
70 const auto& handle_table = kernel.CurrentProcess()->GetHandleTable();
71 Handle* handles = system.Memory().GetPointer<Handle>(handles_address);
72
73 // Copy user handles.
74 if (num_handles > 0) {
75 // Convert the handles to objects.
76 R_UNLESS(handle_table.GetMultipleObjects<KSynchronizationObject>(objs.data(), handles,
77 num_handles),
78 ResultInvalidHandle);
79 for (const auto& obj : objs) {
80 kernel.RegisterInUseObject(obj);
81 }
82 }
83
84 // Ensure handles are closed when we're done.
85 SCOPE_EXIT({
86 for (s32 i = 0; i < num_handles; ++i) {
87 kernel.UnregisterInUseObject(objs[i]);
88 objs[i]->Close();
89 }
90 });
91
92 return KSynchronizationObject::Wait(kernel, index, objs.data(), static_cast<s32>(objs.size()),
93 nano_seconds);
94}
95
96Result WaitSynchronization32(Core::System& system, u32 timeout_low, u32 handles_address,
97 s32 num_handles, u32 timeout_high, s32* index) {
98 const s64 nano_seconds{(static_cast<s64>(timeout_high) << 32) | static_cast<s64>(timeout_low)};
99 return WaitSynchronization(system, index, handles_address, num_handles, nano_seconds);
100}
101
102/// Resumes a thread waiting on WaitSynchronization
103Result CancelSynchronization(Core::System& system, Handle handle) {
104 LOG_TRACE(Kernel_SVC, "called handle=0x{:X}", handle);
105
106 // Get the thread from its handle.
107 KScopedAutoObject thread =
108 system.Kernel().CurrentProcess()->GetHandleTable().GetObject<KThread>(handle);
109 R_UNLESS(thread.IsNotNull(), ResultInvalidHandle);
110
111 // Cancel the thread's wait.
112 thread->WaitCancel();
113 return ResultSuccess;
114}
115
116Result CancelSynchronization32(Core::System& system, Handle handle) {
117 return CancelSynchronization(system, handle);
118}
119
120void SynchronizePreemptionState(Core::System& system) {
121 auto& kernel = system.Kernel();
122
123 // Lock the scheduler.
124 KScopedSchedulerLock sl{kernel};
125
126 // If the current thread is pinned, unpin it.
127 KProcess* cur_process = system.Kernel().CurrentProcess();
128 const auto core_id = GetCurrentCoreId(kernel);
129
130 if (cur_process->GetPinnedThread(core_id) == GetCurrentThreadPointer(kernel)) {
131 // Clear the current thread's interrupt flag.
132 GetCurrentThread(kernel).ClearInterruptFlag();
133
134 // Unpin the current thread.
135 cur_process->UnpinCurrentThread(core_id);
136 }
137}
138
139} // namespace Kernel::Svc
diff --git a/src/core/hle/kernel/svc/svc_thread.cpp b/src/core/hle/kernel/svc/svc_thread.cpp
new file mode 100644
index 000000000..dd9f8e8b1
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_thread.cpp
@@ -0,0 +1,396 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "common/scope_exit.h"
5#include "core/core.h"
6#include "core/core_timing.h"
7#include "core/hle/kernel/k_process.h"
8#include "core/hle/kernel/k_scoped_resource_reservation.h"
9#include "core/hle/kernel/k_thread.h"
10#include "core/hle/kernel/svc.h"
11
12namespace Kernel::Svc {
13namespace {
14
15constexpr bool IsValidVirtualCoreId(int32_t core_id) {
16 return (0 <= core_id && core_id < static_cast<int32_t>(Core::Hardware::NUM_CPU_CORES));
17}
18
19} // Anonymous namespace
20
21/// Creates a new thread
22Result CreateThread(Core::System& system, Handle* out_handle, VAddr entry_point, u64 arg,
23 VAddr stack_bottom, u32 priority, s32 core_id) {
24 LOG_DEBUG(Kernel_SVC,
25 "called entry_point=0x{:08X}, arg=0x{:08X}, stack_bottom=0x{:08X}, "
26 "priority=0x{:08X}, core_id=0x{:08X}",
27 entry_point, arg, stack_bottom, priority, core_id);
28
29 // Adjust core id, if it's the default magic.
30 auto& kernel = system.Kernel();
31 auto& process = *kernel.CurrentProcess();
32 if (core_id == IdealCoreUseProcessValue) {
33 core_id = process.GetIdealCoreId();
34 }
35
36 // Validate arguments.
37 if (!IsValidVirtualCoreId(core_id)) {
38 LOG_ERROR(Kernel_SVC, "Invalid Core ID specified (id={})", core_id);
39 return ResultInvalidCoreId;
40 }
41 if (((1ULL << core_id) & process.GetCoreMask()) == 0) {
42 LOG_ERROR(Kernel_SVC, "Core ID doesn't fall within allowable cores (id={})", core_id);
43 return ResultInvalidCoreId;
44 }
45
46 if (HighestThreadPriority > priority || priority > LowestThreadPriority) {
47 LOG_ERROR(Kernel_SVC, "Invalid priority specified (priority={})", priority);
48 return ResultInvalidPriority;
49 }
50 if (!process.CheckThreadPriority(priority)) {
51 LOG_ERROR(Kernel_SVC, "Invalid allowable thread priority (priority={})", priority);
52 return ResultInvalidPriority;
53 }
54
55 // Reserve a new thread from the process resource limit (waiting up to 100ms).
56 KScopedResourceReservation thread_reservation(
57 kernel.CurrentProcess(), LimitableResource::ThreadCountMax, 1,
58 system.CoreTiming().GetGlobalTimeNs().count() + 100000000);
59 if (!thread_reservation.Succeeded()) {
60 LOG_ERROR(Kernel_SVC, "Could not reserve a new thread");
61 return ResultLimitReached;
62 }
63
64 // Create the thread.
65 KThread* thread = KThread::Create(kernel);
66 if (!thread) {
67 LOG_ERROR(Kernel_SVC, "Unable to create new threads. Thread creation limit reached.");
68 return ResultOutOfResource;
69 }
70 SCOPE_EXIT({ thread->Close(); });
71
72 // Initialize the thread.
73 {
74 KScopedLightLock lk{process.GetStateLock()};
75 R_TRY(KThread::InitializeUserThread(system, thread, entry_point, arg, stack_bottom,
76 priority, core_id, &process));
77 }
78
79 // Set the thread name for debugging purposes.
80 thread->SetName(fmt::format("thread[entry_point={:X}, handle={:X}]", entry_point, *out_handle));
81
82 // Commit the thread reservation.
83 thread_reservation.Commit();
84
85 // Register the new thread.
86 KThread::Register(kernel, thread);
87
88 // Add the thread to the handle table.
89 R_TRY(process.GetHandleTable().Add(out_handle, thread));
90
91 return ResultSuccess;
92}
93
94Result CreateThread32(Core::System& system, Handle* out_handle, u32 priority, u32 entry_point,
95 u32 arg, u32 stack_top, s32 processor_id) {
96 return CreateThread(system, out_handle, entry_point, arg, stack_top, priority, processor_id);
97}
98
99/// Starts the thread for the provided handle
100Result StartThread(Core::System& system, Handle thread_handle) {
101 LOG_DEBUG(Kernel_SVC, "called thread=0x{:08X}", thread_handle);
102
103 // Get the thread from its handle.
104 KScopedAutoObject thread =
105 system.Kernel().CurrentProcess()->GetHandleTable().GetObject<KThread>(thread_handle);
106 R_UNLESS(thread.IsNotNull(), ResultInvalidHandle);
107
108 // Try to start the thread.
109 R_TRY(thread->Run());
110
111 // If we succeeded, persist a reference to the thread.
112 thread->Open();
113 system.Kernel().RegisterInUseObject(thread.GetPointerUnsafe());
114
115 return ResultSuccess;
116}
117
118Result StartThread32(Core::System& system, Handle thread_handle) {
119 return StartThread(system, thread_handle);
120}
121
122/// Called when a thread exits
123void ExitThread(Core::System& system) {
124 LOG_DEBUG(Kernel_SVC, "called, pc=0x{:08X}", system.CurrentArmInterface().GetPC());
125
126 auto* const current_thread = GetCurrentThreadPointer(system.Kernel());
127 system.GlobalSchedulerContext().RemoveThread(current_thread);
128 current_thread->Exit();
129 system.Kernel().UnregisterInUseObject(current_thread);
130}
131
132void ExitThread32(Core::System& system) {
133 ExitThread(system);
134}
135
136/// Sleep the current thread
137void SleepThread(Core::System& system, s64 nanoseconds) {
138 auto& kernel = system.Kernel();
139 const auto yield_type = static_cast<Svc::YieldType>(nanoseconds);
140
141 LOG_TRACE(Kernel_SVC, "called nanoseconds={}", nanoseconds);
142
143 // When the input tick is positive, sleep.
144 if (nanoseconds > 0) {
145 // Convert the timeout from nanoseconds to ticks.
146 // NOTE: Nintendo does not use this conversion logic in WaitSynchronization...
147
148 // Sleep.
149 // NOTE: Nintendo does not check the result of this sleep.
150 static_cast<void>(GetCurrentThread(kernel).Sleep(nanoseconds));
151 } else if (yield_type == Svc::YieldType::WithoutCoreMigration) {
152 KScheduler::YieldWithoutCoreMigration(kernel);
153 } else if (yield_type == Svc::YieldType::WithCoreMigration) {
154 KScheduler::YieldWithCoreMigration(kernel);
155 } else if (yield_type == Svc::YieldType::ToAnyThread) {
156 KScheduler::YieldToAnyThread(kernel);
157 } else {
158 // Nintendo does nothing at all if an otherwise invalid value is passed.
159 ASSERT_MSG(false, "Unimplemented sleep yield type '{:016X}'!", nanoseconds);
160 }
161}
162
163void SleepThread32(Core::System& system, u32 nanoseconds_low, u32 nanoseconds_high) {
164 const auto nanoseconds = static_cast<s64>(u64{nanoseconds_low} | (u64{nanoseconds_high} << 32));
165 SleepThread(system, nanoseconds);
166}
167
168/// Gets the thread context
169Result GetThreadContext(Core::System& system, VAddr out_context, Handle thread_handle) {
170 LOG_DEBUG(Kernel_SVC, "called, out_context=0x{:08X}, thread_handle=0x{:X}", out_context,
171 thread_handle);
172
173 auto& kernel = system.Kernel();
174
175 // Get the thread from its handle.
176 KScopedAutoObject thread =
177 kernel.CurrentProcess()->GetHandleTable().GetObject<KThread>(thread_handle);
178 R_UNLESS(thread.IsNotNull(), ResultInvalidHandle);
179
180 // Require the handle be to a non-current thread in the current process.
181 const auto* current_process = kernel.CurrentProcess();
182 R_UNLESS(current_process == thread->GetOwnerProcess(), ResultInvalidId);
183
184 // Verify that the thread isn't terminated.
185 R_UNLESS(thread->GetState() != ThreadState::Terminated, ResultTerminationRequested);
186
187 /// Check that the thread is not the current one.
188 /// NOTE: Nintendo does not check this, and thus the following loop will deadlock.
189 R_UNLESS(thread.GetPointerUnsafe() != GetCurrentThreadPointer(kernel), ResultInvalidId);
190
191 // Try to get the thread context until the thread isn't current on any core.
192 while (true) {
193 KScopedSchedulerLock sl{kernel};
194
195 // TODO(bunnei): Enforce that thread is suspended for debug here.
196
197 // If the thread's raw state isn't runnable, check if it's current on some core.
198 if (thread->GetRawState() != ThreadState::Runnable) {
199 bool current = false;
200 for (auto i = 0; i < static_cast<s32>(Core::Hardware::NUM_CPU_CORES); ++i) {
201 if (thread.GetPointerUnsafe() == kernel.Scheduler(i).GetSchedulerCurrentThread()) {
202 current = true;
203 break;
204 }
205 }
206
207 // If the thread is current, retry until it isn't.
208 if (current) {
209 continue;
210 }
211 }
212
213 // Get the thread context.
214 std::vector<u8> context;
215 R_TRY(thread->GetThreadContext3(context));
216
217 // Copy the thread context to user space.
218 system.Memory().WriteBlock(out_context, context.data(), context.size());
219
220 return ResultSuccess;
221 }
222
223 return ResultSuccess;
224}
225
226Result GetThreadContext32(Core::System& system, u32 out_context, Handle thread_handle) {
227 return GetThreadContext(system, out_context, thread_handle);
228}
229
230/// Gets the priority for the specified thread
231Result GetThreadPriority(Core::System& system, u32* out_priority, Handle handle) {
232 LOG_TRACE(Kernel_SVC, "called");
233
234 // Get the thread from its handle.
235 KScopedAutoObject thread =
236 system.Kernel().CurrentProcess()->GetHandleTable().GetObject<KThread>(handle);
237 R_UNLESS(thread.IsNotNull(), ResultInvalidHandle);
238
239 // Get the thread's priority.
240 *out_priority = thread->GetPriority();
241 return ResultSuccess;
242}
243
244Result GetThreadPriority32(Core::System& system, u32* out_priority, Handle handle) {
245 return GetThreadPriority(system, out_priority, handle);
246}
247
248/// Sets the priority for the specified thread
249Result SetThreadPriority(Core::System& system, Handle thread_handle, u32 priority) {
250 // Get the current process.
251 KProcess& process = *system.Kernel().CurrentProcess();
252
253 // Validate the priority.
254 R_UNLESS(HighestThreadPriority <= priority && priority <= LowestThreadPriority,
255 ResultInvalidPriority);
256 R_UNLESS(process.CheckThreadPriority(priority), ResultInvalidPriority);
257
258 // Get the thread from its handle.
259 KScopedAutoObject thread = process.GetHandleTable().GetObject<KThread>(thread_handle);
260 R_UNLESS(thread.IsNotNull(), ResultInvalidHandle);
261
262 // Set the thread priority.
263 thread->SetBasePriority(priority);
264 return ResultSuccess;
265}
266
267Result SetThreadPriority32(Core::System& system, Handle thread_handle, u32 priority) {
268 return SetThreadPriority(system, thread_handle, priority);
269}
270
271Result GetThreadList(Core::System& system, u32* out_num_threads, VAddr out_thread_ids,
272 u32 out_thread_ids_size, Handle debug_handle) {
273 // TODO: Handle this case when debug events are supported.
274 UNIMPLEMENTED_IF(debug_handle != InvalidHandle);
275
276 LOG_DEBUG(Kernel_SVC, "called. out_thread_ids=0x{:016X}, out_thread_ids_size={}",
277 out_thread_ids, out_thread_ids_size);
278
279 // If the size is negative or larger than INT32_MAX / sizeof(u64)
280 if ((out_thread_ids_size & 0xF0000000) != 0) {
281 LOG_ERROR(Kernel_SVC, "Supplied size outside [0, 0x0FFFFFFF] range. size={}",
282 out_thread_ids_size);
283 return ResultOutOfRange;
284 }
285
286 auto* const current_process = system.Kernel().CurrentProcess();
287 const auto total_copy_size = out_thread_ids_size * sizeof(u64);
288
289 if (out_thread_ids_size > 0 &&
290 !current_process->PageTable().IsInsideAddressSpace(out_thread_ids, total_copy_size)) {
291 LOG_ERROR(Kernel_SVC, "Address range outside address space. begin=0x{:016X}, end=0x{:016X}",
292 out_thread_ids, out_thread_ids + total_copy_size);
293 return ResultInvalidCurrentMemory;
294 }
295
296 auto& memory = system.Memory();
297 const auto& thread_list = current_process->GetThreadList();
298 const auto num_threads = thread_list.size();
299 const auto copy_amount = std::min(std::size_t{out_thread_ids_size}, num_threads);
300
301 auto list_iter = thread_list.cbegin();
302 for (std::size_t i = 0; i < copy_amount; ++i, ++list_iter) {
303 memory.Write64(out_thread_ids, (*list_iter)->GetThreadID());
304 out_thread_ids += sizeof(u64);
305 }
306
307 *out_num_threads = static_cast<u32>(num_threads);
308 return ResultSuccess;
309}
310
311Result GetThreadCoreMask(Core::System& system, Handle thread_handle, s32* out_core_id,
312 u64* out_affinity_mask) {
313 LOG_TRACE(Kernel_SVC, "called, handle=0x{:08X}", thread_handle);
314
315 // Get the thread from its handle.
316 KScopedAutoObject thread =
317 system.Kernel().CurrentProcess()->GetHandleTable().GetObject<KThread>(thread_handle);
318 R_UNLESS(thread.IsNotNull(), ResultInvalidHandle);
319
320 // Get the core mask.
321 R_TRY(thread->GetCoreMask(out_core_id, out_affinity_mask));
322
323 return ResultSuccess;
324}
325
326Result GetThreadCoreMask32(Core::System& system, Handle thread_handle, s32* out_core_id,
327 u32* out_affinity_mask_low, u32* out_affinity_mask_high) {
328 u64 out_affinity_mask{};
329 const auto result = GetThreadCoreMask(system, thread_handle, out_core_id, &out_affinity_mask);
330 *out_affinity_mask_high = static_cast<u32>(out_affinity_mask >> 32);
331 *out_affinity_mask_low = static_cast<u32>(out_affinity_mask);
332 return result;
333}
334
335Result SetThreadCoreMask(Core::System& system, Handle thread_handle, s32 core_id,
336 u64 affinity_mask) {
337 // Determine the core id/affinity mask.
338 if (core_id == IdealCoreUseProcessValue) {
339 core_id = system.Kernel().CurrentProcess()->GetIdealCoreId();
340 affinity_mask = (1ULL << core_id);
341 } else {
342 // Validate the affinity mask.
343 const u64 process_core_mask = system.Kernel().CurrentProcess()->GetCoreMask();
344 R_UNLESS((affinity_mask | process_core_mask) == process_core_mask, ResultInvalidCoreId);
345 R_UNLESS(affinity_mask != 0, ResultInvalidCombination);
346
347 // Validate the core id.
348 if (IsValidVirtualCoreId(core_id)) {
349 R_UNLESS(((1ULL << core_id) & affinity_mask) != 0, ResultInvalidCombination);
350 } else {
351 R_UNLESS(core_id == IdealCoreNoUpdate || core_id == IdealCoreDontCare,
352 ResultInvalidCoreId);
353 }
354 }
355
356 // Get the thread from its handle.
357 KScopedAutoObject thread =
358 system.Kernel().CurrentProcess()->GetHandleTable().GetObject<KThread>(thread_handle);
359 R_UNLESS(thread.IsNotNull(), ResultInvalidHandle);
360
361 // Set the core mask.
362 R_TRY(thread->SetCoreMask(core_id, affinity_mask));
363
364 return ResultSuccess;
365}
366
367Result SetThreadCoreMask32(Core::System& system, Handle thread_handle, s32 core_id,
368 u32 affinity_mask_low, u32 affinity_mask_high) {
369 const auto affinity_mask = u64{affinity_mask_low} | (u64{affinity_mask_high} << 32);
370 return SetThreadCoreMask(system, thread_handle, core_id, affinity_mask);
371}
372
373/// Get the ID for the specified thread.
374Result GetThreadId(Core::System& system, u64* out_thread_id, Handle thread_handle) {
375 // Get the thread from its handle.
376 KScopedAutoObject thread =
377 system.Kernel().CurrentProcess()->GetHandleTable().GetObject<KThread>(thread_handle);
378 R_UNLESS(thread.IsNotNull(), ResultInvalidHandle);
379
380 // Get the thread's id.
381 *out_thread_id = thread->GetId();
382 return ResultSuccess;
383}
384
385Result GetThreadId32(Core::System& system, u32* out_thread_id_low, u32* out_thread_id_high,
386 Handle thread_handle) {
387 u64 out_thread_id{};
388 const Result result{GetThreadId(system, &out_thread_id, thread_handle)};
389
390 *out_thread_id_low = static_cast<u32>(out_thread_id >> 32);
391 *out_thread_id_high = static_cast<u32>(out_thread_id & std::numeric_limits<u32>::max());
392
393 return result;
394}
395
396} // namespace Kernel::Svc
diff --git a/src/core/hle/kernel/svc/svc_thread_profiler.cpp b/src/core/hle/kernel/svc/svc_thread_profiler.cpp
new file mode 100644
index 000000000..299e22ae6
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_thread_profiler.cpp
@@ -0,0 +1,6 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/hle/kernel/svc.h"
5
6namespace Kernel::Svc {} // namespace Kernel::Svc
diff --git a/src/core/hle/kernel/svc/svc_tick.cpp b/src/core/hle/kernel/svc/svc_tick.cpp
new file mode 100644
index 000000000..e9b4fd5a6
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_tick.cpp
@@ -0,0 +1,33 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/core.h"
5#include "core/core_timing.h"
6#include "core/hle/kernel/kernel.h"
7#include "core/hle/kernel/svc.h"
8
9namespace Kernel::Svc {
10
11/// This returns the total CPU ticks elapsed since the CPU was powered-on
12u64 GetSystemTick(Core::System& system) {
13 LOG_TRACE(Kernel_SVC, "called");
14
15 auto& core_timing = system.CoreTiming();
16
17 // Returns the value of cntpct_el0 (https://switchbrew.org/wiki/SVC#svcGetSystemTick)
18 const u64 result{core_timing.GetClockTicks()};
19
20 if (!system.Kernel().IsMulticore()) {
21 core_timing.AddTicks(400U);
22 }
23
24 return result;
25}
26
27void GetSystemTick32(Core::System& system, u32* time_low, u32* time_high) {
28 const auto time = GetSystemTick(system);
29 *time_low = static_cast<u32>(time);
30 *time_high = static_cast<u32>(time >> 32);
31}
32
33} // namespace Kernel::Svc
diff --git a/src/core/hle/kernel/svc/svc_transfer_memory.cpp b/src/core/hle/kernel/svc/svc_transfer_memory.cpp
new file mode 100644
index 000000000..b14ae24a1
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_transfer_memory.cpp
@@ -0,0 +1,79 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "common/scope_exit.h"
5#include "core/core.h"
6#include "core/hle/kernel/k_process.h"
7#include "core/hle/kernel/k_scoped_resource_reservation.h"
8#include "core/hle/kernel/k_transfer_memory.h"
9#include "core/hle/kernel/svc.h"
10
11namespace Kernel::Svc {
12namespace {
13
14constexpr bool IsValidTransferMemoryPermission(MemoryPermission perm) {
15 switch (perm) {
16 case MemoryPermission::None:
17 case MemoryPermission::Read:
18 case MemoryPermission::ReadWrite:
19 return true;
20 default:
21 return false;
22 }
23}
24
25} // Anonymous namespace
26
27/// Creates a TransferMemory object
28Result CreateTransferMemory(Core::System& system, Handle* out, VAddr address, u64 size,
29 MemoryPermission map_perm) {
30 auto& kernel = system.Kernel();
31
32 // Validate the size.
33 R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress);
34 R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize);
35 R_UNLESS(size > 0, ResultInvalidSize);
36 R_UNLESS((address < address + size), ResultInvalidCurrentMemory);
37
38 // Validate the permissions.
39 R_UNLESS(IsValidTransferMemoryPermission(map_perm), ResultInvalidNewMemoryPermission);
40
41 // Get the current process and handle table.
42 auto& process = *kernel.CurrentProcess();
43 auto& handle_table = process.GetHandleTable();
44
45 // Reserve a new transfer memory from the process resource limit.
46 KScopedResourceReservation trmem_reservation(kernel.CurrentProcess(),
47 LimitableResource::TransferMemoryCountMax);
48 R_UNLESS(trmem_reservation.Succeeded(), ResultLimitReached);
49
50 // Create the transfer memory.
51 KTransferMemory* trmem = KTransferMemory::Create(kernel);
52 R_UNLESS(trmem != nullptr, ResultOutOfResource);
53
54 // Ensure the only reference is in the handle table when we're done.
55 SCOPE_EXIT({ trmem->Close(); });
56
57 // Ensure that the region is in range.
58 R_UNLESS(process.PageTable().Contains(address, size), ResultInvalidCurrentMemory);
59
60 // Initialize the transfer memory.
61 R_TRY(trmem->Initialize(address, size, map_perm));
62
63 // Commit the reservation.
64 trmem_reservation.Commit();
65
66 // Register the transfer memory.
67 KTransferMemory::Register(kernel, trmem);
68
69 // Add the transfer memory to the handle table.
70 R_TRY(handle_table.Add(out, trmem));
71
72 return ResultSuccess;
73}
74
75Result CreateTransferMemory32(Core::System& system, Handle* out, u32 address, u32 size,
76 MemoryPermission map_perm) {
77 return CreateTransferMemory(system, out, address, size, map_perm);
78}
79} // namespace Kernel::Svc
diff --git a/src/core/hle/kernel/svc_wrap.h b/src/core/hle/kernel/svc_wrap.h
index 1ea8c7fbc..052be40dd 100644
--- a/src/core/hle/kernel/svc_wrap.h
+++ b/src/core/hle/kernel/svc_wrap.h
@@ -172,11 +172,11 @@ void SvcWrap64(Core::System& system) {
172} 172}
173 173
174// Used by GetResourceLimitLimitValue. 174// Used by GetResourceLimitLimitValue.
175template <Result func(Core::System&, u64*, Handle, LimitableResource)> 175template <Result func(Core::System&, u64*, Handle, Svc::LimitableResource)>
176void SvcWrap64(Core::System& system) { 176void SvcWrap64(Core::System& system) {
177 u64 param_1 = 0; 177 u64 param_1 = 0;
178 const u32 retval = func(system, &param_1, static_cast<Handle>(Param(system, 1)), 178 const u32 retval = func(system, &param_1, static_cast<Handle>(Param(system, 1)),
179 static_cast<LimitableResource>(Param(system, 2))) 179 static_cast<Svc::LimitableResource>(Param(system, 2)))
180 .raw; 180 .raw;
181 181
182 system.CurrentArmInterface().SetReg(1, param_1); 182 system.CurrentArmInterface().SetReg(1, param_1);
@@ -189,10 +189,10 @@ void SvcWrap64(Core::System& system) {
189} 189}
190 190
191// Used by SetResourceLimitLimitValue 191// Used by SetResourceLimitLimitValue
192template <Result func(Core::System&, Handle, LimitableResource, u64)> 192template <Result func(Core::System&, Handle, Svc::LimitableResource, u64)>
193void SvcWrap64(Core::System& system) { 193void SvcWrap64(Core::System& system) {
194 FuncReturn(system, func(system, static_cast<Handle>(Param(system, 0)), 194 FuncReturn(system, func(system, static_cast<Handle>(Param(system, 0)),
195 static_cast<LimitableResource>(Param(system, 1)), Param(system, 2)) 195 static_cast<Svc::LimitableResource>(Param(system, 1)), Param(system, 2))
196 .raw); 196 .raw);
197} 197}
198 198