diff options
Diffstat (limited to 'src/core/hle/kernel/svc.cpp')
| -rw-r--r-- | src/core/hle/kernel/svc.cpp | 361 |
1 files changed, 259 insertions, 102 deletions
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index 7cfecb68c..ab10db3df 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -20,6 +20,7 @@ #include "core/hle/kernel/address_arbiter.h" #include "core/hle/kernel/client_port.h" #include "core/hle/kernel/client_session.h" +#include "core/hle/kernel/errors.h" #include "core/hle/kernel/handle_table.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/mutex.h" @@ -31,6 +32,7 @@ #include "core/hle/kernel/svc.h" #include "core/hle/kernel/svc_wrap.h" #include "core/hle/kernel/thread.h" +#include "core/hle/kernel/transfer_memory.h" #include "core/hle/kernel/writable_event.h" #include "core/hle/lock.h" #include "core/hle/result.h" @@ -47,23 +49,6 @@ constexpr bool IsValidAddressRange(VAddr address, u64 size) { return address + size > address; } -// Checks if a given address range lies within a larger address range. -constexpr bool IsInsideAddressRange(VAddr address, u64 size, VAddr address_range_begin, - VAddr address_range_end) { - const VAddr end_address = address + size - 1; - return address_range_begin <= address && end_address <= address_range_end - 1; -} - -bool IsInsideAddressSpace(const VMManager& vm, VAddr address, u64 size) { - return IsInsideAddressRange(address, size, vm.GetAddressSpaceBaseAddress(), - vm.GetAddressSpaceEndAddress()); -} - -bool IsInsideNewMapRegion(const VMManager& vm, VAddr address, u64 size) { - return IsInsideAddressRange(address, size, vm.GetNewMapRegionBaseAddress(), - vm.GetNewMapRegionEndAddress()); -} - // 8 GiB constexpr u64 MAIN_MEMORY_SIZE = 0x200000000; @@ -105,14 +90,14 @@ ResultCode MapUnmapMemorySanityChecks(const VMManager& vm_manager, VAddr dst_add return ERR_INVALID_ADDRESS_STATE; } - if (!IsInsideAddressSpace(vm_manager, src_addr, size)) { + if (!vm_manager.IsWithinAddressSpace(src_addr, size)) { LOG_ERROR(Kernel_SVC, "Source is not within the address space, addr=0x{:016X}, size=0x{:016X}", src_addr, size); return ERR_INVALID_ADDRESS_STATE; } - if (!IsInsideNewMapRegion(vm_manager, dst_addr, size)) { + if (!vm_manager.IsWithinNewMapRegion(dst_addr, size)) { LOG_ERROR(Kernel_SVC, "Destination is not within the new map region, addr=0x{:016X}, size=0x{:016X}", dst_addr, size); @@ -190,11 +175,8 @@ static ResultCode SetHeapSize(VAddr* heap_addr, u64 heap_size) { return ERR_INVALID_SIZE; } - auto& vm_manager = Core::CurrentProcess()->VMManager(); - const VAddr heap_base = vm_manager.GetHeapRegionBaseAddress(); - const auto alloc_result = - vm_manager.HeapAllocate(heap_base, heap_size, VMAPermission::ReadWrite); - + auto& vm_manager = Core::System::GetInstance().Kernel().CurrentProcess()->VMManager(); + const auto alloc_result = vm_manager.SetHeapSize(heap_size); if (alloc_result.Failed()) { return alloc_result.Code(); } @@ -238,7 +220,7 @@ static ResultCode SetMemoryPermission(VAddr addr, u64 size, u32 prot) { auto* const current_process = Core::CurrentProcess(); auto& vm_manager = current_process->VMManager(); - if (!IsInsideAddressSpace(vm_manager, addr, size)) { + if (!vm_manager.IsWithinAddressSpace(addr, size)) { LOG_ERROR(Kernel_SVC, "Source is not within the address space, addr=0x{:016X}, size=0x{:016X}", addr, size); @@ -299,7 +281,7 @@ static ResultCode SetMemoryAttribute(VAddr address, u64 size, u32 mask, u32 attr } auto& vm_manager = Core::CurrentProcess()->VMManager(); - if (!IsInsideAddressSpace(vm_manager, address, size)) { + if (!vm_manager.IsWithinAddressSpace(address, size)) { LOG_ERROR(Kernel_SVC, "Given address (0x{:016X}) is outside the bounds of the address space.", address); return ERR_INVALID_ADDRESS_STATE; @@ -567,9 +549,9 @@ static ResultCode ArbitrateLock(Handle holding_thread_handle, VAddr mutex_addr, return ERR_INVALID_ADDRESS; } - auto& handle_table = Core::CurrentProcess()->GetHandleTable(); - return Mutex::TryAcquire(handle_table, mutex_addr, holding_thread_handle, - requesting_thread_handle); + auto* const current_process = Core::System::GetInstance().Kernel().CurrentProcess(); + return current_process->GetMutex().TryAcquire(mutex_addr, holding_thread_handle, + requesting_thread_handle); } /// Unlock a mutex @@ -587,7 +569,8 @@ static ResultCode ArbitrateUnlock(VAddr mutex_addr) { return ERR_INVALID_ADDRESS; } - return Mutex::Release(mutex_addr); + auto* const current_process = Core::System::GetInstance().Kernel().CurrentProcess(); + return current_process->GetMutex().Release(mutex_addr); } enum class BreakType : u32 { @@ -726,7 +709,7 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id) HeapRegionBaseAddr = 4, HeapRegionSize = 5, TotalMemoryUsage = 6, - TotalHeapUsage = 7, + TotalPhysicalMemoryUsed = 7, IsCurrentProcessBeingDebugged = 8, RegisterResourceLimit = 9, IdleTickCount = 10, @@ -762,7 +745,7 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id) case GetInfoType::NewMapRegionBaseAddr: case GetInfoType::NewMapRegionSize: case GetInfoType::TotalMemoryUsage: - case GetInfoType::TotalHeapUsage: + case GetInfoType::TotalPhysicalMemoryUsed: case GetInfoType::IsVirtualAddressMemoryEnabled: case GetInfoType::PersonalMmHeapUsage: case GetInfoType::TitleId: @@ -822,8 +805,8 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id) *result = process->VMManager().GetTotalMemoryUsage(); return RESULT_SUCCESS; - case GetInfoType::TotalHeapUsage: - *result = process->VMManager().GetTotalHeapUsage(); + case GetInfoType::TotalPhysicalMemoryUsed: + *result = process->GetTotalPhysicalMemoryUsed(); return RESULT_SUCCESS; case GetInfoType::IsVirtualAddressMemoryEnabled: @@ -918,6 +901,7 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id) } const auto& system = Core::System::GetInstance(); + const auto& core_timing = system.CoreTiming(); const auto& scheduler = system.CurrentScheduler(); const auto* const current_thread = scheduler.GetCurrentThread(); const bool same_thread = current_thread == thread; @@ -927,9 +911,9 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id) if (same_thread && info_sub_id == 0xFFFFFFFFFFFFFFFF) { const u64 thread_ticks = current_thread->GetTotalCPUTimeTicks(); - out_ticks = thread_ticks + (CoreTiming::GetTicks() - prev_ctx_ticks); + out_ticks = thread_ticks + (core_timing.GetTicks() - prev_ctx_ticks); } else if (same_thread && info_sub_id == system.CurrentCoreIndex()) { - out_ticks = CoreTiming::GetTicks() - prev_ctx_ticks; + out_ticks = core_timing.GetTicks() - prev_ctx_ticks; } *result = out_ticks; @@ -1156,7 +1140,7 @@ static ResultCode UnmapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 return ERR_INVALID_MEMORY_RANGE; } - return shared_memory->Unmap(*current_process, addr); + return shared_memory->Unmap(*current_process, addr, size); } static ResultCode QueryProcessMemory(VAddr memory_info_address, VAddr page_info_address, @@ -1299,10 +1283,14 @@ static ResultCode StartThread(Handle thread_handle) { /// Called when a thread exits static void ExitThread() { - LOG_TRACE(Kernel_SVC, "called, pc=0x{:08X}", Core::CurrentArmInterface().GetPC()); + auto& system = Core::System::GetInstance(); - ExitCurrentThread(); - Core::System::GetInstance().PrepareReschedule(); + LOG_TRACE(Kernel_SVC, "called, pc=0x{:08X}", system.CurrentArmInterface().GetPC()); + + auto* const current_thread = system.CurrentScheduler().GetCurrentThread(); + current_thread->Stop(); + system.CurrentScheduler().RemoveThread(current_thread); + system.PrepareReschedule(); } /// Sleep the current thread @@ -1315,32 +1303,32 @@ static void SleepThread(s64 nanoseconds) { YieldAndWaitForLoadBalancing = -2, }; + auto& system = Core::System::GetInstance(); + auto& scheduler = system.CurrentScheduler(); + auto* const current_thread = scheduler.GetCurrentThread(); + if (nanoseconds <= 0) { - auto& scheduler{Core::System::GetInstance().CurrentScheduler()}; switch (static_cast<SleepType>(nanoseconds)) { case SleepType::YieldWithoutLoadBalancing: - scheduler.YieldWithoutLoadBalancing(GetCurrentThread()); + scheduler.YieldWithoutLoadBalancing(current_thread); break; case SleepType::YieldWithLoadBalancing: - scheduler.YieldWithLoadBalancing(GetCurrentThread()); + scheduler.YieldWithLoadBalancing(current_thread); break; case SleepType::YieldAndWaitForLoadBalancing: - scheduler.YieldAndWaitForLoadBalancing(GetCurrentThread()); + scheduler.YieldAndWaitForLoadBalancing(current_thread); break; default: UNREACHABLE_MSG("Unimplemented sleep yield type '{:016X}'!", nanoseconds); } } else { - // Sleep current thread and check for next thread to schedule - WaitCurrentThread_Sleep(); - - // Create an event to wake the thread up after the specified nanosecond delay has passed - GetCurrentThread()->WakeAfterDelay(nanoseconds); + current_thread->Sleep(nanoseconds); } // Reschedule all CPU cores - for (std::size_t i = 0; i < Core::NUM_CPU_CORES; ++i) - Core::System::GetInstance().CpuCore(i).PrepareReschedule(); + for (std::size_t i = 0; i < Core::NUM_CPU_CORES; ++i) { + system.CpuCore(i).PrepareReschedule(); + } } /// Wait process wide key atomic @@ -1351,17 +1339,21 @@ static ResultCode WaitProcessWideKeyAtomic(VAddr mutex_addr, VAddr condition_var "called mutex_addr={:X}, condition_variable_addr={:X}, thread_handle=0x{:08X}, timeout={}", mutex_addr, condition_variable_addr, thread_handle, nano_seconds); - const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); + auto* const current_process = Core::System::GetInstance().Kernel().CurrentProcess(); + const auto& handle_table = current_process->GetHandleTable(); SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle); ASSERT(thread); - CASCADE_CODE(Mutex::Release(mutex_addr)); + const auto release_result = current_process->GetMutex().Release(mutex_addr); + if (release_result.IsError()) { + return release_result; + } SharedPtr<Thread> current_thread = GetCurrentThread(); current_thread->SetCondVarWaitAddress(condition_variable_addr); current_thread->SetMutexWaitAddress(mutex_addr); current_thread->SetWaitHandle(thread_handle); - current_thread->SetStatus(ThreadStatus::WaitMutex); + current_thread->SetStatus(ThreadStatus::WaitCondVar); current_thread->InvalidateWakeupCallback(); current_thread->WakeAfterDelay(nano_seconds); @@ -1405,10 +1397,10 @@ static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target // them all. std::size_t last = waiting_threads.size(); if (target != -1) - last = target; + last = std::min(waiting_threads.size(), static_cast<std::size_t>(target)); // If there are no threads waiting on this condition variable, just exit - if (last > waiting_threads.size()) + if (last == 0) return RESULT_SUCCESS; for (std::size_t index = 0; index < last; ++index) { @@ -1416,6 +1408,9 @@ static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target ASSERT(thread->GetCondVarWaitAddress() == condition_variable_addr); + // liberate Cond Var Thread. + thread->SetCondVarWaitAddress(0); + std::size_t current_core = Core::System::GetInstance().CurrentCoreIndex(); auto& monitor = Core::System::GetInstance().Monitor(); @@ -1434,10 +1429,9 @@ static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target } } while (!monitor.ExclusiveWrite32(current_core, thread->GetMutexWaitAddress(), thread->GetWaitHandle())); - if (mutex_val == 0) { // We were able to acquire the mutex, resume this thread. - ASSERT(thread->GetStatus() == ThreadStatus::WaitMutex); + ASSERT(thread->GetStatus() == ThreadStatus::WaitCondVar); thread->ResumeFromWait(); auto* const lock_owner = thread->GetLockOwner(); @@ -1447,8 +1441,8 @@ static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target thread->SetLockOwner(nullptr); thread->SetMutexWaitAddress(0); - thread->SetCondVarWaitAddress(0); thread->SetWaitHandle(0); + Core::System::GetInstance().CpuCore(thread->GetProcessorID()).PrepareReschedule(); } else { // Atomically signal that the mutex now has a waiting thread. do { @@ -1467,12 +1461,11 @@ static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); auto owner = handle_table.Get<Thread>(owner_handle); ASSERT(owner); - ASSERT(thread->GetStatus() == ThreadStatus::WaitMutex); + ASSERT(thread->GetStatus() == ThreadStatus::WaitCondVar); thread->InvalidateWakeupCallback(); + thread->SetStatus(ThreadStatus::WaitMutex); owner->AddMutexWaiter(thread); - - Core::System::GetInstance().CpuCore(thread->GetProcessorID()).PrepareReschedule(); } } @@ -1494,20 +1487,10 @@ static ResultCode WaitForAddress(VAddr address, u32 type, s32 value, s64 timeout return ERR_INVALID_ADDRESS; } - switch (static_cast<AddressArbiter::ArbitrationType>(type)) { - case AddressArbiter::ArbitrationType::WaitIfLessThan: - return AddressArbiter::WaitForAddressIfLessThan(address, value, timeout, false); - case AddressArbiter::ArbitrationType::DecrementAndWaitIfLessThan: - return AddressArbiter::WaitForAddressIfLessThan(address, value, timeout, true); - case AddressArbiter::ArbitrationType::WaitIfEqual: - return AddressArbiter::WaitForAddressIfEqual(address, value, timeout); - default: - LOG_ERROR(Kernel_SVC, - "Invalid arbitration type, expected WaitIfLessThan, DecrementAndWaitIfLessThan " - "or WaitIfEqual but got {}", - type); - return ERR_INVALID_ENUM_VALUE; - } + const auto arbitration_type = static_cast<AddressArbiter::ArbitrationType>(type); + auto& address_arbiter = + Core::System::GetInstance().Kernel().CurrentProcess()->GetAddressArbiter(); + return address_arbiter.WaitForAddress(address, arbitration_type, value, timeout); } // Signals to an address (via Address Arbiter) @@ -1525,31 +1508,21 @@ static ResultCode SignalToAddress(VAddr address, u32 type, s32 value, s32 num_to return ERR_INVALID_ADDRESS; } - switch (static_cast<AddressArbiter::SignalType>(type)) { - case AddressArbiter::SignalType::Signal: - return AddressArbiter::SignalToAddress(address, num_to_wake); - case AddressArbiter::SignalType::IncrementAndSignalIfEqual: - return AddressArbiter::IncrementAndSignalToAddressIfEqual(address, value, num_to_wake); - case AddressArbiter::SignalType::ModifyByWaitingCountAndSignalIfEqual: - return AddressArbiter::ModifyByWaitingCountAndSignalToAddressIfEqual(address, value, - num_to_wake); - default: - LOG_ERROR(Kernel_SVC, - "Invalid signal type, expected Signal, IncrementAndSignalIfEqual " - "or ModifyByWaitingCountAndSignalIfEqual but got {}", - type); - return ERR_INVALID_ENUM_VALUE; - } + const auto signal_type = static_cast<AddressArbiter::SignalType>(type); + auto& address_arbiter = + Core::System::GetInstance().Kernel().CurrentProcess()->GetAddressArbiter(); + return address_arbiter.SignalToAddress(address, signal_type, value, num_to_wake); } /// This returns the total CPU ticks elapsed since the CPU was powered-on static u64 GetSystemTick() { LOG_TRACE(Kernel_SVC, "called"); - const u64 result{CoreTiming::GetTicks()}; + auto& core_timing = Core::System::GetInstance().CoreTiming(); + const u64 result{core_timing.GetTicks()}; // Advance time to defeat dumb games that busy-wait for the frame to end. - CoreTiming::AddTicks(400); + core_timing.AddTicks(400); return result; } @@ -1612,14 +1585,121 @@ static ResultCode CreateTransferMemory(Handle* handle, VAddr addr, u64 size, u32 } auto& kernel = Core::System::GetInstance().Kernel(); - auto process = kernel.CurrentProcess(); - auto& handle_table = process->GetHandleTable(); - const auto shared_mem_handle = SharedMemory::Create(kernel, process, size, perms, perms, addr); + auto transfer_mem_handle = TransferMemory::Create(kernel, addr, size, perms); - CASCADE_RESULT(*handle, handle_table.Create(shared_mem_handle)); + auto& handle_table = kernel.CurrentProcess()->GetHandleTable(); + const auto result = handle_table.Create(std::move(transfer_mem_handle)); + if (result.Failed()) { + return result.Code(); + } + + *handle = *result; return RESULT_SUCCESS; } +static ResultCode MapTransferMemory(Handle handle, VAddr address, u64 size, u32 permission_raw) { + LOG_DEBUG(Kernel_SVC, + "called. handle=0x{:08X}, address=0x{:016X}, size=0x{:016X}, permissions=0x{:08X}", + handle, address, size, permission_raw); + + if (!Common::Is4KBAligned(address)) { + LOG_ERROR(Kernel_SVC, "Transfer memory addresses must be 4KB aligned (size=0x{:016X}).", + address); + return ERR_INVALID_ADDRESS; + } + + if (size == 0 || !Common::Is4KBAligned(size)) { + LOG_ERROR(Kernel_SVC, + "Transfer memory sizes must be 4KB aligned and not be zero (size=0x{:016X}).", + size); + return ERR_INVALID_SIZE; + } + + if (!IsValidAddressRange(address, size)) { + LOG_ERROR(Kernel_SVC, + "Given address and size overflows the 64-bit range (address=0x{:016X}, " + "size=0x{:016X}).", + address, size); + return ERR_INVALID_ADDRESS_STATE; + } + + const auto permissions = static_cast<MemoryPermission>(permission_raw); + if (permissions != MemoryPermission::None && permissions != MemoryPermission::Read && + permissions != MemoryPermission::ReadWrite) { + LOG_ERROR(Kernel_SVC, "Invalid transfer memory permissions given (permissions=0x{:08X}).", + permission_raw); + return ERR_INVALID_STATE; + } + + const auto& kernel = Core::System::GetInstance().Kernel(); + const auto* const current_process = kernel.CurrentProcess(); + const auto& handle_table = current_process->GetHandleTable(); + + auto transfer_memory = handle_table.Get<TransferMemory>(handle); + if (!transfer_memory) { + LOG_ERROR(Kernel_SVC, "Nonexistent transfer memory handle given (handle=0x{:08X}).", + handle); + return ERR_INVALID_HANDLE; + } + + if (!current_process->VMManager().IsWithinASLRRegion(address, size)) { + LOG_ERROR(Kernel_SVC, + "Given address and size don't fully fit within the ASLR region " + "(address=0x{:016X}, size=0x{:016X}).", + address, size); + return ERR_INVALID_MEMORY_RANGE; + } + + return transfer_memory->MapMemory(address, size, permissions); +} + +static ResultCode UnmapTransferMemory(Handle handle, VAddr address, u64 size) { + LOG_DEBUG(Kernel_SVC, "called. handle=0x{:08X}, address=0x{:016X}, size=0x{:016X}", handle, + address, size); + + if (!Common::Is4KBAligned(address)) { + LOG_ERROR(Kernel_SVC, "Transfer memory addresses must be 4KB aligned (size=0x{:016X}).", + address); + return ERR_INVALID_ADDRESS; + } + + if (size == 0 || !Common::Is4KBAligned(size)) { + LOG_ERROR(Kernel_SVC, + "Transfer memory sizes must be 4KB aligned and not be zero (size=0x{:016X}).", + size); + return ERR_INVALID_SIZE; + } + + if (!IsValidAddressRange(address, size)) { + LOG_ERROR(Kernel_SVC, + "Given address and size overflows the 64-bit range (address=0x{:016X}, " + "size=0x{:016X}).", + address, size); + return ERR_INVALID_ADDRESS_STATE; + } + + const auto& kernel = Core::System::GetInstance().Kernel(); + const auto* const current_process = kernel.CurrentProcess(); + const auto& handle_table = current_process->GetHandleTable(); + + auto transfer_memory = handle_table.Get<TransferMemory>(handle); + if (!transfer_memory) { + LOG_ERROR(Kernel_SVC, "Nonexistent transfer memory handle given (handle=0x{:08X}).", + handle); + return ERR_INVALID_HANDLE; + } + + if (!current_process->VMManager().IsWithinASLRRegion(address, size)) { + LOG_ERROR(Kernel_SVC, + "Given address and size don't fully fit within the ASLR region " + "(address=0x{:016X}, size=0x{:016X}).", + address, size); + return ERR_INVALID_MEMORY_RANGE; + } + + return transfer_memory->UnmapMemory(address, size); +} + static ResultCode GetThreadCoreMask(Handle thread_handle, u32* core, u64* mask) { LOG_TRACE(Kernel_SVC, "called, handle=0x{:08X}", thread_handle); @@ -1903,6 +1983,83 @@ static ResultCode SetResourceLimitLimitValue(Handle resource_limit, u32 resource return RESULT_SUCCESS; } +static ResultCode GetProcessList(u32* out_num_processes, VAddr out_process_ids, + u32 out_process_ids_size) { + LOG_DEBUG(Kernel_SVC, "called. out_process_ids=0x{:016X}, out_process_ids_size={}", + out_process_ids, out_process_ids_size); + + // If the supplied size is negative or greater than INT32_MAX / sizeof(u64), bail. + if ((out_process_ids_size & 0xF0000000) != 0) { + LOG_ERROR(Kernel_SVC, + "Supplied size outside [0, 0x0FFFFFFF] range. out_process_ids_size={}", + out_process_ids_size); + return ERR_OUT_OF_RANGE; + } + + const auto& kernel = Core::System::GetInstance().Kernel(); + const auto& vm_manager = kernel.CurrentProcess()->VMManager(); + const auto total_copy_size = out_process_ids_size * sizeof(u64); + + if (out_process_ids_size > 0 && + !vm_manager.IsWithinAddressSpace(out_process_ids, total_copy_size)) { + LOG_ERROR(Kernel_SVC, "Address range outside address space. begin=0x{:016X}, end=0x{:016X}", + out_process_ids, out_process_ids + total_copy_size); + return ERR_INVALID_ADDRESS_STATE; + } + + const auto& process_list = kernel.GetProcessList(); + const auto num_processes = process_list.size(); + const auto copy_amount = std::min(std::size_t{out_process_ids_size}, num_processes); + + for (std::size_t i = 0; i < copy_amount; ++i) { + Memory::Write64(out_process_ids, process_list[i]->GetProcessID()); + out_process_ids += sizeof(u64); + } + + *out_num_processes = static_cast<u32>(num_processes); + return RESULT_SUCCESS; +} + +ResultCode GetThreadList(u32* out_num_threads, VAddr out_thread_ids, u32 out_thread_ids_size, + Handle debug_handle) { + // TODO: Handle this case when debug events are supported. + UNIMPLEMENTED_IF(debug_handle != InvalidHandle); + + LOG_DEBUG(Kernel_SVC, "called. out_thread_ids=0x{:016X}, out_thread_ids_size={}", + out_thread_ids, out_thread_ids_size); + + // If the size is negative or larger than INT32_MAX / sizeof(u64) + if ((out_thread_ids_size & 0xF0000000) != 0) { + LOG_ERROR(Kernel_SVC, "Supplied size outside [0, 0x0FFFFFFF] range. size={}", + out_thread_ids_size); + return ERR_OUT_OF_RANGE; + } + + const auto* const current_process = Core::System::GetInstance().Kernel().CurrentProcess(); + const auto& vm_manager = current_process->VMManager(); + const auto total_copy_size = out_thread_ids_size * sizeof(u64); + + if (out_thread_ids_size > 0 && + !vm_manager.IsWithinAddressSpace(out_thread_ids, total_copy_size)) { + LOG_ERROR(Kernel_SVC, "Address range outside address space. begin=0x{:016X}, end=0x{:016X}", + out_thread_ids, out_thread_ids + total_copy_size); + return ERR_INVALID_ADDRESS_STATE; + } + + const auto& thread_list = current_process->GetThreadList(); + const auto num_threads = thread_list.size(); + const auto copy_amount = std::min(std::size_t{out_thread_ids_size}, num_threads); + + auto list_iter = thread_list.cbegin(); + for (std::size_t i = 0; i < copy_amount; ++i, ++list_iter) { + Memory::Write64(out_thread_ids, (*list_iter)->GetThreadID()); + out_thread_ids += sizeof(u64); + } + + *out_num_threads = static_cast<u32>(num_threads); + return RESULT_SUCCESS; +} + namespace { struct FunctionDef { using Func = void(); @@ -1995,8 +2152,8 @@ static const FunctionDef SVC_Table[] = { {0x4E, nullptr, "ReadWriteRegister"}, {0x4F, nullptr, "SetProcessActivity"}, {0x50, SvcWrap<CreateSharedMemory>, "CreateSharedMemory"}, - {0x51, nullptr, "MapTransferMemory"}, - {0x52, nullptr, "UnmapTransferMemory"}, + {0x51, SvcWrap<MapTransferMemory>, "MapTransferMemory"}, + {0x52, SvcWrap<UnmapTransferMemory>, "UnmapTransferMemory"}, {0x53, nullptr, "CreateInterruptEvent"}, {0x54, nullptr, "QueryPhysicalAddress"}, {0x55, nullptr, "QueryIoMapping"}, @@ -2015,8 +2172,8 @@ static const FunctionDef SVC_Table[] = { {0x62, nullptr, "TerminateDebugProcess"}, {0x63, nullptr, "GetDebugEvent"}, {0x64, nullptr, "ContinueDebugEvent"}, - {0x65, nullptr, "GetProcessList"}, - {0x66, nullptr, "GetThreadList"}, + {0x65, SvcWrap<GetProcessList>, "GetProcessList"}, + {0x66, SvcWrap<GetThreadList>, "GetThreadList"}, {0x67, nullptr, "GetDebugThreadContext"}, {0x68, nullptr, "SetDebugThreadContext"}, {0x69, nullptr, "QueryDebugProcessMemory"}, @@ -2058,7 +2215,7 @@ void CallSVC(u32 immediate) { MICROPROFILE_SCOPE(Kernel_SVC); // Lock the global kernel mutex when we enter the kernel HLE. - std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock); + std::lock_guard lock{HLE::g_hle_lock}; const FunctionDef* info = GetSVCInfo(immediate); if (info) { |
