mirror of
https://github.com/AsahiLinux/m1n1
synced 2024-11-22 14:43:08 +00:00
hv_vm: Add support for 128-bit ops, stp/ldp, fix some emu bugs
Signed-off-by: Hector Martin <marcan@marcan.st>
This commit is contained in:
parent
bfe8c94c47
commit
85411d1714
3 changed files with 119 additions and 62 deletions
|
@ -18,8 +18,9 @@ import shell
|
|||
PAC_MASK = 0xfffff00000000000
|
||||
|
||||
class MMIOTraceFlags(Register32):
|
||||
WIDTH = 2, 0
|
||||
WRITE = 3
|
||||
WIDTH = 4, 0
|
||||
WRITE = 5
|
||||
MULTI = 6
|
||||
|
||||
EvtMMIOTrace = Struct(
|
||||
"flags" / RegAdapter(MMIOTraceFlags),
|
||||
|
@ -39,7 +40,7 @@ VMProxyHookData = Struct(
|
|||
"flags" / RegAdapter(MMIOTraceFlags),
|
||||
"id" / Int32ul,
|
||||
"addr" / Hex(Int64ul),
|
||||
"data" / Hex(Int64ul),
|
||||
"data" / Array(2, Hex(Int64ul)),
|
||||
)
|
||||
|
||||
class HV:
|
||||
|
@ -167,11 +168,18 @@ class HV:
|
|||
|
||||
rfunc, wfunc, base, kwargs = self.vm_hooks[data.id]
|
||||
|
||||
d = data.data
|
||||
if data.flags.WIDTH < 3:
|
||||
d = d[0]
|
||||
|
||||
if data.flags.WRITE:
|
||||
wfunc(base, data.addr - base, data.data, 1 << data.flags.WIDTH, **kwargs)
|
||||
wfunc(base, data.addr - base, d, 1 << data.flags.WIDTH, **kwargs)
|
||||
else:
|
||||
val = rfunc(base, data.addr - base, 1 << data.flags.WIDTH, **kwargs)
|
||||
self.p.write64(ctx.data + 16, val)
|
||||
if not isinstance(val, list) and not isinstance(val, tuple):
|
||||
val = [val]
|
||||
for i in range(1 << max(0, data.flags.WIDTH - 3)):
|
||||
self.p.write64(ctx.data + 16 * 8 * i, val[i])
|
||||
|
||||
return True
|
||||
|
||||
|
|
7
src/hv.h
7
src/hv.h
|
@ -9,8 +9,9 @@
|
|||
|
||||
typedef bool(hv_hook_t)(u64 addr, u64 *val, bool write, int width);
|
||||
|
||||
#define MMIO_EVT_WIDTH GENMASK(2, 0)
|
||||
#define MMIO_EVT_WRITE BIT(3)
|
||||
#define MMIO_EVT_WIDTH GENMASK(4, 0)
|
||||
#define MMIO_EVT_WRITE BIT(5)
|
||||
#define MMIO_EVT_MULTI BIT(6)
|
||||
|
||||
struct hv_evt_mmiotrace {
|
||||
u32 flags;
|
||||
|
@ -24,7 +25,7 @@ struct hv_vm_proxy_hook_data {
|
|||
u32 flags;
|
||||
u32 id;
|
||||
u64 addr;
|
||||
u64 data;
|
||||
u64 data[2];
|
||||
};
|
||||
|
||||
typedef enum _hv_entry_type {
|
||||
|
|
156
src/hv_vm.c
156
src/hv_vm.c
|
@ -460,11 +460,15 @@ static bool emulate_load(u64 *regs, u32 insn, u64 *val, u64 *width)
|
|||
CHECK_RN;
|
||||
DECODE_OK;
|
||||
regs[Rn] += imm9;
|
||||
regs[Rt] = EXT(*val, 8 << *width);
|
||||
regs[Rt] = (s64)EXT(*val, 8 << *width);
|
||||
if (insn & (1 << 22))
|
||||
regs[Rt] &= 0xffffffff;
|
||||
} else if ((insn & 0x3fa00000) == 0x39800000) {
|
||||
// LDRSx (immediate) Unsigned offset
|
||||
DECODE_OK;
|
||||
regs[Rt] = EXT(*val, 8 << *width);
|
||||
regs[Rt] = (s64)EXT(*val, 8 << *width);
|
||||
if (insn & (1 << 22))
|
||||
regs[Rt] &= 0xffffffff;
|
||||
} else if ((insn & 0x3fe04c00) == 0x38604800) {
|
||||
// LDRx (register)
|
||||
DECODE_OK;
|
||||
|
@ -472,7 +476,27 @@ static bool emulate_load(u64 *regs, u32 insn, u64 *val, u64 *width)
|
|||
} else if ((insn & 0x3fa04c00) == 0x38a04800) {
|
||||
// LDRSx (register)
|
||||
DECODE_OK;
|
||||
regs[Rt] = EXT(*val, 8 << *width);
|
||||
regs[Rt] = (s64)EXT(*val, 8 << *width);
|
||||
if (insn & (1 << 22))
|
||||
regs[Rt] &= 0xffffffff;
|
||||
} else if ((insn & 0x3fe00c00) == 0x38400000) {
|
||||
// LDURx (unscaled)
|
||||
DECODE_OK;
|
||||
regs[Rt] = *val;
|
||||
} else if ((insn & 0x3fa00c00) == 0x38a00000) {
|
||||
// LDURSx (unscaled)
|
||||
DECODE_OK;
|
||||
regs[Rt] = (s64)EXT(*val, (8 << *width));
|
||||
if (insn & (1 << 22))
|
||||
regs[Rt] &= 0xffffffff;
|
||||
} else if ((insn & 0xffc00000) == 0xa9400000) {
|
||||
// LDP (Signed offset, 64-bit)
|
||||
*width = 4;
|
||||
DECODE_OK;
|
||||
CHECK_RN;
|
||||
u64 Rt2 = (insn >> 10) & 0x1f;
|
||||
regs[Rt] = val[0];
|
||||
regs[Rt2] = val[1];
|
||||
} else {
|
||||
goto bail;
|
||||
}
|
||||
|
@ -506,6 +530,13 @@ static bool emulate_store(u64 *regs, u32 insn, u64 *val, u64 *width)
|
|||
} else if ((insn & 0x3fe04c00) == 0x38204800) {
|
||||
// STRx (register)
|
||||
*val = regs[Rt];
|
||||
} else if ((insn & 0xffc00000) == 0xa9000000) {
|
||||
// STP (Signed offset, 64-bit)
|
||||
CHECK_RN;
|
||||
u64 Rt2 = (insn >> 10) & 0x1f;
|
||||
val[0] = regs[Rt];
|
||||
val[1] = regs[Rt2];
|
||||
*width = 4;
|
||||
} else {
|
||||
goto bail;
|
||||
}
|
||||
|
@ -519,6 +550,29 @@ bail:
|
|||
return false;
|
||||
}
|
||||
|
||||
static void emit_mmiotrace(u64 pc, u64 addr, u64 *data, u64 width, u64 flags, bool sync)
|
||||
{
|
||||
struct hv_evt_mmiotrace evt = {
|
||||
.flags = flags,
|
||||
.pc = pc,
|
||||
.addr = addr,
|
||||
};
|
||||
|
||||
if (width > 3)
|
||||
evt.flags |= FIELD_PREP(MMIO_EVT_WIDTH, 3) | MMIO_EVT_MULTI;
|
||||
else
|
||||
evt.flags |= FIELD_PREP(MMIO_EVT_WIDTH, width);
|
||||
|
||||
for (int i = 0; i < (1 << width); i += 8) {
|
||||
evt.data = *data++;
|
||||
uartproxy_send_event(EVT_MMIOTRACE, &evt, sizeof(evt));
|
||||
if (sync) {
|
||||
iodev_flush(uartproxy_iodev);
|
||||
}
|
||||
evt.addr += 8;
|
||||
}
|
||||
}
|
||||
|
||||
bool hv_handle_dabort(u64 *regs)
|
||||
{
|
||||
u64 esr = hv_get_esr();
|
||||
|
@ -563,26 +617,15 @@ bool hv_handle_dabort(u64 *regs)
|
|||
}
|
||||
|
||||
u32 insn = read32(elr_pa);
|
||||
u64 val = 0;
|
||||
u64 val[2] = {0, 0};
|
||||
u64 width;
|
||||
|
||||
if (esr & ESR_ISS_DABORT_WnR) {
|
||||
if (!emulate_store(regs, insn, &val, &width))
|
||||
if (!emulate_store(regs, insn, val, &width))
|
||||
return false;
|
||||
|
||||
if (pte & SPTE_TRACE_WRITE) {
|
||||
struct hv_evt_mmiotrace evt = {
|
||||
.flags = FIELD_PREP(MMIO_EVT_WIDTH, width) | MMIO_EVT_WRITE,
|
||||
.pc = elr,
|
||||
.addr = ipa,
|
||||
.data = val,
|
||||
};
|
||||
uartproxy_send_event(EVT_MMIOTRACE, &evt, sizeof(evt));
|
||||
if (pte & SPTE_SYNC_TRACE) {
|
||||
iodev_flush(uartproxy_iodev);
|
||||
udelay(1500);
|
||||
}
|
||||
}
|
||||
if (pte & SPTE_TRACE_WRITE)
|
||||
emit_mmiotrace(elr, ipa, val, width, MMIO_EVT_WRITE, pte & SPTE_SYNC_TRACE);
|
||||
|
||||
switch (FIELD_GET(SPTE_TYPE, pte)) {
|
||||
case SPTE_PROXY_HOOK_R:
|
||||
|
@ -590,25 +633,32 @@ bool hv_handle_dabort(u64 *regs)
|
|||
// fallthrough
|
||||
case SPTE_MAP:
|
||||
dprintf("HV: SPTE_MAP[W] @0x%lx 0x%lx -> 0x%lx (w=%d): 0x%lx\n", elr_pa, far, paddr,
|
||||
1 << width, val);
|
||||
1 << width, val[0]);
|
||||
switch (width) {
|
||||
case SAS_8B:
|
||||
write8(paddr, val);
|
||||
case 0:
|
||||
write8(paddr, val[0]);
|
||||
break;
|
||||
case SAS_16B:
|
||||
write16(paddr, val);
|
||||
case 1:
|
||||
write16(paddr, val[0]);
|
||||
break;
|
||||
case SAS_32B:
|
||||
write32(paddr, val);
|
||||
case 2:
|
||||
write32(paddr, val[0]);
|
||||
break;
|
||||
case SAS_64B:
|
||||
write64(paddr, val);
|
||||
case 3:
|
||||
write64(paddr, val[0]);
|
||||
break;
|
||||
case 4:
|
||||
write64(paddr, val[0]);
|
||||
write64(paddr + 8, val[1]);
|
||||
break;
|
||||
default:
|
||||
dprintf("HV: unsupported width %ld\n", width);
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case SPTE_HOOK: {
|
||||
hv_hook_t *hook = (hv_hook_t *)target;
|
||||
if (!hook(ipa, &val, true, width))
|
||||
if (!hook(ipa, val, true, width))
|
||||
return false;
|
||||
dprintf("HV: SPTE_HOOK[W] @0x%lx 0x%lx -> 0x%lx (w=%d) @%p: 0x%lx\n", elr_pa, far,
|
||||
ipa, 1 << width, hook, val);
|
||||
|
@ -620,7 +670,7 @@ bool hv_handle_dabort(u64 *regs)
|
|||
.flags = FIELD_PREP(MMIO_EVT_WIDTH, width) | MMIO_EVT_WRITE,
|
||||
.id = FIELD_GET(PTE_TARGET_MASK_L4, pte),
|
||||
.addr = ipa,
|
||||
.data = val,
|
||||
.data = {val[0], val[1]},
|
||||
};
|
||||
hv_exc_proxy(regs, START_HV, HV_HOOK_VM, &hook);
|
||||
break;
|
||||
|
@ -639,30 +689,37 @@ bool hv_handle_dabort(u64 *regs)
|
|||
// fallthrough
|
||||
case SPTE_MAP:
|
||||
switch (width) {
|
||||
case SAS_8B:
|
||||
val = read8(paddr);
|
||||
case 0:
|
||||
val[0] = read8(paddr);
|
||||
break;
|
||||
case SAS_16B:
|
||||
val = read16(paddr);
|
||||
case 1:
|
||||
val[0] = read16(paddr);
|
||||
break;
|
||||
case SAS_32B:
|
||||
val = read32(paddr);
|
||||
case 2:
|
||||
val[0] = read32(paddr);
|
||||
break;
|
||||
case SAS_64B:
|
||||
val = read64(paddr);
|
||||
case 3:
|
||||
val[0] = read64(paddr);
|
||||
break;
|
||||
case 4:
|
||||
val[0] = read64(paddr);
|
||||
val[1] = read64(paddr + 8);
|
||||
break;
|
||||
default:
|
||||
dprintf("HV: unsupported width %ld\n", width);
|
||||
return false;
|
||||
}
|
||||
dprintf("HV: SPTE_MAP[R] @0x%lx 0x%lx -> 0x%lx (w=%d): 0x%lx\n", elr_pa, far, paddr,
|
||||
1 << width, val);
|
||||
1 << width, val[0]);
|
||||
break;
|
||||
case SPTE_HOOK:
|
||||
val = 0;
|
||||
case SPTE_HOOK: {
|
||||
hv_hook_t *hook = (hv_hook_t *)target;
|
||||
if (!hook(ipa, &val, false, width))
|
||||
if (!hook(ipa, val, false, width))
|
||||
return false;
|
||||
dprintf("HV: SPTE_HOOK[R] @0x%lx 0x%lx -> 0x%lx (w=%d) @%p: 0x%lx\n", elr_pa, far,
|
||||
ipa, 1 << width, hook, val);
|
||||
break;
|
||||
}
|
||||
case SPTE_PROXY_HOOK_RW:
|
||||
case SPTE_PROXY_HOOK_R: {
|
||||
struct hv_vm_proxy_hook_data hook = {
|
||||
|
@ -671,7 +728,7 @@ bool hv_handle_dabort(u64 *regs)
|
|||
.addr = ipa,
|
||||
};
|
||||
hv_exc_proxy(regs, START_HV, HV_HOOK_VM, &hook);
|
||||
val = hook.data;
|
||||
memcpy(val, hook.data, sizeof(val));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
|
@ -679,19 +736,10 @@ bool hv_handle_dabort(u64 *regs)
|
|||
return false;
|
||||
}
|
||||
|
||||
if (pte & SPTE_TRACE_READ) {
|
||||
struct hv_evt_mmiotrace evt = {
|
||||
.flags = FIELD_PREP(MMIO_EVT_WIDTH, width),
|
||||
.pc = elr,
|
||||
.addr = ipa,
|
||||
.data = val,
|
||||
};
|
||||
uartproxy_send_event(EVT_MMIOTRACE, &evt, sizeof(evt));
|
||||
if (pte & SPTE_SYNC_TRACE)
|
||||
iodev_flush(uartproxy_iodev);
|
||||
}
|
||||
if (pte & SPTE_TRACE_READ)
|
||||
emit_mmiotrace(elr, ipa, val, width, 0, pte & SPTE_SYNC_TRACE);
|
||||
|
||||
if (!emulate_load(regs, insn, &val, &width))
|
||||
if (!emulate_load(regs, insn, val, &width))
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue