diff options
Diffstat (limited to 'src/core/hle/kernel')
-rw-r--r-- | src/core/hle/kernel/kernel.cpp | 10 | ||||
-rw-r--r-- | src/core/hle/kernel/process.cpp | 25 | ||||
-rw-r--r-- | src/core/hle/kernel/process.h | 7 | ||||
-rw-r--r-- | src/core/hle/kernel/svc.cpp | 170 | ||||
-rw-r--r-- | src/core/hle/kernel/svc_wrap.h | 7 | ||||
-rw-r--r-- | src/core/hle/kernel/thread.cpp | 14 | ||||
-rw-r--r-- | src/core/hle/kernel/thread.h | 58 | ||||
-rw-r--r-- | src/core/hle/kernel/vm_manager.cpp | 80 | ||||
-rw-r--r-- | src/core/hle/kernel/vm_manager.h | 49 | ||||
-rw-r--r-- | src/core/hle/kernel/wait_object.cpp | 31 | ||||
-rw-r--r-- | src/core/hle/kernel/wait_object.h | 2 |
11 files changed, 361 insertions, 92 deletions
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index 4d58e7c69..757e5f21f 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -46,8 +46,7 @@ static void ThreadWakeupCallback(u64 thread_handle, [[maybe_unused]] s64 cycles_ bool resume = true; - if (thread->GetStatus() == ThreadStatus::WaitSynchAny || - thread->GetStatus() == ThreadStatus::WaitSynchAll || + if (thread->GetStatus() == ThreadStatus::WaitSynch || thread->GetStatus() == ThreadStatus::WaitHLEEvent) { // Remove the thread from each of its waiting objects' waitlists for (const auto& object : thread->GetWaitObjects()) { @@ -182,7 +181,12 @@ void KernelCore::AppendNewProcess(SharedPtr<Process> process) { void KernelCore::MakeCurrentProcess(Process* process) { impl->current_process = process; - Memory::SetCurrentPageTable(&process->VMManager().page_table); + + if (process == nullptr) { + return; + } + + Memory::SetCurrentPageTable(*process); } Process* KernelCore::CurrentProcess() { diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp index 4e94048da..20d01fc88 100644 --- a/src/core/hle/kernel/process.cpp +++ b/src/core/hle/kernel/process.cpp @@ -28,21 +28,20 @@ namespace { * * @param owner_process The parent process for the main thread * @param kernel The kernel instance to create the main thread under. - * @param entry_point The address at which the thread should start execution * @param priority The priority to give the main thread */ -void SetupMainThread(Process& owner_process, KernelCore& kernel, VAddr entry_point, u32 priority) { - // Initialize new "main" thread - const VAddr stack_top = owner_process.VMManager().GetTLSIORegionEndAddress(); +void SetupMainThread(Process& owner_process, KernelCore& kernel, u32 priority) { + const auto& vm_manager = owner_process.VMManager(); + const VAddr entry_point = vm_manager.GetCodeRegionBaseAddress(); + const VAddr stack_top = vm_manager.GetTLSIORegionEndAddress(); auto thread_res = Thread::Create(kernel, "main", entry_point, priority, 0, owner_process.GetIdealCore(), stack_top, owner_process); SharedPtr<Thread> thread = std::move(thread_res).Unwrap(); // Register 1 must be a handle to the main thread - const Handle guest_handle = owner_process.GetHandleTable().Create(thread).Unwrap(); - thread->SetGuestHandle(guest_handle); - thread->GetContext().cpu_registers[1] = guest_handle; + const Handle thread_handle = owner_process.GetHandleTable().Create(thread).Unwrap(); + thread->GetContext().cpu_registers[1] = thread_handle; // Threads by default are dormant, wake up the main thread so it runs when the scheduler fires thread->ResumeFromWait(); @@ -106,8 +105,6 @@ ResultCode Process::LoadFromMetadata(const FileSys::ProgramMetadata& metadata) { is_64bit_process = metadata.Is64BitProgram(); vm_manager.Reset(metadata.GetAddressSpaceType()); - // Ensure that the potentially resized page table is seen by CPU backends. - Memory::SetCurrentPageTable(&vm_manager.page_table); const auto& caps = metadata.GetKernelCapabilities(); const auto capability_init_result = @@ -119,7 +116,7 @@ ResultCode Process::LoadFromMetadata(const FileSys::ProgramMetadata& metadata) { return handle_table.SetSize(capabilities.GetHandleTableSize()); } -void Process::Run(VAddr entry_point, s32 main_thread_priority, u64 stack_size) { +void Process::Run(s32 main_thread_priority, u64 stack_size) { // The kernel always ensures that the given stack size is page aligned. main_thread_stack_size = Common::AlignUp(stack_size, Memory::PAGE_SIZE); @@ -135,7 +132,7 @@ void Process::Run(VAddr entry_point, s32 main_thread_priority, u64 stack_size) { vm_manager.LogLayout(); ChangeStatus(ProcessStatus::Running); - SetupMainThread(*this, kernel, entry_point, main_thread_priority); + SetupMainThread(*this, kernel, main_thread_priority); } void Process::PrepareForTermination() { @@ -150,8 +147,7 @@ void Process::PrepareForTermination() { continue; // TODO(Subv): When are the other running/ready threads terminated? - ASSERT_MSG(thread->GetStatus() == ThreadStatus::WaitSynchAny || - thread->GetStatus() == ThreadStatus::WaitSynchAll, + ASSERT_MSG(thread->GetStatus() == ThreadStatus::WaitSynch, "Exiting processes with non-waiting threads is currently unimplemented"); thread->Stop(); @@ -242,9 +238,6 @@ void Process::LoadModule(CodeSet module_, VAddr base_addr) { MapSegment(module_.DataSegment(), VMAPermission::ReadWrite, MemoryState::CodeData); code_memory_size += module_.memory.size(); - - // Clear instruction cache in CPU JIT - system.InvalidateCpuInstructionCaches(); } Process::Process(Core::System& system) diff --git a/src/core/hle/kernel/process.h b/src/core/hle/kernel/process.h index dda52f4c0..bf3b7eef3 100644 --- a/src/core/hle/kernel/process.h +++ b/src/core/hle/kernel/process.h @@ -225,9 +225,12 @@ public: ResultCode LoadFromMetadata(const FileSys::ProgramMetadata& metadata); /** - * Applies address space changes and launches the process main thread. + * Starts the main application thread for this process. + * + * @param main_thread_priority The priority for the main thread. + * @param stack_size The stack size for the main thread in bytes. */ - void Run(VAddr entry_point, s32 main_thread_priority, u64 stack_size); + void Run(s32 main_thread_priority, u64 stack_size); /** * Prepares a process for termination by stopping all of its threads diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index e5d4d6b55..2dcf174c5 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -424,7 +424,7 @@ static ResultCode GetProcessId(Core::System& system, u64* process_id, Handle han /// Default thread wakeup callback for WaitSynchronization static bool DefaultThreadWakeupCallback(ThreadWakeupReason reason, SharedPtr<Thread> thread, SharedPtr<WaitObject> object, std::size_t index) { - ASSERT(thread->GetStatus() == ThreadStatus::WaitSynchAny); + ASSERT(thread->GetStatus() == ThreadStatus::WaitSynch); if (reason == ThreadWakeupReason::Timeout) { thread->SetWaitSynchronizationResult(RESULT_TIMEOUT); @@ -502,7 +502,7 @@ static ResultCode WaitSynchronization(Core::System& system, Handle* index, VAddr } thread->SetWaitObjects(std::move(objects)); - thread->SetStatus(ThreadStatus::WaitSynchAny); + thread->SetStatus(ThreadStatus::WaitSynch); // Create an event to wake the thread up after the specified nanosecond delay has passed thread->WakeAfterDelay(nano_seconds); @@ -518,16 +518,14 @@ static ResultCode CancelSynchronization(Core::System& system, Handle thread_hand LOG_TRACE(Kernel_SVC, "called thread=0x{:X}", thread_handle); const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); - const SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle); + SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle); if (!thread) { LOG_ERROR(Kernel_SVC, "Thread handle does not exist, thread_handle=0x{:08X}", thread_handle); return ERR_INVALID_HANDLE; } - ASSERT(thread->GetStatus() == ThreadStatus::WaitSynchAny); - thread->SetWaitSynchronizationResult(ERR_SYNCHRONIZATION_CANCELED); - thread->ResumeFromWait(); + thread->CancelWait(); return RESULT_SUCCESS; } @@ -1189,6 +1187,142 @@ static ResultCode QueryMemory(Core::System& system, VAddr memory_info_address, query_address); } +static ResultCode MapProcessCodeMemory(Core::System& system, Handle process_handle, u64 dst_address, + u64 src_address, u64 size) { + LOG_DEBUG(Kernel_SVC, + "called. process_handle=0x{:08X}, dst_address=0x{:016X}, " + "src_address=0x{:016X}, size=0x{:016X}", + process_handle, dst_address, src_address, size); + + if (!Common::Is4KBAligned(src_address)) { + LOG_ERROR(Kernel_SVC, "src_address is not page-aligned (src_address=0x{:016X}).", + src_address); + return ERR_INVALID_ADDRESS; + } + + if (!Common::Is4KBAligned(dst_address)) { + LOG_ERROR(Kernel_SVC, "dst_address is not page-aligned (dst_address=0x{:016X}).", + dst_address); + return ERR_INVALID_ADDRESS; + } + + if (size == 0 || !Common::Is4KBAligned(size)) { + LOG_ERROR(Kernel_SVC, "Size is zero or not page-aligned (size=0x{:016X})", size); + return ERR_INVALID_SIZE; + } + + if (!IsValidAddressRange(dst_address, size)) { + LOG_ERROR(Kernel_SVC, + "Destination address range overflows the address space (dst_address=0x{:016X}, " + "size=0x{:016X}).", + dst_address, size); + return ERR_INVALID_ADDRESS_STATE; + } + + if (!IsValidAddressRange(src_address, size)) { + LOG_ERROR(Kernel_SVC, + "Source address range overflows the address space (src_address=0x{:016X}, " + "size=0x{:016X}).", + src_address, size); + return ERR_INVALID_ADDRESS_STATE; + } + + const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); + auto process = handle_table.Get<Process>(process_handle); + if (!process) { + LOG_ERROR(Kernel_SVC, "Invalid process handle specified (handle=0x{:08X}).", + process_handle); + return ERR_INVALID_HANDLE; + } + + auto& vm_manager = process->VMManager(); + if (!vm_manager.IsWithinAddressSpace(src_address, size)) { + LOG_ERROR(Kernel_SVC, + "Source address range is not within the address space (src_address=0x{:016X}, " + "size=0x{:016X}).", + src_address, size); + return ERR_INVALID_ADDRESS_STATE; + } + + if (!vm_manager.IsWithinASLRRegion(dst_address, size)) { + LOG_ERROR(Kernel_SVC, + "Destination address range is not within the ASLR region (dst_address=0x{:016X}, " + "size=0x{:016X}).", + dst_address, size); + return ERR_INVALID_MEMORY_RANGE; + } + + return vm_manager.MapCodeMemory(dst_address, src_address, size); +} + +ResultCode UnmapProcessCodeMemory(Core::System& system, Handle process_handle, u64 dst_address, + u64 src_address, u64 size) { + LOG_DEBUG(Kernel_SVC, + "called. process_handle=0x{:08X}, dst_address=0x{:016X}, src_address=0x{:016X}, " + "size=0x{:016X}", + process_handle, dst_address, src_address, size); + + if (!Common::Is4KBAligned(dst_address)) { + LOG_ERROR(Kernel_SVC, "dst_address is not page-aligned (dst_address=0x{:016X}).", + dst_address); + return ERR_INVALID_ADDRESS; + } + + if (!Common::Is4KBAligned(src_address)) { + LOG_ERROR(Kernel_SVC, "src_address is not page-aligned (src_address=0x{:016X}).", + src_address); + return ERR_INVALID_ADDRESS; + } + + if (size == 0 || Common::Is4KBAligned(size)) { + LOG_ERROR(Kernel_SVC, "Size is zero or not page-aligned (size=0x{:016X}).", size); + return ERR_INVALID_SIZE; + } + + if (!IsValidAddressRange(dst_address, size)) { + LOG_ERROR(Kernel_SVC, + "Destination address range overflows the address space (dst_address=0x{:016X}, " + "size=0x{:016X}).", + dst_address, size); + return ERR_INVALID_ADDRESS_STATE; + } + + if (!IsValidAddressRange(src_address, size)) { + LOG_ERROR(Kernel_SVC, + "Source address range overflows the address space (src_address=0x{:016X}, " + "size=0x{:016X}).", + src_address, size); + return ERR_INVALID_ADDRESS_STATE; + } + + const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); + auto process = handle_table.Get<Process>(process_handle); + if (!process) { + LOG_ERROR(Kernel_SVC, "Invalid process handle specified (handle=0x{:08X}).", + process_handle); + return ERR_INVALID_HANDLE; + } + + auto& vm_manager = process->VMManager(); + if (!vm_manager.IsWithinAddressSpace(src_address, size)) { + LOG_ERROR(Kernel_SVC, + "Source address range is not within the address space (src_address=0x{:016X}, " + "size=0x{:016X}).", + src_address, size); + return ERR_INVALID_ADDRESS_STATE; + } + + if (!vm_manager.IsWithinASLRRegion(dst_address, size)) { + LOG_ERROR(Kernel_SVC, + "Destination address range is not within the ASLR region (dst_address=0x{:016X}, " + "size=0x{:016X}).", + dst_address, size); + return ERR_INVALID_MEMORY_RANGE; + } + + return vm_manager.UnmapCodeMemory(dst_address, src_address, size); +} + /// Exits the current process static void ExitProcess(Core::System& system) { auto* current_process = system.Kernel().CurrentProcess(); @@ -1244,20 +1378,22 @@ static ResultCode CreateThread(Core::System& system, Handle* out_handle, VAddr e return ERR_INVALID_THREAD_PRIORITY; } - const std::string name = fmt::format("thread-{:X}", entry_point); auto& kernel = system.Kernel(); CASCADE_RESULT(SharedPtr<Thread> thread, - Thread::Create(kernel, name, entry_point, priority, arg, processor_id, stack_top, + Thread::Create(kernel, "", entry_point, priority, arg, processor_id, stack_top, *current_process)); - const auto new_guest_handle = current_process->GetHandleTable().Create(thread); - if (new_guest_handle.Failed()) { + const auto new_thread_handle = current_process->GetHandleTable().Create(thread); + if (new_thread_handle.Failed()) { LOG_ERROR(Kernel_SVC, "Failed to create handle with error=0x{:X}", - new_guest_handle.Code().raw); - return new_guest_handle.Code(); + new_thread_handle.Code().raw); + return new_thread_handle.Code(); } - thread->SetGuestHandle(*new_guest_handle); - *out_handle = *new_guest_handle; + *out_handle = *new_thread_handle; + + // Set the thread name for debugging purposes. + thread->SetName( + fmt::format("thread[entry_point={:X}, handle={:X}]", entry_point, *new_thread_handle)); system.CpuCore(thread->GetProcessorID()).PrepareReschedule(); @@ -2152,7 +2288,7 @@ static const FunctionDef SVC_Table[] = { {0x33, SvcWrap<GetThreadContext>, "GetThreadContext"}, {0x34, SvcWrap<WaitForAddress>, "WaitForAddress"}, {0x35, SvcWrap<SignalToAddress>, "SignalToAddress"}, - {0x36, nullptr, "Unknown"}, + {0x36, nullptr, "SynchronizePreemptionState"}, {0x37, nullptr, "Unknown"}, {0x38, nullptr, "Unknown"}, {0x39, nullptr, "Unknown"}, @@ -2217,8 +2353,8 @@ static const FunctionDef SVC_Table[] = { {0x74, nullptr, "MapProcessMemory"}, {0x75, nullptr, "UnmapProcessMemory"}, {0x76, SvcWrap<QueryProcessMemory>, "QueryProcessMemory"}, - {0x77, nullptr, "MapProcessCodeMemory"}, - {0x78, nullptr, "UnmapProcessCodeMemory"}, + {0x77, SvcWrap<MapProcessCodeMemory>, "MapProcessCodeMemory"}, + {0x78, SvcWrap<UnmapProcessCodeMemory>, "UnmapProcessCodeMemory"}, {0x79, nullptr, "CreateProcess"}, {0x7A, nullptr, "StartProcess"}, {0x7B, nullptr, "TerminateProcess"}, diff --git a/src/core/hle/kernel/svc_wrap.h b/src/core/hle/kernel/svc_wrap.h index b3690b5f3..865473c6f 100644 --- a/src/core/hle/kernel/svc_wrap.h +++ b/src/core/hle/kernel/svc_wrap.h @@ -44,6 +44,13 @@ void SvcWrap(Core::System& system) { func(system, static_cast<u32>(Param(system, 0)), static_cast<u32>(Param(system, 1))).raw); } +template <ResultCode func(Core::System&, u32, u64, u64, u64)> +void SvcWrap(Core::System& system) { + FuncReturn(system, func(system, static_cast<u32>(Param(system, 0)), Param(system, 1), + Param(system, 2), Param(system, 3)) + .raw); +} + template <ResultCode func(Core::System&, u32*)> void SvcWrap(Core::System& system) { u32 param = 0; diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index 1b891f632..2abf9efca 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -101,8 +101,7 @@ void Thread::ResumeFromWait() { ASSERT_MSG(wait_objects.empty(), "Thread is waking up while waiting for objects"); switch (status) { - case ThreadStatus::WaitSynchAll: - case ThreadStatus::WaitSynchAny: + case ThreadStatus::WaitSynch: case ThreadStatus::WaitHLEEvent: case ThreadStatus::WaitSleep: case ThreadStatus::WaitIPC: @@ -142,6 +141,12 @@ void Thread::ResumeFromWait() { ChangeScheduler(); } +void Thread::CancelWait() { + ASSERT(GetStatus() == ThreadStatus::WaitSynch); + SetWaitSynchronizationResult(ERR_SYNCHRONIZATION_CANCELED); + ResumeFromWait(); +} + /** * Resets a thread context, making it ready to be scheduled and run by the CPU * @param context Thread context to reset @@ -220,11 +225,6 @@ void Thread::SetPriority(u32 priority) { UpdatePriority(); } -void Thread::BoostPriority(u32 priority) { - scheduler->SetThreadPriority(this, priority); - current_priority = priority; -} - void Thread::SetWaitSynchronizationResult(ResultCode result) { context.cpu_registers[0] = result.raw; } diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index 83c83e45a..f07332f02 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h @@ -49,8 +49,7 @@ enum class ThreadStatus { WaitHLEEvent, ///< Waiting for hle event to finish WaitSleep, ///< Waiting due to a SleepThread SVC WaitIPC, ///< Waiting for the reply from an IPC request - WaitSynchAny, ///< Waiting due to WaitSynch1 or WaitSynchN with wait_all = false - WaitSynchAll, ///< Waiting due to WaitSynchronizationN with wait_all = true + WaitSynch, ///< Waiting due to WaitSynchronization WaitMutex, ///< Waiting due to an ArbitrateLock svc WaitCondVar, ///< Waiting due to an WaitProcessWideKey svc WaitArb, ///< Waiting due to a SignalToAddress/WaitForAddress svc @@ -102,6 +101,11 @@ public: std::string GetName() const override { return name; } + + void SetName(std::string new_name) { + name = std::move(new_name); + } + std::string GetTypeName() const override { return "Thread"; } @@ -136,12 +140,6 @@ public: */ void SetPriority(u32 priority); - /** - * Temporarily boosts the thread's priority until the next time it is scheduled - * @param priority The new priority - */ - void BoostPriority(u32 priority); - /// Adds a thread to the list of threads that are waiting for a lock held by this thread. void AddMutexWaiter(SharedPtr<Thread> thread); @@ -170,11 +168,17 @@ public: return tls_memory; } - /** - * Resumes a thread from waiting - */ + /// Resumes a thread from waiting void ResumeFromWait(); + /// Cancels a waiting operation that this thread may or may not be within. + /// + /// When the thread is within a waiting state, this will set the thread's + /// waiting result to signal a canceled wait. The function will then resume + /// this thread. + /// + void CancelWait(); + /** * Schedules an event to wake up the specified thread after the specified delay * @param nanoseconds The time this thread will be allowed to sleep for @@ -185,24 +189,27 @@ public: void CancelWakeupTimer(); /** - * Sets the result after the thread awakens (from either WaitSynchronization SVC) + * Sets the result after the thread awakens (from svcWaitSynchronization) * @param result Value to set to the returned result */ void SetWaitSynchronizationResult(ResultCode result); /** - * Sets the output parameter value after the thread awakens (from WaitSynchronizationN SVC only) + * Sets the output parameter value after the thread awakens (from svcWaitSynchronization) * @param output Value to set to the output parameter */ void SetWaitSynchronizationOutput(s32 output); /** * Retrieves the index that this particular object occupies in the list of objects - * that the thread passed to WaitSynchronizationN, starting the search from the last element. - * It is used to set the output value of WaitSynchronizationN when the thread is awakened. + * that the thread passed to WaitSynchronization, starting the search from the last element. + * + * It is used to set the output index of WaitSynchronization when the thread is awakened. + * * When a thread wakes up due to an object signal, the kernel will use the index of the last * matching object in the wait objects list in case of having multiple instances of the same * object in the list. + * * @param object Object to query the index of. */ s32 GetWaitObjectIndex(const WaitObject* object) const; @@ -239,13 +246,9 @@ public: */ VAddr GetCommandBufferAddress() const; - /** - * Returns whether this thread is waiting for all the objects in - * its wait list to become ready, as a result of a WaitSynchronizationN call - * with wait_all = true. - */ - bool IsSleepingOnWaitAll() const { - return status == ThreadStatus::WaitSynchAll; + /// Returns whether this thread is waiting on objects from a WaitSynchronization call. + bool IsSleepingOnWait() const { + return status == ThreadStatus::WaitSynch; } ThreadContext& GetContext() { @@ -345,10 +348,6 @@ public: arb_wait_address = address; } - void SetGuestHandle(Handle handle) { - guest_handle = handle; - } - bool HasWakeupCallback() const { return wakeup_callback != nullptr; } @@ -423,7 +422,7 @@ private: Process* owner_process; /// Objects that the thread is waiting on, in the same order as they were - /// passed to WaitSynchronization1/N. + /// passed to WaitSynchronization. ThreadWaitObjects wait_objects; /// List of threads that are waiting for a mutex that is held by this thread. @@ -442,14 +441,11 @@ private: /// If waiting for an AddressArbiter, this is the address being waited on. VAddr arb_wait_address{0}; - /// Handle used by guest emulated application to access this thread - Handle guest_handle = 0; - /// Handle used as userdata to reference this object when inserting into the CoreTiming queue. Handle callback_handle = 0; /// Callback that will be invoked when the thread is resumed from a waiting state. If the thread - /// was waiting via WaitSynchronizationN then the object will be the last object that became + /// was waiting via WaitSynchronization then the object will be the last object that became /// available. In case of a timeout, the object will be nullptr. WakeupCallback wakeup_callback; diff --git a/src/core/hle/kernel/vm_manager.cpp b/src/core/hle/kernel/vm_manager.cpp index ec0a480ce..f0c0c12fc 100644 --- a/src/core/hle/kernel/vm_manager.cpp +++ b/src/core/hle/kernel/vm_manager.cpp @@ -302,6 +302,86 @@ ResultVal<VAddr> VMManager::SetHeapSize(u64 size) { return MakeResult<VAddr>(heap_region_base); } +ResultCode VMManager::MapCodeMemory(VAddr dst_address, VAddr src_address, u64 size) { + constexpr auto ignore_attribute = MemoryAttribute::LockedForIPC | MemoryAttribute::DeviceMapped; + const auto src_check_result = CheckRangeState( + src_address, size, MemoryState::All, MemoryState::Heap, VMAPermission::All, + VMAPermission::ReadWrite, MemoryAttribute::Mask, MemoryAttribute::None, ignore_attribute); + + if (src_check_result.Failed()) { + return src_check_result.Code(); + } + + const auto mirror_result = + MirrorMemory(dst_address, src_address, size, MemoryState::ModuleCode); + if (mirror_result.IsError()) { + return mirror_result; + } + + // Ensure we lock the source memory region. + const auto src_vma_result = CarveVMARange(src_address, size); + if (src_vma_result.Failed()) { + return src_vma_result.Code(); + } + auto src_vma_iter = *src_vma_result; + src_vma_iter->second.attribute = MemoryAttribute::Locked; + Reprotect(src_vma_iter, VMAPermission::Read); + + // The destination memory region is fine as is, however we need to make it read-only. + return ReprotectRange(dst_address, size, VMAPermission::Read); +} + +ResultCode VMManager::UnmapCodeMemory(VAddr dst_address, VAddr src_address, u64 size) { + constexpr auto ignore_attribute = MemoryAttribute::LockedForIPC | MemoryAttribute::DeviceMapped; + const auto src_check_result = CheckRangeState( + src_address, size, MemoryState::All, MemoryState::Heap, VMAPermission::None, + VMAPermission::None, MemoryAttribute::Mask, MemoryAttribute::Locked, ignore_attribute); + + if (src_check_result.Failed()) { + return src_check_result.Code(); + } + + // Yes, the kernel only checks the first page of the region. + const auto dst_check_result = + CheckRangeState(dst_address, Memory::PAGE_SIZE, MemoryState::FlagModule, + MemoryState::FlagModule, VMAPermission::None, VMAPermission::None, + MemoryAttribute::Mask, MemoryAttribute::None, ignore_attribute); + + if (dst_check_result.Failed()) { + return dst_check_result.Code(); + } + + const auto dst_memory_state = std::get<MemoryState>(*dst_check_result); + const auto dst_contiguous_check_result = CheckRangeState( + dst_address, size, MemoryState::All, dst_memory_state, VMAPermission::None, + VMAPermission::None, MemoryAttribute::Mask, MemoryAttribute::None, ignore_attribute); + + if (dst_contiguous_check_result.Failed()) { + return dst_contiguous_check_result.Code(); + } + + const auto unmap_result = UnmapRange(dst_address, size); + if (unmap_result.IsError()) { + return unmap_result; + } + + // With the mirrored portion unmapped, restore the original region's traits. + const auto src_vma_result = CarveVMARange(src_address, size); + if (src_vma_result.Failed()) { + return src_vma_result.Code(); + } + auto src_vma_iter = *src_vma_result; + src_vma_iter->second.state = MemoryState::Heap; + src_vma_iter->second.attribute = MemoryAttribute::None; + Reprotect(src_vma_iter, VMAPermission::ReadWrite); + + if (dst_memory_state == MemoryState::ModuleCode) { + Core::System::GetInstance().InvalidateCpuInstructionCaches(); + } + + return unmap_result; +} + MemoryInfo VMManager::QueryMemory(VAddr address) const { const auto vma = FindVMA(address); MemoryInfo memory_info{}; diff --git a/src/core/hle/kernel/vm_manager.h b/src/core/hle/kernel/vm_manager.h index 6f484b7bf..288eb9450 100644 --- a/src/core/hle/kernel/vm_manager.h +++ b/src/core/hle/kernel/vm_manager.h @@ -43,6 +43,9 @@ enum class VMAPermission : u8 { ReadExecute = Read | Execute, WriteExecute = Write | Execute, ReadWriteExecute = Read | Write | Execute, + + // Used as a wildcard when checking permissions across memory ranges + All = 0xFF, }; constexpr VMAPermission operator|(VMAPermission lhs, VMAPermission rhs) { @@ -152,6 +155,9 @@ enum class MemoryState : u32 { FlagUncached = 1U << 24, FlagCodeMemory = 1U << 25, + // Wildcard used in range checking to indicate all states. + All = 0xFFFFFFFF, + // Convenience flag sets to reduce repetition IPCFlags = FlagIPC0 | FlagIPC3 | FlagIPC1, @@ -415,6 +421,49 @@ public: /// ResultVal<VAddr> SetHeapSize(u64 size); + /// Maps a region of memory as code memory. + /// + /// @param dst_address The base address of the region to create the aliasing memory region. + /// @param src_address The base address of the region to be aliased. + /// @param size The total amount of memory to map in bytes. + /// + /// @pre Both memory regions lie within the actual addressable address space. + /// + /// @post After this function finishes execution, assuming success, then the address range + /// [dst_address, dst_address+size) will alias the memory region, + /// [src_address, src_address+size). + /// <p> + /// What this also entails is as follows: + /// 1. The aliased region gains the Locked memory attribute. + /// 2. The aliased region becomes read-only. + /// 3. The aliasing region becomes read-only. + /// 4. The aliasing region is created with a memory state of MemoryState::CodeModule. + /// + ResultCode MapCodeMemory(VAddr dst_address, VAddr src_address, u64 size); + + /// Unmaps a region of memory designated as code module memory. + /// + /// @param dst_address The base address of the memory region aliasing the source memory region. + /// @param src_address The base address of the memory region being aliased. + /// @param size The size of the memory region to unmap in bytes. + /// + /// @pre Both memory ranges lie within the actual addressable address space. + /// + /// @pre The memory region being unmapped has been previously been mapped + /// by a call to MapCodeMemory. + /// + /// @post After execution of the function, if successful. the aliasing memory region + /// will be unmapped and the aliased region will have various traits about it + /// restored to what they were prior to the original mapping call preceding + /// this function call. + /// <p> + /// What this also entails is as follows: + /// 1. The state of the memory region will now indicate a general heap region. + /// 2. All memory attributes for the memory region are cleared. + /// 3. Memory permissions for the region are restored to user read/write. + /// + ResultCode UnmapCodeMemory(VAddr dst_address, VAddr src_address, u64 size); + /// Queries the memory manager for information about the given address. /// /// @param address The address to query the memory manager about for information. diff --git a/src/core/hle/kernel/wait_object.cpp b/src/core/hle/kernel/wait_object.cpp index 90580ed93..0e96ba872 100644 --- a/src/core/hle/kernel/wait_object.cpp +++ b/src/core/hle/kernel/wait_object.cpp @@ -30,7 +30,7 @@ void WaitObject::RemoveWaitingThread(Thread* thread) { waiting_threads.erase(itr); } -SharedPtr<Thread> WaitObject::GetHighestPriorityReadyThread() { +SharedPtr<Thread> WaitObject::GetHighestPriorityReadyThread() const { Thread* candidate = nullptr; u32 candidate_priority = THREADPRIO_LOWEST + 1; @@ -38,8 +38,7 @@ SharedPtr<Thread> WaitObject::GetHighestPriorityReadyThread() { const ThreadStatus thread_status = thread->GetStatus(); // The list of waiting threads must not contain threads that are not waiting to be awakened. - ASSERT_MSG(thread_status == ThreadStatus::WaitSynchAny || - thread_status == ThreadStatus::WaitSynchAll || + ASSERT_MSG(thread_status == ThreadStatus::WaitSynch || thread_status == ThreadStatus::WaitHLEEvent, "Inconsistent thread statuses in waiting_threads"); @@ -49,10 +48,10 @@ SharedPtr<Thread> WaitObject::GetHighestPriorityReadyThread() { if (ShouldWait(thread.get())) continue; - // A thread is ready to run if it's either in ThreadStatus::WaitSynchAny or - // in ThreadStatus::WaitSynchAll and the rest of the objects it is waiting on are ready. + // A thread is ready to run if it's either in ThreadStatus::WaitSynch + // and the rest of the objects it is waiting on are ready. bool ready_to_run = true; - if (thread_status == ThreadStatus::WaitSynchAll) { + if (thread_status == ThreadStatus::WaitSynch) { ready_to_run = thread->AllWaitObjectsReady(); } @@ -68,33 +67,35 @@ SharedPtr<Thread> WaitObject::GetHighestPriorityReadyThread() { void WaitObject::WakeupWaitingThread(SharedPtr<Thread> thread) { ASSERT(!ShouldWait(thread.get())); - if (!thread) + if (!thread) { return; + } - if (!thread->IsSleepingOnWaitAll()) { - Acquire(thread.get()); - } else { + if (thread->IsSleepingOnWait()) { for (const auto& object : thread->GetWaitObjects()) { ASSERT(!object->ShouldWait(thread.get())); object->Acquire(thread.get()); } + } else { + Acquire(thread.get()); } const std::size_t index = thread->GetWaitObjectIndex(this); - for (const auto& object : thread->GetWaitObjects()) + for (const auto& object : thread->GetWaitObjects()) { object->RemoveWaitingThread(thread.get()); + } thread->ClearWaitObjects(); thread->CancelWakeupTimer(); bool resume = true; - - if (thread->HasWakeupCallback()) + if (thread->HasWakeupCallback()) { resume = thread->InvokeWakeupCallback(ThreadWakeupReason::Signal, thread, this, index); - - if (resume) + } + if (resume) { thread->ResumeFromWait(); + } } void WaitObject::WakeupAllWaitingThreads() { diff --git a/src/core/hle/kernel/wait_object.h b/src/core/hle/kernel/wait_object.h index 04464a51a..3271a30a7 100644 --- a/src/core/hle/kernel/wait_object.h +++ b/src/core/hle/kernel/wait_object.h @@ -54,7 +54,7 @@ public: void WakeupWaitingThread(SharedPtr<Thread> thread); /// Obtains the highest priority thread that is ready to run from this object's waiting list. - SharedPtr<Thread> GetHighestPriorityReadyThread(); + SharedPtr<Thread> GetHighestPriorityReadyThread() const; /// Get a const reference to the waiting threads list for debug use const std::vector<SharedPtr<Thread>>& GetWaitingThreads() const; |