mirror of
https://github.com/EnderIce2/Fennix.git
synced 2025-07-07 21:39:16 +00:00
Compare commits
24 Commits
7d37f8a8a1
...
nightly
Author | SHA1 | Date | |
---|---|---|---|
2c1d6c2608
|
|||
97a892d114
|
|||
3be150da53
|
|||
455224ceb4
|
|||
e01f1dc97c
|
|||
76113df5a9
|
|||
1addd310ad
|
|||
3d92a87bef
|
|||
f7177f92cf
|
|||
6a6c3bfc67
|
|||
8dbeee4d9a
|
|||
5d5b674aed
|
|||
31bbc29c9f
|
|||
f5c8ae9323
|
|||
154d857c2e
|
|||
12ae5a83da
|
|||
13ce994edf
|
|||
814175ddaf
|
|||
43e7ddb9de
|
|||
0187fa5b66
|
|||
33c284091d
|
|||
9538589c11
|
|||
905b6933c9
|
|||
07d0ca0438
|
@ -25,17 +25,13 @@
|
||||
}
|
||||
},
|
||||
// From this line below are for qemu, so not that important.
|
||||
"initializeCommand": "[ -x \"$(command -v xhost)\" ] && xhost +local:docker || true", // "xhost -local:docker" to disable
|
||||
"mounts": [
|
||||
{
|
||||
"source": "/tmp/.X11-unix",
|
||||
"target": "/tmp/.X11-unix",
|
||||
"type": "bind"
|
||||
},
|
||||
{
|
||||
"source": "${localEnv:XAUTHORITY}",
|
||||
"target": "/home/vscode/.Xauthority",
|
||||
"type": "bind"
|
||||
},
|
||||
{
|
||||
"source": "/run/user/1000/pulse/native",
|
||||
"target": "/run/user/1000/pulse/native",
|
||||
|
26
.github/workflows/makefile.yml
vendored
26
.github/workflows/makefile.yml
vendored
@ -26,9 +26,7 @@ jobs:
|
||||
run: |
|
||||
sudo mkdir -p /tmp/.X11-unix
|
||||
sudo mkdir -p /run/user/1000/pulse
|
||||
sudo touch /tmp/.Xauthority
|
||||
sudo touch /run/user/1000/pulse/native
|
||||
echo "XAUTHORITY=/tmp/.Xauthority" >> $GITHUB_ENV
|
||||
|
||||
- name: Run make ci-setup in dev container
|
||||
if: steps.cache-cross.outputs.cache-hit != 'true'
|
||||
@ -134,9 +132,7 @@ jobs:
|
||||
run: |
|
||||
sudo mkdir -p /tmp/.X11-unix
|
||||
sudo mkdir -p /run/user/1000/pulse
|
||||
sudo touch /tmp/.Xauthority
|
||||
sudo touch /run/user/1000/pulse/native
|
||||
echo "XAUTHORITY=/tmp/.Xauthority" >> $GITHUB_ENV
|
||||
|
||||
- name: Build AMD64 Debug
|
||||
if: always()
|
||||
@ -205,5 +201,25 @@ jobs:
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: Fennix
|
||||
name: artifacts
|
||||
path: artifacts/
|
||||
|
||||
nightly:
|
||||
if: always()
|
||||
name: Upload Nightly Build to GitHub Releases
|
||||
runs-on: ubuntu-latest
|
||||
needs: [compile]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Download All Builds
|
||||
uses: actions/download-artifact@v4
|
||||
|
||||
- name: Update Nightly
|
||||
run: gh release upload nightly artifacts/* -R ${{github.repository}} --clobber
|
||||
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
|
@ -27,6 +27,9 @@ License information can be found in the [LICENSES.md](LICENSES.md) file.
|
||||
## CPUID 0x7
|
||||
- [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
|
||||
- [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)
|
||||
|
@ -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)
|
||||
endef
|
||||
|
||||
BMP_SOURCES := $(call find-sources,*.bmp)
|
||||
PNG_SOURCES := $(call find-sources,*.png)
|
||||
PSF_SOURCES := $(call find-sources,*.psf)
|
||||
S_SOURCES := $(call find-sources,*.S)
|
||||
s_SOURCES := $(call find-sources,*.s)
|
||||
C_SOURCES := $(call find-sources,*.c)
|
||||
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)
|
||||
GCNO_OBJ = $(C_SOURCES:.c=.gcno) $(CXX_SOURCES:.cpp=.gcno)
|
||||
|
||||
@ -134,7 +134,7 @@ else ifeq ($(OSARCH), aarch64)
|
||||
endif
|
||||
$(__CONF_NM) $@
|
||||
|
||||
%.o: %.bmp
|
||||
%.o: %.png
|
||||
ifeq ($(OSARCH), amd64)
|
||||
$(__CONF_OBJCOPY) -O elf64-x86-64 -I binary $< $@
|
||||
else ifeq ($(OSARCH), i386)
|
||||
|
@ -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)
|
||||
|
@ -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!",
|
||||
|
@ -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 */);
|
||||
|
@ -25,6 +25,8 @@
|
||||
|
||||
#include "../kernel.h"
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
namespace KernelConsole
|
||||
{
|
||||
static int TermColors[] = {
|
||||
|
@ -273,7 +273,7 @@ namespace v0
|
||||
{
|
||||
dbg_api("%d, %d", DriverID, Milliseconds);
|
||||
|
||||
TaskManager->Sleep(Milliseconds);
|
||||
TaskManager->Sleep(Time::FromMilliseconds(Milliseconds));
|
||||
}
|
||||
|
||||
/* --------- */
|
||||
|
@ -302,7 +302,7 @@ namespace Interrupts
|
||||
}
|
||||
else
|
||||
fixme("APIC not found for core %d", Core);
|
||||
// TODO: Handle PIC too
|
||||
// TODO: Handle PIC too
|
||||
|
||||
#endif
|
||||
assert(!"EOI not handled.");
|
||||
@ -423,11 +423,10 @@ namespace Interrupts
|
||||
{
|
||||
for (auto ev : RegisteredEvents)
|
||||
{
|
||||
if (ev.IRQ == InterruptNumber)
|
||||
{
|
||||
warn("IRQ%d is already registered.",
|
||||
InterruptNumber);
|
||||
}
|
||||
if (ev.IRQ != InterruptNumber)
|
||||
continue;
|
||||
|
||||
warn("IRQ%d is already registered.", InterruptNumber);
|
||||
}
|
||||
|
||||
this->InterruptNumber = InterruptNumber;
|
||||
@ -441,15 +440,12 @@ namespace Interrupts
|
||||
0, /* Priority */
|
||||
Critical}; /* Critical */
|
||||
RegisteredEvents.push_back(newEvent);
|
||||
debug("Registered interrupt handler for IRQ%d.",
|
||||
InterruptNumber);
|
||||
debug("Registered interrupt handler for IRQ%d.", InterruptNumber);
|
||||
}
|
||||
|
||||
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()
|
||||
@ -459,16 +455,14 @@ namespace Interrupts
|
||||
|
||||
Handler::~Handler()
|
||||
{
|
||||
debug("Unregistering interrupt handler for IRQ%d.",
|
||||
this->InterruptNumber);
|
||||
|
||||
debug("Unregistering interrupt handler for IRQ%d.", this->InterruptNumber);
|
||||
forItr(itr, RegisteredEvents)
|
||||
{
|
||||
if (itr->IRQ == this->InterruptNumber)
|
||||
{
|
||||
RegisteredEvents.erase(itr);
|
||||
return;
|
||||
}
|
||||
if (itr->IRQ != this->InterruptNumber)
|
||||
continue;
|
||||
|
||||
RegisteredEvents.erase(itr);
|
||||
return;
|
||||
}
|
||||
warn("Event %d not found.", this->InterruptNumber);
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -194,6 +194,15 @@ namespace Memory
|
||||
#pragma GCC diagnostic pop
|
||||
this->ReservePages(SDTHdr, TO_PAGES(SDTHdr->Length));
|
||||
}
|
||||
|
||||
if (bInfo.EFI.Info.Enabled)
|
||||
{
|
||||
debug("Reserving EFI related...");
|
||||
if (bInfo.EFI.Info.IH)
|
||||
this->ReservePage(bInfo.EFI.ImageHandle);
|
||||
if (bInfo.EFI.Info.ST)
|
||||
this->ReservePage(bInfo.EFI.SystemTable);
|
||||
}
|
||||
}
|
||||
#elif defined(__aarch64__)
|
||||
#endif
|
||||
|
@ -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;
|
||||
|
||||
|
@ -897,8 +897,7 @@ namespace PCI
|
||||
BAR[4] = hdr0->BAR4;
|
||||
BAR[5] = hdr0->BAR5;
|
||||
|
||||
debug("Type: %d; IOBase: %#lx; MemoryBase: %#lx",
|
||||
BAR[0] & 1, BAR[1] & (~3), BAR[0] & (~15));
|
||||
debug("Type: %d; IOBase: %#lx; MemoryBase: %#lx", BAR[0] & 1, BAR[1] & (~3), BAR[0] & (~15));
|
||||
|
||||
/* BARs Size */
|
||||
for (short i = 0; i < 6; i++)
|
||||
@ -927,7 +926,7 @@ namespace PCI
|
||||
size = size & UINT32_MAX;
|
||||
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++;
|
||||
}
|
||||
BARsSize[i] = size;
|
||||
@ -943,7 +942,7 @@ namespace PCI
|
||||
size = size & UINT16_MAX;
|
||||
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++;
|
||||
}
|
||||
BARsSize[i] = size;
|
||||
@ -972,10 +971,7 @@ namespace PCI
|
||||
uintptr_t BARBase = BAR[i] & (~3);
|
||||
size_t BARSize = BARsSize[i];
|
||||
|
||||
debug("Mapping BAR%d %#x-%#x", i, BARBase, BARBase + BARSize);
|
||||
|
||||
Memory::Virtual(Table).Map((void *)BARBase, (void *)BARBase,
|
||||
BARSize, Memory::RW | Memory::PWT | Memory::PCD);
|
||||
debug("no need to map BAR%d %#x-%#x as it's an I/O space", i, BARBase, BARBase + BARSize);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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()
|
||||
{
|
||||
}
|
||||
}
|
@ -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()
|
||||
{
|
||||
}
|
||||
}
|
@ -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()
|
||||
{
|
||||
}
|
||||
}
|
@ -19,6 +19,7 @@
|
||||
|
||||
#include <boot/binfo.h>
|
||||
#include <debug.h>
|
||||
#include <memory/virtual.hpp>
|
||||
|
||||
extern struct BootInfo bInfo;
|
||||
|
||||
@ -150,9 +151,14 @@ VOID InitializeMemoryNoBS()
|
||||
|
||||
EFI_STATUS EFIAPI efi_main(EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable)
|
||||
{
|
||||
Memory::Virtual va;
|
||||
bool validST = va.Check(SystemTable);
|
||||
bool validIH = va.Check(ImageHandle);
|
||||
trace("map: ST:%d IH:%d", validST, validIH);
|
||||
|
||||
#ifdef DEBUG
|
||||
debug("efi info: %x", bInfo.EFI.Info.raw);
|
||||
if (bInfo.EFI.Info.ST)
|
||||
if (bInfo.EFI.Info.ST && validST)
|
||||
{
|
||||
EFI_GUID EfiAcpi20Table = EFI_ACPI_20_TABLE_GUID;
|
||||
EFI_GUID AcpiTable = ACPI_TABLE_GUID;
|
||||
@ -238,7 +244,7 @@ EFI_STATUS EFIAPI efi_main(EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable
|
||||
}
|
||||
#endif
|
||||
|
||||
if (bInfo.EFI.Info.ST == 1)
|
||||
if (bInfo.EFI.Info.ST == 1 && validST)
|
||||
{
|
||||
SearchSMBIOS(SystemTable);
|
||||
SearchRSDP(SystemTable);
|
||||
|
BIN
Kernel/files/logo.png
Normal file
BIN
Kernel/files/logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 6.0 KiB |
202
Kernel/include/cpu/x86/cpuid_kvm.hpp
Normal file
202
Kernel/include/cpu/x86/cpuid_kvm.hpp
Normal 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
|
@ -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,
|
||||
|
@ -21,9 +21,24 @@
|
||||
#include <types.h>
|
||||
#include <debug.h>
|
||||
#include <cassert>
|
||||
#include <vector>
|
||||
|
||||
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<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;
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -245,7 +245,14 @@ public:
|
||||
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_MIN (-INT8_MAX - 1)
|
||||
|
@ -409,4 +409,84 @@ namespace std
|
||||
|
||||
template <class InputIt1, class InputIt2>
|
||||
constexpr auto lexicographical_compare_three_way(InputIt1 first1, InputIt1 last1, InputIt2 first2, InputIt2 last2);
|
||||
|
||||
namespace detail
|
||||
{
|
||||
template <class T = void>
|
||||
struct __algo_less
|
||||
{
|
||||
constexpr bool operator()(const T &lhs, const T &rhs) const
|
||||
{
|
||||
return lhs < rhs;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct __algo_less<T *>
|
||||
{
|
||||
constexpr bool operator()(const T *lhs, const T *rhs) const
|
||||
{
|
||||
if (__builtin_is_constant_evaluated())
|
||||
return lhs < rhs;
|
||||
return (__UINTPTR_TYPE__)lhs < (__UINTPTR_TYPE__)rhs;
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
class __algo_less<void>
|
||||
{
|
||||
public:
|
||||
template <class T, class U>
|
||||
constexpr auto operator()(T &&lhs, U &&rhs) const -> decltype(std::forward<T>(lhs) < std::forward<U>(rhs))
|
||||
{
|
||||
return std::forward<T>(lhs) < std::forward<U>(rhs);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
template <class ForwardIt, class Compare>
|
||||
ForwardIt is_sorted_until(ForwardIt first, ForwardIt last, Compare comp)
|
||||
{
|
||||
if (first == last)
|
||||
return last;
|
||||
|
||||
ForwardIt next = first;
|
||||
while (++next != last)
|
||||
{
|
||||
if (comp(*next, *first))
|
||||
return next;
|
||||
first = next;
|
||||
}
|
||||
return last;
|
||||
}
|
||||
|
||||
template <class ForwardIt>
|
||||
ForwardIt is_sorted_until(ForwardIt first, ForwardIt last)
|
||||
{
|
||||
return is_sorted_until(first, last, detail::__algo_less<>());
|
||||
}
|
||||
|
||||
template <class ExecutionPolicy, class ForwardIt>
|
||||
ForwardIt is_sorted_until(ExecutionPolicy &&policy, ForwardIt first, ForwardIt last);
|
||||
|
||||
template <class ExecutionPolicy, class ForwardIt, class Compare>
|
||||
ForwardIt is_sorted_until(ExecutionPolicy &&policy, ForwardIt first, ForwardIt last, Compare comp);
|
||||
|
||||
template <class ForwardIt>
|
||||
constexpr bool is_sorted(ForwardIt first, ForwardIt last)
|
||||
{
|
||||
return std::is_sorted_until(first, last) == last;
|
||||
}
|
||||
|
||||
template <class ExecutionPolicy, class ForwardIt>
|
||||
bool is_sorted(ExecutionPolicy &&policy, ForwardIt first, ForwardIt last);
|
||||
|
||||
template <class ForwardIt, class Compare>
|
||||
constexpr bool is_sorted(ForwardIt first, ForwardIt last, Compare comp)
|
||||
{
|
||||
return std::is_sorted_until(first, last, comp) == last;
|
||||
}
|
||||
|
||||
template <class ExecutionPolicy, class ForwardIt, class Compare>
|
||||
bool is_sorted(ExecutionPolicy &&policy, ForwardIt first, ForwardIt last, Compare comp);
|
||||
}
|
||||
|
@ -28,22 +28,25 @@ namespace std
|
||||
class duration
|
||||
{
|
||||
private:
|
||||
Rep rep_;
|
||||
std::ratio<Period::num, Period::denom> period_;
|
||||
Rep _rep;
|
||||
std::ratio<Period::num, Period::den> _period;
|
||||
|
||||
public:
|
||||
using rep = Rep;
|
||||
using period = Period;
|
||||
|
||||
constexpr duration() = default;
|
||||
duration(const duration &) = default;
|
||||
|
||||
template <class Rep2>
|
||||
constexpr explicit duration(const Rep2 &r) { rep_ = r; }
|
||||
constexpr explicit duration(const Rep2 &r) { _rep = r; }
|
||||
|
||||
template <class Rep2, class Period2>
|
||||
constexpr duration(const duration<Rep2, Period2> &d);
|
||||
|
||||
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 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 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++()
|
||||
{
|
||||
++rep_;
|
||||
++_rep;
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr duration &operator--()
|
||||
{
|
||||
--rep_;
|
||||
--_rep;
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr duration &operator+=(const duration &d)
|
||||
{
|
||||
rep_ += d.count();
|
||||
_rep += d.count();
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr duration &operator-=(const duration &d)
|
||||
{
|
||||
rep_ -= d.count();
|
||||
_rep -= d.count();
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr duration &operator*=(const Rep &rhs)
|
||||
{
|
||||
rep_ *= rhs;
|
||||
_rep *= rhs;
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr duration &operator/=(const Rep &rhs)
|
||||
{
|
||||
rep_ /= rhs;
|
||||
_rep /= rhs;
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr duration &operator%=(const Rep &rhs)
|
||||
{
|
||||
rep_ %= rhs;
|
||||
_rep %= rhs;
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr duration &operator%=(const duration &rhs)
|
||||
{
|
||||
rep_ %= rhs.count();
|
||||
_rep %= rhs.count();
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
@ -120,7 +123,91 @@ namespace std
|
||||
template <class ToDuration, class Rep, class Period>
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
789
Kernel/include_std/foward_list
Normal file
789
Kernel/include_std/foward_list
Normal file
@ -0,0 +1,789 @@
|
||||
/*
|
||||
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 <memory>
|
||||
#include <functional>
|
||||
#include <initializer_list>
|
||||
#include <compare>
|
||||
#include <iterator>
|
||||
#include <limits>
|
||||
#include <utility>
|
||||
#include <type_traits>
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
|
||||
namespace std
|
||||
{
|
||||
template <class T, class Allocator = std::allocator<T>>
|
||||
class forward_list
|
||||
{
|
||||
public:
|
||||
using value_type = T;
|
||||
using allocator_type = Allocator;
|
||||
using size_type = std::size_t;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using reference = value_type &;
|
||||
using const_reference = const value_type &;
|
||||
using pointer = std::allocator_traits<Allocator>::pointer;
|
||||
using const_pointer = std::allocator_traits<Allocator>::const_pointer;
|
||||
using iterator_category = std::forward_iterator_tag;
|
||||
// using iterator = value_type;
|
||||
// using const_iterator = const value_type;
|
||||
|
||||
private:
|
||||
struct node
|
||||
{
|
||||
node *Next;
|
||||
value_type Value;
|
||||
|
||||
template <class... Args>
|
||||
node(node *next, Args &&...args)
|
||||
: Next(next), Value(std::forward<Args>(args)...) {}
|
||||
};
|
||||
|
||||
using node_allocator_type = typename std::allocator_traits<Allocator>::template rebind_alloc<node>;
|
||||
node_allocator_type NodeAlloc;
|
||||
node *Head; /* Points to dummy node (before first element) */
|
||||
|
||||
node *__CreateNode(const T &value)
|
||||
{
|
||||
node *n = std::allocator_traits<node_allocator_type>::allocate(NodeAlloc, 1);
|
||||
try
|
||||
{
|
||||
std::allocator_traits<node_allocator_type>::construct(NodeAlloc, n, nullptr, value);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
std::allocator_traits<node_allocator_type>::deallocate(NodeAlloc, n, 1);
|
||||
throw;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
node *__CreateNode(T &&value)
|
||||
{
|
||||
node *n = std::allocator_traits<node_allocator_type>::allocate(NodeAlloc, 1);
|
||||
try
|
||||
{
|
||||
std::allocator_traits<node_allocator_type>::construct(NodeAlloc, n, nullptr, std::move(value));
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
std::allocator_traits<node_allocator_type>::deallocate(NodeAlloc, n, 1);
|
||||
throw;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
template <class... Args>
|
||||
node *__CreateNodeEmplace(Args &&...args)
|
||||
{
|
||||
node *n = std::allocator_traits<node_allocator_type>::allocate(NodeAlloc, 1);
|
||||
try
|
||||
{
|
||||
std::allocator_traits<node_allocator_type>::construct(NodeAlloc, n, nullptr, std::forward<Args>(args)...);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
std::allocator_traits<node_allocator_type>::deallocate(NodeAlloc, n, 1);
|
||||
throw;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
void __DestroyNode(node *n)
|
||||
{
|
||||
std::allocator_traits<node_allocator_type>::destroy(NodeAlloc, n);
|
||||
std::allocator_traits<node_allocator_type>::deallocate(NodeAlloc, n, 1);
|
||||
}
|
||||
|
||||
void __ClearNodes(node *start)
|
||||
{
|
||||
while (start)
|
||||
{
|
||||
node *next = start->Next;
|
||||
__DestroyNode(start);
|
||||
start = next;
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
class iterator
|
||||
{
|
||||
friend class forward_list;
|
||||
node *ptr;
|
||||
|
||||
public:
|
||||
typedef std::forward_iterator_tag iterator_category;
|
||||
typedef T value_type;
|
||||
typedef std::ptrdiff_t difference_type;
|
||||
typedef T *pointer;
|
||||
typedef T &reference;
|
||||
using self_type = iterator;
|
||||
|
||||
iterator() : ptr(nullptr) {}
|
||||
iterator(node *p) : ptr(p) {}
|
||||
iterator(const iterator &other) = default;
|
||||
iterator &operator=(const iterator &other) = default;
|
||||
~iterator() = default;
|
||||
|
||||
reference operator*() { return ptr->Value; }
|
||||
pointer operator->() { return &(ptr->Value); }
|
||||
const_reference operator*() const { return ptr->Value; }
|
||||
const_pointer operator->() const { return &(ptr->Value); }
|
||||
|
||||
self_type &operator++()
|
||||
{
|
||||
ptr = ptr->Next;
|
||||
return *this;
|
||||
}
|
||||
|
||||
self_type operator++(int)
|
||||
{
|
||||
self_type tmp = *this;
|
||||
++(*this);
|
||||
return tmp;
|
||||
}
|
||||
|
||||
bool operator==(const self_type &other) const { return ptr == other.ptr; }
|
||||
bool operator!=(const self_type &other) const { return ptr != other.ptr; }
|
||||
};
|
||||
|
||||
class const_iterator
|
||||
{
|
||||
friend class forward_list;
|
||||
const node *ptr;
|
||||
|
||||
public:
|
||||
typedef std::forward_iterator_tag iterator_category;
|
||||
typedef T value_type;
|
||||
typedef std::ptrdiff_t difference_type;
|
||||
typedef const T *pointer;
|
||||
typedef const T &reference;
|
||||
using self_type = const_iterator;
|
||||
|
||||
const_iterator() : ptr(nullptr) {}
|
||||
const_iterator(const node *p) : ptr(p) {}
|
||||
const_iterator(const iterator &it) : ptr(it.ptr) {}
|
||||
const_iterator(const const_iterator &other) = default;
|
||||
const_iterator &operator=(const const_iterator &other) = default;
|
||||
~const_iterator() = default;
|
||||
|
||||
reference operator*() const { return ptr->Value; }
|
||||
pointer operator->() const { return &(ptr->Value); }
|
||||
|
||||
self_type &operator++()
|
||||
{
|
||||
ptr = ptr->Next;
|
||||
return *this;
|
||||
}
|
||||
|
||||
self_type operator++(int)
|
||||
{
|
||||
self_type tmp = *this;
|
||||
++(*this);
|
||||
return tmp;
|
||||
}
|
||||
|
||||
bool operator==(const self_type &other) const { return ptr == other.ptr; }
|
||||
bool operator!=(const self_type &other) const { return ptr != other.ptr; }
|
||||
};
|
||||
|
||||
#pragma region Constructors
|
||||
|
||||
forward_list() : NodeAlloc(), Head(__CreateNodeEmplace()) {}
|
||||
|
||||
explicit forward_list(const Allocator &alloc) : NodeAlloc(alloc), Head(__CreateNodeEmplace()) {}
|
||||
|
||||
explicit forward_list(size_type count, const Allocator &alloc = Allocator())
|
||||
: NodeAlloc(alloc), Head(__CreateNodeEmplace())
|
||||
{
|
||||
node *cur = Head;
|
||||
for (size_type i = 0; i < count; ++i)
|
||||
{
|
||||
cur->Next = __CreateNode(T());
|
||||
cur = cur->Next;
|
||||
}
|
||||
}
|
||||
|
||||
forward_list(size_type count, const T &value, const Allocator &alloc = Allocator())
|
||||
: NodeAlloc(alloc), Head(__CreateNodeEmplace())
|
||||
{
|
||||
node *cur = Head;
|
||||
for (size_type i = 0; i < count; ++i)
|
||||
{
|
||||
cur->Next = __CreateNode(value);
|
||||
cur = cur->Next;
|
||||
}
|
||||
}
|
||||
|
||||
template <class InputIt, typename = std::enable_if_t<!std::is_integral<InputIt>::value>>
|
||||
forward_list(InputIt first, InputIt last, const Allocator &alloc = Allocator())
|
||||
: NodeAlloc(alloc), Head(__CreateNodeEmplace())
|
||||
{
|
||||
node *cur = Head;
|
||||
for (; first != last; ++first)
|
||||
{
|
||||
cur->Next = __CreateNode(*first);
|
||||
cur = cur->Next;
|
||||
}
|
||||
}
|
||||
|
||||
forward_list(const forward_list &other)
|
||||
: NodeAlloc(std::allocator_traits<Allocator>::select_on_container_copy_construction(other.NodeAlloc)), Head(__CreateNodeEmplace())
|
||||
{
|
||||
node *cur = Head;
|
||||
for (node *n = other.Head->Next; n; n = n->Next)
|
||||
{
|
||||
cur->Next = __CreateNode(n->Value);
|
||||
cur = cur->Next;
|
||||
}
|
||||
}
|
||||
|
||||
forward_list(forward_list &&other)
|
||||
: NodeAlloc(std::move(other.NodeAlloc)), Head(other.Head)
|
||||
{
|
||||
other.Head = nullptr;
|
||||
}
|
||||
|
||||
forward_list(const forward_list &other, const std::type_identity_t<Allocator> &alloc)
|
||||
: NodeAlloc(alloc), Head(__CreateNodeEmplace())
|
||||
{
|
||||
node *cur = Head;
|
||||
for (node *n = other.Head->Next; n; n = n->Next)
|
||||
{
|
||||
cur->Next = __CreateNode(n->Value);
|
||||
cur = cur->Next;
|
||||
}
|
||||
}
|
||||
|
||||
forward_list(forward_list &&other, const std::type_identity_t<Allocator> &alloc)
|
||||
: NodeAlloc(alloc), Head(__CreateNodeEmplace())
|
||||
{
|
||||
if (other.empty())
|
||||
return;
|
||||
|
||||
node *cur = Head;
|
||||
for (node *n = other.Head->Next; n; n = n->Next)
|
||||
{
|
||||
cur->Next = __CreateNode(std::move(n->Value));
|
||||
cur = cur->Next;
|
||||
}
|
||||
other.clear();
|
||||
}
|
||||
|
||||
forward_list(std::initializer_list<T> init, const Allocator &alloc = Allocator())
|
||||
: NodeAlloc(alloc), Head(__CreateNodeEmplace())
|
||||
{
|
||||
node *cur = Head;
|
||||
for (const auto &v : init)
|
||||
{
|
||||
cur->Next = __CreateNode(v);
|
||||
cur = cur->Next;
|
||||
}
|
||||
}
|
||||
|
||||
~forward_list()
|
||||
{
|
||||
if (Head == nullptr)
|
||||
return;
|
||||
|
||||
clear();
|
||||
__DestroyNode(Head);
|
||||
Head = nullptr;
|
||||
}
|
||||
|
||||
#pragma endregion Constructors
|
||||
|
||||
forward_list &operator=(const forward_list &other)
|
||||
{
|
||||
if (this != &other)
|
||||
{
|
||||
clear();
|
||||
node *cur = Head;
|
||||
for (node *n = other.Head->Next; n; n = n->Next)
|
||||
{
|
||||
cur->Next = __CreateNode(n->Value);
|
||||
cur = cur->Next;
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
forward_list &operator=(forward_list &&other) noexcept(std::allocator_traits<Allocator>::is_always_equal::value)
|
||||
{
|
||||
if (this != &other)
|
||||
{
|
||||
clear();
|
||||
__DestroyNode(Head);
|
||||
Head = other.Head;
|
||||
NodeAlloc = std::move(other.NodeAlloc);
|
||||
other.Head = nullptr;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
forward_list &operator=(std::initializer_list<value_type> ilist)
|
||||
{
|
||||
clear();
|
||||
node *cur = Head;
|
||||
for (const auto &v : ilist)
|
||||
{
|
||||
cur->Next = __CreateNode(v);
|
||||
cur = cur->Next;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
void assign(size_type count, const T &value)
|
||||
{
|
||||
clear();
|
||||
node *cur = Head;
|
||||
for (size_type i = 0; i < count; ++i)
|
||||
{
|
||||
cur->Next = __CreateNode(value);
|
||||
cur = cur->Next;
|
||||
}
|
||||
}
|
||||
|
||||
template <class InputIt>
|
||||
void assign(InputIt first, InputIt last)
|
||||
{
|
||||
clear();
|
||||
node *cur = Head;
|
||||
for (; first != last; ++first)
|
||||
{
|
||||
cur->Next = __CreateNode(*first);
|
||||
cur = cur->Next;
|
||||
}
|
||||
}
|
||||
|
||||
void assign(std::initializer_list<T> ilist)
|
||||
{
|
||||
clear();
|
||||
node *cur = Head;
|
||||
for (const auto &v : ilist)
|
||||
{
|
||||
cur->Next = __CreateNode(v);
|
||||
cur = cur->Next;
|
||||
}
|
||||
}
|
||||
|
||||
allocator_type get_allocator() const noexcept { return allocator_type(NodeAlloc); }
|
||||
|
||||
#pragma region Element Access
|
||||
|
||||
reference front() { return Head->Next->Value; }
|
||||
const_reference front() const { return Head->Next->Value; }
|
||||
|
||||
#pragma endregion Element Access
|
||||
|
||||
#pragma region Iterators
|
||||
|
||||
iterator before_begin() noexcept { return iterator(Head); }
|
||||
const_iterator before_begin() const noexcept { return const_iterator(Head); }
|
||||
const_iterator cbefore_begin() const noexcept { return const_iterator(Head); }
|
||||
|
||||
iterator begin() noexcept { return iterator(Head->Next); }
|
||||
const_iterator begin() const noexcept { return const_iterator(Head->Next); }
|
||||
const_iterator cbegin() const noexcept { return const_iterator(Head->Next); }
|
||||
|
||||
iterator end() noexcept { return iterator(nullptr); }
|
||||
const_iterator end() const noexcept { return const_iterator(nullptr); }
|
||||
const_iterator cend() const noexcept { return const_iterator(nullptr); }
|
||||
|
||||
#pragma endregion Iterators
|
||||
|
||||
#pragma region Capacity
|
||||
|
||||
bool empty() const noexcept { return Head->Next == nullptr; }
|
||||
size_type max_size() const noexcept { return std::numeric_limits<size_type>::max(); }
|
||||
|
||||
#pragma endregion Capacity
|
||||
|
||||
#pragma region Modifiers
|
||||
|
||||
void clear() noexcept
|
||||
{
|
||||
node *cur = Head->Next;
|
||||
Head->Next = nullptr;
|
||||
__ClearNodes(cur);
|
||||
}
|
||||
|
||||
iterator insert_after(const_iterator pos, const T &value)
|
||||
{
|
||||
node *p = const_cast<node *>(pos.ptr);
|
||||
node *n = __CreateNode(value);
|
||||
n->Next = p->Next;
|
||||
p->Next = n;
|
||||
return iterator(n);
|
||||
}
|
||||
|
||||
iterator insert_after(const_iterator pos, T &&value)
|
||||
{
|
||||
node *p = const_cast<node *>(pos.ptr);
|
||||
node *n = __CreateNode(std::move(value));
|
||||
n->Next = p->Next;
|
||||
p->Next = n;
|
||||
return iterator(n);
|
||||
}
|
||||
|
||||
iterator insert_after(const_iterator pos, size_type count, const T &value)
|
||||
{
|
||||
node *p = const_cast<node *>(pos.ptr);
|
||||
node *last = p;
|
||||
|
||||
for (size_type i = 0; i < count; ++i)
|
||||
{
|
||||
node *n = __CreateNode(value);
|
||||
n->Next = last->Next;
|
||||
last->Next = n;
|
||||
last = n;
|
||||
}
|
||||
|
||||
return iterator(last);
|
||||
}
|
||||
|
||||
template <class InputIt>
|
||||
iterator insert_after(const_iterator pos, InputIt first, InputIt last)
|
||||
{
|
||||
node *p = const_cast<node *>(pos.ptr);
|
||||
node *cur = p;
|
||||
|
||||
for (; first != last; ++first)
|
||||
{
|
||||
node *n = __CreateNode(*first);
|
||||
n->Next = cur->Next;
|
||||
cur->Next = n;
|
||||
cur = n;
|
||||
}
|
||||
|
||||
return iterator(cur);
|
||||
}
|
||||
|
||||
iterator insert_after(const_iterator pos, std::initializer_list<T> ilist)
|
||||
{
|
||||
node *p = const_cast<node *>(pos.ptr);
|
||||
node *cur = p;
|
||||
|
||||
for (const auto &v : ilist)
|
||||
{
|
||||
node *n = __CreateNode(v);
|
||||
n->Next = cur->Next;
|
||||
cur->Next = n;
|
||||
cur = n;
|
||||
}
|
||||
|
||||
return iterator(cur);
|
||||
}
|
||||
|
||||
template <class... Args>
|
||||
iterator emplace_after(const_iterator pos, Args &&...args)
|
||||
{
|
||||
node *p = const_cast<node *>(pos.ptr);
|
||||
node *n = __CreateNodeEmplace(std::forward<Args>(args)...);
|
||||
n->Next = p->Next;
|
||||
p->Next = n;
|
||||
return iterator(n);
|
||||
}
|
||||
|
||||
iterator erase_after(const_iterator pos)
|
||||
{
|
||||
node *p = const_cast<node *>(pos.ptr);
|
||||
node *to_delete = p->Next;
|
||||
|
||||
if (to_delete)
|
||||
{
|
||||
p->Next = to_delete->Next;
|
||||
__DestroyNode(to_delete);
|
||||
}
|
||||
|
||||
return iterator(p->Next);
|
||||
}
|
||||
|
||||
iterator erase_after(const_iterator first, const_iterator last)
|
||||
{
|
||||
node *p = const_cast<node *>(first.ptr);
|
||||
node *end = const_cast<node *>(last.ptr);
|
||||
node *cur = p->Next;
|
||||
|
||||
while (cur != end)
|
||||
{
|
||||
node *next = cur->Next;
|
||||
__DestroyNode(cur);
|
||||
cur = next;
|
||||
}
|
||||
|
||||
p->Next = end;
|
||||
return iterator(end);
|
||||
}
|
||||
|
||||
void push_front(const T &value) { insert_after(before_begin(), value); }
|
||||
void push_front(T &&value) { insert_after(before_begin(), std::move(value)); }
|
||||
|
||||
template <class... Args>
|
||||
reference emplace_front(Args &&...args)
|
||||
{
|
||||
iterator it = emplace_after(before_begin(), std::forward<Args>(args)...);
|
||||
return *it;
|
||||
}
|
||||
|
||||
void pop_front() { erase_after(before_begin()); }
|
||||
|
||||
void resize(size_type count) { resize(count, T()); }
|
||||
|
||||
void resize(size_type count, const value_type &value)
|
||||
{
|
||||
node *cur = Head;
|
||||
size_type n = 0;
|
||||
|
||||
while (cur->Next && n < count)
|
||||
{
|
||||
cur = cur->Next;
|
||||
++n;
|
||||
}
|
||||
|
||||
if (n == count)
|
||||
{
|
||||
/* Remove remaining */
|
||||
__ClearNodes(cur->Next);
|
||||
cur->Next = nullptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Add more */
|
||||
for (; n < count; ++n)
|
||||
{
|
||||
cur->Next = __CreateNode(value);
|
||||
cur = cur->Next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void swap(forward_list &other) noexcept(std::allocator_traits<Allocator>::is_always_equal::value)
|
||||
{
|
||||
std::swap(Head, other.Head);
|
||||
/* FIXME FIXME FIXME FIXME */
|
||||
// std::swap(NodeAlloc, other.NodeAlloc);
|
||||
}
|
||||
|
||||
#pragma endregion Modifiers
|
||||
|
||||
#pragma region Operations
|
||||
|
||||
void merge(forward_list &other) { merge(other, std::less<T>()); }
|
||||
void merge(forward_list &&other) { merge(other, std::less<T>()); }
|
||||
|
||||
template <class Compare>
|
||||
void merge(forward_list &other, Compare comp)
|
||||
{
|
||||
if (this == &other)
|
||||
return;
|
||||
|
||||
node *this_prev = Head;
|
||||
node *this_cur = Head->Next;
|
||||
node *other_cur = other.Head->Next;
|
||||
|
||||
while (this_cur && other_cur)
|
||||
{
|
||||
if (comp(other_cur->Value, this_cur->Value))
|
||||
{
|
||||
node *next = other_cur->Next;
|
||||
this_prev->Next = other_cur;
|
||||
other_cur->Next = this_cur;
|
||||
this_prev = other_cur;
|
||||
other_cur = next;
|
||||
}
|
||||
else
|
||||
{
|
||||
this_prev = this_cur;
|
||||
this_cur = this_cur->Next;
|
||||
}
|
||||
}
|
||||
|
||||
if (other_cur)
|
||||
this_prev->Next = other_cur;
|
||||
|
||||
other.Head->Next = nullptr;
|
||||
}
|
||||
|
||||
template <class Compare>
|
||||
void merge(forward_list &&other, Compare comp) { merge(other, comp); }
|
||||
|
||||
void splice_after(const_iterator pos, forward_list &other)
|
||||
{
|
||||
if (&other == this || other.empty())
|
||||
return;
|
||||
splice_after(pos, other, other.before_begin(), other.end());
|
||||
}
|
||||
|
||||
void splice_after(const_iterator pos, forward_list &&other) { splice_after(pos, other); }
|
||||
void splice_after(const_iterator pos, forward_list &other, const_iterator it) { splice_after(pos, other, it, std::next(it)); }
|
||||
void splice_after(const_iterator pos, forward_list &&other, const_iterator it) { splice_after(pos, other, it); }
|
||||
|
||||
void splice_after(const_iterator pos, forward_list &other, const_iterator first, const_iterator last)
|
||||
{
|
||||
if (first == last)
|
||||
return;
|
||||
|
||||
node *p = const_cast<node *>(pos.ptr);
|
||||
node *f = const_cast<node *>(first.ptr);
|
||||
node *l = const_cast<node *>(last.ptr);
|
||||
|
||||
node *start = f->Next;
|
||||
node *end = start;
|
||||
|
||||
if (!start)
|
||||
return;
|
||||
|
||||
while (end->Next && end->Next != l)
|
||||
end = end->Next;
|
||||
|
||||
f->Next = l;
|
||||
node *after = p->Next;
|
||||
p->Next = start;
|
||||
end->Next = after;
|
||||
}
|
||||
|
||||
void splice_after(const_iterator pos, forward_list &&other, const_iterator first, const_iterator last)
|
||||
{
|
||||
splice_after(pos, other, first, last);
|
||||
}
|
||||
|
||||
size_type remove(const T &value)
|
||||
{
|
||||
size_type count = 0;
|
||||
node *prev = Head;
|
||||
node *cur = Head->Next;
|
||||
while (cur)
|
||||
{
|
||||
if (cur->Value == value)
|
||||
{
|
||||
node *to_delete = cur;
|
||||
prev->Next = cur->Next;
|
||||
cur = cur->Next;
|
||||
__DestroyNode(to_delete);
|
||||
++count;
|
||||
}
|
||||
else
|
||||
{
|
||||
prev = cur;
|
||||
cur = cur->Next;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
template <class UnaryPred>
|
||||
size_type remove_if(UnaryPred p)
|
||||
{
|
||||
size_type count = 0;
|
||||
node *prev = Head;
|
||||
node *cur = Head->Next;
|
||||
while (cur)
|
||||
{
|
||||
if (p(cur->Value))
|
||||
{
|
||||
node *to_delete = cur;
|
||||
prev->Next = cur->Next;
|
||||
cur = cur->Next;
|
||||
__DestroyNode(to_delete);
|
||||
++count;
|
||||
}
|
||||
else
|
||||
{
|
||||
prev = cur;
|
||||
cur = cur->Next;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
void reverse() noexcept
|
||||
{
|
||||
node *prev = nullptr;
|
||||
node *cur = Head->Next;
|
||||
while (cur)
|
||||
{
|
||||
node *next = cur->Next;
|
||||
cur->Next = prev;
|
||||
prev = cur;
|
||||
cur = next;
|
||||
}
|
||||
Head->Next = prev;
|
||||
}
|
||||
|
||||
size_type unique() { return unique(std::equal_to<T>()); }
|
||||
|
||||
template <class BinaryPred>
|
||||
size_type unique(BinaryPred p)
|
||||
{
|
||||
size_type count = 0;
|
||||
node *cur = Head->Next;
|
||||
while (cur && cur->Next)
|
||||
{
|
||||
if (p(cur->Value, cur->Next->Value))
|
||||
{
|
||||
node *to_delete = cur->Next;
|
||||
cur->Next = to_delete->Next;
|
||||
__DestroyNode(to_delete);
|
||||
++count;
|
||||
}
|
||||
else
|
||||
cur = cur->Next;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
void sort() { sort(std::less<T>()); }
|
||||
|
||||
template <class Compare>
|
||||
void sort(Compare comp)
|
||||
{
|
||||
if (!Head->Next || !Head->Next->Next)
|
||||
return;
|
||||
|
||||
std::vector<T> vec;
|
||||
for (node *cur = Head->Next; cur; cur = cur->Next)
|
||||
vec.push_back(cur->Value);
|
||||
|
||||
std::sort(vec.begin(), vec.end(), comp);
|
||||
node *cur = Head->Next;
|
||||
for (auto &v : vec)
|
||||
{
|
||||
cur->Value = v;
|
||||
cur = cur->Next;
|
||||
}
|
||||
}
|
||||
|
||||
#pragma endregion Operations
|
||||
};
|
||||
|
||||
template <class T, class Alloc>
|
||||
bool operator==(const std::forward_list<T, Alloc> &lhs, const std::forward_list<T, Alloc> &rhs)
|
||||
{
|
||||
return std::equal(lhs.begin(), lhs.end(), rhs.begin(), rhs.end());
|
||||
}
|
||||
|
||||
template <class T, class Alloc>
|
||||
auto operator<=>(const std::forward_list<T, Alloc> &lhs, const std::forward_list<T, Alloc> &rhs)
|
||||
{
|
||||
return std::lexicographical_compare_three_way(lhs.begin(), lhs.end(), rhs.begin(), rhs.end(), __synth_three_way);
|
||||
}
|
||||
}
|
@ -17,40 +17,173 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <types.h>
|
||||
#include <type_traits>
|
||||
|
||||
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>
|
||||
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:
|
||||
typedef ratio<Num, Denom> type;
|
||||
static constexpr intmax_t num = Num;
|
||||
static constexpr intmax_t denom = Denom;
|
||||
static constexpr intmax_t num = Denom < 0 ? -Num / __gcd : Num / __gcd;
|
||||
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>
|
||||
using ratio_add = ratio<R1::num * R2::denom + R2::num * R1::denom, R1::denom * R2::denom>;
|
||||
template <class R1, class R2>
|
||||
using ratio_subtract = ratio<R1::num * R2::denom - R2::num * R1::denom, R1::denom * R2::denom>;
|
||||
template <class R1, class R2>
|
||||
using ratio_multiply = ratio<R1::num * R2::num, R1::denom * R2::denom>;
|
||||
template <class R1, class R2>
|
||||
using ratio_divide = ratio<R1::num * R2::denom, R1::denom * R2::num>;
|
||||
struct ratio_add
|
||||
{
|
||||
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>
|
||||
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>
|
||||
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>
|
||||
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>
|
||||
struct ratio_less_equal;
|
||||
struct ratio_equal : public std::integral_constant<bool, detail::__is_equal<R1, 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>
|
||||
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, 1000000000000000000000000000> ronto;
|
||||
|
@ -21,6 +21,7 @@
|
||||
#include <task.hpp>
|
||||
#include <debug.h>
|
||||
#include <smp.hpp>
|
||||
#include <ostream>
|
||||
#include <chrono>
|
||||
|
||||
extern Tasking::Task *TaskManager;
|
||||
@ -84,17 +85,26 @@ namespace std
|
||||
|
||||
namespace this_thread
|
||||
{
|
||||
thread::id get_id() noexcept;
|
||||
|
||||
void yield() noexcept;
|
||||
|
||||
template <class Clock, class Duration>
|
||||
void sleep_until(const chrono::time_point<Clock, Duration> &abs_time);
|
||||
thread::id get_id() noexcept;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@ -18,7 +18,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <type_traits>
|
||||
#include <algorithm>
|
||||
#include <tuple>
|
||||
|
||||
namespace std
|
||||
|
@ -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++)
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
|
94
Kernel/subsystem/time/hpet.cpp
Normal file
94
Kernel/subsystem/time/hpet.cpp
Normal 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
|
||||
}
|
||||
}
|
71
Kernel/subsystem/time/kvm_clock.cpp
Normal file
71
Kernel/subsystem/time/kvm_clock.cpp
Normal 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()
|
||||
{
|
||||
}
|
||||
}
|
106
Kernel/subsystem/time/manager.cpp
Normal file
106
Kernel/subsystem/time/manager.cpp
Normal 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) {}
|
||||
}
|
105
Kernel/subsystem/time/tsc.cpp
Normal file
105
Kernel/subsystem/time/tsc.cpp
Normal 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 */
|
||||
}
|
||||
}
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
|
@ -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 &&
|
||||
|
@ -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();
|
||||
}
|
||||
|
92
Kernel/tests/stl/foward_list.cpp
Normal file
92
Kernel/tests/stl/foward_list.cpp
Normal file
@ -0,0 +1,92 @@
|
||||
/*
|
||||
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/>.
|
||||
*/
|
||||
|
||||
#ifdef DEBUG
|
||||
|
||||
#include <foward_list>
|
||||
#include <iostream>
|
||||
#include <cassert>
|
||||
#include <algorithm>
|
||||
|
||||
void test_stl_forward_list()
|
||||
{
|
||||
debug("Running forward_list tests...");
|
||||
|
||||
std::forward_list<int> fl1(3, 7);
|
||||
assert(std::distance(fl1.begin(), fl1.end()) == 3);
|
||||
for (int v : fl1)
|
||||
assert(v == 7);
|
||||
|
||||
fl1.assign({1, 2, 3});
|
||||
assert(std::distance(fl1.begin(), fl1.end()) == 3);
|
||||
assert(*fl1.begin() == 1);
|
||||
|
||||
auto alloc = fl1.get_allocator();
|
||||
int *test = alloc.allocate(1);
|
||||
alloc.deallocate(test, 1);
|
||||
|
||||
assert(fl1.front() == 9 || fl1.front() == 1);
|
||||
|
||||
auto before = fl1.before_begin();
|
||||
auto begin = fl1.begin();
|
||||
auto end = fl1.end();
|
||||
assert(std::distance(begin, end) >= 0);
|
||||
|
||||
assert(!fl1.empty());
|
||||
assert(fl1.max_size() > 0);
|
||||
|
||||
fl1.insert_after(before, 42);
|
||||
fl1.emplace_after(fl1.begin(), 99);
|
||||
|
||||
fl1.erase_after(fl1.begin());
|
||||
|
||||
fl1.emplace_front(88);
|
||||
fl1.pop_front();
|
||||
|
||||
fl1.resize(5, -1);
|
||||
fl1.resize(2);
|
||||
|
||||
std::forward_list<int> fl2 = {100, 200};
|
||||
fl1.swap(fl2);
|
||||
assert(*fl1.begin() == 100);
|
||||
|
||||
std::forward_list<int> a = {1, 3, 5};
|
||||
std::forward_list<int> b = {2, 4, 6};
|
||||
a.merge(b);
|
||||
assert(std::is_sorted(a.begin(), a.end()));
|
||||
|
||||
std::forward_list<int> src = {9, 8};
|
||||
a.splice_after(a.before_begin(), src);
|
||||
assert(*a.begin() == 9);
|
||||
|
||||
a.remove(3);
|
||||
a.remove_if([](int x)
|
||||
{ return x == 5; });
|
||||
|
||||
a.reverse();
|
||||
|
||||
a.push_front(2);
|
||||
a.push_front(2);
|
||||
a.unique();
|
||||
|
||||
a.sort();
|
||||
assert(std::is_sorted(a.begin(), a.end()));
|
||||
|
||||
debug("All forward_list tests passed.");
|
||||
}
|
||||
|
||||
#endif
|
32
Kernel/tests/stl/is_sorted.cpp
Normal file
32
Kernel/tests/stl/is_sorted.cpp
Normal file
@ -0,0 +1,32 @@
|
||||
/*
|
||||
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/>.
|
||||
*/
|
||||
|
||||
#ifdef DEBUG
|
||||
|
||||
#include <assert.h>
|
||||
#include <algorithm>
|
||||
|
||||
void test_stl_is_sorted()
|
||||
{
|
||||
int a[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
|
||||
assert(std::is_sorted(a, a + (sizeof(a) / sizeof(int))));
|
||||
|
||||
int b[] = {1, 2, 3, 4, 5, 6, 7, 8, 10, 9};
|
||||
assert(!std::is_sorted(b, b + (sizeof(b) / sizeof(int))));
|
||||
}
|
||||
|
||||
#endif
|
@ -26,6 +26,8 @@ void test_stl_bitset();
|
||||
void test_stl_string();
|
||||
void test_stl_unordered_map() {}
|
||||
void test_stl_future();
|
||||
void test_stl_forward_list();
|
||||
void test_stl_is_sorted();
|
||||
void test_stl_array();
|
||||
void test_stl_shared_ptr();
|
||||
void test_stl_set();
|
||||
@ -42,6 +44,8 @@ void Test_stl()
|
||||
test_stl_string();
|
||||
test_stl_unordered_map();
|
||||
test_stl_future();
|
||||
test_stl_forward_list();
|
||||
test_stl_is_sorted();
|
||||
test_stl_array();
|
||||
test_stl_shared_ptr();
|
||||
test_stl_set();
|
||||
|
@ -29,7 +29,7 @@ void TaskHeartbeat()
|
||||
while (true)
|
||||
{
|
||||
debug("Task Heartbeat");
|
||||
TaskManager->Sleep(5000);
|
||||
TaskManager->Sleep(Time::FromMilliseconds(5000));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
2
Makefile
2
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 \
|
||||
|
Reference in New Issue
Block a user