AquilaOS documentation
How it works, and its code
Loading...
Searching...
No Matches
irq.c
Go to the documentation of this file.
1
21
22#include <core/system.h>
23#include <core/arch.h>
24#include <core/panic.h>
25#include <core/string.h>
26#include <cpu/cpu.h>
27#include <dev/irq.h>
28#include <platform/misc.h>
29#include <sys/sched.h>
30#include <sys/signal.h>
31#include <sys/lock.h>
32#include <mm/pmap.h>
33
34MALLOC_DEFINE(M_IRQ, "irq", "IRQ handler");
35
37struct handler {
39 void *arg;
40 struct handler *next;
41};
42
43/* each IRQ's handlers, how many times it came, and its own controller */
44static struct {
45 struct handler *handlers;
47 struct x86_irq_chip *chip; /* its own controller (MSI), else NULL */
49
50/* the system's interrupt controller: the 8259s or the APIC */
51static struct x86_irq_chip *chip;
52
53/* the first IRQ x86_irq_alloc() may give; none until the APIC sets it */
54static unsigned alloc_first = X86_IRQ_TIMER;
55
57static struct x86_irq_chip *chip_of(unsigned irq)
58{
59 return irqs[irq].chip ? irqs[irq].chip : chip;
60}
61
68{
69 chip = c;
70}
71
77const char *x86_irq_chip_name(void)
78{
79 return chip ? chip->name : "none";
80}
81
88void x86_irq_alloc_from(unsigned first)
89{
90 alloc_first = first;
91}
92
100{
101 for (unsigned irq = alloc_first; irq < X86_IRQ_TIMER; ++irq) {
102 if (!irqs[irq].handlers && !irqs[irq].chip) {
103 irqs[irq].chip = c;
104 return irq;
105 }
106 }
107
108 return -1;
109}
110
111/*
112 * Installs a handler for an IRQ. The first handler unmasks the IRQ at its
113 * controller.
114 */
115int irq_install(unsigned irq, irq_handler_t fn, void *arg)
116{
117 if (irq >= X86_IRQ_NR)
118 return -EINVAL;
119
120 struct handler *h = kmalloc(sizeof(*h), &M_IRQ, M_ZERO);
121
122 if (!h)
123 return -ENOMEM;
124
125 h->fn = fn;
126 h->arg = arg;
127
128 /* the first handler: the controller lets the IRQ through */
129 int first = !irqs[irq].handlers;
130 struct handler **p = &irqs[irq].handlers;
131
132 while (*p)
133 p = &(*p)->next;
134
135 *p = h;
136
137 if (first && chip_of(irq) && irq != X86_IRQ_TIMER)
138 chip_of(irq)->unmask(irq);
139
140 return 0;
141}
142
143/*
144 * Removes a handler of an IRQ. The last one gone masks the IRQ at its
145 * controller.
146 */
147void irq_uninstall(unsigned irq, irq_handler_t fn, void *arg)
148{
149 if (irq >= X86_IRQ_NR)
150 return;
151
152 for (struct handler **p = &irqs[irq].handlers; *p; p = &(*p)->next) {
153 struct handler *h = *p;
154
155 if (h->fn == fn && h->arg == arg) {
156 *p = h->next;
157 kfree(h);
158 break;
159 }
160 }
161
162 /* the last handler is gone: the controller keeps the IRQ out */
163 if (!irqs[irq].handlers && chip_of(irq) && irq != X86_IRQ_TIMER)
164 chip_of(irq)->mask(irq);
165}
166
170{
171 curcpu()->arch.tick_switch = 1;
172}
173
176static void irq_handle(struct x86_regs *r)
177{
178 uint32_t vec = r->vec;
179 unsigned irq = vec == X86_TIMER_VECTOR ? X86_IRQ_TIMER : vec - 32;
180
181 if (irq < X86_IRQ_NR) {
182 irqs[irq].count++;
183 curcpu()->arch.irq_regs = r;
184
185 for (struct handler *h = irqs[irq].handlers; h; h = h->next)
186 h->fn(h->arg);
187 }
188
189 struct x86_irq_chip *c = irq < X86_IRQ_NR ? chip_of(irq) : chip;
190
191 if (c)
192 c->eoi(irq);
193}
194
197static void irq_dispatch(struct x86_regs *r)
198{
199 irq_handle(r);
200
201 /* the tick asked for a switch: the running thread goes back to the
202 * ready list, and the next one runs (if the CPU was idle, it runs) */
203 if (curcpu()->arch.tick_switch) {
204 curcpu()->arch.tick_switch = 0;
205
206 if (curcpu()->idle) {
207 schedule();
208 } else {
209 extern void x86_preempt(void);
210 x86_preempt();
211 }
212 }
213
215
216 /* an urgent thread (the network's) was woken: it runs now, not at the
217 * next tick, and the interrupted thread is ready again */
218 if (sched_urgent && (r->cs & 3) == 3 && curthread) {
219 extern void x86_preempt(void);
220 x86_preempt();
221 }
222
223 /* return to user mode (maybe after other threads ran), delivering the
224 * signals that came meanwhile */
225 x86_return_to_user(r, 0);
226}
227
238{
239 if (r->vec == X86_TLB_VECTOR) {
240 x86_tlb_ipi();
242 return;
243 }
244
245 int took = kernel_enter();
246
247 /* a CPU woken up for the first time starts its tick, now that the
248 * scheduler runs */
249 if (r->vec == X86_WAKE_VECTOR && !curcpu()->arch.ticking)
251
252 /* its process was killed while it ran (from another CPU): handle the
253 * interrupt (the device's, and the controller's EOI), then the thread
254 * ends */
255 if (took && curthread && curthread->killed) {
256 irq_handle(r);
258 }
259
260 irq_dispatch(r);
261 kernel_leave(took);
262}
263
267{
268 extern void __x86_irq0(void), __x86_irq1(void), __x86_irq2(void), __x86_irq3(void),
269 __x86_irq4(void), __x86_irq5(void), __x86_irq6(void), __x86_irq7(void),
270 __x86_irq8(void), __x86_irq9(void), __x86_irq10(void), __x86_irq11(void),
271 __x86_irq12(void), __x86_irq13(void), __x86_irq14(void), __x86_irq15(void),
272 __x86_irq16(void), __x86_irq17(void), __x86_irq18(void), __x86_irq19(void),
273 __x86_irq20(void), __x86_irq21(void), __x86_irq22(void), __x86_irq23(void),
274 __x86_irq24(void), __x86_irq25(void), __x86_irq26(void), __x86_irq27(void),
275 __x86_irq28(void), __x86_irq29(void), __x86_irq30(void), __x86_irq31(void),
276 __x86_irq32(void), __x86_irq33(void), __x86_irq34(void), __x86_irq35(void),
277 __x86_irq36(void), __x86_irq37(void), __x86_irq38(void), __x86_irq39(void),
278 __x86_irq40(void), __x86_irq41(void), __x86_irq42(void), __x86_irq43(void),
279 __x86_irq44(void), __x86_irq45(void), __x86_irq46(void), __x86_irq47(void),
280 __x86_irq240(void), __x86_irq241(void), __x86_irq242(void), __x86_spurious(void);
281
282 static void (*stubs[48])(void) = {
283 __x86_irq0, __x86_irq1, __x86_irq2, __x86_irq3, __x86_irq4, __x86_irq5,
284 __x86_irq6, __x86_irq7, __x86_irq8, __x86_irq9, __x86_irq10, __x86_irq11,
285 __x86_irq12, __x86_irq13, __x86_irq14, __x86_irq15, __x86_irq16, __x86_irq17,
286 __x86_irq18, __x86_irq19, __x86_irq20, __x86_irq21, __x86_irq22, __x86_irq23,
287 __x86_irq24, __x86_irq25, __x86_irq26, __x86_irq27, __x86_irq28, __x86_irq29,
288 __x86_irq30, __x86_irq31, __x86_irq32, __x86_irq33, __x86_irq34, __x86_irq35,
289 __x86_irq36, __x86_irq37, __x86_irq38, __x86_irq39, __x86_irq40, __x86_irq41,
290 __x86_irq42, __x86_irq43, __x86_irq44, __x86_irq45, __x86_irq46, __x86_irq47,
291 };
292
293 for (int i = 0; i < 48; ++i)
294 x86_idt_gate_set(32 + i, (uintptr_t) stubs[i]);
295
300}
301
310int arch_interrupts_info(char *buf, size_t size)
311{
312 int n = snprintf(buf, size, "controller: %s\n", x86_irq_chip_name());
313
314 for (unsigned irq = 0; irq < X86_IRQ_NR && (size_t) n < size; ++irq) {
315 if (!irqs[irq].handlers && !irqs[irq].count)
316 continue;
317
318 char line[96];
319
320 if (irq == X86_IRQ_TIMER)
321 snprintf(line, sizeof(line), "timer: %ld %s\n", irqs[irq].count,
322 chip && chip->timer ? chip->timer : "");
323 else
324 snprintf(line, sizeof(line), "%d: %ld %s\n", irq, irqs[irq].count,
325 chip_of(irq) && chip_of(irq)->describe ? chip_of(irq)->describe(irq) : "");
326
327 n += snprintf(buf + n, size - n, "%s", line);
328 }
329
330 /* how many TLB flushes each CPU did for another (mm/tlb.c) */
331 if (cpus_count > 1 && (size_t) n < size) {
332 n += snprintf(buf + n, size - n, "tlb:");
333
334 for (int i = 0; i < cpus_count && (size_t) n < size; ++i)
335 n += snprintf(buf + n, size - n, " %ld", cpus[i].arch.tlb_flushes);
336
337 if ((size_t) n < size)
338 n += snprintf(buf + n, size - n, "\n");
339 }
340
341 return n;
342}
void irq_uninstall(unsigned irq, irq_handler_t fn, void *arg)
Removes a handler that irq_install() installed.
Definition irq.c:62
int arch_interrupts_info(char *buf, size_t size)
Definition irq.c:105
int irq_install(unsigned irq, irq_handler_t fn, void *arg)
Installs a handler for an IRQ.
Definition irq.c:32
static struct handler * handlers[IRQ_NR]
Definition irq.c:26
void x86_return_to_user(struct x86_regs *r, int syscall)
Delivers the current process's pending signals on the way back to user mode: stops and default action...
Definition signal.c:246
void x86_tlb_ipi(void)
Does the pending request if it is for the running CPU.
Definition tlb.c:87
static struct x86_irq_chip * chip
Definition irq.c:51
static unsigned alloc_first
Definition irq.c:54
void __x86_irq_handler(struct x86_regs *r)
Handles an interrupt: a device's, the tick, or an IPI.
Definition irq.c:237
static struct @150152003336234104023037216251060113077236146312 irqs[X86_IRQ_NR]
static void irq_dispatch(struct x86_regs *r)
Handles an interrupt, then switches threads if the tick asked for it or an urgent thread was woken,...
Definition irq.c:197
static struct x86_irq_chip * chip_of(unsigned irq)
Returns the controller of an IRQ: its own, or the system's.
Definition irq.c:57
void x86_idt_gate_set(uint32_t id, uintptr_t offset)
Sets an interrupt gate that only the hardware (and the kernel) can raise: an exception or a device in...
Definition idt.c:45
struct cpu cpus[CPUS_MAX]
Definition sched.c:28
int cpus_count
Definition sched.c:29
void(*) irq_handler_t(void *arg)
Definition irq.h:21
#define MALLOC_DEFINE(type, _name, _desc)
Definition kvmem.h:30
void * kmalloc(size_t size, struct malloc_type *type, int flags)
Allocates memory for one of the kernel's structures.
Definition kvmem.c:123
#define M_ZERO
Definition kvmem.h:26
void kfree(void *)
Frees an object that kmalloc() gave.
Definition kvmem.c:146
void kernel_leave(int took)
Lets go of the kernel lock on leaving the kernel, if kernel_enter() took it.
Definition lock.c:83
int kernel_enter(void)
Takes the kernel lock on entering the kernel, unless this CPU holds it already (an exception inside t...
Definition lock.c:68
#define X86_TLB_VECTOR
Definition misc.h:15
void x86_irq_gates_setup(void)
Sets the IDT's gates for the interrupts: vectors 32 to 79, the local APIC's timer,...
Definition irq.c:266
#define X86_IRQ_NR
Definition misc.h:12
void x86_irq_tick_switch(void)
Asks for a thread switch once the current interrupt is done: the tick calls it.
Definition irq.c:169
int x86_irq_alloc(struct x86_irq_chip *chip)
Gives a free IRQ with a controller of its own, for an MSI.
Definition irq.c:99
void x86_lapic_eoi(void)
Tells the running CPU's local APIC that its interrupt (an IPI) is handled.
Definition apic.c:216
void x86_irq_chip_set(struct x86_irq_chip *chip)
Sets the system's interrupt controller.
Definition irq.c:67
#define X86_TIMER_VECTOR
Definition misc.h:14
const char * x86_irq_chip_name(void)
Returns the name of the system's interrupt controller.
Definition irq.c:77
#define X86_IRQ_TIMER
Definition misc.h:13
#define X86_WAKE_VECTOR
Definition misc.h:16
void x86_irq_alloc_from(unsigned first)
Sets the first IRQ that x86_irq_alloc() may give: past the I/O APICs' last pin.
Definition irq.c:88
void x86_lapic_timer_cpu_start(void)
Starts the running CPU's local APIC timer, with the boot CPU's calibration, once it has been done.
Definition apic.c:529
#define X86_SPURIOUS_VECTOR
Definition misc.h:17
void sched_idle_wakeup(void)
Runs a ready thread now if an interrupt made one ready while this CPU was idle, rather than at the ne...
Definition sched.c:347
int sched_urgent
Definition sched.c:306
void sched_thread_killed(void)
Ends the current thread, which was killed while it ran on this CPU (thread->killed).
Definition sched.c:258
void schedule(void)
Switches to the next ready thread.
Definition sched.c:400
#define curthread
Definition sched.h:12
int snprintf(char *s, size_t n, const char *fmt,...)
Formats a string into a buffer.
Definition snprintf.c:226
__SIZE_TYPE__ uintptr_t
Definition stddef.h:12
unsigned int uint32_t
Definition stddef.h:22
__SIZE_TYPE__ size_t
Definition stddef.h:7
unsigned long long int uint64_t
Definition stddef.h:23
uint64_t count
struct handler * handlers
struct x86_irq_chip * chip
An installed handler, on its interrupt's list.
Definition irq.c:19
void * arg
Definition irq.c:21
struct handler * next
Definition irq.c:22
irq_handler_t fn
Definition irq.c:20
An interrupt controller: the 8259s, or the APIC.
Definition misc.h:20
const char *(*) describe(unsigned irq)
Definition misc.h:25
void(*) unmask(unsigned irq)
Definition misc.h:23
const char * timer
Definition misc.h:26
const char * name
Definition misc.h:21
void(*) eoi(unsigned irq)
Definition misc.h:24
void(*) mask(unsigned irq)
Definition misc.h:22
The registers an interrupt or exception saved, on the kernel stack, as cpu.S's stubs push them: the g...
Definition cpu.h:12
uint64_t cs
Definition cpu.h:24
uint64_t vec
Definition cpu.h:23
void irq_handle(void)
Runs the handlers of the interrupt that came, then tells the GIC it's done, before any thread switch,...
Definition irq.c:84