Emulate TPIDR_EL0 accesses using TLS

This commit is contained in:
Billy Laws 2020-06-27 15:27:09 +01:00 committed by ◱ PixelyIon
parent c423a66020
commit 138e219e0c
3 changed files with 69 additions and 3 deletions

View File

@ -209,6 +209,7 @@ namespace skyline {
}
std::vector<u32> NCE::PatchCode(std::vector<u8> &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<instr::Svc *>(address);
auto instrMrs = reinterpret_cast<instr::Mrs *>(address);
auto instrMsr = reinterpret_cast<instr::Msr *>(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);

View File

@ -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
};

View File

@ -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.
*/