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