10 Commits

Author SHA1 Message Date
f5c8ae9323 fix(devcontainer): use xhost to allow qemu to run inside the container
Some checks failed
Build OS / Build Cross-Compiler & Toolchain (push) Has been cancelled
Build OS / Analyze (c-cpp) (push) Has been cancelled
Build OS / Build OS (push) Has been cancelled
I did this because on every reboot xauth directory was changing, and had to rebuild the container.
2025-05-30 01:50:05 +00:00
154d857c2e refactor(kernel): remove debug log from Sleep function 2025-05-28 02:15:11 +00:00
12ae5a83da feat(kernel): 🎨 add whileto macro for conditional looping with timeout 2025-05-28 02:05:16 +00:00
13ce994edf fix(kernel): 🐛 interrupt handler was broken for PCI 2025-05-28 00:43:21 +00:00
814175ddaf fix(kernel/pci): 🔥 do not map I/O BARs 2025-05-24 17:24:32 +00:00
43e7ddb9de fix(kernel): remove redundant HPET counter test loop 2025-05-24 00:39:20 +00:00
0187fa5b66 feat(kernel): 🎨 add the logo to the kernel as a png resource 2025-05-23 23:32:37 +00:00
33c284091d refactor(kernel): ♻️ rewrite time manager 2025-05-23 23:30:04 +00:00
9538589c11 feat(kernel): add kvm cpuid structures 2025-05-23 20:52:29 +00:00
905b6933c9 fix(kernel/std): 🎨 implementation for <chrono>, <ratio> & <thread> were broken 2025-05-23 15:16:04 +00:00
42 changed files with 1066 additions and 646 deletions

View File

