Skip to content

Commit

Permalink
[a64] Implement OPCODE_CACHE_CONTROL
Browse files Browse the repository at this point in the history
`dc civac` causes an illegal-instruciton on Windows-ARM. This is likely
as a security measure against cache-attacks. On Linux this instruction
is trapped into an EL1 kernel function. Windows does not seem to have
any user-mode cache-maintenance instructions available for
data-cache(only instruction-cache via `FlushInstructionCache`).

The closest thing we can do for now is a full data memory-barrier with
`dsb ish`.

Prefetches are implemented using `prfm pldl1keep, ...`.
  • Loading branch information
Wunkolo committed Jun 2, 2024
1 parent 1076c28 commit c785488
Showing 1 changed file with 67 additions and 1 deletion.
68 changes: 67 additions & 1 deletion src/xenia/cpu/backend/a64/a64_seq_memory.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1082,7 +1082,73 @@ struct CACHE_CONTROL
}
size_t cache_line_size = i.src2.value;

// TODO(wunkolo): Arm64 cache-control
XReg addr = X0;
uint32_t address_constant;
if (i.src1.is_constant) {
// TODO(benvanik): figure out how to do this without a temp.
// Since the constant is often 0x8... if we tried to use that as a
// displacement it would be sign extended and mess things up.
address_constant = static_cast<uint32_t>(i.src1.constant());
if (address_constant < 0x80000000) {
e.ADD(addr, e.GetMembaseReg(), address_constant);
} else {
if (address_constant >= 0xE0000000 &&
xe::memory::allocation_granularity() > 0x1000) {
e.MOV(X1, address_constant + 0x1000);
} else {
e.MOV(X1, address_constant);
}
e.ADD(addr, e.GetMembaseReg(), X1);
}
} else {
if (xe::memory::allocation_granularity() > 0x1000) {
// Emulate the 4 KB physical address offset in 0xE0000000+ when can't do
// it via memory mapping.
e.MOV(X1, 0xE0000000);
e.CMP(i.src1.reg(), X1);
e.CSET(X1, Cond::HS);
e.ADD(X1, i.src1.reg(), X1, LSL, 12);
} else {
// Clear the top 32 bits, as they are likely garbage.
e.MOV(W1, i.src1.reg().toW());
}
e.ADD(addr, e.GetMembaseReg(), X1);
}

if (is_clflush) {
// TODO(wunkolo): These kind of cache-maintenance instructions cause an
// illegal-instruction on windows, but is trapped to proper EL1 code on
// Linux. Need a way to do cache-maintenance on Windows-Arm
// e.DC(DcOp::CIVAC, addr);

// Full data sync
e.DSB(BarrierOp::ISH);
}
if (is_prefetch) {
e.PRFM(PrfOp::PLDL1KEEP, addr);
}

if (cache_line_size >= 128) {
// Prefetch the other 64 bytes of the 128-byte cache line.
if (i.src1.is_constant && address_constant < 0x80000000) {
e.ADD(addr, e.GetMembaseReg(), address_constant ^ 64);
} else {
e.EOR(X1, X1, 64);
}
if (is_clflush) {
// TODO(wunkolo): These kind of cache-maintenance instructions cause an
// illegal-instruction on windows, but is trapped to proper EL1 code on
// Linux. Need a way to do cache-maintenance on Windows-Arm
// e.DC(DcOp::CIVAC, addr);

// Full data sync
e.DSB(BarrierOp::ISH);
}
if (is_prefetch) {
e.PRFM(PrfOp::PLDL1KEEP, addr);
}
assert_true(cache_line_size == 128);
}
}
};
EMITTER_OPCODE_TABLE(OPCODE_CACHE_CONTROL, CACHE_CONTROL);
Expand Down

0 comments on commit c785488

Please sign in to comment.