Direct Page Table Access: Handle address spaces less than the full 64-bit in size

This commit is contained in:
MerryMage 2018-02-12 20:49:52 +00:00
parent f45a5e17c6
commit a6cc667509
3 changed files with 45 additions and 20 deletions

View file

@ -112,7 +112,19 @@ struct UserConfig {
const std::uint64_t* tpidrro_el0 = nullptr; const std::uint64_t* tpidrro_el0 = nullptr;
/// Pointer to the page table which we can use for direct page table access. /// Pointer to the page table which we can use for direct page table access.
/// If an entry in page_table is null, the relevant memory callback will be called.
/// If page_table is nullptr, all memory accesses hit the memory callbacks.
void** page_table = nullptr; void** page_table = nullptr;
/// Declares how many valid address bits are there in virtual addresses.
/// Determines the size of page_table. Valid values are between 12 and 64 inclusive.
/// This is only used if page_table is not nullptr.
size_t page_table_address_space_bits = 36;
/// Determines what happens if the guest accesses an entry that is off the end of the
/// page table. If true, Dynarmic will silently mirror page_table's address space. If
/// false, accessing memory outside of page_table bounds will result in a call to the
/// relevant memory callback.
/// This is only used if page_table is not nullptr.
bool silently_mirror_page_table = true;
// Determines whether AddTicks and GetTicksRemaining are called. // Determines whether AddTicks and GetTicksRemaining are called.
// If false, execution will continue until soon after Jit::HaltExecution is called. // If false, execution will continue until soon after Jit::HaltExecution is called.

View file

@ -480,19 +480,38 @@ void A64EmitX64::EmitA64GetTPIDRRO(A64EmitContext& ctx, IR::Inst* inst) {
ctx.reg_alloc.DefineValue(inst, result); ctx.reg_alloc.DefineValue(inst, result);
} }
static std::tuple<Xbyak::Reg, Xbyak::RegExp> EmitVAddrLookup(const A64::UserConfig& conf, BlockOfCode& code, A64EmitContext& ctx, Xbyak::Reg64 vaddr, boost::optional<Xbyak::Reg64> arg_scratch = {}) { static Xbyak::RegExp EmitVAddrLookup(const A64::UserConfig& conf, BlockOfCode& code, A64EmitContext& ctx, Xbyak::Label& abort, Xbyak::Reg64 vaddr, boost::optional<Xbyak::Reg64> arg_scratch = {}) {
constexpr int PAGE_BITS = 12; constexpr size_t PAGE_BITS = 12;
constexpr u64 PAGE_SIZE = 1 << PAGE_BITS; constexpr size_t PAGE_SIZE = 1 << PAGE_BITS;
const size_t valid_page_index_bits = conf.page_table_address_space_bits - PAGE_BITS;
const size_t unused_top_bits = 64 - conf.page_table_address_space_bits;
Xbyak::Reg64 page_table = arg_scratch.value_or_eval([&]{ return ctx.reg_alloc.ScratchGpr(); }); Xbyak::Reg64 page_table = arg_scratch.value_or_eval([&]{ return ctx.reg_alloc.ScratchGpr(); });
Xbyak::Reg64 tmp = ctx.reg_alloc.ScratchGpr(); Xbyak::Reg64 tmp = ctx.reg_alloc.ScratchGpr();
code.mov(page_table, reinterpret_cast<u64>(conf.page_table)); code.mov(page_table, reinterpret_cast<u64>(conf.page_table));
code.mov(tmp, vaddr); code.mov(tmp, vaddr);
code.shr(tmp, PAGE_BITS); if (unused_top_bits == 0) {
code.shr(tmp, int(PAGE_BITS));
} else if (conf.silently_mirror_page_table) {
if (valid_page_index_bits >= 32) {
code.shl(tmp, int(unused_top_bits));
code.shr(tmp, int(unused_top_bits + PAGE_BITS));
} else {
code.shr(tmp, int(PAGE_BITS));
code.and_(tmp, u32((1 << valid_page_index_bits) - 1));
}
} else {
ASSERT(valid_page_index_bits < 32);
code.shr(tmp, int(PAGE_BITS));
code.test(tmp, u32(-(1 << valid_page_index_bits)));
code.jnz(abort, code.T_NEAR);
}
code.mov(page_table, qword[page_table + tmp * sizeof(void*)]); code.mov(page_table, qword[page_table + tmp * sizeof(void*)]);
code.test(page_table, page_table);
code.jz(abort, code.T_NEAR);
code.mov(tmp, vaddr); code.mov(tmp, vaddr);
code.and_(tmp, PAGE_SIZE - 1); code.and_(tmp, static_cast<u32>(PAGE_SIZE - 1));
return {page_table, page_table + tmp}; return page_table + tmp;
} }
void A64EmitX64::EmitDirectPageTableMemoryRead(A64EmitContext& ctx, IR::Inst* inst, size_t bitsize) { void A64EmitX64::EmitDirectPageTableMemoryRead(A64EmitContext& ctx, IR::Inst* inst, size_t bitsize) {
@ -502,9 +521,7 @@ void A64EmitX64::EmitDirectPageTableMemoryRead(A64EmitContext& ctx, IR::Inst* in
Xbyak::Reg64 vaddr = ctx.reg_alloc.UseGpr(args[0]); Xbyak::Reg64 vaddr = ctx.reg_alloc.UseGpr(args[0]);
Xbyak::Reg64 value = ctx.reg_alloc.ScratchGpr(); Xbyak::Reg64 value = ctx.reg_alloc.ScratchGpr();
auto [page, src_ptr] = EmitVAddrLookup(conf, code, ctx, vaddr, value); auto src_ptr = EmitVAddrLookup(conf, code, ctx, abort, vaddr, value);
code.test(page, page);
code.jz(abort, code.T_NEAR);
switch (bitsize) { switch (bitsize) {
case 8: case 8:
code.movzx(value.cvt32(), code.byte[src_ptr]); code.movzx(value.cvt32(), code.byte[src_ptr]);
@ -537,9 +554,7 @@ void A64EmitX64::EmitDirectPageTableMemoryWrite(A64EmitContext& ctx, IR::Inst* i
Xbyak::Reg64 vaddr = ctx.reg_alloc.UseGpr(args[0]); Xbyak::Reg64 vaddr = ctx.reg_alloc.UseGpr(args[0]);
Xbyak::Reg64 value = ctx.reg_alloc.UseGpr(args[1]); Xbyak::Reg64 value = ctx.reg_alloc.UseGpr(args[1]);
auto [page, dest_ptr] = EmitVAddrLookup(conf, code, ctx, vaddr); auto dest_ptr = EmitVAddrLookup(conf, code, ctx, abort, vaddr);
code.test(page, page);
code.jz(abort, code.T_NEAR);
switch (bitsize) { switch (bitsize) {
case 8: case 8:
code.mov(code.byte[dest_ptr], value.cvt8()); code.mov(code.byte[dest_ptr], value.cvt8());
@ -615,10 +630,8 @@ void A64EmitX64::EmitA64ReadMemory128(A64EmitContext& ctx, IR::Inst* inst) {
Xbyak::Reg64 vaddr = ctx.reg_alloc.UseGpr(args[0]); Xbyak::Reg64 vaddr = ctx.reg_alloc.UseGpr(args[0]);
Xbyak::Xmm value = ctx.reg_alloc.ScratchXmm(); Xbyak::Xmm value = ctx.reg_alloc.ScratchXmm();
auto [page, dest_ptr] = EmitVAddrLookup(conf, code, ctx, vaddr); auto src_ptr = EmitVAddrLookup(conf, code, ctx, abort, vaddr);
code.test(page, page); code.movups(value, xword[src_ptr]);
code.jz(abort, code.T_NEAR);
code.movups(value, xword[dest_ptr]);
code.L(end); code.L(end);
code.SwitchToFarCode(); code.SwitchToFarCode();
@ -689,9 +702,7 @@ void A64EmitX64::EmitA64WriteMemory128(A64EmitContext& ctx, IR::Inst* inst) {
Xbyak::Reg64 vaddr = ctx.reg_alloc.UseGpr(args[0]); Xbyak::Reg64 vaddr = ctx.reg_alloc.UseGpr(args[0]);
Xbyak::Xmm value = ctx.reg_alloc.UseXmm(args[1]); Xbyak::Xmm value = ctx.reg_alloc.UseXmm(args[1]);
auto [page, dest_ptr] = EmitVAddrLookup(conf, code, ctx, vaddr); auto dest_ptr = EmitVAddrLookup(conf, code, ctx, abort, vaddr);
code.test(page, page);
code.jz(abort, code.T_NEAR);
code.movups(xword[dest_ptr], value); code.movups(xword[dest_ptr], value);
code.L(end); code.L(end);

View file

@ -40,7 +40,9 @@ public:
: conf(conf) : conf(conf)
, block_of_code(GenRunCodeCallbacks(conf.callbacks, &GetCurrentBlockThunk, this), JitStateInfo{jit_state}) , block_of_code(GenRunCodeCallbacks(conf.callbacks, &GetCurrentBlockThunk, this), JitStateInfo{jit_state})
, emitter(block_of_code, conf) , emitter(block_of_code, conf)
{} {
ASSERT(conf.page_table_address_space_bits >= 12 && conf.page_table_address_space_bits <= 64);
}
~Impl() = default; ~Impl() = default;