diff --git a/include/dynarmic/A64/config.h b/include/dynarmic/A64/config.h index 93315d51..145c98f2 100644 --- a/include/dynarmic/A64/config.h +++ b/include/dynarmic/A64/config.h @@ -120,6 +120,10 @@ struct UserConfig { /// Executing DC ZVA in this mode will result in zeros being written to memory. bool hook_data_cache_operations = false; + /// Counter-timer frequency register. The value of the register is not interpreted by + /// dynarmic. + std::uint32_t cntfrq_el0 = 600000000; + /// CTR_EL0<27:24> is log2 of the cache writeback granule in words. /// CTR_EL0<23:20> is log2 of the exclusives reservation granule in words. /// CTR_EL0<19:16> is log2 of the smallest data/unifed cacheline in words. diff --git a/src/backend_x64/a64_emit_x64.cpp b/src/backend_x64/a64_emit_x64.cpp index e8a5eb05..7c59a3f1 100644 --- a/src/backend_x64/a64_emit_x64.cpp +++ b/src/backend_x64/a64_emit_x64.cpp @@ -538,6 +538,12 @@ void A64EmitX64::EmitA64DataMemoryBarrier(A64EmitContext&, IR::Inst*) { code.lfence(); } +void A64EmitX64::EmitA64GetCNTFRQ(A64EmitContext& ctx, IR::Inst* inst) { + Xbyak::Reg32 result = ctx.reg_alloc.ScratchGpr().cvt32(); + code.mov(result, conf.cntfrq_el0); + ctx.reg_alloc.DefineValue(inst, result); +} + void A64EmitX64::EmitA64GetCNTPCT(A64EmitContext& ctx, IR::Inst* inst) { ctx.reg_alloc.HostCall(inst); code.UpdateTicks(); @@ -567,16 +573,6 @@ void A64EmitX64::EmitA64GetTPIDR(A64EmitContext& ctx, IR::Inst* inst) { ctx.reg_alloc.DefineValue(inst, result); } -void A64EmitX64::EmitA64SetTPIDR(A64EmitContext& ctx, IR::Inst* inst) { - auto args = ctx.reg_alloc.GetArgumentInfo(inst); - Xbyak::Reg64 value = ctx.reg_alloc.UseGpr(args[0]); - Xbyak::Reg64 addr = ctx.reg_alloc.ScratchGpr(); - if (conf.tpidr_el0) { - code.mov(addr, u64(conf.tpidr_el0)); - code.mov(qword[addr], value); - } -} - void A64EmitX64::EmitA64GetTPIDRRO(A64EmitContext& ctx, IR::Inst* inst) { Xbyak::Reg64 result = ctx.reg_alloc.ScratchGpr(); if (conf.tpidrro_el0) { @@ -588,6 +584,16 @@ void A64EmitX64::EmitA64GetTPIDRRO(A64EmitContext& ctx, IR::Inst* inst) { ctx.reg_alloc.DefineValue(inst, result); } +void A64EmitX64::EmitA64SetTPIDR(A64EmitContext& ctx, IR::Inst* inst) { + auto args = ctx.reg_alloc.GetArgumentInfo(inst); + Xbyak::Reg64 value = ctx.reg_alloc.UseGpr(args[0]); + Xbyak::Reg64 addr = ctx.reg_alloc.ScratchGpr(); + if (conf.tpidr_el0) { + code.mov(addr, u64(conf.tpidr_el0)); + code.mov(qword[addr], value); + } +} + void A64EmitX64::EmitA64ClearExclusive(A64EmitContext&, IR::Inst*) { code.mov(code.byte[r15 + offsetof(A64JitState, exclusive_state)], u8(0)); } diff --git a/src/frontend/A64/ir_emitter.cpp b/src/frontend/A64/ir_emitter.cpp index 5a0a850c..2b6f287d 100644 --- a/src/frontend/A64/ir_emitter.cpp +++ b/src/frontend/A64/ir_emitter.cpp @@ -57,6 +57,10 @@ void IREmitter::DataMemoryBarrier() { Inst(Opcode::A64DataMemoryBarrier); } +IR::U32 IREmitter::GetCNTFRQ() { + return Inst(Opcode::A64GetCNTFRQ); +} + IR::U64 IREmitter::GetCNTPCT() { return Inst(Opcode::A64GetCNTPCT); } diff --git a/src/frontend/A64/ir_emitter.h b/src/frontend/A64/ir_emitter.h index b10eaed7..a97d8e1a 100644 --- a/src/frontend/A64/ir_emitter.h +++ b/src/frontend/A64/ir_emitter.h @@ -45,12 +45,13 @@ public: void DataCacheOperationRaised(DataCacheOperation op, const IR::U64& value); void DataSynchronizationBarrier(); void DataMemoryBarrier(); + IR::U32 GetCNTFRQ(); IR::U64 GetCNTPCT(); // TODO: Ensure sub-basic-block cycle counts are updated before this. IR::U32 GetCTR(); IR::U32 GetDCZID(); IR::U64 GetTPIDR(); - void SetTPIDR(const IR::U64& value); IR::U64 GetTPIDRRO(); + void SetTPIDR(const IR::U64& value); void ClearExclusive(); void SetExclusive(const IR::U64& vaddr, size_t byte_size); diff --git a/src/frontend/A64/translate/impl/system.cpp b/src/frontend/A64/translate/impl/system.cpp index 859bc44e..e670eb75 100644 --- a/src/frontend/A64/translate/impl/system.cpp +++ b/src/frontend/A64/translate/impl/system.cpp @@ -10,6 +10,8 @@ namespace Dynarmic::A64 { // Register encodings used by MRS and MSR. enum class SystemRegisterEncoding : u32 { + // Counter-timer Frequency register + CNTFRQ_EL0 = 0b11'011'1110'0000'000, // Counter-timer Physical Count register CNTPCT_EL0 = 0b11'011'1110'0000'001, // Cache Type Register @@ -72,9 +74,6 @@ bool TranslatorVisitor::DMB(Imm<4> /*CRm*/) { bool TranslatorVisitor::MSR_reg(Imm<1> o0, Imm<3> op1, Imm<4> CRn, Imm<4> CRm, Imm<3> op2, Reg Rt) { const auto sys_reg = concatenate(Imm<1>{1}, o0, op1, CRn, CRm, op2).ZeroExtend(); switch (sys_reg) { - case SystemRegisterEncoding::TPIDR_EL0: - ir.SetTPIDR(X(64, Rt)); - return true; case SystemRegisterEncoding::FPCR: ir.SetFPCR(X(32, Rt)); ir.SetPC(ir.Imm64(ir.current_location->PC() + 4)); @@ -83,6 +82,9 @@ bool TranslatorVisitor::MSR_reg(Imm<1> o0, Imm<3> op1, Imm<4> CRn, Imm<4> CRm, I case SystemRegisterEncoding::FPSR: ir.SetFPSR(X(32, Rt)); return true; + case SystemRegisterEncoding::TPIDR_EL0: + ir.SetTPIDR(X(64, Rt)); + return true; default: break; } @@ -92,17 +94,8 @@ bool TranslatorVisitor::MSR_reg(Imm<1> o0, Imm<3> op1, Imm<4> CRn, Imm<4> CRm, I bool TranslatorVisitor::MRS(Imm<1> o0, Imm<3> op1, Imm<4> CRn, Imm<4> CRm, Imm<3> op2, Reg Rt) { const auto sys_reg = concatenate(Imm<1>{1}, o0, op1, CRn, CRm, op2).ZeroExtend(); switch (sys_reg) { - case SystemRegisterEncoding::TPIDR_EL0: - X(64, Rt, ir.GetTPIDR()); - return true; - case SystemRegisterEncoding::TPIDRRO_EL0: - X(64, Rt, ir.GetTPIDRRO()); - return true; - case SystemRegisterEncoding::DCZID_EL0: - X(32, Rt, ir.GetDCZID()); - return true; - case SystemRegisterEncoding::CTR_EL0: - X(32, Rt, ir.GetCTR()); + case SystemRegisterEncoding::CNTFRQ_EL0: + X(32, Rt, ir.GetCNTFRQ()); return true; case SystemRegisterEncoding::CNTPCT_EL0: // HACK: Ensure that this is the first instruction in the block it's emitted in, so the cycle count is most up-to-date. @@ -113,12 +106,25 @@ bool TranslatorVisitor::MRS(Imm<1> o0, Imm<3> op1, Imm<4> CRn, Imm<4> CRm, Imm<3 } X(64, Rt, ir.GetCNTPCT()); return true; + case SystemRegisterEncoding::CTR_EL0: + X(32, Rt, ir.GetCTR()); + return true; + case SystemRegisterEncoding::DCZID_EL0: + X(32, Rt, ir.GetDCZID()); + return true; case SystemRegisterEncoding::FPCR: X(32, Rt, ir.GetFPCR()); return true; case SystemRegisterEncoding::FPSR: X(32, Rt, ir.GetFPSR()); return true; + case SystemRegisterEncoding::TPIDR_EL0: + X(64, Rt, ir.GetTPIDR()); + return true; + case SystemRegisterEncoding::TPIDRRO_EL0: + X(64, Rt, ir.GetTPIDRRO()); + return true; + } return InterpretThisInstruction(); } diff --git a/src/frontend/ir/opcodes.inc b/src/frontend/ir/opcodes.inc index dcadb703..69030b32 100644 --- a/src/frontend/ir/opcodes.inc +++ b/src/frontend/ir/opcodes.inc @@ -66,12 +66,13 @@ A64OPC(ExceptionRaised, T::Void, T::U64, A64OPC(DataCacheOperationRaised, T::Void, T::U64, T::U64 ) A64OPC(DataSynchronizationBarrier, T::Void, ) A64OPC(DataMemoryBarrier, T::Void, ) +A64OPC(GetCNTFRQ, T::U32, ) A64OPC(GetCNTPCT, T::U64, ) A64OPC(GetCTR, T::U32, ) A64OPC(GetDCZID, T::U32, ) A64OPC(GetTPIDR, T::U64, ) -A64OPC(SetTPIDR, T::Void, T::U64 ) A64OPC(GetTPIDRRO, T::U64, ) +A64OPC(SetTPIDR, T::Void, T::U64 ) // Hints OPCODE(PushRSB, T::Void, T::U64 ) diff --git a/tests/A64/fuzz_with_unicorn.cpp b/tests/A64/fuzz_with_unicorn.cpp index 90cd3afa..35a35499 100644 --- a/tests/A64/fuzz_with_unicorn.cpp +++ b/tests/A64/fuzz_with_unicorn.cpp @@ -78,6 +78,8 @@ static u32 GenRandomInst(u64 pc, bool is_last_inst) { "STXR", "STLXR", "STXP", "STLXP", "LDXR", "LDAXR", "LDXP", "LDAXP", // QEMU's implementation of FDIV is incorrect "FDIV_1", "FDIV_2", + // Behaviour differs from QEMU + "MSR_reg", "MSR_imm", "MRS", }; for (const auto& [fn, bitstring] : list) {