A32/A64: Allow std::nullopt from MemoryReadCode
Raise a fault at runtime if this block is executed
This commit is contained in:
parent
5ad1d02351
commit
d40557b751
12 changed files with 99 additions and 64 deletions
|
@ -28,19 +28,22 @@ IR::Block TranslateArm(LocationDescriptor descriptor, TranslateCallbacks* tcb, c
|
|||
bool should_continue = true;
|
||||
do {
|
||||
const u32 arm_pc = visitor.ir.current_location.PC();
|
||||
const u32 arm_instruction = tcb->MemoryReadCode(arm_pc);
|
||||
visitor.current_instruction_size = 4;
|
||||
|
||||
tcb->PreCodeTranslationHook(false, arm_pc, visitor.ir);
|
||||
if (const auto arm_instruction = tcb->MemoryReadCode(arm_pc)) {
|
||||
tcb->PreCodeTranslationHook(false, arm_pc, visitor.ir);
|
||||
|
||||
if (const auto vfp_decoder = DecodeVFP<TranslatorVisitor>(arm_instruction)) {
|
||||
should_continue = vfp_decoder->get().call(visitor, arm_instruction);
|
||||
} else if (const auto asimd_decoder = DecodeASIMD<TranslatorVisitor>(arm_instruction)) {
|
||||
should_continue = asimd_decoder->get().call(visitor, arm_instruction);
|
||||
} else if (const auto decoder = DecodeArm<TranslatorVisitor>(arm_instruction)) {
|
||||
should_continue = decoder->get().call(visitor, arm_instruction);
|
||||
if (const auto vfp_decoder = DecodeVFP<TranslatorVisitor>(*arm_instruction)) {
|
||||
should_continue = vfp_decoder->get().call(visitor, *arm_instruction);
|
||||
} else if (const auto asimd_decoder = DecodeASIMD<TranslatorVisitor>(*arm_instruction)) {
|
||||
should_continue = asimd_decoder->get().call(visitor, *arm_instruction);
|
||||
} else if (const auto decoder = DecodeArm<TranslatorVisitor>(*arm_instruction)) {
|
||||
should_continue = decoder->get().call(visitor, *arm_instruction);
|
||||
} else {
|
||||
should_continue = visitor.arm_UDF();
|
||||
}
|
||||
} else {
|
||||
should_continue = visitor.arm_UDF();
|
||||
should_continue = visitor.RaiseException(Exception::NoExecuteFault);
|
||||
}
|
||||
|
||||
if (visitor.cond_state == ConditionalState::Break) {
|
||||
|
|
|
@ -4,18 +4,19 @@
|
|||
*/
|
||||
#pragma once
|
||||
|
||||
#include <mcl/stdint.hpp>
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
|
||||
namespace Dynarmic::A32 {
|
||||
|
||||
using VAddr = u32;
|
||||
using VAddr = std::uint32_t;
|
||||
|
||||
class IREmitter;
|
||||
|
||||
struct TranslateCallbacks {
|
||||
// All reads through this callback are 4-byte aligned.
|
||||
// Memory must be interpreted as little endian.
|
||||
virtual std::uint32_t MemoryReadCode(VAddr vaddr) = 0;
|
||||
virtual std::optional<std::uint32_t> MemoryReadCode(VAddr vaddr) = 0;
|
||||
|
||||
// Thus function is called before the instruction at pc is interpreted.
|
||||
// IR code can be emitted by the callee prior to translation of the instruction.
|
||||
|
|
|
@ -44,28 +44,40 @@ bool IsUnconditionalInstruction(bool is_thumb_16, u32 instruction) {
|
|||
return false;
|
||||
}
|
||||
|
||||
std::tuple<u32, ThumbInstSize> ReadThumbInstruction(u32 arm_pc, TranslateCallbacks* tcb) {
|
||||
u32 first_part = tcb->MemoryReadCode(arm_pc & 0xFFFFFFFC);
|
||||
if ((arm_pc & 0x2) != 0) {
|
||||
first_part >>= 16;
|
||||
}
|
||||
first_part &= 0xFFFF;
|
||||
std::optional<std::tuple<u32, ThumbInstSize>> ReadThumbInstruction(u32 arm_pc, TranslateCallbacks* tcb) {
|
||||
u32 instruction;
|
||||
|
||||
if (IsThumb16(static_cast<u16>(first_part))) {
|
||||
const std::optional<u32> first_part = tcb->MemoryReadCode(arm_pc & 0xFFFFFFFC);
|
||||
if (!first_part)
|
||||
return std::nullopt;
|
||||
|
||||
if ((arm_pc & 0x2) != 0) {
|
||||
instruction = *first_part >> 16;
|
||||
} else {
|
||||
instruction = *first_part & 0xFFFF;
|
||||
}
|
||||
|
||||
if (IsThumb16(static_cast<u16>(instruction))) {
|
||||
// 16-bit thumb instruction
|
||||
return std::make_tuple(first_part, ThumbInstSize::Thumb16);
|
||||
return std::make_tuple(instruction, ThumbInstSize::Thumb16);
|
||||
}
|
||||
|
||||
// 32-bit thumb instruction
|
||||
// These always start with 0b11101, 0b11110 or 0b11111.
|
||||
|
||||
u32 second_part = tcb->MemoryReadCode((arm_pc + 2) & 0xFFFFFFFC);
|
||||
if (((arm_pc + 2) & 0x2) != 0) {
|
||||
second_part >>= 16;
|
||||
}
|
||||
second_part &= 0xFFFF;
|
||||
instruction <<= 16;
|
||||
|
||||
return std::make_tuple(static_cast<u32>((first_part << 16) | second_part), ThumbInstSize::Thumb32);
|
||||
const std::optional<u32> second_part = tcb->MemoryReadCode((arm_pc + 2) & 0xFFFFFFFC);
|
||||
if (!second_part)
|
||||
return std::nullopt;
|
||||
|
||||
if (((arm_pc + 2) & 0x2) != 0) {
|
||||
instruction |= *second_part >> 16;
|
||||
} else {
|
||||
instruction |= *second_part & 0xFFFF;
|
||||
}
|
||||
|
||||
return std::make_tuple(instruction, ThumbInstSize::Thumb32);
|
||||
}
|
||||
|
||||
// Convert from thumb ASIMD format to ARM ASIMD format.
|
||||
|
@ -97,43 +109,48 @@ IR::Block TranslateThumb(LocationDescriptor descriptor, TranslateCallbacks* tcb,
|
|||
bool should_continue = true;
|
||||
do {
|
||||
const u32 arm_pc = visitor.ir.current_location.PC();
|
||||
const auto [thumb_instruction, inst_size] = ReadThumbInstruction(arm_pc, tcb);
|
||||
const bool is_thumb_16 = inst_size == ThumbInstSize::Thumb16;
|
||||
visitor.current_instruction_size = is_thumb_16 ? 2 : 4;
|
||||
if (const auto maybe_instruction = ReadThumbInstruction(arm_pc, tcb)) {
|
||||
const auto [thumb_instruction, inst_size] = *maybe_instruction;
|
||||
const bool is_thumb_16 = inst_size == ThumbInstSize::Thumb16;
|
||||
visitor.current_instruction_size = is_thumb_16 ? 2 : 4;
|
||||
|
||||
tcb->PreCodeTranslationHook(false, arm_pc, visitor.ir);
|
||||
tcb->PreCodeTranslationHook(false, arm_pc, visitor.ir);
|
||||
|
||||
if (IsUnconditionalInstruction(is_thumb_16, thumb_instruction) || visitor.ThumbConditionPassed()) {
|
||||
if (is_thumb_16) {
|
||||
if (const auto decoder = DecodeThumb16<TranslatorVisitor>(static_cast<u16>(thumb_instruction))) {
|
||||
should_continue = decoder->get().call(visitor, static_cast<u16>(thumb_instruction));
|
||||
if (IsUnconditionalInstruction(is_thumb_16, thumb_instruction) || visitor.ThumbConditionPassed()) {
|
||||
if (is_thumb_16) {
|
||||
if (const auto decoder = DecodeThumb16<TranslatorVisitor>(static_cast<u16>(thumb_instruction))) {
|
||||
should_continue = decoder->get().call(visitor, static_cast<u16>(thumb_instruction));
|
||||
} else {
|
||||
should_continue = visitor.thumb16_UDF();
|
||||
}
|
||||
} else {
|
||||
should_continue = visitor.thumb16_UDF();
|
||||
}
|
||||
} else {
|
||||
if (MaybeVFPOrASIMDInstruction(thumb_instruction)) {
|
||||
if (const auto vfp_decoder = DecodeVFP<TranslatorVisitor>(thumb_instruction)) {
|
||||
should_continue = vfp_decoder->get().call(visitor, thumb_instruction);
|
||||
} else if (const auto asimd_decoder = DecodeASIMD<TranslatorVisitor>(ConvertASIMDInstruction(thumb_instruction))) {
|
||||
should_continue = asimd_decoder->get().call(visitor, ConvertASIMDInstruction(thumb_instruction));
|
||||
if (MaybeVFPOrASIMDInstruction(thumb_instruction)) {
|
||||
if (const auto vfp_decoder = DecodeVFP<TranslatorVisitor>(thumb_instruction)) {
|
||||
should_continue = vfp_decoder->get().call(visitor, thumb_instruction);
|
||||
} else if (const auto asimd_decoder = DecodeASIMD<TranslatorVisitor>(ConvertASIMDInstruction(thumb_instruction))) {
|
||||
should_continue = asimd_decoder->get().call(visitor, ConvertASIMDInstruction(thumb_instruction));
|
||||
} else if (const auto decoder = DecodeThumb32<TranslatorVisitor>(thumb_instruction)) {
|
||||
should_continue = decoder->get().call(visitor, thumb_instruction);
|
||||
} else {
|
||||
should_continue = visitor.thumb32_UDF();
|
||||
}
|
||||
} else if (const auto decoder = DecodeThumb32<TranslatorVisitor>(thumb_instruction)) {
|
||||
should_continue = decoder->get().call(visitor, thumb_instruction);
|
||||
} else {
|
||||
should_continue = visitor.thumb32_UDF();
|
||||
}
|
||||
} else if (const auto decoder = DecodeThumb32<TranslatorVisitor>(thumb_instruction)) {
|
||||
should_continue = decoder->get().call(visitor, thumb_instruction);
|
||||
} else {
|
||||
should_continue = visitor.thumb32_UDF();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
visitor.current_instruction_size = 2;
|
||||
should_continue = visitor.RaiseException(Exception::NoExecuteFault);
|
||||
}
|
||||
|
||||
if (visitor.cond_state == ConditionalState::Break) {
|
||||
break;
|
||||
}
|
||||
|
||||
visitor.ir.current_location = visitor.ir.current_location.AdvancePC(is_thumb_16 ? 2 : 4).AdvanceIT();
|
||||
visitor.ir.current_location = visitor.ir.current_location.AdvancePC(static_cast<int>(visitor.current_instruction_size)).AdvanceIT();
|
||||
block.CycleCount()++;
|
||||
} while (should_continue && CondCanContinue(visitor.cond_state, visitor.ir) && !single_step);
|
||||
|
||||
|
|
|
@ -22,12 +22,15 @@ IR::Block Translate(LocationDescriptor descriptor, MemoryReadCodeFuncType memory
|
|||
bool should_continue = true;
|
||||
do {
|
||||
const u64 pc = visitor.ir.current_location->PC();
|
||||
const u32 instruction = memory_read_code(pc);
|
||||
|
||||
if (auto decoder = Decode<TranslatorVisitor>(instruction)) {
|
||||
should_continue = decoder->get().call(visitor, instruction);
|
||||
if (const auto instruction = memory_read_code(pc)) {
|
||||
if (auto decoder = Decode<TranslatorVisitor>(*instruction)) {
|
||||
should_continue = decoder->get().call(visitor, *instruction);
|
||||
} else {
|
||||
should_continue = visitor.InterpretThisInstruction();
|
||||
}
|
||||
} else {
|
||||
should_continue = visitor.InterpretThisInstruction();
|
||||
should_continue = visitor.RaiseException(Exception::NoExecuteFault);
|
||||
}
|
||||
|
||||
visitor.ir.current_location = visitor.ir.current_location->AdvancePC(4);
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <optional>
|
||||
|
||||
#include <mcl/stdint.hpp>
|
||||
|
||||
|
@ -18,7 +19,7 @@ namespace A64 {
|
|||
|
||||
class LocationDescriptor;
|
||||
|
||||
using MemoryReadCodeFuncType = std::function<u32(u64 vaddr)>;
|
||||
using MemoryReadCodeFuncType = std::function<std::optional<u32>(u64 vaddr)>;
|
||||
|
||||
struct TranslationOptions {
|
||||
/// This changes what IR we emit when we translate an unpredictable instruction.
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
|
||||
#include "dynarmic/frontend/A32/translate/translate_callbacks.h"
|
||||
#include "dynarmic/interface/A32/arch_version.h"
|
||||
|
@ -51,6 +52,9 @@ enum class Exception {
|
|||
PreloadDataWithIntentToWrite,
|
||||
/// A PLI instruction was executed. (Hint instruction.)
|
||||
PreloadInstruction,
|
||||
/// Attempted to execute a code block at an address for which MemoryReadCode returned std::nullopt.
|
||||
/// (Intended to be used to emulate memory protection faults.)
|
||||
NoExecuteFault,
|
||||
};
|
||||
|
||||
/// These function pointers may be inserted into compiled code.
|
||||
|
@ -59,7 +63,7 @@ struct UserCallbacks : public TranslateCallbacks {
|
|||
|
||||
// All reads through this callback are 4-byte aligned.
|
||||
// Memory must be interpreted as little endian.
|
||||
std::uint32_t MemoryReadCode(VAddr vaddr) override { return MemoryRead32(vaddr); }
|
||||
std::optional<std::uint32_t> MemoryReadCode(VAddr vaddr) override { return MemoryRead32(vaddr); }
|
||||
|
||||
// Thus function is called before the instruction at pc is interpreted.
|
||||
// IR code can be emitted by the callee prior to translation of the instruction.
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
|
||||
#include "dynarmic/interface/optimization_flags.h"
|
||||
|
||||
|
@ -45,6 +46,9 @@ enum class Exception {
|
|||
Yield,
|
||||
/// A BRK instruction was executed. (Hint instruction.)
|
||||
Breakpoint,
|
||||
/// Attempted to execute a code block at an address for which MemoryReadCode returned std::nullopt.
|
||||
/// (Intended to be used to emulate memory protection faults.)
|
||||
NoExecuteFault,
|
||||
};
|
||||
|
||||
enum class DataCacheOperation {
|
||||
|
@ -82,7 +86,7 @@ struct UserCallbacks {
|
|||
|
||||
// All reads through this callback are 4-byte aligned.
|
||||
// Memory must be interpreted as little endian.
|
||||
virtual std::uint32_t MemoryReadCode(VAddr vaddr) { return MemoryRead32(vaddr); }
|
||||
virtual std::optional<std::uint32_t> MemoryReadCode(VAddr vaddr) { return MemoryRead32(vaddr); }
|
||||
|
||||
// Reads through these callbacks may not be aligned.
|
||||
virtual std::uint8_t MemoryRead8(VAddr vaddr) = 0;
|
||||
|
|
|
@ -16,10 +16,12 @@ namespace Dynarmic::Optimization {
|
|||
|
||||
void A64MergeInterpretBlocksPass(IR::Block& block, A64::UserCallbacks* cb) {
|
||||
const auto is_interpret_instruction = [cb](A64::LocationDescriptor location) {
|
||||
const u32 instruction = cb->MemoryReadCode(location.PC());
|
||||
const auto instruction = cb->MemoryReadCode(location.PC());
|
||||
if (!instruction)
|
||||
return false;
|
||||
|
||||
IR::Block new_block{location};
|
||||
A64::TranslateSingleInstruction(new_block, location, instruction);
|
||||
A64::TranslateSingleInstruction(new_block, location, *instruction);
|
||||
|
||||
if (!new_block.Instructions().empty())
|
||||
return false;
|
||||
|
|
|
@ -48,7 +48,7 @@ public:
|
|||
return vaddr < sizeof(InstructionType) * code_mem.size();
|
||||
}
|
||||
|
||||
std::uint32_t MemoryReadCode(u32 vaddr) override {
|
||||
std::optional<std::uint32_t> MemoryReadCode(u32 vaddr) override {
|
||||
if (IsInCodeMem(vaddr)) {
|
||||
u32 value;
|
||||
std::memcpy(&value, &code_mem[vaddr / sizeof(InstructionType)], sizeof(u32));
|
||||
|
@ -95,11 +95,11 @@ public:
|
|||
MemoryWrite32(vaddr + 4, static_cast<u32>(value >> 32));
|
||||
}
|
||||
|
||||
void InterpreterFallback(u32 pc, size_t num_instructions) override { ASSERT_MSG(false, "InterpreterFallback({:08x}, {}) code = {:08x}", pc, num_instructions, MemoryReadCode(pc)); }
|
||||
void InterpreterFallback(u32 pc, size_t num_instructions) override { ASSERT_MSG(false, "InterpreterFallback({:08x}, {}) code = {:08x}", pc, num_instructions, *MemoryReadCode(pc)); }
|
||||
|
||||
void CallSVC(std::uint32_t swi) override { ASSERT_MSG(false, "CallSVC({})", swi); }
|
||||
|
||||
void ExceptionRaised(u32 pc, Dynarmic::A32::Exception /*exception*/) override { ASSERT_MSG(false, "ExceptionRaised({:08x}) code = {:08x}", pc, MemoryReadCode(pc)); }
|
||||
void ExceptionRaised(u32 pc, Dynarmic::A32::Exception /*exception*/) override { ASSERT_MSG(false, "ExceptionRaised({:08x}) code = {:08x}", pc, *MemoryReadCode(pc)); }
|
||||
|
||||
void AddTicks(std::uint64_t ticks) override {
|
||||
if (ticks > ticks_left) {
|
||||
|
@ -135,7 +135,7 @@ public:
|
|||
memcpy(backing_memory + vaddr, &value, sizeof(T));
|
||||
}
|
||||
|
||||
std::uint32_t MemoryReadCode(std::uint32_t vaddr) override {
|
||||
std::optional<std::uint32_t> MemoryReadCode(std::uint32_t vaddr) override {
|
||||
return read<std::uint32_t>(vaddr);
|
||||
}
|
||||
|
||||
|
|
|
@ -30,7 +30,7 @@ public:
|
|||
return vaddr >= code_mem_start_address && vaddr < code_mem_start_address + code_mem.size() * 4;
|
||||
}
|
||||
|
||||
std::uint32_t MemoryReadCode(u64 vaddr) override {
|
||||
std::optional<std::uint32_t> MemoryReadCode(u64 vaddr) override {
|
||||
if (!IsInCodeMem(vaddr)) {
|
||||
return 0x14000000; // B .
|
||||
}
|
||||
|
@ -145,7 +145,7 @@ public:
|
|||
memcpy(backing_memory + vaddr, &value, sizeof(T));
|
||||
}
|
||||
|
||||
std::uint32_t MemoryReadCode(u64 vaddr) override {
|
||||
std::optional<std::uint32_t> MemoryReadCode(u64 vaddr) override {
|
||||
return read<std::uint32_t>(vaddr);
|
||||
}
|
||||
|
||||
|
|
|
@ -157,7 +157,7 @@ public:
|
|||
}
|
||||
|
||||
void InterpreterFallback(u32 pc, size_t num_instructions) override {
|
||||
fmt::print("> InterpreterFallback({:08x}, {}) code = {:08x}\n", pc, num_instructions, MemoryReadCode(pc));
|
||||
fmt::print("> InterpreterFallback({:08x}, {}) code = {:08x}\n", pc, num_instructions, *MemoryReadCode(pc));
|
||||
}
|
||||
void CallSVC(std::uint32_t swi) override {
|
||||
fmt::print("> CallSVC({})\n", swi);
|
||||
|
|
|
@ -52,7 +52,7 @@ void A32Unicorn<TestEnvironment>::Run() {
|
|||
return;
|
||||
}
|
||||
if (auto cerr_ = uc_emu_start(uc, pc, END_ADDRESS, 0, 1)) {
|
||||
fmt::print("uc_emu_start failed @ {:08x} (code = {:08x}) with error {} ({})", pc, testenv.MemoryReadCode(pc), cerr_, uc_strerror(cerr_));
|
||||
fmt::print("uc_emu_start failed @ {:08x} (code = {:08x}) with error {} ({})", pc, *testenv.MemoryReadCode(pc), cerr_, uc_strerror(cerr_));
|
||||
throw "A32Unicorn::Run() failure";
|
||||
}
|
||||
testenv.ticks_left--;
|
||||
|
|
Loading…
Reference in a new issue