From fe615e601a95033c160b464728d8a0c195739f72 Mon Sep 17 00:00:00 2001 From: Tom Date: Sun, 25 Oct 2020 09:13:47 -0600 Subject: [PATCH] Kernel: Set up and calibrate APIC timer, and enable timer on all CPUs This enables the APIC timer on all CPUs, which means Scheduler::timer_tick is now called on all CPUs independently. We still don't do anything on the APs as it instantly crashes due to a number of other problems. --- Kernel/CMakeLists.txt | 2 +- Kernel/Interrupts/APIC.cpp | 94 ++++++++- Kernel/Interrupts/APIC.h | 15 ++ Kernel/Interrupts/GenericInterruptHandler.h | 2 + Kernel/Scheduler.cpp | 15 +- Kernel/Time/APICTimer.cpp | 183 ++++++++++++++++++ .../Time/{HardwareTimer.cpp => APICTimer.h} | 55 +++--- Kernel/Time/HPETComparator.h | 2 +- Kernel/Time/HardwareTimer.h | 107 ++++++++-- Kernel/Time/PIT.h | 2 +- Kernel/Time/RTC.h | 2 +- Kernel/Time/TimeManagement.cpp | 49 +++-- Kernel/Time/TimeManagement.h | 17 +- Kernel/init.cpp | 3 +- 14 files changed, 478 insertions(+), 70 deletions(-) create mode 100644 Kernel/Time/APICTimer.cpp rename Kernel/Time/{HardwareTimer.cpp => APICTimer.h} (50%) diff --git a/Kernel/CMakeLists.txt b/Kernel/CMakeLists.txt index 529de517fe..e6c7997231 100644 --- a/Kernel/CMakeLists.txt +++ b/Kernel/CMakeLists.txt @@ -169,9 +169,9 @@ set(KERNEL_SOURCES Tasks/SyncTask.cpp Thread.cpp ThreadTracer.cpp + Time/APICTimer.cpp Time/HPET.cpp Time/HPETComparator.cpp - Time/HardwareTimer.cpp Time/PIT.cpp Time/RTC.cpp Time/TimeManagement.cpp diff --git a/Kernel/Interrupts/APIC.cpp b/Kernel/Interrupts/APIC.cpp index 56bd3d3099..7cd96d14ad 100644 --- a/Kernel/Interrupts/APIC.cpp +++ b/Kernel/Interrupts/APIC.cpp @@ -36,6 +36,7 @@ #include #include #include +#include #include #include #include @@ -43,6 +44,7 @@ //#define APIC_DEBUG //#define APIC_SMP_DEBUG +#define IRQ_APIC_TIMER (0xfc - IRQ_VECTOR_BASE) #define IRQ_APIC_IPI (0xfd - IRQ_VECTOR_BASE) #define IRQ_APIC_ERR (0xfe - IRQ_VECTOR_BASE) #define IRQ_APIC_SPURIOUS (0xff - IRQ_VECTOR_BASE) @@ -66,6 +68,9 @@ #define APIC_REG_LVT_LINT0 0x350 #define APIC_REG_LVT_LINT1 0x360 #define APIC_REG_LVT_ERR 0x370 +#define APIC_REG_TIMER_INITIAL_COUNT 0x380 +#define APIC_REG_TIMER_CURRENT_COUNT 0x390 +#define APIC_REG_TIMER_CONFIGURATION 0x3e0 namespace Kernel { @@ -197,9 +202,13 @@ void APIC::write_icr(const ICRReg& icr) write_register(APIC_REG_ICR_LOW, icr.low()); } +#define APIC_LVT_TIMER_ONESHOT 0 +#define APIC_LVT_TIMER_PERIODIC (1 << 17) +#define APIC_LVT_TIMER_TSCDEADLINE (1 << 18) + #define APIC_LVT_MASKED (1 << 16) #define APIC_LVT_TRIGGER_LEVEL (1 << 14) -#define APIC_LVT(iv, dm) ((iv & 0xff) | ((dm & 0x7) << 8)) +#define APIC_LVT(iv, dm) (((iv)&0xff) | (((dm)&0x7) << 8)) extern "C" void apic_ap_start(void); extern "C" u16 apic_ap_start_size; @@ -519,6 +528,83 @@ void APIC::send_ipi(u32 cpu) write_icr(ICRReg(IRQ_APIC_IPI + IRQ_VECTOR_BASE, ICRReg::Fixed, ICRReg::Logical, ICRReg::Assert, ICRReg::TriggerMode::Edge, ICRReg::NoShorthand, 1u << cpu)); } +APICTimer* APIC::initialize_timers(HardwareTimerBase& calibration_timer) +{ + if (!m_apic_base) + return nullptr; + + // We should only initialize and calibrate the APIC timer once on the BSP! + ASSERT(Processor::current().id() == 0); + ASSERT(!m_apic_timer); + + m_apic_timer = APICTimer::initialize(IRQ_APIC_TIMER, calibration_timer); + return m_apic_timer; +} + +void APIC::setup_local_timer(u32 ticks, TimerMode timer_mode, bool enable) +{ + u32 flags = 0; + switch (timer_mode) { + case TimerMode::OneShot: + flags |= APIC_LVT_TIMER_ONESHOT; + break; + case TimerMode::Periodic: + flags |= APIC_LVT_TIMER_PERIODIC; + break; + case TimerMode::TSCDeadline: + flags |= APIC_LVT_TIMER_TSCDEADLINE; + break; + } + if (!enable) + flags |= APIC_LVT_MASKED; + write_register(APIC_REG_LVT_TIMER, APIC_LVT(IRQ_APIC_TIMER + IRQ_VECTOR_BASE, 0) | flags); + + u32 config = read_register(APIC_REG_TIMER_CONFIGURATION); + config &= ~0xf; // clear divisor (bits 0-3) + switch (get_timer_divisor()) { + case 1: + config |= (1 << 3) | 3; + break; + case 2: + break; + case 4: + config |= 1; + break; + case 8: + config |= 2; + break; + case 16: + config |= 3; + break; + case 32: + config |= (1 << 3); + break; + case 64: + config |= (1 << 3) | 1; + break; + case 128: + config |= (1 << 3) | 2; + break; + default: + ASSERT_NOT_REACHED(); + } + config |= 3; // divide by 16 + write_register(APIC_REG_TIMER_CONFIGURATION, config); + + if (timer_mode == TimerMode::Periodic) + write_register(APIC_REG_TIMER_INITIAL_COUNT, ticks / get_timer_divisor()); +} + +u32 APIC::get_timer_current_count() +{ + return read_register(APIC_REG_TIMER_CURRENT_COUNT); +} + +u32 APIC::get_timer_divisor() +{ + return 16; +} + void APICIPIInterruptHandler::handle_interrupt(const RegisterState&) { #ifdef APIC_SMP_DEBUG @@ -546,4 +632,10 @@ bool APICErrInterruptHandler::eoi() return true; } +bool HardwareTimer::eoi() +{ + APIC::the().eoi(); + return true; +} + } diff --git a/Kernel/Interrupts/APIC.h b/Kernel/Interrupts/APIC.h index 41a71fc95b..2d64a623e5 100644 --- a/Kernel/Interrupts/APIC.h +++ b/Kernel/Interrupts/APIC.h @@ -27,10 +27,13 @@ #pragma once #include +#include #include namespace Kernel { +class APICTimer; + struct LocalAPIC { u32 apic_id; }; @@ -52,6 +55,17 @@ public: Thread* get_idle_thread(u32 cpu) const; u32 enabled_processor_count() const { return m_processor_enabled_cnt; } + APICTimer* initialize_timers(HardwareTimerBase&); + APICTimer* get_timer() const { return m_apic_timer; } + enum class TimerMode { + OneShot, + Periodic, + TSCDeadline + }; + void setup_local_timer(u32, TimerMode, bool); + u32 get_timer_current_count(); + u32 get_timer_divisor(); + private: class ICRReg { u32 m_low { 0 }; @@ -102,6 +116,7 @@ private: AK::Atomic m_apic_ap_continue { 0 }; u32 m_processor_cnt { 0 }; u32 m_processor_enabled_cnt { 0 }; + APICTimer* m_apic_timer { nullptr }; static PhysicalAddress get_base(); static void set_base(const PhysicalAddress& base); diff --git a/Kernel/Interrupts/GenericInterruptHandler.h b/Kernel/Interrupts/GenericInterruptHandler.h index d15041ab76..991a1337de 100644 --- a/Kernel/Interrupts/GenericInterruptHandler.h +++ b/Kernel/Interrupts/GenericInterruptHandler.h @@ -65,6 +65,8 @@ protected: void change_interrupt_number(u8 number); GenericInterruptHandler(u8 interrupt_number, bool disable_remap = false); + void disable_remap() { m_disable_remap = true; } + private: size_t m_invoking_count { 0 }; u8 m_interrupt_number { 0 }; diff --git a/Kernel/Scheduler.cpp b/Kernel/Scheduler.cpp index c510743b6b..e3e27900e5 100644 --- a/Kernel/Scheduler.cpp +++ b/Kernel/Scheduler.cpp @@ -777,15 +777,19 @@ void Scheduler::timer_tick(const RegisterState& regs) ASSERT_INTERRUPTS_DISABLED(); ASSERT(Processor::current().in_irq()); - if (Processor::current().id() > 0) - return; auto current_thread = Processor::current().current_thread(); if (!current_thread) return; - ++g_uptime; + bool is_bsp = Processor::current().id() == 0; + if (!is_bsp) + return; // TODO: This prevents scheduling on other CPUs! + if (is_bsp) { + // TODO: We should probably move this out of the scheduler + ++g_uptime; - g_timeofday = TimeManagement::now_as_timeval(); + g_timeofday = TimeManagement::now_as_timeval(); + } if (current_thread->process().is_profiling()) { SmapDisabler disabler; @@ -799,7 +803,8 @@ void Scheduler::timer_tick(const RegisterState& regs) } } - TimerQueue::the().fire(); + if (is_bsp) + TimerQueue::the().fire(); if (current_thread->tick()) return; diff --git a/Kernel/Time/APICTimer.cpp b/Kernel/Time/APICTimer.cpp new file mode 100644 index 0000000000..2175f89a31 --- /dev/null +++ b/Kernel/Time/APICTimer.cpp @@ -0,0 +1,183 @@ +/* + * Copyright (c) 2020, the SerenityOS developers + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include + +namespace Kernel { + +#define APIC_TIMER_MEASURE_CPU_CLOCK + +APICTimer* APICTimer::initialize(u8 interrupt_number, HardwareTimerBase& calibration_source) +{ + auto* timer = new APICTimer(interrupt_number, nullptr); + if (!timer->calibrate(calibration_source)) { + delete timer; + return nullptr; + } + return timer; +} + +APICTimer::APICTimer(u8 interrupt_number, Function callback) + : HardwareTimer(interrupt_number, move(callback)) +{ + disable_remap(); +} + +bool APICTimer::calibrate(HardwareTimerBase& calibration_source) +{ + ASSERT_INTERRUPTS_DISABLED(); + + klog() << "APICTimer: Using " << calibration_source.model() << " as calibration source"; + + auto& apic = APIC::the(); +#ifdef APIC_TIMER_MEASURE_CPU_CLOCK + bool supports_tsc = Processor::current().has_feature(CPUFeature::TSC); +#endif + + // temporarily replace the timer callbacks + const size_t ticks_in_100ms = calibration_source.ticks_per_second() / 10; + Atomic calibration_ticks = 0; +#ifdef APIC_TIMER_MEASURE_CPU_CLOCK + volatile u64 start_tsc = 0, end_tsc = 0; +#endif + volatile u32 start_apic_count = 0, end_apic_count = 0; + auto original_source_callback = calibration_source.set_callback([&](const RegisterState&) { + u32 current_timer_count = apic.get_timer_current_count(); +#ifdef APIC_TIMER_MEASURE_CPU_CLOCK + u64 current_tsc = supports_tsc ? read_tsc() : 0; +#endif + + auto prev_tick = calibration_ticks.fetch_add(1, AK::memory_order_acq_rel); + if (prev_tick == 0) { +#ifdef APIC_TIMER_MEASURE_CPU_CLOCK + start_tsc = current_tsc; +#endif + start_apic_count = current_timer_count; + } else if (prev_tick + 1 == ticks_in_100ms) { +#ifdef APIC_TIMER_MEASURE_CPU_CLOCK + end_tsc = current_tsc; +#endif + end_apic_count = current_timer_count; + } + }); + + // Setup a counter that should be much longer than our calibration time. + // We don't want the APIC timer to actually fire. We do however want the + // calbibration_source timer to fire so that we can read the current + // tick count from the APIC timer + auto original_callback = set_callback([&](const RegisterState&) { + klog() << "APICTimer: Timer fired during calibration!"; + ASSERT_NOT_REACHED(); // TODO: How should we handle this? + }); + apic.setup_local_timer(0xffffffff, APIC::TimerMode::Periodic, true); + + sti(); + // Loop for about 100 ms + while (calibration_ticks.load(AK::memory_order_relaxed) < ticks_in_100ms) + ; + cli(); + + // Restore timer callbacks + calibration_source.set_callback(move(original_source_callback)); + set_callback(move(original_callback)); + + disable_local_timer(); + + auto delta_apic_count = end_apic_count - start_apic_count; + m_timer_period = (delta_apic_count * apic.get_timer_divisor()) / ticks_in_100ms; + + auto apic_freq = (delta_apic_count * 16) / apic.get_timer_divisor(); + if (apic_freq < 1000000) { + klog() << "APICTimer: Frequency too slow!"; + return false; + } + klog() << "APICTimer: Bus clock speed: " << (apic_freq / 1000000) << "." << (apic_freq % 1000000) << " MHz"; +#ifdef APIC_TIMER_MEASURE_CPU_CLOCK + if (supports_tsc) { + auto delta_tsc = end_tsc - start_tsc; + klog() << "APICTimer: CPU clock speed: " << (delta_tsc / 1000000) << "." << (delta_tsc % 1000000) << " MHz"; + } +#endif + + enable_local_timer(); + return true; +} + +void APICTimer::enable_local_timer() +{ + APIC::the().setup_local_timer(m_timer_period, m_timer_mode, true); +} + +void APICTimer::disable_local_timer() +{ + APIC::the().setup_local_timer(0, APIC::TimerMode::OneShot, false); +} + +size_t APICTimer::ticks_per_second() const +{ + return m_frequency; +} + +void APICTimer::set_periodic() +{ + // FIXME: Implement it... + ASSERT_NOT_REACHED(); +} +void APICTimer::set_non_periodic() +{ + // FIXME: Implement it... + ASSERT_NOT_REACHED(); +} + +void APICTimer::reset_to_default_ticks_per_second() +{ +} + +bool APICTimer::try_to_set_frequency(size_t frequency) +{ + (void)frequency; + return true; +} + +bool APICTimer::is_capable_of_frequency(size_t frequency) const +{ + (void)frequency; + return false; +} + +size_t APICTimer::calculate_nearest_possible_frequency(size_t frequency) const +{ + (void)frequency; + return 0; +} + +} diff --git a/Kernel/Time/HardwareTimer.cpp b/Kernel/Time/APICTimer.h similarity index 50% rename from Kernel/Time/HardwareTimer.cpp rename to Kernel/Time/APICTimer.h index 6773f4123b..15f12a31b1 100644 --- a/Kernel/Time/HardwareTimer.cpp +++ b/Kernel/Time/APICTimer.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Liav A. + * Copyright (c) 2020, the SerenityOS developers * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -24,35 +24,42 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#pragma once + +#include +#include +#include #include -#include namespace Kernel { -HardwareTimer::HardwareTimer(u8 irq_number, Function callback) - : IRQHandler(irq_number) - , m_callback(move(callback)) -{ -} +class APICTimer final : public HardwareTimer { +public: + static APICTimer* initialize(u8, HardwareTimerBase&); + virtual HardwareTimerType timer_type() const override { return HardwareTimerType::LocalAPICTimer; } + virtual const char* model() const override { return "LocalAPIC"; } + virtual size_t ticks_per_second() const override; -void HardwareTimer::handle_irq(const RegisterState& regs) -{ - if (m_callback) - m_callback(regs); -} + virtual bool is_periodic() const override { return m_timer_mode == APIC::TimerMode::Periodic; } + virtual bool is_periodic_capable() const override { return true; } + virtual void set_periodic() override; + virtual void set_non_periodic() override; -const char* HardwareTimer::purpose() const -{ - if (TimeManagement::the().is_system_timer(*this)) - return "System Timer"; - return model(); -} + virtual void reset_to_default_ticks_per_second() override; + virtual bool try_to_set_frequency(size_t frequency) override; + virtual bool is_capable_of_frequency(size_t frequency) const override; + virtual size_t calculate_nearest_possible_frequency(size_t frequency) const override; -void HardwareTimer::set_callback(Function callback) -{ - disable_irq(); - m_callback = move(callback); - enable_irq(); -} + void enable_local_timer(); + void disable_local_timer(); + +private: + explicit APICTimer(u8, Function); + + bool calibrate(HardwareTimerBase&); + + u32 m_timer_period { 0 }; + APIC::TimerMode m_timer_mode { APIC::TimerMode::Periodic }; +}; } diff --git a/Kernel/Time/HPETComparator.h b/Kernel/Time/HPETComparator.h index 86debaac2d..d4c79f2a0b 100644 --- a/Kernel/Time/HPETComparator.h +++ b/Kernel/Time/HPETComparator.h @@ -32,7 +32,7 @@ #include namespace Kernel { -class HPETComparator final : public HardwareTimer { +class HPETComparator final : public HardwareTimer { friend class HPET; public: diff --git a/Kernel/Time/HardwareTimer.h b/Kernel/Time/HardwareTimer.h index a62512b2b4..f88435ac86 100644 --- a/Kernel/Time/HardwareTimer.h +++ b/Kernel/Time/HardwareTimer.h @@ -35,37 +35,114 @@ namespace Kernel { enum class HardwareTimerType { - i8253 = 0x1, /* PIT */ - RTC = 0x2, /* Real Time Clock */ - HighPrecisionEventTimer = 0x3 /* also known as IA-PC HPET */ + i8253 = 0x1, /* PIT */ + RTC = 0x2, /* Real Time Clock */ + HighPrecisionEventTimer = 0x3, /* also known as IA-PC HPET */ + LocalAPICTimer = 0x4 /* Local APIC */ }; -class HardwareTimer - : public RefCounted - , public IRQHandler { +template +class HardwareTimer; + +class HardwareTimerBase + : public RefCounted { public: - virtual HardwareTimerType timer_type() const = 0; + virtual ~HardwareTimerBase() { } + virtual const char* model() const = 0; - virtual const char* purpose() const override; - - void set_callback(Function); - - virtual size_t ticks_per_second() const = 0; + virtual HardwareTimerType timer_type() const = 0; + virtual Function set_callback(Function) = 0; virtual bool is_periodic() const = 0; virtual bool is_periodic_capable() const = 0; virtual void set_periodic() = 0; virtual void set_non_periodic() = 0; + virtual size_t ticks_per_second() const = 0; + virtual void reset_to_default_ticks_per_second() = 0; virtual bool try_to_set_frequency(size_t frequency) = 0; virtual bool is_capable_of_frequency(size_t frequency) const = 0; virtual size_t calculate_nearest_possible_frequency(size_t frequency) const = 0; +}; + +template<> +class HardwareTimer + : public HardwareTimerBase + , public IRQHandler { +public: + virtual const char* purpose() const override + { + if (TimeManagement::the().is_system_timer(*this)) + return "System Timer"; + return model(); + } + + virtual Function set_callback(Function callback) override + { + disable_irq(); + auto previous_callback = move(m_callback); + m_callback = move(callback); + enable_irq(); + return previous_callback; + } protected: - HardwareTimer(u8 irq_number, Function = nullptr); - //^IRQHandler - virtual void handle_irq(const RegisterState&) override; + HardwareTimer(u8 irq_number, Function callback = nullptr) + : IRQHandler(irq_number) + , m_callback(move(callback)) + { + } + + virtual void handle_irq(const RegisterState& regs) override + { + if (m_callback) + m_callback(regs); + } + + u64 m_frequency { OPTIMAL_TICKS_PER_SECOND_RATE }; + +private: + Function m_callback; +}; + +template<> +class HardwareTimer + : public HardwareTimerBase + , public GenericInterruptHandler { +public: + virtual const char* purpose() const override + { + return model(); + } + + virtual Function set_callback(Function callback) override + { + auto previous_callback = move(m_callback); + m_callback = move(callback); + return previous_callback; + } + + virtual size_t sharing_devices_count() const override { return 0; } + virtual bool is_shared_handler() const override { return false; } + virtual bool is_sharing_with_others() const { return false; } + virtual HandlerType type() const override { return HandlerType::IRQHandler; } + virtual const char* controller() const override { return nullptr; } + virtual bool eoi() override; + +protected: + HardwareTimer(u8 irq_number, Function callback = nullptr) + : GenericInterruptHandler(irq_number) + , m_callback(move(callback)) + { + } + + virtual void handle_interrupt(const RegisterState& regs) override + { + if (m_callback) + m_callback(regs); + } + u64 m_frequency { OPTIMAL_TICKS_PER_SECOND_RATE }; private: diff --git a/Kernel/Time/PIT.h b/Kernel/Time/PIT.h index b6d2f3ad01..b00eee520e 100644 --- a/Kernel/Time/PIT.h +++ b/Kernel/Time/PIT.h @@ -52,7 +52,7 @@ namespace Kernel { #define BASE_FREQUENCY 1193182 -class PIT final : public HardwareTimer { +class PIT final : public HardwareTimer { public: static NonnullRefPtr initialize(Function); virtual HardwareTimerType timer_type() const override { return HardwareTimerType::i8253; } diff --git a/Kernel/Time/RTC.h b/Kernel/Time/RTC.h index 0e211eeb2f..e2427dff01 100644 --- a/Kernel/Time/RTC.h +++ b/Kernel/Time/RTC.h @@ -31,7 +31,7 @@ #include namespace Kernel { -class RealTimeClock final : public HardwareTimer { +class RealTimeClock final : public HardwareTimer { public: static NonnullRefPtr create(Function callback); virtual HardwareTimerType timer_type() const override { return HardwareTimerType::RTC; } diff --git a/Kernel/Time/TimeManagement.cpp b/Kernel/Time/TimeManagement.cpp index ab0ed2bd8c..c04f096073 100644 --- a/Kernel/Time/TimeManagement.cpp +++ b/Kernel/Time/TimeManagement.cpp @@ -28,7 +28,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -48,7 +50,7 @@ TimeManagement& TimeManagement::the() return *s_the; } -bool TimeManagement::is_system_timer(const HardwareTimer& timer) const +bool TimeManagement::is_system_timer(const HardwareTimerBase& timer) const { return &timer == m_system_timer.ptr(); } @@ -69,11 +71,36 @@ timespec TimeManagement::epoch_time() const return ts; } -void TimeManagement::initialize() +void TimeManagement::initialize(u32 cpu) { - ASSERT(!s_the.is_initialized()); - s_the.ensure_instance(); + if (cpu == 0) { + ASSERT(!s_the.is_initialized()); + s_the.ensure_instance(); + + // Initialize the APIC timers after the other timers as the + // initialization needs to briefly enable interrupts, which then + // would trigger a deadlock trying to get the s_the instance while + // creating it. + if (auto* apic_timer = APIC::the().initialize_timers(*s_the->m_system_timer)) { + klog() << "Time: Using APIC timer as system timer"; + s_the->set_system_timer(*apic_timer); + } + } else { + ASSERT(s_the.is_initialized()); + if (auto* apic_timer = APIC::the().get_timer()) { + klog() << "Time: Enable APIC timer on CPU #" << cpu; + apic_timer->enable_local_timer(); + } + } } + +void TimeManagement::set_system_timer(HardwareTimerBase& timer) +{ + auto original_callback = m_system_timer->set_callback(nullptr); + timer.set_callback(move(original_callback)); + m_system_timer = timer; +} + time_t TimeManagement::seconds_since_boot() const { return m_seconds_since_boot; @@ -112,11 +139,9 @@ TimeManagement::TimeManagement() if (!probe_and_set_non_legacy_hardware_timers()) if (!probe_and_set_legacy_hardware_timers()) ASSERT_NOT_REACHED(); - return; + } else if (!probe_and_set_legacy_hardware_timers()) { + ASSERT_NOT_REACHED(); } - if (probe_and_set_legacy_hardware_timers()) - return; - ASSERT_NOT_REACHED(); } timeval TimeManagement::now_as_timeval() @@ -127,11 +152,11 @@ timeval TimeManagement::now_as_timeval() return tv; } -Vector TimeManagement::scan_and_initialize_periodic_timers() +Vector TimeManagement::scan_and_initialize_periodic_timers() { bool should_enable = is_hpet_periodic_mode_allowed(); dbg() << "Time: Scanning for periodic timers"; - Vector timers; + Vector timers; for (auto& hardware_timer : m_hardware_timers) { if (hardware_timer.is_periodic_capable()) { timers.append(&hardware_timer); @@ -142,10 +167,10 @@ Vector TimeManagement::scan_and_initialize_periodic_timers() return timers; } -Vector TimeManagement::scan_for_non_periodic_timers() +Vector TimeManagement::scan_for_non_periodic_timers() { dbg() << "Time: Scanning for non-periodic timers"; - Vector timers; + Vector timers; for (auto& hardware_timer : m_hardware_timers) { if (!hardware_timer.is_periodic_capable()) timers.append(&hardware_timer); diff --git a/Kernel/Time/TimeManagement.h b/Kernel/Time/TimeManagement.h index 9169713e3f..f3a3bb0188 100644 --- a/Kernel/Time/TimeManagement.h +++ b/Kernel/Time/TimeManagement.h @@ -35,7 +35,7 @@ namespace Kernel { #define OPTIMAL_TICKS_PER_SECOND_RATE 1000 -class HardwareTimer; +class HardwareTimerBase; class TimeManagement { AK_MAKE_ETERNAL; @@ -43,7 +43,7 @@ class TimeManagement { public: TimeManagement(); static bool initialized(); - static void initialize(); + static void initialize(u32 cpu); static TimeManagement& the(); timespec epoch_time() const; @@ -53,7 +53,7 @@ public: time_t ticks_this_second() const; time_t boot_time() const; - bool is_system_timer(const HardwareTimer&) const; + bool is_system_timer(const HardwareTimerBase&) const; static void update_time(const RegisterState&); void increment_time_since_boot(const RegisterState&); @@ -65,15 +65,16 @@ public: private: bool probe_and_set_legacy_hardware_timers(); bool probe_and_set_non_legacy_hardware_timers(); - Vector scan_and_initialize_periodic_timers(); - Vector scan_for_non_periodic_timers(); - NonnullRefPtrVector m_hardware_timers; + Vector scan_and_initialize_periodic_timers(); + Vector scan_for_non_periodic_timers(); + NonnullRefPtrVector m_hardware_timers; + void set_system_timer(HardwareTimerBase&); u32 m_ticks_this_second { 0 }; u32 m_seconds_since_boot { 0 }; timespec m_epoch_time { 0, 0 }; - RefPtr m_system_timer; - RefPtr m_time_keeper_timer; + RefPtr m_system_timer; + RefPtr m_time_keeper_timer; }; } diff --git a/Kernel/init.cpp b/Kernel/init.cpp index 70f2e4ac25..9de6695ebf 100644 --- a/Kernel/init.cpp +++ b/Kernel/init.cpp @@ -149,7 +149,7 @@ extern "C" [[noreturn]] void init() __stack_chk_guard = get_fast_random(); - TimeManagement::initialize(); + TimeManagement::initialize(0); NullDevice::initialize(); if (!get_serial_debug()) @@ -208,6 +208,7 @@ extern "C" void init_finished(u32 cpu) // TODO: we can reuse the boot stack, maybe for kmalloc()? } else { APIC::the().init_finished(cpu); + TimeManagement::initialize(cpu); } }