tests/A32: Add coprocessor tests
This commit is contained in:
parent
94f5ae4f37
commit
d2deb496da
2 changed files with 229 additions and 0 deletions
228
tests/A32/test_coprocessor.cpp
Normal file
228
tests/A32/test_coprocessor.cpp
Normal file
|
@ -0,0 +1,228 @@
|
||||||
|
/* This file is part of the dynarmic project.
|
||||||
|
* Copyright (c) 2022 MerryMage
|
||||||
|
* SPDX-License-Identifier: 0BSD
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#include <catch2/catch.hpp>
|
||||||
|
|
||||||
|
#include "./testenv.h"
|
||||||
|
#include "dynarmic/frontend/A32/a32_location_descriptor.h"
|
||||||
|
#include "dynarmic/interface/A32/a32.h"
|
||||||
|
#include "dynarmic/interface/A32/coprocessor.h"
|
||||||
|
|
||||||
|
using namespace Dynarmic;
|
||||||
|
|
||||||
|
struct CP15State {
|
||||||
|
u32 cp15_thread_uprw = 0;
|
||||||
|
u32 cp15_thread_uro = 0;
|
||||||
|
u32 cp15_flush_prefetch_buffer = 0; ///< dummy value
|
||||||
|
u32 cp15_data_sync_barrier = 0; ///< dummy value
|
||||||
|
u32 cp15_data_memory_barrier = 0; ///< dummy value
|
||||||
|
};
|
||||||
|
|
||||||
|
class TestCP15 final : public Dynarmic::A32::Coprocessor {
|
||||||
|
public:
|
||||||
|
using CoprocReg = Dynarmic::A32::CoprocReg;
|
||||||
|
|
||||||
|
explicit TestCP15(CP15State&);
|
||||||
|
~TestCP15() override;
|
||||||
|
|
||||||
|
std::optional<Callback> CompileInternalOperation(bool two, unsigned opc1, CoprocReg CRd, CoprocReg CRn, CoprocReg CRm, unsigned opc2) override;
|
||||||
|
CallbackOrAccessOneWord CompileSendOneWord(bool two, unsigned opc1, CoprocReg CRn, CoprocReg CRm, unsigned opc2) override;
|
||||||
|
CallbackOrAccessTwoWords CompileSendTwoWords(bool two, unsigned opc, CoprocReg CRm) override;
|
||||||
|
CallbackOrAccessOneWord CompileGetOneWord(bool two, unsigned opc1, CoprocReg CRn, CoprocReg CRm, unsigned opc2) override;
|
||||||
|
CallbackOrAccessTwoWords CompileGetTwoWords(bool two, unsigned opc, CoprocReg CRm) override;
|
||||||
|
std::optional<Callback> CompileLoadWords(bool two, bool long_transfer, CoprocReg CRd, std::optional<u8> option) override;
|
||||||
|
std::optional<Callback> CompileStoreWords(bool two, bool long_transfer, CoprocReg CRd, std::optional<u8> option) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
CP15State& state;
|
||||||
|
};
|
||||||
|
|
||||||
|
using Callback = Dynarmic::A32::Coprocessor::Callback;
|
||||||
|
using CallbackOrAccessOneWord = Dynarmic::A32::Coprocessor::CallbackOrAccessOneWord;
|
||||||
|
using CallbackOrAccessTwoWords = Dynarmic::A32::Coprocessor::CallbackOrAccessTwoWords;
|
||||||
|
|
||||||
|
TestCP15::TestCP15(CP15State& state)
|
||||||
|
: state(state) {}
|
||||||
|
|
||||||
|
TestCP15::~TestCP15() = default;
|
||||||
|
|
||||||
|
std::optional<Callback> TestCP15::CompileInternalOperation([[maybe_unused]] bool two, [[maybe_unused]] unsigned opc1, [[maybe_unused]] CoprocReg CRd, [[maybe_unused]] CoprocReg CRn, [[maybe_unused]] CoprocReg CRm, [[maybe_unused]] unsigned opc2) {
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
CallbackOrAccessOneWord TestCP15::CompileSendOneWord(bool two, unsigned opc1, CoprocReg CRn, CoprocReg CRm, unsigned opc2) {
|
||||||
|
if (!two && CRn == CoprocReg::C7 && opc1 == 0 && CRm == CoprocReg::C5 && opc2 == 4) {
|
||||||
|
return Callback{
|
||||||
|
[](void* user_arg, std::uint32_t, std::uint32_t) -> std::uint64_t {
|
||||||
|
CP15State& state = *reinterpret_cast<CP15State*>(user_arg);
|
||||||
|
state.cp15_flush_prefetch_buffer = 1;
|
||||||
|
return 0;
|
||||||
|
},
|
||||||
|
reinterpret_cast<void*>(&state),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!two && CRn == CoprocReg::C7 && opc1 == 0 && CRm == CoprocReg::C10) {
|
||||||
|
switch (opc2) {
|
||||||
|
case 4:
|
||||||
|
return Callback{
|
||||||
|
[](void* user_arg, std::uint32_t, std::uint32_t) -> std::uint64_t {
|
||||||
|
CP15State& state = *reinterpret_cast<CP15State*>(user_arg);
|
||||||
|
state.cp15_data_sync_barrier = 1;
|
||||||
|
return 0;
|
||||||
|
},
|
||||||
|
reinterpret_cast<void*>(&state),
|
||||||
|
};
|
||||||
|
case 5:
|
||||||
|
return Callback{
|
||||||
|
[](void* user_arg, std::uint32_t, std::uint32_t) -> std::uint64_t {
|
||||||
|
CP15State& state = *reinterpret_cast<CP15State*>(user_arg);
|
||||||
|
state.cp15_data_memory_barrier = 1;
|
||||||
|
return 0;
|
||||||
|
},
|
||||||
|
reinterpret_cast<void*>(&state),
|
||||||
|
};
|
||||||
|
default:
|
||||||
|
return std::monostate{};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!two && CRn == CoprocReg::C13 && opc1 == 0 && CRm == CoprocReg::C0 && opc2 == 2) {
|
||||||
|
return &state.cp15_thread_uprw;
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::monostate{};
|
||||||
|
}
|
||||||
|
|
||||||
|
CallbackOrAccessTwoWords TestCP15::CompileSendTwoWords([[maybe_unused]] bool two, [[maybe_unused]] unsigned opc, [[maybe_unused]] CoprocReg CRm) {
|
||||||
|
return std::monostate{};
|
||||||
|
}
|
||||||
|
|
||||||
|
CallbackOrAccessOneWord TestCP15::CompileGetOneWord(bool two, unsigned opc1, CoprocReg CRn, CoprocReg CRm, unsigned opc2) {
|
||||||
|
// TODO(merry): Privileged CP15 registers
|
||||||
|
|
||||||
|
if (!two && CRn == CoprocReg::C13 && opc1 == 0 && CRm == CoprocReg::C0) {
|
||||||
|
switch (opc2) {
|
||||||
|
case 2:
|
||||||
|
return &state.cp15_thread_uprw;
|
||||||
|
case 3:
|
||||||
|
return &state.cp15_thread_uro;
|
||||||
|
default:
|
||||||
|
return std::monostate{};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::monostate{};
|
||||||
|
}
|
||||||
|
|
||||||
|
CallbackOrAccessTwoWords TestCP15::CompileGetTwoWords([[maybe_unused]] bool two, [[maybe_unused]] unsigned opc, [[maybe_unused]] CoprocReg CRm) {
|
||||||
|
return std::monostate{};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<Callback> TestCP15::CompileLoadWords([[maybe_unused]] bool two, [[maybe_unused]] bool long_transfer, [[maybe_unused]] CoprocReg CRd, [[maybe_unused]] std::optional<u8> option) {
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<Callback> TestCP15::CompileStoreWords([[maybe_unused]] bool two, [[maybe_unused]] bool long_transfer, [[maybe_unused]] CoprocReg CRd, [[maybe_unused]] std::optional<u8> option) {
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
static A32::UserConfig GetUserConfig(ArmTestEnv* testenv, CP15State& cp15_state) {
|
||||||
|
A32::UserConfig user_config;
|
||||||
|
user_config.optimizations &= ~OptimizationFlag::FastDispatch;
|
||||||
|
user_config.callbacks = testenv;
|
||||||
|
user_config.coprocessors[15] = std::make_unique<TestCP15>(cp15_state);
|
||||||
|
return user_config;
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("arm: Test coprocessor (Read TPIDRURO)", "[arm][A32]") {
|
||||||
|
ArmTestEnv test_env;
|
||||||
|
CP15State cp15_state;
|
||||||
|
A32::Jit jit{GetUserConfig(&test_env, cp15_state)};
|
||||||
|
|
||||||
|
cp15_state.cp15_thread_uro = 0xf00d;
|
||||||
|
cp15_state.cp15_thread_uprw = 0xcafe;
|
||||||
|
jit.Regs()[0] = 0xaaaa;
|
||||||
|
|
||||||
|
test_env.code_mem = {
|
||||||
|
0xee1d1f70, // mrc p15, 0, r1, c13, c0, 3 (Read TPIDRURO into R1)
|
||||||
|
0xeafffffe, // b +#0
|
||||||
|
};
|
||||||
|
|
||||||
|
jit.SetCpsr(0x000001d0); // User-mode
|
||||||
|
|
||||||
|
test_env.ticks_left = 2;
|
||||||
|
jit.Run();
|
||||||
|
|
||||||
|
REQUIRE(jit.Regs()[1] == 0xf00d);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("arm: Test coprocessor (Read TPIDRURW)", "[arm][A32]") {
|
||||||
|
ArmTestEnv test_env;
|
||||||
|
CP15State cp15_state;
|
||||||
|
A32::Jit jit{GetUserConfig(&test_env, cp15_state)};
|
||||||
|
|
||||||
|
cp15_state.cp15_thread_uro = 0xf00d;
|
||||||
|
cp15_state.cp15_thread_uprw = 0xcafe;
|
||||||
|
jit.Regs()[0] = 0xaaaa;
|
||||||
|
|
||||||
|
test_env.code_mem = {
|
||||||
|
0xee1d1f50, // mrc p15, 0, r1, c13, c0, 2 (Read TPIDRURW into R1)
|
||||||
|
0xeafffffe, // b +#0
|
||||||
|
};
|
||||||
|
|
||||||
|
jit.SetCpsr(0x000001d0); // User-mode
|
||||||
|
|
||||||
|
test_env.ticks_left = 2;
|
||||||
|
jit.Run();
|
||||||
|
|
||||||
|
REQUIRE(jit.Regs()[1] == 0xcafe);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("arm: Test coprocessor (Write TPIDRURW)", "[arm][A32]") {
|
||||||
|
ArmTestEnv test_env;
|
||||||
|
CP15State cp15_state;
|
||||||
|
A32::Jit jit{GetUserConfig(&test_env, cp15_state)};
|
||||||
|
|
||||||
|
cp15_state.cp15_thread_uro = 0xf00d;
|
||||||
|
cp15_state.cp15_thread_uprw = 0xcafe;
|
||||||
|
jit.Regs()[0] = 0xaaaa;
|
||||||
|
|
||||||
|
test_env.code_mem = {
|
||||||
|
0xee0d0f50, // mcr p15, 0, r0, c13, c0, 2 (Write R0 into TPIDRURW)
|
||||||
|
0xeafffffe, // b +#0
|
||||||
|
};
|
||||||
|
|
||||||
|
jit.SetCpsr(0x000001d0); // User-mode
|
||||||
|
|
||||||
|
test_env.ticks_left = 2;
|
||||||
|
jit.Run();
|
||||||
|
|
||||||
|
REQUIRE(cp15_state.cp15_thread_uprw == 0xaaaa);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("arm: Test coprocessor (DMB)", "[arm][A32]") {
|
||||||
|
ArmTestEnv test_env;
|
||||||
|
CP15State cp15_state;
|
||||||
|
A32::Jit jit{GetUserConfig(&test_env, cp15_state)};
|
||||||
|
|
||||||
|
cp15_state.cp15_thread_uro = 0xf00d;
|
||||||
|
cp15_state.cp15_thread_uprw = 0xcafe;
|
||||||
|
jit.Regs()[0] = 0xaaaa;
|
||||||
|
|
||||||
|
test_env.code_mem = {
|
||||||
|
0xee070fba, // mcr p15, 0, r0, c7, c10, 5 (Data Memory Barrier)
|
||||||
|
0xeafffffe, // b +#0
|
||||||
|
};
|
||||||
|
|
||||||
|
jit.SetCpsr(0x000001d0); // User-mode
|
||||||
|
|
||||||
|
test_env.ticks_left = 2;
|
||||||
|
jit.Run();
|
||||||
|
|
||||||
|
REQUIRE(cp15_state.cp15_data_memory_barrier == 1);
|
||||||
|
}
|
|
@ -11,6 +11,7 @@ if ("A32" IN_LIST DYNARMIC_FRONTENDS)
|
||||||
target_sources(dynarmic_tests PRIVATE
|
target_sources(dynarmic_tests PRIVATE
|
||||||
A32/test_arm_disassembler.cpp
|
A32/test_arm_disassembler.cpp
|
||||||
A32/test_arm_instructions.cpp
|
A32/test_arm_instructions.cpp
|
||||||
|
A32/test_coprocessor.cpp
|
||||||
A32/test_thumb_instructions.cpp
|
A32/test_thumb_instructions.cpp
|
||||||
A32/testenv.h
|
A32/testenv.h
|
||||||
decoder_tests.cpp
|
decoder_tests.cpp
|
||||||
|
|
Loading…
Reference in a new issue