Direct Page Table Access: Handle address spaces less than the full 64-bit in size
This commit is contained in:
parent
f45a5e17c6
commit
a6cc667509
3 changed files with 45 additions and 20 deletions
|
@ -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.
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue