summaryrefslogtreecommitdiff
path: root/tools/src/hv_aic.c
diff options
context:
space:
mode:
authormagh <magh@maghmogh.com>2023-03-06 18:44:55 -0600
committermagh <magh@maghmogh.com>2023-03-06 18:44:55 -0600
commite80d9d8871b325a04b18f90a9ea4bb7fd148fb25 (patch)
tree79dbdb8506b7ff1e92549188d1b94cfc0b3503ae /tools/src/hv_aic.c
add m1n1HEADmaster
Diffstat (limited to 'tools/src/hv_aic.c')
-rw-r--r--tools/src/hv_aic.c95
1 files changed, 95 insertions, 0 deletions
diff --git a/tools/src/hv_aic.c b/tools/src/hv_aic.c
new file mode 100644
index 0000000..cc5406a
--- /dev/null
+++ b/tools/src/hv_aic.c
@@ -0,0 +1,95 @@
+/* SPDX-License-Identifier: MIT */
+
+#include "adt.h"
+#include "aic.h"
+#include "aic_regs.h"
+#include "hv.h"
+#include "uartproxy.h"
+#include "utils.h"
+
+#define IRQTRACE_IRQ BIT(0)
+
+static u32 trace_hw_num[AIC_MAX_DIES][AIC_MAX_HW_NUM / 32];
+
+static void emit_irqtrace(u16 die, u16 type, u16 num)
+{
+ struct hv_evt_irqtrace evt = {
+ .flags = IRQTRACE_IRQ,
+ .type = type,
+ .num = die * aic->max_irq + num,
+ };
+
+ hv_wdt_suspend();
+ uartproxy_send_event(EVT_IRQTRACE, &evt, sizeof(evt));
+ hv_wdt_resume();
+}
+
+static bool trace_aic_event(struct exc_info *ctx, u64 addr, u64 *val, bool write, int width)
+{
+ if (!hv_pa_rw(ctx, addr, val, write, width))
+ return false;
+
+ if (addr != (aic->base + aic->regs.event) || write || width != 2) {
+ return true;
+ }
+
+ u16 die = FIELD_GET(AIC_EVENT_DIE, *val);
+ u16 type = FIELD_GET(AIC_EVENT_TYPE, *val);
+ u16 num = FIELD_GET(AIC_EVENT_NUM, *val);
+
+ if (die > AIC_MAX_DIES)
+ return true;
+
+ switch (type) {
+ case AIC_EVENT_TYPE_HW:
+ if (trace_hw_num[die][num / 32] & BIT(num & 31)) {
+ emit_irqtrace(die, 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) {
+ u32 die = num / aic->max_irq;
+ num %= AIC_MAX_HW_NUM;
+ if (die >= aic->max_irq || num >= AIC_MAX_HW_NUM || count > AIC_MAX_HW_NUM - num) {
+ printf("HV: invalid IRQ range: (%u, %u) for die %u\n", num, num + count, die);
+ return false;
+ }
+ for (u32 n = num; n < num + count; n++) {
+ switch (flags) {
+ case IRQTRACE_IRQ:
+ trace_hw_num[die][n / 32] |= BIT(n & 31);
+ break;
+ default:
+ trace_hw_num[die][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) {
+ printf("HV: AIC not initialized\n");
+ return false;
+ }
+
+ static bool hooked = false;
+
+ if (aic && !hooked) {
+ hv_map_hook(aic->base, trace_aic_event, aic->regs.reg_size);
+ hooked = true;
+ }
+
+ return true;
+}