The Pedigree Project 0.1
mach_virt/Timer.cc
1/*
2 * Copyright (c) 2026, Pedigree Developers
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted.
6 */
7
8#include "Timer.h"
9#include "pedigree/kernel/LockGuard.h"
10#include "pedigree/kernel/machine/IrqManager.h"
11#include "pedigree/kernel/machine/Machine.h"
12#include "pedigree/kernel/process/Event.h"
13#include "pedigree/kernel/process/Thread.h"
14#include "pedigree/kernel/processor/Processor.h"
15#include "pedigree/kernel/utilities/String.h"
16
17#include "DeviceTree.h"
18#include "GenericTimer.h"
19
20namespace {
21constexpr uint64_t NanosecondsPerSecond = 1000000000ULL;
22
23bool leapYear(size_t year) {
24 return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0);
25}
26
27uint8_t daysInMonth(size_t year, uint8_t month) {
28 static constexpr uint8_t days[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
29 return month == 2 && leapYear(year) ? 29 : days[month - 1];
30}
31
32uint64_t addDuration(uint64_t deadline, size_t count, uint64_t multiplier) {
33 if (count > (Time::Infinity - deadline) / multiplier) {
34 return Time::Infinity;
35 }
36 return deadline + count * multiplier;
37}
38} // namespace
39
40VirtTimer VirtTimer::m_Instance;
41
42VirtTimer& VirtTimer::instance() {
43 return m_Instance;
44}
45
46VirtTimer::VirtTimer()
47 : SplitIrqHandler(MakeConstantString("Virt timer bottom half")),
48 m_Frequency(0),
49 m_BootCount(0),
50 m_LastCount(0),
51 m_RtcBaseCount(0),
52 m_RtcBaseSeconds(0),
53 m_ElapsedSinceSync(0),
54 m_IntervalTicks(0),
55 m_IrqId(0),
56 m_Year(1970),
57 m_Month(1),
58 m_Day(1),
59 m_DayOfWeek(4),
60 m_Hour(0),
61 m_Minute(0),
62 m_Second(0),
63 m_Prepared(false),
64 m_Initialised(false),
65 m_Handlers(),
66 m_Alarms(),
67 m_AlarmLock(false) {}
68
69VirtTimer::~VirtTimer() {
70 uninitialise();
71}
72
73uint64_t VirtTimer::counter() {
74 return VirtGenericTimer::count();
75}
76
77uint64_t VirtTimer::ticksToNanoseconds(uint64_t ticks) const {
78 if (!m_Frequency) {
79 return 0;
80 }
81 return (ticks / m_Frequency) * NanosecondsPerSecond +
82 (ticks % m_Frequency) * NanosecondsPerSecond / m_Frequency;
83}
84
85bool VirtTimer::initialise1() {
86 if (m_Prepared) {
87 return false;
88 }
89 m_Frequency = VirtGenericTimer::frequency();
90 if (!m_Frequency) {
91 return false;
92 }
93 m_IntervalTicks = static_cast<uint32_t>(m_Frequency / 1000);
94 if (!m_IntervalTicks) {
95 m_IntervalTicks = 1;
96 }
97 m_BootCount = m_LastCount = counter();
98 m_Handlers.reset();
100 VirtGenericTimer::physicalControl(0);
101 m_Prepared = true;
102 return true;
103}
104
105bool VirtTimer::initialise3() {
106 if (!m_Prepared || m_Initialised || !initialiseSplitIrq()) {
107 return false;
108 }
109 IrqManager& manager = *Machine::instance().getIrqManager();
110 m_IrqId =
111 registerIsaSplitIrq(manager, VirtDeviceTree::physicalTimerIrq(), IrqPolicy::levelHard());
112 if (!m_IrqId) {
114 return false;
115 }
116 m_LastCount = counter();
118 m_Initialised = true;
119 return true;
120}
121
122void VirtTimer::uninitialise() {
123 if (!m_Prepared) {
124 return;
125 }
126 if (m_Initialised) {
127 if (!shutdownSplitIrq()) {
128 return;
129 }
130 m_IrqId = 0;
131 m_Initialised = false;
132 }
133 m_Handlers.reset();
134 {
135 LockGuard<Spinlock> guard(m_AlarmLock);
136 for (List<Alarm*>::Iterator it = m_Alarms.begin(); it != m_Alarms.end(); ++it) {
137 delete *it;
138 }
139 m_Alarms.clear();
140 }
141 m_Prepared = false;
142}
143
144VirtTimer::HardStageDisposition VirtTimer::hardIrq(irq_id_t number, InterruptState&, size_t& work) {
145 if (number != m_IrqId) {
146 return HardStageDisposition::NotHandled;
147 }
148 VirtGenericTimer::physicalControl(0);
149 work = 1;
150 return HardStageDisposition::Deferred;
151}
152
153void VirtTimer::threadedIrq(size_t work) {
154 if (!work) {
155 return;
156 }
157 const uint64_t now = counter();
158 const uint64_t delta = ticksToNanoseconds(now - m_LastCount);
159 m_LastCount = now;
160 processElapsed(delta);
161}
162
164 VirtGenericTimer::physicalControl(0);
165 return true;
166}
167
169 VirtGenericTimer::setPhysicalTimer(m_IntervalTicks);
170}
171
173 return getTickCountNano() / Time::Multiplier::Millisecond;
174}
175
177 return ticksToNanoseconds(counter() - m_BootCount);
178}
179
181 return getTickCountNano();
182}
183
185 return m_RtcBaseSeconds + ticksToNanoseconds(counter() - m_RtcBaseCount) / NanosecondsPerSecond;
186}
187
189 return ticksToNanoseconds(counter() - m_RtcBaseCount) % NanosecondsPerSecond;
190}
191
192void VirtTimer::updateCivilTime(uint64_t seconds) {
193 uint64_t days = seconds / 86400;
194 uint64_t remainder = seconds % 86400;
195 m_DayOfWeek = static_cast<uint8_t>((days + 4) % 7);
196 m_Year = 1970;
197 while (days >= (leapYear(m_Year) ? 366U : 365U)) {
198 days -= leapYear(m_Year) ? 366U : 365U;
199 ++m_Year;
200 }
201 m_Month = 1;
202 while (days >= daysInMonth(m_Year, m_Month)) {
203 days -= daysInMonth(m_Year, m_Month);
204 ++m_Month;
205 }
206 m_Day = static_cast<uint8_t>(days + 1);
207 m_Hour = static_cast<uint8_t>(remainder / 3600);
208 remainder %= 3600;
209 m_Minute = static_cast<uint8_t>(remainder / 60);
210 m_Second = static_cast<uint8_t>(remainder % 60);
211}
212
213void VirtTimer::synchronise(bool tohw) {
214 if (tohw) {
215 return;
216 }
217 const uintptr_t rtc = VirtDeviceTree::rtcBase();
218 const uint64_t count = counter();
219 m_RtcBaseSeconds = rtc ? *reinterpret_cast<volatile uint32_t*>(rtc) : 0;
220 m_RtcBaseCount = count;
221 updateCivilTime(m_RtcBaseSeconds);
222}
223
225 return m_Year;
226}
227
229 return m_Month;
230}
231
233 return m_Day;
234}
235
237 return m_DayOfWeek;
238}
239
241 return m_Hour;
242}
243
245 return m_Minute;
246}
247
249 return m_Second;
250}
251
252bool VirtTimer::registerHandler(TimerHandler* handler) {
253 return m_Handlers.registerHandler(handler);
254}
255
256bool VirtTimer::unregisterHandler(TimerHandler* handler) {
257 return m_Handlers.unregisterHandler(handler);
258}
259
260void VirtTimer::addAlarm(Event* event, size_t seconds, size_t microseconds) {
261 LockGuard<Spinlock> guard(m_AlarmLock);
262 uint64_t deadline = getTickCountNano();
263 deadline = addDuration(deadline, seconds, Time::Multiplier::Second);
264 deadline = addDuration(deadline, microseconds, Time::Multiplier::Microsecond);
265 m_Alarms.pushBack(new Alarm{event, Processor::information().getCurrentThread(), deadline});
266}
267
269 removeAlarm(event, true);
270}
271
272size_t VirtTimer::removeAlarm(Event* event, bool returnZero) {
273 LockGuard<Spinlock> guard(m_AlarmLock);
274 for (List<Alarm*>::Iterator it = m_Alarms.begin(); it != m_Alarms.end(); ++it) {
275 Alarm* alarm = *it;
276 if (alarm->event != event) {
277 continue;
278 }
279 size_t remaining = 0;
280 if (!returnZero && alarm->deadline > getTickCountNano()) {
281 const uint64_t delta = alarm->deadline - getTickCountNano();
282 remaining = delta / Time::Multiplier::Second + (delta % Time::Multiplier::Second != 0);
283 }
284 m_Alarms.erase(it);
285 delete alarm;
286 return remaining;
287 }
288 return 0;
289}
290
291void VirtTimer::processElapsed(uint64_t nanoseconds) {
292 m_ElapsedSinceSync += nanoseconds;
293 if (m_ElapsedSinceSync >= NanosecondsPerSecond) {
294 synchronise();
295 m_ElapsedSinceSync %= NanosecondsPerSecond;
296 }
297
298 m_AlarmLock.acquire();
299 const uint64_t now = getTickCountNano();
300 while (true) {
301 bool dispatched = false;
302 for (List<Alarm*>::Iterator it = m_Alarms.begin(); it != m_Alarms.end(); ++it) {
303 Alarm* alarm = *it;
304 if (alarm->deadline <= now) {
305 alarm->thread->sendEvent(alarm->event);
306 m_Alarms.erase(it);
307 delete alarm;
308 dispatched = true;
309 break;
310 }
311 }
312 if (!dispatched) {
313 break;
314 }
315 }
316 m_AlarmLock.release();
317
318 Machine::instance().getIrqManager()->tick();
319 m_Handlers.dispatch(nanoseconds);
320}
Definition Event.h:49
virtual void tick()
Definition IrqManager.cc:53
::Iterator< T, node_t > Iterator
Definition List.h:67
static ProcessorInformation & information()
void release()
Definition Spinlock.cc:168
bool acquire(bool recurse=false, bool safe=true)
Definition Spinlock.cc:36
irq_id_t registerIsaSplitIrq(IrqManager &manager, uint8_t irq, const IrqPolicy &policy)
bool dispatch(uint64_t delta, TimerHandler *onlyHandler=nullptr)
bool unregisterHandler(TimerHandler *handler)
void removeAlarm(Event *event) override
size_t getYear() override
void threadedIrq(size_t work) override
uint8_t getDayOfWeek() override
void addAlarm(Event *event, size_t seconds, size_t microseconds=0) override
HardStageDisposition hardIrq(irq_id_t number, InterruptState &state, size_t &work) override
Time::Timestamp getUnixTimestamp() override
void synchronise(bool tohw=false) override
Synchronises the timer with the hardware. Useful for updating the fields returned by get*,...
uint64_t getTickCountNano() override
uint64_t getTickCount() override
bool quiesceIrqSources() override
uint8_t getHour() override
uint8_t getMinute() override
uint8_t getSecond() override
void rearmIrqSources(size_t work) override
uint64_t getNanosecond() override
uint8_t getDayOfMonth() override
uint64_t getTickCountNanoFast() override
uint8_t getMonth() override