1 // Copyright 2018 yuzu emulator team
2 // Licensed under GPLv2 or any later version
3 // Refer to the license.txt file included.
4 
5 #include <algorithm>
6 #include <cinttypes>
7 #include <iterator>
8 #include <mutex>
9 #include <vector>
10 
11 #include "common/alignment.h"
12 #include "common/assert.h"
13 #include "common/fiber.h"
14 #include "common/logging/log.h"
15 #include "common/microprofile.h"
16 #include "common/string_util.h"
17 #include "core/arm/exclusive_monitor.h"
18 #include "core/core.h"
19 #include "core/core_timing.h"
20 #include "core/core_timing_util.h"
21 #include "core/cpu_manager.h"
22 #include "core/hle/kernel/address_arbiter.h"
23 #include "core/hle/kernel/client_port.h"
24 #include "core/hle/kernel/client_session.h"
25 #include "core/hle/kernel/errors.h"
26 #include "core/hle/kernel/handle_table.h"
27 #include "core/hle/kernel/kernel.h"
28 #include "core/hle/kernel/memory/memory_block.h"
29 #include "core/hle/kernel/memory/page_table.h"
30 #include "core/hle/kernel/mutex.h"
31 #include "core/hle/kernel/physical_core.h"
32 #include "core/hle/kernel/process.h"
33 #include "core/hle/kernel/readable_event.h"
34 #include "core/hle/kernel/resource_limit.h"
35 #include "core/hle/kernel/scheduler.h"
36 #include "core/hle/kernel/shared_memory.h"
37 #include "core/hle/kernel/svc.h"
38 #include "core/hle/kernel/svc_types.h"
39 #include "core/hle/kernel/svc_wrap.h"
40 #include "core/hle/kernel/synchronization.h"
41 #include "core/hle/kernel/thread.h"
42 #include "core/hle/kernel/time_manager.h"
43 #include "core/hle/kernel/transfer_memory.h"
44 #include "core/hle/kernel/writable_event.h"
45 #include "core/hle/lock.h"
46 #include "core/hle/result.h"
47 #include "core/hle/service/service.h"
48 #include "core/memory.h"
49 #include "core/reporter.h"
50 
51 namespace Kernel::Svc {
52 namespace {
53 
54 // Checks if address + size is greater than the given address
55 // This can return false if the size causes an overflow of a 64-bit type
56 // or if the given size is zero.
IsValidAddressRange(VAddr address,u64 size)57 constexpr bool IsValidAddressRange(VAddr address, u64 size) {
58     return address + size > address;
59 }
60 
61 // Helper function that performs the common sanity checks for svcMapMemory
62 // and svcUnmapMemory. This is doable, as both functions perform their sanitizing
63 // in the same order.
MapUnmapMemorySanityChecks(const Memory::PageTable & manager,VAddr dst_addr,VAddr src_addr,u64 size)64 ResultCode MapUnmapMemorySanityChecks(const Memory::PageTable& manager, VAddr dst_addr,
65                                       VAddr src_addr, u64 size) {
66     if (!Common::Is4KBAligned(dst_addr)) {
67         LOG_ERROR(Kernel_SVC, "Destination address is not aligned to 4KB, 0x{:016X}", dst_addr);
68         return ERR_INVALID_ADDRESS;
69     }
70 
71     if (!Common::Is4KBAligned(src_addr)) {
72         LOG_ERROR(Kernel_SVC, "Source address is not aligned to 4KB, 0x{:016X}", src_addr);
73         return ERR_INVALID_SIZE;
74     }
75 
76     if (size == 0) {
77         LOG_ERROR(Kernel_SVC, "Size is 0");
78         return ERR_INVALID_SIZE;
79     }
80 
81     if (!Common::Is4KBAligned(size)) {
82         LOG_ERROR(Kernel_SVC, "Size is not aligned to 4KB, 0x{:016X}", size);
83         return ERR_INVALID_SIZE;
84     }
85 
86     if (!IsValidAddressRange(dst_addr, size)) {
87         LOG_ERROR(Kernel_SVC,
88                   "Destination is not a valid address range, addr=0x{:016X}, size=0x{:016X}",
89                   dst_addr, size);
90         return ERR_INVALID_ADDRESS_STATE;
91     }
92 
93     if (!IsValidAddressRange(src_addr, size)) {
94         LOG_ERROR(Kernel_SVC, "Source is not a valid address range, addr=0x{:016X}, size=0x{:016X}",
95                   src_addr, size);
96         return ERR_INVALID_ADDRESS_STATE;
97     }
98 
99     if (!manager.IsInsideAddressSpace(src_addr, size)) {
100         LOG_ERROR(Kernel_SVC,
101                   "Source is not within the address space, addr=0x{:016X}, size=0x{:016X}",
102                   src_addr, size);
103         return ERR_INVALID_ADDRESS_STATE;
104     }
105 
106     if (manager.IsOutsideStackRegion(dst_addr, size)) {
107         LOG_ERROR(Kernel_SVC,
108                   "Destination is not within the stack region, addr=0x{:016X}, size=0x{:016X}",
109                   dst_addr, size);
110         return ERR_INVALID_MEMORY_RANGE;
111     }
112 
113     if (manager.IsInsideHeapRegion(dst_addr, size)) {
114         LOG_ERROR(Kernel_SVC,
115                   "Destination does not fit within the heap region, addr=0x{:016X}, "
116                   "size=0x{:016X}",
117                   dst_addr, size);
118         return ERR_INVALID_MEMORY_RANGE;
119     }
120 
121     if (manager.IsInsideAliasRegion(dst_addr, size)) {
122         LOG_ERROR(Kernel_SVC,
123                   "Destination does not fit within the map region, addr=0x{:016X}, "
124                   "size=0x{:016X}",
125                   dst_addr, size);
126         return ERR_INVALID_MEMORY_RANGE;
127     }
128 
129     return RESULT_SUCCESS;
130 }
131 
132 enum class ResourceLimitValueType {
133     CurrentValue,
134     LimitValue,
135 };
136 
RetrieveResourceLimitValue(Core::System & system,Handle resource_limit,u32 resource_type,ResourceLimitValueType value_type)137 ResultVal<s64> RetrieveResourceLimitValue(Core::System& system, Handle resource_limit,
138                                           u32 resource_type, ResourceLimitValueType value_type) {
139     std::lock_guard lock{HLE::g_hle_lock};
140     const auto type = static_cast<ResourceType>(resource_type);
141     if (!IsValidResourceType(type)) {
142         LOG_ERROR(Kernel_SVC, "Invalid resource limit type: '{}'", resource_type);
143         return ERR_INVALID_ENUM_VALUE;
144     }
145 
146     const auto* const current_process = system.Kernel().CurrentProcess();
147     ASSERT(current_process != nullptr);
148 
149     const auto resource_limit_object =
150         current_process->GetHandleTable().Get<ResourceLimit>(resource_limit);
151     if (!resource_limit_object) {
152         LOG_ERROR(Kernel_SVC, "Handle to non-existent resource limit instance used. Handle={:08X}",
153                   resource_limit);
154         return ERR_INVALID_HANDLE;
155     }
156 
157     if (value_type == ResourceLimitValueType::CurrentValue) {
158         return MakeResult(resource_limit_object->GetCurrentResourceValue(type));
159     }
160 
161     return MakeResult(resource_limit_object->GetMaxResourceValue(type));
162 }
163 } // Anonymous namespace
164 
165 /// Set the process heap to a given Size. It can both extend and shrink the heap.
SetHeapSize(Core::System & system,VAddr * heap_addr,u64 heap_size)166 static ResultCode SetHeapSize(Core::System& system, VAddr* heap_addr, u64 heap_size) {
167     std::lock_guard lock{HLE::g_hle_lock};
168     LOG_TRACE(Kernel_SVC, "called, heap_size=0x{:X}", heap_size);
169 
170     // Size must be a multiple of 0x200000 (2MB) and be equal to or less than 8GB.
171     if ((heap_size % 0x200000) != 0) {
172         LOG_ERROR(Kernel_SVC, "The heap size is not a multiple of 2MB, heap_size=0x{:016X}",
173                   heap_size);
174         return ERR_INVALID_SIZE;
175     }
176 
177     if (heap_size >= 0x200000000) {
178         LOG_ERROR(Kernel_SVC, "The heap size is not less than 8GB, heap_size=0x{:016X}", heap_size);
179         return ERR_INVALID_SIZE;
180     }
181 
182     auto& page_table{system.Kernel().CurrentProcess()->PageTable()};
183 
184     CASCADE_RESULT(*heap_addr, page_table.SetHeapSize(heap_size));
185 
186     return RESULT_SUCCESS;
187 }
188 
SetHeapSize32(Core::System & system,u32 * heap_addr,u32 heap_size)189 static ResultCode SetHeapSize32(Core::System& system, u32* heap_addr, u32 heap_size) {
190     VAddr temp_heap_addr{};
191     const ResultCode result{SetHeapSize(system, &temp_heap_addr, heap_size)};
192     *heap_addr = static_cast<u32>(temp_heap_addr);
193     return result;
194 }
195 
SetMemoryAttribute(Core::System & system,VAddr address,u64 size,u32 mask,u32 attribute)196 static ResultCode SetMemoryAttribute(Core::System& system, VAddr address, u64 size, u32 mask,
197                                      u32 attribute) {
198     std::lock_guard lock{HLE::g_hle_lock};
199     LOG_DEBUG(Kernel_SVC,
200               "called, address=0x{:016X}, size=0x{:X}, mask=0x{:08X}, attribute=0x{:08X}", address,
201               size, mask, attribute);
202 
203     if (!Common::Is4KBAligned(address)) {
204         LOG_ERROR(Kernel_SVC, "Address not page aligned (0x{:016X})", address);
205         return ERR_INVALID_ADDRESS;
206     }
207 
208     if (size == 0 || !Common::Is4KBAligned(size)) {
209         LOG_ERROR(Kernel_SVC, "Invalid size (0x{:X}). Size must be non-zero and page aligned.",
210                   size);
211         return ERR_INVALID_ADDRESS;
212     }
213 
214     if (!IsValidAddressRange(address, size)) {
215         LOG_ERROR(Kernel_SVC, "Address range overflowed (Address: 0x{:016X}, Size: 0x{:016X})",
216                   address, size);
217         return ERR_INVALID_ADDRESS_STATE;
218     }
219 
220     const auto attributes{static_cast<Memory::MemoryAttribute>(mask | attribute)};
221     if (attributes != static_cast<Memory::MemoryAttribute>(mask) ||
222         (attributes | Memory::MemoryAttribute::Uncached) != Memory::MemoryAttribute::Uncached) {
223         LOG_ERROR(Kernel_SVC,
224                   "Memory attribute doesn't match the given mask (Attribute: 0x{:X}, Mask: {:X}",
225                   attribute, mask);
226         return ERR_INVALID_COMBINATION;
227     }
228 
229     auto& page_table{system.Kernel().CurrentProcess()->PageTable()};
230 
231     return page_table.SetMemoryAttribute(address, size, static_cast<Memory::MemoryAttribute>(mask),
232                                          static_cast<Memory::MemoryAttribute>(attribute));
233 }
234 
SetMemoryAttribute32(Core::System & system,u32 address,u32 size,u32 mask,u32 attribute)235 static ResultCode SetMemoryAttribute32(Core::System& system, u32 address, u32 size, u32 mask,
236                                        u32 attribute) {
237     return SetMemoryAttribute(system, address, size, mask, attribute);
238 }
239 
240 /// Maps a memory range into a different range.
MapMemory(Core::System & system,VAddr dst_addr,VAddr src_addr,u64 size)241 static ResultCode MapMemory(Core::System& system, VAddr dst_addr, VAddr src_addr, u64 size) {
242     std::lock_guard lock{HLE::g_hle_lock};
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 ResultCode result{MapUnmapMemorySanityChecks(page_table, dst_addr, src_addr, size)};
249         result.IsError()) {
250         return result;
251     }
252 
253     return page_table.Map(dst_addr, src_addr, size);
254 }
255 
MapMemory32(Core::System & system,u32 dst_addr,u32 src_addr,u32 size)256 static ResultCode MapMemory32(Core::System& system, u32 dst_addr, u32 src_addr, u32 size) {
257     return MapMemory(system, dst_addr, src_addr, size);
258 }
259 
260 /// Unmaps a region that was previously mapped with svcMapMemory
UnmapMemory(Core::System & system,VAddr dst_addr,VAddr src_addr,u64 size)261 static ResultCode UnmapMemory(Core::System& system, VAddr dst_addr, VAddr src_addr, u64 size) {
262     std::lock_guard lock{HLE::g_hle_lock};
263     LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr,
264               src_addr, size);
265 
266     auto& page_table{system.Kernel().CurrentProcess()->PageTable()};
267 
268     if (const ResultCode result{MapUnmapMemorySanityChecks(page_table, dst_addr, src_addr, size)};
269         result.IsError()) {
270         return result;
271     }
272 
273     return page_table.Unmap(dst_addr, src_addr, size);
274 }
275 
UnmapMemory32(Core::System & system,u32 dst_addr,u32 src_addr,u32 size)276 static ResultCode UnmapMemory32(Core::System& system, u32 dst_addr, u32 src_addr, u32 size) {
277     return UnmapMemory(system, dst_addr, src_addr, size);
278 }
279 
280 /// Connect to an OS service given the port name, returns the handle to the port to out
ConnectToNamedPort(Core::System & system,Handle * out_handle,VAddr port_name_address)281 static ResultCode ConnectToNamedPort(Core::System& system, Handle* out_handle,
282                                      VAddr port_name_address) {
283     std::lock_guard lock{HLE::g_hle_lock};
284     auto& memory = system.Memory();
285 
286     if (!memory.IsValidVirtualAddress(port_name_address)) {
287         LOG_ERROR(Kernel_SVC,
288                   "Port Name Address is not a valid virtual address, port_name_address=0x{:016X}",
289                   port_name_address);
290         return ERR_NOT_FOUND;
291     }
292 
293     static constexpr std::size_t PortNameMaxLength = 11;
294     // Read 1 char beyond the max allowed port name to detect names that are too long.
295     const std::string port_name = memory.ReadCString(port_name_address, PortNameMaxLength + 1);
296     if (port_name.size() > PortNameMaxLength) {
297         LOG_ERROR(Kernel_SVC, "Port name is too long, expected {} but got {}", PortNameMaxLength,
298                   port_name.size());
299         return ERR_OUT_OF_RANGE;
300     }
301 
302     LOG_TRACE(Kernel_SVC, "called port_name={}", port_name);
303 
304     auto& kernel = system.Kernel();
305     const auto it = kernel.FindNamedPort(port_name);
306     if (!kernel.IsValidNamedPort(it)) {
307         LOG_WARNING(Kernel_SVC, "tried to connect to unknown port: {}", port_name);
308         return ERR_NOT_FOUND;
309     }
310 
311     ASSERT(kernel.CurrentProcess()->GetResourceLimit()->Reserve(ResourceType::Sessions, 1));
312 
313     auto client_port = it->second;
314 
315     std::shared_ptr<ClientSession> client_session;
316     CASCADE_RESULT(client_session, client_port->Connect());
317 
318     // Return the client session
319     auto& handle_table = kernel.CurrentProcess()->GetHandleTable();
320     CASCADE_RESULT(*out_handle, handle_table.Create(client_session));
321     return RESULT_SUCCESS;
322 }
323 
ConnectToNamedPort32(Core::System & system,Handle * out_handle,u32 port_name_address)324 static ResultCode ConnectToNamedPort32(Core::System& system, Handle* out_handle,
325                                        u32 port_name_address) {
326 
327     return ConnectToNamedPort(system, out_handle, port_name_address);
328 }
329 
330 /// Makes a blocking IPC call to an OS service.
SendSyncRequest(Core::System & system,Handle handle)331 static ResultCode SendSyncRequest(Core::System& system, Handle handle) {
332     const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
333     std::shared_ptr<ClientSession> session = handle_table.Get<ClientSession>(handle);
334     if (!session) {
335         LOG_ERROR(Kernel_SVC, "called with invalid handle=0x{:08X}", handle);
336         return ERR_INVALID_HANDLE;
337     }
338 
339     LOG_TRACE(Kernel_SVC, "called handle=0x{:08X}({})", handle, session->GetName());
340 
341     auto thread = system.CurrentScheduler().GetCurrentThread();
342     {
343         SchedulerLock lock(system.Kernel());
344         thread->InvalidateHLECallback();
345         thread->SetStatus(ThreadStatus::WaitIPC);
346         session->SendSyncRequest(SharedFrom(thread), system.Memory(), system.CoreTiming());
347     }
348 
349     if (thread->HasHLECallback()) {
350         Handle event_handle = thread->GetHLETimeEvent();
351         if (event_handle != InvalidHandle) {
352             auto& time_manager = system.Kernel().TimeManager();
353             time_manager.UnscheduleTimeEvent(event_handle);
354         }
355 
356         {
357             SchedulerLock lock(system.Kernel());
358             auto* sync_object = thread->GetHLESyncObject();
359             sync_object->RemoveWaitingThread(SharedFrom(thread));
360         }
361 
362         thread->InvokeHLECallback(SharedFrom(thread));
363     }
364 
365     return thread->GetSignalingResult();
366 }
367 
SendSyncRequest32(Core::System & system,Handle handle)368 static ResultCode SendSyncRequest32(Core::System& system, Handle handle) {
369     return SendSyncRequest(system, handle);
370 }
371 
372 /// Get the ID for the specified thread.
GetThreadId(Core::System & system,u64 * thread_id,Handle thread_handle)373 static ResultCode GetThreadId(Core::System& system, u64* thread_id, Handle thread_handle) {
374     LOG_TRACE(Kernel_SVC, "called thread=0x{:08X}", thread_handle);
375 
376     const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
377     const std::shared_ptr<Thread> thread = handle_table.Get<Thread>(thread_handle);
378     if (!thread) {
379         LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle=0x{:08X}", thread_handle);
380         return ERR_INVALID_HANDLE;
381     }
382 
383     *thread_id = thread->GetThreadID();
384     return RESULT_SUCCESS;
385 }
386 
GetThreadId32(Core::System & system,u32 * thread_id_low,u32 * thread_id_high,Handle thread_handle)387 static ResultCode GetThreadId32(Core::System& system, u32* thread_id_low, u32* thread_id_high,
388                                 Handle thread_handle) {
389     u64 thread_id{};
390     const ResultCode result{GetThreadId(system, &thread_id, thread_handle)};
391 
392     *thread_id_low = static_cast<u32>(thread_id >> 32);
393     *thread_id_high = static_cast<u32>(thread_id & std::numeric_limits<u32>::max());
394 
395     return result;
396 }
397 
398 /// Gets the ID of the specified process or a specified thread's owning process.
GetProcessId(Core::System & system,u64 * process_id,Handle handle)399 static ResultCode GetProcessId(Core::System& system, u64* process_id, Handle handle) {
400     LOG_DEBUG(Kernel_SVC, "called handle=0x{:08X}", handle);
401 
402     const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
403     const std::shared_ptr<Process> process = handle_table.Get<Process>(handle);
404     if (process) {
405         *process_id = process->GetProcessID();
406         return RESULT_SUCCESS;
407     }
408 
409     const std::shared_ptr<Thread> thread = handle_table.Get<Thread>(handle);
410     if (thread) {
411         const Process* const owner_process = thread->GetOwnerProcess();
412         if (!owner_process) {
413             LOG_ERROR(Kernel_SVC, "Non-existent owning process encountered.");
414             return ERR_INVALID_HANDLE;
415         }
416 
417         *process_id = owner_process->GetProcessID();
418         return RESULT_SUCCESS;
419     }
420 
421     // NOTE: This should also handle debug objects before returning.
422 
423     LOG_ERROR(Kernel_SVC, "Handle does not exist, handle=0x{:08X}", handle);
424     return ERR_INVALID_HANDLE;
425 }
426 
GetProcessId32(Core::System & system,u32 * process_id_low,u32 * process_id_high,Handle handle)427 static ResultCode GetProcessId32(Core::System& system, u32* process_id_low, u32* process_id_high,
428                                  Handle handle) {
429     u64 process_id{};
430     const auto result = GetProcessId(system, &process_id, handle);
431     *process_id_low = static_cast<u32>(process_id);
432     *process_id_high = static_cast<u32>(process_id >> 32);
433     return result;
434 }
435 
436 /// Wait for the given handles to synchronize, timeout after the specified nanoseconds
WaitSynchronization(Core::System & system,Handle * index,VAddr handles_address,u64 handle_count,s64 nano_seconds)437 static ResultCode WaitSynchronization(Core::System& system, Handle* index, VAddr handles_address,
438                                       u64 handle_count, s64 nano_seconds) {
439     LOG_TRACE(Kernel_SVC, "called handles_address=0x{:X}, handle_count={}, nano_seconds={}",
440               handles_address, handle_count, nano_seconds);
441 
442     auto& memory = system.Memory();
443     if (!memory.IsValidVirtualAddress(handles_address)) {
444         LOG_ERROR(Kernel_SVC,
445                   "Handle address is not a valid virtual address, handle_address=0x{:016X}",
446                   handles_address);
447         return ERR_INVALID_POINTER;
448     }
449 
450     static constexpr u64 MaxHandles = 0x40;
451 
452     if (handle_count > MaxHandles) {
453         LOG_ERROR(Kernel_SVC, "Handle count specified is too large, expected {} but got {}",
454                   MaxHandles, handle_count);
455         return ERR_OUT_OF_RANGE;
456     }
457 
458     auto& kernel = system.Kernel();
459     Thread::ThreadSynchronizationObjects objects(handle_count);
460     const auto& handle_table = kernel.CurrentProcess()->GetHandleTable();
461 
462     for (u64 i = 0; i < handle_count; ++i) {
463         const Handle handle = memory.Read32(handles_address + i * sizeof(Handle));
464         const auto object = handle_table.Get<SynchronizationObject>(handle);
465 
466         if (object == nullptr) {
467             LOG_ERROR(Kernel_SVC, "Object is a nullptr");
468             return ERR_INVALID_HANDLE;
469         }
470 
471         objects[i] = object;
472     }
473     auto& synchronization = kernel.Synchronization();
474     const auto [result, handle_result] = synchronization.WaitFor(objects, nano_seconds);
475     *index = handle_result;
476     return result;
477 }
478 
WaitSynchronization32(Core::System & system,u32 timeout_low,u32 handles_address,s32 handle_count,u32 timeout_high,Handle * index)479 static ResultCode WaitSynchronization32(Core::System& system, u32 timeout_low, u32 handles_address,
480                                         s32 handle_count, u32 timeout_high, Handle* index) {
481     const s64 nano_seconds{(static_cast<s64>(timeout_high) << 32) | static_cast<s64>(timeout_low)};
482     return WaitSynchronization(system, index, handles_address, handle_count, nano_seconds);
483 }
484 
485 /// Resumes a thread waiting on WaitSynchronization
CancelSynchronization(Core::System & system,Handle thread_handle)486 static ResultCode CancelSynchronization(Core::System& system, Handle thread_handle) {
487     LOG_TRACE(Kernel_SVC, "called thread=0x{:X}", thread_handle);
488 
489     const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
490     std::shared_ptr<Thread> thread = handle_table.Get<Thread>(thread_handle);
491     if (!thread) {
492         LOG_ERROR(Kernel_SVC, "Thread handle does not exist, thread_handle=0x{:08X}",
493                   thread_handle);
494         return ERR_INVALID_HANDLE;
495     }
496 
497     thread->CancelWait();
498     return RESULT_SUCCESS;
499 }
500 
CancelSynchronization32(Core::System & system,Handle thread_handle)501 static ResultCode CancelSynchronization32(Core::System& system, Handle thread_handle) {
502     return CancelSynchronization(system, thread_handle);
503 }
504 
505 /// Attempts to locks a mutex, creating it if it does not already exist
ArbitrateLock(Core::System & system,Handle holding_thread_handle,VAddr mutex_addr,Handle requesting_thread_handle)506 static ResultCode ArbitrateLock(Core::System& system, Handle holding_thread_handle,
507                                 VAddr mutex_addr, Handle requesting_thread_handle) {
508     LOG_TRACE(Kernel_SVC,
509               "called holding_thread_handle=0x{:08X}, mutex_addr=0x{:X}, "
510               "requesting_current_thread_handle=0x{:08X}",
511               holding_thread_handle, mutex_addr, requesting_thread_handle);
512 
513     if (Core::Memory::IsKernelVirtualAddress(mutex_addr)) {
514         LOG_ERROR(Kernel_SVC, "Mutex Address is a kernel virtual address, mutex_addr={:016X}",
515                   mutex_addr);
516         return ERR_INVALID_ADDRESS_STATE;
517     }
518 
519     if (!Common::IsWordAligned(mutex_addr)) {
520         LOG_ERROR(Kernel_SVC, "Mutex Address is not word aligned, mutex_addr={:016X}", mutex_addr);
521         return ERR_INVALID_ADDRESS;
522     }
523 
524     auto* const current_process = system.Kernel().CurrentProcess();
525     return current_process->GetMutex().TryAcquire(mutex_addr, holding_thread_handle,
526                                                   requesting_thread_handle);
527 }
528 
ArbitrateLock32(Core::System & system,Handle holding_thread_handle,u32 mutex_addr,Handle requesting_thread_handle)529 static ResultCode ArbitrateLock32(Core::System& system, Handle holding_thread_handle,
530                                   u32 mutex_addr, Handle requesting_thread_handle) {
531     return ArbitrateLock(system, holding_thread_handle, mutex_addr, requesting_thread_handle);
532 }
533 
534 /// Unlock a mutex
ArbitrateUnlock(Core::System & system,VAddr mutex_addr)535 static ResultCode ArbitrateUnlock(Core::System& system, VAddr mutex_addr) {
536     LOG_TRACE(Kernel_SVC, "called mutex_addr=0x{:X}", mutex_addr);
537 
538     if (Core::Memory::IsKernelVirtualAddress(mutex_addr)) {
539         LOG_ERROR(Kernel_SVC, "Mutex Address is a kernel virtual address, mutex_addr={:016X}",
540                   mutex_addr);
541         return ERR_INVALID_ADDRESS_STATE;
542     }
543 
544     if (!Common::IsWordAligned(mutex_addr)) {
545         LOG_ERROR(Kernel_SVC, "Mutex Address is not word aligned, mutex_addr={:016X}", mutex_addr);
546         return ERR_INVALID_ADDRESS;
547     }
548 
549     auto* const current_process = system.Kernel().CurrentProcess();
550     return current_process->GetMutex().Release(mutex_addr);
551 }
552 
ArbitrateUnlock32(Core::System & system,u32 mutex_addr)553 static ResultCode ArbitrateUnlock32(Core::System& system, u32 mutex_addr) {
554     return ArbitrateUnlock(system, mutex_addr);
555 }
556 
557 enum class BreakType : u32 {
558     Panic = 0,
559     AssertionFailed = 1,
560     PreNROLoad = 3,
561     PostNROLoad = 4,
562     PreNROUnload = 5,
563     PostNROUnload = 6,
564     CppException = 7,
565 };
566 
567 struct BreakReason {
568     union {
569         u32 raw;
570         BitField<0, 30, BreakType> break_type;
571         BitField<31, 1, u32> signal_debugger;
572     };
573 };
574 
575 /// Break program execution
Break(Core::System & system,u32 reason,u64 info1,u64 info2)576 static void Break(Core::System& system, u32 reason, u64 info1, u64 info2) {
577     BreakReason break_reason{reason};
578     bool has_dumped_buffer{};
579     std::vector<u8> debug_buffer;
580 
581     const auto handle_debug_buffer = [&](VAddr addr, u64 sz) {
582         if (sz == 0 || addr == 0 || has_dumped_buffer) {
583             return;
584         }
585 
586         auto& memory = system.Memory();
587 
588         // This typically is an error code so we're going to assume this is the case
589         if (sz == sizeof(u32)) {
590             LOG_CRITICAL(Debug_Emulated, "debug_buffer_err_code={:X}", memory.Read32(addr));
591         } else {
592             // We don't know what's in here so we'll hexdump it
593             debug_buffer.resize(sz);
594             memory.ReadBlock(addr, debug_buffer.data(), sz);
595             std::string hexdump;
596             for (std::size_t i = 0; i < debug_buffer.size(); i++) {
597                 hexdump += fmt::format("{:02X} ", debug_buffer[i]);
598                 if (i != 0 && i % 16 == 0) {
599                     hexdump += '\n';
600                 }
601             }
602             LOG_CRITICAL(Debug_Emulated, "debug_buffer=\n{}", hexdump);
603         }
604         has_dumped_buffer = true;
605     };
606     switch (break_reason.break_type) {
607     case BreakType::Panic:
608         LOG_CRITICAL(Debug_Emulated, "Signalling debugger, PANIC! info1=0x{:016X}, info2=0x{:016X}",
609                      info1, info2);
610         handle_debug_buffer(info1, info2);
611         break;
612     case BreakType::AssertionFailed:
613         LOG_CRITICAL(Debug_Emulated,
614                      "Signalling debugger, Assertion failed! info1=0x{:016X}, info2=0x{:016X}",
615                      info1, info2);
616         handle_debug_buffer(info1, info2);
617         break;
618     case BreakType::PreNROLoad:
619         LOG_WARNING(
620             Debug_Emulated,
621             "Signalling debugger, Attempting to load an NRO at 0x{:016X} with size 0x{:016X}",
622             info1, info2);
623         break;
624     case BreakType::PostNROLoad:
625         LOG_WARNING(Debug_Emulated,
626                     "Signalling debugger, Loaded an NRO at 0x{:016X} with size 0x{:016X}", info1,
627                     info2);
628         break;
629     case BreakType::PreNROUnload:
630         LOG_WARNING(
631             Debug_Emulated,
632             "Signalling debugger, Attempting to unload an NRO at 0x{:016X} with size 0x{:016X}",
633             info1, info2);
634         break;
635     case BreakType::PostNROUnload:
636         LOG_WARNING(Debug_Emulated,
637                     "Signalling debugger, Unloaded an NRO at 0x{:016X} with size 0x{:016X}", info1,
638                     info2);
639         break;
640     case BreakType::CppException:
641         LOG_CRITICAL(Debug_Emulated, "Signalling debugger. Uncaught C++ exception encountered.");
642         break;
643     default:
644         LOG_WARNING(
645             Debug_Emulated,
646             "Signalling debugger, Unknown break reason {}, info1=0x{:016X}, info2=0x{:016X}",
647             static_cast<u32>(break_reason.break_type.Value()), info1, info2);
648         handle_debug_buffer(info1, info2);
649         break;
650     }
651 
652     system.GetReporter().SaveSvcBreakReport(
653         static_cast<u32>(break_reason.break_type.Value()), break_reason.signal_debugger, info1,
654         info2, has_dumped_buffer ? std::make_optional(debug_buffer) : std::nullopt);
655 
656     if (!break_reason.signal_debugger) {
657         SchedulerLock lock(system.Kernel());
658         LOG_CRITICAL(
659             Debug_Emulated,
660             "Emulated program broke execution! reason=0x{:016X}, info1=0x{:016X}, info2=0x{:016X}",
661             reason, info1, info2);
662 
663         handle_debug_buffer(info1, info2);
664 
665         auto* const current_thread = system.CurrentScheduler().GetCurrentThread();
666         const auto thread_processor_id = current_thread->GetProcessorID();
667         system.ArmInterface(static_cast<std::size_t>(thread_processor_id)).LogBacktrace();
668 
669         // Kill the current thread
670         system.Kernel().ExceptionalExit();
671         current_thread->Stop();
672     }
673 }
674 
Break32(Core::System & system,u32 reason,u32 info1,u32 info2)675 static void Break32(Core::System& system, u32 reason, u32 info1, u32 info2) {
676     Break(system, reason, info1, info2);
677 }
678 
679 /// Used to output a message on a debug hardware unit - does nothing on a retail unit
OutputDebugString(Core::System & system,VAddr address,u64 len)680 static void OutputDebugString(Core::System& system, VAddr address, u64 len) {
681     if (len == 0) {
682         return;
683     }
684 
685     std::string str(len, '\0');
686     system.Memory().ReadBlock(address, str.data(), str.size());
687     LOG_DEBUG(Debug_Emulated, "{}", str);
688 }
689 
690 /// Gets system/memory information for the current process
GetInfo(Core::System & system,u64 * result,u64 info_id,u64 handle,u64 info_sub_id)691 static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, u64 handle,
692                           u64 info_sub_id) {
693     std::lock_guard lock{HLE::g_hle_lock};
694     LOG_TRACE(Kernel_SVC, "called info_id=0x{:X}, info_sub_id=0x{:X}, handle=0x{:08X}", info_id,
695               info_sub_id, handle);
696 
697     enum class GetInfoType : u64 {
698         // 1.0.0+
699         AllowedCPUCoreMask = 0,
700         AllowedThreadPriorityMask = 1,
701         MapRegionBaseAddr = 2,
702         MapRegionSize = 3,
703         HeapRegionBaseAddr = 4,
704         HeapRegionSize = 5,
705         TotalPhysicalMemoryAvailable = 6,
706         TotalPhysicalMemoryUsed = 7,
707         IsCurrentProcessBeingDebugged = 8,
708         RegisterResourceLimit = 9,
709         IdleTickCount = 10,
710         RandomEntropy = 11,
711         ThreadTickCount = 0xF0000002,
712         // 2.0.0+
713         ASLRRegionBaseAddr = 12,
714         ASLRRegionSize = 13,
715         StackRegionBaseAddr = 14,
716         StackRegionSize = 15,
717         // 3.0.0+
718         SystemResourceSize = 16,
719         SystemResourceUsage = 17,
720         TitleId = 18,
721         // 4.0.0+
722         PrivilegedProcessId = 19,
723         // 5.0.0+
724         UserExceptionContextAddr = 20,
725         // 6.0.0+
726         TotalPhysicalMemoryAvailableWithoutSystemResource = 21,
727         TotalPhysicalMemoryUsedWithoutSystemResource = 22,
728     };
729 
730     const auto info_id_type = static_cast<GetInfoType>(info_id);
731 
732     switch (info_id_type) {
733     case GetInfoType::AllowedCPUCoreMask:
734     case GetInfoType::AllowedThreadPriorityMask:
735     case GetInfoType::MapRegionBaseAddr:
736     case GetInfoType::MapRegionSize:
737     case GetInfoType::HeapRegionBaseAddr:
738     case GetInfoType::HeapRegionSize:
739     case GetInfoType::ASLRRegionBaseAddr:
740     case GetInfoType::ASLRRegionSize:
741     case GetInfoType::StackRegionBaseAddr:
742     case GetInfoType::StackRegionSize:
743     case GetInfoType::TotalPhysicalMemoryAvailable:
744     case GetInfoType::TotalPhysicalMemoryUsed:
745     case GetInfoType::SystemResourceSize:
746     case GetInfoType::SystemResourceUsage:
747     case GetInfoType::TitleId:
748     case GetInfoType::UserExceptionContextAddr:
749     case GetInfoType::TotalPhysicalMemoryAvailableWithoutSystemResource:
750     case GetInfoType::TotalPhysicalMemoryUsedWithoutSystemResource: {
751         if (info_sub_id != 0) {
752             LOG_ERROR(Kernel_SVC, "Info sub id is non zero! info_id={}, info_sub_id={}", info_id,
753                       info_sub_id);
754             return ERR_INVALID_ENUM_VALUE;
755         }
756 
757         const auto& current_process_handle_table =
758             system.Kernel().CurrentProcess()->GetHandleTable();
759         const auto process = current_process_handle_table.Get<Process>(static_cast<Handle>(handle));
760         if (!process) {
761             LOG_ERROR(Kernel_SVC, "Process is not valid! info_id={}, info_sub_id={}, handle={:08X}",
762                       info_id, info_sub_id, handle);
763             return ERR_INVALID_HANDLE;
764         }
765 
766         switch (info_id_type) {
767         case GetInfoType::AllowedCPUCoreMask:
768             *result = process->GetCoreMask();
769             return RESULT_SUCCESS;
770 
771         case GetInfoType::AllowedThreadPriorityMask:
772             *result = process->GetPriorityMask();
773             return RESULT_SUCCESS;
774 
775         case GetInfoType::MapRegionBaseAddr:
776             *result = process->PageTable().GetAliasRegionStart();
777             return RESULT_SUCCESS;
778 
779         case GetInfoType::MapRegionSize:
780             *result = process->PageTable().GetAliasRegionSize();
781             return RESULT_SUCCESS;
782 
783         case GetInfoType::HeapRegionBaseAddr:
784             *result = process->PageTable().GetHeapRegionStart();
785             return RESULT_SUCCESS;
786 
787         case GetInfoType::HeapRegionSize:
788             *result = process->PageTable().GetHeapRegionSize();
789             return RESULT_SUCCESS;
790 
791         case GetInfoType::ASLRRegionBaseAddr:
792             *result = process->PageTable().GetAliasCodeRegionStart();
793             return RESULT_SUCCESS;
794 
795         case GetInfoType::ASLRRegionSize:
796             *result = process->PageTable().GetAliasCodeRegionSize();
797             return RESULT_SUCCESS;
798 
799         case GetInfoType::StackRegionBaseAddr:
800             *result = process->PageTable().GetStackRegionStart();
801             return RESULT_SUCCESS;
802 
803         case GetInfoType::StackRegionSize:
804             *result = process->PageTable().GetStackRegionSize();
805             return RESULT_SUCCESS;
806 
807         case GetInfoType::TotalPhysicalMemoryAvailable:
808             *result = process->GetTotalPhysicalMemoryAvailable();
809             return RESULT_SUCCESS;
810 
811         case GetInfoType::TotalPhysicalMemoryUsed:
812             *result = process->GetTotalPhysicalMemoryUsed();
813             return RESULT_SUCCESS;
814 
815         case GetInfoType::SystemResourceSize:
816             *result = process->GetSystemResourceSize();
817             return RESULT_SUCCESS;
818 
819         case GetInfoType::SystemResourceUsage:
820             LOG_WARNING(Kernel_SVC, "(STUBBED) Attempted to query system resource usage");
821             *result = process->GetSystemResourceUsage();
822             return RESULT_SUCCESS;
823 
824         case GetInfoType::TitleId:
825             *result = process->GetTitleID();
826             return RESULT_SUCCESS;
827 
828         case GetInfoType::UserExceptionContextAddr:
829             *result = process->GetTLSRegionAddress();
830             return RESULT_SUCCESS;
831 
832         case GetInfoType::TotalPhysicalMemoryAvailableWithoutSystemResource:
833             *result = process->GetTotalPhysicalMemoryAvailableWithoutSystemResource();
834             return RESULT_SUCCESS;
835 
836         case GetInfoType::TotalPhysicalMemoryUsedWithoutSystemResource:
837             *result = process->GetTotalPhysicalMemoryUsedWithoutSystemResource();
838             return RESULT_SUCCESS;
839 
840         default:
841             break;
842         }
843 
844         LOG_ERROR(Kernel_SVC, "Unimplemented svcGetInfo id=0x{:016X}", info_id);
845         return ERR_INVALID_ENUM_VALUE;
846     }
847 
848     case GetInfoType::IsCurrentProcessBeingDebugged:
849         *result = 0;
850         return RESULT_SUCCESS;
851 
852     case GetInfoType::RegisterResourceLimit: {
853         if (handle != 0) {
854             LOG_ERROR(Kernel, "Handle is non zero! handle={:08X}", handle);
855             return ERR_INVALID_HANDLE;
856         }
857 
858         if (info_sub_id != 0) {
859             LOG_ERROR(Kernel, "Info sub id is non zero! info_id={}, info_sub_id={}", info_id,
860                       info_sub_id);
861             return ERR_INVALID_COMBINATION;
862         }
863 
864         Process* const current_process = system.Kernel().CurrentProcess();
865         HandleTable& handle_table = current_process->GetHandleTable();
866         const auto resource_limit = current_process->GetResourceLimit();
867         if (!resource_limit) {
868             *result = KernelHandle::InvalidHandle;
869             // Yes, the kernel considers this a successful operation.
870             return RESULT_SUCCESS;
871         }
872 
873         const auto table_result = handle_table.Create(resource_limit);
874         if (table_result.Failed()) {
875             return table_result.Code();
876         }
877 
878         *result = *table_result;
879         return RESULT_SUCCESS;
880     }
881 
882     case GetInfoType::RandomEntropy:
883         if (handle != 0) {
884             LOG_ERROR(Kernel_SVC, "Process Handle is non zero, expected 0 result but got {:016X}",
885                       handle);
886             return ERR_INVALID_HANDLE;
887         }
888 
889         if (info_sub_id >= Process::RANDOM_ENTROPY_SIZE) {
890             LOG_ERROR(Kernel_SVC, "Entropy size is out of range, expected {} but got {}",
891                       Process::RANDOM_ENTROPY_SIZE, info_sub_id);
892             return ERR_INVALID_COMBINATION;
893         }
894 
895         *result = system.Kernel().CurrentProcess()->GetRandomEntropy(info_sub_id);
896         return RESULT_SUCCESS;
897 
898     case GetInfoType::PrivilegedProcessId:
899         LOG_WARNING(Kernel_SVC,
900                     "(STUBBED) Attempted to query privileged process id bounds, returned 0");
901         *result = 0;
902         return RESULT_SUCCESS;
903 
904     case GetInfoType::ThreadTickCount: {
905         constexpr u64 num_cpus = 4;
906         if (info_sub_id != 0xFFFFFFFFFFFFFFFF && info_sub_id >= num_cpus) {
907             LOG_ERROR(Kernel_SVC, "Core count is out of range, expected {} but got {}", num_cpus,
908                       info_sub_id);
909             return ERR_INVALID_COMBINATION;
910         }
911 
912         const auto thread = system.Kernel().CurrentProcess()->GetHandleTable().Get<Thread>(
913             static_cast<Handle>(handle));
914         if (!thread) {
915             LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle=0x{:08X}",
916                       static_cast<Handle>(handle));
917             return ERR_INVALID_HANDLE;
918         }
919 
920         const auto& core_timing = system.CoreTiming();
921         const auto& scheduler = system.CurrentScheduler();
922         const auto* const current_thread = scheduler.GetCurrentThread();
923         const bool same_thread = current_thread == thread.get();
924 
925         const u64 prev_ctx_ticks = scheduler.GetLastContextSwitchTicks();
926         u64 out_ticks = 0;
927         if (same_thread && info_sub_id == 0xFFFFFFFFFFFFFFFF) {
928             const u64 thread_ticks = current_thread->GetTotalCPUTimeTicks();
929 
930             out_ticks = thread_ticks + (core_timing.GetCPUTicks() - prev_ctx_ticks);
931         } else if (same_thread && info_sub_id == system.CurrentCoreIndex()) {
932             out_ticks = core_timing.GetCPUTicks() - prev_ctx_ticks;
933         }
934 
935         *result = out_ticks;
936         return RESULT_SUCCESS;
937     }
938 
939     default:
940         LOG_ERROR(Kernel_SVC, "Unimplemented svcGetInfo id=0x{:016X}", info_id);
941         return ERR_INVALID_ENUM_VALUE;
942     }
943 }
944 
GetInfo32(Core::System & system,u32 * result_low,u32 * result_high,u32 sub_id_low,u32 info_id,u32 handle,u32 sub_id_high)945 static ResultCode GetInfo32(Core::System& system, u32* result_low, u32* result_high, u32 sub_id_low,
946                             u32 info_id, u32 handle, u32 sub_id_high) {
947     const u64 sub_id{u64{sub_id_low} | (u64{sub_id_high} << 32)};
948     u64 res_value{};
949 
950     const ResultCode result{GetInfo(system, &res_value, info_id, handle, sub_id)};
951     *result_high = static_cast<u32>(res_value >> 32);
952     *result_low = static_cast<u32>(res_value & std::numeric_limits<u32>::max());
953 
954     return result;
955 }
956 
957 /// Maps memory at a desired address
MapPhysicalMemory(Core::System & system,VAddr addr,u64 size)958 static ResultCode MapPhysicalMemory(Core::System& system, VAddr addr, u64 size) {
959     std::lock_guard lock{HLE::g_hle_lock};
960     LOG_DEBUG(Kernel_SVC, "called, addr=0x{:016X}, size=0x{:X}", addr, size);
961 
962     if (!Common::Is4KBAligned(addr)) {
963         LOG_ERROR(Kernel_SVC, "Address is not aligned to 4KB, 0x{:016X}", addr);
964         return ERR_INVALID_ADDRESS;
965     }
966 
967     if (!Common::Is4KBAligned(size)) {
968         LOG_ERROR(Kernel_SVC, "Size is not aligned to 4KB, 0x{:X}", size);
969         return ERR_INVALID_SIZE;
970     }
971 
972     if (size == 0) {
973         LOG_ERROR(Kernel_SVC, "Size is zero");
974         return ERR_INVALID_SIZE;
975     }
976 
977     if (!(addr < addr + size)) {
978         LOG_ERROR(Kernel_SVC, "Size causes 64-bit overflow of address");
979         return ERR_INVALID_MEMORY_RANGE;
980     }
981 
982     Process* const current_process{system.Kernel().CurrentProcess()};
983     auto& page_table{current_process->PageTable()};
984 
985     if (current_process->GetSystemResourceSize() == 0) {
986         LOG_ERROR(Kernel_SVC, "System Resource Size is zero");
987         return ERR_INVALID_STATE;
988     }
989 
990     if (!page_table.IsInsideAddressSpace(addr, size)) {
991         LOG_ERROR(Kernel_SVC,
992                   "Address is not within the address space, addr=0x{:016X}, size=0x{:016X}", addr,
993                   size);
994         return ERR_INVALID_MEMORY_RANGE;
995     }
996 
997     if (page_table.IsOutsideAliasRegion(addr, size)) {
998         LOG_ERROR(Kernel_SVC,
999                   "Address is not within the alias region, addr=0x{:016X}, size=0x{:016X}", addr,
1000                   size);
1001         return ERR_INVALID_MEMORY_RANGE;
1002     }
1003 
1004     return page_table.MapPhysicalMemory(addr, size);
1005 }
1006 
MapPhysicalMemory32(Core::System & system,u32 addr,u32 size)1007 static ResultCode MapPhysicalMemory32(Core::System& system, u32 addr, u32 size) {
1008     return MapPhysicalMemory(system, addr, size);
1009 }
1010 
1011 /// Unmaps memory previously mapped via MapPhysicalMemory
UnmapPhysicalMemory(Core::System & system,VAddr addr,u64 size)1012 static ResultCode UnmapPhysicalMemory(Core::System& system, VAddr addr, u64 size) {
1013     std::lock_guard lock{HLE::g_hle_lock};
1014     LOG_DEBUG(Kernel_SVC, "called, addr=0x{:016X}, size=0x{:X}", addr, size);
1015 
1016     if (!Common::Is4KBAligned(addr)) {
1017         LOG_ERROR(Kernel_SVC, "Address is not aligned to 4KB, 0x{:016X}", addr);
1018         return ERR_INVALID_ADDRESS;
1019     }
1020 
1021     if (!Common::Is4KBAligned(size)) {
1022         LOG_ERROR(Kernel_SVC, "Size is not aligned to 4KB, 0x{:X}", size);
1023         return ERR_INVALID_SIZE;
1024     }
1025 
1026     if (size == 0) {
1027         LOG_ERROR(Kernel_SVC, "Size is zero");
1028         return ERR_INVALID_SIZE;
1029     }
1030 
1031     if (!(addr < addr + size)) {
1032         LOG_ERROR(Kernel_SVC, "Size causes 64-bit overflow of address");
1033         return ERR_INVALID_MEMORY_RANGE;
1034     }
1035 
1036     Process* const current_process{system.Kernel().CurrentProcess()};
1037     auto& page_table{current_process->PageTable()};
1038 
1039     if (current_process->GetSystemResourceSize() == 0) {
1040         LOG_ERROR(Kernel_SVC, "System Resource Size is zero");
1041         return ERR_INVALID_STATE;
1042     }
1043 
1044     if (!page_table.IsInsideAddressSpace(addr, size)) {
1045         LOG_ERROR(Kernel_SVC,
1046                   "Address is not within the address space, addr=0x{:016X}, size=0x{:016X}", addr,
1047                   size);
1048         return ERR_INVALID_MEMORY_RANGE;
1049     }
1050 
1051     if (page_table.IsOutsideAliasRegion(addr, size)) {
1052         LOG_ERROR(Kernel_SVC,
1053                   "Address is not within the alias region, addr=0x{:016X}, size=0x{:016X}", addr,
1054                   size);
1055         return ERR_INVALID_MEMORY_RANGE;
1056     }
1057 
1058     return page_table.UnmapPhysicalMemory(addr, size);
1059 }
1060 
UnmapPhysicalMemory32(Core::System & system,u32 addr,u32 size)1061 static ResultCode UnmapPhysicalMemory32(Core::System& system, u32 addr, u32 size) {
1062     return UnmapPhysicalMemory(system, addr, size);
1063 }
1064 
1065 /// Sets the thread activity
SetThreadActivity(Core::System & system,Handle handle,u32 activity)1066 static ResultCode SetThreadActivity(Core::System& system, Handle handle, u32 activity) {
1067     LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, activity=0x{:08X}", handle, activity);
1068     if (activity > static_cast<u32>(ThreadActivity::Paused)) {
1069         return ERR_INVALID_ENUM_VALUE;
1070     }
1071 
1072     const auto* current_process = system.Kernel().CurrentProcess();
1073     const std::shared_ptr<Thread> thread = current_process->GetHandleTable().Get<Thread>(handle);
1074     if (!thread) {
1075         LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle=0x{:08X}", handle);
1076         return ERR_INVALID_HANDLE;
1077     }
1078 
1079     if (thread->GetOwnerProcess() != current_process) {
1080         LOG_ERROR(Kernel_SVC,
1081                   "The current process does not own the current thread, thread_handle={:08X} "
1082                   "thread_pid={}, "
1083                   "current_process_pid={}",
1084                   handle, thread->GetOwnerProcess()->GetProcessID(),
1085                   current_process->GetProcessID());
1086         return ERR_INVALID_HANDLE;
1087     }
1088 
1089     if (thread.get() == system.CurrentScheduler().GetCurrentThread()) {
1090         LOG_ERROR(Kernel_SVC, "The thread handle specified is the current running thread");
1091         return ERR_BUSY;
1092     }
1093 
1094     return thread->SetActivity(static_cast<ThreadActivity>(activity));
1095 }
1096 
SetThreadActivity32(Core::System & system,Handle handle,u32 activity)1097 static ResultCode SetThreadActivity32(Core::System& system, Handle handle, u32 activity) {
1098     return SetThreadActivity(system, handle, activity);
1099 }
1100 
1101 /// Gets the thread context
GetThreadContext(Core::System & system,VAddr thread_context,Handle handle)1102 static ResultCode GetThreadContext(Core::System& system, VAddr thread_context, Handle handle) {
1103     LOG_DEBUG(Kernel_SVC, "called, context=0x{:08X}, thread=0x{:X}", thread_context, handle);
1104 
1105     const auto* current_process = system.Kernel().CurrentProcess();
1106     const std::shared_ptr<Thread> thread = current_process->GetHandleTable().Get<Thread>(handle);
1107     if (!thread) {
1108         LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle=0x{:08X}", handle);
1109         return ERR_INVALID_HANDLE;
1110     }
1111 
1112     if (thread->GetOwnerProcess() != current_process) {
1113         LOG_ERROR(Kernel_SVC,
1114                   "The current process does not own the current thread, thread_handle={:08X} "
1115                   "thread_pid={}, "
1116                   "current_process_pid={}",
1117                   handle, thread->GetOwnerProcess()->GetProcessID(),
1118                   current_process->GetProcessID());
1119         return ERR_INVALID_HANDLE;
1120     }
1121 
1122     if (thread.get() == system.CurrentScheduler().GetCurrentThread()) {
1123         LOG_ERROR(Kernel_SVC, "The thread handle specified is the current running thread");
1124         return ERR_BUSY;
1125     }
1126 
1127     Core::ARM_Interface::ThreadContext64 ctx = thread->GetContext64();
1128     // Mask away mode bits, interrupt bits, IL bit, and other reserved bits.
1129     ctx.pstate &= 0xFF0FFE20;
1130 
1131     // If 64-bit, we can just write the context registers directly and we're good.
1132     // However, if 32-bit, we have to ensure some registers are zeroed out.
1133     if (!current_process->Is64BitProcess()) {
1134         std::fill(ctx.cpu_registers.begin() + 15, ctx.cpu_registers.end(), 0);
1135         std::fill(ctx.vector_registers.begin() + 16, ctx.vector_registers.end(), u128{});
1136     }
1137 
1138     system.Memory().WriteBlock(thread_context, &ctx, sizeof(ctx));
1139     return RESULT_SUCCESS;
1140 }
1141 
GetThreadContext32(Core::System & system,u32 thread_context,Handle handle)1142 static ResultCode GetThreadContext32(Core::System& system, u32 thread_context, Handle handle) {
1143     return GetThreadContext(system, thread_context, handle);
1144 }
1145 
1146 /// Gets the priority for the specified thread
GetThreadPriority(Core::System & system,u32 * priority,Handle handle)1147 static ResultCode GetThreadPriority(Core::System& system, u32* priority, Handle handle) {
1148     LOG_TRACE(Kernel_SVC, "called");
1149 
1150     const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
1151     const std::shared_ptr<Thread> thread = handle_table.Get<Thread>(handle);
1152     if (!thread) {
1153         *priority = 0;
1154         LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle=0x{:08X}", handle);
1155         return ERR_INVALID_HANDLE;
1156     }
1157 
1158     *priority = thread->GetPriority();
1159     return RESULT_SUCCESS;
1160 }
1161 
GetThreadPriority32(Core::System & system,u32 * priority,Handle handle)1162 static ResultCode GetThreadPriority32(Core::System& system, u32* priority, Handle handle) {
1163     return GetThreadPriority(system, priority, handle);
1164 }
1165 
1166 /// Sets the priority for the specified thread
SetThreadPriority(Core::System & system,Handle handle,u32 priority)1167 static ResultCode SetThreadPriority(Core::System& system, Handle handle, u32 priority) {
1168     LOG_TRACE(Kernel_SVC, "called");
1169 
1170     if (priority > THREADPRIO_LOWEST) {
1171         LOG_ERROR(
1172             Kernel_SVC,
1173             "An invalid priority was specified, expected {} but got {} for thread_handle={:08X}",
1174             THREADPRIO_LOWEST, priority, handle);
1175         return ERR_INVALID_THREAD_PRIORITY;
1176     }
1177 
1178     const auto* const current_process = system.Kernel().CurrentProcess();
1179 
1180     std::shared_ptr<Thread> thread = current_process->GetHandleTable().Get<Thread>(handle);
1181     if (!thread) {
1182         LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle=0x{:08X}", handle);
1183         return ERR_INVALID_HANDLE;
1184     }
1185 
1186     thread->SetPriority(priority);
1187 
1188     return RESULT_SUCCESS;
1189 }
1190 
SetThreadPriority32(Core::System & system,Handle handle,u32 priority)1191 static ResultCode SetThreadPriority32(Core::System& system, Handle handle, u32 priority) {
1192     return SetThreadPriority(system, handle, priority);
1193 }
1194 
1195 /// Get which CPU core is executing the current thread
GetCurrentProcessorNumber(Core::System & system)1196 static u32 GetCurrentProcessorNumber(Core::System& system) {
1197     LOG_TRACE(Kernel_SVC, "called");
1198     return static_cast<u32>(system.CurrentPhysicalCore().CoreIndex());
1199 }
1200 
GetCurrentProcessorNumber32(Core::System & system)1201 static u32 GetCurrentProcessorNumber32(Core::System& system) {
1202     return GetCurrentProcessorNumber(system);
1203 }
1204 
MapSharedMemory(Core::System & system,Handle shared_memory_handle,VAddr addr,u64 size,u32 permissions)1205 static ResultCode MapSharedMemory(Core::System& system, Handle shared_memory_handle, VAddr addr,
1206                                   u64 size, u32 permissions) {
1207     std::lock_guard lock{HLE::g_hle_lock};
1208     LOG_TRACE(Kernel_SVC,
1209               "called, shared_memory_handle=0x{:X}, addr=0x{:X}, size=0x{:X}, permissions=0x{:08X}",
1210               shared_memory_handle, addr, size, permissions);
1211 
1212     if (!Common::Is4KBAligned(addr)) {
1213         LOG_ERROR(Kernel_SVC, "Address is not aligned to 4KB, addr=0x{:016X}", addr);
1214         return ERR_INVALID_ADDRESS;
1215     }
1216 
1217     if (size == 0) {
1218         LOG_ERROR(Kernel_SVC, "Size is 0");
1219         return ERR_INVALID_SIZE;
1220     }
1221 
1222     if (!Common::Is4KBAligned(size)) {
1223         LOG_ERROR(Kernel_SVC, "Size is not aligned to 4KB, size=0x{:016X}", size);
1224         return ERR_INVALID_SIZE;
1225     }
1226 
1227     if (!IsValidAddressRange(addr, size)) {
1228         LOG_ERROR(Kernel_SVC, "Region is not a valid address range, addr=0x{:016X}, size=0x{:016X}",
1229                   addr, size);
1230         return ERR_INVALID_ADDRESS_STATE;
1231     }
1232 
1233     const auto permission_type = static_cast<Memory::MemoryPermission>(permissions);
1234     if ((permission_type | Memory::MemoryPermission::Write) !=
1235         Memory::MemoryPermission::ReadAndWrite) {
1236         LOG_ERROR(Kernel_SVC, "Expected Read or ReadWrite permission but got permissions=0x{:08X}",
1237                   permissions);
1238         return ERR_INVALID_MEMORY_PERMISSIONS;
1239     }
1240 
1241     auto* const current_process{system.Kernel().CurrentProcess()};
1242     auto& page_table{current_process->PageTable()};
1243 
1244     if (page_table.IsInvalidRegion(addr, size)) {
1245         LOG_ERROR(Kernel_SVC,
1246                   "Addr does not fit within the valid region, addr=0x{:016X}, "
1247                   "size=0x{:016X}",
1248                   addr, size);
1249         return ERR_INVALID_MEMORY_RANGE;
1250     }
1251 
1252     if (page_table.IsInsideHeapRegion(addr, size)) {
1253         LOG_ERROR(Kernel_SVC,
1254                   "Addr does not fit within the heap region, addr=0x{:016X}, "
1255                   "size=0x{:016X}",
1256                   addr, size);
1257         return ERR_INVALID_MEMORY_RANGE;
1258     }
1259 
1260     if (page_table.IsInsideAliasRegion(addr, size)) {
1261         LOG_ERROR(Kernel_SVC,
1262                   "Address does not fit within the map region, addr=0x{:016X}, "
1263                   "size=0x{:016X}",
1264                   addr, size);
1265         return ERR_INVALID_MEMORY_RANGE;
1266     }
1267 
1268     auto shared_memory{current_process->GetHandleTable().Get<SharedMemory>(shared_memory_handle)};
1269     if (!shared_memory) {
1270         LOG_ERROR(Kernel_SVC, "Shared memory does not exist, shared_memory_handle=0x{:08X}",
1271                   shared_memory_handle);
1272         return ERR_INVALID_HANDLE;
1273     }
1274 
1275     return shared_memory->Map(*current_process, addr, size, permission_type);
1276 }
1277 
MapSharedMemory32(Core::System & system,Handle shared_memory_handle,u32 addr,u32 size,u32 permissions)1278 static ResultCode MapSharedMemory32(Core::System& system, Handle shared_memory_handle, u32 addr,
1279                                     u32 size, u32 permissions) {
1280     return MapSharedMemory(system, shared_memory_handle, addr, size, permissions);
1281 }
1282 
QueryProcessMemory(Core::System & system,VAddr memory_info_address,VAddr page_info_address,Handle process_handle,VAddr address)1283 static ResultCode QueryProcessMemory(Core::System& system, VAddr memory_info_address,
1284                                      VAddr page_info_address, Handle process_handle,
1285                                      VAddr address) {
1286     std::lock_guard lock{HLE::g_hle_lock};
1287     LOG_TRACE(Kernel_SVC, "called process=0x{:08X} address={:X}", process_handle, address);
1288     const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
1289     std::shared_ptr<Process> process = handle_table.Get<Process>(process_handle);
1290     if (!process) {
1291         LOG_ERROR(Kernel_SVC, "Process handle does not exist, process_handle=0x{:08X}",
1292                   process_handle);
1293         return ERR_INVALID_HANDLE;
1294     }
1295 
1296     auto& memory{system.Memory()};
1297     const auto memory_info{process->PageTable().QueryInfo(address).GetSvcMemoryInfo()};
1298 
1299     memory.Write64(memory_info_address + 0x00, memory_info.addr);
1300     memory.Write64(memory_info_address + 0x08, memory_info.size);
1301     memory.Write32(memory_info_address + 0x10, static_cast<u32>(memory_info.state) & 0xff);
1302     memory.Write32(memory_info_address + 0x14, static_cast<u32>(memory_info.attr));
1303     memory.Write32(memory_info_address + 0x18, static_cast<u32>(memory_info.perm));
1304     memory.Write32(memory_info_address + 0x1c, memory_info.ipc_refcount);
1305     memory.Write32(memory_info_address + 0x20, memory_info.device_refcount);
1306     memory.Write32(memory_info_address + 0x24, 0);
1307 
1308     // Page info appears to be currently unused by the kernel and is always set to zero.
1309     memory.Write32(page_info_address, 0);
1310 
1311     return RESULT_SUCCESS;
1312 }
1313 
QueryMemory(Core::System & system,VAddr memory_info_address,VAddr page_info_address,VAddr query_address)1314 static ResultCode QueryMemory(Core::System& system, VAddr memory_info_address,
1315                               VAddr page_info_address, VAddr query_address) {
1316     LOG_TRACE(Kernel_SVC,
1317               "called, memory_info_address=0x{:016X}, page_info_address=0x{:016X}, "
1318               "query_address=0x{:016X}",
1319               memory_info_address, page_info_address, query_address);
1320 
1321     return QueryProcessMemory(system, memory_info_address, page_info_address, CurrentProcess,
1322                               query_address);
1323 }
1324 
QueryMemory32(Core::System & system,u32 memory_info_address,u32 page_info_address,u32 query_address)1325 static ResultCode QueryMemory32(Core::System& system, u32 memory_info_address,
1326                                 u32 page_info_address, u32 query_address) {
1327     return QueryMemory(system, memory_info_address, page_info_address, query_address);
1328 }
1329 
MapProcessCodeMemory(Core::System & system,Handle process_handle,u64 dst_address,u64 src_address,u64 size)1330 static ResultCode MapProcessCodeMemory(Core::System& system, Handle process_handle, u64 dst_address,
1331                                        u64 src_address, u64 size) {
1332     LOG_DEBUG(Kernel_SVC,
1333               "called. process_handle=0x{:08X}, dst_address=0x{:016X}, "
1334               "src_address=0x{:016X}, size=0x{:016X}",
1335               process_handle, dst_address, src_address, size);
1336 
1337     if (!Common::Is4KBAligned(src_address)) {
1338         LOG_ERROR(Kernel_SVC, "src_address is not page-aligned (src_address=0x{:016X}).",
1339                   src_address);
1340         return ERR_INVALID_ADDRESS;
1341     }
1342 
1343     if (!Common::Is4KBAligned(dst_address)) {
1344         LOG_ERROR(Kernel_SVC, "dst_address is not page-aligned (dst_address=0x{:016X}).",
1345                   dst_address);
1346         return ERR_INVALID_ADDRESS;
1347     }
1348 
1349     if (size == 0 || !Common::Is4KBAligned(size)) {
1350         LOG_ERROR(Kernel_SVC, "Size is zero or not page-aligned (size=0x{:016X})", size);
1351         return ERR_INVALID_SIZE;
1352     }
1353 
1354     if (!IsValidAddressRange(dst_address, size)) {
1355         LOG_ERROR(Kernel_SVC,
1356                   "Destination address range overflows the address space (dst_address=0x{:016X}, "
1357                   "size=0x{:016X}).",
1358                   dst_address, size);
1359         return ERR_INVALID_ADDRESS_STATE;
1360     }
1361 
1362     if (!IsValidAddressRange(src_address, size)) {
1363         LOG_ERROR(Kernel_SVC,
1364                   "Source address range overflows the address space (src_address=0x{:016X}, "
1365                   "size=0x{:016X}).",
1366                   src_address, size);
1367         return ERR_INVALID_ADDRESS_STATE;
1368     }
1369 
1370     const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
1371     auto process = handle_table.Get<Process>(process_handle);
1372     if (!process) {
1373         LOG_ERROR(Kernel_SVC, "Invalid process handle specified (handle=0x{:08X}).",
1374                   process_handle);
1375         return ERR_INVALID_HANDLE;
1376     }
1377 
1378     auto& page_table = process->PageTable();
1379     if (!page_table.IsInsideAddressSpace(src_address, size)) {
1380         LOG_ERROR(Kernel_SVC,
1381                   "Source address range is not within the address space (src_address=0x{:016X}, "
1382                   "size=0x{:016X}).",
1383                   src_address, size);
1384         return ERR_INVALID_ADDRESS_STATE;
1385     }
1386 
1387     if (!page_table.IsInsideASLRRegion(dst_address, size)) {
1388         LOG_ERROR(Kernel_SVC,
1389                   "Destination address range is not within the ASLR region (dst_address=0x{:016X}, "
1390                   "size=0x{:016X}).",
1391                   dst_address, size);
1392         return ERR_INVALID_MEMORY_RANGE;
1393     }
1394 
1395     return page_table.MapProcessCodeMemory(dst_address, src_address, size);
1396 }
1397 
UnmapProcessCodeMemory(Core::System & system,Handle process_handle,u64 dst_address,u64 src_address,u64 size)1398 static ResultCode UnmapProcessCodeMemory(Core::System& system, Handle process_handle,
1399                                          u64 dst_address, u64 src_address, u64 size) {
1400     LOG_DEBUG(Kernel_SVC,
1401               "called. process_handle=0x{:08X}, dst_address=0x{:016X}, src_address=0x{:016X}, "
1402               "size=0x{:016X}",
1403               process_handle, dst_address, src_address, size);
1404 
1405     if (!Common::Is4KBAligned(dst_address)) {
1406         LOG_ERROR(Kernel_SVC, "dst_address is not page-aligned (dst_address=0x{:016X}).",
1407                   dst_address);
1408         return ERR_INVALID_ADDRESS;
1409     }
1410 
1411     if (!Common::Is4KBAligned(src_address)) {
1412         LOG_ERROR(Kernel_SVC, "src_address is not page-aligned (src_address=0x{:016X}).",
1413                   src_address);
1414         return ERR_INVALID_ADDRESS;
1415     }
1416 
1417     if (size == 0 || Common::Is4KBAligned(size)) {
1418         LOG_ERROR(Kernel_SVC, "Size is zero or not page-aligned (size=0x{:016X}).", size);
1419         return ERR_INVALID_SIZE;
1420     }
1421 
1422     if (!IsValidAddressRange(dst_address, size)) {
1423         LOG_ERROR(Kernel_SVC,
1424                   "Destination address range overflows the address space (dst_address=0x{:016X}, "
1425                   "size=0x{:016X}).",
1426                   dst_address, size);
1427         return ERR_INVALID_ADDRESS_STATE;
1428     }
1429 
1430     if (!IsValidAddressRange(src_address, size)) {
1431         LOG_ERROR(Kernel_SVC,
1432                   "Source address range overflows the address space (src_address=0x{:016X}, "
1433                   "size=0x{:016X}).",
1434                   src_address, size);
1435         return ERR_INVALID_ADDRESS_STATE;
1436     }
1437 
1438     const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
1439     auto process = handle_table.Get<Process>(process_handle);
1440     if (!process) {
1441         LOG_ERROR(Kernel_SVC, "Invalid process handle specified (handle=0x{:08X}).",
1442                   process_handle);
1443         return ERR_INVALID_HANDLE;
1444     }
1445 
1446     auto& page_table = process->PageTable();
1447     if (!page_table.IsInsideAddressSpace(src_address, size)) {
1448         LOG_ERROR(Kernel_SVC,
1449                   "Source address range is not within the address space (src_address=0x{:016X}, "
1450                   "size=0x{:016X}).",
1451                   src_address, size);
1452         return ERR_INVALID_ADDRESS_STATE;
1453     }
1454 
1455     if (!page_table.IsInsideASLRRegion(dst_address, size)) {
1456         LOG_ERROR(Kernel_SVC,
1457                   "Destination address range is not within the ASLR region (dst_address=0x{:016X}, "
1458                   "size=0x{:016X}).",
1459                   dst_address, size);
1460         return ERR_INVALID_MEMORY_RANGE;
1461     }
1462 
1463     return page_table.UnmapProcessCodeMemory(dst_address, src_address, size);
1464 }
1465 
1466 /// Exits the current process
ExitProcess(Core::System & system)1467 static void ExitProcess(Core::System& system) {
1468     auto* current_process = system.Kernel().CurrentProcess();
1469     UNIMPLEMENTED();
1470 
1471     LOG_INFO(Kernel_SVC, "Process {} exiting", current_process->GetProcessID());
1472     ASSERT_MSG(current_process->GetStatus() == ProcessStatus::Running,
1473                "Process has already exited");
1474 
1475     current_process->PrepareForTermination();
1476 
1477     // Kill the current thread
1478     system.CurrentScheduler().GetCurrentThread()->Stop();
1479 }
1480 
ExitProcess32(Core::System & system)1481 static void ExitProcess32(Core::System& system) {
1482     ExitProcess(system);
1483 }
1484 
1485 /// Creates a new thread
CreateThread(Core::System & system,Handle * out_handle,VAddr entry_point,u64 arg,VAddr stack_top,u32 priority,s32 processor_id)1486 static ResultCode CreateThread(Core::System& system, Handle* out_handle, VAddr entry_point, u64 arg,
1487                                VAddr stack_top, u32 priority, s32 processor_id) {
1488     LOG_DEBUG(Kernel_SVC,
1489               "called entrypoint=0x{:08X}, arg=0x{:08X}, stacktop=0x{:08X}, "
1490               "threadpriority=0x{:08X}, processorid=0x{:08X} : created handle=0x{:08X}",
1491               entry_point, arg, stack_top, priority, processor_id, *out_handle);
1492 
1493     auto* const current_process = system.Kernel().CurrentProcess();
1494 
1495     if (processor_id == THREADPROCESSORID_IDEAL) {
1496         // Set the target CPU to the one specified by the process.
1497         processor_id = current_process->GetIdealCore();
1498         ASSERT(processor_id != THREADPROCESSORID_IDEAL);
1499     }
1500 
1501     if (processor_id < THREADPROCESSORID_0 || processor_id > THREADPROCESSORID_3) {
1502         LOG_ERROR(Kernel_SVC, "Invalid thread processor ID: {}", processor_id);
1503         return ERR_INVALID_PROCESSOR_ID;
1504     }
1505 
1506     const u64 core_mask = current_process->GetCoreMask();
1507     if ((core_mask | (1ULL << processor_id)) != core_mask) {
1508         LOG_ERROR(Kernel_SVC, "Invalid thread core specified ({})", processor_id);
1509         return ERR_INVALID_PROCESSOR_ID;
1510     }
1511 
1512     if (priority > THREADPRIO_LOWEST) {
1513         LOG_ERROR(Kernel_SVC,
1514                   "Invalid thread priority specified ({}). Must be within the range 0-64",
1515                   priority);
1516         return ERR_INVALID_THREAD_PRIORITY;
1517     }
1518 
1519     if (((1ULL << priority) & current_process->GetPriorityMask()) == 0) {
1520         LOG_ERROR(Kernel_SVC, "Invalid thread priority specified ({})", priority);
1521         return ERR_INVALID_THREAD_PRIORITY;
1522     }
1523 
1524     auto& kernel = system.Kernel();
1525 
1526     ASSERT(kernel.CurrentProcess()->GetResourceLimit()->Reserve(ResourceType::Threads, 1));
1527 
1528     ThreadType type = THREADTYPE_USER;
1529     CASCADE_RESULT(std::shared_ptr<Thread> thread,
1530                    Thread::Create(system, type, "", entry_point, priority, arg, processor_id,
1531                                   stack_top, current_process));
1532 
1533     const auto new_thread_handle = current_process->GetHandleTable().Create(thread);
1534     if (new_thread_handle.Failed()) {
1535         LOG_ERROR(Kernel_SVC, "Failed to create handle with error=0x{:X}",
1536                   new_thread_handle.Code().raw);
1537         return new_thread_handle.Code();
1538     }
1539     *out_handle = *new_thread_handle;
1540 
1541     // Set the thread name for debugging purposes.
1542     thread->SetName(
1543         fmt::format("thread[entry_point={:X}, handle={:X}]", entry_point, *new_thread_handle));
1544 
1545     return RESULT_SUCCESS;
1546 }
1547 
CreateThread32(Core::System & system,Handle * out_handle,u32 priority,u32 entry_point,u32 arg,u32 stack_top,s32 processor_id)1548 static ResultCode CreateThread32(Core::System& system, Handle* out_handle, u32 priority,
1549                                  u32 entry_point, u32 arg, u32 stack_top, s32 processor_id) {
1550     return CreateThread(system, out_handle, entry_point, arg, stack_top, priority, processor_id);
1551 }
1552 
1553 /// Starts the thread for the provided handle
StartThread(Core::System & system,Handle thread_handle)1554 static ResultCode StartThread(Core::System& system, Handle thread_handle) {
1555     LOG_DEBUG(Kernel_SVC, "called thread=0x{:08X}", thread_handle);
1556 
1557     const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
1558     const std::shared_ptr<Thread> thread = handle_table.Get<Thread>(thread_handle);
1559     if (!thread) {
1560         LOG_ERROR(Kernel_SVC, "Thread handle does not exist, thread_handle=0x{:08X}",
1561                   thread_handle);
1562         return ERR_INVALID_HANDLE;
1563     }
1564 
1565     ASSERT(thread->GetStatus() == ThreadStatus::Dormant);
1566 
1567     return thread->Start();
1568 }
1569 
StartThread32(Core::System & system,Handle thread_handle)1570 static ResultCode StartThread32(Core::System& system, Handle thread_handle) {
1571     return StartThread(system, thread_handle);
1572 }
1573 
1574 /// Called when a thread exits
ExitThread(Core::System & system)1575 static void ExitThread(Core::System& system) {
1576     LOG_DEBUG(Kernel_SVC, "called, pc=0x{:08X}", system.CurrentArmInterface().GetPC());
1577 
1578     auto* const current_thread = system.CurrentScheduler().GetCurrentThread();
1579     system.GlobalScheduler().RemoveThread(SharedFrom(current_thread));
1580     current_thread->Stop();
1581 }
1582 
ExitThread32(Core::System & system)1583 static void ExitThread32(Core::System& system) {
1584     ExitThread(system);
1585 }
1586 
1587 /// Sleep the current thread
SleepThread(Core::System & system,s64 nanoseconds)1588 static void SleepThread(Core::System& system, s64 nanoseconds) {
1589     LOG_DEBUG(Kernel_SVC, "called nanoseconds={}", nanoseconds);
1590 
1591     enum class SleepType : s64 {
1592         YieldWithoutLoadBalancing = 0,
1593         YieldWithLoadBalancing = -1,
1594         YieldAndWaitForLoadBalancing = -2,
1595     };
1596 
1597     auto& scheduler = system.CurrentScheduler();
1598     auto* const current_thread = scheduler.GetCurrentThread();
1599     bool is_redundant = false;
1600 
1601     if (nanoseconds <= 0) {
1602         switch (static_cast<SleepType>(nanoseconds)) {
1603         case SleepType::YieldWithoutLoadBalancing: {
1604             auto pair = current_thread->YieldSimple();
1605             is_redundant = pair.second;
1606             break;
1607         }
1608         case SleepType::YieldWithLoadBalancing: {
1609             auto pair = current_thread->YieldAndBalanceLoad();
1610             is_redundant = pair.second;
1611             break;
1612         }
1613         case SleepType::YieldAndWaitForLoadBalancing: {
1614             auto pair = current_thread->YieldAndWaitForLoadBalancing();
1615             is_redundant = pair.second;
1616             break;
1617         }
1618         default:
1619             UNREACHABLE_MSG("Unimplemented sleep yield type '{:016X}'!", nanoseconds);
1620         }
1621     } else {
1622         current_thread->Sleep(nanoseconds);
1623     }
1624 
1625     if (is_redundant && !system.Kernel().IsMulticore()) {
1626         system.Kernel().ExitSVCProfile();
1627         system.CoreTiming().AddTicks(1000U);
1628         system.GetCpuManager().PreemptSingleCore();
1629         system.Kernel().EnterSVCProfile();
1630     }
1631 }
1632 
SleepThread32(Core::System & system,u32 nanoseconds_low,u32 nanoseconds_high)1633 static void SleepThread32(Core::System& system, u32 nanoseconds_low, u32 nanoseconds_high) {
1634     const auto nanoseconds = static_cast<s64>(u64{nanoseconds_low} | (u64{nanoseconds_high} << 32));
1635     SleepThread(system, nanoseconds);
1636 }
1637 
1638 /// Wait process wide key atomic
WaitProcessWideKeyAtomic(Core::System & system,VAddr mutex_addr,VAddr condition_variable_addr,Handle thread_handle,s64 nano_seconds)1639 static ResultCode WaitProcessWideKeyAtomic(Core::System& system, VAddr mutex_addr,
1640                                            VAddr condition_variable_addr, Handle thread_handle,
1641                                            s64 nano_seconds) {
1642     LOG_TRACE(
1643         Kernel_SVC,
1644         "called mutex_addr={:X}, condition_variable_addr={:X}, thread_handle=0x{:08X}, timeout={}",
1645         mutex_addr, condition_variable_addr, thread_handle, nano_seconds);
1646 
1647     if (Core::Memory::IsKernelVirtualAddress(mutex_addr)) {
1648         LOG_ERROR(
1649             Kernel_SVC,
1650             "Given mutex address must not be within the kernel address space. address=0x{:016X}",
1651             mutex_addr);
1652         return ERR_INVALID_ADDRESS_STATE;
1653     }
1654 
1655     if (!Common::IsWordAligned(mutex_addr)) {
1656         LOG_ERROR(Kernel_SVC, "Given mutex address must be word-aligned. address=0x{:016X}",
1657                   mutex_addr);
1658         return ERR_INVALID_ADDRESS;
1659     }
1660 
1661     ASSERT(condition_variable_addr == Common::AlignDown(condition_variable_addr, 4));
1662     auto& kernel = system.Kernel();
1663     Handle event_handle;
1664     Thread* current_thread = system.CurrentScheduler().GetCurrentThread();
1665     auto* const current_process = system.Kernel().CurrentProcess();
1666     {
1667         SchedulerLockAndSleep lock(kernel, event_handle, current_thread, nano_seconds);
1668         const auto& handle_table = current_process->GetHandleTable();
1669         std::shared_ptr<Thread> thread = handle_table.Get<Thread>(thread_handle);
1670         ASSERT(thread);
1671 
1672         current_thread->SetSynchronizationResults(nullptr, RESULT_TIMEOUT);
1673 
1674         if (thread->IsPendingTermination()) {
1675             lock.CancelSleep();
1676             return ERR_THREAD_TERMINATING;
1677         }
1678 
1679         const auto release_result = current_process->GetMutex().Release(mutex_addr);
1680         if (release_result.IsError()) {
1681             lock.CancelSleep();
1682             return release_result;
1683         }
1684 
1685         if (nano_seconds == 0) {
1686             lock.CancelSleep();
1687             return RESULT_TIMEOUT;
1688         }
1689 
1690         current_thread->SetCondVarWaitAddress(condition_variable_addr);
1691         current_thread->SetMutexWaitAddress(mutex_addr);
1692         current_thread->SetWaitHandle(thread_handle);
1693         current_thread->SetStatus(ThreadStatus::WaitCondVar);
1694         current_process->InsertConditionVariableThread(SharedFrom(current_thread));
1695     }
1696 
1697     if (event_handle != InvalidHandle) {
1698         auto& time_manager = kernel.TimeManager();
1699         time_manager.UnscheduleTimeEvent(event_handle);
1700     }
1701 
1702     {
1703         SchedulerLock lock(kernel);
1704 
1705         auto* owner = current_thread->GetLockOwner();
1706         if (owner != nullptr) {
1707             owner->RemoveMutexWaiter(SharedFrom(current_thread));
1708         }
1709 
1710         current_process->RemoveConditionVariableThread(SharedFrom(current_thread));
1711     }
1712     // Note: Deliberately don't attempt to inherit the lock owner's priority.
1713 
1714     return current_thread->GetSignalingResult();
1715 }
1716 
WaitProcessWideKeyAtomic32(Core::System & system,u32 mutex_addr,u32 condition_variable_addr,Handle thread_handle,u32 nanoseconds_low,u32 nanoseconds_high)1717 static ResultCode WaitProcessWideKeyAtomic32(Core::System& system, u32 mutex_addr,
1718                                              u32 condition_variable_addr, Handle thread_handle,
1719                                              u32 nanoseconds_low, u32 nanoseconds_high) {
1720     const auto nanoseconds = static_cast<s64>(nanoseconds_low | (u64{nanoseconds_high} << 32));
1721     return WaitProcessWideKeyAtomic(system, mutex_addr, condition_variable_addr, thread_handle,
1722                                     nanoseconds);
1723 }
1724 
1725 /// Signal process wide key
SignalProcessWideKey(Core::System & system,VAddr condition_variable_addr,s32 target)1726 static void SignalProcessWideKey(Core::System& system, VAddr condition_variable_addr, s32 target) {
1727     LOG_TRACE(Kernel_SVC, "called, condition_variable_addr=0x{:X}, target=0x{:08X}",
1728               condition_variable_addr, target);
1729 
1730     ASSERT(condition_variable_addr == Common::AlignDown(condition_variable_addr, 4));
1731 
1732     // Retrieve a list of all threads that are waiting for this condition variable.
1733     auto& kernel = system.Kernel();
1734     SchedulerLock lock(kernel);
1735     auto* const current_process = kernel.CurrentProcess();
1736     std::vector<std::shared_ptr<Thread>> waiting_threads =
1737         current_process->GetConditionVariableThreads(condition_variable_addr);
1738 
1739     // Only process up to 'target' threads, unless 'target' is less equal 0, in which case process
1740     // them all.
1741     std::size_t last = waiting_threads.size();
1742     if (target > 0) {
1743         last = std::min(waiting_threads.size(), static_cast<std::size_t>(target));
1744     }
1745     for (std::size_t index = 0; index < last; ++index) {
1746         auto& thread = waiting_threads[index];
1747 
1748         ASSERT(thread->GetCondVarWaitAddress() == condition_variable_addr);
1749 
1750         // liberate Cond Var Thread.
1751         current_process->RemoveConditionVariableThread(thread);
1752 
1753         const std::size_t current_core = system.CurrentCoreIndex();
1754         auto& monitor = system.Monitor();
1755 
1756         // Atomically read the value of the mutex.
1757         u32 mutex_val = 0;
1758         u32 update_val = 0;
1759         const VAddr mutex_address = thread->GetMutexWaitAddress();
1760         do {
1761             // If the mutex is not yet acquired, acquire it.
1762             mutex_val = monitor.ExclusiveRead32(current_core, mutex_address);
1763 
1764             if (mutex_val != 0) {
1765                 update_val = mutex_val | Mutex::MutexHasWaitersFlag;
1766             } else {
1767                 update_val = thread->GetWaitHandle();
1768             }
1769         } while (!monitor.ExclusiveWrite32(current_core, mutex_address, update_val));
1770         monitor.ClearExclusive();
1771         if (mutex_val == 0) {
1772             // We were able to acquire the mutex, resume this thread.
1773             auto* const lock_owner = thread->GetLockOwner();
1774             if (lock_owner != nullptr) {
1775                 lock_owner->RemoveMutexWaiter(thread);
1776             }
1777 
1778             thread->SetLockOwner(nullptr);
1779             thread->SetSynchronizationResults(nullptr, RESULT_SUCCESS);
1780             thread->ResumeFromWait();
1781         } else {
1782             // The mutex is already owned by some other thread, make this thread wait on it.
1783             const Handle owner_handle = static_cast<Handle>(mutex_val & Mutex::MutexOwnerMask);
1784             const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
1785             auto owner = handle_table.Get<Thread>(owner_handle);
1786             ASSERT(owner);
1787             if (thread->GetStatus() == ThreadStatus::WaitCondVar) {
1788                 thread->SetStatus(ThreadStatus::WaitMutex);
1789             }
1790 
1791             owner->AddMutexWaiter(thread);
1792         }
1793     }
1794 }
1795 
SignalProcessWideKey32(Core::System & system,u32 condition_variable_addr,s32 target)1796 static void SignalProcessWideKey32(Core::System& system, u32 condition_variable_addr, s32 target) {
1797     SignalProcessWideKey(system, condition_variable_addr, target);
1798 }
1799 
1800 // Wait for an address (via Address Arbiter)
WaitForAddress(Core::System & system,VAddr address,u32 type,s32 value,s64 timeout)1801 static ResultCode WaitForAddress(Core::System& system, VAddr address, u32 type, s32 value,
1802                                  s64 timeout) {
1803     LOG_TRACE(Kernel_SVC, "called, address=0x{:X}, type=0x{:X}, value=0x{:X}, timeout={}", address,
1804               type, value, timeout);
1805 
1806     // If the passed address is a kernel virtual address, return invalid memory state.
1807     if (Core::Memory::IsKernelVirtualAddress(address)) {
1808         LOG_ERROR(Kernel_SVC, "Address is a kernel virtual address, address={:016X}", address);
1809         return ERR_INVALID_ADDRESS_STATE;
1810     }
1811 
1812     // If the address is not properly aligned to 4 bytes, return invalid address.
1813     if (!Common::IsWordAligned(address)) {
1814         LOG_ERROR(Kernel_SVC, "Address is not word aligned, address={:016X}", address);
1815         return ERR_INVALID_ADDRESS;
1816     }
1817 
1818     const auto arbitration_type = static_cast<AddressArbiter::ArbitrationType>(type);
1819     auto& address_arbiter = system.Kernel().CurrentProcess()->GetAddressArbiter();
1820     const ResultCode result =
1821         address_arbiter.WaitForAddress(address, arbitration_type, value, timeout);
1822     return result;
1823 }
1824 
WaitForAddress32(Core::System & system,u32 address,u32 type,s32 value,u32 timeout_low,u32 timeout_high)1825 static ResultCode WaitForAddress32(Core::System& system, u32 address, u32 type, s32 value,
1826                                    u32 timeout_low, u32 timeout_high) {
1827     const auto timeout = static_cast<s64>(timeout_low | (u64{timeout_high} << 32));
1828     return WaitForAddress(system, address, type, value, timeout);
1829 }
1830 
1831 // Signals to an address (via Address Arbiter)
SignalToAddress(Core::System & system,VAddr address,u32 type,s32 value,s32 num_to_wake)1832 static ResultCode SignalToAddress(Core::System& system, VAddr address, u32 type, s32 value,
1833                                   s32 num_to_wake) {
1834     LOG_TRACE(Kernel_SVC, "called, address=0x{:X}, type=0x{:X}, value=0x{:X}, num_to_wake=0x{:X}",
1835               address, type, value, num_to_wake);
1836 
1837     // If the passed address is a kernel virtual address, return invalid memory state.
1838     if (Core::Memory::IsKernelVirtualAddress(address)) {
1839         LOG_ERROR(Kernel_SVC, "Address is a kernel virtual address, address={:016X}", address);
1840         return ERR_INVALID_ADDRESS_STATE;
1841     }
1842 
1843     // If the address is not properly aligned to 4 bytes, return invalid address.
1844     if (!Common::IsWordAligned(address)) {
1845         LOG_ERROR(Kernel_SVC, "Address is not word aligned, address={:016X}", address);
1846         return ERR_INVALID_ADDRESS;
1847     }
1848 
1849     const auto signal_type = static_cast<AddressArbiter::SignalType>(type);
1850     auto& address_arbiter = system.Kernel().CurrentProcess()->GetAddressArbiter();
1851     return address_arbiter.SignalToAddress(address, signal_type, value, num_to_wake);
1852 }
1853 
SignalToAddress32(Core::System & system,u32 address,u32 type,s32 value,s32 num_to_wake)1854 static ResultCode SignalToAddress32(Core::System& system, u32 address, u32 type, s32 value,
1855                                     s32 num_to_wake) {
1856     return SignalToAddress(system, address, type, value, num_to_wake);
1857 }
1858 
KernelDebug(Core::System & system,u32 kernel_debug_type,u64 param1,u64 param2,u64 param3)1859 static void KernelDebug([[maybe_unused]] Core::System& system,
1860                         [[maybe_unused]] u32 kernel_debug_type, [[maybe_unused]] u64 param1,
1861                         [[maybe_unused]] u64 param2, [[maybe_unused]] u64 param3) {
1862     // Intentionally do nothing, as this does nothing in released kernel binaries.
1863 }
1864 
ChangeKernelTraceState(Core::System & system,u32 trace_state)1865 static void ChangeKernelTraceState([[maybe_unused]] Core::System& system,
1866                                    [[maybe_unused]] u32 trace_state) {
1867     // Intentionally do nothing, as this does nothing in released kernel binaries.
1868 }
1869 
1870 /// This returns the total CPU ticks elapsed since the CPU was powered-on
GetSystemTick(Core::System & system)1871 static u64 GetSystemTick(Core::System& system) {
1872     LOG_TRACE(Kernel_SVC, "called");
1873 
1874     auto& core_timing = system.CoreTiming();
1875 
1876     // Returns the value of cntpct_el0 (https://switchbrew.org/wiki/SVC#svcGetSystemTick)
1877     const u64 result{system.CoreTiming().GetClockTicks()};
1878 
1879     if (!system.Kernel().IsMulticore()) {
1880         core_timing.AddTicks(400U);
1881     }
1882 
1883     return result;
1884 }
1885 
GetSystemTick32(Core::System & system,u32 * time_low,u32 * time_high)1886 static void GetSystemTick32(Core::System& system, u32* time_low, u32* time_high) {
1887     const auto time = GetSystemTick(system);
1888     *time_low = static_cast<u32>(time);
1889     *time_high = static_cast<u32>(time >> 32);
1890 }
1891 
1892 /// Close a handle
CloseHandle(Core::System & system,Handle handle)1893 static ResultCode CloseHandle(Core::System& system, Handle handle) {
1894     LOG_TRACE(Kernel_SVC, "Closing handle 0x{:08X}", handle);
1895 
1896     auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
1897     return handle_table.Close(handle);
1898 }
1899 
CloseHandle32(Core::System & system,Handle handle)1900 static ResultCode CloseHandle32(Core::System& system, Handle handle) {
1901     return CloseHandle(system, handle);
1902 }
1903 
1904 /// Clears the signaled state of an event or process.
ResetSignal(Core::System & system,Handle handle)1905 static ResultCode ResetSignal(Core::System& system, Handle handle) {
1906     LOG_DEBUG(Kernel_SVC, "called handle 0x{:08X}", handle);
1907 
1908     const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
1909 
1910     auto event = handle_table.Get<ReadableEvent>(handle);
1911     if (event) {
1912         return event->Reset();
1913     }
1914 
1915     auto process = handle_table.Get<Process>(handle);
1916     if (process) {
1917         return process->ClearSignalState();
1918     }
1919 
1920     LOG_ERROR(Kernel_SVC, "Invalid handle (0x{:08X})", handle);
1921     return ERR_INVALID_HANDLE;
1922 }
1923 
ResetSignal32(Core::System & system,Handle handle)1924 static ResultCode ResetSignal32(Core::System& system, Handle handle) {
1925     return ResetSignal(system, handle);
1926 }
1927 
1928 /// Creates a TransferMemory object
CreateTransferMemory(Core::System & system,Handle * handle,VAddr addr,u64 size,u32 permissions)1929 static ResultCode CreateTransferMemory(Core::System& system, Handle* handle, VAddr addr, u64 size,
1930                                        u32 permissions) {
1931     std::lock_guard lock{HLE::g_hle_lock};
1932     LOG_DEBUG(Kernel_SVC, "called addr=0x{:X}, size=0x{:X}, perms=0x{:08X}", addr, size,
1933               permissions);
1934 
1935     if (!Common::Is4KBAligned(addr)) {
1936         LOG_ERROR(Kernel_SVC, "Address ({:016X}) is not page aligned!", addr);
1937         return ERR_INVALID_ADDRESS;
1938     }
1939 
1940     if (!Common::Is4KBAligned(size) || size == 0) {
1941         LOG_ERROR(Kernel_SVC, "Size ({:016X}) is not page aligned or equal to zero!", size);
1942         return ERR_INVALID_ADDRESS;
1943     }
1944 
1945     if (!IsValidAddressRange(addr, size)) {
1946         LOG_ERROR(Kernel_SVC, "Address and size cause overflow! (address={:016X}, size={:016X})",
1947                   addr, size);
1948         return ERR_INVALID_ADDRESS_STATE;
1949     }
1950 
1951     const auto perms{static_cast<Memory::MemoryPermission>(permissions)};
1952     if (perms > Memory::MemoryPermission::ReadAndWrite ||
1953         perms == Memory::MemoryPermission::Write) {
1954         LOG_ERROR(Kernel_SVC, "Invalid memory permissions for transfer memory! (perms={:08X})",
1955                   permissions);
1956         return ERR_INVALID_MEMORY_PERMISSIONS;
1957     }
1958 
1959     auto& kernel = system.Kernel();
1960     auto transfer_mem_handle = TransferMemory::Create(kernel, system.Memory(), addr, size, perms);
1961 
1962     if (const auto reserve_result{transfer_mem_handle->Reserve()}; reserve_result.IsError()) {
1963         return reserve_result;
1964     }
1965 
1966     auto& handle_table = kernel.CurrentProcess()->GetHandleTable();
1967     const auto result{handle_table.Create(std::move(transfer_mem_handle))};
1968     if (result.Failed()) {
1969         return result.Code();
1970     }
1971 
1972     *handle = *result;
1973     return RESULT_SUCCESS;
1974 }
1975 
CreateTransferMemory32(Core::System & system,Handle * handle,u32 addr,u32 size,u32 permissions)1976 static ResultCode CreateTransferMemory32(Core::System& system, Handle* handle, u32 addr, u32 size,
1977                                          u32 permissions) {
1978     return CreateTransferMemory(system, handle, addr, size, permissions);
1979 }
1980 
GetThreadCoreMask(Core::System & system,Handle thread_handle,u32 * core,u64 * mask)1981 static ResultCode GetThreadCoreMask(Core::System& system, Handle thread_handle, u32* core,
1982                                     u64* mask) {
1983     LOG_TRACE(Kernel_SVC, "called, handle=0x{:08X}", thread_handle);
1984 
1985     const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
1986     const std::shared_ptr<Thread> thread = handle_table.Get<Thread>(thread_handle);
1987     if (!thread) {
1988         LOG_ERROR(Kernel_SVC, "Thread handle does not exist, thread_handle=0x{:08X}",
1989                   thread_handle);
1990         *core = 0;
1991         *mask = 0;
1992         return ERR_INVALID_HANDLE;
1993     }
1994 
1995     *core = thread->GetIdealCore();
1996     *mask = thread->GetAffinityMask();
1997 
1998     return RESULT_SUCCESS;
1999 }
2000 
GetThreadCoreMask32(Core::System & system,Handle thread_handle,u32 * core,u32 * mask_low,u32 * mask_high)2001 static ResultCode GetThreadCoreMask32(Core::System& system, Handle thread_handle, u32* core,
2002                                       u32* mask_low, u32* mask_high) {
2003     u64 mask{};
2004     const auto result = GetThreadCoreMask(system, thread_handle, core, &mask);
2005     *mask_high = static_cast<u32>(mask >> 32);
2006     *mask_low = static_cast<u32>(mask);
2007     return result;
2008 }
2009 
SetThreadCoreMask(Core::System & system,Handle thread_handle,u32 core,u64 affinity_mask)2010 static ResultCode SetThreadCoreMask(Core::System& system, Handle thread_handle, u32 core,
2011                                     u64 affinity_mask) {
2012     LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, core=0x{:X}, affinity_mask=0x{:016X}",
2013               thread_handle, core, affinity_mask);
2014 
2015     const auto* const current_process = system.Kernel().CurrentProcess();
2016 
2017     if (core == static_cast<u32>(THREADPROCESSORID_IDEAL)) {
2018         const u8 ideal_cpu_core = current_process->GetIdealCore();
2019 
2020         ASSERT(ideal_cpu_core != static_cast<u8>(THREADPROCESSORID_IDEAL));
2021 
2022         // Set the target CPU to the ideal core specified by the process.
2023         core = ideal_cpu_core;
2024         affinity_mask = 1ULL << core;
2025     } else {
2026         const u64 core_mask = current_process->GetCoreMask();
2027 
2028         if ((core_mask | affinity_mask) != core_mask) {
2029             LOG_ERROR(
2030                 Kernel_SVC,
2031                 "Invalid processor ID specified (core_mask=0x{:08X}, affinity_mask=0x{:016X})",
2032                 core_mask, affinity_mask);
2033             return ERR_INVALID_PROCESSOR_ID;
2034         }
2035 
2036         if (affinity_mask == 0) {
2037             LOG_ERROR(Kernel_SVC, "Specfified affinity mask is zero.");
2038             return ERR_INVALID_COMBINATION;
2039         }
2040 
2041         if (core < Core::Hardware::NUM_CPU_CORES) {
2042             if ((affinity_mask & (1ULL << core)) == 0) {
2043                 LOG_ERROR(Kernel_SVC,
2044                           "Core is not enabled for the current mask, core={}, mask={:016X}", core,
2045                           affinity_mask);
2046                 return ERR_INVALID_COMBINATION;
2047             }
2048         } else if (core != static_cast<u32>(THREADPROCESSORID_DONT_CARE) &&
2049                    core != static_cast<u32>(THREADPROCESSORID_DONT_UPDATE)) {
2050             LOG_ERROR(Kernel_SVC, "Invalid processor ID specified (core={}).", core);
2051             return ERR_INVALID_PROCESSOR_ID;
2052         }
2053     }
2054 
2055     const auto& handle_table = current_process->GetHandleTable();
2056     const std::shared_ptr<Thread> thread = handle_table.Get<Thread>(thread_handle);
2057     if (!thread) {
2058         LOG_ERROR(Kernel_SVC, "Thread handle does not exist, thread_handle=0x{:08X}",
2059                   thread_handle);
2060         return ERR_INVALID_HANDLE;
2061     }
2062 
2063     return thread->SetCoreAndAffinityMask(core, affinity_mask);
2064 }
2065 
SetThreadCoreMask32(Core::System & system,Handle thread_handle,u32 core,u32 affinity_mask_low,u32 affinity_mask_high)2066 static ResultCode SetThreadCoreMask32(Core::System& system, Handle thread_handle, u32 core,
2067                                       u32 affinity_mask_low, u32 affinity_mask_high) {
2068     const auto affinity_mask = u64{affinity_mask_low} | (u64{affinity_mask_high} << 32);
2069     return SetThreadCoreMask(system, thread_handle, core, affinity_mask);
2070 }
2071 
CreateEvent(Core::System & system,Handle * write_handle,Handle * read_handle)2072 static ResultCode CreateEvent(Core::System& system, Handle* write_handle, Handle* read_handle) {
2073     LOG_DEBUG(Kernel_SVC, "called");
2074 
2075     auto& kernel = system.Kernel();
2076     const auto [readable_event, writable_event] =
2077         WritableEvent::CreateEventPair(kernel, "CreateEvent");
2078 
2079     HandleTable& handle_table = kernel.CurrentProcess()->GetHandleTable();
2080 
2081     const auto write_create_result = handle_table.Create(writable_event);
2082     if (write_create_result.Failed()) {
2083         return write_create_result.Code();
2084     }
2085     *write_handle = *write_create_result;
2086 
2087     const auto read_create_result = handle_table.Create(readable_event);
2088     if (read_create_result.Failed()) {
2089         handle_table.Close(*write_create_result);
2090         return read_create_result.Code();
2091     }
2092     *read_handle = *read_create_result;
2093 
2094     LOG_DEBUG(Kernel_SVC,
2095               "successful. Writable event handle=0x{:08X}, Readable event handle=0x{:08X}",
2096               *write_create_result, *read_create_result);
2097     return RESULT_SUCCESS;
2098 }
2099 
CreateEvent32(Core::System & system,Handle * write_handle,Handle * read_handle)2100 static ResultCode CreateEvent32(Core::System& system, Handle* write_handle, Handle* read_handle) {
2101     return CreateEvent(system, write_handle, read_handle);
2102 }
2103 
ClearEvent(Core::System & system,Handle handle)2104 static ResultCode ClearEvent(Core::System& system, Handle handle) {
2105     LOG_TRACE(Kernel_SVC, "called, event=0x{:08X}", handle);
2106 
2107     const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
2108 
2109     auto writable_event = handle_table.Get<WritableEvent>(handle);
2110     if (writable_event) {
2111         writable_event->Clear();
2112         return RESULT_SUCCESS;
2113     }
2114 
2115     auto readable_event = handle_table.Get<ReadableEvent>(handle);
2116     if (readable_event) {
2117         readable_event->Clear();
2118         return RESULT_SUCCESS;
2119     }
2120 
2121     LOG_ERROR(Kernel_SVC, "Event handle does not exist, handle=0x{:08X}", handle);
2122     return ERR_INVALID_HANDLE;
2123 }
2124 
ClearEvent32(Core::System & system,Handle handle)2125 static ResultCode ClearEvent32(Core::System& system, Handle handle) {
2126     return ClearEvent(system, handle);
2127 }
2128 
SignalEvent(Core::System & system,Handle handle)2129 static ResultCode SignalEvent(Core::System& system, Handle handle) {
2130     LOG_DEBUG(Kernel_SVC, "called. Handle=0x{:08X}", handle);
2131 
2132     HandleTable& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
2133     auto writable_event = handle_table.Get<WritableEvent>(handle);
2134 
2135     if (!writable_event) {
2136         LOG_ERROR(Kernel_SVC, "Non-existent writable event handle used (0x{:08X})", handle);
2137         return ERR_INVALID_HANDLE;
2138     }
2139 
2140     writable_event->Signal();
2141     return RESULT_SUCCESS;
2142 }
2143 
SignalEvent32(Core::System & system,Handle handle)2144 static ResultCode SignalEvent32(Core::System& system, Handle handle) {
2145     return SignalEvent(system, handle);
2146 }
2147 
GetProcessInfo(Core::System & system,u64 * out,Handle process_handle,u32 type)2148 static ResultCode GetProcessInfo(Core::System& system, u64* out, Handle process_handle, u32 type) {
2149     LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, type=0x{:X}", process_handle, type);
2150 
2151     // This function currently only allows retrieving a process' status.
2152     enum class InfoType {
2153         Status,
2154     };
2155 
2156     const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
2157     const auto process = handle_table.Get<Process>(process_handle);
2158     if (!process) {
2159         LOG_ERROR(Kernel_SVC, "Process handle does not exist, process_handle=0x{:08X}",
2160                   process_handle);
2161         return ERR_INVALID_HANDLE;
2162     }
2163 
2164     const auto info_type = static_cast<InfoType>(type);
2165     if (info_type != InfoType::Status) {
2166         LOG_ERROR(Kernel_SVC, "Expected info_type to be Status but got {} instead", type);
2167         return ERR_INVALID_ENUM_VALUE;
2168     }
2169 
2170     *out = static_cast<u64>(process->GetStatus());
2171     return RESULT_SUCCESS;
2172 }
2173 
CreateResourceLimit(Core::System & system,Handle * out_handle)2174 static ResultCode CreateResourceLimit(Core::System& system, Handle* out_handle) {
2175     std::lock_guard lock{HLE::g_hle_lock};
2176     LOG_DEBUG(Kernel_SVC, "called");
2177 
2178     auto& kernel = system.Kernel();
2179     auto resource_limit = ResourceLimit::Create(kernel);
2180 
2181     auto* const current_process = kernel.CurrentProcess();
2182     ASSERT(current_process != nullptr);
2183 
2184     const auto handle = current_process->GetHandleTable().Create(std::move(resource_limit));
2185     if (handle.Failed()) {
2186         return handle.Code();
2187     }
2188 
2189     *out_handle = *handle;
2190     return RESULT_SUCCESS;
2191 }
2192 
GetResourceLimitLimitValue(Core::System & system,u64 * out_value,Handle resource_limit,u32 resource_type)2193 static ResultCode GetResourceLimitLimitValue(Core::System& system, u64* out_value,
2194                                              Handle resource_limit, u32 resource_type) {
2195     LOG_DEBUG(Kernel_SVC, "called. Handle={:08X}, Resource type={}", resource_limit, resource_type);
2196 
2197     const auto limit_value = RetrieveResourceLimitValue(system, resource_limit, resource_type,
2198                                                         ResourceLimitValueType::LimitValue);
2199     if (limit_value.Failed()) {
2200         return limit_value.Code();
2201     }
2202 
2203     *out_value = static_cast<u64>(*limit_value);
2204     return RESULT_SUCCESS;
2205 }
2206 
GetResourceLimitCurrentValue(Core::System & system,u64 * out_value,Handle resource_limit,u32 resource_type)2207 static ResultCode GetResourceLimitCurrentValue(Core::System& system, u64* out_value,
2208                                                Handle resource_limit, u32 resource_type) {
2209     LOG_DEBUG(Kernel_SVC, "called. Handle={:08X}, Resource type={}", resource_limit, resource_type);
2210 
2211     const auto current_value = RetrieveResourceLimitValue(system, resource_limit, resource_type,
2212                                                           ResourceLimitValueType::CurrentValue);
2213     if (current_value.Failed()) {
2214         return current_value.Code();
2215     }
2216 
2217     *out_value = static_cast<u64>(*current_value);
2218     return RESULT_SUCCESS;
2219 }
2220 
SetResourceLimitLimitValue(Core::System & system,Handle resource_limit,u32 resource_type,u64 value)2221 static ResultCode SetResourceLimitLimitValue(Core::System& system, Handle resource_limit,
2222                                              u32 resource_type, u64 value) {
2223     LOG_DEBUG(Kernel_SVC, "called. Handle={:08X}, Resource type={}, Value={}", resource_limit,
2224               resource_type, value);
2225 
2226     const auto type = static_cast<ResourceType>(resource_type);
2227     if (!IsValidResourceType(type)) {
2228         LOG_ERROR(Kernel_SVC, "Invalid resource limit type: '{}'", resource_type);
2229         return ERR_INVALID_ENUM_VALUE;
2230     }
2231 
2232     auto* const current_process = system.Kernel().CurrentProcess();
2233     ASSERT(current_process != nullptr);
2234 
2235     auto resource_limit_object =
2236         current_process->GetHandleTable().Get<ResourceLimit>(resource_limit);
2237     if (!resource_limit_object) {
2238         LOG_ERROR(Kernel_SVC, "Handle to non-existent resource limit instance used. Handle={:08X}",
2239                   resource_limit);
2240         return ERR_INVALID_HANDLE;
2241     }
2242 
2243     const auto set_result = resource_limit_object->SetLimitValue(type, static_cast<s64>(value));
2244     if (set_result.IsError()) {
2245         LOG_ERROR(
2246             Kernel_SVC,
2247             "Attempted to lower resource limit ({}) for category '{}' below its current value ({})",
2248             resource_limit_object->GetMaxResourceValue(type), resource_type,
2249             resource_limit_object->GetCurrentResourceValue(type));
2250         return set_result;
2251     }
2252 
2253     return RESULT_SUCCESS;
2254 }
2255 
GetProcessList(Core::System & system,u32 * out_num_processes,VAddr out_process_ids,u32 out_process_ids_size)2256 static ResultCode GetProcessList(Core::System& system, u32* out_num_processes,
2257                                  VAddr out_process_ids, u32 out_process_ids_size) {
2258     LOG_DEBUG(Kernel_SVC, "called. out_process_ids=0x{:016X}, out_process_ids_size={}",
2259               out_process_ids, out_process_ids_size);
2260 
2261     // If the supplied size is negative or greater than INT32_MAX / sizeof(u64), bail.
2262     if ((out_process_ids_size & 0xF0000000) != 0) {
2263         LOG_ERROR(Kernel_SVC,
2264                   "Supplied size outside [0, 0x0FFFFFFF] range. out_process_ids_size={}",
2265                   out_process_ids_size);
2266         return ERR_OUT_OF_RANGE;
2267     }
2268 
2269     const auto& kernel = system.Kernel();
2270     const auto total_copy_size = out_process_ids_size * sizeof(u64);
2271 
2272     if (out_process_ids_size > 0 && !kernel.CurrentProcess()->PageTable().IsInsideAddressSpace(
2273                                         out_process_ids, total_copy_size)) {
2274         LOG_ERROR(Kernel_SVC, "Address range outside address space. begin=0x{:016X}, end=0x{:016X}",
2275                   out_process_ids, out_process_ids + total_copy_size);
2276         return ERR_INVALID_ADDRESS_STATE;
2277     }
2278 
2279     auto& memory = system.Memory();
2280     const auto& process_list = kernel.GetProcessList();
2281     const auto num_processes = process_list.size();
2282     const auto copy_amount = std::min(std::size_t{out_process_ids_size}, num_processes);
2283 
2284     for (std::size_t i = 0; i < copy_amount; ++i) {
2285         memory.Write64(out_process_ids, process_list[i]->GetProcessID());
2286         out_process_ids += sizeof(u64);
2287     }
2288 
2289     *out_num_processes = static_cast<u32>(num_processes);
2290     return RESULT_SUCCESS;
2291 }
2292 
GetThreadList(Core::System & system,u32 * out_num_threads,VAddr out_thread_ids,u32 out_thread_ids_size,Handle debug_handle)2293 static ResultCode GetThreadList(Core::System& system, u32* out_num_threads, VAddr out_thread_ids,
2294                                 u32 out_thread_ids_size, Handle debug_handle) {
2295     // TODO: Handle this case when debug events are supported.
2296     UNIMPLEMENTED_IF(debug_handle != InvalidHandle);
2297 
2298     LOG_DEBUG(Kernel_SVC, "called. out_thread_ids=0x{:016X}, out_thread_ids_size={}",
2299               out_thread_ids, out_thread_ids_size);
2300 
2301     // If the size is negative or larger than INT32_MAX / sizeof(u64)
2302     if ((out_thread_ids_size & 0xF0000000) != 0) {
2303         LOG_ERROR(Kernel_SVC, "Supplied size outside [0, 0x0FFFFFFF] range. size={}",
2304                   out_thread_ids_size);
2305         return ERR_OUT_OF_RANGE;
2306     }
2307 
2308     const auto* const current_process = system.Kernel().CurrentProcess();
2309     const auto total_copy_size = out_thread_ids_size * sizeof(u64);
2310 
2311     if (out_thread_ids_size > 0 &&
2312         !current_process->PageTable().IsInsideAddressSpace(out_thread_ids, total_copy_size)) {
2313         LOG_ERROR(Kernel_SVC, "Address range outside address space. begin=0x{:016X}, end=0x{:016X}",
2314                   out_thread_ids, out_thread_ids + total_copy_size);
2315         return ERR_INVALID_ADDRESS_STATE;
2316     }
2317 
2318     auto& memory = system.Memory();
2319     const auto& thread_list = current_process->GetThreadList();
2320     const auto num_threads = thread_list.size();
2321     const auto copy_amount = std::min(std::size_t{out_thread_ids_size}, num_threads);
2322 
2323     auto list_iter = thread_list.cbegin();
2324     for (std::size_t i = 0; i < copy_amount; ++i, ++list_iter) {
2325         memory.Write64(out_thread_ids, (*list_iter)->GetThreadID());
2326         out_thread_ids += sizeof(u64);
2327     }
2328 
2329     *out_num_threads = static_cast<u32>(num_threads);
2330     return RESULT_SUCCESS;
2331 }
2332 
FlushProcessDataCache32(Core::System & system,Handle handle,u32 address,u32 size)2333 static ResultCode FlushProcessDataCache32([[maybe_unused]] Core::System& system,
2334                                           [[maybe_unused]] Handle handle,
2335                                           [[maybe_unused]] u32 address, [[maybe_unused]] u32 size) {
2336     // Note(Blinkhawk): For emulation purposes of the data cache this is mostly a no-op,
2337     // as all emulation is done in the same cache level in host architecture, thus data cache
2338     // does not need flushing.
2339     LOG_DEBUG(Kernel_SVC, "called");
2340     return RESULT_SUCCESS;
2341 }
2342 
2343 namespace {
2344 struct FunctionDef {
2345     using Func = void(Core::System&);
2346 
2347     u32 id;
2348     Func* func;
2349     const char* name;
2350 };
2351 } // namespace
2352 
2353 static const FunctionDef SVC_Table_32[] = {
2354     {0x00, nullptr, "Unknown"},
2355     {0x01, SvcWrap32<SetHeapSize32>, "SetHeapSize32"},
2356     {0x02, nullptr, "Unknown"},
2357     {0x03, SvcWrap32<SetMemoryAttribute32>, "SetMemoryAttribute32"},
2358     {0x04, SvcWrap32<MapMemory32>, "MapMemory32"},
2359     {0x05, SvcWrap32<UnmapMemory32>, "UnmapMemory32"},
2360     {0x06, SvcWrap32<QueryMemory32>, "QueryMemory32"},
2361     {0x07, SvcWrap32<ExitProcess32>, "ExitProcess32"},
2362     {0x08, SvcWrap32<CreateThread32>, "CreateThread32"},
2363     {0x09, SvcWrap32<StartThread32>, "StartThread32"},
2364     {0x0a, SvcWrap32<ExitThread32>, "ExitThread32"},
2365     {0x0b, SvcWrap32<SleepThread32>, "SleepThread32"},
2366     {0x0c, SvcWrap32<GetThreadPriority32>, "GetThreadPriority32"},
2367     {0x0d, SvcWrap32<SetThreadPriority32>, "SetThreadPriority32"},
2368     {0x0e, SvcWrap32<GetThreadCoreMask32>, "GetThreadCoreMask32"},
2369     {0x0f, SvcWrap32<SetThreadCoreMask32>, "SetThreadCoreMask32"},
2370     {0x10, SvcWrap32<GetCurrentProcessorNumber32>, "GetCurrentProcessorNumber32"},
2371     {0x11, SvcWrap32<SignalEvent32>, "SignalEvent32"},
2372     {0x12, SvcWrap32<ClearEvent32>, "ClearEvent32"},
2373     {0x13, SvcWrap32<MapSharedMemory32>, "MapSharedMemory32"},
2374     {0x14, nullptr, "UnmapSharedMemory32"},
2375     {0x15, SvcWrap32<CreateTransferMemory32>, "CreateTransferMemory32"},
2376     {0x16, SvcWrap32<CloseHandle32>, "CloseHandle32"},
2377     {0x17, SvcWrap32<ResetSignal32>, "ResetSignal32"},
2378     {0x18, SvcWrap32<WaitSynchronization32>, "WaitSynchronization32"},
2379     {0x19, SvcWrap32<CancelSynchronization32>, "CancelSynchronization32"},
2380     {0x1a, SvcWrap32<ArbitrateLock32>, "ArbitrateLock32"},
2381     {0x1b, SvcWrap32<ArbitrateUnlock32>, "ArbitrateUnlock32"},
2382     {0x1c, SvcWrap32<WaitProcessWideKeyAtomic32>, "WaitProcessWideKeyAtomic32"},
2383     {0x1d, SvcWrap32<SignalProcessWideKey32>, "SignalProcessWideKey32"},
2384     {0x1e, SvcWrap32<GetSystemTick32>, "GetSystemTick32"},
2385     {0x1f, SvcWrap32<ConnectToNamedPort32>, "ConnectToNamedPort32"},
2386     {0x20, nullptr, "Unknown"},
2387     {0x21, SvcWrap32<SendSyncRequest32>, "SendSyncRequest32"},
2388     {0x22, nullptr, "SendSyncRequestWithUserBuffer32"},
2389     {0x23, nullptr, "Unknown"},
2390     {0x24, SvcWrap32<GetProcessId32>, "GetProcessId32"},
2391     {0x25, SvcWrap32<GetThreadId32>, "GetThreadId32"},
2392     {0x26, SvcWrap32<Break32>, "Break32"},
2393     {0x27, nullptr, "OutputDebugString32"},
2394     {0x28, nullptr, "Unknown"},
2395     {0x29, SvcWrap32<GetInfo32>, "GetInfo32"},
2396     {0x2a, nullptr, "Unknown"},
2397     {0x2b, nullptr, "Unknown"},
2398     {0x2c, SvcWrap32<MapPhysicalMemory32>, "MapPhysicalMemory32"},
2399     {0x2d, SvcWrap32<UnmapPhysicalMemory32>, "UnmapPhysicalMemory32"},
2400     {0x2e, nullptr, "Unknown"},
2401     {0x2f, nullptr, "Unknown"},
2402     {0x30, nullptr, "Unknown"},
2403     {0x31, nullptr, "Unknown"},
2404     {0x32, SvcWrap32<SetThreadActivity32>, "SetThreadActivity32"},
2405     {0x33, SvcWrap32<GetThreadContext32>, "GetThreadContext32"},
2406     {0x34, SvcWrap32<WaitForAddress32>, "WaitForAddress32"},
2407     {0x35, SvcWrap32<SignalToAddress32>, "SignalToAddress32"},
2408     {0x36, nullptr, "Unknown"},
2409     {0x37, nullptr, "Unknown"},
2410     {0x38, nullptr, "Unknown"},
2411     {0x39, nullptr, "Unknown"},
2412     {0x3a, nullptr, "Unknown"},
2413     {0x3b, nullptr, "Unknown"},
2414     {0x3c, nullptr, "Unknown"},
2415     {0x3d, nullptr, "Unknown"},
2416     {0x3e, nullptr, "Unknown"},
2417     {0x3f, nullptr, "Unknown"},
2418     {0x40, nullptr, "CreateSession32"},
2419     {0x41, nullptr, "AcceptSession32"},
2420     {0x42, nullptr, "Unknown"},
2421     {0x43, nullptr, "ReplyAndReceive32"},
2422     {0x44, nullptr, "Unknown"},
2423     {0x45, SvcWrap32<CreateEvent32>, "CreateEvent32"},
2424     {0x46, nullptr, "Unknown"},
2425     {0x47, nullptr, "Unknown"},
2426     {0x48, nullptr, "Unknown"},
2427     {0x49, nullptr, "Unknown"},
2428     {0x4a, nullptr, "Unknown"},
2429     {0x4b, nullptr, "Unknown"},
2430     {0x4c, nullptr, "Unknown"},
2431     {0x4d, nullptr, "Unknown"},
2432     {0x4e, nullptr, "Unknown"},
2433     {0x4f, nullptr, "Unknown"},
2434     {0x50, nullptr, "Unknown"},
2435     {0x51, nullptr, "Unknown"},
2436     {0x52, nullptr, "Unknown"},
2437     {0x53, nullptr, "Unknown"},
2438     {0x54, nullptr, "Unknown"},
2439     {0x55, nullptr, "Unknown"},
2440     {0x56, nullptr, "Unknown"},
2441     {0x57, nullptr, "Unknown"},
2442     {0x58, nullptr, "Unknown"},
2443     {0x59, nullptr, "Unknown"},
2444     {0x5a, nullptr, "Unknown"},
2445     {0x5b, nullptr, "Unknown"},
2446     {0x5c, nullptr, "Unknown"},
2447     {0x5d, nullptr, "Unknown"},
2448     {0x5e, nullptr, "Unknown"},
2449     {0x5F, SvcWrap32<FlushProcessDataCache32>, "FlushProcessDataCache32"},
2450     {0x60, nullptr, "Unknown"},
2451     {0x61, nullptr, "Unknown"},
2452     {0x62, nullptr, "Unknown"},
2453     {0x63, nullptr, "Unknown"},
2454     {0x64, nullptr, "Unknown"},
2455     {0x65, nullptr, "GetProcessList32"},
2456     {0x66, nullptr, "Unknown"},
2457     {0x67, nullptr, "Unknown"},
2458     {0x68, nullptr, "Unknown"},
2459     {0x69, nullptr, "Unknown"},
2460     {0x6A, nullptr, "Unknown"},
2461     {0x6B, nullptr, "Unknown"},
2462     {0x6C, nullptr, "Unknown"},
2463     {0x6D, nullptr, "Unknown"},
2464     {0x6E, nullptr, "Unknown"},
2465     {0x6f, nullptr, "GetSystemInfo32"},
2466     {0x70, nullptr, "CreatePort32"},
2467     {0x71, nullptr, "ManageNamedPort32"},
2468     {0x72, nullptr, "ConnectToPort32"},
2469     {0x73, nullptr, "SetProcessMemoryPermission32"},
2470     {0x74, nullptr, "Unknown"},
2471     {0x75, nullptr, "Unknown"},
2472     {0x76, nullptr, "Unknown"},
2473     {0x77, nullptr, "MapProcessCodeMemory32"},
2474     {0x78, nullptr, "UnmapProcessCodeMemory32"},
2475     {0x79, nullptr, "Unknown"},
2476     {0x7A, nullptr, "Unknown"},
2477     {0x7B, nullptr, "TerminateProcess32"},
2478 };
2479 
2480 static const FunctionDef SVC_Table_64[] = {
2481     {0x00, nullptr, "Unknown"},
2482     {0x01, SvcWrap64<SetHeapSize>, "SetHeapSize"},
2483     {0x02, nullptr, "SetMemoryPermission"},
2484     {0x03, SvcWrap64<SetMemoryAttribute>, "SetMemoryAttribute"},
2485     {0x04, SvcWrap64<MapMemory>, "MapMemory"},
2486     {0x05, SvcWrap64<UnmapMemory>, "UnmapMemory"},
2487     {0x06, SvcWrap64<QueryMemory>, "QueryMemory"},
2488     {0x07, SvcWrap64<ExitProcess>, "ExitProcess"},
2489     {0x08, SvcWrap64<CreateThread>, "CreateThread"},
2490     {0x09, SvcWrap64<StartThread>, "StartThread"},
2491     {0x0A, SvcWrap64<ExitThread>, "ExitThread"},
2492     {0x0B, SvcWrap64<SleepThread>, "SleepThread"},
2493     {0x0C, SvcWrap64<GetThreadPriority>, "GetThreadPriority"},
2494     {0x0D, SvcWrap64<SetThreadPriority>, "SetThreadPriority"},
2495     {0x0E, SvcWrap64<GetThreadCoreMask>, "GetThreadCoreMask"},
2496     {0x0F, SvcWrap64<SetThreadCoreMask>, "SetThreadCoreMask"},
2497     {0x10, SvcWrap64<GetCurrentProcessorNumber>, "GetCurrentProcessorNumber"},
2498     {0x11, SvcWrap64<SignalEvent>, "SignalEvent"},
2499     {0x12, SvcWrap64<ClearEvent>, "ClearEvent"},
2500     {0x13, SvcWrap64<MapSharedMemory>, "MapSharedMemory"},
2501     {0x14, nullptr, "UnmapSharedMemory"},
2502     {0x15, SvcWrap64<CreateTransferMemory>, "CreateTransferMemory"},
2503     {0x16, SvcWrap64<CloseHandle>, "CloseHandle"},
2504     {0x17, SvcWrap64<ResetSignal>, "ResetSignal"},
2505     {0x18, SvcWrap64<WaitSynchronization>, "WaitSynchronization"},
2506     {0x19, SvcWrap64<CancelSynchronization>, "CancelSynchronization"},
2507     {0x1A, SvcWrap64<ArbitrateLock>, "ArbitrateLock"},
2508     {0x1B, SvcWrap64<ArbitrateUnlock>, "ArbitrateUnlock"},
2509     {0x1C, SvcWrap64<WaitProcessWideKeyAtomic>, "WaitProcessWideKeyAtomic"},
2510     {0x1D, SvcWrap64<SignalProcessWideKey>, "SignalProcessWideKey"},
2511     {0x1E, SvcWrap64<GetSystemTick>, "GetSystemTick"},
2512     {0x1F, SvcWrap64<ConnectToNamedPort>, "ConnectToNamedPort"},
2513     {0x20, nullptr, "SendSyncRequestLight"},
2514     {0x21, SvcWrap64<SendSyncRequest>, "SendSyncRequest"},
2515     {0x22, nullptr, "SendSyncRequestWithUserBuffer"},
2516     {0x23, nullptr, "SendAsyncRequestWithUserBuffer"},
2517     {0x24, SvcWrap64<GetProcessId>, "GetProcessId"},
2518     {0x25, SvcWrap64<GetThreadId>, "GetThreadId"},
2519     {0x26, SvcWrap64<Break>, "Break"},
2520     {0x27, SvcWrap64<OutputDebugString>, "OutputDebugString"},
2521     {0x28, nullptr, "ReturnFromException"},
2522     {0x29, SvcWrap64<GetInfo>, "GetInfo"},
2523     {0x2A, nullptr, "FlushEntireDataCache"},
2524     {0x2B, nullptr, "FlushDataCache"},
2525     {0x2C, SvcWrap64<MapPhysicalMemory>, "MapPhysicalMemory"},
2526     {0x2D, SvcWrap64<UnmapPhysicalMemory>, "UnmapPhysicalMemory"},
2527     {0x2E, nullptr, "GetFutureThreadInfo"},
2528     {0x2F, nullptr, "GetLastThreadInfo"},
2529     {0x30, SvcWrap64<GetResourceLimitLimitValue>, "GetResourceLimitLimitValue"},
2530     {0x31, SvcWrap64<GetResourceLimitCurrentValue>, "GetResourceLimitCurrentValue"},
2531     {0x32, SvcWrap64<SetThreadActivity>, "SetThreadActivity"},
2532     {0x33, SvcWrap64<GetThreadContext>, "GetThreadContext"},
2533     {0x34, SvcWrap64<WaitForAddress>, "WaitForAddress"},
2534     {0x35, SvcWrap64<SignalToAddress>, "SignalToAddress"},
2535     {0x36, nullptr, "SynchronizePreemptionState"},
2536     {0x37, nullptr, "Unknown"},
2537     {0x38, nullptr, "Unknown"},
2538     {0x39, nullptr, "Unknown"},
2539     {0x3A, nullptr, "Unknown"},
2540     {0x3B, nullptr, "Unknown"},
2541     {0x3C, SvcWrap64<KernelDebug>, "KernelDebug"},
2542     {0x3D, SvcWrap64<ChangeKernelTraceState>, "ChangeKernelTraceState"},
2543     {0x3E, nullptr, "Unknown"},
2544     {0x3F, nullptr, "Unknown"},
2545     {0x40, nullptr, "CreateSession"},
2546     {0x41, nullptr, "AcceptSession"},
2547     {0x42, nullptr, "ReplyAndReceiveLight"},
2548     {0x43, nullptr, "ReplyAndReceive"},
2549     {0x44, nullptr, "ReplyAndReceiveWithUserBuffer"},
2550     {0x45, SvcWrap64<CreateEvent>, "CreateEvent"},
2551     {0x46, nullptr, "Unknown"},
2552     {0x47, nullptr, "Unknown"},
2553     {0x48, nullptr, "MapPhysicalMemoryUnsafe"},
2554     {0x49, nullptr, "UnmapPhysicalMemoryUnsafe"},
2555     {0x4A, nullptr, "SetUnsafeLimit"},
2556     {0x4B, nullptr, "CreateCodeMemory"},
2557     {0x4C, nullptr, "ControlCodeMemory"},
2558     {0x4D, nullptr, "SleepSystem"},
2559     {0x4E, nullptr, "ReadWriteRegister"},
2560     {0x4F, nullptr, "SetProcessActivity"},
2561     {0x50, nullptr, "CreateSharedMemory"},
2562     {0x51, nullptr, "MapTransferMemory"},
2563     {0x52, nullptr, "UnmapTransferMemory"},
2564     {0x53, nullptr, "CreateInterruptEvent"},
2565     {0x54, nullptr, "QueryPhysicalAddress"},
2566     {0x55, nullptr, "QueryIoMapping"},
2567     {0x56, nullptr, "CreateDeviceAddressSpace"},
2568     {0x57, nullptr, "AttachDeviceAddressSpace"},
2569     {0x58, nullptr, "DetachDeviceAddressSpace"},
2570     {0x59, nullptr, "MapDeviceAddressSpaceByForce"},
2571     {0x5A, nullptr, "MapDeviceAddressSpaceAligned"},
2572     {0x5B, nullptr, "MapDeviceAddressSpace"},
2573     {0x5C, nullptr, "UnmapDeviceAddressSpace"},
2574     {0x5D, nullptr, "InvalidateProcessDataCache"},
2575     {0x5E, nullptr, "StoreProcessDataCache"},
2576     {0x5F, nullptr, "FlushProcessDataCache"},
2577     {0x60, nullptr, "DebugActiveProcess"},
2578     {0x61, nullptr, "BreakDebugProcess"},
2579     {0x62, nullptr, "TerminateDebugProcess"},
2580     {0x63, nullptr, "GetDebugEvent"},
2581     {0x64, nullptr, "ContinueDebugEvent"},
2582     {0x65, SvcWrap64<GetProcessList>, "GetProcessList"},
2583     {0x66, SvcWrap64<GetThreadList>, "GetThreadList"},
2584     {0x67, nullptr, "GetDebugThreadContext"},
2585     {0x68, nullptr, "SetDebugThreadContext"},
2586     {0x69, nullptr, "QueryDebugProcessMemory"},
2587     {0x6A, nullptr, "ReadDebugProcessMemory"},
2588     {0x6B, nullptr, "WriteDebugProcessMemory"},
2589     {0x6C, nullptr, "SetHardwareBreakPoint"},
2590     {0x6D, nullptr, "GetDebugThreadParam"},
2591     {0x6E, nullptr, "Unknown"},
2592     {0x6F, nullptr, "GetSystemInfo"},
2593     {0x70, nullptr, "CreatePort"},
2594     {0x71, nullptr, "ManageNamedPort"},
2595     {0x72, nullptr, "ConnectToPort"},
2596     {0x73, nullptr, "SetProcessMemoryPermission"},
2597     {0x74, nullptr, "MapProcessMemory"},
2598     {0x75, nullptr, "UnmapProcessMemory"},
2599     {0x76, SvcWrap64<QueryProcessMemory>, "QueryProcessMemory"},
2600     {0x77, SvcWrap64<MapProcessCodeMemory>, "MapProcessCodeMemory"},
2601     {0x78, SvcWrap64<UnmapProcessCodeMemory>, "UnmapProcessCodeMemory"},
2602     {0x79, nullptr, "CreateProcess"},
2603     {0x7A, nullptr, "StartProcess"},
2604     {0x7B, nullptr, "TerminateProcess"},
2605     {0x7C, SvcWrap64<GetProcessInfo>, "GetProcessInfo"},
2606     {0x7D, SvcWrap64<CreateResourceLimit>, "CreateResourceLimit"},
2607     {0x7E, SvcWrap64<SetResourceLimitLimitValue>, "SetResourceLimitLimitValue"},
2608     {0x7F, nullptr, "CallSecureMonitor"},
2609 };
2610 
GetSVCInfo32(u32 func_num)2611 static const FunctionDef* GetSVCInfo32(u32 func_num) {
2612     if (func_num >= std::size(SVC_Table_32)) {
2613         LOG_ERROR(Kernel_SVC, "Unknown svc=0x{:02X}", func_num);
2614         return nullptr;
2615     }
2616     return &SVC_Table_32[func_num];
2617 }
2618 
GetSVCInfo64(u32 func_num)2619 static const FunctionDef* GetSVCInfo64(u32 func_num) {
2620     if (func_num >= std::size(SVC_Table_64)) {
2621         LOG_ERROR(Kernel_SVC, "Unknown svc=0x{:02X}", func_num);
2622         return nullptr;
2623     }
2624     return &SVC_Table_64[func_num];
2625 }
2626 
Call(Core::System & system,u32 immediate)2627 void Call(Core::System& system, u32 immediate) {
2628     system.ExitDynarmicProfile();
2629     auto& kernel = system.Kernel();
2630     kernel.EnterSVCProfile();
2631 
2632     auto* thread = system.CurrentScheduler().GetCurrentThread();
2633     thread->SetContinuousOnSVC(true);
2634 
2635     const FunctionDef* info = system.CurrentProcess()->Is64BitProcess() ? GetSVCInfo64(immediate)
2636                                                                         : GetSVCInfo32(immediate);
2637     if (info) {
2638         if (info->func) {
2639             info->func(system);
2640         } else {
2641             LOG_CRITICAL(Kernel_SVC, "Unimplemented SVC function {}(..)", info->name);
2642         }
2643     } else {
2644         LOG_CRITICAL(Kernel_SVC, "Unknown SVC function 0x{:X}", immediate);
2645     }
2646 
2647     kernel.ExitSVCProfile();
2648 
2649     if (!thread->IsContinuousOnSVC()) {
2650         auto* host_context = thread->GetHostContext().get();
2651         host_context->Rewind();
2652     }
2653 
2654     system.EnterDynarmicProfile();
2655 }
2656 
2657 } // namespace Kernel::Svc
2658