@@ -25,17 +25,13 @@
} }
}, },
// From this line below are for qemu, so not that important. // From this line below are for qemu, so not that important.
"initializeCommand": "xhost +local:docker", // "xhost -local:docker" to disable
"mounts": [ "mounts": [
{ {
"source": "/tmp/.X11-unix", "source": "/tmp/.X11-unix",
"target": "/tmp/.X11-unix", "target": "/tmp/.X11-unix",
"type": "bind" "type": "bind"
}, },
{
"source": "${localEnv:XAUTHORITY}",
"target": "/home/vscode/.Xauthority",
"type": "bind"
},
{ {
"source": "/run/user/1000/pulse/native", "source": "/run/user/1000/pulse/native",
"target": "/run/user/1000/pulse/native", "target": "/run/user/1000/pulse/native",

View File

@@ -27,6 +27,9 @@ License information can be found in the [LICENSES.md](LICENSES.md) file.
## CPUID 0x7 ## CPUID 0x7
- [CPUID](https://en.wikipedia.org/wiki/CPUID) - [CPUID](https://en.wikipedia.org/wiki/CPUID)
## KVM CPUID
- [kernel.org KVM CPUID](https://www.kernel.org/doc/html/v6.9/virt/kvm/x86/cpuid.html?highlight=cpuid)
## Network ## Network
- [Beej's Guide to Network Programming](https://web.archive.org/web/20051210132103/http://users.pcnet.ro/dmoroian/beej/Beej.html) - [Beej's Guide to Network Programming](https://web.archive.org/web/20051210132103/http://users.pcnet.ro/dmoroian/beej/Beej.html)
- [UDP Socket Programming](https://web.archive.org/web/20060229214053/http://www.cs.rutgers.edu/~pxk/417/notes/sockets/udp.html) - [UDP Socket Programming](https://web.archive.org/web/20060229214053/http://www.cs.rutgers.edu/~pxk/417/notes/sockets/udp.html)

View File

@@ -10,14 +10,14 @@ define find-sources
$(shell find ./ -type f -name '$1' $(shell echo $(foreach arch,$(filter-out $(OSARCH),$(AVAILABLE_ARCHS)), -not -path \"./arch/$(arch)/*\")) -print0 | xargs -0) $(shell find ./ -type f -name '$1' $(shell echo $(foreach arch,$(filter-out $(OSARCH),$(AVAILABLE_ARCHS)), -not -path \"./arch/$(arch)/*\")) -print0 | xargs -0)
endef endef
BMP_SOURCES := $(call find-sources,*.bmp) PNG_SOURCES := $(call find-sources,*.png)
PSF_SOURCES := $(call find-sources,*.psf) PSF_SOURCES := $(call find-sources,*.psf)
S_SOURCES := $(call find-sources,*.S) S_SOURCES := $(call find-sources,*.S)
s_SOURCES := $(call find-sources,*.s) s_SOURCES := $(call find-sources,*.s)
C_SOURCES := $(call find-sources,*.c) C_SOURCES := $(call find-sources,*.c)
CXX_SOURCES := $(call find-sources,*.cpp) CXX_SOURCES := $(call find-sources,*.cpp)
OBJ = $(BMP_SOURCES:.bmp=.o) $(PSF_SOURCES:.psf=.o) $(s_SOURCES:.s=.o) $(S_SOURCES:.S=.o) $(C_SOURCES:.c=.o) $(CXX_SOURCES:.cpp=.o) OBJ = $(PNG_SOURCES:.png=.o) $(PSF_SOURCES:.psf=.o) $(s_SOURCES:.s=.o) $(S_SOURCES:.S=.o) $(C_SOURCES:.c=.o) $(CXX_SOURCES:.cpp=.o)
STACK_USAGE_OBJ = $(C_SOURCES:.c=.su) $(CXX_SOURCES:.cpp=.su) STACK_USAGE_OBJ = $(C_SOURCES:.c=.su) $(CXX_SOURCES:.cpp=.su)
GCNO_OBJ = $(C_SOURCES:.c=.gcno) $(CXX_SOURCES:.cpp=.gcno) GCNO_OBJ = $(C_SOURCES:.c=.gcno) $(CXX_SOURCES:.cpp=.gcno)
@@ -134,7 +134,7 @@ else ifeq ($(OSARCH), aarch64)
endif endif
$(__CONF_NM) $@ $(__CONF_NM) $@
%.o: %.bmp %.o: %.png
ifeq ($(OSARCH), amd64) ifeq ($(OSARCH), amd64)
$(__CONF_OBJCOPY) -O elf64-x86-64 -I binary $< $@ $(__CONF_OBJCOPY) -O elf64-x86-64 -I binary $< $@
else ifeq ($(OSARCH), i386) else ifeq ($(OSARCH), i386)

View File

@@ -442,7 +442,7 @@ namespace APIC
this->lapic->Write(APIC_TICR, 0xFFFFFFFF); this->lapic->Write(APIC_TICR, 0xFFFFFFFF);
} }
TimeManager->Sleep(1, Time::Units::Milliseconds); TimeManager->Sleep(Time::FromMilliseconds(1));
// Mask the timer // Mask the timer
if (this->lapic->x2APIC) if (this->lapic->x2APIC)

View File

@@ -167,14 +167,14 @@ namespace SMP
} }
apic->SendInitIPI(lapic->APICId); apic->SendInitIPI(lapic->APICId);
TimeManager->Sleep(20, Time::Units::Milliseconds); TimeManager->Sleep(Time::FromMilliseconds(20));
apic->SendStartupIPI(lapic->APICId, TRAMPOLINE_START); apic->SendStartupIPI(lapic->APICId, TRAMPOLINE_START);
debug("Waiting for CPU %d to load...", lapic->APICId); 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) while (CPUEnabled.load(std::memory_order_acquire) == false)
{ {
if (TimeManager->GetCounter() > Timeout) if (TimeManager->GetTimeNs() > Timeout)
{ {
error("CPU %d failed to load!", lapic->APICId); error("CPU %d failed to load!", lapic->APICId);
KPrint("\x1b[1;37;41mCPU %d failed to load!", KPrint("\x1b[1;37;41mCPU %d failed to load!",

View File

@@ -368,7 +368,7 @@ namespace APIC
this->lapic->Write(APIC_TDCR, DivideBy128); this->lapic->Write(APIC_TDCR, DivideBy128);
else else
this->lapic->Write(APIC_TDCR, DivideBy16); 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)); 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_TDCR, Divider);
this->lapic->Write(APIC_TICR, 0xFFFFFFFF); this->lapic->Write(APIC_TICR, 0xFFFFFFFF);
TimeManager->Sleep(1, Time::Units::Milliseconds); TimeManager->Sleep(Time::FromMilliseconds(1));
// Mask the timer // Mask the timer
this->lapic->Write(APIC_TIMER, 0x10000 /* LVTTimer.Mask flag */); this->lapic->Write(APIC_TIMER, 0x10000 /* LVTTimer.Mask flag */);

View File

@@ -25,6 +25,8 @@
#include "../kernel.h" #include "../kernel.h"
using namespace std::chrono_literals;
namespace KernelConsole namespace KernelConsole
{ {
static int TermColors[] = { static int TermColors[] = {

View File

@@ -273,7 +273,7 @@ namespace v0
{ {
dbg_api("%d, %d", DriverID, Milliseconds); dbg_api("%d, %d", DriverID, Milliseconds);
TaskManager->Sleep(Milliseconds); TaskManager->Sleep(Time::FromMilliseconds(Milliseconds));
} }
/* --------- */ /* --------- */

View File

@@ -302,7 +302,7 @@ namespace Interrupts
} }
else else
fixme("APIC not found for core %d", Core); fixme("APIC not found for core %d", Core);
// TODO: Handle PIC too // TODO: Handle PIC too
#endif #endif
assert(!"EOI not handled."); assert(!"EOI not handled.");
@@ -423,11 +423,10 @@ namespace Interrupts
{ {
for (auto ev : RegisteredEvents) for (auto ev : RegisteredEvents)
{ {
if (ev.IRQ == InterruptNumber) if (ev.IRQ != InterruptNumber)
{ continue;
warn("IRQ%d is already registered.",
InterruptNumber); warn("IRQ%d is already registered.", InterruptNumber);
}
} }
this->InterruptNumber = InterruptNumber; this->InterruptNumber = InterruptNumber;
@@ -441,15 +440,12 @@ namespace Interrupts
0, /* Priority */ 0, /* Priority */
Critical}; /* Critical */ Critical}; /* Critical */
RegisteredEvents.push_back(newEvent); RegisteredEvents.push_back(newEvent);
debug("Registered interrupt handler for IRQ%d.", debug("Registered interrupt handler for IRQ%d.", InterruptNumber);
InterruptNumber);
} }
Handler::Handler(PCI::PCIDevice Device, bool Critical) Handler::Handler(PCI::PCIDevice Device, bool Critical)
: Handler(((PCI::PCIHeader0 *)Device.Header)->InterruptLine, Critical)
{ {
PCI::PCIHeader0 *hdr0 =
(PCI::PCIHeader0 *)Device.Header;
Handler(hdr0->InterruptLine, Critical);
} }
Handler::Handler() Handler::Handler()
@@ -459,16 +455,14 @@ namespace Interrupts
Handler::~Handler() Handler::~Handler()
{ {
debug("Unregistering interrupt handler for IRQ%d.", debug("Unregistering interrupt handler for IRQ%d.", this->InterruptNumber);
this->InterruptNumber);
forItr(itr, RegisteredEvents) forItr(itr, RegisteredEvents)
{ {
if (itr->IRQ == this->InterruptNumber) if (itr->IRQ != this->InterruptNumber)
{ continue;
RegisteredEvents.erase(itr);
return; RegisteredEvents.erase(itr);
} return;
} }
warn("Event %d not found.", this->InterruptNumber); warn("Event %d not found.", this->InterruptNumber);
} }

View File

@@ -195,7 +195,7 @@ void LockClass::TimeoutDeadLock(SpinLockData &Lock, uint64_t Timeout)
if (CoreData != nullptr) if (CoreData != nullptr)
CCore = CoreData->ID; 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).", 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", Lock.AttemptingToGet, Lock.CurrentHolder, Lock.Count, Lock.Count > 1 ? "locks" : "lock",
@@ -235,8 +235,7 @@ Retry:
if (i >= DEADLOCK_TIMEOUT) if (i >= DEADLOCK_TIMEOUT)
{ {
if (Target.load() == 0) if (Target.load() == 0)
Target.store(TimeManager->CalculateTarget(Timeout, Target.store(TimeManager->GetTimeNs() + Timeout);
Time::Units::Milliseconds));
TimeoutDeadLock(LockData, Target.load()); TimeoutDeadLock(LockData, Target.load());
goto Retry; goto Retry;
} }

View File

@@ -58,7 +58,7 @@ nsa bool CrashXHCIKeyboardDriver::TakeOwnership()
return true; return true;
exCap->USBLEGSUP.OSOwnsHC = 1; exCap->USBLEGSUP.OSOwnsHC = 1;
TimeManager->Sleep(200, Time::Milliseconds); TimeManager->Sleep(Time::FromMilliseconds(200));
if (exCap->USBLEGSUP.BIOSOwnsHC == 0) if (exCap->USBLEGSUP.BIOSOwnsHC == 0)
return true; return true;

View File

@@ -897,8 +897,7 @@ namespace PCI
BAR[4] = hdr0->BAR4; BAR[4] = hdr0->BAR4;
BAR[5] = hdr0->BAR5; BAR[5] = hdr0->BAR5;
debug("Type: %d; IOBase: %#lx; MemoryBase: %#lx", debug("Type: %d; IOBase: %#lx; MemoryBase: %#lx", BAR[0] & 1, BAR[1] & (~3), BAR[0] & (~15));
BAR[0] & 1, BAR[1] & (~3), BAR[0] & (~15));
/* BARs Size */ /* BARs Size */
for (short i = 0; i < 6; i++) for (short i = 0; i < 6; i++)
@@ -927,7 +926,7 @@ namespace PCI
size = size & UINT32_MAX; size = size & UINT32_MAX;
if (size == 0) if (size == 0)
{ {
warn("BAR%d size is zero! Device: %#x:%#x", i, Device.Header->VendorID, Device.Header->DeviceID); warn("MEM BAR%d size is zero! Device: %#x:%#x", i, Device.Header->VendorID, Device.Header->DeviceID);
size++; size++;
} }
BARsSize[i] = size; BARsSize[i] = size;
@@ -943,7 +942,7 @@ namespace PCI
size = size & UINT16_MAX; size = size & UINT16_MAX;
if (size == 0) if (size == 0)
{ {
warn("BAR%d size is zero! Device: %#x:%#x", i, Device.Header->VendorID, Device.Header->DeviceID); warn("I/O BAR%d size is zero! Device: %#x:%#x", i, Device.Header->VendorID, Device.Header->DeviceID);
size++; size++;
} }
BARsSize[i] = size; BARsSize[i] = size;
@@ -972,10 +971,7 @@ namespace PCI
uintptr_t BARBase = BAR[i] & (~3); uintptr_t BARBase = BAR[i] & (~3);
size_t BARSize = BARsSize[i]; size_t BARSize = BARsSize[i];
debug("Mapping BAR%d %#x-%#x", i, BARBase, BARBase + BARSize); debug("no need to map BAR%d %#x-%#x as it's an I/O space", i, BARBase, BARBase + BARSize);
Memory::Virtual(Table).Map((void *)BARBase, (void *)BARBase,
BARSize, Memory::RW | Memory::PWT | Memory::PCD);
} }
} }
break; break;

View File

@@ -59,7 +59,7 @@ extern "C" uintptr_t SystemCallsHandler(SyscallsFrame *Frame)
and switch back when this function returns. */ and switch back when this function returns. */
AutoSwitchPageTable PageSwitcher; AutoSwitchPageTable PageSwitcher;
uint64_t _ctime = TimeManager->GetCounter(); uint64_t _ctime = TimeManager->GetTimeNs();
Tasking::TaskInfo *Ptinfo = &thisProcess->Info; Tasking::TaskInfo *Ptinfo = &thisProcess->Info;
Tasking::TaskInfo *Ttinfo = &thisThread->Info; Tasking::TaskInfo *Ttinfo = &thisThread->Info;
uintptr_t ret; uintptr_t ret;
@@ -97,7 +97,7 @@ extern "C" uintptr_t SystemCallsHandler(SyscallsFrame *Frame)
} }
Ret: Ret:
Ptinfo->KernelTime += TimeManager->GetCounter() - _ctime; Ptinfo->KernelTime += TimeManager->GetTimeNs() - _ctime;
Ttinfo->KernelTime += TimeManager->GetCounter() - _ctime; Ttinfo->KernelTime += TimeManager->GetTimeNs() - _ctime;
return ret; return ret;
} }

