1#include <acpi/hpet.h>
2#include <acpi/ioapic.h>
3#include <acpi/lapic.h>
4#include <irq/idt.h>
5#include <mem/alloc.h>
6#include <mem/alloc_or_die.h>
7#include <sch/sched.h>
8#include <sync/semaphore.h>
9#include <sync/spinlock.h>
10#include <thread/workqueue.h>
11
12struct deferred_event_queue {
13 struct deferred_event *head;
14 size_t timer;
15 struct spinlock lock;
16 size_t next_fire_time;
17 struct semaphore semaphore;
18 struct work work;
19};
20
21static struct deferred_event_queue *defer_queues = NULL;
22static struct workqueue *defer_workqueue;
23
24static inline uint64_t this_timer(void) {
25 return HPET_CURRENT;
26}
27
28static inline struct deferred_event_queue *this_defer_queue(void) {
29 return &defer_queues[this_timer()];
30}
31
32static void hpet_work(void *a, void *b) {
33 while (true) {
34 time_t now = hpet_timestamp_ms();
35 struct deferred_event_queue *defer_queue = a;
36 struct deferred_event *head = defer_queue->head;
37
38 enum irql irql = spin_lock_irq_disable(lock: &defer_queue->lock);
39 while (head && head->timestamp_ms <= now) {
40 struct deferred_event *ev = head;
41 head = ev->next;
42 defer_queue->head = head;
43
44 spin_unlock(lock: &defer_queue->lock, old: irql);
45
46 if (ev->callback)
47 ev->callback(ev->args.arg1, ev->args.arg2);
48
49 kfree(ev);
50 irql = spin_lock_irq_disable(lock: &defer_queue->lock);
51 }
52
53 if (defer_queue->head) {
54 defer_queue->next_fire_time = defer_queue->head->timestamp_ms;
55 hpet_program_oneshot(future_ms: defer_queue->head->timestamp_ms);
56 } else {
57 defer_queue->next_fire_time = UINT64_MAX;
58 }
59
60 spin_unlock(lock: &defer_queue->lock, old: irql);
61 semaphore_wait(s: &defer_queue->semaphore);
62 }
63}
64
65static enum irq_result hpet_irq_handler(void *ctx, irq_t irq,
66 struct irq_context *rsp) {
67 (void) irq, (void) ctx, (void) rsp;
68
69 struct deferred_event_queue *defer_queue = this_defer_queue();
70
71 semaphore_post(s: &defer_queue->semaphore);
72
73 hpet_clear_interrupt_status();
74 return IRQ_HANDLED;
75}
76
77bool defer_enqueue(work_function func, struct work_args args,
78 uint64_t delay_ms) {
79 struct deferred_event *ev =
80 kmalloc(sizeof(struct deferred_event), ALLOC_FLAGS_ZERO);
81 if (!ev)
82 return false;
83
84 uint64_t now = hpet_timestamp_ms();
85 ev->timestamp_ms = now + delay_ms;
86 ev->callback = func;
87 ev->args = args;
88
89 struct deferred_event_queue *queue = this_defer_queue();
90 enum irql irql = spin_lock_irq_disable(lock: &queue->lock);
91
92 if (!queue->head || ev->timestamp_ms < queue->head->timestamp_ms) {
93
94 ev->next = queue->head;
95 queue->head = ev;
96
97 if (ev->timestamp_ms < queue->next_fire_time) {
98 queue->next_fire_time = ev->timestamp_ms;
99 hpet_program_oneshot(future_ms: ev->timestamp_ms);
100 }
101 } else {
102 struct deferred_event *curr = queue->head;
103 while (curr->next && curr->next->timestamp_ms < ev->timestamp_ms)
104 curr = curr->next;
105
106 ev->next = curr->next;
107 curr->next = ev;
108 }
109
110 spin_unlock(lock: &queue->lock, old: irql);
111 return true;
112}
113
114void defer_init(void) {
115 defer_queues = alloc_or_die(
116 kmalloc(sizeof(struct deferred_event_queue) * hpet_timer_count,
117 ALLOC_FLAGS_ZERO));
118
119 struct cpu_mask mask;
120 alloc_or_die(cpu_mask_init(&mask, global.core_count));
121
122 cpu_mask_set_all(m: &mask);
123 struct workqueue_attributes attrs = {
124 .capacity = WORKQUEUE_DEFAULT_CAPACITY,
125 .flags = WORKQUEUE_FLAG_ON_DEMAND | WORKQUEUE_FLAG_NO_WORKER_GC,
126 .max_workers = 1,
127 .min_workers = 1,
128 .idle_check = WORKQUEUE_DEFAULT_IDLE_CHECK,
129 .spawn_delay = WORKQUEUE_DEFAULT_SPAWN_DELAY,
130 .worker_cpu_mask = mask,
131 };
132 defer_workqueue = workqueue_create(/* fmt = */ NULL, attrs: &attrs);
133
134 for (uint64_t i = 0; i < hpet_timer_count; i++) {
135 work_init(work: &defer_queues[i].work, fn: hpet_work,
136 WORK_ARGS(&defer_queues[i], NULL));
137
138 defer_queues[i].next_fire_time = UINT64_MAX;
139 defer_queues[i].timer = i;
140 workqueue_enqueue(queue: defer_workqueue, work: &defer_queues[i].work);
141 semaphore_init(s: &defer_queues[i].semaphore, value: 0,
142 SEMAPHORE_INIT_IRQ_DISABLE);
143 spinlock_init(lock: &defer_queues[i].lock);
144
145 uint8_t vector = irq_alloc_entry();
146
147 irq_register(name: "hpet_irq", vector, handler: hpet_irq_handler, NULL, flags: IRQ_FLAG_NONE);
148 irq_set_chip(vector, chip: lapic_get_chip(), NULL);
149 ioapic_route_irq(irq: i + 3, vector, dest_apic_id: i, false);
150
151 hpet_setup_timer(timer_index: i, irq_line: i + 3, false, true);
152
153 log_msg(LOG_INFO, "Timer %llu routed to IRQ %u (vector %u on core %u)",
154 i, i + HPET_IRQ_BASE, vector, i);
155 }
156}
157