PatchworkOS  19e446b
A non-POSIX operating system.
Loading...
Searching...
No Matches
irq.c
Go to the documentation of this file.
1#include <kernel/cpu/irq.h>
2
3#include <kernel/cpu/cpu.h>
4#include <kernel/cpu/gdt.h>
6#include <kernel/cpu/percpu.h>
7#include <kernel/log/log.h>
8#include <kernel/log/panic.h>
10
11#include <assert.h>
12#include <errno.h>
13#include <stdlib.h>
14#include <sys/bitmap.h>
15#include <sys/list.h>
16#include <sys/math.h>
17
19
20/// @todo Optimize domain lookup?
23
25{
26 if (virt < VECTOR_EXTERNAL_START || virt >= VECTOR_EXTERNAL_END)
27 {
28 return NULL;
29 }
30
31 return &irqs[virt - VECTOR_EXTERNAL_START];
32}
33
35{
36 if (irq->domain == NULL || irq->domain->chip == NULL)
37 {
38 return 0; // Nothing to do
39 }
40
41 if (!list_is_empty(&irq->handlers))
42 {
43 return irq->domain->chip->enable(irq);
44 }
45
46 irq->domain->chip->disable(irq);
47 return 0;
48}
49
50// Must be called with domainsLock held
52{
53 irq_domain_t* domain;
54 LIST_FOR_EACH(domain, &domains, entry)
55 {
56 if (phys >= domain->start && phys < domain->end)
57 {
58 return domain;
59 }
60 }
61
62 return NULL;
63}
64
66{
67 for (irq_virt_t virt = VECTOR_EXTERNAL_START; virt < VECTOR_EXTERNAL_END; virt++)
68 {
69 irq_t* irq = irq_get(virt);
70 assert(irq != NULL);
71
73 if (irq->refCount == 0 || irq->phys < newDomain->start || irq->phys >= newDomain->end)
74 {
76 continue;
77 }
78
79 irq->domain = newDomain;
80 if (irq_update(irq) == ERR)
81 {
82 irq->domain = NULL;
84
85 for (irq_virt_t revVirt = VECTOR_EXTERNAL_START; revVirt < virt; revVirt++)
86 {
87 irq_t* revIrq = irq_get(revVirt);
88 assert(revIrq != NULL);
89 RWLOCK_WRITE_SCOPE(&revIrq->lock);
90
91 if (revIrq == NULL || revIrq->domain != newDomain)
92 {
93 continue;
94 }
95
96 if (!list_is_empty(&revIrq->handlers))
97 {
98 newDomain->chip->disable(revIrq);
99 }
100 revIrq->domain = NULL;
101 }
102 return ERR;
103 }
104
105 LOG_INFO("mapped IRQ 0x%02x to 0x%02x while adding '%s'\n", irq->phys, virt, irq->domain->chip->name);
107 }
108
109 return 0;
110}
111
112void irq_init(void)
113{
114 for (uint64_t i = 0; i < VECTOR_EXTERNAL_AMOUNT; i++)
115 {
116 irq_t* irq = &irqs[i];
117 irq->phys = IRQ_PHYS_NONE;
118 irq->virt = VECTOR_EXTERNAL_START + i;
119 irq->flags = 0;
120 irq->cpu = NULL;
121 irq->domain = NULL;
122 irq->refCount = 0;
123 list_init(&irq->handlers);
124 rwlock_init(&irq->lock);
125 }
126}
127
129{
130 assert(frame != NULL);
131
132 irq_t* irq = irq_get(frame->vector);
133 if (irq == NULL)
134 {
135 panic(NULL, "Unexpected vector 0x%x dispatched through IRQ system", frame->vector);
136 }
137
138 RWLOCK_READ_SCOPE(&irq->lock);
139
140 if (irq->domain == NULL || irq->domain->chip == NULL)
141 {
142 LOG_WARN("unhandled IRQ 0x%x received with no domain\n", frame->vector);
143 return;
144 }
145 irq_chip_t* chip = irq->domain->chip;
146
147 if (chip->ack != NULL)
148 {
149 chip->ack(irq);
150 }
151
152 irq_handler_t* handler;
153 LIST_FOR_EACH(handler, &irq->handlers, entry)
154 {
156 .frame = frame,
157 .virt = frame->vector,
158 .data = handler->data,
159 };
160
161 handler->func(&data);
162 }
163
164 if (chip->eoi != NULL)
165 {
166 chip->eoi(irq);
167 }
168}
169
171{
172 if (out == NULL)
173 {
174 errno = EINVAL;
175 return ERR;
176 }
177
179
180 if (cpu == NULL)
181 {
182 cpu = SELF->self;
183 }
184
185 irq_virt_t targetVirt = 0;
186 for (irq_virt_t virt = VECTOR_EXTERNAL_START; virt < VECTOR_EXTERNAL_END; virt++)
187 {
188 irq_t* irq = irq_get(virt);
189 assert(irq != NULL);
191
192 if (irq->refCount > 0 && irq->phys == phys)
193 {
194 if (irq->flags != flags || irq->flags & IRQ_EXCLUSIVE)
195 {
197 errno = EBUSY;
198 return ERR;
199 }
200
201 targetVirt = virt;
202 break; // Lock still held
203 }
205 }
206
207 if (targetVirt == 0)
208 {
209 for (irq_virt_t virt = VECTOR_EXTERNAL_START; virt < VECTOR_EXTERNAL_END; virt++)
210 {
211 irq_t* irq = irq_get(virt);
212 assert(irq != NULL);
214
215 if (irq->refCount == 0)
216 {
217 targetVirt = virt;
218 break; // Lock still held
219 }
221 }
222 }
223
224 if (targetVirt == 0)
225 {
226 errno = ENOSPC;
227 return ERR;
228 }
229
230 irq_t* irq = irq_get(targetVirt);
231 assert(irq != NULL);
232
233 irq->phys = phys;
234 irq->virt = targetVirt;
235 irq->flags = flags;
236 irq->cpu = cpu;
237 irq->domain = irq_domain_lookup(phys);
238
239 if (irq_update(irq) == ERR)
240 {
242 return ERR;
243 }
244
245 irq->refCount++;
247
248 *out = targetVirt;
249 return 0;
250}
251
253{
254 irq_t* irq = irq_get(virt);
255 if (irq == NULL)
256 {
257 return;
258 }
259
261
262 if (irq->refCount == 0)
263 {
264 return;
265 }
266
267 irq->refCount--;
268 if (irq->refCount > 0)
269 {
270 return;
271 }
272
273 if (!list_is_empty(&irq->handlers))
274 {
275 irq->domain->chip->disable(irq);
276 }
277 irq->phys = IRQ_PHYS_NONE;
278 irq->flags = 0;
279 irq->cpu = NULL;
280 irq->domain = NULL;
281
282 while (!list_is_empty(&irq->handlers))
283 {
285 free(handler);
286 }
287}
288
290{
291 if (cpu == NULL)
292 {
293 errno = EINVAL;
294 return ERR;
295 }
296
297 irq_t* irq = irq_get(virt);
298 if (irq == NULL)
299 {
300 errno = ENOENT;
301 return ERR;
302 }
304
305 if (irq->domain == NULL || irq->domain->chip == NULL)
306 {
307 errno = ENODEV;
308 return ERR;
309 }
310
311 if (!list_is_empty(&irq->handlers))
312 {
313 irq->domain->chip->disable(irq);
314 irq->cpu = cpu;
315 if (irq->domain->chip->enable(irq) == ERR)
316 {
317 return ERR;
318 }
319 }
320 else
321 {
322 irq->cpu = cpu;
323 }
324
325 return 0;
326}
327
329{
330 if (chip == NULL || chip->enable == NULL || chip->disable == NULL || start >= end)
331 {
332 errno = EINVAL;
333 return ERR;
334 }
335
337
338 irq_domain_t* existing;
339 LIST_FOR_EACH(existing, &domains, entry)
340 {
341 if (MAX(start, existing->start) < MIN(end, existing->end))
342 {
343 errno = EEXIST;
344 return ERR;
345 }
346 }
347
348 irq_domain_t* domain = malloc(sizeof(irq_domain_t));
349 if (domain == NULL)
350 {
351 errno = ENOMEM;
352 return ERR;
353 }
354 list_entry_init(&domain->entry);
355 domain->chip = chip;
356 domain->data = data;
357 domain->start = start;
358 domain->end = end;
359
360 list_push_back(&domains, &domain->entry);
361
363 {
364 list_remove(&domain->entry);
365 free(domain);
366 return ERR;
367 }
368
369 return 0;
370}
371
373{
374 if (chip == NULL || start >= end)
375 {
376 return;
377 }
378
380
381 irq_domain_t* domain;
382 irq_domain_t* temp;
383 LIST_FOR_EACH_SAFE(domain, temp, &domains, entry)
384 {
385 if (domain->chip != chip)
386 {
387 continue;
388 }
389
390 if (MAX(start, domain->start) >= MIN(end, domain->end))
391 {
392 continue;
393 }
394
395 for (irq_virt_t virt = VECTOR_EXTERNAL_START; virt < VECTOR_EXTERNAL_END; virt++)
396 {
397 irq_t* irq = irq_get(virt);
398 assert(irq != NULL);
399
401 if (irq->domain == domain)
402 {
403 if (!list_is_empty(&irq->handlers))
404 {
405 irq->domain->chip->disable(irq);
406 }
407 irq->domain = NULL;
408 }
410 }
411
412 list_remove(&domain->entry);
413 free(domain);
414 }
415}
416
422
424{
425 if (func == NULL)
426 {
427 errno = EINVAL;
428 return ERR;
429 }
430
431 irq_t* irq = irq_get(virt);
432 if (irq == NULL)
433 {
434 errno = ENOENT;
435 return ERR;
436 }
437
439
440 irq_handler_t* iter;
441 LIST_FOR_EACH(iter, &irq->handlers, entry)
442 {
443 if (iter->func == func)
444 {
445 errno = EEXIST;
446 return ERR;
447 }
448 }
449
450 irq_handler_t* handler = malloc(sizeof(irq_handler_t));
451 if (handler == NULL)
452 {
453 errno = ENOMEM;
454 return ERR;
455 }
456 list_entry_init(&handler->entry);
457 handler->func = func;
458 handler->data = data;
459 handler->virt = virt;
460
461 list_push_back(&irq->handlers, &handler->entry);
462
463 if (irq_update(irq) == ERR)
464 {
465 free(handler);
466 return ERR;
467 }
468
469 return 0;
470}
471
473{
474 if (func == NULL)
475 {
476 return;
477 }
478
479 irq_t* irq = irq_get(virt);
480 if (irq == NULL)
481 {
482 return;
483 }
484
486
487 irq_handler_t* iter;
488 LIST_FOR_EACH(iter, &irq->handlers, entry)
489 {
490 if (iter->func == func)
491 {
492 list_remove(&iter->entry);
493 free(iter);
494
495 if (list_is_empty(&irq->handlers))
496 {
497 irq->domain->chip->disable(irq);
498 }
499 return;
500 }
501 }
502
503 LOG_WARN("attempted to unregister non-registered irq handler %p for irq 0x%x\n", func, virt);
504}
#define assert(expression)
Definition assert.h:29
static void start()
Definition main.c:542
static fd_t data
Definition dwm.c:21
@ VECTOR_EXTERNAL_END
Exclusive end of external interrupts.
Definition interrupt.h:173
@ VECTOR_EXTERNAL_START
Inclusive start of external interrupts (handled by the IRQ system).
Definition interrupt.h:172
@ VECTOR_EXTERNAL_AMOUNT
Definition interrupt.h:175
void irq_chip_unregister(irq_chip_t *chip, irq_phys_t start, irq_phys_t end)
Unregister all instances of the given IRQ chip within the specified range.
Definition irq.c:372
void irq_virt_free(irq_virt_t virt)
Free a previously allocated virtual IRQ.
Definition irq.c:252
void irq_handler_unregister(irq_func_t func, irq_virt_t virt)
Unregister an IRQ handler.
Definition irq.c:472
void irq_init(void)
Initialize the IRQ subsystem.
Definition irq.c:112
#define IRQ_PHYS_NONE
Constant representing no physical IRQ.
Definition irq.h:51
uint64_t irq_virt_set_affinity(irq_virt_t virt, cpu_t *cpu)
Change the CPU responsible for an IRQ.
Definition irq.c:289
uint32_t irq_phys_t
Physical IRQ numbers.
Definition irq.h:46
uint64_t irq_handler_register(irq_virt_t virt, irq_func_t func, void *data)
Register an IRQ handler for a virtual IRQ.
Definition irq.c:423
irq_flags_t
IRQ flags.
Definition irq.h:93
uint64_t irq_chip_register(irq_chip_t *chip, irq_phys_t start, irq_phys_t end, void *data)
Register an IRQ chip for a range of physical IRQs.
Definition irq.c:328
void irq_dispatch(interrupt_frame_t *frame)
Dispatch an IRQ.
Definition irq.c:128
uint64_t irq_chip_amount(void)
Get the number of registered IRQ chips.
Definition irq.c:417
void(* irq_func_t)(irq_func_data_t *data)
Callback function type for IRQs.
Definition irq.h:73
uint64_t irq_virt_alloc(irq_virt_t *out, irq_phys_t phys, irq_flags_t flags, cpu_t *cpu)
Allocate a virtual IRQ mapped to the given physical IRQ.
Definition irq.c:170
uint8_t irq_virt_t
Virtual IRQ numbers.
Definition irq.h:57
@ IRQ_EXCLUSIVE
If set, the IRQ is exclusive (not shared).
Definition irq.h:98
#define SELF
Macro to access data in the current cpu.
Definition percpu.h:85
NORETURN void panic(const interrupt_frame_t *frame, const char *format,...)
Panic the kernel, printing a message and halting.
Definition panic.c:292
#define LOG_WARN(format,...)
Definition log.h:92
#define LOG_INFO(format,...)
Definition log.h:91
static void rwlock_write_release(rwlock_t *lock)
Releases a rwlock from writing.
Definition rwlock.h:196
static void rwlock_init(rwlock_t *lock)
Initializes a rwlock.
Definition rwlock.h:79
#define RWLOCK_READ_SCOPE(lock)
Acquires a rwlock for reading for the reminder of the current scope.
Definition rwlock.h:34
static void rwlock_write_acquire(rwlock_t *lock)
Acquires a rwlock for writing, blocking until it is available.
Definition rwlock.h:158
#define RWLOCK_CREATE()
Create a rwlock initializer.
Definition rwlock.h:52
#define RWLOCK_WRITE_SCOPE(lock)
Acquires a rwlock for writing for the reminder of the current scope.
Definition rwlock.h:43
#define ENOENT
No such file or directory.
Definition errno.h:42
#define ENOSPC
No space left on device.
Definition errno.h:172
#define EEXIST
File exists.
Definition errno.h:117
#define EINVAL
Invalid argument.
Definition errno.h:142
#define ENOMEM
Out of memory.
Definition errno.h:92
#define EBUSY
Device or resource busy.
Definition errno.h:112
#define errno
Error number variable.
Definition errno.h:27
#define ENODEV
No such device.
Definition errno.h:127
#define LIST_FOR_EACH(elem, list, member)
Iterates over a list.
Definition list.h:58
static void list_remove(list_entry_t *entry)
Removes a list entry from its current list.
Definition list.h:290
static void list_push_back(list_t *list, list_entry_t *entry)
Pushes an entry to the end of the list.
Definition list.h:322
#define LIST_CREATE(name)
Creates a list initializer.
Definition list.h:163
#define LIST_FOR_EACH_SAFE(elem, temp, list, member)
Safely iterates over a list, allowing for element removal during iteration.
Definition list.h:73
static bool list_is_empty(list_t *list)
Checks if a list is empty.
Definition list.h:210
static uint64_t list_size(list_t *list)
Gets the size of the list.
Definition list.h:478
static void list_entry_init(list_entry_t *entry)
Initializes a list entry.
Definition list.h:173
static list_entry_t * list_pop_front(list_t *list)
Pops the first entry from the list.
Definition list.h:366
static void list_init(list_t *list)
Initializes a list.
Definition list.h:185
#define MIN(x, y)
Definition math.h:18
#define MAX(x, y)
Definition math.h:17
#define NULL
Pointer error value.
Definition NULL.h:25
#define ERR
Integer error value.
Definition ERR.h:17
#define CONTAINER_OF(ptr, type, member)
Container of macro.
static list_t domains
Definition irq.c:21
static irq_domain_t * irq_domain_lookup(irq_phys_t phys)
Definition irq.c:51
static irq_t irqs[VECTOR_EXTERNAL_AMOUNT]
Definition irq.c:18
static rwlock_t domainsLock
Definition irq.c:22
static uint64_t irq_domain_rebind_orphaned_irqs(irq_domain_t *newDomain)
Definition irq.c:65
static irq_t * irq_get(irq_virt_t virt)
Definition irq.c:24
static uint64_t irq_update(irq_t *irq)
Definition irq.c:34
static const path_flag_t flags[]
Definition path.c:47
__UINT64_TYPE__ uint64_t
Definition stdint.h:17
_PUBLIC void * malloc(size_t size)
Definition malloc.c:5
_PUBLIC void free(void *ptr)
Definition free.c:11
CPU structure.
Definition cpu.h:84
cpu_t * self
Definition cpu.h:85
Trap Frame Structure.
Definition interrupt.h:195
IRQ chip structure.
Definition irq.h:142
void(* disable)(irq_t *irq)
Disable the given IRQ, must be defined.
Definition irq.h:145
const char * name
Definition irq.h:143
uint64_t(* enable)(irq_t *irq)
Enable the given IRQ, must be defined.
Definition irq.h:144
void(* eoi)(irq_t *irq)
Send End-Of-Interrupt for the given IRQ.
Definition irq.h:147
void(* ack)(irq_t *irq)
Send a acknowledge for the given IRQ.
Definition irq.h:146
IRQ domain structure.
Definition irq.h:127
irq_phys_t start
Inclusive.
Definition irq.h:131
irq_chip_t * chip
Definition irq.h:129
list_entry_t entry
Definition irq.h:128
void * data
Definition irq.h:130
irq_phys_t end
Exclusive.
Definition irq.h:132
Data passed to IRQ functions.
Definition irq.h:64
interrupt_frame_t * frame
Definition irq.h:65
Structure to hold an IRQ function and its data.
Definition irq.h:79
irq_func_t func
Definition irq.h:81
void * data
Definition irq.h:82
list_entry_t entry
Definition irq.h:80
irq_virt_t virt
Definition irq.h:83
IRQ structure.
Definition irq.h:109
irq_virt_t virt
Definition irq.h:111
irq_domain_t * domain
Definition irq.h:114
irq_phys_t phys
Definition irq.h:110
uint64_t refCount
Definition irq.h:115
list_t handlers
Definition irq.h:116
irq_flags_t flags
Definition irq.h:112
cpu_t * cpu
The CPU with affinity for this IRQ, may be NULL.
Definition irq.h:113
rwlock_t lock
Definition irq.h:117
A doubly linked list.
Definition list.h:46
Read-Write Ticket Lock structure.
Definition rwlock.h:66