diff --git a/sdk/xtdk/amd64/hltypes.h b/sdk/xtdk/amd64/hltypes.h index c52440c..68cdfa2 100644 --- a/sdk/xtdk/amd64/hltypes.h +++ b/sdk/xtdk/amd64/hltypes.h @@ -307,6 +307,17 @@ typedef enum _PIC_I8259_ICW4_SYSTEM_MODE New8086Mode } PIC_I8259_ICW4_SYSTEM_MODE, *PPIC_I8259_ICW4_SYSTEM_MODE; +/* Supported hardware timer backends */ +typedef enum _TIMER_TYPE +{ + TimerNone, + TimerAcpiPm, + TimerHpet, + TimerLapic, + TimerPit, + TimerTsc +} TIMER_TYPE, *PTIMER_TYPE; + /* APIC Base Register */ typedef union _APIC_BASE_REGISTER { @@ -500,6 +511,7 @@ typedef struct _HPET_REGISTERS } Timers[]; } HPET_REGISTERS, *PHPET_REGISTERS; +/* Hardware timer capabilities and CPU clock features */ typedef struct _TIMER_CAPABILITIES { BOOLEAN Arat; diff --git a/sdk/xtdk/amd64/ketypes.h b/sdk/xtdk/amd64/ketypes.h index 5ccd61d..9ccc36f 100644 --- a/sdk/xtdk/amd64/ketypes.h +++ b/sdk/xtdk/amd64/ketypes.h @@ -497,6 +497,7 @@ typedef struct _KPROCESSOR_CONTROL_BLOCK ULONG_PTR MultiThreadProcessorSet; SINGLE_LIST_ENTRY DeferredReadyListHead; PROCESSOR_POWER_STATE PowerState; + ULONG ProfilingCountdown; } KPROCESSOR_CONTROL_BLOCK, *PKPROCESSOR_CONTROL_BLOCK; /* Processor Block structure definition */ diff --git a/sdk/xtdk/amd64/xtstruct.h b/sdk/xtdk/amd64/xtstruct.h index b6a620e..41975a1 100644 --- a/sdk/xtdk/amd64/xtstruct.h +++ b/sdk/xtdk/amd64/xtstruct.h @@ -37,6 +37,7 @@ typedef enum _PIC_I8259_ICW1_OPERATING_MODE PIC_I8259_ICW1_OPERATING_MODE, *PPIC typedef enum _PIC_I8259_ICW4_BUFFERED_MODE PIC_I8259_ICW4_BUFFERED_MODE, *PPIC_I8259_ICW4_BUFFERED_MODE; typedef enum _PIC_I8259_ICW4_EOI_MODE PIC_I8259_ICW4_EOI_MODE, *PPIC_I8259_ICW4_EOI_MODE; typedef enum _PIC_I8259_ICW4_SYSTEM_MODE PIC_I8259_ICW4_SYSTEM_MODE, *PPIC_I8259_ICW4_SYSTEM_MODE; +typedef enum _TIMER_TYPE TIMER_TYPE, *PTIMER_TYPE; typedef enum _TRAMPOLINE_TYPE TRAMPOLINE_TYPE, *PTRAMPOLINE_TYPE; /* Architecture-specific structures forward references */ diff --git a/sdk/xtdk/hltypes.h b/sdk/xtdk/hltypes.h index 649a2b5..2bb4c83 100644 --- a/sdk/xtdk/hltypes.h +++ b/sdk/xtdk/hltypes.h @@ -188,14 +188,33 @@ #define COMPORT_REG_MSR 0x06 /* Modem Status Register */ #define COMPORT_REG_SR 0x07 /* Scratch Register */ +/* Standard system clock rates (in 100-nanosecond units)*/ +#define HL_CLOCK_RATE_1000HZ 10000 /* 1 ms (1000 Hz) - Best Performance */ +#define HL_CLOCK_RATE_500HZ 20000 /* 2 ms (500 Hz) - High Responsiveness */ +#define HL_CLOCK_RATE_300HZ 33333 /* 3.33ms (300 Hz) - Multimedia Sync */ +#define HL_CLOCK_RATE_250HZ 40000 /* 4 ms (250 Hz) - Optimal Balance */ +#define HL_CLOCK_RATE_100HZ 100000 /* 10 ms (100 Hz) - Power Saving */ +#define HL_CLOCK_RATE_50HZ 200000 /* 20 ms (50 Hz) - Deep Power Saving */ + +/* Minimum and maximum system clock rate definitions */ +#define HL_MINIMUM_CLOCK_RATE HL_CLOCK_RATE_1000HZ +#define HL_MAXIMUM_CLOCK_RATE HL_CLOCK_RATE_50HZ + /* Minimum and maximum profile intervals */ -#define MIN_PROFILE_INTERVAL 10000 -#define MAX_PROFILE_INTERVAL 10000000 +#define MIN_PROFILE_INTERVAL 10000 +#define MAX_PROFILE_INTERVAL 10000000 /* C/C++ specific code */ #ifndef __XTOS_ASSEMBLER__ +/* Hardware Layer routine callbacks */ +typedef XTSTATUS (XTAPI *PHALP_INITIALIZE_CLOCK)(VOID); +typedef ULONGLONG (XTAPI *PHALP_QUERY_PERF_COUNTER)(VOID); +typedef ULONG (XTAPI *PHALP_QUERY_TIME_DELTA)(VOID); +typedef ULONG (XTAPI *PHALP_SET_CLOCK_RATE)(IN ULONG Increment); +typedef VOID (XTAPI *PHALP_STALL_EXECUTION)(IN ULONG MicroSeconds); + /* Generic Address structure */ typedef struct _GENERIC_ADDRESS { @@ -498,5 +517,15 @@ typedef struct _SMBIOS3_TABLE_HEADER ULONGLONG TableAddress; } SMBIOS3_TABLE_HEADER, *PSMBIOS3_TABLE_HEADER; +/* Timer dispatch table */ +typedef struct _TIMER_ROUTINES +{ + PHALP_INITIALIZE_CLOCK InitializeClock; + PHALP_QUERY_PERF_COUNTER QueryPerformanceCounter; + PHALP_QUERY_TIME_DELTA QueryTimeDelta; + PHALP_SET_CLOCK_RATE SetClockRate; + PHALP_STALL_EXECUTION StallExecution; +} TIMER_ROUTINES, *PTIMER_ROUTINES; + #endif /* __XTOS_ASSEMBLER__ */ #endif /* __XTDK_HLTYPES_H */ diff --git a/sdk/xtdk/i686/hltypes.h b/sdk/xtdk/i686/hltypes.h index 4dee845..898ed95 100644 --- a/sdk/xtdk/i686/hltypes.h +++ b/sdk/xtdk/i686/hltypes.h @@ -315,6 +315,17 @@ typedef enum _PIC_I8259_ICW4_SYSTEM_MODE New8086Mode } PIC_I8259_ICW4_SYSTEM_MODE, *PPIC_I8259_ICW4_SYSTEM_MODE; +/* Supported hardware timer backends */ +typedef enum _TIMER_TYPE +{ + TimerNone, + TimerAcpiPm, + TimerHpet, + TimerLapic, + TimerPit, + TimerTsc +} TIMER_TYPE, *PTIMER_TYPE; + /* APIC Base Register */ typedef union _APIC_BASE_REGISTER { @@ -508,7 +519,7 @@ typedef struct _HPET_REGISTERS } Timers[]; } HPET_REGISTERS, *PHPET_REGISTERS; -/* Timer Capabilities */ +/* Hardware timer capabilities and CPU clock features */ typedef struct _TIMER_CAPABILITIES { BOOLEAN Arat; diff --git a/sdk/xtdk/i686/ketypes.h b/sdk/xtdk/i686/ketypes.h index 6ffd11f..462082f 100644 --- a/sdk/xtdk/i686/ketypes.h +++ b/sdk/xtdk/i686/ketypes.h @@ -456,6 +456,7 @@ typedef struct _KPROCESSOR_CONTROL_BLOCK VOLATILE ULONG_PTR TimerRequest; SINGLE_LIST_ENTRY DeferredReadyListHead; PROCESSOR_POWER_STATE PowerState; + ULONG ProfilingCountdown; } KPROCESSOR_CONTROL_BLOCK, *PKPROCESSOR_CONTROL_BLOCK; /* Processor Block structure definition */ diff --git a/sdk/xtdk/i686/xtstruct.h b/sdk/xtdk/i686/xtstruct.h index 17f30c8..eb12ff2 100644 --- a/sdk/xtdk/i686/xtstruct.h +++ b/sdk/xtdk/i686/xtstruct.h @@ -37,6 +37,7 @@ typedef enum _PIC_I8259_ICW1_OPERATING_MODE PIC_I8259_ICW1_OPERATING_MODE, *PPIC typedef enum _PIC_I8259_ICW4_BUFFERED_MODE PIC_I8259_ICW4_BUFFERED_MODE, *PPIC_I8259_ICW4_BUFFERED_MODE; typedef enum _PIC_I8259_ICW4_EOI_MODE PIC_I8259_ICW4_EOI_MODE, *PPIC_I8259_ICW4_EOI_MODE; typedef enum _PIC_I8259_ICW4_SYSTEM_MODE PIC_I8259_ICW4_SYSTEM_MODE, *PPIC_I8259_ICW4_SYSTEM_MODE; +typedef enum _TIMER_TYPE TIMER_TYPE, *PTIMER_TYPE; typedef enum _TRAMPOLINE_TYPE TRAMPOLINE_TYPE, *PTRAMPOLINE_TYPE; /* Architecture-specific structures forward references */ diff --git a/sdk/xtdk/xtstruct.h b/sdk/xtdk/xtstruct.h index 30f9bfd..ff591e7 100644 --- a/sdk/xtdk/xtstruct.h +++ b/sdk/xtdk/xtstruct.h @@ -338,6 +338,7 @@ typedef struct _STRING32 STRING32, *PSTRING32; typedef struct _STRING64 STRING64, *PSTRING64; typedef struct _THREAD_INFORMATION_BLOCK THREAD_INFORMATION_BLOCK, *PTHREAD_INFORMATION_BLOCK; typedef struct _TIME_FIELDS TIME_FIELDS, *PTIME_FIELDS; +typedef struct _TIMER_ROUTINES TIMER_ROUTINES, *PTIMER_ROUTINES; typedef struct _UEFI_FIRMWARE_INFORMATION UEFI_FIRMWARE_INFORMATION, *PUEFI_FIRMWARE_INFORMATION; typedef struct _UNICODE_STRING UNICODE_STRING, *PUNICODE_STRING; typedef struct _UNICODE_STRING32 UNICODE_STRING32, *PUNICODE_STRING32; @@ -359,7 +360,6 @@ typedef struct _XTBL_MODULE_AUTHORS XTBL_MODULE_AUTHORS, *PXTBL_MODULE_AUTHORS; typedef struct _XTBL_MODULE_DEPS XTBL_MODULE_DEPS, *PXTBL_MODULE_DEPS; typedef struct _XTBL_MODULE_INFO XTBL_MODULE_INFO, *PXTBL_MODULE_INFO; typedef struct _XTBL_PAGE_MAPPING XTBL_PAGE_MAPPING, *PXTBL_PAGE_MAPPING; -typedef struct _XTBL_SHELL_COMMAND XTBL_SHELL_COMMAND, *PXTBL_SHELL_COMMAND; typedef struct _XTBL_STATUS XTBL_STATUS, *PXTBL_STATUS; /* Unions forward references */ diff --git a/xtoskrnl/hl/data.cc b/xtoskrnl/hl/data.cc index 4a2b899..f20821d 100644 --- a/xtoskrnl/hl/data.cc +++ b/xtoskrnl/hl/data.cc @@ -9,59 +9,104 @@ #include -/* ACPI tables cache count */ +/* Tracks the total number of currently cached ACPI tables */ ULONG HL::Acpi::CacheCount = 0; -/* ACPI tables cache entries */ +/* Array holding the cached ACPI tables */ ACPI_CACHE_LIST HL::Acpi::CacheEntries[ACPI_MAX_CACHED_TABLES]; -/* ACPI tables cache list */ +/* Head of the linked list tracking dynamically mapped ACPI tables */ LIST_ENTRY HL::Acpi::CacheList; -/* ACPI Root System Description Pointer (RSDP) */ +/* Pointer to the ACPI Root System Description Pointer (RSDP) */ PACPI_RSDP HL::Acpi::RsdpStructure; -/* System information */ +/* Global architectural system states and hardware feature flags parsed from the ACPI tables */ ACPI_SYSTEM_INFO HL::Acpi::SystemInfo; -/* ACPI timer information */ +/* Hardware configuration details and port addresses for the ACPI Power Management Timer */ ACPI_TIMER_INFO HL::Acpi::TimerInfo; -/* Active processors count */ +/* Represents the number of active processors */ KAFFINITY HL::Cpu::ActiveProcessors; -/* FrameBuffer information */ +/* Metadata detailing the linear frame buffer geometry */ HL_FRAMEBUFFER_DATA HL::FrameBuffer::FrameBufferData; /* Pointer to the RAM shadow buffer used for double-buffered rendering */ PVOID HL::FrameBuffer::ScreenShadowBuffer; -/* Scroll region information */ +/* Tracks the bounding box, dimensions, and cursor position for the kernel video console */ HL_SCROLL_REGION_DATA HL::FrameBuffer::ScrollRegionData; -/* APIC mode */ +/* Indicates the active hardware interrupt controller mode */ APIC_MODE HL::Pic::ApicMode; -/* Number of I/O APIC controllers */ +/* Total number of I/O APIC chips discovered and initialized */ ULONG HL::Pic::ControllerCount; -/* I/O APIC controllers information */ +/* Array containing MMIO bases, IDs, and line counts for all I/O APICs */ IOAPIC_DATA HL::Pic::Controllers[IOAPIC_MAX_CONTROLLERS]; -/* Number of I/O APIC overrides */ +/* Total number of legacy ISA interrupt routing overrides */ ULONG HL::Pic::IrqOverrideCount; -/* I/O APIC overrides information */ +/* Hardware routing definitions mapping legacy ISA interrupts to Global System Interrupts (GSI) */ ACPI_MADT_INTERRUPT_OVERRIDE HL::Pic::IrqOverrides[IOAPIC_MAX_OVERRIDES]; -/* Mapped interrupt vectors */ +/* Lookup table mapping allocated hardware APIC vectors to their assigned Run Levels */ UCHAR HL::Pic::MappedVectors[256]; -/* Kernel profiling interval */ -ULONG HL::Timer::ProfilingInterval; +/* Accumulated tick value of the ACPI Power Management Timer */ +ULONG HL::Timer::AcpiPmPerformanceCounter = 0; + +/* Primary hardware timer driving the periodic system clock interrupt */ +TIMER_TYPE HL::Timer::ClockType; + +/* Fractional remainder used to maintain long-term system clock accuracy */ +ULONG HL::Timer::FractionalIncrement = 0; + +/* Virtual address mapped to the HPET hardware MMIO registers */ +PVOID HL::Timer::HpetAddress = NULLPTR; + +/* Operating frequency of the High Precision Event Timer in ticks per second */ +ULONGLONG HL::Timer::HpetFrequency = 0; + +/* Spinlock protecting concurrent multiprocessor access to the global performance counters */ +KSPIN_LOCK HL::Timer::PerformanceCounterLock; + +/* The performance counter frequency in ticks per second */ +ULONGLONG HL::Timer::PerformanceFrequency = 0; + +/* Absolute monotonic tick count driven by the legacy Programmable Interval Timer */ +ULONGLONG HL::Timer::PitPerformanceCounter; + +/* Programmed hardware divider interval used to increment the PIT performance counter */ +ULONG HL::Timer::PitRollover; + +/* Flag indicating whether statistical system execution profiling is currently active */ +BOOLEAN HL::Timer::ProfilingEnabled = FALSE; + +/* Tick interval required to trigger a profile interrupt */ +ULONG HL::Timer::ProfilingTicks; + +/* Global accumulator for fractional time drift and precision compensation */ +ULONG HL::Timer::RunningFraction = 0; + +/* System counter driven by the highest precision available hardware */ +ULONGLONG HL::Timer::SystemPerformanceCounter; + +/* Current base clock increment in standard 100-nanosecond intervals */ +ULONG HL::Timer::TimeIncrement = 0; /* Timer capabilities */ TIMER_CAPABILITIES HL::Timer::TimerCapabilities = {0}; /* APIC timer frequency */ ULONG HL::Timer::TimerFrequency; + +/* Function dispatch table for the active hardware timer backend */ +TIMER_ROUTINES HL::Timer::TimerRoutines = {0}; + +/* Identifies the hardware timer backing */ +TIMER_TYPE HL::Timer::TimerType; diff --git a/xtoskrnl/hl/x86/timer.cc b/xtoskrnl/hl/x86/timer.cc index e19256e..5e7a1d4 100644 --- a/xtoskrnl/hl/x86/timer.cc +++ b/xtoskrnl/hl/x86/timer.cc @@ -2,7 +2,7 @@ * PROJECT: ExectOS * COPYRIGHT: See COPYING.md in the top level directory * FILE: xtoskrnl/hl/x86/timer.cc - * DESCRIPTION: APIC Timer support for x86 (i686/AMD64) support + * DESCRIPTION: Timer support for x86 (i686/AMD64) * DEVELOPERS: Aiken Harris */ @@ -36,8 +36,8 @@ HL::Timer::CalibrateApicTimer() /* Load the initial count into the APIC Timer and begin the countdown */ HL::Pic::WriteApicRegister(APIC_TICR, InitialCount); - /* Wait for 10ms */ - StallExecution(10000); + /* Stall CPU execution for exactly 10 milliseconds */ + StallExecutionPit(10000); /* Read current tick count from APIC timer and clear APIC timer */ CurrentCount = HL::Pic::ReadApicRegister(APIC_TCCR); @@ -62,6 +62,278 @@ HL::Timer::CalibrateApicTimer() return STATUS_SUCCESS; } +/** + * Calibrates the Invariant TSC frequency. + * + * @return This routine returns the calculated TSC frequency in Hz. + * + * @since XT 1.0 + */ +XTAPI +ULONGLONG +HL::Timer::CalibrateTscCounter(VOID) +{ + ULONGLONG InitialTickCount, FinalTickCount; + ULONG TscAux; + + /* Get TSC frequency from the Core Crystal Clock */ + if(TimerCapabilities.Art && TimerCapabilities.TimerFrequency != 0 && TimerCapabilities.TscDenominator != 0) + { + /* CCC available, use it as the source of TSC frequency */ + return (TimerCapabilities.TimerFrequency * TimerCapabilities.TscNumerator) / TimerCapabilities.TscDenominator; + } + + /* Latch the initial TSC value */ + InitialTickCount = AR::CpuFunc::ReadTimeStampCounterProcessor(&TscAux); + + /* Stall CPU execution for exactly 10 milliseconds */ + StallExecutionPit(10000); + + /* Read current tick count from TSC */ + FinalTickCount = AR::CpuFunc::ReadTimeStampCounterProcessor(&TscAux); + + /* Calculate the elapsed ticks over the 10ms window */ + return (FinalTickCount - InitialTickCount) * 100; +} + +/** + * Calculates and configures the system time increments based on hardware timer properties. + * + * @param BaseFrequency + * Supplies the base running frequency of the hardware timer in Hz. + * + * @param HardwareDivider + * Supplies the programmed threshold/divider for the interrupt generation. + */ +XTAPI +VOID +HL::Timer::ConfigureTimeIncrement(IN ULONGLONG BaseFrequency, IN ULONGLONG HardwareDivider) +{ + /* Calculate the time increment */ + TimeIncrement = (ULONG)((10000000ULL * HardwareDivider) / BaseFrequency); + + /* Extract the fractional remainder */ + FractionalIncrement = (ULONG)((10000000000ULL * HardwareDivider) / BaseFrequency) % 1000; + RunningFraction = 0; + + /* Synchronize the kernel's global timekeeping state with the new resolution settings */ + KE::SystemTime::SetTimeIncrement(TimeIncrement, TimeIncrement); +} + +/** + * Initializes the High Precision Event Timer (HPET) by discovering its ACPI configuration and mapping + * its hardware registers into the kernel's virtual address space. + * + * @return This routine returns a status code. + * + * @since XT 1.0 + */ +XTAPI +XTSTATUS +HL::Timer::DetectHpet(VOID) +{ + ULONGLONG CounterPeriod; + PACPI_HPET HpetTable; + PHPET_REGISTERS Hpet; + XTSTATUS Status; + + /* Reset global HPET state */ + HpetAddress = NULLPTR; + HpetFrequency = 0; + + /* Retrieve the HPET table from the ACPI subsystem */ + Status = HL::Acpi::GetAcpiTable(ACPI_HPET_SIGNATURE, (PACPI_DESCRIPTION_HEADER*)&HpetTable); + if(Status != STATUS_SUCCESS || !HpetTable) + { + /* HPET is not present on this system, return error code */ + return STATUS_NOT_FOUND; + } + + /* Verify that the hardware registers are accessible via MMIO */ + if(HpetTable->BaseAddress.AddressSpaceID != ACPI_ADDRESS_SPACE_MEMORY) + { + /* HPET base address not found, return error */ + return STATUS_NOT_SUPPORTED; + } + + /* Extract the physical base address from the Generic Address Structure (GAS) */ + if(HpetTable->BaseAddress.Address.QuadPart == 0) + { + /* Invalid address provided by firmware, return error code */ + return STATUS_UNSUCCESSFUL; + } + + /* Map the physical hardware registers into the kernel's virtual memory space */ + Status = MM::HardwarePool::MapHardwareMemory(HpetTable->BaseAddress.Address, 1, FALSE, &HpetAddress); + if(Status != STATUS_SUCCESS || !HpetAddress) + { + /* Memory mapping failed, return error code */ + return Status; + } + + /* Configure the mapped memory region with Write-Through caching semantics */ + MM::HardwarePool::MarkHardwareMemoryWriteThrough(HpetAddress, 1); + + /* Extract the main counter period */ + Hpet = (PHPET_REGISTERS)HpetAddress; + CounterPeriod = (Hpet->GeneralCapabilities >> 32) & 0xFFFFFFFF; + + /* Calculate the HPET operating frequency */ + if(CounterPeriod != 0) + { + /* Convert femtoseconds per tick to ticks per second */ + HpetFrequency = 1000000000000000ULL / CounterPeriod; + } + else + { + /* Assume the standard minimum HPET frequency (14.31818 MHz) */ + HpetFrequency = 14318180; + } + + /* Return success */ + return STATUS_SUCCESS; +} + +/** + * Services the primary hardware clock interrupt, advancing the global system time, + * maintaining local CPU thread quantums and performing profiling. + * + * @param TrapFrame + * Supplies a pointer to the hardware trap frame representing the interrupted execution context. + * + * @return This routine does not return any value. + * + * @since XT 1.0 + */ +XTCDECL +VOID +HL::Timer::HandleClockInterrupt(IN PKTRAP_FRAME TrapFrame) +{ + PKPROCESSOR_CONTROL_BLOCK Prcb; + KRUNLEVEL RunLevel; + ULONG Increment; + + /* Start the interrupt */ + HL::Irq::BeginSystemInterrupt(CLOCK_LEVEL, &RunLevel); + + /* Check if PIT is the source of the interrupt */ + if(ClockType == TimerPit) + { + /* Check if system clock initialized the rollover */ + if(PitRollover != 0) + { + /* Update the PIT counter */ + PitPerformanceCounter += PitRollover; + } + } + + /* Check if profiling is currently active */ + if(ProfilingEnabled) + { + /* Retrieve the processor-specific control block */ + Prcb = KE::Processor::GetCurrentProcessorControlBlock(); + + /* Trigger a profile sample if the countdown has expired */ + if(Prcb->ProfilingCountdown == 0) + { + /* Dispatch the profile interrupt */ + HL::Irq::HandleProfileInterrupt(TrapFrame); + + /* Reset the local countdown */ + Prcb->ProfilingCountdown = ProfilingTicks; + } + + /* Decrement the profiling countdown for the current hardware tick */ + Prcb->ProfilingCountdown--; + } + + /* Initialize the increment and update the fractional drift accumulator */ + Increment = TimeIncrement; + RunningFraction += FractionalIncrement; + + /* Check for fractional overflow */ + if(RunningFraction >= 1000) + { + /* Apply the compensation tick and normalize the fractional remainder */ + Increment++; + RunningFraction -= 1000; + } + + /* Route the timekeeping logic based on the underlying timer topology */ + if(ClockType == TimerLapic) + { + /* Restrict global system time updates exclusively to the Bootstrap Processor */ + if(KE::Processor::GetCurrentProcessorNumber() == 0) + { + /* Advance the global system time */ + KE::SystemTime::UpdateSystemTime(TrapFrame, Increment, RunLevel); + } + else + { + /* Limit Application Processors (APs) to update runtimes */ + KE::Dispatcher::UpdateRunTime(TrapFrame, RunLevel); + } + } + else + { + /* Advance the global system time */ + KE::SystemTime::UpdateSystemTime(TrapFrame, Increment, RunLevel); + + /* Broadcast an IPI to awaken all APs for local quantum updates */ + HL::Pic::SendBroadcastIpi(APIC_VECTOR_CLOCK_IPI, FALSE); + } + + /* End the interrupt */ + HL::Irq::EndInterrupt(TrapFrame, RunLevel); +} + +/** + * Services the inter-processor clock interrupt, maintaining local CPU thread quantums and performing profiling. + * + * @param TrapFrame + * Supplies a pointer to the hardware trap frame representing the interrupted execution context. + * + * @return This routine does not return any value. + * + * @since XT 1.0 + */ +XTCDECL +VOID +HL::Timer::HandleClockIpiInterrupt(IN PKTRAP_FRAME TrapFrame) +{ + PKPROCESSOR_CONTROL_BLOCK Prcb; + KRUNLEVEL RunLevel; + + /* Start the interrupt */ + HL::Irq::BeginSystemInterrupt(CLOCK_LEVEL, &RunLevel); + + /* Check if profiling is currently active */ + if(ProfilingEnabled) + { + /* Retrieve the processor-specific control block */ + Prcb = KE::Processor::GetCurrentProcessorControlBlock(); + + /* Trigger a profile sample if the countdown has expired */ + if(Prcb->ProfilingCountdown == 0) + { + /* Dispatch the profile interrupt */ + HL::Irq::HandleProfileInterrupt(TrapFrame); + + /* Reset the local countdown */ + Prcb->ProfilingCountdown = ProfilingTicks; + } + + /* Decrement the profiling countdown for the current hardware tick */ + Prcb->ProfilingCountdown--; + } + + /* Call the kernel to update runtimes */ + KE::Dispatcher::UpdateRunTime(TrapFrame, RunLevel); + + /* End the interrupt */ + HL::Irq::EndInterrupt(TrapFrame, RunLevel); +} + /** * Initializes and calibrates the Local APIC Timer. * @@ -70,10 +342,12 @@ HL::Timer::CalibrateApicTimer() * @since XT 1.0 */ XTAPI -VOID +XTSTATUS HL::Timer::InitializeApicTimer(VOID) { + APIC_LVT_REGISTER LvtRegister; XTSTATUS Status; + ULONG Divider; /* Set APIC timer to divide by 1 */ HL::Pic::WriteApicRegister(APIC_TDCR, TIMER_DivideBy1); @@ -82,19 +356,149 @@ HL::Timer::InitializeApicTimer(VOID) Status = CalibrateApicTimer(); if(Status != STATUS_SUCCESS) { - /* System cannot operate without a calibrated system timer, raise kernel panic */ - KE::Crash::Panic(0); + /* APIC calibration failed, return error code */ + return Status; } - /* Set the default system profile interval */ - HL::Timer::SetProfileInterval(1000); + /* Calculate the hardware threshold */ + Divider = TimerFrequency / 1000; /* Program the APIC timer for periodic mode */ - StopProfileInterrupt(ProfileXtKernel); + LvtRegister.Long = 0; + LvtRegister.Mask = 0; + LvtRegister.TimerMode = 1; + LvtRegister.DeliveryMode = APIC_DM_FIXED; + LvtRegister.TriggerMode = APIC_TGM_EDGE; + LvtRegister.Vector = APIC_VECTOR_CLOCK; + HL::Pic::WriteApicRegister(APIC_TMRLVTR, LvtRegister.Long); + + /* Jump-start the heartbeat */ + HL::Pic::WriteApicRegister(APIC_TICR, Divider); + + /* Configure the kernel timekeeping abstractions based on calibrated APIC metrics */ + HL::Timer::ConfigureTimeIncrement(TimerFrequency, Divider); + + /* Return success */ + return STATUS_SUCCESS; } /** - * Performs a basic Timer initialization. + * Initializes the High Precision Event Timer (HPET) Timer. + * + * @return This routine returns a status code. + * + * @since XT 1.0 + */ +XTAPI +XTSTATUS +HL::Timer::InitializeHpetTimer(VOID) +{ + ULONGLONG ClockPeriodTicks, TicksPerMillisecond; + PHPET_REGISTERS Hpet; + + /* Ensure the HPET hardware registers are successfully mapped */ + if(!HpetAddress) + { + /* The hardware mapping is not present, return error code */ + return STATUS_UNSUCCESSFUL; + } + + /* Cast the mapped virtual address to the HPET hardware register */ + Hpet = (PHPET_REGISTERS)HpetAddress; + + /* Halt the main counter and disable legacy routing */ + Hpet->GeneralConfiguration &= ~(HPET_CONFIG_ENABLE | HPET_CONFIG_LEGACY_REPLACEMENT); + + /* Reset the main counter to a known baseline */ + Hpet->MainCounterValue = 0; + + /* Calculate the required hardware ticks for a standard 1-millisecond system clock interval */ + TicksPerMillisecond = HpetFrequency / 1000; + ClockPeriodTicks = TicksPerMillisecond * 1; + + /* Configure Comparator 0 for periodic mode with interrupt enable */ + Hpet->Timers[0].Configuration |= (HPET_TIMER_CONFIG_ENABLED | + HPET_TIMER_CONFIG_PERIODIC | + HPET_TIMER_CONFIG_VALUE_ACCUMULATOR); + + /* Write the initial comparator value */ + Hpet->Timers[0].Comparator = ClockPeriodTicks; + + /* Write the periodic interval into the accumulator */ + Hpet->Timers[0].Comparator = ClockPeriodTicks; + + /* Enable the main counter and activate legacy replacement routing */ + Hpet->GeneralConfiguration |= (HPET_CONFIG_ENABLE | HPET_CONFIG_LEGACY_REPLACEMENT); + + /* Configure the kernel timekeeping abstractions based on HPET metrics */ + ConfigureTimeIncrement(HpetFrequency, ClockPeriodTicks); + + /* Route the IRQ 0 to the primary system clock vector */ + HL::Pic::AllocateSystemInterrupt(0, CLOCK_LEVEL, APIC_VECTOR_CLOCK); + + /* Return success */ + return STATUS_SUCCESS; +} + +/** + * Initializes the legacy Programmable Interval Timer (PIT). + * + * @return This routine returns a status code. + * + * @since XT 1.0 + */ +XTAPI +XTSTATUS +HL::Timer::InitializePitTimer(VOID) +{ + UCHAR LowByte, HighByte; + ULONG Divider; + + /* Set the target frequency (1000 Hz) and calculate the required hardware divider */ + Divider = PIT_BASE_FREQUENCY / 1000; + + /* Clamp the calculated divider to prevent 16-bit hardware overflow */ + if(Divider > 65535) + { + /* Cap the maximum possible interval */ + Divider = 65535; + } + else if(Divider < 1) + { + /* Enforce a minimum hardware divider */ + Divider = 1; + } + + /* Persist the calculated divider into a global state */ + PitRollover = Divider; + + /* Split the 16-bit divider into an independent LSB and MSB */ + LowByte = (UCHAR)(Divider & 0xFF); + HighByte = (UCHAR)((Divider >> 8) & 0xFF); + + /* Configure Channel 0 for LSB/MSB access, Mode 2 */ + HL::IoPort::WritePort8(PIT_COMMAND_PORT, PIT_CMD_CHANNEL0 | + PIT_CMD_ACCESS_LOWBYTE_HIGHBYTE | + PIT_MODE2_RATE_GENERATOR); + + /* Transmit the Least Significant Byte (LSB) */ + HL::IoPort::WritePort8(PIT_DATA_PORT0, LowByte); + + /* Transmit the Most Significant Byte (MSB) */ + HL::IoPort::WritePort8(PIT_DATA_PORT0, HighByte); + + /* Configure the kernel timekeeping abstractions based on legacy PIT metrics */ + ConfigureTimeIncrement(PIT_BASE_FREQUENCY, Divider); + + /* Route the standard PIT IRQ 0 to the primary system clock vector */ + HL::Pic::AllocateSystemInterrupt(0, CLOCK_LEVEL, APIC_VECTOR_CLOCK); + + /* Return success */ + return STATUS_SUCCESS; +} + +/** + * Performs the primary initialization of the system timer. * * @return This routine does not return any value. * @@ -104,18 +508,36 @@ XTAPI VOID HL::Timer::InitializeTimer(VOID) { - /* Query timer capabilities */ - QueryTimerCapabilities(); + XTSTATUS Status; - /* Initialize the APIC timer */ - InitializeApicTimer(); + /* Probe hardware capabilities */ + ProbeTimerType(); + + /* Check if a suitable hardware timer was selected */ + if(TimerRoutines.InitializeClock) + { + /* Proceed with system clock initialization */ + Status = TimerRoutines.InitializeClock(); + if(Status != STATUS_SUCCESS) + { + /* System cannot operate without a functional system clock interrupt */ + KE::Crash::Panic(0); + } + } + + /* Set the default system profile interval */ + HL::Timer::SetProfileInterval(1000); + + /* Ensure the profile interrupt generation is explicitly disabled */ + StopProfileInterrupt(ProfileXtKernel); + + /* Register the system clock interrupt handler */ + HL::Irq::RegisterSystemInterruptHandler(APIC_VECTOR_CLOCK, HandleClockInterrupt); + HL::Irq::RegisterSystemInterruptHandler(APIC_VECTOR_CLOCK_IPI, HandleClockIpiInterrupt); } /** - * Stalls the CPU execution for a specified duration (maximum 3 seconds) using the legacy PIT timer. - * - * @param MicroSeconds - * Specifies the number of microseconds to stall execution. + * Performs an initial Timer initialization, discovering hardware and routing the Timer Dispatch table. * * @return This routine does not return any value. * @@ -123,53 +545,366 @@ HL::Timer::InitializeTimer(VOID) */ XTAPI VOID -HL::Timer::PitStallExecution(IN ULONG MicroSeconds) +HL::Timer::ProbeTimerType(VOID) { - USHORT CurrentCount, PreviousCount; - ULONG TargetTicks, TickCounter; + PACPI_TIMER_INFO AcpiTimerInfo; + WCHAR ParameterValue[16]; + XTSTATUS Status; - /* Validate input parameter */ - if(MicroSeconds == 0) + /* Enumerate hardware timing capabilities */ + QueryTimerCapabilities(); + + /* Discover and map the High Precision Event Timer */ + Status = DetectHpet(); + if(Status != STATUS_SUCCESS) { - /* Nothing to do */ - return; - } - else if(MicroSeconds > 3000000) - { - /* Cap execution stall to 3 seconds */ - MicroSeconds = 3000000; + /* Diagnostic warning upon HPET subsystem initialization failure */ + DebugPrint(L"TIMER: HPET initialization failed or HPET not present\n"); } - /* Convert us to PIT ticks and initialize tick counter */ - TargetTicks = (MicroSeconds * 1193) / 1000; - TickCounter = 0; + /* Initialize the hardware selection states */ + ClockType = TimerNone; + TimerType = TimerNone; - /* Configure PIT Channel 0: Read/Write LSB then MSB, Mode 0 (Interrupt on Terminal Count) */ - HL::IoPort::WritePort8(PIT_COMMAND_PORT, 0x30); - - /* Initialize the PIT counter with the maximum possible value (0xFFFF) */ - HL::IoPort::WritePort8(PIT_DATA_PORT0, 0xFF); - HL::IoPort::WritePort8(PIT_DATA_PORT0, 0xFF); - - /* Latch and read the initial counter value */ - HL::IoPort::WritePort8(PIT_COMMAND_PORT, 0x00); - PreviousCount = HL::IoPort::ReadPort8(PIT_DATA_PORT0); - PreviousCount |= (HL::IoPort::ReadPort8(PIT_DATA_PORT0) << 8); - - /* Poll the PIT */ - while(TickCounter < TargetTicks) + /* Query the kernel boot environment for a user-specified system clock override */ + if(KE::BootInformation::GetKernelParameterValue(L"CLOCK", ParameterValue, 16) == STATUS_SUCCESS) { - /* Latch the current counter value without stopping the timer */ + /* Evaluate the boot parameter string */ + if(RTL::WideString::FindWideStringInsensitive(ParameterValue, L"HPET")) + { + /* Designate the HPET */ + ClockType = TimerHpet; + } + else if(RTL::WideString::FindWideStringInsensitive(ParameterValue, L"LAPIC")) + { + /* Designate the Local APIC */ + ClockType = TimerLapic; + } + else if(RTL::WideString::FindWideStringInsensitive(ParameterValue, L"PIT")) + { + /* Designate the legacy PIT */ + ClockType = TimerPit; + } + } + + /* Query the kernel boot environment for a user-specified performance counter override */ + if(KE::BootInformation::GetKernelParameterValue(L"TIMER", ParameterValue, 16) == STATUS_SUCCESS) + { + /* Evaluate the boot parameter string */ + if(RTL::WideString::FindWideStringInsensitive(ParameterValue, L"ACPI") || + RTL::WideString::FindWideStringInsensitive(ParameterValue, L"PM")) + { + /* Designate the ACPI PM Timer */ + TimerType = TimerAcpiPm; + } + else if(RTL::WideString::FindWideStringInsensitive(ParameterValue, L"HPET")) + { + /* Designate the HPET */ + TimerType = TimerHpet; + } + else if(RTL::WideString::FindWideStringInsensitive(ParameterValue, L"PIT")) + { + /* Designate the legacy PIT */ + TimerType = TimerPit; + } + else if(RTL::WideString::FindWideStringInsensitive(ParameterValue, L"TSC")) + { + /* Designate the Invariant TSC */ + TimerType = TimerTsc; + } + } + + /* Verify the hardware viability of the requested system clock */ + if(ClockType != TimerNone && !ValidateTimerSupport(ClockType, TRUE)) + { + /* Invalidate the clock selection upon hardware validation failure */ + DebugPrint(L"TIMER: Requested system clock [%d] unavailable\n", ClockType); + ClockType = TimerNone; + } + + /* Verify the hardware viability of the requested performance counter */ + if(TimerType != TimerNone && !ValidateTimerSupport(TimerType, FALSE)) + { + /* Invalidate the counter selection upon hardware validation failure */ + DebugPrint(L"TIMER: Requested hardware counter [%d] unavailable\n", TimerType); + TimerType = TimerNone; + } + + /* Execute the autonomous hardware selection for the system clock */ + if(ClockType == TimerNone) + { + /* Probe system clock suitability */ + if(ValidateTimerSupport(TimerLapic, TRUE)) + { + /* Select the Local APIC */ + ClockType = TimerLapic; + } + else if(ValidateTimerSupport(TimerHpet, TRUE)) + { + /* Select the HPET */ + ClockType = TimerHpet; + } + else + { + /* Fallback to the legacy PIT */ + ClockType = TimerPit; + } + } + + /* Execute the autonomous hardware selection for the performance counter */ + if(TimerType == TimerNone) + { + /* Probe the performance counter suitability */ + if(ValidateTimerSupport(TimerTsc, FALSE)) + { + /* Select the Invariant TSC */ + TimerType = TimerTsc; + } + else if(ValidateTimerSupport(TimerHpet, FALSE)) + { + /* Select the HPET */ + TimerType = TimerHpet; + } + else if(ValidateTimerSupport(TimerAcpiPm, FALSE)) + { + /* Select the ACPI PM Timer */ + TimerType = TimerAcpiPm; + } + else + { + /* Fallback to the legacy PIT */ + TimerType = TimerPit; + } + } + + /* Retrieve the ACPI PM Timer hardware configuration */ + HL::Acpi::GetAcpiTimerInfo(&AcpiTimerInfo); + + /* Determine if the ACPI PM Timer port is physically provisioned */ + if(AcpiTimerInfo->TimerPort != 0) + { + /* Route execution stalls through the active ACPI PM hardware */ + TimerRoutines.StallExecution = StallExecutionAcpiPm; + } + else + { + /* Route execution stalls through the legacy PIT hardware */ + TimerRoutines.StallExecution = StallExecutionPit; + } + + /* Dispatch the initialization routines based on the resolved system clock */ + switch(ClockType) + { + case TimerLapic: + /* Register the Local APIC initialization handler */ + TimerRoutines.InitializeClock = InitializeApicTimer; + TimerRoutines.SetClockRate = SetClockRateApic; + DebugPrint(L"System Clock: Local APIC Timer (ARAT), "); + break; + case TimerHpet: + /* Register the HPET comparator initialization handler */ + TimerRoutines.InitializeClock = InitializeHpetTimer; + TimerRoutines.SetClockRate = NULLPTR; + DebugPrint(L"System Clock: HPET Comparator, "); + break; + default: + /* Register the legacy PIT initialization handler */ + TimerRoutines.InitializeClock = InitializePitTimer; + TimerRoutines.SetClockRate = NULLPTR; + DebugPrint(L"System Clock: Legacy PIT, "); + break; + } + + /* Dispatch the routines and configure baseline frequencies for the performance counter */ + switch(TimerType) + { + case TimerTsc: + /* Register the TSC */ + TimerRoutines.QueryPerformanceCounter = QueryPerformanceCounterTsc; + PerformanceFrequency = CalibrateTscCounter(); + DebugPrint(L"Performance Counter: Invariant TSC @ %lluHz\n", PerformanceFrequency); + break; + case TimerHpet: + /* Register the HPET */ + TimerRoutines.QueryPerformanceCounter = QueryPerformanceCounterHpet; + PerformanceFrequency = HpetFrequency; + DebugPrint(L"Performance Counter: HPET @ %lluHz\n", PerformanceFrequency); + break; + case TimerAcpiPm: + /* Register the ACPI PM */ + TimerRoutines.QueryPerformanceCounter = QueryPerformanceCounterAcpiPm; + PerformanceFrequency = 3579545; + KeInitializeSpinLock(&PerformanceCounterLock); + DebugPrint(L"Performance Counter: ACPI PM Timer\n"); + break; + default: + /* Register the legacy PIT */ + TimerRoutines.QueryPerformanceCounter = QueryPerformanceCounterPit; + PerformanceFrequency = 1193182; + KeInitializeSpinLock(&PerformanceCounterLock); + DebugPrint(L"Performance Counter: Legacy PIT\n"); + break; + } +} + +/** + * Retrieves the current value of the high-resolution performance counter. + * + * @param PerformanceFrequency + * Suplies an optional pointer to a variable that receives the performance counter frequency in Hz. + * + * @return This routine returns the current 64-bit monotonic tick count. + * + * @since XT 1.0 + */ +XTAPI +LARGE_INTEGER +HL::Timer::QueryPerformanceCounter(OUT PLARGE_INTEGER Frequency) +{ + LARGE_INTEGER CurrentCounter; + + /* Check if the caller requested the frequency */ + if(Frequency) + { + /* Assign the cached hardware frequency */ + Frequency->QuadPart = PerformanceFrequency; + } + + /* Dispatch to the specific hardware implementation */ + CurrentCounter.QuadPart = TimerRoutines.QueryPerformanceCounter(); + + /* Return the retrieved timestamp */ + return CurrentCounter; +} + +/** + * Queries the current value of the ACPI Power Management Timer. + * + * @return This routine returns the current ACPI timer tick count. + * + * @since XT 1.0 + */ +XTAPI +ULONGLONG +HL::Timer::QueryPerformanceCounterAcpiPm(VOID) +{ + PACPI_TIMER_INFO AcpiTimerInfo; + ULONG CurrentValue; + + /* Retrieve the ACPI Timer configuration */ + HL::Acpi::GetAcpiTimerInfo(&AcpiTimerInfo); + + /* Raise RunLevel to DISPATCH_LEVEL and acquire spinlock */ + KE::RaiseRunLevel RunLevel(DISPATCH_LEVEL); + KE::SpinLockGuard SpinLockGuard(&PerformanceCounterLock); + + /* Read the current hardware value and apply the bitmask */ + CurrentValue = HL::IoPort::ReadPort32(AcpiTimerInfo->TimerPort) & AcpiTimerInfo->MsbMask; + + /* Calculate delta, accumulate the results and update last value for the next call */ + SystemPerformanceCounter += (CurrentValue - AcpiPmPerformanceCounter) & AcpiTimerInfo->MsbMask; + AcpiPmPerformanceCounter = CurrentValue; + + /* Return the accumulated value */ + return SystemPerformanceCounter; +} + +/** + * Queries the current value of the High Precision Event Timer (HPET). + * + * @return This routine returns the current HPET main counter value. + * + * @since XT 1.0 + */ +XTAPI +ULONGLONG +HL::Timer::QueryPerformanceCounterHpet(VOID) +{ + /* Perform a direct MMIO read from the register address */ + return ((PHPET_REGISTERS)HpetAddress)->MainCounterValue; +} + +/** + * Queries the current value of the Legacy PIT. + * + * @return This routine returns the interpolated tick count. + * + * @since XT 1.0 + */ +XTAPI +ULONGLONG +HL::Timer::QueryPerformanceCounterPit(VOID) +{ + ULONG ClockDelta, CounterValue; + ULONGLONG PerformanceCounter; + + /* Check if system clock initialized the rollover */ + if(PitRollover == 0) + { + /* Return the baseline counter value */ + return SystemPerformanceCounter; + } + + /* Raise RunLevel to DISPATCH_LEVEL and acquire spinlock */ + KE::RaiseRunLevel RunLevel(DISPATCH_LEVEL); + KE::SpinLockGuard SpinLockGuard(&PerformanceCounterLock); + + /* Repeatedly sample the global tick count and the hardware counter */ + do + { + /* Get the current global performance tick updated by ISR */ + PerformanceCounter = PitPerformanceCounter; + + /* Send the LATCH command to freeze value in the PIT buffer */ HL::IoPort::WritePort8(PIT_COMMAND_PORT, 0x00); - CurrentCount = HL::IoPort::ReadPort8(PIT_DATA_PORT0); - CurrentCount |= (HL::IoPort::ReadPort8(PIT_DATA_PORT0) << 8); - /* Calculate elapsed ticks since the last read */ - TickCounter += (PreviousCount - CurrentCount) & 0xFFFF; - - /* Update the tracking variable */ - PreviousCount = CurrentCount; + /* Read the remaining count from the data port */ + CounterValue = HL::IoPort::ReadPort8(PIT_DATA_PORT0) | (HL::IoPort::ReadPort8(PIT_DATA_PORT0) << 8); } + while(PerformanceCounter != PitPerformanceCounter); + + /* Clamp the value in case of a hardware glitch right at the rollover point */ + if(CounterValue > PitRollover) + { + /* Force the sampled counter value to the maximum programmed reload threshold */ + CounterValue = PitRollover; + } + + /* Calculate how many ticks have passed since the last interrupt */ + ClockDelta = PitRollover - CounterValue; + + /* Synthesize the final high-precision timestamp */ + PerformanceCounter += ClockDelta; + + /* Guard against time drifting backward */ + if(PerformanceCounter < SystemPerformanceCounter) + { + /* Compensate missing interrupt */ + PerformanceCounter += PitRollover; + } + + /* Update the last recorded counter */ + SystemPerformanceCounter = PerformanceCounter; + + /* Return the timestamp */ + return PerformanceCounter; +} + +/** + * Queries the current value of the Time Stamp Counter (TSC). + * + * @return This routine returns the current invariant TSC tick count. + * + * @since XT 1.0 + */ +XTAPI +ULONGLONG +HL::Timer::QueryPerformanceCounterTsc(VOID) +{ + ULONG TscAux; + + /* Retrieve the timestamp */ + return AR::CpuFunc::ReadTimeStampCounterProcessor(&TscAux); } /** @@ -214,6 +949,7 @@ HL::Timer::QueryTimerCapabilities(VOID) /* Check TSC-Deadline mode if leaf supported */ if(MaxStandardLeaf >= CPUID_GET_STANDARD1_FEATURES) { + /* Query the standard feature CPUI leaf */ CpuRegisters.Leaf = CPUID_GET_STANDARD1_FEATURES; CpuRegisters.SubLeaf = 0; CpuRegisters.Eax = 0; @@ -225,6 +961,7 @@ HL::Timer::QueryTimerCapabilities(VOID) /* Verify TSC-Deadline support */ if(CpuRegisters.Ecx & CPUID_FEATURES_ECX_TSC_DEADLINE) { + /* Mark the TSC-Deadline mode as supported */ TimerCapabilities.TscDeadline = TRUE; } } @@ -232,6 +969,7 @@ HL::Timer::QueryTimerCapabilities(VOID) /* Check Always Running APIC Timer - ARAT if leaf supported */ if(MaxStandardLeaf >= CPUID_GET_POWER_MANAGEMENT) { + /* Query the thermal and power management features CPUID leaf */ CpuRegisters.Leaf = CPUID_GET_POWER_MANAGEMENT; CpuRegisters.SubLeaf = 0; CpuRegisters.Eax = 0; @@ -240,9 +978,10 @@ HL::Timer::QueryTimerCapabilities(VOID) CpuRegisters.Edx = 0; AR::CpuFunc::CpuId(&CpuRegisters); - /* Verify ARAT support */ + /* Verify Always Running APIC Timer support */ if(CpuRegisters.Eax & CPUID_FEATURES_EAX_ARAT) { + /* Mark the ARAT as supported */ TimerCapabilities.Arat = TRUE; } } @@ -250,6 +989,7 @@ HL::Timer::QueryTimerCapabilities(VOID) /* Check Always Running Timer - ART if leaf supported */ if(MaxStandardLeaf >= CPUID_GET_TSC_CRYSTAL_CLOCK) { + /* Query the Time Stamp Counter and Core Crystal Clock information CPUID leaf */ CpuRegisters.Leaf = CPUID_GET_TSC_CRYSTAL_CLOCK; CpuRegisters.SubLeaf = 0; CpuRegisters.Eax = 0; @@ -258,9 +998,10 @@ HL::Timer::QueryTimerCapabilities(VOID) CpuRegisters.Edx = 0; AR::CpuFunc::CpuId(&CpuRegisters); - /* Verify ART support */ + /* Verify Always Running Timer support */ if(CpuRegisters.Eax != 0 && CpuRegisters.Ebx != 0) { + /* Mark the ART as supported */ TimerCapabilities.Art = TRUE; /* Save the TSC scaling ratios */ @@ -279,6 +1020,7 @@ HL::Timer::QueryTimerCapabilities(VOID) /* Check RDTSCP instruction support if leaf supported */ if(MaxExtendedLeaf >= CPUID_GET_EXTENDED_FEATURES) { + /* Query the extended processor features CPUID leaf */ CpuRegisters.Leaf = CPUID_GET_EXTENDED_FEATURES; CpuRegisters.SubLeaf = 0; CpuRegisters.Eax = 0; @@ -290,6 +1032,7 @@ HL::Timer::QueryTimerCapabilities(VOID) /* Verify RDTSCP support */ if(CpuRegisters.Edx & CPUID_FEATURES_EDX_RDTSCP) { + /* Mark the RDTSCP instruction as supported */ TimerCapabilities.RDTSCP = TRUE; } } @@ -297,6 +1040,7 @@ HL::Timer::QueryTimerCapabilities(VOID) /* Check Invariant TSC if leaf supported */ if(MaxExtendedLeaf >= CPUID_GET_ADVANCED_POWER_MANAGEMENT) { + /* Query the advanced power management features CPUID leaf */ CpuRegisters.Leaf = CPUID_GET_ADVANCED_POWER_MANAGEMENT; CpuRegisters.SubLeaf = 0; CpuRegisters.Eax = 0; @@ -308,11 +1052,109 @@ HL::Timer::QueryTimerCapabilities(VOID) /* Verify Invariant TSC support */ if(CpuRegisters.Edx & CPUID_FEATURES_EDX_TSCI) { + /* Mark the Invariant TSC feature as supported */ TimerCapabilities.InvariantTsc = TRUE; } } } +/** + * Requests a dynamic adjustment of the system clock resolution. + * + * @param Rate + * Supplies the requested clock rate change in 100-nanosecond units. + * + * @return This routine returns the actual clock rate granted by the hardware. + * + * @since XT 1.0 + */ +XTAPI +ULONG +HL::Timer::SetClockRate(IN ULONG Rate) +{ + ULONG HardwareRate; + + /* Validate and clamp the clock rate against architectural limits */ + if(Rate < HL_MINIMUM_CLOCK_RATE) + { + /* Enforce minimum resolution to prevent system-wide interrupt storms */ + Rate = HL_MINIMUM_CLOCK_RATE; + } + else if(Rate > HL_MAXIMUM_CLOCK_RATE) + { + /* Cap the clock rate to ensure responsiveness */ + Rate = HL_MAXIMUM_CLOCK_RATE; + } + + /* Check if the active hardware backend supports dynamic rate scaling */ + if(TimerRoutines.SetClockRate) + { + /* Dispatch the reprogramming request */ + HardwareRate = TimerRoutines.SetClockRate(Rate); + } + else + { + /* Fallback to the current fixed rate as scaling is unsupported */ + HardwareRate = TimeIncrement; + } + + /* Return the actual clock rate set */ + return HardwareRate; +} + +/** + * Adjusts the Local APIC Timer frequency to match the requested resolution. + * + * @param Rate + * Supplies the requested clock rate change in 100-nanosecond units. + * + * @return This routine returns the actual clock rate granted by the hardware. + * + * @since XT 1.0 + */ +XTAPI +ULONG +HL::Timer::SetClockRateApic(ULONG Rate) +{ + ULONG NewDivider; + BOOLEAN Interrupts; + + /* TODO: Implement IPI broadcast to synchronize all cores in SMP mode */ + UNIMPLEMENTED; + + /* Calculate the hardware-specific tick count for the requested rate */ + NewDivider = (ULONG)(((ULONGLONG)TimerFrequency * Rate) / 10000000ULL); + + /* Prevent an invalid zero-count state */ + if(NewDivider == 0) + { + /* Enforce a single-tick */ + NewDivider = 1; + } + + /* Check whether interrupts are enabled */ + Interrupts = AR::CpuFunc::InterruptsEnabled(); + + /* Disable interrupts */ + AR::CpuFunc::ClearInterruptFlag(); + + /* Commit the new divider to the TICR register */ + HL::Pic::WriteApicRegister(APIC_TICR, NewDivider); + + /* Synchronize the kernel's timekeeping math with the new hardware state */ + ConfigureTimeIncrement(TimerFrequency, NewDivider); + + /* Check whether interrupts need to be re-enabled */ + if(Interrupts) + { + /* Re-enable interrupts */ + AR::CpuFunc::SetInterruptFlag(); + } + + /* Return the actual clock rate set */ + return TimeIncrement; +} + /** * Sets the profile interrupt interval. The interval may be bounded by hardware capabilities. * @@ -339,11 +1181,8 @@ HL::Timer::SetProfileInterval(IN ULONG_PTR Interval) Interval = MAX_PROFILE_INTERVAL; } - /* Calculate the number of APIC timer ticks required for the requested interval */ - ProfilingInterval = (TimerFrequency / 10000) * (Interval / 1000); - - /* Update the APIC Timer Initial Count Register (TICR) to apply the new interval immediately */ - HL::Pic::WriteApicRegister(APIC_TICR, ProfilingInterval); + /* Calculate the required number of ticks for the requested interval */ + ProfilingTicks = (ULONG)(Interval / 10000); /* Return the actual interval */ return Interval; @@ -363,10 +1202,137 @@ XTAPI VOID HL::Timer::StallExecution(IN ULONG MicroSeconds) { - UNIMPLEMENTED; + /* Dispatch the stall request */ + TimerRoutines.StallExecution(MicroSeconds); +} - /* ACPI PM Timer not supported, fall back to PIT */ - PitStallExecution(MicroSeconds); +/** + * Stalls the CPU execution for a specified duration using the ACPI Power Management Timer. + * + * @param MicroSeconds + * Supplies the number of microseconds to stall execution. + * + * @return This routine does not return any value. + * + * @since XT 1.0 + */ +XTAPI +VOID +HL::Timer::StallExecutionAcpiPm(IN ULONG MicroSeconds) +{ + PACPI_TIMER_INFO AcpiTimerInfo; + ULONG StartTick, CurrentTick, Delta, TicksElapsed; + ULONGLONG TargetTicks; + + /* Validate input parameter */ + if(MicroSeconds == 0) + { + /* Nothing to do */ + return; + } + else if(MicroSeconds > 3000000) + { + /* Cap execution stall to 3 seconds */ + MicroSeconds = 3000000; + } + + /* Retrieve the ACPI PM Timer hardware configuration */ + HL::Acpi::GetAcpiTimerInfo(&AcpiTimerInfo); + + /* Calculate the target number of ticks based on the standard ACPI PM frequency */ + TargetTicks = ((ULONGLONG)MicroSeconds * 3579545ULL) / 1000000ULL; + TicksElapsed = 0; + + /* Sample the initial hardware tick count and apply the hardware-specific bitmask */ + StartTick = HL::IoPort::ReadPort32(AcpiTimerInfo->TimerPort) & AcpiTimerInfo->MsbMask; + + /* Spin the processor until the accumulated hardware ticks reach the calculated target */ + while(TicksElapsed < TargetTicks) + { + /* Sample the current hardware tick count */ + CurrentTick = HL::IoPort::ReadPort32(AcpiTimerInfo->TimerPort) & AcpiTimerInfo->MsbMask; + + /* Calculate the tick delta */ + Delta = (CurrentTick - StartTick) & AcpiTimerInfo->MsbMask; + + /* Accumulate the elapsed ticks and advance the start marker */ + TicksElapsed += Delta; + StartTick = CurrentTick; + + /* Issue a PAUSE instruction to relieve memory bus contention */ + AR::CpuFunc::YieldProcessor(); + } +} + +/** + * Stalls the CPU execution for a specified duration (maximum 3 seconds) using the legacy PIT timer. + * + * @param MicroSeconds + * Specifies the number of microseconds to stall execution. + * + * @return This routine does not return any value. + * + * @since XT 1.0 + */ +XTAPI +VOID +HL::Timer::StallExecutionPit(IN ULONG MicroSeconds) +{ + USHORT CurrentCount, PreviousCount; + ULONG TargetTicks, TickCounter; + UCHAR Port61State; + + /* Validate input parameter */ + if(MicroSeconds == 0) + { + /* Nothing to do */ + return; + } + else if(MicroSeconds > 3000000) + { + /* Cap execution stall to 3 seconds */ + MicroSeconds = 3000000; + } + + /* Convert microseconds to PIT ticks using exact frequency arithmetic */ + TargetTicks = (ULONG)(((ULONGLONG)MicroSeconds * 1193182ULL) / 1000000ULL); + TickCounter = 0; + + /* Save the current state of System Control Port B */ + Port61State = HL::IoPort::ReadPort8(0x61); + + /* Enable PIT Channel 2 and disable the PC Speaker */ + HL::IoPort::WritePort8(0x61, (Port61State & ~0x02) | 0x01); + + /* Configure PIT Channel 2 for Read/Write LSB then MSB, Mode 0 */ + HL::IoPort::WritePort8(PIT_COMMAND_PORT, 0xB0); + + /* Initialize the PIT counter with the maximum possible value */ + HL::IoPort::WritePort8(PIT_DATA_PORT2, 0xFF); + HL::IoPort::WritePort8(PIT_DATA_PORT2, 0xFF); + + /* Latch and read the initial counter value */ + HL::IoPort::WritePort8(PIT_COMMAND_PORT, 0x80); + PreviousCount = HL::IoPort::ReadPort8(PIT_DATA_PORT2); + PreviousCount |= (HL::IoPort::ReadPort8(PIT_DATA_PORT2) << 8); + + /* Poll the PIT */ + while(TickCounter < TargetTicks) + { + /* Latch the current counter value without stopping the timer */ + HL::IoPort::WritePort8(PIT_COMMAND_PORT, 0x80); + CurrentCount = HL::IoPort::ReadPort8(PIT_DATA_PORT2); + CurrentCount |= (HL::IoPort::ReadPort8(PIT_DATA_PORT2) << 8); + + /* Calculate elapsed ticks since the last read */ + TickCounter += (PreviousCount - CurrentCount) & 0xFFFF; + + /* Update the tracking variable */ + PreviousCount = CurrentCount; + } + + /* Restore the original state of PIT Port */ + HL::IoPort::WritePort8(0x61, Port61State); } /** @@ -383,8 +1349,6 @@ XTAPI VOID HL::Timer::StartProfileInterrupt(IN KPROFILE_SOURCE ProfileSource) { - APIC_LVT_REGISTER LvtRegister; - /* Handle only ProfileTime and ProfileXtKernel */ if(ProfileSource != ProfileTime && ProfileSource != ProfileXtKernel) { @@ -392,17 +1356,8 @@ HL::Timer::StartProfileInterrupt(IN KPROFILE_SOURCE ProfileSource) return; } - /* Set the interval */ - HL::Pic::WriteApicRegister(APIC_TICR, ProfilingInterval); - - /* Unmask interrupt */ - LvtRegister.Long = 0; - LvtRegister.Mask = 0; - LvtRegister.DeliveryMode = APIC_DM_FIXED; - LvtRegister.TimerMode = 1; - LvtRegister.TriggerMode = APIC_TGM_EDGE; - LvtRegister.Vector = APIC_VECTOR_PROFILE; - HL::Pic::WriteApicRegister(APIC_TMRLVTR, LvtRegister.Long); + /* Set the global software flag to enable profiling */ + ProfilingEnabled = TRUE; } /** @@ -419,8 +1374,6 @@ XTAPI VOID HL::Timer::StopProfileInterrupt(IN KPROFILE_SOURCE ProfileSource) { - APIC_LVT_REGISTER LvtRegister; - /* Handle only ProfileTime and ProfileXtKernel */ if(ProfileSource != ProfileTime && ProfileSource != ProfileXtKernel) { @@ -428,12 +1381,54 @@ HL::Timer::StopProfileInterrupt(IN KPROFILE_SOURCE ProfileSource) return; } - /* Mask interrupt */ - LvtRegister.Long = 0; - LvtRegister.Mask = 1; - LvtRegister.DeliveryMode = APIC_DM_FIXED; - LvtRegister.TimerMode = 1; - LvtRegister.TriggerMode = APIC_TGM_EDGE; - LvtRegister.Vector = APIC_VECTOR_PROFILE; - HL::Pic::WriteApicRegister(APIC_TMRLVTR, LvtRegister.Long); + /* Clear the global software flag to disable profiling */ + ProfilingEnabled = FALSE; +} + +/** + * Checks if the specified hardware timer is supported and available on the current system. + * + * @param TimerType + * Supplies the hardware timer type to query. + * + * @param IsClock + * Supplies TRUE if the timer is intended to be used as an interrupt generator (System Clock), + * or FALSE if it is intended to be used as a performance counter (Stoper). + * + * @return This routine returns TRUE if the timer is available and supported, or FALSE otherwise. + * + * @since XT 1.0 + */ +XTAPI +BOOLEAN +HL::Timer::ValidateTimerSupport(IN TIMER_TYPE TimerType, + IN BOOLEAN IsClock) +{ + PACPI_TIMER_INFO AcpiTimerInfo; + + /* Query ACPI timer info */ + HL::Acpi::GetAcpiTimerInfo(&AcpiTimerInfo); + + /* Validate timer type */ + switch(TimerType) + { + case TimerTsc: + /* Check if TSC and RDTSCP are supported */ + return (TimerCapabilities.InvariantTsc && TimerCapabilities.RDTSCP) ? TRUE : FALSE; + case TimerHpet: + /* Check if HPET is supported */ + return (HpetAddress != NULLPTR) ? TRUE : FALSE; + case TimerAcpiPm: + /* Check if ACPI PM Timer is supported */ + return (!IsClock && AcpiTimerInfo->TimerPort != 0) ? TRUE : FALSE; + case TimerLapic: + /* Check if LAPIC Timer (ARAT) is supported */ + return (IsClock && TimerCapabilities.Arat) ? TRUE : FALSE; + case TimerPit: + /* PIT Timer is always supported */ + return TRUE; + default: + /* Invalid timer type, return FALSE */ + return FALSE; + } } diff --git a/xtoskrnl/includes/hl/timer.hh b/xtoskrnl/includes/hl/timer.hh index a1f64f7..8be24eb 100644 --- a/xtoskrnl/includes/hl/timer.hh +++ b/xtoskrnl/includes/hl/timer.hh @@ -18,22 +18,56 @@ namespace HL class Timer { private: - STATIC ULONG ProfilingInterval; + STATIC ULONG AcpiPmPerformanceCounter; + STATIC TIMER_TYPE ClockType; + STATIC ULONG FractionalIncrement; + STATIC PVOID HpetAddress; + STATIC ULONGLONG HpetFrequency; + STATIC KSPIN_LOCK PerformanceCounterLock; + STATIC ULONGLONG PerformanceFrequency; + STATIC ULONGLONG PitPerformanceCounter; + STATIC ULONG PitRollover; + STATIC BOOLEAN ProfilingEnabled; + STATIC ULONG ProfilingTicks; + STATIC ULONG RunningFraction; + STATIC ULONGLONG SystemPerformanceCounter; + STATIC ULONG TimeIncrement; STATIC TIMER_CAPABILITIES TimerCapabilities; STATIC ULONG TimerFrequency; + STATIC TIMER_ROUTINES TimerRoutines; + STATIC TIMER_TYPE TimerType; public: STATIC XTAPI VOID InitializeTimer(VOID); + STATIC XTAPI LARGE_INTEGER QueryPerformanceCounter(OUT PLARGE_INTEGER PerformanceFrequency); + STATIC XTAPI ULONG SetClockRate(IN ULONG Rate); STATIC XTAPI ULONG_PTR SetProfileInterval(IN ULONG_PTR Interval); + STATIC XTAPI VOID StallExecution(IN ULONG MicroSeconds); STATIC XTAPI VOID StartProfileInterrupt(IN KPROFILE_SOURCE ProfileSource); STATIC XTAPI VOID StopProfileInterrupt(IN KPROFILE_SOURCE ProfileSource); private: STATIC XTAPI XTSTATUS CalibrateApicTimer(); - STATIC XTAPI VOID InitializeApicTimer(VOID); - STATIC XTAPI VOID PitStallExecution(IN ULONG MicroSeconds); + STATIC XTAPI ULONGLONG CalibrateTscCounter(VOID); + STATIC XTAPI VOID ConfigureTimeIncrement(IN ULONGLONG BaseFrequency, + IN ULONGLONG HardwareDivider); + STATIC XTAPI XTSTATUS DetectHpet(VOID); + STATIC XTCDECL VOID HandleClockInterrupt(IN PKTRAP_FRAME TrapFrame); + STATIC XTCDECL VOID HandleClockIpiInterrupt(IN PKTRAP_FRAME TrapFrame); + STATIC XTAPI XTSTATUS InitializeApicTimer(VOID); + STATIC XTAPI XTSTATUS InitializeHpetTimer(VOID); + STATIC XTAPI XTSTATUS InitializePitTimer(VOID); + STATIC XTAPI VOID ProbeTimerType(VOID); + STATIC XTAPI ULONGLONG QueryPerformanceCounterAcpiPm(VOID); + STATIC XTAPI ULONGLONG QueryPerformanceCounterHpet(VOID); + STATIC XTAPI ULONGLONG QueryPerformanceCounterPit(VOID); + STATIC XTAPI ULONGLONG QueryPerformanceCounterTsc(VOID); STATIC XTAPI VOID QueryTimerCapabilities(VOID); - STATIC XTAPI VOID StallExecution(IN ULONG MicroSeconds); + STATIC XTAPI ULONG SetClockRateApic(ULONG TargetIncrement); + STATIC XTAPI VOID StallExecutionAcpiPm(IN ULONG MicroSeconds); + STATIC XTAPI VOID StallExecutionPit(IN ULONG MicroSeconds); + STATIC XTAPI BOOLEAN ValidateTimerSupport(IN TIMER_TYPE TimerType, + IN BOOLEAN IsClock); }; }