View File

@@ -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 <https://www.gnu.org/licenses/>.
*/
#include <time.hpp>
#include <memory.hpp>
#include <acpi.hpp>
#include <debug.h>
#include <io.h>
#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()
{
}
}

View File

@@ -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 <https://www.gnu.org/licenses/>.
*/
#include <time.hpp>
#include <memory.hpp>
#include <acpi.hpp>
#include <debug.h>
#include <io.h>
#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()
{
}
}

View File

@@ -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 <https://www.gnu.org/licenses/>.
*/
#include <time.hpp>
#include <memory.hpp>
#include <acpi.hpp>
#include <debug.h>
#include <io.h>
#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()
{
}
}

BIN
Kernel/files/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

View File

@@ -0,0 +1,202 @@
/*
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 <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <types.h>
#include <debug.h>
#if defined(__amd64__)
typedef uint64_t cpuid_t;
#elif defined(__i386__)
typedef uint32_t cpuid_t;
#else
typedef uint64_t cpuid_t;
#endif // __amd64__ || __i386__
#if defined(__amd64__) || defined(__i386__)
#define __kvm_cpuid_init(leaf) \
CPUID##leaf() \
{ \
asmv("cpuid" : "=a"(EAX.raw), "=b"(EBX.raw), \
"=c"(ECX.raw), "=d"(EDX.raw) : "a"(leaf)); \
if (!EAX.raw && !EBX.raw && !ECX.raw && !EDX.raw) \
warn("cpuid not supported"); \
}
#define __kvm_cpuid_init2(leaf, leaf2, suffix) \
CPUID##leaf##suffix() \
{ \
asmv("cpuid" : "=a"(EAX.raw), "=b"(EBX.raw), \
"=c"(ECX.raw), "=d"(EDX.raw) : "a"(leaf), "c"(leaf2)); \
if (!EAX.raw && !EBX.raw && !ECX.raw && !EDX.raw) \
warn("cpuid not supported"); \
}
#else
#define __kvm_cpuid_init(leaf) \
CPUID##leaf() \
{ \
}
#define __kvm_cpuid_init2(leaf, leaf2, suffix) \
CPUID##leaf##suffix() \
{ \
}
#endif
namespace CPU
{
namespace x86
{
namespace KVM
{
/* KVM_CPUID_SIGNATURE */
struct CPUID0x40000000
{
__kvm_cpuid_init(0x40000000);
union
{
struct
{
uint32_t MaximumFunction : 32;
};
cpuid_t raw;
} EAX;
union
{
struct
{
char Vendor[4];
};
cpuid_t raw;
} EBX;
union
{
struct
{
char Vendor[4];
};
cpuid_t raw;
} ECX;
union
{
struct
{
char Vendor[4];
};
cpuid_t raw;
} EDX;
};
/* KVM_CPUID_FEATURES */
struct CPUID0x40000001
{
__kvm_cpuid_init(0x40000001);
union
{
struct
{
/** kvmclock available at msrs 0x11 and 0x12 */
uint32_t KVM_FEATURE_CLOCKSOURCE : 1;
/** not necessary to perform delays on PIO operations */
uint32_t KVM_FEATURE_NOP_IO_DELAY : 1;
/** deprecated */
uint32_t KVM_FEATURE_MMU_OP : 1;
/** kvmclock available at msrs 0x4b564d00 and 0x4b564d01 */
uint32_t KVM_FEATURE_CLOCKSOURCE2 : 1;
/** async pf can be enabled by writing to msr 0x4b564d02 */
uint32_t KVM_FEATURE_ASYNC_PF : 1;
/** steal time can be enabled by writing to msr 0x4b564d03 */
uint32_t KVM_FEATURE_STEAL_TIME : 1;
/** paravirtualized end of interrupt handler can be enabled by writing to msr 0x4b564d04 */
uint32_t KVM_FEATURE_PV_EOI : 1;
/** guest checks this feature bit before enabling paravirtualized spinlock support */
uint32_t KVM_FEATURE_PV_UNHAULT : 1;
uint32_t _reserved8 : 1;
/** guest checks this feature bit before enabling paravirtualized tlb flush */
uint32_t KVM_FEATURE_PV_TLB_FLUSH : 1;
/** paravirtualized async PF VM EXIT can be enabled by setting bit 2 when writing to msr 0x4b564d02 */
uint32_t KVM_FEATURE_ASYNC_PF_VMEXIT : 1;
/** guest checks this feature bit before enabling paravirtualized send IPIs */
uint32_t KVM_FEATURE_PV_SEND_IPI : 1;
/** host-side polling on HLT can be disabled by writing to msr 0x4b564d05 */
uint32_t KVM_FEATURE_PV_POLL_CONTROL : 1;
/** guest checks this feature bit before using paravirtualized sched yield */
uint32_t KVM_FEATURE_PV_SCHED_YIELD : 1;
uint32_t __reserved14_23 : 10;
/** host will warn if no guest-side per-cpu warps are expected in kvmclock */
uint32_t KVM_FEATURE_CLOCKSOURCE_STABLE_BIT : 1;
uint32_t __reserved25_31 : 7;
};
cpuid_t raw;
} EAX;
union
{
struct
{
uint32_t __reserved0_31;
};
cpuid_t raw;
} EBX;
union
{
struct
{
uint32_t __reserved0_31;
};
cpuid_t raw;
} ECX;
union
{
struct
{
/** guest checks this feature bit to determine that vCPUs are never preempted for an unlimited time allowing optimizations */
uint32_t KVM_HINTS_REALTIME : 1;
};
cpuid_t raw;
} EDX;
};
}
}
}
#undef __kvm_cpuid_init
#undef __kvm_cpuid_init2

View File

