diff --git a/Kernel/arch/amd64/cpu/apic.cpp b/Kernel/arch/amd64/cpu/apic.cpp
index c0588bc9..0b4169d6 100644
--- a/Kernel/arch/amd64/cpu/apic.cpp
+++ b/Kernel/arch/amd64/cpu/apic.cpp
@@ -442,7 +442,7 @@ namespace APIC
this->lapic->Write(APIC_TICR, 0xFFFFFFFF);
}
- TimeManager->Sleep(1, Time::Units::Milliseconds);
+ TimeManager->Sleep(Time::FromMilliseconds(1));
// Mask the timer
if (this->lapic->x2APIC)
diff --git a/Kernel/arch/amd64/cpu/smp.cpp b/Kernel/arch/amd64/cpu/smp.cpp
index 428d66c2..6432f520 100644
--- a/Kernel/arch/amd64/cpu/smp.cpp
+++ b/Kernel/arch/amd64/cpu/smp.cpp
@@ -167,14 +167,14 @@ namespace SMP
}
apic->SendInitIPI(lapic->APICId);
- TimeManager->Sleep(20, Time::Units::Milliseconds);
+ TimeManager->Sleep(Time::FromMilliseconds(20));
apic->SendStartupIPI(lapic->APICId, TRAMPOLINE_START);
debug("Waiting for CPU %d to load...", lapic->APICId);
- uint64_t Timeout = TimeManager->CalculateTarget(2, Time::Units::Seconds);
+ uint64_t Timeout = TimeManager->GetTimeNs() + Time::FromSeconds(2);
while (CPUEnabled.load(std::memory_order_acquire) == false)
{
- if (TimeManager->GetCounter() > Timeout)
+ if (TimeManager->GetTimeNs() > Timeout)
{
error("CPU %d failed to load!", lapic->APICId);
KPrint("\x1b[1;37;41mCPU %d failed to load!",
diff --git a/Kernel/arch/i386/cpu/apic.cpp b/Kernel/arch/i386/cpu/apic.cpp
index a0dd750b..e450082b 100644
--- a/Kernel/arch/i386/cpu/apic.cpp
+++ b/Kernel/arch/i386/cpu/apic.cpp
@@ -368,7 +368,7 @@ namespace APIC
this->lapic->Write(APIC_TDCR, DivideBy128);
else
this->lapic->Write(APIC_TDCR, DivideBy16);
- this->lapic->Write(APIC_TICR, s_cst(uint32_t, Ticks * Miliseconds));
+ this->lapic->Write(APIC_TICR, s_cst(uint32_t, Ticks *Miliseconds));
this->lapic->Write(APIC_TIMER, s_cst(uint32_t, timer.raw));
}
@@ -383,7 +383,7 @@ namespace APIC
this->lapic->Write(APIC_TDCR, Divider);
this->lapic->Write(APIC_TICR, 0xFFFFFFFF);
- TimeManager->Sleep(1, Time::Units::Milliseconds);
+ TimeManager->Sleep(Time::FromMilliseconds(1));
// Mask the timer
this->lapic->Write(APIC_TIMER, 0x10000 /* LVTTimer.Mask flag */);
diff --git a/Kernel/core/console.cpp b/Kernel/core/console.cpp
index f36bba05..f3181d02 100644
--- a/Kernel/core/console.cpp
+++ b/Kernel/core/console.cpp
@@ -25,6 +25,8 @@
#include "../kernel.h"
+using namespace std::chrono_literals;
+
namespace KernelConsole
{
static int TermColors[] = {
diff --git a/Kernel/core/driver/api.cpp b/Kernel/core/driver/api.cpp
index a44e8741..a4bd9a97 100644
--- a/Kernel/core/driver/api.cpp
+++ b/Kernel/core/driver/api.cpp
@@ -273,7 +273,7 @@ namespace v0
{
dbg_api("%d, %d", DriverID, Milliseconds);
- TaskManager->Sleep(Milliseconds);
+ TaskManager->Sleep(Time::FromMilliseconds(Milliseconds));
}
/* --------- */
diff --git a/Kernel/core/lock.cpp b/Kernel/core/lock.cpp
index 647c1e20..1f7baf79 100644
--- a/Kernel/core/lock.cpp
+++ b/Kernel/core/lock.cpp
@@ -195,7 +195,7 @@ void LockClass::TimeoutDeadLock(SpinLockData &Lock, uint64_t Timeout)
if (CoreData != nullptr)
CCore = CoreData->ID;
- uint64_t Counter = TimeManager->GetCounter();
+ uint64_t Counter = TimeManager->GetTimeNs();
warn("Potential deadlock in lock '%s' held by '%s'! %ld %s in queue. Interrupts are %s. Core %ld held by %ld. Timeout in %ld (%ld ticks remaining).",
Lock.AttemptingToGet, Lock.CurrentHolder, Lock.Count, Lock.Count > 1 ? "locks" : "lock",
@@ -235,8 +235,7 @@ Retry:
if (i >= DEADLOCK_TIMEOUT)
{
if (Target.load() == 0)
- Target.store(TimeManager->CalculateTarget(Timeout,
- Time::Units::Milliseconds));
+ Target.store(TimeManager->GetTimeNs() + Timeout);
TimeoutDeadLock(LockData, Target.load());
goto Retry;
}
diff --git a/Kernel/core/panic/kbd/xhci.cpp b/Kernel/core/panic/kbd/xhci.cpp
index 7e8177cd..f2e8a62d 100644
--- a/Kernel/core/panic/kbd/xhci.cpp
+++ b/Kernel/core/panic/kbd/xhci.cpp
@@ -58,7 +58,7 @@ nsa bool CrashXHCIKeyboardDriver::TakeOwnership()
return true;
exCap->USBLEGSUP.OSOwnsHC = 1;
- TimeManager->Sleep(200, Time::Milliseconds);
+ TimeManager->Sleep(Time::FromMilliseconds(200));
if (exCap->USBLEGSUP.BIOSOwnsHC == 0)
return true;
diff --git a/Kernel/core/syscalls.cpp b/Kernel/core/syscalls.cpp
index 28496fe9..5c0ac92b 100644
--- a/Kernel/core/syscalls.cpp
+++ b/Kernel/core/syscalls.cpp
@@ -59,7 +59,7 @@ extern "C" uintptr_t SystemCallsHandler(SyscallsFrame *Frame)
and switch back when this function returns. */
AutoSwitchPageTable PageSwitcher;
- uint64_t _ctime = TimeManager->GetCounter();
+ uint64_t _ctime = TimeManager->GetTimeNs();
Tasking::TaskInfo *Ptinfo = &thisProcess->Info;
Tasking::TaskInfo *Ttinfo = &thisThread->Info;
uintptr_t ret;
@@ -97,7 +97,7 @@ extern "C" uintptr_t SystemCallsHandler(SyscallsFrame *Frame)
}
Ret:
- Ptinfo->KernelTime += TimeManager->GetCounter() - _ctime;
- Ttinfo->KernelTime += TimeManager->GetCounter() - _ctime;
+ Ptinfo->KernelTime += TimeManager->GetTimeNs() - _ctime;
+ Ttinfo->KernelTime += TimeManager->GetTimeNs() - _ctime;
return ret;
}
diff --git a/Kernel/core/time/hpet.cpp b/Kernel/core/time/hpet.cpp
deleted file mode 100644
index 36991a70..00000000
--- a/Kernel/core/time/hpet.cpp
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
- This file is part of Fennix Kernel.
-
- Fennix Kernel is free software: you can redistribute it and/or
- modify it under the terms of the GNU General Public License as
- published by the Free Software Foundation, either version 3 of
- the License, or (at your option) any later version.
-
- Fennix Kernel is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with Fennix Kernel. If not, see .
-*/
-
-#include
-
-#include
-#include
-#include
-#include
-
-#include "../../kernel.h"
-
-namespace Time
-{
- bool HighPrecisionEventTimer::Sleep(size_t Duration, Units Unit)
- {
-#if defined(__amd64__)
- uint64_t Target = mminq(&hpet->MainCounterValue) + (Duration * ConvertUnit(Unit)) / clk;
- while (mminq(&hpet->MainCounterValue) < Target)
- CPU::Pause();
- return true;
-#elif defined(__i386__)
- uint64_t Target = mminl(&hpet->MainCounterValue) + (Duration * ConvertUnit(Unit)) / clk;
- while (mminl(&hpet->MainCounterValue) < Target)
- CPU::Pause();
- return true;
-#endif
- return false;
- }
-
- uint64_t HighPrecisionEventTimer::GetCounter()
- {
-#if defined(__amd64__)
- return mminq(&hpet->MainCounterValue);
-#elif defined(__i386__)
- return mminl(&hpet->MainCounterValue);
-#else
- return 0;
-#endif
- }
-
- uint64_t HighPrecisionEventTimer::CalculateTarget(uint64_t Target, Units Unit)
- {
-#if defined(__amd64__)
- return mminq(&hpet->MainCounterValue) + (Target * ConvertUnit(Unit)) / clk;
-#elif defined(__i386__)
- return mminl(&hpet->MainCounterValue) + (Target * ConvertUnit(Unit)) / clk;
-#else
- return 0;
-#endif
- }
-
- uint64_t HighPrecisionEventTimer::GetNanosecondsSinceClassCreation()
- {
-#if defined(__amd64__) || defined(__i386__)
- uint64_t Subtraction = this->GetCounter() - this->ClassCreationTime;
- if (Subtraction <= 0 || this->clk <= 0)
- return 0;
-
- Subtraction *= ConvertUnit(Units::Nanoseconds);
- return uint64_t(Subtraction / this->clk);
-#else
- return 0;
-#endif
- }
-
- HighPrecisionEventTimer::HighPrecisionEventTimer(void *hpet)
- {
-#if defined(__amd64__) || defined(__i386__)
- ACPI::ACPI::HPETHeader *HPET_HDR = (ACPI::ACPI::HPETHeader *)hpet;
- Memory::Virtual vmm;
- vmm.Map((void *)HPET_HDR->Address.Address,
- (void *)HPET_HDR->Address.Address,
- Memory::PTFlag::RW | Memory::PTFlag::PCD);
- this->hpet = (HPET *)HPET_HDR->Address.Address;
- trace("%s timer is at address %016p",
- HPET_HDR->Header.OEMID,
- (void *)HPET_HDR->Address.Address);
- clk = s_cst(uint32_t, (uint64_t)this->hpet->GeneralCapabilities >> 32);
- KPrint("HPET clock is %u Hz", clk);
-#ifdef __amd64__
- mmoutq(&this->hpet->GeneralConfiguration, 0);
- mmoutq(&this->hpet->MainCounterValue, 0);
- mmoutq(&this->hpet->GeneralConfiguration, 1);
-#else
- mmoutl(&this->hpet->GeneralConfiguration, 0);
- mmoutl(&this->hpet->MainCounterValue, 0);
- mmoutl(&this->hpet->GeneralConfiguration, 1);
-#endif
- ClassCreationTime = this->GetCounter();
-#endif
- }
-
- HighPrecisionEventTimer::~HighPrecisionEventTimer()
- {
- }
-}
diff --git a/Kernel/core/time/timer.cpp b/Kernel/core/time/timer.cpp
deleted file mode 100644
index 7b78150e..00000000
--- a/Kernel/core/time/timer.cpp
+++ /dev/null
@@ -1,208 +0,0 @@
-/*
- This file is part of Fennix Kernel.
-
- Fennix Kernel is free software: you can redistribute it and/or
- modify it under the terms of the GNU General Public License as
- published by the Free Software Foundation, either version 3 of
- the License, or (at your option) any later version.
-
- Fennix Kernel is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with Fennix Kernel. If not, see .
-*/
-
-#include
-
-#include
-#include
-#include
-#include
-
-
-#include "../../kernel.h"
-
-namespace Time
-{
- bool time::Sleep(size_t Duration, Units Unit)
- {
- switch (ActiveTimer)
- {
- case NONE:
- error("No timer is active");
- return false;
- case RTC:
- fixme("RTC sleep not implemented");
- return false;
- case PIT:
- fixme("PIT sleep not implemented");
- return false;
- case HPET:
- return this->hpet->Sleep(Duration, Unit);
- case ACPI:
- fixme("ACPI sleep not implemented");
- return false;
- case APIC:
- fixme("APIC sleep not implemented");
- return false;
- case TSC:
- return this->tsc->Sleep(Duration, Unit);
- default:
- error("Unknown timer");
- return false;
- }
- }
-
- uint64_t time::GetCounter()
- {
- switch (ActiveTimer)
- {
- case NONE:
- error("No timer is active");
- return 0;
- case RTC:
- fixme("RTC sleep not implemented");
- return 0;
- case PIT:
- fixme("PIT sleep not implemented");
- return 0;
- case HPET:
- return this->hpet->GetCounter();
- case ACPI:
- fixme("ACPI sleep not implemented");
- return 0;
- case APIC:
- fixme("APIC sleep not implemented");
- return 0;
- case TSC:
- return this->tsc->GetCounter();
- default:
- error("Unknown timer");
- return 0;
- }
- }
-
- uint64_t time::CalculateTarget(uint64_t Target, Units Unit)
- {
- switch (ActiveTimer)
- {
- case NONE:
- error("No timer is active");
- return 0;
- case RTC:
- fixme("RTC sleep not implemented");
- return 0;
- case PIT:
- fixme("PIT sleep not implemented");
- return 0;
- case HPET:
- return this->hpet->CalculateTarget(Target, Unit);
- case ACPI:
- fixme("ACPI sleep not implemented");
- return 0;
- case APIC:
- fixme("APIC sleep not implemented");
- return 0;
- case TSC:
- return this->tsc->CalculateTarget(Target, Unit);
- default:
- error("Unknown timer");
- return 0;
- }
- }
-
- uint64_t time::GetNanosecondsSinceClassCreation()
- {
- switch (ActiveTimer)
- {
- case NONE:
- error("No timer is active");
- return 0;
- case RTC:
- fixme("RTC sleep not implemented");
- return 0;
- case PIT:
- fixme("PIT sleep not implemented");
- return 0;
- case HPET:
- return this->hpet->GetNanosecondsSinceClassCreation();
- case ACPI:
- fixme("ACPI sleep not implemented");
- return 0;
- case APIC:
- fixme("APIC sleep not implemented");
- return 0;
- case TSC:
- return this->tsc->GetNanosecondsSinceClassCreation();
- default:
- error("Unknown timer");
- return 0;
- }
- }
-
- void time::FindTimers(void *acpi)
- {
-#if defined(__amd64__) || defined(__i386__)
- /* TODO: RTC check */
- /* TODO: PIT check */
-
- if (acpi)
- {
- if (((ACPI::ACPI *)acpi)->HPET)
- {
- hpet = new HighPrecisionEventTimer(((ACPI::ACPI *)acpi)->HPET);
- ActiveTimer = HPET;
- SupportedTimers |= HPET;
- KPrint("HPET found");
- }
- else
- {
- KPrint("\x1b[33mHPET not found");
- }
-
- /* TODO: ACPI check */
- /* TODO: APIC check */
- }
- else
- {
- KPrint("\x1b[33mACPI not found");
- }
-
- bool TSCInvariant = false;
- if (strcmp(CPU::Vendor(), x86_CPUID_VENDOR_AMD) == 0)
- {
- CPU::x86::AMD::CPUID0x80000007 cpuid80000007;
- if (cpuid80000007.EDX.TscInvariant)
- TSCInvariant = true;
- }
- else if (strcmp(CPU::Vendor(), x86_CPUID_VENDOR_INTEL) == 0)
- {
- // TODO: Intel 0x80000007
- CPU::x86::AMD::CPUID0x80000007 cpuid80000007;
- if (cpuid80000007.EDX.TscInvariant)
- TSCInvariant = true;
- }
-
- if (TSCInvariant)
- {
- tsc = new TimeStampCounter;
- // FIXME: ActiveTimer = TSC;
- SupportedTimers |= TSC;
- KPrint("Invariant TSC found");
- }
- else
- KPrint("\x1b[33mTSC is not invariant");
-#endif
- }
-
- time::time()
- {
- }
-
- time::~time()
- {
- }
-}
diff --git a/Kernel/core/time/tsc.cpp b/Kernel/core/time/tsc.cpp
deleted file mode 100644
index c860d8cf..00000000
--- a/Kernel/core/time/tsc.cpp
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- This file is part of Fennix Kernel.
-
- Fennix Kernel is free software: you can redistribute it and/or
- modify it under the terms of the GNU General Public License as
- published by the Free Software Foundation, either version 3 of
- the License, or (at your option) any later version.
-
- Fennix Kernel is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with Fennix Kernel. If not, see .
-*/
-
-#include
-
-#include
-#include
-#include
-#include
-
-#include "../../kernel.h"
-
-namespace Time
-{
- bool TimeStampCounter::Sleep(size_t Duration, Units Unit)
- {
-#if defined(__amd64__) || defined(__i386__)
- uint64_t Target = this->GetCounter() + (Duration * ConvertUnit(Unit)) / this->clk;
- while (this->GetCounter() < Target)
- CPU::Pause();
- return true;
-#elif defined(__aarch64__)
- return 0;
-#endif
- }
-
- uint64_t TimeStampCounter::GetCounter()
- {
-#if defined(__amd64__) || defined(__i386__)
- return CPU::Counter();
-#elif defined(__aarch64__)
- return 0;
-#endif
- }
-
- uint64_t TimeStampCounter::CalculateTarget(uint64_t Target, Units Unit)
- {
-#if defined(__amd64__) || defined(__i386__)
- return uint64_t((this->GetCounter() + (Target * ConvertUnit(Unit))) / this->clk);
-#elif defined(__aarch64__)
- return 0;
-#endif
- }
-
- uint64_t TimeStampCounter::GetNanosecondsSinceClassCreation()
- {
-#if defined(__amd64__) || defined(__i386__)
- return uint64_t((this->GetCounter() - this->ClassCreationTime) / this->clk);
-#elif defined(__aarch64__)
- return 0;
-#endif
- }
-
- TimeStampCounter::TimeStampCounter()
- {
-#if defined(__amd64__) || defined(__i386__)
- stub; // FIXME: This is not a good way to measure the clock speed
- uint64_t Start = CPU::Counter();
- TimeManager->Sleep(1, Units::Milliseconds);
- uint64_t End = CPU::Counter();
-
- this->clk = End - Start;
- this->ClassCreationTime = this->GetCounter();
-#endif
- }
-
- TimeStampCounter::~TimeStampCounter()
- {
- }
-}
diff --git a/Kernel/include/task.hpp b/Kernel/include/task.hpp
index 02dda6b1..16571209 100644
--- a/Kernel/include/task.hpp
+++ b/Kernel/include/task.hpp
@@ -626,11 +626,11 @@ namespace Tasking
void WaitForThreadStatus(TCB *tcb, TaskState State);
/**
- * Sleep for a given amount of milliseconds
+ * Sleep for a given amount of nenoseconds
*
- * @param Milliseconds Amount of milliseconds to sleep
+ * @param Nanoseconds Amount of nenoseconds to sleep
*/
- void Sleep(uint64_t Milliseconds, bool NoSwitch = false);
+ void Sleep(uint64_t Nanoseconds, bool NoSwitch = false);
PCB *CreateProcess(PCB *Parent,
const char *Name,
diff --git a/Kernel/include/time.hpp b/Kernel/include/time.hpp
index 59fea95d..93936ab9 100644
--- a/Kernel/include/time.hpp
+++ b/Kernel/include/time.hpp
@@ -21,9 +21,24 @@
#include
#include
#include
+#include
namespace Time
{
+ class ITimer
+ {
+ protected:
+ uint64_t ClassCreationTime = 0;
+
+ public:
+ virtual const char *Name() const = 0;
+ virtual bool IsAvailable() const = 0;
+ virtual bool SupportsNanoseconds() const = 0;
+ virtual bool Sleep(uint64_t Nanoseconds) = 0;
+ virtual uint64_t GetNanoseconds() = 0;
+ virtual ~ITimer() = default;
+ };
+
struct Clock
{
int Year, Month, Day, Hour, Minute, Second;
@@ -33,138 +48,148 @@ namespace Time
Clock ReadClock();
Clock ConvertFromUnix(int Timestamp);
- enum Units
+ inline uint64_t FromSeconds(uint64_t Seconds) { return Seconds * 1'000'000'000ULL; }
+ inline uint64_t FromMilliseconds(uint64_t Milliseconds) { return Milliseconds * 1'000'000ULL; }
+
+ inline uint64_t ToSeconds(uint64_t Nanoseconds) { return Nanoseconds / 1'000'000'000ULL; }
+ inline uint64_t ToMilliseconds(uint64_t Nanoseconds) { return Nanoseconds / 1'000'000ULL; }
+
+ class ProgrammableIntervalTimer : public ITimer
{
- Femtoseconds,
- Picoseconds,
- Nanoseconds,
- Microseconds,
- Milliseconds,
- Seconds,
- Minutes,
- Hours,
- Days,
- Months,
- Years
+ public:
+ const char *Name() const override { return "PIT"; }
+ bool IsAvailable() const override;
+ bool SupportsNanoseconds() const override { return false; }
+
+ bool Sleep(uint64_t Nanoseconds) override;
+ uint64_t GetNanoseconds() override { return 0; }
+
+ ProgrammableIntervalTimer();
+ ~ProgrammableIntervalTimer();
};
- /** @deprecated this shouldn't be used */
- inline uint64_t ConvertUnit(const Units Unit)
+ class RealTimeClock : public ITimer
{
- switch (Unit)
- {
- case Femtoseconds:
- return 1;
- case Picoseconds:
- return 1000;
- case Nanoseconds:
- return 1000000;
- case Microseconds:
- return 1000000000;
- case Milliseconds:
- return 1000000000000;
- case Seconds:
- return 1000000000000000;
- case Minutes:
- return 1000000000000000000;
- // case Hours:
- // return 1000000000000000000000;
- // case Days:
- // return 1000000000000000000000000;
- // case Months:
- // return 1000000000000000000000000000;
- // case Years:
- // return 1000000000000000000000000000000;
- default:
- assert(!"Invalid time unit");
- }
- }
+ public:
+ const char *Name() const override { return "RTC"; }
+ bool IsAvailable() const override;
+ bool SupportsNanoseconds() const override { return false; }
- class HighPrecisionEventTimer
+ bool Sleep(uint64_t Nanoseconds) override;
+ uint64_t GetNanoseconds() override { return 0; }
+
+ RealTimeClock();
+ ~RealTimeClock();
+ };
+
+ class HighPrecisionEventTimer : public ITimer
{
private:
struct HPET
{
- uint64_t GeneralCapabilities;
- uint64_t Reserved0;
- uint64_t GeneralConfiguration;
- uint64_t Reserved1;
- uint64_t GeneralIntStatus;
- uint64_t Reserved2;
- uint64_t Reserved3[24];
- uint64_t MainCounterValue;
- uint64_t Reserved4;
+ uint64_t CapabilitiesID;
+ uint64_t __reserved0;
+ uint64_t Configuration;
+ uint64_t __reserved1;
+ uint64_t InterruptStatus;
+ uint64_t __reserved2[25];
+ uint64_t MainCounter;
+ uint64_t __reserved3;
};
- uint32_t clk = 0;
+ uint64_t Period = 0;
HPET *hpet = nullptr;
- uint64_t ClassCreationTime = 0;
public:
- bool Sleep(size_t Duration, Units Unit);
- uint64_t GetCounter();
- uint64_t CalculateTarget(uint64_t Target, Units Unit);
- uint64_t GetNanosecondsSinceClassCreation();
+ const char *Name() const override { return "HPET"; }
+ bool IsAvailable() const override { return hpet != nullptr; }
+ bool SupportsNanoseconds() const override { return true; }
+ bool Sleep(uint64_t Nanoseconds) override;
+ uint64_t GetNanoseconds() override;
HighPrecisionEventTimer(void *hpet);
~HighPrecisionEventTimer();
};
- class TimeStampCounter
+ class TimeStampCounter : public ITimer
{
private:
uint64_t clk = 0;
- uint64_t ClassCreationTime = 0;
public:
- bool Sleep(size_t Duration, Units Unit);
- uint64_t GetCounter();
- uint64_t CalculateTarget(uint64_t Target, Units Unit);
- uint64_t GetNanosecondsSinceClassCreation();
+ const char *Name() const override { return "TSC"; }
+ bool IsAvailable() const override { return clk != 0; }
+ bool SupportsNanoseconds() const override { return true; }
+ bool Sleep(uint64_t Nanoseconds) override;
+ uint64_t GetNanoseconds() override;
TimeStampCounter();
- ~TimeStampCounter();
+ ~TimeStampCounter() = default;
};
- class time
+ class KVMClock : public ITimer
{
- public:
- enum TimeActiveTimer
+ private:
+ struct kvm_clock_pairing
{
- NONE = 0b0,
- RTC = 0b1,
- PIT = 0b10,
- HPET = 0b100,
- ACPI = 0b1000,
- APIC = 0b10000,
- TSC = 0b100000
+ int64_t sec;
+ int64_t nsec;
+ uint64_t tsc;
+ uint32_t flags;
+ uint32_t pad[9];
};
- private:
- int SupportedTimers = 0;
- TimeActiveTimer ActiveTimer = NONE;
+ struct pvclock_vcpu_time_info
+ {
+ uint32_t version;
+ uint32_t pad0;
+ uint64_t tsc_timestamp;
+ uint64_t system_time;
+ uint32_t tsc_to_system_mul;
+ int8_t tsc_shift;
+ uint8_t flags;
+ uint8_t pad[2];
+ };
- HighPrecisionEventTimer *hpet;
- TimeStampCounter *tsc;
+ struct ms_hyperv_tsc_page
+ {
+ volatile uint32_t tsc_sequence;
+ uint32_t reserved1;
+ volatile uint64_t tsc_scale;
+ volatile int64_t tsc_offset;
+ uint64_t reserved2[509];
+ };
+
+ uint64_t clk = 0;
+ kvm_clock_pairing *Pairing = nullptr;
public:
- int GetSupportedTimers() { return SupportedTimers; }
- TimeActiveTimer GetActiveTimer() { return ActiveTimer; }
- bool ChangeActiveTimer(TimeActiveTimer Timer)
- {
- if (!(SupportedTimers & Timer))
- return false;
- ActiveTimer = Timer;
- return true;
- }
+ const char *Name() const override { return "KVM"; }
+ bool IsAvailable() const override { return clk != 0; }
+ bool SupportsNanoseconds() const override { return true; }
+ bool Sleep(uint64_t Nanoseconds) override;
+ uint64_t GetNanoseconds() override;
- bool Sleep(size_t Duration, Units Unit);
- uint64_t GetCounter();
- uint64_t CalculateTarget(uint64_t Target, Units Unit);
- uint64_t GetNanosecondsSinceClassCreation();
- void FindTimers(void *acpi);
- time();
- ~time();
+ KVMClock();
+ ~KVMClock();
+ };
+
+ class Manager
+ {
+ private:
+ void *acpi = nullptr;
+ std::vector Timers;
+ int ActiveTimer = -1;
+
+ public:
+ void CheckActiveTimer();
+ bool Sleep(uint64_t Nanoseconds);
+ uint64_t GetTimeNs();
+ const char *GetActiveTimerName();
+
+ void InitializeTimers();
+ Manager(void *acpi);
+ ~Manager() = delete;
};
}
diff --git a/Kernel/kernel.cpp b/Kernel/kernel.cpp
index 09a6244c..03eaa420 100644
--- a/Kernel/kernel.cpp
+++ b/Kernel/kernel.cpp
@@ -57,7 +57,7 @@ struct KernelConfig Config = {
Video::Display *Display = nullptr;
SymbolResolver::Symbols *KernelSymbolTable = nullptr;
Power::Power *PowerManager = nullptr;
-Time::time *TimeManager = nullptr;
+Time::Manager *TimeManager = nullptr;
Tasking::Task *TaskManager = nullptr;
PCI::Manager *PCIManager = nullptr;
Driver::Manager *DriverManager = nullptr;
@@ -76,14 +76,14 @@ EXTERNC void _KPrint(const char *Format, va_list Args)
{
SmartLock(KernelLock);
- uint64_t nano = TimeManager ? TimeManager->GetNanosecondsSinceClassCreation() : 0;
+ uint64_t nano = TimeManager ? TimeManager->GetTimeNs() : 0;
#if defined(__amd64__)
- printf("\x1b[1;30m[\x1b[1;34m%lu.%07lu\x1b[1;30m]\x1b[0m ", nano / 10000000, nano % 10000000);
+ printf("\x1b[1;30m[\x1b[1;34m%lu.%07lu\x1b[1;30m]\x1b[0m ", Time::ToSeconds(nano), nano % 10000000);
#elif defined(__i386__)
- printf("\x1b[1;30m[\x1b[1;34m%llu.%07llu\x1b[1;30m]\x1b[0m ", nano / 10000000, nano % 10000000);
+ printf("\x1b[1;30m[\x1b[1;34m%llu.%07llu\x1b[1;30m]\x1b[0m ", Time::ToSeconds(nano), nano % 10000000);
#elif defined(__aarch64__)
- printf("\x1b[1;30m[\x1b[1;34m%lu.%07lu\x1b[1;30m]\x1b[0m ", nano / 10000000, nano % 10000000);
+ printf("\x1b[1;30m[\x1b[1;34m%lu.%07lu\x1b[1;30m]\x1b[0m ", Time::ToSeconds(nano), nano % 10000000);
#endif
vprintf(Format, Args);
@@ -232,8 +232,8 @@ EXTERNC nif cold void Main()
#endif
KPrint("Initializing Timers");
- TimeManager = new Time::time;
- TimeManager->FindTimers(PowerManager->GetACPI());
+ TimeManager = new Time::Manager(PowerManager->GetACPI());
+ TimeManager->InitializeTimers();
KPrint("Initializing PCI Manager");
PCIManager = new PCI::Manager;
@@ -388,12 +388,6 @@ EXTERNC __no_stack_protector void BeforeShutdown(bool Reboot)
if (fs)
delete fs, fs = nullptr;
- KPrint("Stopping timers");
- if (TimeManager)
- delete TimeManager, TimeManager = nullptr;
-
- // PowerManager should not be called
-
// https://wiki.osdev.org/Calling_Global_Constructors
KPrint("Calling destructors");
for (CallPtr *fct = __fini_array_start; fct != __fini_array_end; fct++)
diff --git a/Kernel/kernel.h b/Kernel/kernel.h
index 2e8d54e7..6162c16f 100644
--- a/Kernel/kernel.h
+++ b/Kernel/kernel.h
@@ -48,7 +48,7 @@ extern bool DebuggerIsAttached;
extern Video::Display *Display;
extern SymbolResolver::Symbols *KernelSymbolTable;
extern Power::Power *PowerManager;
-extern Time::time *TimeManager;
+extern Time::Manager *TimeManager;
extern PCI::Manager *PCIManager;
extern vfs::Virtual *fs;
extern Tasking::Task *TaskManager;
diff --git a/Kernel/kernel_thread.cpp b/Kernel/kernel_thread.cpp
index be496881..00b3ad16 100644
--- a/Kernel/kernel_thread.cpp
+++ b/Kernel/kernel_thread.cpp
@@ -169,7 +169,7 @@ Exit:
ExitCode, ExitCode < 0 ? -ExitCode : ExitCode);
KPrint("Dropping to kernel shell");
- TaskManager->Sleep(1000);
+ TaskManager->Sleep(Time::FromMilliseconds(1000));
TaskManager->CreateThread(thisProcess, Tasking::IP(KShellThread))->Rename("Kernel Shell");
CPU::Halt(true);
}
diff --git a/Kernel/kshell/commands/uptime.cpp b/Kernel/kshell/commands/uptime.cpp
index 718c2131..9fa97499 100644
--- a/Kernel/kshell/commands/uptime.cpp
+++ b/Kernel/kshell/commands/uptime.cpp
@@ -28,7 +28,7 @@ void cmd_uptime(const char *)
if (TimeManager)
{
uint64_t Nanoseconds =
- TimeManager->GetNanosecondsSinceClassCreation();
+ TimeManager->GetTimeNs();
uint64_t Seconds = Nanoseconds / 10000000;
uint64_t Minutes = Seconds / 60;
uint64_t Hours = Minutes / 60;
diff --git a/Kernel/subsystem/linux/syscall.cpp b/Kernel/subsystem/linux/syscall.cpp
index 9d4804a1..f98868f0 100644
--- a/Kernel/subsystem/linux/syscall.cpp
+++ b/Kernel/subsystem/linux/syscall.cpp
@@ -1389,10 +1389,10 @@ static int linux_nanosleep(SysFrm *,
pReq->tv_nsec, pReq->tv_sec);
uint64_t nanoTime = pReq->tv_nsec;
- uint64_t secTime = pReq->tv_sec * 1000000000; /* Nano */
+ uint64_t secTime = Time::FromSeconds(pReq->tv_sec);
- uint64_t time = TimeManager->GetCounter();
- uint64_t sleepTime = TimeManager->CalculateTarget(nanoTime + secTime, Time::Nanoseconds);
+ uint64_t time = TimeManager->GetTimeNs();
+ uint64_t sleepTime = TimeManager->GetTimeNs() + secTime + nanoTime;
debug("time=%ld secTime=%ld nanoTime=%ld sleepTime=%ld",
time, secTime, nanoTime, sleepTime);
@@ -1406,7 +1406,7 @@ static int linux_nanosleep(SysFrm *,
}
pcb->GetContext()->Yield();
- time = TimeManager->GetCounter();
+ time = TimeManager->GetTimeNs();
}
debug("time= %ld", time);
debug("sleepTime=%ld", sleepTime);
@@ -2582,7 +2582,7 @@ static int linux_sysinfo(SysFrm *, struct sysinfo *info)
if (pInfo == nullptr)
return -linux_EFAULT;
- uint64_t nano = TimeManager->GetNanosecondsSinceClassCreation();
+ uint64_t nano = TimeManager->GetTimeNs();
if (nano != 0)
nano /= 10000000;
@@ -3185,18 +3185,18 @@ static int linux_clock_gettime(SysFrm *, clockid_t clockid, struct timespec *tp)
{
case linux_CLOCK_REALTIME:
{
- uint64_t time = TimeManager->GetCounter();
- pTp->tv_sec = time / Time::ConvertUnit(Time::Seconds);
- pTp->tv_nsec = time / Time::ConvertUnit(Time::Nanoseconds);
+ uint64_t time = TimeManager->GetTimeNs();
+ pTp->tv_sec = Time::ToSeconds(time);
+ pTp->tv_nsec = time;
debug("time=%ld sec=%ld nsec=%ld",
time, pTp->tv_sec, pTp->tv_nsec);
break;
}
case linux_CLOCK_MONOTONIC:
{
- uint64_t time = TimeManager->GetCounter();
- pTp->tv_sec = time / Time::ConvertUnit(Time::Seconds);
- pTp->tv_nsec = time / Time::ConvertUnit(Time::Nanoseconds);
+ uint64_t time = TimeManager->GetTimeNs();
+ pTp->tv_sec = Time::ToSeconds(time);
+ pTp->tv_nsec = time;
debug("time=%ld sec=%ld nsec=%ld",
time, pTp->tv_sec, pTp->tv_nsec);
break;
@@ -3244,9 +3244,8 @@ static int linux_clock_nanosleep(SysFrm *, clockid_t clockid, int flags,
case linux_CLOCK_REALTIME:
case linux_CLOCK_MONOTONIC:
{
- uint64_t time = TimeManager->GetCounter();
- uint64_t rqTime = pRequest->tv_sec * Time::ConvertUnit(Time::Seconds) +
- pRequest->tv_nsec * Time::ConvertUnit(Time::Nanoseconds);
+ uint64_t time = TimeManager->GetTimeNs();
+ uint64_t rqTime = Time::FromSeconds(pRequest->tv_sec) + pRequest->tv_nsec;
debug("Sleeping for %ld", rqTime - time);
if (rqTime > time)
diff --git a/Kernel/subsystem/time/hpet.cpp b/Kernel/subsystem/time/hpet.cpp
new file mode 100644
index 00000000..c1aca55b
--- /dev/null
+++ b/Kernel/subsystem/time/hpet.cpp
@@ -0,0 +1,100 @@
+/*
+ This file is part of Fennix Kernel.
+
+ Fennix Kernel is free software: you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation, either version 3 of
+ the License, or (at your option) any later version.
+
+ Fennix Kernel is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Fennix Kernel. If not, see .
+*/
+
+#include
+
+#include
+#include
+#include
+#include
+
+#include "../../kernel.h"
+
+namespace Time
+{
+ bool HighPrecisionEventTimer::Sleep(size_t Nanoseconds)
+ {
+ uint64_t target = this->GetNanoseconds() + Nanoseconds;
+ while (this->GetNanoseconds() < target)
+ CPU::Pause();
+ return true;
+ }
+
+ uint64_t HighPrecisionEventTimer::GetNanoseconds()
+ {
+#if defined(__amd64__)
+ uint64_t counter = mminq(&this->hpet->MainCounter);
+#elif defined(__i386__)
+ uint64_t counter = mminl(&this->hpet->MainCounter);
+#else
+ return 0;
+#endif
+ /* convert ticks to nanoseconds: counter * period_fs / 1e6 */
+ return (counter * 1'000'000'000ULL) / this->Period;
+ }
+
+ HighPrecisionEventTimer::HighPrecisionEventTimer(void *hpet)
+ {
+#if defined(__amd64__) || defined(__i386__)
+ ACPI::ACPI::HPETHeader *hdr = (ACPI::ACPI::HPETHeader *)hpet;
+ Memory::Virtual vmm;
+ vmm.Map((void *)hdr->Address.Address, (void *)hdr->Address.Address, Memory::RW | Memory::PCD | Memory::PWT);
+ this->hpet = reinterpret_cast(hdr->Address.Address);
+ debug("%s timer is at address %#lx", hdr->Header.OEMID, hdr->Address.Address);
+ uint64_t period_fs = this->hpet->CapabilitiesID >> 32;
+ if (period_fs == 0)
+ {
+ warn("HPET: Invalid period in CapabilitiesID");
+ return;
+ }
+
+ /* Hz = 1e15 / period_fs */
+ this->Period = 1'000'000'000'000'000ULL / period_fs;
+ KPrint("HPET tick period: %lu femtoseconds -> %u Hz", period_fs, this->Period);
+#ifdef __amd64__
+ mmoutq(&this->hpet->Configuration, 0);
+ mmoutq(&this->hpet->MainCounter, 0);
+ mmoutq(&this->hpet->Configuration, 1);
+#else
+ mmoutl(&this->hpet->Configuration, 0);
+ mmoutl(&this->hpet->MainCounter, 0);
+ mmoutl(&this->hpet->Configuration, 1);
+#endif
+
+ for (int i = 0; i < 5; i++)
+ {
+ uint64_t val = mminq(&this->hpet->MainCounter);
+ KPrint("HPET counter test %d: %llu", i, val);
+ }
+
+ uint64_t cfg = mminq(&this->hpet->Configuration);
+ if (!(cfg & 1))
+ warn("HPET counter is not enabled!");
+
+ ClassCreationTime = this->GetNanoseconds();
+#endif
+ }
+
+ HighPrecisionEventTimer::~HighPrecisionEventTimer()
+ {
+#ifdef __amd64__
+ mmoutq(&this->hpet->Configuration, 0);
+#else
+ mmoutl(&this->hpet->Configuration, 0);
+#endif
+ }
+}
diff --git a/Kernel/subsystem/time/kvm_clock.cpp b/Kernel/subsystem/time/kvm_clock.cpp
new file mode 100644
index 00000000..f445c3e3
--- /dev/null
+++ b/Kernel/subsystem/time/kvm_clock.cpp
@@ -0,0 +1,71 @@
+/*
+ This file is part of Fennix Kernel.
+
+ Fennix Kernel is free software: you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation, either version 3 of
+ the License, or (at your option) any later version.
+
+ Fennix Kernel is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Fennix Kernel. If not, see .
+*/
+
+#include
+
+#include
+#include
+#include
+#include
+
+#include "../../kernel.h"
+
+#define KVM_CLOCK_PAIRING_WALLCLOCK 0
+
+namespace Time
+{
+ extern "C" void kvm_hc_clock_pairing(uint64_t phys_addr, uint64_t clock_type)
+ {
+#if defined(__amd64__)
+ asm volatile(
+ "mov $9, %%eax\n\t" /* KVM_HC_CLOCK_PAIRING */
+ "mov %%rdi, %%rbx\n\t"
+ "mov %%rsi, %%rcx\n\t"
+ "vmcall\n\t"
+ :
+ : "D"(phys_addr), "S"(clock_type)
+ : "rax", "rbx", "rcx");
+#else
+#warning "KVM clock pairing not implemented for this architecture"
+#endif
+ }
+
+ bool KVMClock::Sleep(uint64_t Nanoseconds)
+ {
+ return true;
+ }
+
+ uint64_t KVMClock::GetNanoseconds()
+ {
+ return 0;
+ }
+
+ KVMClock::KVMClock()
+ {
+ if (strcmp(CPU::Hypervisor(), x86_CPUID_VENDOR_KVM) != 0)
+ return;
+
+ this->Pairing = (kvm_clock_pairing *)KernelAllocator.RequestPages(TO_PAGES(sizeof(kvm_clock_pairing)));
+ kvm_hc_clock_pairing((uint64_t)this->Pairing, KVM_CLOCK_PAIRING_WALLCLOCK);
+ // KPrint("sec: %lld, nsec: %lld, tsc: %lld", this->Pairing->sec, this->Pairing->nsec, this->Pairing->tsc);
+ // KPrint("flags: %x", this->Pairing->flags);
+ }
+
+ KVMClock::~KVMClock()
+ {
+ }
+}
diff --git a/Kernel/subsystem/time/manager.cpp b/Kernel/subsystem/time/manager.cpp
new file mode 100644
index 00000000..6c1a9674
--- /dev/null
+++ b/Kernel/subsystem/time/manager.cpp
@@ -0,0 +1,107 @@
+/*
+ This file is part of Fennix Kernel.
+
+ Fennix Kernel is free software: you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation, either version 3 of
+ the License, or (at your option) any later version.
+
+ Fennix Kernel is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Fennix Kernel. If not, see .
+*/
+
+#include
+
+#include
+#include
+#include
+#include
+
+#include "../../kernel.h"
+
+namespace Time
+{
+ void Manager::CheckActiveTimer()
+ {
+ if (unlikely(Timers[ActiveTimer]->IsAvailable() == false))
+ {
+ for (size_t i = Timers.size(); i-- > 0;)
+ {
+ if (Timers[i]->IsAvailable() == false)
+ continue;
+ ActiveTimer = i;
+ break;
+ }
+ }
+ }
+
+ bool Manager::Sleep(size_t Nanoseconds)
+ {
+ if (unlikely(Timers.empty()))
+ return false;
+
+ this->CheckActiveTimer();
+ debug("sleep for %d ns in timer %s", Nanoseconds, Timers[ActiveTimer]->Name());
+ return Timers[ActiveTimer]->Sleep(Nanoseconds);
+ }
+
+ uint64_t Manager::GetTimeNs()
+ {
+ if (unlikely(Timers.empty()))
+ return 0;
+
+ this->CheckActiveTimer();
+ return Timers[ActiveTimer]->GetNanoseconds();
+ }
+
+ const char *Manager::GetActiveTimerName()
+ {
+ if (unlikely(Timers.empty()))
+ return "\0";
+
+ this->CheckActiveTimer();
+ return Timers[ActiveTimer]->Name();
+ }
+
+ void Manager::InitializeTimers()
+ {
+#if defined(__amd64__) || defined(__i386__)
+ /* TODO: RTC check */
+ /* TODO: PIT check */
+
+ if (acpi)
+ {
+ if (((ACPI::ACPI *)acpi)->HPET)
+ {
+ ITimer *hpet = new HighPrecisionEventTimer(((ACPI::ACPI *)acpi)->HPET);
+ ActiveTimer = Timers.size();
+ Timers.push_back(hpet);
+ }
+
+ /* TODO: ACPI check */
+ /* TODO: APIC check */
+ }
+ else
+ {
+ KPrint("\x1b[33mACPI not available");
+ }
+
+ ITimer *tsc = new TimeStampCounter;
+ ActiveTimer = Timers.size();
+ Timers.push_back(tsc);
+
+ ITimer *kvmclock = new KVMClock;
+ ActiveTimer = Timers.size();
+ Timers.push_back(kvmclock);
+#endif
+
+ assert(Timers.empty() == false);
+ }
+
+ Manager::Manager(void *_acpi) : acpi(_acpi) {}
+}
diff --git a/Kernel/core/time/time.cpp b/Kernel/subsystem/time/rtc.cpp
similarity index 100%
rename from Kernel/core/time/time.cpp
rename to Kernel/subsystem/time/rtc.cpp
diff --git a/Kernel/subsystem/time/tsc.cpp b/Kernel/subsystem/time/tsc.cpp
new file mode 100644
index 00000000..83025978
--- /dev/null
+++ b/Kernel/subsystem/time/tsc.cpp
@@ -0,0 +1,105 @@
+/*
+ This file is part of Fennix Kernel.
+
+ Fennix Kernel is free software: you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation, either version 3 of
+ the License, or (at your option) any later version.
+
+ Fennix Kernel is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Fennix Kernel. If not, see .
+*/
+
+#include
+
+#include
+#include
+#include
+#include
+
+#include "../../kernel.h"
+
+namespace Time
+{
+ static inline uint64_t rdtsc()
+ {
+#if defined(__amd64__) || defined(__i386__)
+ unsigned int lo, hi;
+ __asm__ __volatile__("rdtsc" : "=a"(lo), "=d"(hi));
+ return ((uint64_t)hi << 32) | lo;
+#else
+ return 0;
+#endif
+ }
+
+ bool TimeStampCounter::Sleep(uint64_t Nanoseconds)
+ {
+ uint64_t target = this->GetNanoseconds() + Nanoseconds;
+ while (this->GetNanoseconds() < target)
+ CPU::Pause();
+ return true;
+ }
+
+ uint64_t TimeStampCounter::GetNanoseconds()
+ {
+ uint64_t tsc = rdtsc();
+ return (tsc * 1000000000ULL) / this->clk;
+ }
+
+ TimeStampCounter::TimeStampCounter()
+ {
+ bool TSCInvariant = false;
+ if (strcmp(CPU::Vendor(), x86_CPUID_VENDOR_AMD) == 0)
+ {
+ CPU::x86::AMD::CPUID0x80000007 cpuid80000007;
+ if (cpuid80000007.EDX.TscInvariant)
+ TSCInvariant = true;
+ }
+ else if (strcmp(CPU::Vendor(), x86_CPUID_VENDOR_INTEL) == 0)
+ {
+ // TODO: Intel 0x80000007
+ CPU::x86::AMD::CPUID0x80000007 cpuid80000007;
+ if (cpuid80000007.EDX.TscInvariant)
+ TSCInvariant = true;
+ }
+
+ if (!TSCInvariant)
+ {
+ KPrint("\x1b[33mTSC is not invariant");
+ return;
+ }
+
+ const int attempts = 5;
+ uint64_t ns = 10000000ULL; /* 10 ms */
+ uint64_t total_clk = 0;
+ uint64_t overhead = 0;
+
+ for (int i = 0; i < attempts; ++i)
+ {
+ uint64_t t0 = rdtsc();
+ uint64_t t1 = rdtsc();
+ overhead += (t1 - t0);
+ }
+ overhead /= attempts;
+
+ for (int i = 0; i < attempts; ++i)
+ {
+ uint64_t tsc_start = rdtsc();
+ uint64_t hpet_start = TimeManager->GetTimeNs();
+ while (TimeManager->GetTimeNs() - hpet_start < ns)
+ CPU::Pause();
+ uint64_t tsc_end = rdtsc();
+ total_clk += (tsc_end - tsc_start - overhead) * 1000000000ULL / ns;
+ }
+ this->clk = total_clk / attempts;
+ KPrint("TSC frequency: %lu MHz", this->clk / 1000000);
+ this->ClassCreationTime = this->GetNanoseconds();
+ fixme("tsc not working as expected");
+ this->clk = 0; /* disable */
+ }
+}
diff --git a/Kernel/tasking/process.cpp b/Kernel/tasking/process.cpp
index 7e2b530e..a58a0fae 100644
--- a/Kernel/tasking/process.cpp
+++ b/Kernel/tasking/process.cpp
@@ -251,7 +251,7 @@ namespace Tasking
this->AllocatedMemory += sizeof(Memory::ProgramBreak);
this->AllocatedMemory += sizeof(SymbolResolver::Symbols);
- this->Info.SpawnTime = TimeManager->GetCounter();
+ this->Info.SpawnTime = TimeManager->GetTimeNs();
if (Parent)
Parent->Children.push_back(this);
diff --git a/Kernel/tasking/scheduler/custom.cpp b/Kernel/tasking/scheduler/custom.cpp
index 97e66198..6ccb78af 100644
--- a/Kernel/tasking/scheduler/custom.cpp
+++ b/Kernel/tasking/scheduler/custom.cpp
@@ -290,7 +290,7 @@ namespace Tasking::Scheduler
hot nsa void Custom::UpdateUsage(TaskInfo *Info, TaskExecutionMode Mode, int Core)
{
UNUSED(Core);
- uint64_t CurrentTime = TimeManager->GetCounter();
+ uint64_t CurrentTime = TimeManager->GetTimeNs();
uint64_t TimePassed = Info->LastUpdateTime - CurrentTime;
Info->LastUpdateTime = CurrentTime;
@@ -529,7 +529,7 @@ namespace Tasking::Scheduler
continue;
/* Check if the thread is ready to wake up. */
- if (unlikely(thread->Info.SleepUntil < TimeManager->GetCounter()))
+ if (unlikely(thread->Info.SleepUntil < TimeManager->GetTimeNs()))
{
if (pState == TaskState::Sleeping)
process->State.store(TaskState::Ready);
@@ -541,7 +541,7 @@ namespace Tasking::Scheduler
else
{
wut_schedbg("Thread \"%s\"(%d) is not ready to wake up. (SleepUntil: %d, Counter: %d)",
- thread->Name, thread->ID, thread->Info.SleepUntil, TimeManager->GetCounter());
+ thread->Name, thread->ID, thread->Info.SleepUntil, TimeManager->GetTimeNs());
}
}
}
@@ -574,7 +574,7 @@ namespace Tasking::Scheduler
return;
}
bool ProcessNotChanged = false;
- uint64_t SchedTmpTicks = TimeManager->GetCounter();
+ uint64_t SchedTmpTicks = TimeManager->GetTimeNs();
this->LastTaskTicks.store(size_t(SchedTmpTicks - this->SchedulerTicks.load()));
CPUData *CurrentCPU = GetCurrentCPU();
this->LastCore.store(CurrentCPU->ID);
@@ -621,7 +621,7 @@ namespace Tasking::Scheduler
CurrentCPU->CurrentProcess->State.store(TaskState::Running);
CurrentCPU->CurrentThread->State.store(TaskState::Running);
*Frame = CurrentCPU->CurrentThread->Registers;
- this->SchedulerTicks.store(size_t(TimeManager->GetCounter() - SchedTmpTicks));
+ this->SchedulerTicks.store(size_t(TimeManager->GetTimeNs() - SchedTmpTicks));
return;
}
@@ -696,8 +696,8 @@ namespace Tasking::Scheduler
CurrentCPU->CurrentProcess->Signals.HandleSignal(Frame, CurrentCPU->CurrentThread.load());
if (!ProcessNotChanged)
- (&CurrentCPU->CurrentProcess->Info)->LastUpdateTime = TimeManager->GetCounter();
- (&CurrentCPU->CurrentThread->Info)->LastUpdateTime = TimeManager->GetCounter();
+ (&CurrentCPU->CurrentProcess->Info)->LastUpdateTime = TimeManager->GetTimeNs();
+ (&CurrentCPU->CurrentThread->Info)->LastUpdateTime = TimeManager->GetTimeNs();
this->OneShot(CurrentCPU->CurrentThread->Info.Priority);
if (CurrentCPU->CurrentThread->Security.IsDebugEnabled &&
@@ -720,7 +720,7 @@ namespace Tasking::Scheduler
#endif
}
- this->SchedulerTicks.store(size_t(TimeManager->GetCounter() - SchedTmpTicks));
+ this->SchedulerTicks.store(size_t(TimeManager->GetTimeNs() - SchedTmpTicks));
}
hot nsa nif void Custom::OnInterruptReceived(CPU::SchedulerFrame *Frame)
@@ -754,7 +754,7 @@ namespace Tasking::Scheduler
}
debug("Waiting for processes to terminate");
- uint64_t timeout = TimeManager->CalculateTarget(20, Time::Units::Seconds);
+ uint64_t timeout = TimeManager->GetTimeNs() + Time::FromSeconds(20);
while (this->GetProcessList().size() > 0)
{
trace("Waiting for %d processes to terminate", this->GetProcessList().size());
@@ -780,7 +780,7 @@ namespace Tasking::Scheduler
ctx->GetCurrentProcess()->Name,
ctx->GetCurrentProcess()->ID);
- if (TimeManager->GetCounter() > timeout)
+ if (TimeManager->GetTimeNs() > timeout)
{
error("Timeout waiting for processes to terminate");
break;
diff --git a/Kernel/tasking/task.cpp b/Kernel/tasking/task.cpp
index 87342ec2..814c6500 100644
--- a/Kernel/tasking/task.cpp
+++ b/Kernel/tasking/task.cpp
@@ -167,7 +167,7 @@ namespace Tasking
this->Yield();
}
- void Task::Sleep(uint64_t Milliseconds, bool NoSwitch)
+ void Task::Sleep(uint64_t Nanoseconds, bool NoSwitch)
{
TCB *thread = this->GetCurrentThread();
PCB *process = thread->Parent;
@@ -179,13 +179,11 @@ namespace Tasking
if (process->Threads.size() == 1)
process->SetState(TaskState::Sleeping);
- thread->Info.SleepUntil =
- TimeManager->CalculateTarget(Milliseconds,
- Time::Units::Milliseconds);
+ thread->Info.SleepUntil = TimeManager->GetTimeNs() + Nanoseconds;
}
// #ifdef DEBUG
- // uint64_t TicksNow = TimeManager->GetCounter();
+ // uint64_t TicksNow = TimeManager->GetTimeNs();
// #endif
// debug("Thread \"%s\"(%d) is going to sleep until %llu, current %llu, diff %llu",
// thread->Name, thread->ID, thread->Info.SleepUntil,
diff --git a/Kernel/tasking/thread.cpp b/Kernel/tasking/thread.cpp
index d9eeb918..d949b1cf 100644
--- a/Kernel/tasking/thread.cpp
+++ b/Kernel/tasking/thread.cpp
@@ -650,7 +650,7 @@ namespace Tasking
this->AllocatedMemory += strlen(this->Parent->Name) + 1;
this->AllocatedMemory += sizeof(Memory::StackGuard);
- this->Info.SpawnTime = TimeManager->GetCounter();
+ this->Info.SpawnTime = TimeManager->GetTimeNs();
this->Parent->Threads.push_back(this);
if (this->Parent->Threads.size() == 1 &&
diff --git a/Kernel/tests/readdir.cpp b/Kernel/tests/readdir.cpp
index a9cb11b5..2c31fe9e 100644
--- a/Kernel/tests/readdir.cpp
+++ b/Kernel/tests/readdir.cpp
@@ -70,23 +70,23 @@ void readdir_sanity_tests()
KPrint("TEST /");
TestReadDirectory(t0);
- TaskManager->Sleep(2000);
+ TaskManager->Sleep(Time::FromMilliseconds(2000));
KPrint("TEST /dev");
TestReadDirectory(t1);
- TaskManager->Sleep(2000);
+ TaskManager->Sleep(Time::FromMilliseconds(2000));
KPrint("TEST /home");
TestReadDirectory(t2);
- TaskManager->Sleep(2000);
+ TaskManager->Sleep(Time::FromMilliseconds(2000));
KPrint("TEST /var");
TestReadDirectory(t3);
- TaskManager->Sleep(2000);
+ TaskManager->Sleep(Time::FromMilliseconds(2000));
KPrint("TEST /tmp");
TestReadDirectory(t4);
- TaskManager->Sleep(2000);
+ TaskManager->Sleep(Time::FromMilliseconds(2000));
CPU::Stop();
}
diff --git a/Kernel/tests/task_heartbeat.cpp b/Kernel/tests/task_heartbeat.cpp
index 13c4470d..48a58c18 100644
--- a/Kernel/tests/task_heartbeat.cpp
+++ b/Kernel/tests/task_heartbeat.cpp
@@ -29,7 +29,7 @@ void TaskHeartbeat()
while (true)
{
debug("Task Heartbeat");
- TaskManager->Sleep(5000);
+ TaskManager->Sleep(Time::FromMilliseconds(5000));
}
}
diff --git a/Kernel/tests/taskmgr.cpp b/Kernel/tests/taskmgr.cpp
index b2705598..32401bbf 100644
--- a/Kernel/tests/taskmgr.cpp
+++ b/Kernel/tests/taskmgr.cpp
@@ -67,7 +67,7 @@ void TaskMgr_Dummy100Usage()
void TaskMgr_Dummy0Usage()
{
while (1)
- TaskManager->Sleep(1000000);
+ TaskManager->Sleep(Time::FromMilliseconds(1000000));
}
uint64_t GetUsage(uint64_t OldSystemTime, Tasking::TaskInfo *Info)
@@ -75,7 +75,7 @@ uint64_t GetUsage(uint64_t OldSystemTime, Tasking::TaskInfo *Info)
/* https://github.com/reactos/reactos/blob/560671a784c1e0e0aa7590df5e0598c1e2f41f5a/base/applications/taskmgr/perfdata.c#L347 */
if (Info->OldKernelTime || Info->OldUserTime)
{
- uint64_t SystemTime = TimeManager->GetCounter() - OldSystemTime;
+ uint64_t SystemTime = TimeManager->GetTimeNs() - OldSystemTime;
uint64_t CurrentTime = Info->KernelTime + Info->UserTime;
uint64_t OldTime = Info->OldKernelTime + Info->OldUserTime;
uint64_t CpuUsage = (CurrentTime - OldTime) / SystemTime;
@@ -168,7 +168,7 @@ void TaskMgr()
#endif
}
}
- OldSystemTime = TimeManager->GetCounter();
+ OldSystemTime = TimeManager->GetTimeNs();
#if defined(__amd64__)
register uintptr_t CurrentStackAddress asm("rsp");
printf("Sanity: %d, Stack: %#lx", sanity++, CurrentStackAddress);
@@ -185,7 +185,7 @@ void TaskMgr()
if (!Config.Quiet)
Display->UpdateBuffer();
- TaskManager->Sleep(100);
+ TaskManager->Sleep(Time::FromMilliseconds(100));
}
}
diff --git a/Makefile b/Makefile
index f98fd2fa..ea04b2f1 100644
--- a/Makefile
+++ b/Makefile
@@ -14,6 +14,7 @@ QEMUFLAGS := -display gtk
ifeq ($(OSARCH), amd64)
QEMUFLAGS += -device vmware-svga -M q35 \
+ -cpu max \
-usb \
-device qemu-xhci,id=xhci \
-net user \
@@ -40,6 +41,7 @@ QEMUFLAGS += -device vmware-svga -M q35 \
-acpitable file=tools/acpi/SSDT1.dat
else ifeq ($(OSARCH), i386)
QEMUFLAGS += -M q35 \
+ -cpu max \
-usb \
-device qemu-xhci,id=xhci \
-device usb-mouse,bus=xhci.0,pcap=mousex.pcap \