The Pedigree Project 0.1
mach_virt/SchedulerTimer.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 "SchedulerTimer.h"
9#include "pedigree/kernel/machine/IrqManager.h"
10#include "pedigree/kernel/machine/Machine.h"
11#include "pedigree/kernel/machine/SchedulerTimerDispatchCleanup.h"
12#include "pedigree/kernel/machine/SchedulerTimerHandler.h"
13#include "pedigree/kernel/processor/Processor.h"
14
15#include "DeviceTree.h"
16#include "GenericTimer.h"
17
18namespace {
19constexpr uint64_t NanosecondsPerSecond = 1000000000ULL;
20constexpr uint32_t TicksPerSecond = 100;
21} // namespace
22
23VirtSchedulerTimer VirtSchedulerTimer::m_Instance;
24
25VirtSchedulerTimer& VirtSchedulerTimer::instance() {
26 return m_Instance;
27}
28
29VirtSchedulerTimer::VirtSchedulerTimer()
30 : m_Handler(),
31 m_Frequency(0),
32 m_LastCount(0),
33 m_IntervalTicks(0),
34 m_IrqId(0),
35 m_Initialised(false) {}
36
37VirtSchedulerTimer::~VirtSchedulerTimer() {
38 uninitialise();
39}
40
41uint64_t VirtSchedulerTimer::counter() const {
42 return VirtGenericTimer::count();
43}
44
45uint64_t VirtSchedulerTimer::ticksToNanoseconds(uint64_t ticks) const {
46 return (ticks / m_Frequency) * NanosecondsPerSecond +
47 (ticks % m_Frequency) * NanosecondsPerSecond / m_Frequency;
48}
49
50bool VirtSchedulerTimer::initialise() {
51 if (m_Initialised) {
52 return false;
53 }
54 m_Frequency = VirtGenericTimer::frequency();
55 if (!m_Frequency) {
56 return false;
57 }
58 m_IntervalTicks = static_cast<uint32_t>(m_Frequency / TicksPerSecond);
59 if (!m_IntervalTicks) {
60 m_IntervalTicks = 1;
61 }
62 VirtGenericTimer::virtualControl(0);
63
64 IrqManager& manager = *Machine::instance().getIrqManager();
65 m_IrqId = manager.registerSchedulerIrqHandler(VirtDeviceTree::virtualTimerIrq(), this,
66 IrqPolicy::levelHard());
67 if (!m_IrqId) {
68 return false;
69 }
70
71 m_LastCount = counter();
72 VirtGenericTimer::setVirtualTimer(m_IntervalTicks);
73 m_Initialised = true;
74 return true;
75}
76
77void VirtSchedulerTimer::uninitialise() {
78 if (!m_Initialised) {
79 return;
80 }
81 VirtGenericTimer::virtualControl(0);
82 IrqManager& manager = *Machine::instance().getIrqManager();
83 if (!manager.unregisterSchedulerIrqHandler(m_IrqId, this)) {
84 return;
85 }
86 m_IrqId = 0;
87 m_Initialised = false;
88}
89
91 return m_Handler.publish(Processor::id(), handler);
92}
93
97
98uint64_t VirtSchedulerTimer::nominalQuantumNs() const {
99 return NanosecondsPerSecond / TicksPerSecond;
100}
101
102void VirtSchedulerTimer::schedulerIrq(irq_id_t number, InterruptState& state) {
103 if (number != m_IrqId) {
104 return;
105 }
106
107 // Re-arm before the callback: a context switch may never return here.
108 VirtGenericTimer::setVirtualTimer(m_IntervalTicks);
109 const uint64_t now = counter();
110 const uint64_t elapsed = ticksToNanoseconds(now - m_LastCount);
111 m_LastCount = now;
112
114 if (m_Handler.beginDispatch(Processor::id(), dispatch)) {
115 SchedulerTimerDispatchCleanup cleanup(dispatch);
116 ExecutionContextGuard context(ExecutionContext::SchedulerIrq);
117 dispatch.handler()->timer(elapsed, state);
118 }
119}
virtual irq_id_t registerSchedulerIrqHandler(uint8_t irq, SchedulerIrqHandler *handler, const IrqPolicy &policy)
Definition IrqManager.cc:41
virtual bool unregisterSchedulerIrqHandler(irq_id_t Id, SchedulerIrqHandler *handler)
Definition IrqManager.cc:45
static ProcessorId id()
bool beginDispatch(size_t owner, DispatchGuard &guard)
bool publish(size_t owner, SchedulerTimerHandler *handler)
bool unpublish(size_t owner, SchedulerTimerHandler *handler)
virtual void timer(uint64_t delta, InterruptState &state)=0
static bool canRemoveHandlerInCurrentContext()
bool registerHandler(SchedulerTimerHandler *handler) override
bool removeHandler(SchedulerTimerHandler *handler) override