@@ -626,11 +626,11 @@ namespace Tasking
void WaitForThreadStatus(TCB *tcb, TaskState State); 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, PCB *CreateProcess(PCB *Parent,
const char *Name, const char *Name,

View File

@@ -21,9 +21,24 @@
#include <types.h> #include <types.h>
#include <debug.h> #include <debug.h>
#include <cassert> #include <cassert>
#include <vector>
namespace Time 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 struct Clock
{ {
int Year, Month, Day, Hour, Minute, Second; int Year, Month, Day, Hour, Minute, Second;
@@ -33,138 +48,148 @@ namespace Time
Clock ReadClock(); Clock ReadClock();
Clock ConvertFromUnix(int Timestamp); 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, public:
Picoseconds, const char *Name() const override { return "PIT"; }
Nanoseconds, bool IsAvailable() const override;
Microseconds, bool SupportsNanoseconds() const override { return false; }
Milliseconds,
Seconds, bool Sleep(uint64_t Nanoseconds) override;
Minutes, uint64_t GetNanoseconds() override { return 0; }
Hours,
Days, ProgrammableIntervalTimer();
Months, ~ProgrammableIntervalTimer();
Years
}; };
/** @deprecated this shouldn't be used */ class RealTimeClock : public ITimer
inline uint64_t ConvertUnit(const Units Unit)
{ {
switch (Unit) public:
{ const char *Name() const override { return "RTC"; }
case Femtoseconds: bool IsAvailable() const override;
return 1; bool SupportsNanoseconds() const override { return false; }
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");
}
}
class HighPrecisionEventTimer bool Sleep(uint64_t Nanoseconds) override;
uint64_t GetNanoseconds() override { return 0; }
RealTimeClock();
~RealTimeClock();
};
class HighPrecisionEventTimer : public ITimer
{ {
private: private:
struct HPET struct HPET
{ {
uint64_t GeneralCapabilities; uint64_t CapabilitiesID;
uint64_t Reserved0; uint64_t __reserved0;
uint64_t GeneralConfiguration; uint64_t Configuration;
uint64_t Reserved1; uint64_t __reserved1;
uint64_t GeneralIntStatus; uint64_t InterruptStatus;
uint64_t Reserved2; uint64_t __reserved2[25];
uint64_t Reserved3[24]; uint64_t MainCounter;
uint64_t MainCounterValue; uint64_t __reserved3;
uint64_t Reserved4;
}; };
uint32_t clk = 0; uint64_t Period = 0;
HPET *hpet = nullptr; HPET *hpet = nullptr;
uint64_t ClassCreationTime = 0;
public: public:
bool Sleep(size_t Duration, Units Unit); const char *Name() const override { return "HPET"; }
uint64_t GetCounter(); bool IsAvailable() const override { return hpet != nullptr; }
uint64_t CalculateTarget(uint64_t Target, Units Unit); bool SupportsNanoseconds() const override { return true; }
uint64_t GetNanosecondsSinceClassCreation(); bool Sleep(uint64_t Nanoseconds) override;
uint64_t GetNanoseconds() override;
HighPrecisionEventTimer(void *hpet); HighPrecisionEventTimer(void *hpet);
~HighPrecisionEventTimer(); ~HighPrecisionEventTimer();
}; };
class TimeStampCounter class TimeStampCounter : public ITimer
{ {
private: private:
uint64_t clk = 0; uint64_t clk = 0;
uint64_t ClassCreationTime = 0;
public: public:
bool Sleep(size_t Duration, Units Unit); const char *Name() const override { return "TSC"; }
uint64_t GetCounter(); bool IsAvailable() const override { return clk != 0; }
uint64_t CalculateTarget(uint64_t Target, Units Unit); bool SupportsNanoseconds() const override { return true; }
uint64_t GetNanosecondsSinceClassCreation(); bool Sleep(uint64_t Nanoseconds) override;
uint64_t GetNanoseconds() override;
TimeStampCounter(); TimeStampCounter();
~TimeStampCounter(); ~TimeStampCounter() = default;
}; };
class time class KVMClock : public ITimer
{ {
public: private:
enum TimeActiveTimer struct kvm_clock_pairing
{ {
NONE = 0b0, int64_t sec;
RTC = 0b1, int64_t nsec;
PIT = 0b10, uint64_t tsc;
HPET = 0b100, uint32_t flags;
ACPI = 0b1000, uint32_t pad[9];
APIC = 0b10000,
TSC = 0b100000
}; };
private: struct pvclock_vcpu_time_info
int SupportedTimers = 0; {
TimeActiveTimer ActiveTimer = NONE; 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; struct ms_hyperv_tsc_page
TimeStampCounter *tsc; {
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: public:
int GetSupportedTimers() { return SupportedTimers; } const char *Name() const override { return "KVM"; }
TimeActiveTimer GetActiveTimer() { return ActiveTimer; } bool IsAvailable() const override { return clk != 0; }
bool ChangeActiveTimer(TimeActiveTimer Timer) bool SupportsNanoseconds() const override { return true; }
{ bool Sleep(uint64_t Nanoseconds) override;
if (!(SupportedTimers & Timer)) uint64_t GetNanoseconds() override;
return false;
ActiveTimer = Timer;
return true;
}
bool Sleep(size_t Duration, Units Unit); KVMClock();
uint64_t GetCounter(); ~KVMClock();
uint64_t CalculateTarget(uint64_t Target, Units Unit); };
uint64_t GetNanosecondsSinceClassCreation();
void FindTimers(void *acpi); class Manager
time(); {
~time(); private:
void *acpi = nullptr;
std::vector<ITimer *> 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;
}; };
} }

View File

@@ -245,7 +245,14 @@ public:
T operator->() { return ptr; } T operator->() { return ptr; }
T operator*() { return *ptr; } T operator*() { return *ptr; }
}; };
#endif // __cplusplus #endif
/** Usage: int timed_out = 0; whileto(cond, n, timed_out) { ... }
Loops while (cond) is true, up to n times, then breaks.
Sets timed_out to 1 if the loop exited due to timeout, 0 otherwise. */
#define whileto(cond, n, timed_out) \
for (int __whileto_count = 0, __whileto_break = 1; __whileto_break && (cond) && __whileto_count < (n); ++__whileto_count, __whileto_break = 1) \
for (; __whileto_break; __whileto_break = 0, timed_out = !((cond) && __whileto_count < (n)))
#define INT8_MAX __INT8_MAX__ #define INT8_MAX __INT8_MAX__
#define INT8_MIN (-INT8_MAX - 1) #define INT8_MIN (-INT8_MAX - 1)

View File

