summaryrefslogtreecommitdiffstats
path: root/src/core/hle/kernel
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/hle/kernel')
-rw-r--r--src/core/hle/kernel/kernel.cpp10
-rw-r--r--src/core/hle/kernel/process.cpp25
-rw-r--r--src/core/hle/kernel/process.h7
-rw-r--r--src/core/hle/kernel/svc.cpp170
-rw-r--r--src/core/hle/kernel/svc_wrap.h7
-rw-r--r--src/core/hle/kernel/thread.cpp14
-rw-r--r--src/core/hle/kernel/thread.h58
-rw-r--r--src/core/hle/kernel/vm_manager.cpp80
-rw-r--r--src/core/hle/kernel/vm_manager.h49
-rw-r--r--src/core/hle/kernel/wait_object.cpp31
-rw-r--r--src/core/hle/kernel/wait_object.h2
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;