From 138e219e0ccba0f1398708a081d0a7c5b80ec0c6 Mon Sep 17 00:00:00 2001 From: Billy Laws Date: Sat, 27 Jun 2020 15:27:09 +0100 Subject: [PATCH] Emulate TPIDR_EL0 accesses using TLS --- app/src/main/cpp/skyline/nce.cpp | 48 +++++++++++++++++++-- app/src/main/cpp/skyline/nce/guest_common.h | 1 + app/src/main/cpp/skyline/nce/instructions.h | 23 ++++++++++ 3 files changed, 69 insertions(+), 3 deletions(-) diff --git a/app/src/main/cpp/skyline/nce.cpp b/app/src/main/cpp/skyline/nce.cpp index 560564e8..c624f7d2 100644 --- a/app/src/main/cpp/skyline/nce.cpp +++ b/app/src/main/cpp/skyline/nce.cpp @@ -209,6 +209,7 @@ namespace skyline { } std::vector NCE::PatchCode(std::vector &code, u64 baseAddress, i64 offset) { + constexpr u32 TpidrEl0 = 0x5e82; // ID of TPIDR_EL0 in MRS constexpr u32 TpidrroEl0 = 0x5E83; // ID of TPIDRRO_EL0 in MRS constexpr u32 CntfrqEl0 = 0x5F00; // ID of CNTFRQ_EL0 in MRS constexpr u32 CntpctEl0 = 0x5F01; // ID of CNTPCT_EL0 in MRS @@ -237,6 +238,7 @@ namespace skyline { for (u32 *address = start; address < end; address++) { auto instrSvc = reinterpret_cast(address); auto instrMrs = reinterpret_cast(address); + auto instrMsr = reinterpret_cast(address); if (instrSvc->Verify()) { // If this is an SVC we need to branch to saveCtx then to the SVC Handler after putting the PC + SVC into X0 and W1 and finally loadCtx before returning to where we were before @@ -277,8 +279,8 @@ namespace skyline { patch.push_back(ldrLr); patch.push_back(bret.raw); } else if (instrMrs->Verify()) { - if (instrMrs->srcReg == TpidrroEl0) { - // If this moves TPIDRRO_EL0 into a register then we retrieve the value of our virtual TPIDRRO_EL0 from TLS and write it to the register + if (instrMrs->srcReg == TpidrroEl0 || instrMrs->srcReg == TpidrEl0) { + // If this moves TPIDR(RO)_EL0 into a register then we retrieve the value of our virtual TPIDR(RO)_EL0 from TLS and write it to the register instr::B bJunc(offset); u32 strX0{}; @@ -290,7 +292,12 @@ namespace skyline { constexpr u32 mrsX0 = 0xD53BD040; // MRS X0, TPIDR_EL0 offset += sizeof(mrsX0); - constexpr u32 ldrTls = 0xF9408000; // LDR X0, [X0, #256] (ThreadContext::tpidrroEl0) + u32 ldrTls; + if (instrMrs->srcReg == TpidrroEl0) + ldrTls = 0xF9408000; // LDR X0, [X0, #256] (ThreadContext::tpidrroEl0) + else + ldrTls = 0xF9408400; // LDR X0, [X0, #264] (ThreadContext::tpidrEl0) + offset += sizeof(ldrTls); u32 movXn{}; @@ -362,6 +369,41 @@ namespace skyline { *address = instr::Mrs(CntvctEl0, regs::X(instrMrs->destReg)).raw; } } + } else if (instrMsr->Verify()) { + if (instrMsr->destReg == TpidrEl0) { + // If this moves a register into TPIDR_EL0 then we retrieve the value of the register and write it to our virtual TPIDR_EL0 in TLS + instr::B bJunc(offset); + + // Used to avoid conflicts as we cannot read the source register from the stack + bool x0x1 = instrMrs->srcReg != regs::X0 && instrMrs->srcReg != regs::X1; + + // Push two registers to stack that can be used to load the TLS and arguments into + u32 pushXn = x0x1 ? 0xA9BF07E0 : 0xA9BF0FE2; // STP X(0/2), X(1/3), [SP, #-16]! + offset += sizeof(pushXn); + + u32 loadRealTls = x0x1 ? 0xD53BD040 : 0xD53BD042; // MRS X(0/2), TPIDR_EL0 + offset += sizeof(loadRealTls); + + instr::Mov moveParam(x0x1 ? regs::X1 : regs::X3, regs::X(instrMsr->srcReg)); + offset += sizeof(moveParam); + + u32 storeEmuTls = x0x1 ? 0xF9008401 : 0xF9008403; // STR X(1/3), [X0, #264] (ThreadContext::tpidrEl0) + offset += sizeof(storeEmuTls); + + u32 popXn = x0x1 ? 0xA8C107E0 : 0xA8C10FE2; // LDP X(0/2), X(1/3), [SP], #16 + offset += sizeof(popXn); + + instr::B bret(-offset + sizeof(u32)); + offset += sizeof(bret); + + *address = bJunc.raw; + patch.push_back(pushXn); + patch.push_back(loadRealTls); + patch.push_back(moveParam.raw); + patch.push_back(storeEmuTls); + patch.push_back(popXn); + patch.push_back(bret.raw); + } } offset -= sizeof(u32); diff --git a/app/src/main/cpp/skyline/nce/guest_common.h b/app/src/main/cpp/skyline/nce/guest_common.h index 9cf47cc2..05bcc34e 100644 --- a/app/src/main/cpp/skyline/nce/guest_common.h +++ b/app/src/main/cpp/skyline/nce/guest_common.h @@ -153,6 +153,7 @@ namespace skyline { u64 pc; //!< The program counter register on the guest Registers registers; //!< The general purpose registers on the guest u64 tpidrroEl0; //!< The value for TPIDRRO_EL0 for the current thread + u64 tpidrEl0; //!< The value for TPIDR_EL0 for the current thread u64 faultAddress; //!< The address a fault has occurred at during guest crash u64 sp; //!< The current location of the stack pointer set during guest crash }; diff --git a/app/src/main/cpp/skyline/nce/instructions.h b/app/src/main/cpp/skyline/nce/instructions.h index 2547fb9b..2879126b 100644 --- a/app/src/main/cpp/skyline/nce/instructions.h +++ b/app/src/main/cpp/skyline/nce/instructions.h @@ -101,6 +101,29 @@ namespace skyline { }; static_assert(sizeof(Mrs) == sizeof(u32)); + /** + * @brief A bit-field struct that encapsulates a MSR instruction. See https://developer.arm.com/docs/ddi0596/g/base-instructions-alphabetic-order/msr-register-move-general-purpose-register-to-system-register. + */ + struct Msr { + /** + * @brief Returns if the opcode is valid or not + * @return If the opcode represents a valid MSR instruction + */ + inline constexpr bool Verify() { + return (sig == 0xD51); + } + + union { + struct { + u8 srcReg : 5; //!< 5-bit destination register + u32 destReg : 15; //!< 15-bit source register + u16 sig : 12; //!< 16-bit signature (0xD51) + }; + u32 raw{}; //!< The raw value of the instruction + }; + }; + static_assert(sizeof(Msr) == sizeof(u32)); + /** * @brief A bit-field struct that encapsulates a B instruction. See https://developer.arm.com/docs/ddi0596/latest/base-instructions-alphabetic-order/b-branch. */