@@ -28,22 +28,25 @@ namespace std
class duration class duration
{ {
private: private:
Rep rep_; Rep _rep;
std::ratio<Period::num, Period::denom> period_; std::ratio<Period::num, Period::den> _period;
public: public:
using rep = Rep;
using period = Period;
constexpr duration() = default; constexpr duration() = default;
duration(const duration &) = default; duration(const duration &) = default;
template <class Rep2> template <class Rep2>
constexpr explicit duration(const Rep2 &r) { rep_ = r; } constexpr explicit duration(const Rep2 &r) { _rep = r; }
template <class Rep2, class Period2> template <class Rep2, class Period2>
constexpr duration(const duration<Rep2, Period2> &d); constexpr duration(const duration<Rep2, Period2> &d);
duration &operator=(const duration &other) = default; duration &operator=(const duration &other) = default;
constexpr Rep count() const { return rep_; } constexpr Rep count() const { return _rep; }
static constexpr duration zero() noexcept; static constexpr duration zero() noexcept;
static constexpr duration min() noexcept; static constexpr duration min() noexcept;
@@ -51,54 +54,54 @@ namespace std
constexpr std::common_type_t<duration> operator+() const; constexpr std::common_type_t<duration> operator+() const;
constexpr std::common_type_t<duration> operator-() const; constexpr std::common_type_t<duration> operator-() const;
constexpr duration operator++(int) { return duration(rep_++); } constexpr duration operator++(int) { return duration(_rep++); }
constexpr duration operator--(int) { return duration(rep_--); } constexpr duration operator--(int) { return duration(_rep--); }
constexpr duration &operator++() constexpr duration &operator++()
{ {
++rep_; ++_rep;
return *this; return *this;
} }
constexpr duration &operator--() constexpr duration &operator--()
{ {
--rep_; --_rep;
return *this; return *this;
} }
constexpr duration &operator+=(const duration &d) constexpr duration &operator+=(const duration &d)
{ {
rep_ += d.count(); _rep += d.count();
return *this; return *this;
} }
constexpr duration &operator-=(const duration &d) constexpr duration &operator-=(const duration &d)
{ {
rep_ -= d.count(); _rep -= d.count();
return *this; return *this;
} }
constexpr duration &operator*=(const Rep &rhs) constexpr duration &operator*=(const Rep &rhs)
{ {
rep_ *= rhs; _rep *= rhs;
return *this; return *this;
} }
constexpr duration &operator/=(const Rep &rhs) constexpr duration &operator/=(const Rep &rhs)
{ {
rep_ /= rhs; _rep /= rhs;
return *this; return *this;
} }
constexpr duration &operator%=(const Rep &rhs) constexpr duration &operator%=(const Rep &rhs)
{ {
rep_ %= rhs; _rep %= rhs;
return *this; return *this;
} }
constexpr duration &operator%=(const duration &rhs) constexpr duration &operator%=(const duration &rhs)
{ {
rep_ %= rhs.count(); _rep %= rhs.count();
return *this; return *this;
} }
}; };
@@ -120,7 +123,91 @@ namespace std
template <class ToDuration, class Rep, class Period> template <class ToDuration, class Rep, class Period>
constexpr ToDuration duration_cast(const std::chrono::duration<Rep, Period> &d) constexpr ToDuration duration_cast(const std::chrono::duration<Rep, Period> &d)
{ {
return ToDuration(d.count()); typedef typename ToDuration::rep ToRep;
typedef typename ToDuration::period ToPeriod;
typedef std::ratio_divide<Period, ToPeriod> CF;
typedef typename std::common_type<Rep, ToRep, intmax_t>::type CR;
CR cr_count = static_cast<CR>(d.count());
CR cr_num = static_cast<CR>(CF::num);
CR cr_den = static_cast<CR>(CF::den);
if constexpr (CF::num == 1 && CF::den == 1)
return ToDuration(static_cast<ToRep>(cr_count));
else if constexpr (CF::den == 1)
return ToDuration(static_cast<ToRep>(cr_count * cr_num));
else if constexpr (CF::num == 1)
return ToDuration(static_cast<ToRep>(cr_count / cr_den));
else
return ToDuration(static_cast<ToRep>(cr_count * cr_num / cr_den));
}
}
inline namespace literals
{
inline namespace chrono_literals
{
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wliteral-suffix"
constexpr std::chrono::hours operator""h(unsigned long long h)
{
return std::chrono::hours(h);
}
constexpr std::chrono::duration<long double, std::ratio<3600, 1>> operator""h(long double h)
{
return std::chrono::duration<long double, std::ratio<3600, 1>>(h);
}
constexpr std::chrono::minutes operator""min(unsigned long long m)
{
return std::chrono::minutes(m);
}
constexpr std::chrono::duration<long double, std::ratio<60, 1>> operator""min(long double m)
{
return std::chrono::duration<long double, std::ratio<60, 1>>(m);
}
constexpr std::chrono::seconds operator""s(unsigned long long s)
{
return std::chrono::seconds(s);
}
constexpr std::chrono::duration<long double> operator""s(long double s)
{
return std::chrono::duration<long double>(s);
}
constexpr std::chrono::milliseconds operator""ms(unsigned long long ms)
{
return std::chrono::milliseconds(ms);
}
constexpr std::chrono::duration<long double, std::milli> operator""ms(long double ms)
{
return std::chrono::duration<long double, std::milli>(ms);
}
constexpr std::chrono::microseconds operator""us(unsigned long long us)
{
return std::chrono::microseconds(us);
}
constexpr std::chrono::duration<long double, std::micro> operator""us(long double us)
{
return std::chrono::duration<long double, std::micro>(us);
}
constexpr std::chrono::nanoseconds operator""ns(unsigned long long ns)
{
return std::chrono::nanoseconds(ns);
}
constexpr std::chrono::duration<long double, std::nano> operator""ns(long double ns)
{
return std::chrono::duration<long double, std::nano>(ns);
}
#pragma GCC diagnostic pop
} }
} }
} }

View File

@@ -17,40 +17,173 @@
#pragma once #pragma once
#include <types.h> #include <type_traits>
namespace std namespace std
{ {
namespace detail
{
template <intmax_t A, intmax_t B>
struct GCD
{
static constexpr intmax_t value = GCD<B, A % B>::value;
};
template <intmax_t A>
struct GCD<A, 0>
{
static constexpr intmax_t value = A;
};
}
template <intmax_t Num, intmax_t Denom = 1> template <intmax_t Num, intmax_t Denom = 1>
class ratio class ratio
{ {
private:
static_assert(Denom != 0, "Denominator cannot be zero");
static_assert(Num >= -__INTMAX_MAX__ && Denom >= -__INTMAX_MAX__, "Overflow");
private:
static constexpr intmax_t __first = Num < 0 ? -Num : Num;
static constexpr intmax_t __second = Denom < 0 ? -Denom : Denom;
static constexpr intmax_t __gcd = detail::GCD<__first, __second>::value;
public: public:
typedef ratio<Num, Denom> type; typedef ratio<Num, Denom> type;
static constexpr intmax_t num = Num; static constexpr intmax_t num = Denom < 0 ? -Num / __gcd : Num / __gcd;
static constexpr intmax_t denom = Denom; static constexpr intmax_t den = Denom < 0 ? -Denom / __gcd : Denom / __gcd;
};
namespace detail
{
template <typename _Tp>
constexpr bool __ta_is_ratio = false;
template <intmax_t _Num, intmax_t _Den>
constexpr bool __ta_is_ratio<ratio<_Num, _Den>> = true;
template <class R1, class R2>
constexpr bool __is_equal()
{
return R1::num == R2::num && R1::den == R2::den;
}
template <class R1, class R2>
constexpr bool __is_less()
{
return R1::num * R2::den < R2::num * R1::den;
}
template <class R1, class R2>
constexpr bool __is_less_or_equal()
{
return R1::num * R2::den <= R2::num * R1::den;
}
template <class R1, class R2>
constexpr bool __is_greater()
{
return R1::num * R2::den > R2::num * R1::den;
}
template <class R1, class R2>
constexpr bool __is_greater_or_equal()
{
return R1::num * R2::den >= R2::num * R1::den;
}
}; };
template <class R1, class R2> template <class R1, class R2>
using ratio_add = ratio<R1::num * R2::denom + R2::num * R1::denom, R1::denom * R2::denom>; struct ratio_add
template <class R1, class R2> {
using ratio_subtract = ratio<R1::num * R2::denom - R2::num * R1::denom, R1::denom * R2::denom>; static_assert(detail::__ta_is_ratio<R1> && detail::__ta_is_ratio<R2>, "Both template arguments must be std::ratio");
template <class R1, class R2>
using ratio_multiply = ratio<R1::num * R2::num, R1::denom * R2::denom>; static constexpr intmax_t num = R1::num * R2::den + R2::num * R1::den;
template <class R1, class R2> static constexpr intmax_t den = R1::den * R2::den;
using ratio_divide = ratio<R1::num * R2::denom, R1::denom * R2::num>;
using type = ratio<num, den>;
};
template <class R1, class R2> template <class R1, class R2>
struct ratio_equal; struct ratio_subtract
{
static_assert(detail::__ta_is_ratio<R1> && detail::__ta_is_ratio<R2>, "Both template arguments must be std::ratio");
static constexpr intmax_t num = R1::num * R2::den - R2::num * R1::den;
static constexpr intmax_t den = R1::den * R2::den;
using type = ratio<num, den>;
};
template <class R1, class R2> template <class R1, class R2>
struct ratio_not_equal; struct ratio_multiply
{
static_assert(detail::__ta_is_ratio<R1> && detail::__ta_is_ratio<R2>, "Both templates must be ratios");
static constexpr intmax_t num = R1::num * R2::num;
static constexpr intmax_t den = R1::den * R2::den;
using type = ratio<num, den>;
};
template <class R1, class R2> template <class R1, class R2>
struct ratio_less; struct ratio_divide
{
static_assert(R2::num != 0, "Divide by zero");
static constexpr intmax_t num = R1::num * R2::den;
static constexpr intmax_t den = R1::den * R2::num;
using type = ratio<num, den>;
};
template <class R1, class R2> template <class R1, class R2>
struct ratio_less_equal; struct ratio_equal : public std::integral_constant<bool, detail::__is_equal<R1, R2>()>
{
};
template <class R1, class R2> template <class R1, class R2>
struct ratio_greater; struct ratio_not_equal : public std::integral_constant<bool, !detail::__is_equal<R1, R2>()>
{
};
template <class R1, class R2> template <class R1, class R2>
struct ratio_greater_equal; struct ratio_less : public std::integral_constant<bool, detail::__is_less<R1, R2>()>
{
};
template <class R1, class R2>
struct ratio_less_equal : public std::integral_constant<bool, detail::__is_less_or_equal<R1, R2>()>
{
};
template <class R1, class R2>
struct ratio_greater : public std::integral_constant<bool, detail::__is_greater<R1, R2>()>
{
};
template <class R1, class R2>
struct ratio_greater_equal : public std::integral_constant<bool, detail::__is_greater_or_equal<R1, R2>()>
{
};
template <class R1, class R2>
constexpr bool ratio_equal_v = ratio_equal<R1, R2>::value;
template <class R1, class R2>
constexpr bool ratio_not_equal_v = ratio_not_equal<R1, R2>::value;
template <class R1, class R2>
constexpr bool ratio_less_v = ratio_less<R1, R2>::value;
template <class R1, class R2>
constexpr bool ratio_less_equal_v = ratio_less_equal<R1, R2>::value;
template <class R1, class R2>
constexpr bool ratio_greater_v = ratio_greater<R1, R2>::value;
template <class R1, class R2>
constexpr bool ratio_greater_equal_v = ratio_greater_equal<R1, R2>::value;
// typedef ratio<1, 1000000000000000000000000000000> quecto; // typedef ratio<1, 1000000000000000000000000000000> quecto;
// typedef ratio<1, 1000000000000000000000000000> ronto; // typedef ratio<1, 1000000000000000000000000000> ronto;

View File

@@ -21,6 +21,7 @@
#include <task.hpp> #include <task.hpp>
#include <debug.h> #include <debug.h>
#include <smp.hpp> #include <smp.hpp>
#include <ostream>
#include <chrono> #include <chrono>
extern Tasking::Task *TaskManager; extern Tasking::Task *TaskManager;
@@ -84,17 +85,26 @@ namespace std
namespace this_thread namespace this_thread
{ {
thread::id get_id() noexcept;
void yield() noexcept; void yield() noexcept;
template <class Clock, class Duration> thread::id get_id() noexcept;
void sleep_until(const chrono::time_point<Clock, Duration> &abs_time);
template <class Rep, class Period> template <class Rep, class Period>
void sleep_for(const chrono::duration<Rep, Period> &rel_time) void sleep_for(const chrono::duration<Rep, Period> &sleep_duration)
{ {
TaskManager->Sleep(chrono::duration_cast<std::chrono::milliseconds>(rel_time).count()); TaskManager->Sleep(chrono::duration_cast<std::chrono::nanoseconds>(sleep_duration).count());
}
template <class Clock, class Duration>
void sleep_until(const std::chrono::time_point<Clock, Duration> &sleep_time)
{
TaskManager->Sleep(chrono::duration_cast<std::chrono::nanoseconds>(sleep_time - Clock::now()).count());
} }
} }
template <class CharT, class Traits>
std::basic_ostream<CharT, Traits> &operator<<(std::basic_ostream<CharT, Traits> &ost, std::thread::id id)
{
return ost << id;
}
} }

