diff --git a/Kernel/Time/HPET.cpp b/Kernel/Time/HPET.cpp index 24f4f316a7..c1d77dbffe 100644 --- a/Kernel/Time/HPET.cpp +++ b/Kernel/Time/HPET.cpp @@ -179,7 +179,8 @@ void HPET::update_periodic_comparator_value() m_main_counter_drift += previous_main_value - m_main_counter_last_read; m_main_counter_last_read = 0; regs.main_counter_value.low = 0; - regs.main_counter_value.high = 0; + if (m_main_counter_64bits) + regs.main_counter_value.high = 0; for (auto& comparator : m_comparators) { auto& timer = regs.timers[comparator.comparator_number()]; if (!comparator.is_enabled()) @@ -195,8 +196,10 @@ void HPET::update_periodic_comparator_value() value, previous_main_value); timer.comparator_value.low = (u32)value; - timer.capabilities = timer.capabilities | (u32)HPETFlags::TimerConfiguration::ValueSet; - timer.comparator_value.high = (u32)(value >> 32); + if (comparator.is_64bit_capable()) { + timer.capabilities = timer.capabilities | (u32)HPETFlags::TimerConfiguration::ValueSet; + timer.comparator_value.high = (u32)(value >> 32); + } } else { // Set the new target comparator value to the delta to the remaining ticks u64 current_value = (u64)timer.comparator_value.low | ((u64)timer.comparator_value.high << 32); @@ -207,7 +210,8 @@ void HPET::update_periodic_comparator_value() value, previous_main_value); timer.comparator_value.low = (u32)value; - timer.comparator_value.high = (u32)(value >> 32); + if (comparator.is_64bit_capable()) + timer.comparator_value.high = (u32)(value >> 32); } } @@ -233,10 +237,14 @@ u64 HPET::update_time(u64& seconds_since_boot, u32& ticks_this_second, bool quer // Should only be called by the time keeper interrupt handler! u64 current_value = read_main_counter(); u64 delta_ticks = m_main_counter_drift; - if (current_value >= m_main_counter_last_read) + if (current_value >= m_main_counter_last_read) { delta_ticks += current_value - m_main_counter_last_read; - else - delta_ticks += m_main_counter_last_read - current_value; // the counter wrapped around + } else { + // the counter wrapped around + delta_ticks += m_main_counter_last_read - current_value; + if (!m_main_counter_64bits) + m_32bit_main_counter_wraps++; + } u64 ticks_since_last_second = (u64)ticks_this_second + delta_ticks; auto ticks_per_second = frequency(); if (ticks_since_last_second >= ticks_per_second) { @@ -258,12 +266,24 @@ u64 HPET::update_time(u64& seconds_since_boot, u32& ticks_this_second, bool quer u64 HPET::read_main_counter_unsafe() const { auto& main_counter = registers().main_counter_value; - return ((u64)main_counter.high << 32) | (u64)main_counter.low; + if (m_main_counter_64bits) + return ((u64)main_counter.high << 32) | (u64)main_counter.low; + + return ((u64)m_32bit_main_counter_wraps << 32) | (u64)main_counter.low; } u64 HPET::read_main_counter() const { - return read_register_safe64(registers().main_counter_value); + if (m_main_counter_64bits) + return read_register_safe64(registers().main_counter_value); + + auto& main_counter = registers().main_counter_value; + u32 wraps = m_32bit_main_counter_wraps; + u32 last_read_value = m_main_counter_last_read & 0xffffffff; + u32 current_value = main_counter.low; + if (current_value < last_read_value) + wraps++; + return ((u64)wraps << 32) | (u64)current_value; } void HPET::enable_periodic_interrupt(const HPETComparator& comparator) @@ -348,6 +368,13 @@ bool HPET::is_periodic_capable(u8 comparator_number) const return comparator_registers.capabilities & (u32)HPETFlags::TimerConfiguration::PeriodicInterruptCapable; } +bool HPET::is_64bit_capable(u8 comparator_number) const +{ + VERIFY(comparator_number <= m_comparators.size()); + auto& comparator_registers = registers().timers[comparator_number]; + return comparator_registers.capabilities & (u32)HPETFlags::TimerConfiguration::Timer64BitsCapable; +} + void HPET::set_comparators_to_optimal_interrupt_state(size_t) { // FIXME: Implement this method for allowing to use HPET timers 2-31... @@ -398,8 +425,9 @@ UNMAP_AFTER_INIT HPET::HPET(PhysicalAddress acpi_hpet) // Note: We must do a 32 bit access to offsets 0x0, or 0x4 only. size_t timers_count = ((regs.capabilities.attributes >> 8) & 0x1f) + 1; + m_main_counter_64bits = (regs.capabilities.attributes & (u32)HPETFlags::Attributes::Counter64BitCapable) != 0; dmesgln("HPET: Timers count - {}", timers_count); - dmesgln("HPET: Main counter size: {}", ((regs.capabilities.attributes & (u32)HPETFlags::Attributes::Counter64BitCapable) ? "64-bit" : "32-bit")); + dmesgln("HPET: Main counter size: {}", (m_main_counter_64bits ? "64-bit" : "32-bit")); for (size_t i = 0; i < timers_count; i++) { bool capable_64_bit = regs.timers[i].capabilities & (u32)HPETFlags::TimerConfiguration::Timer64BitsCapable; dmesgln("HPET: Timer[{}] comparator size: {}, mode: {}", i, @@ -421,8 +449,8 @@ UNMAP_AFTER_INIT HPET::HPET(PhysicalAddress acpi_hpet) if (regs.capabilities.attributes & (u32)HPETFlags::Attributes::LegacyReplacementRouteCapable) regs.configuration.low = regs.configuration.low | (u32)HPETFlags::Configuration::LegacyReplacementRoute; - m_comparators.append(HPETComparator::create(0, 0, is_periodic_capable(0))); - m_comparators.append(HPETComparator::create(1, 8, is_periodic_capable(1))); + m_comparators.append(HPETComparator::create(0, 0, is_periodic_capable(0), is_64bit_capable(0))); + m_comparators.append(HPETComparator::create(1, 8, is_periodic_capable(1), is_64bit_capable(1))); global_enable(); } diff --git a/Kernel/Time/HPET.h b/Kernel/Time/HPET.h index 83b899b585..04e21714ce 100644 --- a/Kernel/Time/HPET.h +++ b/Kernel/Time/HPET.h @@ -56,6 +56,7 @@ private: void global_enable(); bool is_periodic_capable(u8 comparator_number) const; + bool is_64bit_capable(u8 comparator_number) const; void set_comparators_to_optimal_interrupt_state(size_t timers_count); u64 nanoseconds_to_raw_ticks() const; @@ -68,12 +69,13 @@ private: u64 m_main_counter_last_read { 0 }; u64 m_main_counter_drift { 0 }; + u32 m_32bit_main_counter_wraps { 0 }; u16 m_vendor_id; u16 m_minimum_tick; u64 m_frequency; u8 m_revision_id; - bool counter_is_64_bit_capable : 1; + bool m_main_counter_64bits : 1; bool legacy_replacement_route_capable : 1; NonnullRefPtrVector m_comparators; diff --git a/Kernel/Time/HPETComparator.cpp b/Kernel/Time/HPETComparator.cpp index 7ba16bef36..21c1bda5a6 100644 --- a/Kernel/Time/HPETComparator.cpp +++ b/Kernel/Time/HPETComparator.cpp @@ -11,18 +11,19 @@ namespace Kernel { -UNMAP_AFTER_INIT NonnullRefPtr HPETComparator::create(u8 number, u8 irq, bool periodic_capable) +UNMAP_AFTER_INIT NonnullRefPtr HPETComparator::create(u8 number, u8 irq, bool periodic_capable, bool is_64bit_capable) { - auto timer = adopt_ref(*new HPETComparator(number, irq, periodic_capable)); + auto timer = adopt_ref(*new HPETComparator(number, irq, periodic_capable, is_64bit_capable)); timer->register_interrupt_handler(); return timer; } -UNMAP_AFTER_INIT HPETComparator::HPETComparator(u8 number, u8 irq, bool periodic_capable) +UNMAP_AFTER_INIT HPETComparator::HPETComparator(u8 number, u8 irq, bool periodic_capable, bool is_64bit_capable) : HardwareTimer(irq) , m_periodic(false) , m_periodic_capable(periodic_capable) , m_enabled(false) + , m_is_64bit_capable(is_64bit_capable) , m_comparator_number(number) { } diff --git a/Kernel/Time/HPETComparator.h b/Kernel/Time/HPETComparator.h index d993c1ea38..5fa0d0cdd1 100644 --- a/Kernel/Time/HPETComparator.h +++ b/Kernel/Time/HPETComparator.h @@ -16,13 +16,14 @@ class HPETComparator final : public HardwareTimer { friend class HPET; public: - static NonnullRefPtr create(u8 number, u8 irq, bool periodic_capable); + static NonnullRefPtr create(u8 number, u8 irq, bool periodic_capable, bool is_64bit_capable); virtual HardwareTimerType timer_type() const override { return HardwareTimerType::HighPrecisionEventTimer; } virtual const char* model() const override { return "HPET"; } u8 comparator_number() const { return m_comparator_number; } bool is_enabled() const { return m_enabled; } + bool is_64bit_capable() const { return m_is_64bit_capable; } virtual size_t ticks_per_second() const override; @@ -43,10 +44,11 @@ public: private: void set_new_countdown(); virtual void handle_irq(const RegisterState&) override; - HPETComparator(u8 number, u8 irq, bool periodic_capable); + HPETComparator(u8 number, u8 irq, bool periodic_capable, bool is_64bit_capable); bool m_periodic : 1; bool m_periodic_capable : 1; bool m_enabled : 1; + bool m_is_64bit_capable : 1; u8 m_comparator_number { 0 }; }; }