diff --git a/src/core/arm/arm_interface.h b/src/core/arm/arm_interface.h
index 8416e73b0467a99cc589a4324511ddd9332de824..28a99defed85f6fc20f17d92936fa57a686e9bb4 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 3572ee7b93d31cdeb088956a76d128b772e5377e..df47d5ee88c93304fc692dc4509d5ceec6baee22 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 ed724c3f168b9005e9733860ea11f786d35324e3..a9891ac4fbebb97915755c93df4427f84cff70a1 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 d2d699e9bd9ea4cf32ad4528dd2e9834f80e5d32..44a46bf04844839392228fa6a807d5938df7dfb4 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 a78a0acf2612833a9dee63e53c7bae95bd9909a5..af7943352cd0c03ea7e4ff1af7d441666a90ede6 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 1f4abfbe8b27ce667f7d836c6c90051896a4c8a8..82829f6bbb9c836dc788a3a01e6224609cc69a45 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 e7fd6c842fd13dcffc7400dcd4bf134d6205e067..7f1e18831a281abb6cbc1762ac911094cf12d316 100644
--- a/src/core/hle/kernel/thread.cpp
+++ b/src/core/hle/kernel/thread.cpp
@@ -313,6 +313,7 @@ ResultVal<SharedPtr<Thread>> 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 f1e759802671c926877afc8dcdf10cece18855de..5fe72c55c350cbc21da0c780a4b8163b95963269 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<Process> owner_process; ///< Process that owns this thread