View File

@@ -57,7 +57,7 @@ struct KernelConfig Config = {
Video::Display *Display = nullptr; Video::Display *Display = nullptr;
SymbolResolver::Symbols *KernelSymbolTable = nullptr; SymbolResolver::Symbols *KernelSymbolTable = nullptr;
Power::Power *PowerManager = nullptr; Power::Power *PowerManager = nullptr;
Time::time *TimeManager = nullptr; Time::Manager *TimeManager = nullptr;
Tasking::Task *TaskManager = nullptr; Tasking::Task *TaskManager = nullptr;
PCI::Manager *PCIManager = nullptr; PCI::Manager *PCIManager = nullptr;
Driver::Manager *DriverManager = nullptr; Driver::Manager *DriverManager = nullptr;
@@ -76,14 +76,14 @@ EXTERNC void _KPrint(const char *Format, va_list Args)
{ {
SmartLock(KernelLock); SmartLock(KernelLock);
uint64_t nano = TimeManager ? TimeManager->GetNanosecondsSinceClassCreation() : 0; uint64_t nano = TimeManager ? TimeManager->GetTimeNs() : 0;
#if defined(__amd64__) #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__) #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__) #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 #endif
vprintf(Format, Args); vprintf(Format, Args);
@@ -232,8 +232,8 @@ EXTERNC nif cold void Main()
#endif #endif
KPrint("Initializing Timers"); KPrint("Initializing Timers");
TimeManager = new Time::time; TimeManager = new Time::Manager(PowerManager->GetACPI());
TimeManager->FindTimers(PowerManager->GetACPI()); TimeManager->InitializeTimers();
KPrint("Initializing PCI Manager"); KPrint("Initializing PCI Manager");
PCIManager = new PCI::Manager; PCIManager = new PCI::Manager;
@@ -388,12 +388,6 @@ EXTERNC __no_stack_protector void BeforeShutdown(bool Reboot)
if (fs) if (fs)
delete fs, fs = nullptr; 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 // https://wiki.osdev.org/Calling_Global_Constructors
KPrint("Calling destructors"); KPrint("Calling destructors");
for (CallPtr *fct = __fini_array_start; fct != __fini_array_end; fct++) for (CallPtr *fct = __fini_array_start; fct != __fini_array_end; fct++)

View File

@@ -48,7 +48,7 @@ extern bool DebuggerIsAttached;
extern Video::Display *Display; extern Video::Display *Display;
extern SymbolResolver::Symbols *KernelSymbolTable; extern SymbolResolver::Symbols *KernelSymbolTable;
extern Power::Power *PowerManager; extern Power::Power *PowerManager;
extern Time::time *TimeManager; extern Time::Manager *TimeManager;
extern PCI::Manager *PCIManager; extern PCI::Manager *PCIManager;
extern vfs::Virtual *fs; extern vfs::Virtual *fs;
extern Tasking::Task *TaskManager; extern Tasking::Task *TaskManager;

View File

@@ -169,7 +169,7 @@ Exit:
ExitCode, ExitCode < 0 ? -ExitCode : ExitCode); ExitCode, ExitCode < 0 ? -ExitCode : ExitCode);
KPrint("Dropping to kernel shell"); KPrint("Dropping to kernel shell");
TaskManager->Sleep(1000); TaskManager->Sleep(Time::FromMilliseconds(1000));
TaskManager->CreateThread(thisProcess, Tasking::IP(KShellThread))->Rename("Kernel Shell"); TaskManager->CreateThread(thisProcess, Tasking::IP(KShellThread))->Rename("Kernel Shell");
CPU::Halt(true); CPU::Halt(true);
} }

View File

