24#include "pedigree/kernel/BootstrapInfo.h"
25#include "pedigree/kernel/LockGuard.h"
26#include "pedigree/kernel/Log.h"
27#include "pedigree/kernel/TargetInfo.h"
28#include "pedigree/kernel/compiler.h"
29#include "pedigree/kernel/core/SlamAllocator.h"
30#include "pedigree/kernel/machine/IrqManager.h"
31#include "pedigree/kernel/machine/Machine.h"
32#include "pedigree/kernel/machine/SchedulerTimer.h"
33#include "pedigree/kernel/machine/Serial.h"
34#include "pedigree/kernel/machine/TimerHandler.h"
35#include "pedigree/kernel/panic.h"
36#include "pedigree/kernel/process/PerProcessorScheduler.h"
37#include "pedigree/kernel/process/Process.h"
38#include "pedigree/kernel/process/Scheduler.h"
39#include "pedigree/kernel/process/TerminationDeferral.h"
40#include "pedigree/kernel/process/Thread.h"
41#include "pedigree/kernel/processor/Processor.h"
42#include "pedigree/kernel/processor/ProcessorInformation.h"
43#include "pedigree/kernel/time/Time.h"
44#include "pedigree/kernel/utilities/Iterator.h"
45#include "pedigree/kernel/utilities/StaticString.h"
46#include "pedigree/kernel/utilities/String.h"
47#include "pedigree/kernel/utilities/StringView.h"
48#include "pedigree/kernel/utilities/Vector.h"
49#include "pedigree/kernel/utilities/assert.h"
50#include "pedigree/kernel/utilities/utility.h"
52#include "RtcCalendar.h"
53#include "RtcTimeAccounting.h"
62#define INITIAL_RTC_HZ 64
64#define INITIAL_RTC_HZ 512
68constexpr uint8_t RtcPeriodicInterruptEnable = 1U << 6;
69constexpr uint8_t RtcAlarmInterruptEnable = 1U << 5;
70constexpr uint8_t RtcUpdateEndedInterruptEnable = 1U << 4;
71constexpr uint8_t RtcInterruptEnableMask =
72 RtcPeriodicInterruptEnable | RtcAlarmInterruptEnable | RtcUpdateEndedInterruptEnable;
73constexpr uint8_t RtcUpdateInhibit = 1U << 7;
74constexpr uint8_t RtcInterruptRequested = 1U << 7;
75constexpr uint8_t RtcPeriodicFlag = 1U << 6;
79constexpr Time::Timestamp RtcUpdateTimeout = 25 * Time::Multiplier::Millisecond;
80constexpr size_t RtcUpdateMaximumPolls = 1000000;
81constexpr size_t RtcCalibrationMaximumPolls = 100000000;
86 asm volatile(
"lfence\nrdtsc" :
"=d"(edx),
"=a"(eax) : :
"memory");
87 return (
static_cast<uint64_t
>(edx) << 32U) | eax;
91bool calibrateTscFromPmTimer(uint64_t& tsc0, uint64_t& tsc1, uint64_t& elapsedNanoseconds) {
94 if (!Acpi::instance().getPmTimerPort(port, mask)) {
97 IoPort timer(
"ACPI PM timer calibration");
98 if (!timer.allocate(port, 4)) {
99 WARNING(
"RTC: ACPI PM timer port unavailable; using RTC calibration");
103 constexpr uint64_t Frequency = 3579545;
104 constexpr uint64_t SampleTicks = Frequency / 10;
106 for (
size_t attempt = 0; attempt < 3; ++attempt) {
107 const uint64_t startBefore = readOrderedTsc();
108 uint32_t previous = timer.read32() & mask;
109 const uint64_t startAfter = readOrderedTsc();
110 uint64_t elapsedTicks = 0;
111 while (elapsedTicks < SampleTicks) {
112 const uint32_t current = timer.read32() & mask;
114 elapsedTicks += (current - previous) & mask;
116 if (++polls >= RtcCalibrationMaximumPolls) {
117 WARNING(
"RTC: ACPI PM timer calibration timed out; using RTC calibration");
122 const uint64_t endBefore = readOrderedTsc();
123 const uint32_t current = timer.read32() & mask;
124 const uint64_t endAfter = readOrderedTsc();
125 elapsedTicks += (current - previous) & mask;
127 if (startAfter < startBefore || endBefore <= startAfter || endAfter < endBefore) {
130 const uint64_t startSpan = startAfter - startBefore;
131 const uint64_t endSpan = endAfter - endBefore;
132 tsc0 = startBefore + startSpan / 2;
133 tsc1 = endBefore + endSpan / 2;
135 if (startSpan + endSpan > (tsc1 - tsc0) / 1000) {
138 elapsedNanoseconds = elapsedTicks * Time::Multiplier::Second / Frequency;
141 WARNING(
"RTC: ACPI PM timer calibration endpoints were delayed; using RTC calibration");
148 {4, 0x0e, {250000000ULL, 250000000ULL}}, {8, 0x0d, {125000000ULL, 125000000ULL}},
149 {16, 0x0c, {62500000ULL, 62500000ULL}}, {32, 0x0b, {31250000ULL, 31250000ULL}},
150 {64, 0x0a, {15625000ULL, 15625000ULL}}, {128, 0x09, {7812500ULL, 7812500ULL}},
151 {256, 0x08, {3906250ULL, 3906250ULL}}, {512, 0x07, {1953125ULL, 1953125ULL}},
152 {1024, 0x06, {976562ULL, 976563ULL}}, {2048, 0x05, {488281ULL, 488281ULL}},
153 {4096, 0x04, {244140ULL, 244141ULL}}, {8192, 0x03, {122070ULL, 122070ULL}},
156static uint64_t addAlarmDuration(uint64_t deadline,
size_t count, uint64_t multiplier) {
157 if (count > ((Time::Infinity - deadline) / multiplier)) {
158 return Time::Infinity;
160 return deadline + (count * multiplier);
165bool Rtc::CmosTransactionGuard::waitableContext() {
170Rtc::CmosTransactionGuard::CmosTransactionGuard(
Mutex& lock)
171 : m_Waitable(waitableContext()),
172 m_TerminationDeferral(m_Waitable),
176 panic(
"RTC CMOS transaction attempted from a hard IRQ");
179 m_Owned = m_Waitable ? m_Lock.acquireForCompletion() : m_Lock.tryAcquire();
181 panic(
"RTC CMOS transaction contended in an atomic context");
185Rtc::CmosTransactionGuard::~CmosTransactionGuard() {
191static void* currentAlarmDispatchOwner() {
196 Alarm* pAlarm =
nullptr;
209 uint64_t target = addAlarmDuration(now, alarmSecs, Time::Multiplier::Second);
210 target = addAlarmDuration(target, alarmUsecs, Time::Multiplier::Microsecond);
213 if (m_DeadlineMode) {
214 publishNextDeadlineLocked();
222 uintptr_t previousDebugAddress = 0;
224 bool debuggingDrain =
false;
228 const bool dispatching =
m_AlarmQueue.hasRemoteInFlight(pEvent, owner);
236 if (!debuggingDrain) {
237 previousDebugState = current->
getDebugState(previousDebugAddress);
238 current->
setDebugState(Thread::CallbackDrain,
reinterpret_cast<uintptr_t
>(pEvent));
239 debuggingDrain =
true;
247 if (debuggingDrain) {
248 current->
setDebugState(previousDebugState, previousDebugAddress);
253 void* owner = currentAlarmDispatchOwner();
254 bool remoteInFlight =
false;
255 bool selfDeferred =
false;
258 Alarm* reclaim =
m_AlarmQueue.removeAllQueued(pEvent, owner, remoteInFlight, selfDeferred);
260 if (m_DeadlineMode) {
261 publishNextDeadlineLocked();
266 if (remoteInFlight) {
272 void* owner = currentAlarmDispatchOwner();
277 if (removal.record) {
280 if (m_DeadlineMode) {
281 publishNextDeadlineLocked();
285 if (removal.disposition == RtcAlarmQueue::RemovalDisposition::RemoteInFlight) {
289 if (removal.disposition != RtcAlarmQueue::RemovalDisposition::Removed || bRetZero ||
290 removal.deadline < currTime) {
294 const uint64_t diff = removal.deadline - currTime;
295 size_t ret = diff / Time::Multiplier::Second;
296 if (diff % Time::Multiplier::Second) {
308 if (removed && m_DeadlineMode) {
310 publishNextDeadlineLocked();
316 if (!m_DeadlineMode) {
323 publishNextDeadlineLocked();
327void Rtc::publishNextDeadlineLocked() {
328 assert(m_DeadlineScheduler);
331 if (handlerDeadline < next) {
332 next = handlerDeadline;
338 if (!m_DeadlineMode) {
341 m_DeadlinePending = 1;
345int Rtc::deadlineWorkerEntry(
void* context) {
346 return reinterpret_cast<Rtc*
>(context)->runDeadlineWorker();
349int Rtc::runDeadlineWorker() {
351 while (!m_StopDeadlineWorker.value()) {
352 if (m_DeadlinePending.compareAndSwap(1, 0)) {
358 publishNextDeadlineLocked();
363 auto guard = m_DeadlineWaiters.acquire();
364 if (!m_DeadlinePending.value() && !m_StopDeadlineWorker.value()) {
365 const WaitQueue::WakeReason reason =
382 static size_t monthnumbers[] = {0, 3, 3, 6, 1, 4, 6, 2, 5, 0, 3, 5};
386 dayOfWeek += monthnumbers[
m_Month - 1];
387 dayOfWeek += ((
m_Year % 100) + ((
m_Year % 100) / 4)) % 7;
388 dayOfWeek -= ((
m_Year / 100) % 4 - 3) * 2;
410 return m_DeadlineMode ? Time::getTimeNanoseconds() / Time::Multiplier::Second
422 uint64_t anchorTsc =
m_Tsc0;
423 uint64_t anchorNanoseconds = 0;
425 const uint64_t tsc = readOrderedTsc();
428 uint64_t candidate = PcTscClock::fromAnchor(tsc, anchorTsc, anchorNanoseconds,
m_TscCalibration);
430 if (candidate < coarseFloor) {
431 candidate = coarseFloor;
446 uint64_t anchorTsc =
m_Tsc0;
447 uint64_t anchorNanoseconds = 0;
449 processor.getTscClockAnchor(anchorTsc, anchorNanoseconds);
450 const uint64_t timestamp =
451 PcTscClock::fromAnchor(readOrderedTsc(), anchorTsc, anchorNanoseconds,
m_TscCalibration);
456 NOTICE(
"Rtc::initialise1");
468 if (!RtcCalendar::validCenturyIndex(centuryIndex)) {
469 ERROR(
"RTC: unsupported FADT century index " <<
Hex << centuryIndex);
472 m_CenturyIndex = centuryIndex;
474 NOTICE(
"RTC: FADT century index " <<
Hex << centuryIndex);
476 NOTICE(
"RTC: no century register; interpreting years as 1970-2069");
480 if (!read(0x0B, statusB) ||
481 !
write(0x0B, statusB & ~(RtcUpdateInhibit | RtcInterruptEnableMask | 1U))) {
486 uint8_t rateBits = 0x06;
487 for (
size_t i = 0; i < 12; i++)
498 uint8_t verifiedB = 0;
499 if (!
write(0x0A, 0x20 | rateBits) || !read(0x0A, statusA) || !read(0x0B, verifiedB) ||
500 (statusA & 0x7f) != (0x20 | rateBits) ||
501 verifiedB != (statusB & ~(RtcUpdateInhibit | RtcInterruptEnableMask | 1U))) {
502 ERROR(
"RTC: control register configuration did not stick");
506 ERROR(
"RTC: initial hardware clock is invalid or unresponsive");
513 NOTICE(
"Rtc::initialise2");
517 uint64_t elapsedNanoseconds = 0;
518 bool usedPmTimer =
false;
520 usedPmTimer = calibrateTscFromPmTimer(tsc0, tsc1, elapsedNanoseconds);
525 constexpr size_t CalibrationPeriods = 50;
527 size_t calibrationPolls = 0;
528 while (!(read(0x0C) & RtcPeriodicFlag)) {
529 if (++calibrationPolls >= RtcCalibrationMaximumPolls) {
531 ERROR(
"RTC: timed out waiting for TSC calibration to start");
537 tsc0 = readOrderedTsc();
540 while (periods < CalibrationPeriods) {
541 if (read(0x0C) & RtcPeriodicFlag) {
544 if (++calibrationPolls >= RtcCalibrationMaximumPolls) {
546 ERROR(
"RTC: timed out collecting periodic TSC calibration samples");
552 tsc1 = readOrderedTsc();
555 for (
size_t i = 0; i < CalibrationPeriods; ++i) {
559 if (tsc1 <= tsc0 || !elapsedNanoseconds) {
560 ERROR(
"RTC: invalid TSC calibration interval");
563 const uint64_t elapsedCycles = tsc1 - tsc0;
565 NOTICE(
"TSC calibration: " << elapsedCycles <<
" cycles / " << elapsedNanoseconds <<
" ns ("
566 << (usedPmTimer ?
"ACPI PM timer" :
"RTC") <<
")");
573#if PEDIGREE_LATENCY_ACCOUNTING
576 LatencyAccounting::armCpu();
587 const uint64_t tsc = readOrderedTsc();
590 if (nanoseconds < coarseFloor) {
591 nanoseconds = coarseFloor;
593 processor.initialiseTscClockAnchor(tsc, nanoseconds);
594 LatencyAccounting::armCpu();
600 bool rateSpecified =
false;
601 const char* commandLine = g_pBootstrapInfo->getCommandLine();
604 for (
const auto& argument : arguments) {
606 constexpr size_t PrefixLength = 9;
607 if (!(view ==
"--rtc-hz") &&
608 (view.length() < PrefixLength || !(view.
substring(0, PrefixLength) ==
"--rtc-hz=")))
612 bool valid = !rateSpecified && view.length() > PrefixLength;
613 for (
size_t i = PrefixLength; valid && i < view.length(); ++i) {
614 const char digit = view[i];
615 valid = digit >=
'0' && digit <=
'9' && hz <= 8192;
617 hz = hz * 10 +
static_cast<size_t>(digit -
'0');
622 if (!valid || index == 12) {
623 ERROR(
"RTC: invalid or repeated --rtc-hz option: " << argument);
626 selectedIndex = index;
627 rateSpecified =
true;
634 uint8_t verifiedA = 0;
635 if (!
write(0x0A, statusA) || !read(0x0A, verifiedA) || (verifiedA & 0x7f) != statusA) {
636 ERROR(
"RTC: runtime interrupt frequency did not stick");
642 m_DeadlineScheduler = Scheduler::schedulerForCpu(0);
643 if (!m_DeadlineScheduler) {
649 if (!Time::anchorRealtime(Time::getTimeNanoseconds())) {
654 nullptr,
false,
true,
true);
655 worker->setName(
"clock deadline worker");
656 m_DeadlineWorker.adopt(
worker);
657 m_DeadlineMode =
true;
659 FATAL(
"RTC deadline worker could not be started.");
663 publishNextDeadlineLocked();
665 NOTICE(
"RTC: runtime IRQ8 disabled; CPU 0 LAPIC handles timer deadlines");
669 IrqManager& irqManager = *Machine::instance().getIrqManager();
678 NOTICE(
"RTC: periodic interrupt frequency " <<
Dec <<
periodicIrqInfo[selectedIndex].Hz <<
" Hz");
688 ERROR(
"RTC: hardware clock write failed (invalid date or timeout)");
690 ERROR(
"RTC: hardware clock read failed (invalid date or timeout)");
695 if (m_DeadlineMode) {
696 m_StopDeadlineWorker = 1;
699 m_DeadlineWorker.
join();
705 if (
m_IrqId && !Machine::instance().getIrqManager()->unregisterHandler(
m_IrqId,
this)) {
706 panic(
"RTC teardown could not drain its threaded IRQ callback");
714 Alarm* reclaim =
nullptr;
715 Alarm* freeAlarms =
nullptr;
722 Alarm* next = reclaim->next();
727 Alarm* next = freeAlarms->next();
751 m_DeadlineScheduler(nullptr),
755 m_DeadlinePending(0),
756 m_StopDeadlineWorker(0),
757 m_DeadlineMode(false),
766extern size_t g_AllocedPages;
774 status = readLocked(0x0C);
777 if (!(status & RtcInterruptRequested)) {
778 return IrqDisposition::Handled;
780 if (!(status & RtcPeriodicFlag)) {
781 return IrqDisposition::Handled;
785 return IrqDisposition::Handled;
789 uint64_t elapsedSeconds = 0;
790 const uint64_t delta = advanceCivilClock(observed, &elapsedSeconds);
808 Thread* target =
reinterpret_cast<Thread*
>(claimed->target());
809 Event*
event =
reinterpret_cast<Event*
>(claimed->event());
817 if (
UNLIKELY(delta >= Time::Multiplier::Millisecond ||
821 Machine::instance().getIrqManager()->
tick();
825#if MEMORY_LOGGING_ENABLED
828 memoryLogStr +=
"Heap: ";
830 memoryLogStr +=
"K\tPages: ";
832 memoryLogStr +=
"K\t Free: ";
834 memoryLogStr +=
"K\n";
836 pSerial->write_str(memoryLogStr);
844 Process* pProcess = processLease.get();
852 processListStr.append(
"\tProcess #");
853 processListStr.append(pProcess->
getId(), 10);
854 processListStr.append(
" '");
856 processListStr.append(
"' V=");
857 processListStr.append(virtK, 10);
858 processListStr.append(
"K P=");
859 processListStr.append(physK, 10);
860 processListStr.append(
"K S=");
861 processListStr.append(shrK, 10);
862 processListStr.append(
"K Heap=");
863 processListStr.append(heapK, 10);
864 processListStr.append(
"K\n");
865 pSerial->write_str(processListStr);
871 if (!m_DeadlineMode) {
876uint64_t Rtc::advanceCivilClock(uint64_t observed, uint64_t* elapsedSeconds) {
884 const uint64_t seconds = RtcTimeAccounting::advanceCivilTime(civilTime, delta);
892 if (elapsedSeconds) {
893 *elapsedSeconds = seconds;
904 if (index > 9 && index != m_CenturyIndex) {
908 const Time::Timestamp started = Time::getTicks();
911 bool updating =
false;
920 if (++polls >= RtcUpdateMaximumPolls || (Time::getTicks() - started) >= RtcUpdateTimeout) {
922 "RTC: timed out waiting for update completion before CMOS "
934uint8_t Rtc::read(uint8_t index) {
935 uint8_t value = 0xFF;
936 (void)read(index, value);
940bool Rtc::read(uint8_t index, uint8_t& value) {
945 value = readLocked(index);
949uint8_t Rtc::readLocked(uint8_t index) {
960 writeLocked(index, value);
964void Rtc::writeCmos(uint8_t index, uint8_t value) {
968bool Rtc::inhibitClockUpdatesLocked(uint8_t& status) {
973 if (readLocked(0x0A) & 0x80) {
977 status = readLocked(0x0B);
978 writeLocked(0x0B, status | RtcUpdateInhibit);
979 const bool inhibited = (readLocked(0x0B) & RtcUpdateInhibit) != 0;
990 if (!inhibitClockUpdatesLocked(status)) {
995 RtcCalendar::read([
this](uint8_t index) {
return readLocked(index); }, m_CenturyIndex, time);
996 writeLocked(0x0B, status);
1009bool Rtc::writeHardwareClock() {
1012 if (!RtcCalendar::valid(time) || (!m_CenturyIndex && time.year > 2069) ||
1018 if (!inhibitClockUpdatesLocked(status)) {
1021 const bool success = RtcCalendar::write(
1022 [
this](uint8_t index) {
return readLocked(index); },
1023 [
this](uint8_t index, uint8_t value) { writeLocked(index, value); }, m_CenturyIndex, time);
1024 writeLocked(0x0B, status);
1028void Rtc::writeLocked(uint8_t index, uint8_t value) {
1035 const uint8_t status = readLocked(0x0B);
1036 const uint8_t quiescentStatus =
static_cast<uint8_t
>(status & ~RtcInterruptEnableMask);
1037 writeLocked(0x0B, enabled ?
static_cast<uint8_t
>(quiescentStatus | RtcPeriodicInterruptEnable)
1041 (void)readLocked(0x0C);
bool allocate(io_port_t ioPort, size_t size)
virtual void write8(uint8_t value, size_t offset=0)
virtual uint8_t read8(size_t offset=0)
virtual irq_id_t registerIsaIrqHandler(uint8_t irq, IrqHandler *handler, const IrqPolicy &policy)=0
virtual SchedulerTimer * getSchedulerTimer()=0
virtual Serial * getSerial(size_t n)=0
void setClockDeadline(uint64_t deadline)
void ringIrqWorkDoorbell()
void registerWorkerWake(SchedulerWorkerWake &worker, WaitQueue &waiters)
void unregisterWorkerWake(SchedulerWorkerWake &worker)
ssize_t getHeapUsage() const
LargeStaticString & description()
static bool getInterrupts()
static ProcessorInformation & information()
static bool inDeviceHardIrq()
static void setInterrupts(bool bEnable)
static void * dispatchOwnerForContext(Context &context)
virtual uint64_t getTickCount()
void setIndexLocked(uint8_t index)
RtcAlarmQueue m_AlarmQueue
static periodicIrqInfo_t periodicIrqInfo[12]
static EXPORTED_PUBLIC uint8_t readCmos(uint8_t index)
virtual uint8_t getHour()
size_t m_PeriodicIrqInfoIndex
virtual uint8_t getDayOfMonth()
void processElapsedTime(uint64_t observed)
virtual uint8_t getMinute()
virtual void addAlarm(class Event *pEvent, size_t alarmSecs, size_t alarmUsecs=0)
Time::CpuTimeSample sampleCpuTime() override
virtual void removeAlarm(class Event *pEvent)
uint64_t m_ProcessedTickCount
virtual uint8_t getMonth()
Time::Timestamp getUnixTimestamp() override
virtual void synchronise(bool tohw=false)
bool armHandler(TimerHandler *handler, uint64_t absoluteDeadlineNs) override
virtual uint64_t getNanosecond()
virtual uint64_t getTickCountNano()
virtual uint8_t getSecond()
bool initialise1(uint8_t centuryIndex) INITIALISATION_ONLY
virtual uint8_t getDayOfWeek()
bool waitForUpdateCompletion(uint8_t index)
IrqDisposition irq(irq_id_t number) override
void setPeriodicInterruptEnabled(bool enabled)
Rtc() INITIALISATION_ONLY
bool initialise2() INITIALISATION_ONLY
PcTscClock::Calibration m_TscCalibration
PcTscClock::MonotonicPublication m_MonotonicTicks
void deadlineInterrupt() override
Atomic< uint64_t > m_TickCount
void drainRemoteAlarmDispatch(class Event *pEvent, void *owner)
virtual uint64_t getTickCountNanoFast()
TimerHandlerRegistry m_HandlerRegistry
bool write(uint8_t index, uint8_t value)
void initialiseProcessorClock() INITIALISATION_ONLY
virtual bool supportsOneShot() const
static Scheduler & instance()
MUST_USE_RESULT bool acquireProcess(ProcessLease &lease, size_t n)
bool acquire(bool recurse=false, bool safe=true)
StringView substring(size_t start, size_t end, bool hashed=HASH_STRINGVIEWS_BY_DEFAULT) const
static constexpr size_t getPageSize() noexcept
void setDebugState(DebugState state, uintptr_t address)
DebugState getDebugState(uintptr_t &address)
bool sendEvent(Event *pEvent)
bool armHandler(TimerHandler *handler, uint64_t deadline, uint64_t now)
uint64_t nextDeadline() const
bool dispatchDue(uint64_t now)
bool dispatch(uint64_t delta, TimerHandler *onlyHandler=nullptr)
bool unregisterHandler(TimerHandler *handler)
virtual Time::Timestamp getUnixTimestamp()
A vector / dynamic array.
MUST_USE_RESULT WakeReason wait(const Channel &channel=Channel(), size_t debugState=0, uintptr_t debugAddress=0, StackDiscardCleanup onStackDiscard=nullptr, void *stackDiscardContext=nullptr)
void EXPORTED_PUBLIC panic(const char *msg) NORETURN