From 85411d171400983665f816360f589c6fe7b9630e Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Thu, 27 May 2021 21:17:01 +0900 Subject: [PATCH] hv_vm: Add support for 128-bit ops, stp/ldp, fix some emu bugs Signed-off-by: Hector Martin --- proxyclient/hv.py | 18 ++++-- src/hv.h | 7 ++- src/hv_vm.c | 156 ++++++++++++++++++++++++++++++---------------- 3 files changed, 119 insertions(+), 62 deletions(-) diff --git a/proxyclient/hv.py b/proxyclient/hv.py index 3920c2f3..e724943a 100644 --- a/proxyclient/hv.py +++ b/proxyclient/hv.py @@ -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 diff --git a/src/hv.h b/src/hv.h index 4a7b0453..ad644601 100644 --- a/src/hv.h +++ b/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 { diff --git a/src/hv_vm.c b/src/hv_vm.c index acc48a3a..f5fa06e0 100644 --- a/src/hv_vm.c +++ b/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; }