mirror of
https://github.com/AsahiLinux/m1n1
synced 2024-11-10 09:44:13 +00:00
hv: add AIC interrupt tracing
Implemented by MMIO tracing of AIC's event register. Proposed by pipcet. Signed-off-by: Janne Grunau <j@jannau.net>
This commit is contained in:
parent
012d8964f9
commit
88275b5cb5
9 changed files with 244 additions and 43 deletions
2
Makefile
2
Makefile
|
@ -44,7 +44,7 @@ OBJECTS := \
|
|||
fb.o font.o font_retina.o \
|
||||
gxf.o gxf_asm.o \
|
||||
heapblock.o \
|
||||
hv.o hv_vm.o hv_exc.o hv_vuart.o hv_wdt.o hv_asm.o \
|
||||
hv.o hv_vm.o hv_exc.o hv_vuart.o hv_wdt.o hv_asm.o hv_aic.o \
|
||||
i2c.o \
|
||||
iodev.o \
|
||||
kboot.o \
|
||||
|
|
|
@ -30,6 +30,12 @@ EvtMMIOTrace = Struct(
|
|||
"data" / Hex(Int64ul),
|
||||
)
|
||||
|
||||
EvtIRQTrace = Struct(
|
||||
"flags" / Int32ul,
|
||||
"type" / Hex(Int16ul),
|
||||
"num" / Int16ul,
|
||||
)
|
||||
|
||||
class HV_EVENT(IntEnum):
|
||||
HOOK_VM = 1
|
||||
VTIMER = 2
|
||||
|
@ -89,6 +95,9 @@ class HV:
|
|||
GXF_ENTER_EL1: GXF_ENTER_EL12,
|
||||
}
|
||||
|
||||
AIC_EVT_TYPE_HW = 1
|
||||
IRQTRACE_IRQ = 1
|
||||
|
||||
def __init__(self, iface, proxy, utils):
|
||||
self.iface = iface
|
||||
self.p = proxy
|
||||
|
@ -104,6 +113,7 @@ class HV:
|
|||
self._in_handler = False
|
||||
self._sigint_pending = False
|
||||
self.vm_hooks = []
|
||||
self.interrupt_map = {}
|
||||
|
||||
def unmap(self, ipa, size):
|
||||
assert self.p.hv_map(ipa, 0, size, 0) >= 0
|
||||
|
@ -129,6 +139,14 @@ class HV:
|
|||
self.vm_hooks.append((read, write, ipa, kwargs))
|
||||
assert self.p.hv_map(ipa, (index << 2) | t, size, 1) >= 0
|
||||
|
||||
def trace_irq(self, device, num, count, flags):
|
||||
for n in range(num, num + count):
|
||||
if flags & self.IRQTRACE_IRQ:
|
||||
self.interrupt_map[n] = device
|
||||
else:
|
||||
self.interrupt_map.pop(n, None)
|
||||
assert self.p.hv_trace_irq(self.AIC_EVT_TYPE_HW, num, count, flags) > 0
|
||||
|
||||
def addr(self, addr):
|
||||
unslid_addr = addr + self.sym_offset
|
||||
if addr < self.tba.virt_base or unslid_addr < self.macho.vmin:
|
||||
|
@ -171,6 +189,13 @@ class HV:
|
|||
print(f"[0x{evt.pc:016x}] MMIO: {t}.{1<<evt.flags.WIDTH:<2}{m} " +
|
||||
f"0x{evt.addr:x} ({dev}, offset {evt.addr - zone.start:#04x}) = 0x{evt.data:x}")
|
||||
|
||||
def handle_irqtrace(self, data):
|
||||
evt = EvtIRQTrace.parse(data)
|
||||
|
||||
if evt.type == self.AIC_EVT_TYPE_HW and evt.flags & self.IRQTRACE_IRQ:
|
||||
dev = self.interrupt_map[int(evt.num)]
|
||||
print(f"IRQ: {dev}: {evt.num}")
|
||||
|
||||
def handle_vm_hook(self, ctx):
|
||||
data = self.iface.readstruct(ctx.data, VMProxyHookData)
|
||||
|
||||
|
@ -571,6 +596,7 @@ class HV:
|
|||
self.iface.set_handler(START.HV, HV_EVENT.VTIMER, self.handle_exception)
|
||||
self.iface.set_handler(START.HV, HV_EVENT.WDT_BARK, self.handle_bark)
|
||||
self.iface.set_event_handler(EVENT.MMIOTRACE, self.handle_mmiotrace)
|
||||
self.iface.set_event_handler(EVENT.IRQTRACE, self.handle_irqtrace)
|
||||
|
||||
#self.map_sw(0x2_00000000,
|
||||
#0x2_00000000 | self.SPTE_TRACE_READ | self.SPTE_TRACE_WRITE,
|
||||
|
@ -610,6 +636,20 @@ class HV:
|
|||
print(f"Pass: 0x{addr:x} [0x{size:x}] ({path})")
|
||||
self.map_hw(addr, addr, size)
|
||||
|
||||
# trace IRQs
|
||||
aic_phandle = getattr(self.adt["/arm-io/aic"], "AAPL,phandle")
|
||||
for path in (
|
||||
"/arm-io/usb-drd1",
|
||||
"/arm-io/gpio",
|
||||
):
|
||||
node = self.adt[path]
|
||||
if getattr(node, "interrupt-parent") != aic_phandle:
|
||||
print(f"{path} has no direct AIC interrupts.")
|
||||
continue
|
||||
for irq in getattr(node, "interrupts"):
|
||||
#self.trace_irq(node.name, irq, 1, self.IRQTRACE_IRQ)
|
||||
pass
|
||||
|
||||
# Sync PMGR stuff
|
||||
#self.map_sw(0x2_3b700000,
|
||||
#0x2_3b700000 | self.SPTE_TRACE_READ | self.SPTE_TRACE_WRITE | self.SPTE_SYNC_TRACE,
|
||||
|
|
|
@ -102,6 +102,7 @@ class EXC(IntEnum):
|
|||
|
||||
class EVENT(IntEnum):
|
||||
MMIOTRACE = 1
|
||||
IRQTRACE = 2
|
||||
|
||||
class EXC_RET(IntEnum):
|
||||
UNHANDLED = 1
|
||||
|
@ -584,6 +585,7 @@ class M1N1Proxy:
|
|||
P_HV_TRANSLATE = 0xc03
|
||||
P_HV_PT_WALK = 0xc04
|
||||
P_HV_MAP_VUART = 0xc05
|
||||
P_HV_TRACE_IRQ = 0xc06
|
||||
|
||||
P_FB_INIT = 0xd00
|
||||
P_FB_SHUTDOWN = 0xd01
|
||||
|
@ -945,6 +947,8 @@ class M1N1Proxy:
|
|||
return self.request(self.P_HV_PT_WALK, addr)
|
||||
def hv_map_vuart(self, base, iodev):
|
||||
return self.request(self.P_HV_MAP_VUART, base, iodev)
|
||||
def hv_trace_irq(self, evt_type, num, count, flags):
|
||||
return self.request(self.P_HV_TRACE_IRQ, evt_type, num, count, flags)
|
||||
|
||||
def fb_init(self):
|
||||
return self.request(self.P_FB_INIT)
|
||||
|
|
12
src/hv.h
12
src/hv.h
|
@ -21,6 +21,12 @@ struct hv_evt_mmiotrace {
|
|||
u64 data;
|
||||
};
|
||||
|
||||
struct hv_evt_irqtrace {
|
||||
u32 flags;
|
||||
u16 type;
|
||||
u16 num;
|
||||
};
|
||||
|
||||
struct hv_vm_proxy_hook_data {
|
||||
u32 flags;
|
||||
u32 id;
|
||||
|
@ -46,6 +52,12 @@ int hv_map_proxy_hook(u64 from, u64 id, u64 size);
|
|||
u64 hv_translate(u64 addr, bool s1only, bool w);
|
||||
u64 hv_pt_walk(u64 addr);
|
||||
bool hv_handle_dabort(u64 *regs);
|
||||
bool hv_pa_write(u64 addr, u64 *val, int width);
|
||||
bool hv_pa_read(u64 addr, u64 *val, int width);
|
||||
bool hv_pa_rw(u64 addr, u64 *val, bool write, int width);
|
||||
|
||||
/* AIC events through tracing the MMIO event address */
|
||||
bool hv_trace_irq(u32 type, u32 num, u32 count, u32 flags);
|
||||
|
||||
/* Virtual peripherals */
|
||||
void hv_map_vuart(u64 base, iodev_id_t iodev);
|
||||
|
|
119
src/hv_aic.c
Normal file
119
src/hv_aic.c
Normal file
|
@ -0,0 +1,119 @@
|
|||
/* SPDX-License-Identifier: MIT */
|
||||
|
||||
#include "adt.h"
|
||||
#include "hv.h"
|
||||
#include "uartproxy.h"
|
||||
#include "utils.h"
|
||||
|
||||
#define AIC_INFO 0x0004
|
||||
#define AIC_INFO_NR_HW GENMASK(15, 0)
|
||||
|
||||
#define AIC_EVENT 0x2004
|
||||
#define AIC_EVENT_TYPE GENMASK(31, 16)
|
||||
#define AIC_EVENT_NUM GENMASK(15, 0)
|
||||
|
||||
#define AIC_EVENT_TYPE_HW 1
|
||||
#define AIC_EVENT_TYPE_IPI 4
|
||||
#define AIC_EVENT_IPI_OTHER 1
|
||||
#define AIC_EVENT_IPI_SELF 2
|
||||
|
||||
#define AIC_MAX_HW_NUM (28 * 32)
|
||||
|
||||
#define IRQTRACE_IRQ BIT(0)
|
||||
|
||||
static u64 aic_base;
|
||||
|
||||
static u32 trace_hw_num[AIC_MAX_HW_NUM / 32];
|
||||
|
||||
static void emit_irqtrace(u16 type, u16 num)
|
||||
{
|
||||
struct hv_evt_irqtrace evt = {
|
||||
.flags = IRQTRACE_IRQ,
|
||||
.type = type,
|
||||
.num = num,
|
||||
};
|
||||
|
||||
hv_wdt_suspend();
|
||||
uartproxy_send_event(EVT_IRQTRACE, &evt, sizeof(evt));
|
||||
hv_wdt_resume();
|
||||
}
|
||||
|
||||
static bool trace_aic_event(u64 addr, u64 *val, bool write, int width)
|
||||
{
|
||||
if (!hv_pa_rw(addr, val, write, width))
|
||||
return false;
|
||||
|
||||
if (addr != (aic_base + AIC_EVENT) || write || width != 2) {
|
||||
return true;
|
||||
}
|
||||
|
||||
u16 type = (*val & AIC_EVENT_TYPE) >> 16;
|
||||
u16 num = *val & AIC_EVENT_NUM;
|
||||
|
||||
switch (type) {
|
||||
case AIC_EVENT_TYPE_HW:
|
||||
if (trace_hw_num[num / 32] & BIT(num & 31)) {
|
||||
emit_irqtrace(type, num);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
// ignore
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool hv_trace_irq(u32 type, u32 num, u32 count, u32 flags)
|
||||
{
|
||||
dprintf("HV: hv_trace_irq type: %u start: %u num: %u flags: 0x%x\n", type, num, count, flags);
|
||||
if (type == AIC_EVENT_TYPE_HW) {
|
||||
if (num >= AIC_MAX_HW_NUM || count > AIC_MAX_HW_NUM - num) {
|
||||
printf("HV: invalid IRQ range: (%u, %u)\n", num, num + count);
|
||||
return false;
|
||||
}
|
||||
for (u32 n = num; n < num + count; n++) {
|
||||
switch (flags) {
|
||||
case IRQTRACE_IRQ:
|
||||
trace_hw_num[n / 32] |= BIT(n & 31);
|
||||
break;
|
||||
default:
|
||||
trace_hw_num[n / 32] &= ~(BIT(n & 31));
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
printf("HV: not handling AIC event type: 0x%02x num: %u\n", type, num);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!aic_base) {
|
||||
static const char path[] = "/arm-io/aic";
|
||||
int adt_path[8];
|
||||
|
||||
int node = adt_path_offset_trace(adt, path, adt_path);
|
||||
if (node < 0) {
|
||||
printf("HV: Error getting %s node\n", path);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (adt_get_reg(adt, adt_path, "reg", 0, &aic_base, NULL) < 0) {
|
||||
printf("HV: Error getting AIC base address.\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static bool hooked = false;
|
||||
|
||||
if (aic_base && !hooked) {
|
||||
u32 nr_hw = FIELD_GET(AIC_INFO_NR_HW, read32(aic_base + AIC_INFO));
|
||||
if (nr_hw > AIC_MAX_HW_NUM) {
|
||||
printf("HV: AIC supports more IRQs than expected! nr_hw: %u\n", nr_hw);
|
||||
return false;
|
||||
}
|
||||
hv_map_hook(aic_base, trace_aic_event, 0x4000);
|
||||
hooked = true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
104
src/hv_vm.c
104
src/hv_vm.c
|
@ -636,6 +636,64 @@ static void emit_mmiotrace(u64 pc, u64 addr, u64 *data, u64 width, u64 flags, bo
|
|||
}
|
||||
}
|
||||
|
||||
bool hv_pa_write(u64 addr, u64 *val, int width)
|
||||
{
|
||||
switch (width) {
|
||||
case 0:
|
||||
write8(addr, val[0]);
|
||||
return true;
|
||||
case 1:
|
||||
write16(addr, val[0]);
|
||||
return true;
|
||||
case 2:
|
||||
write32(addr, val[0]);
|
||||
return true;
|
||||
case 3:
|
||||
write64(addr, val[0]);
|
||||
return true;
|
||||
case 4:
|
||||
write64(addr, val[0]);
|
||||
write64(addr + 8, val[1]);
|
||||
return true;
|
||||
default:
|
||||
dprintf("HV: unsupported write width %ld\n", width);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool hv_pa_read(u64 addr, u64 *val, int width)
|
||||
{
|
||||
switch (width) {
|
||||
case 0:
|
||||
val[0] = read8(addr);
|
||||
return true;
|
||||
case 1:
|
||||
val[0] = read16(addr);
|
||||
return true;
|
||||
case 2:
|
||||
val[0] = read32(addr);
|
||||
return true;
|
||||
case 3:
|
||||
val[0] = read64(addr);
|
||||
return true;
|
||||
case 4:
|
||||
val[0] = read64(addr);
|
||||
val[1] = read64(addr + 8);
|
||||
return true;
|
||||
default:
|
||||
dprintf("HV: unsupported read width %ld\n", width);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool hv_pa_rw(u64 addr, u64 *val, bool write, int width)
|
||||
{
|
||||
if (write)
|
||||
return hv_pa_write(addr, val, width);
|
||||
else
|
||||
return hv_pa_read(addr, val, width);
|
||||
}
|
||||
|
||||
bool hv_handle_dabort(u64 *regs)
|
||||
{
|
||||
hv_wdt_breadcrumb('0');
|
||||
|
@ -711,27 +769,8 @@ bool hv_handle_dabort(u64 *regs)
|
|||
hv_wdt_breadcrumb('5');
|
||||
dprintf("HV: SPTE_MAP[W] @0x%lx 0x%lx -> 0x%lx (w=%d): 0x%lx\n", elr_pa, far, paddr,
|
||||
1 << width, val[0]);
|
||||
switch (width) {
|
||||
case 0:
|
||||
write8(paddr, val[0]);
|
||||
break;
|
||||
case 1:
|
||||
write16(paddr, val[0]);
|
||||
break;
|
||||
case 2:
|
||||
write32(paddr, val[0]);
|
||||
break;
|
||||
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;
|
||||
}
|
||||
if (!hv_pa_write(paddr, val, width))
|
||||
return false;
|
||||
break;
|
||||
case SPTE_HOOK: {
|
||||
hv_wdt_breadcrumb('6');
|
||||
|
@ -773,27 +812,8 @@ bool hv_handle_dabort(u64 *regs)
|
|||
// fallthrough
|
||||
case SPTE_MAP:
|
||||
hv_wdt_breadcrumb('4');
|
||||
switch (width) {
|
||||
case 0:
|
||||
val[0] = read8(paddr);
|
||||
break;
|
||||
case 1:
|
||||
val[0] = read16(paddr);
|
||||
break;
|
||||
case 2:
|
||||
val[0] = read32(paddr);
|
||||
break;
|
||||
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;
|
||||
}
|
||||
if (!hv_pa_read(paddr, val, 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[0]);
|
||||
break;
|
||||
|
|
|
@ -427,6 +427,10 @@ int proxy_process(ProxyRequest *request, ProxyReply *reply)
|
|||
case P_HV_MAP_VUART:
|
||||
hv_map_vuart(request->args[0], request->args[1]);
|
||||
break;
|
||||
case P_HV_TRACE_IRQ:
|
||||
reply->retval = hv_trace_irq(request->args[0], request->args[1], request->args[2],
|
||||
request->args[3]);
|
||||
break;
|
||||
|
||||
case P_FB_INIT:
|
||||
fb_init();
|
||||
|
|
|
@ -118,6 +118,7 @@ typedef enum {
|
|||
P_HV_TRANSLATE,
|
||||
P_HV_PT_WALK,
|
||||
P_HV_MAP_VUART,
|
||||
P_HV_TRACE_IRQ,
|
||||
|
||||
P_FB_INIT = 0xd00,
|
||||
P_FB_SHUTDOWN,
|
||||
|
|
|
@ -30,6 +30,7 @@ typedef enum _uartproxy_exc_ret_t {
|
|||
|
||||
typedef enum _uartproxy_event_type_t {
|
||||
EVT_MMIOTRACE = 1,
|
||||
EVT_IRQTRACE = 2,
|
||||
} uartproxy_event_type_t;
|
||||
|
||||
struct uartproxy_exc_info {
|
||||
|
|
Loading…
Reference in a new issue