diff --git a/src/core/arm/arm_interface.h b/src/core/arm/arm_interface.h index 8416e73b04..28a99defed 100644 --- a/src/core/arm/arm_interface.h +++ b/src/core/arm/arm_interface.h @@ -104,6 +104,10 @@ public: virtual void SetTlsAddress(VAddr address) = 0; + virtual u64 GetTPIDR_EL0() const = 0; + + virtual void SetTPIDR_EL0(u64 value) = 0; + /** * Saves the current CPU context * @param ctx Thread context to save diff --git a/src/core/arm/dynarmic/arm_dynarmic.cpp b/src/core/arm/dynarmic/arm_dynarmic.cpp index 3572ee7b93..df47d5ee88 100644 --- a/src/core/arm/dynarmic/arm_dynarmic.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic.cpp @@ -196,6 +196,14 @@ void ARM_Dynarmic::SetTlsAddress(u64 address) { cb->tpidrro_el0 = address; } +u64 ARM_Dynarmic::GetTPIDR_EL0() const { + return cb->tpidr_el0; +} + +void ARM_Dynarmic::SetTPIDR_EL0(u64 value) { + cb->tpidr_el0 = value; +} + void ARM_Dynarmic::SaveContext(ARM_Interface::ThreadContext& ctx) { ctx.cpu_registers = jit->GetRegisters(); ctx.sp = jit->GetSP(); diff --git a/src/core/arm/dynarmic/arm_dynarmic.h b/src/core/arm/dynarmic/arm_dynarmic.h index ed724c3f16..a9891ac4fb 100644 --- a/src/core/arm/dynarmic/arm_dynarmic.h +++ b/src/core/arm/dynarmic/arm_dynarmic.h @@ -34,6 +34,8 @@ public: void SetCPSR(u32 cpsr) override; VAddr GetTlsAddress() const override; void SetTlsAddress(VAddr address) override; + void SetTPIDR_EL0(u64 value) override; + u64 GetTPIDR_EL0() const override; void SaveContext(ThreadContext& ctx) override; void LoadContext(const ThreadContext& ctx) override; diff --git a/src/core/arm/unicorn/arm_unicorn.cpp b/src/core/arm/unicorn/arm_unicorn.cpp index d2d699e9bd..44a46bf048 100644 --- a/src/core/arm/unicorn/arm_unicorn.cpp +++ b/src/core/arm/unicorn/arm_unicorn.cpp @@ -169,6 +169,16 @@ void ARM_Unicorn::SetTlsAddress(VAddr base) { CHECKED(uc_reg_write(uc, UC_ARM64_REG_TPIDRRO_EL0, &base)); } +u64 ARM_Unicorn::GetTPIDR_EL0() const { + u64 value{}; + CHECKED(uc_reg_read(uc, UC_ARM64_REG_TPIDR_EL0, &value)); + return value; +} + +void ARM_Unicorn::SetTPIDR_EL0(u64 value) { + CHECKED(uc_reg_write(uc, UC_ARM64_REG_TPIDR_EL0, &value)); +} + void ARM_Unicorn::Run() { if (GDBStub::IsServerEnabled()) { ExecuteInstructions(std::max(4000000, 0)); diff --git a/src/core/arm/unicorn/arm_unicorn.h b/src/core/arm/unicorn/arm_unicorn.h index a78a0acf26..af7943352c 100644 --- a/src/core/arm/unicorn/arm_unicorn.h +++ b/src/core/arm/unicorn/arm_unicorn.h @@ -28,6 +28,8 @@ public: void SetCPSR(u32 cpsr) override; VAddr GetTlsAddress() const override; void SetTlsAddress(VAddr address) override; + void SetTPIDR_EL0(u64 value) override; + u64 GetTPIDR_EL0() const override; void SaveContext(ThreadContext& ctx) override; void LoadContext(const ThreadContext& ctx) override; void PrepareReschedule() override; diff --git a/src/core/hle/kernel/scheduler.cpp b/src/core/hle/kernel/scheduler.cpp index 1f4abfbe8b..82829f6bbb 100644 --- a/src/core/hle/kernel/scheduler.cpp +++ b/src/core/hle/kernel/scheduler.cpp @@ -56,6 +56,8 @@ void Scheduler::SwitchContext(Thread* new_thread) { if (previous_thread) { previous_thread->last_running_ticks = CoreTiming::GetTicks(); cpu_core->SaveContext(previous_thread->context); + // Save the TPIDR_EL0 system register in case it was modified. + previous_thread->tpidr_el0 = cpu_core->GetTPIDR_EL0(); if (previous_thread->status == THREADSTATUS_RUNNING) { // This is only the case when a reschedule is triggered without the current thread @@ -87,6 +89,7 @@ void Scheduler::SwitchContext(Thread* new_thread) { cpu_core->LoadContext(new_thread->context); cpu_core->SetTlsAddress(new_thread->GetTLSAddress()); + cpu_core->SetTPIDR_EL0(new_thread->GetTPIDR_EL0()); cpu_core->ClearExclusiveState(); } else { current_thread = nullptr; diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index e7fd6c842f..7f1e18831a 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -313,6 +313,7 @@ ResultVal> Thread::Create(std::string name, VAddr entry_point, thread->status = THREADSTATUS_DORMANT; thread->entry_point = entry_point; thread->stack_top = stack_top; + thread->tpidr_el0 = 0; thread->nominal_priority = thread->current_priority = priority; thread->last_running_ticks = CoreTiming::GetTicks(); thread->processor_id = processor_id; diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index f1e7598026..5fe72c55c3 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h @@ -182,6 +182,14 @@ public: return tls_address; } + /* + * Returns the value of the TPIDR_EL0 Read/Write system register for this thread. + * @returns The value of the TPIDR_EL0 register. + */ + u64 GetTPIDR_EL0() const { + return tpidr_el0; + } + /* * Returns the address of the current thread's command buffer, located in the TLS. * @returns VAddr of the thread's command buffer. @@ -213,6 +221,7 @@ public: s32 processor_id; VAddr tls_address; ///< Virtual address of the Thread Local Storage of the thread + u64 tpidr_el0; ///< TPIDR_EL0 read/write system register. SharedPtr owner_process; ///< Process that owns this thread