@@ -28,7 +28,7 @@ void cmd_uptime(const char *)
if (TimeManager) if (TimeManager)
{ {
uint64_t Nanoseconds = uint64_t Nanoseconds =
TimeManager->GetNanosecondsSinceClassCreation(); TimeManager->GetTimeNs();
uint64_t Seconds = Nanoseconds / 10000000; uint64_t Seconds = Nanoseconds / 10000000;
uint64_t Minutes = Seconds / 60; uint64_t Minutes = Seconds / 60;
uint64_t Hours = Minutes / 60; uint64_t Hours = Minutes / 60;

View File

@@ -1389,10 +1389,10 @@ static int linux_nanosleep(SysFrm *,
pReq->tv_nsec, pReq->tv_sec); pReq->tv_nsec, pReq->tv_sec);
uint64_t nanoTime = pReq->tv_nsec; 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 time = TimeManager->GetTimeNs();
uint64_t sleepTime = TimeManager->CalculateTarget(nanoTime + secTime, Time::Nanoseconds); uint64_t sleepTime = TimeManager->GetTimeNs() + secTime + nanoTime;
debug("time=%ld secTime=%ld nanoTime=%ld sleepTime=%ld", debug("time=%ld secTime=%ld nanoTime=%ld sleepTime=%ld",
time, secTime, nanoTime, sleepTime); time, secTime, nanoTime, sleepTime);
@@ -1406,7 +1406,7 @@ static int linux_nanosleep(SysFrm *,
} }
pcb->GetContext()->Yield(); pcb->GetContext()->Yield();
time = TimeManager->GetCounter(); time = TimeManager->GetTimeNs();
} }
debug("time= %ld", time); debug("time= %ld", time);
debug("sleepTime=%ld", sleepTime); debug("sleepTime=%ld", sleepTime);
@@ -2582,7 +2582,7 @@ static int linux_sysinfo(SysFrm *, struct sysinfo *info)
if (pInfo == nullptr) if (pInfo == nullptr)
return -linux_EFAULT; return -linux_EFAULT;
uint64_t nano = TimeManager->GetNanosecondsSinceClassCreation(); uint64_t nano = TimeManager->GetTimeNs();
if (nano != 0) if (nano != 0)
nano /= 10000000; nano /= 10000000;
@@ -3185,18 +3185,18 @@ static int linux_clock_gettime(SysFrm *, clockid_t clockid, struct timespec *tp)
{ {
case linux_CLOCK_REALTIME: case linux_CLOCK_REALTIME:
{ {
uint64_t time = TimeManager->GetCounter(); uint64_t time = TimeManager->GetTimeNs();
pTp->tv_sec = time / Time::ConvertUnit(Time::Seconds); pTp->tv_sec = Time::ToSeconds(time);
pTp->tv_nsec = time / Time::ConvertUnit(Time::Nanoseconds); pTp->tv_nsec = time;
debug("time=%ld sec=%ld nsec=%ld", debug("time=%ld sec=%ld nsec=%ld",
time, pTp->tv_sec, pTp->tv_nsec); time, pTp->tv_sec, pTp->tv_nsec);
break; break;
} }
case linux_CLOCK_MONOTONIC: case linux_CLOCK_MONOTONIC:
{ {
uint64_t time = TimeManager->GetCounter(); uint64_t time = TimeManager->GetTimeNs();
pTp->tv_sec = time / Time::ConvertUnit(Time::Seconds); pTp->tv_sec = Time::ToSeconds(time);
pTp->tv_nsec = time / Time::ConvertUnit(Time::Nanoseconds); pTp->tv_nsec = time;
debug("time=%ld sec=%ld nsec=%ld", debug("time=%ld sec=%ld nsec=%ld",
time, pTp->tv_sec, pTp->tv_nsec); time, pTp->tv_sec, pTp->tv_nsec);
break; break;
@@ -3244,9 +3244,8 @@ static int linux_clock_nanosleep(SysFrm *, clockid_t clockid, int flags,
case linux_CLOCK_REALTIME: case linux_CLOCK_REALTIME:
case linux_CLOCK_MONOTONIC: case linux_CLOCK_MONOTONIC:
{ {
uint64_t time = TimeManager->GetCounter(); uint64_t time = TimeManager->GetTimeNs();
uint64_t rqTime = pRequest->tv_sec * Time::ConvertUnit(Time::Seconds) + uint64_t rqTime = Time::FromSeconds(pRequest->tv_sec) + pRequest->tv_nsec;
pRequest->tv_nsec * Time::ConvertUnit(Time::Nanoseconds);
debug("Sleeping for %ld", rqTime - time); debug("Sleeping for %ld", rqTime - time);
if (rqTime > time) if (rqTime > time)

View File

@@ -0,0 +1,94 @@
/*
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 <https://www.gnu.org/licenses/>.
*/
#include <time.hpp>
#include <memory.hpp>
#include <acpi.hpp>
#include <debug.h>
#include <io.h>
#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<HPET *>(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
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
}
}

View File

@@ -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 <https://www.gnu.org/licenses/>.
*/
#include <time.hpp>
#include <memory.hpp>
#include <acpi.hpp>
#include <debug.h>
#include <io.h>
#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()
{
}
}

View File

@@ -0,0 +1,106 @@
/*
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 <https://www.gnu.org/licenses/>.
*/
#include <time.hpp>
#include <memory.hpp>
#include <acpi.hpp>
#include <debug.h>
#include <io.h>
#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();
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) {}
}

View File

@@ -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 <https://www.gnu.org/licenses/>.
*/
#include <time.hpp>
#include <memory.hpp>
#include <acpi.hpp>
#include <debug.h>
#include <io.h>
#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 */
}
}

View File

@@ -251,7 +251,7 @@ namespace Tasking
this->AllocatedMemory += sizeof(Memory::ProgramBreak); this->AllocatedMemory += sizeof(Memory::ProgramBreak);
this->AllocatedMemory += sizeof(SymbolResolver::Symbols); this->AllocatedMemory += sizeof(SymbolResolver::Symbols);
this->Info.SpawnTime = TimeManager->GetCounter(); this->Info.SpawnTime = TimeManager->GetTimeNs();
if (Parent) if (Parent)
Parent->Children.push_back(this); Parent->Children.push_back(this);

View File

