1/* @title: IRQs */
2#pragma once
3#include <console/crash.h>
4#include <enforce.h>
5#include <smp/core.h>
6#include <stdbool.h>
7#include <stdint.h>
8#include <structures/cpu_mask.h>
9#include <structures/list.h>
10#include <types/types.h>
11
12#define IRQ_DIV_BY_Z 0x0
13#define IRQ_DEBUG 0x1
14#define IRQ_NMI 0x2
15#define IRQ_BREAKPOINT 0x3
16#define IRQ_DBF 0x8
17#define IRQ_SSF 0xC
18#define IRQ_GPF 0xD
19#define IRQ_PAGE_FAULT 0xE
20#define IRQ_TIMER 0x20
21#define IRQ_TLB_SHOOTDOWN 0x22
22#define IRQ_SCHEDULER 0x23
23#define IRQ_NOP 0x24
24#define IRQ_DPC 0x25
25#define IRQ_EXCEPTION_COUNT 32
26
27struct irq_context;
28struct irq_desc;
29
30enum irq_result {
31 IRQ_NONE = 0,
32 IRQ_HANDLED = 1,
33};
34
35typedef enum irq_result (*irq_handler_t)(struct irq_context *ictx, void *ctx);
36
37enum irq_flags {
38 IRQ_FLAG_SHARED = 1, /* IRQ is shared between things */
39 IRQ_FLAG_LEVEL_TRIGGERED = 1 << 1,
40 IRQ_FLAG_EDGE_TRIGGERED = 1 << 2,
41 IRQ_FLAG_NONE = 0,
42};
43
44struct irq_action {
45 irq_handler_t handler;
46 void *data;
47 struct list_node list;
48};
49
50struct irq_chip {
51 const char *name;
52
53 void (*mask)(struct irq_desc *);
54 void (*unmask)(struct irq_desc *);
55 void (*eoi)(struct irq_desc *);
56
57 void (*set_affinity)(struct irq_desc *, struct cpu_mask *);
58 int (*set_rate_limit)(struct irq_desc *, time_us_t interval);
59};
60
61struct irq_desc {
62 uint8_t vector;
63 enum irq_flags flags;
64
65 const char *name;
66
67 struct irq_chip *chip;
68 void *chip_data;
69
70 struct list_head actions;
71
72 struct cpu_mask affinity;
73 struct cpu_mask masked_cpus;
74
75 bool present; /* Have we set handlers? */
76 bool allocated; /* Has this been allocated? */
77 bool enabled;
78};
79
80struct irq_registers {
81 uint64_t rax;
82 uint64_t rbx;
83 uint64_t rcx;
84 uint64_t rdx;
85 uint64_t rbp;
86 uint64_t rdi;
87 uint64_t rsi;
88 uint64_t r8;
89 uint64_t r9;
90 uint64_t r10;
91 uint64_t r11;
92 uint64_t r12;
93 uint64_t r13;
94 uint64_t r14;
95 uint64_t r15;
96 uint64_t error_code;
97 uint64_t rip;
98 uint64_t cs;
99 uint64_t rflags;
100 uint64_t rsp;
101 uint64_t ss;
102};
103
104struct irq_context {
105 irq_t vector;
106 struct irq_registers *regs;
107
108 /* This scratch buffer is stack allocated, and is set upon
109 * IRQ entry, allowing the top half of the IRQ to modify it
110 *
111 * For exception_sync_cb, it is passed into the callback as a parameter */
112 uint8_t *irq_stack_scratch_buf;
113
114 /* Remains valid in the top half, once the bottom half is
115 * reached, this becomes IRQL_NONE */
116 enum irql irq_entered_irql; /* What IRQL were we at before
117 * entering an ISR (if !in_interrupt,
118 * this should be IRQL_NONE */
119};
120
121static inline void irq_context_to_crash_regs(const struct irq_registers *ictx,
122 struct crash_regs *out) {
123 if (!ictx || !out)
124 return;
125 out->rip = ictx->rip;
126 out->rflags = ictx->rflags;
127 __asm__ volatile("mov %%cr2, %0" : "=r"(out->cr2));
128 __asm__ volatile("mov %%cr3, %0" : "=r"(out->cr3));
129 out->rax = ictx->rax;
130 out->rbx = ictx->rbx;
131 out->rcx = ictx->rcx;
132 out->rdx = ictx->rdx;
133 out->rbp = ictx->rbp;
134 out->rdi = ictx->rdi;
135 out->rsi = ictx->rsi;
136 out->r8 = ictx->r8;
137 out->r9 = ictx->r9;
138 out->r10 = ictx->r10;
139 out->r11 = ictx->r11;
140 out->r12 = ictx->r12;
141 out->r13 = ictx->r13;
142 out->r14 = ictx->r14;
143 out->r15 = ictx->r15;
144 out->rsp = ictx->rsp;
145}
146
147void irq_register(char *name, uint8_t vector, irq_handler_t handler, void *ctx,
148 enum irq_flags flags);
149void irq_register_full(struct irq_desc *d);
150void irq_set_chip(uint8_t vector, struct irq_chip *chip, void *data);
151
152/* We DO NOT set _IRQ here because _NONE is used as
153 * this set of functions will set the flags _NONE checks */
154static inline uint32_t irq_mark_self_in_interrupt(bool new) {
155 uint32_t old = smp_ctx_irq_count(smp_ctx: smp_ctx(c: TOPC_NONE));
156 if (new)
157 smp_ctx_add(c: TOPC_NONE, SMP_CTX_IRQ_ONE, SMP_CTX_IRQ_MASK);
158 else
159 smp_ctx_sub(c: TOPC_NONE, SMP_CTX_IRQ_ONE, SMP_CTX_IRQ_MASK);
160 return old;
161}
162
163static inline uint32_t irq_mark_self_in_nmi(bool new) {
164 uint32_t old = smp_ctx_nmi_count(smp_ctx: smp_ctx(c: TOPC_NONE));
165 if (new)
166 smp_ctx_add(c: TOPC_NONE, SMP_CTX_NMI_ONE, SMP_CTX_NMI_MASK);
167 else
168 smp_ctx_sub(c: TOPC_NONE, SMP_CTX_NMI_ONE, SMP_CTX_NMI_MASK);
169 return old;
170}
171
172/* These are called from the verification logic,
173 * they are simple reads. cc_pure so assume() accepts it: NMI nesting is
174 * balanced, so it is invariant from the interrupted code's point of view */
175static inline cc_pure bool irq_in_nmi(void) {
176 return (smp_ctx(c: TOPC_NONE) & SMP_CTX_NMI_MASK) != 0;
177}
178
179static inline bool irq_in_interrupt(void) {
180 return (smp_ctx(c: TOPC_NONE) & SMP_CTX_IRQ_MASK) != 0;
181}
182
183static inline bool irq_not_in_interrupt(void) {
184 return (smp_ctx(c: TOPC_NONE) & SMP_CTX_IN_INTERRUPT_MASK) == 0;
185}
186
187static inline bool irq_vector_is_exception(uint8_t vector) {
188 return vector < IRQ_EXCEPTION_COUNT;
189}
190
191void irq_set_alloc(int32_t entry, bool used);
192int32_t irq_alloc_entry(void);
193void irq_free_entry(int32_t entry);
194bool irq_is_installed(int32_t entry);
195void irq_free_entry(int32_t entry);
196void irq_vector_disable(irq_t irq);
197void irq_vector_enable(irq_t irq);
198