@@ -290,7 +290,7 @@ namespace Tasking::Scheduler
hot nsa void Custom::UpdateUsage(TaskInfo *Info, TaskExecutionMode Mode, int Core) hot nsa void Custom::UpdateUsage(TaskInfo *Info, TaskExecutionMode Mode, int Core)
{ {
UNUSED(Core); UNUSED(Core);
uint64_t CurrentTime = TimeManager->GetCounter(); uint64_t CurrentTime = TimeManager->GetTimeNs();
uint64_t TimePassed = Info->LastUpdateTime - CurrentTime; uint64_t TimePassed = Info->LastUpdateTime - CurrentTime;
Info->LastUpdateTime = CurrentTime; Info->LastUpdateTime = CurrentTime;
@@ -529,7 +529,7 @@ namespace Tasking::Scheduler
continue; continue;
/* Check if the thread is ready to wake up. */ /* 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) if (pState == TaskState::Sleeping)
process->State.store(TaskState::Ready); process->State.store(TaskState::Ready);
@@ -541,7 +541,7 @@ namespace Tasking::Scheduler
else else
{ {
wut_schedbg("Thread \"%s\"(%d) is not ready to wake up. (SleepUntil: %d, Counter: %d)", 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; return;
} }
bool ProcessNotChanged = false; bool ProcessNotChanged = false;
uint64_t SchedTmpTicks = TimeManager->GetCounter(); uint64_t SchedTmpTicks = TimeManager->GetTimeNs();
this->LastTaskTicks.store(size_t(SchedTmpTicks - this->SchedulerTicks.load())); this->LastTaskTicks.store(size_t(SchedTmpTicks - this->SchedulerTicks.load()));
CPUData *CurrentCPU = GetCurrentCPU(); CPUData *CurrentCPU = GetCurrentCPU();
this->LastCore.store(CurrentCPU->ID); this->LastCore.store(CurrentCPU->ID);
@@ -621,7 +621,7 @@ namespace Tasking::Scheduler
CurrentCPU->CurrentProcess->State.store(TaskState::Running); CurrentCPU->CurrentProcess->State.store(TaskState::Running);
CurrentCPU->CurrentThread->State.store(TaskState::Running); CurrentCPU->CurrentThread->State.store(TaskState::Running);
*Frame = CurrentCPU->CurrentThread->Registers; *Frame = CurrentCPU->CurrentThread->Registers;
this->SchedulerTicks.store(size_t(TimeManager->GetCounter() - SchedTmpTicks)); this->SchedulerTicks.store(size_t(TimeManager->GetTimeNs() - SchedTmpTicks));
return; return;
} }
@@ -696,8 +696,8 @@ namespace Tasking::Scheduler
CurrentCPU->CurrentProcess->Signals.HandleSignal(Frame, CurrentCPU->CurrentThread.load()); CurrentCPU->CurrentProcess->Signals.HandleSignal(Frame, CurrentCPU->CurrentThread.load());
if (!ProcessNotChanged) if (!ProcessNotChanged)
(&CurrentCPU->CurrentProcess->Info)->LastUpdateTime = TimeManager->GetCounter(); (&CurrentCPU->CurrentProcess->Info)->LastUpdateTime = TimeManager->GetTimeNs();
(&CurrentCPU->CurrentThread->Info)->LastUpdateTime = TimeManager->GetCounter(); (&CurrentCPU->CurrentThread->Info)->LastUpdateTime = TimeManager->GetTimeNs();
this->OneShot(CurrentCPU->CurrentThread->Info.Priority); this->OneShot(CurrentCPU->CurrentThread->Info.Priority);
if (CurrentCPU->CurrentThread->Security.IsDebugEnabled && if (CurrentCPU->CurrentThread->Security.IsDebugEnabled &&
@@ -720,7 +720,7 @@ namespace Tasking::Scheduler
#endif #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) hot nsa nif void Custom::OnInterruptReceived(CPU::SchedulerFrame *Frame)
@@ -754,7 +754,7 @@ namespace Tasking::Scheduler
} }
debug("Waiting for processes to terminate"); 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) while (this->GetProcessList().size() > 0)
{ {
trace("Waiting for %d processes to terminate", this->GetProcessList().size()); trace("Waiting for %d processes to terminate", this->GetProcessList().size());
@@ -780,7 +780,7 @@ namespace Tasking::Scheduler
ctx->GetCurrentProcess()->Name, ctx->GetCurrentProcess()->Name,
ctx->GetCurrentProcess()->ID); ctx->GetCurrentProcess()->ID);
if (TimeManager->GetCounter() > timeout) if (TimeManager->GetTimeNs() > timeout)
{ {
error("Timeout waiting for processes to terminate"); error("Timeout waiting for processes to terminate");
break; break;

View File

@@ -167,7 +167,7 @@ namespace Tasking
this->Yield(); this->Yield();
} }
void Task::Sleep(uint64_t Milliseconds, bool NoSwitch) void Task::Sleep(uint64_t Nanoseconds, bool NoSwitch)
{ {
TCB *thread = this->GetCurrentThread(); TCB *thread = this->GetCurrentThread();
PCB *process = thread->Parent; PCB *process = thread->Parent;
@@ -179,13 +179,11 @@ namespace Tasking
if (process->Threads.size() == 1) if (process->Threads.size() == 1)
process->SetState(TaskState::Sleeping); process->SetState(TaskState::Sleeping);
thread->Info.SleepUntil = thread->Info.SleepUntil = TimeManager->GetTimeNs() + Nanoseconds;
TimeManager->CalculateTarget(Milliseconds,
Time::Units::Milliseconds);
} }
// #ifdef DEBUG // #ifdef DEBUG
// uint64_t TicksNow = TimeManager->GetCounter(); // uint64_t TicksNow = TimeManager->GetTimeNs();
// #endif // #endif
// debug("Thread \"%s\"(%d) is going to sleep until %llu, current %llu, diff %llu", // debug("Thread \"%s\"(%d) is going to sleep until %llu, current %llu, diff %llu",
// thread->Name, thread->ID, thread->Info.SleepUntil, // thread->Name, thread->ID, thread->Info.SleepUntil,

View File

@@ -650,7 +650,7 @@ namespace Tasking
this->AllocatedMemory += strlen(this->Parent->Name) + 1; this->AllocatedMemory += strlen(this->Parent->Name) + 1;
this->AllocatedMemory += sizeof(Memory::StackGuard); this->AllocatedMemory += sizeof(Memory::StackGuard);
this->Info.SpawnTime = TimeManager->GetCounter(); this->Info.SpawnTime = TimeManager->GetTimeNs();
this->Parent->Threads.push_back(this); this->Parent->Threads.push_back(this);
if (this->Parent->Threads.size() == 1 && if (this->Parent->Threads.size() == 1 &&

View File

@@ -70,23 +70,23 @@ void readdir_sanity_tests()
KPrint("TEST /"); KPrint("TEST /");
TestReadDirectory(t0); TestReadDirectory(t0);
TaskManager->Sleep(2000); TaskManager->Sleep(Time::FromMilliseconds(2000));
KPrint("TEST /dev"); KPrint("TEST /dev");
TestReadDirectory(t1); TestReadDirectory(t1);
TaskManager->Sleep(2000); TaskManager->Sleep(Time::FromMilliseconds(2000));
KPrint("TEST /home"); KPrint("TEST /home");
TestReadDirectory(t2); TestReadDirectory(t2);
TaskManager->Sleep(2000); TaskManager->Sleep(Time::FromMilliseconds(2000));
KPrint("TEST /var"); KPrint("TEST /var");
TestReadDirectory(t3); TestReadDirectory(t3);
TaskManager->Sleep(2000); TaskManager->Sleep(Time::FromMilliseconds(2000));
KPrint("TEST /tmp"); KPrint("TEST /tmp");
TestReadDirectory(t4); TestReadDirectory(t4);
TaskManager->Sleep(2000); TaskManager->Sleep(Time::FromMilliseconds(2000));
CPU::Stop(); CPU::Stop();
} }

View File

@@ -29,7 +29,7 @@ void TaskHeartbeat()
while (true) while (true)
{ {
debug("Task Heartbeat"); debug("Task Heartbeat");
TaskManager->Sleep(5000); TaskManager->Sleep(Time::FromMilliseconds(5000));
} }
} }

View File

@@ -67,7 +67,7 @@ void TaskMgr_Dummy100Usage()
void TaskMgr_Dummy0Usage() void TaskMgr_Dummy0Usage()
{ {
while (1) while (1)
TaskManager->Sleep(1000000); TaskManager->Sleep(Time::FromMilliseconds(1000000));
} }
uint64_t GetUsage(uint64_t OldSystemTime, Tasking::TaskInfo *Info) 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 */ /* https://github.com/reactos/reactos/blob/560671a784c1e0e0aa7590df5e0598c1e2f41f5a/base/applications/taskmgr/perfdata.c#L347 */
if (Info->OldKernelTime || Info->OldUserTime) 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 CurrentTime = Info->KernelTime + Info->UserTime;
uint64_t OldTime = Info->OldKernelTime + Info->OldUserTime; uint64_t OldTime = Info->OldKernelTime + Info->OldUserTime;
uint64_t CpuUsage = (CurrentTime - OldTime) / SystemTime; uint64_t CpuUsage = (CurrentTime - OldTime) / SystemTime;
@@ -168,7 +168,7 @@ void TaskMgr()
#endif #endif
} }
} }
OldSystemTime = TimeManager->GetCounter(); OldSystemTime = TimeManager->GetTimeNs();
#if defined(__amd64__) #if defined(__amd64__)
register uintptr_t CurrentStackAddress asm("rsp"); register uintptr_t CurrentStackAddress asm("rsp");
printf("Sanity: %d, Stack: %#lx", sanity++, CurrentStackAddress); printf("Sanity: %d, Stack: %#lx", sanity++, CurrentStackAddress);
@@ -185,7 +185,7 @@ void TaskMgr()
if (!Config.Quiet) if (!Config.Quiet)
Display->UpdateBuffer(); Display->UpdateBuffer();
TaskManager->Sleep(100); TaskManager->Sleep(Time::FromMilliseconds(100));
} }
} }

View File

@@ -14,6 +14,7 @@ QEMUFLAGS := -display gtk
ifeq ($(OSARCH), amd64) ifeq ($(OSARCH), amd64)
QEMUFLAGS += -device vmware-svga -M q35 \ QEMUFLAGS += -device vmware-svga -M q35 \
-cpu max \
-usb \ -usb \
-device qemu-xhci,id=xhci \ -device qemu-xhci,id=xhci \
-net user \ -net user \
@@ -40,6 +41,7 @@ QEMUFLAGS += -device vmware-svga -M q35 \
-acpitable file=tools/acpi/SSDT1.dat -acpitable file=tools/acpi/SSDT1.dat
else ifeq ($(OSARCH), i386) else ifeq ($(OSARCH), i386)
QEMUFLAGS += -M q35 \ QEMUFLAGS += -M q35 \
-cpu max \
-usb \ -usb \
-device qemu-xhci,id=xhci \ -device qemu-xhci,id=xhci \
-device usb-mouse,bus=xhci.0,pcap=mousex.pcap \ -device usb-mouse,bus=xhci.0,pcap=mousex.pcap \