mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-12-28 21:54:40 +03:00
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.
This commit is contained in:
parent
9d347352a1
commit
fe615e601a
Notes:
sideshowbarker
2024-07-19 01:43:30 +09:00
Author: https://github.com/tomuta Commit: https://github.com/SerenityOS/serenity/commit/fe615e601a9 Pull-request: https://github.com/SerenityOS/serenity/pull/3850
@ -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
|
||||
|
@ -36,6 +36,7 @@
|
||||
#include <Kernel/Interrupts/APIC.h>
|
||||
#include <Kernel/Interrupts/SpuriousInterruptHandler.h>
|
||||
#include <Kernel/Thread.h>
|
||||
#include <Kernel/Time/APICTimer.h>
|
||||
#include <Kernel/VM/MemoryManager.h>
|
||||
#include <Kernel/VM/PageDirectory.h>
|
||||
#include <Kernel/VM/TypedMapping.h>
|
||||
@ -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<GenericInterruptHandler>::eoi()
|
||||
{
|
||||
APIC::the().eoi();
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -27,10 +27,13 @@
|
||||
#pragma once
|
||||
|
||||
#include <AK/Types.h>
|
||||
#include <Kernel/Time/HardwareTimer.h>
|
||||
#include <Kernel/VM/MemoryManager.h>
|
||||
|
||||
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<u8> 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);
|
||||
|
@ -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 };
|
||||
|
@ -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;
|
||||
|
183
Kernel/Time/APICTimer.cpp
Normal file
183
Kernel/Time/APICTimer.cpp
Normal file
@ -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 <Kernel/Arch/i386/CPU.h>
|
||||
#include <Kernel/IO.h>
|
||||
#include <Kernel/Interrupts/APIC.h>
|
||||
#include <Kernel/Scheduler.h>
|
||||
#include <Kernel/Thread.h>
|
||||
#include <Kernel/Time/APICTimer.h>
|
||||
#include <Kernel/Time/TimeManagement.h>
|
||||
|
||||
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<void(const RegisterState&)> callback)
|
||||
: HardwareTimer<GenericInterruptHandler>(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<size_t> 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;
|
||||
}
|
||||
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2020, Liav A. <liavalb@hotmail.co.il>
|
||||
* 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 <AK/NonnullRefPtr.h>
|
||||
#include <AK/Types.h>
|
||||
#include <Kernel/Interrupts/GenericInterruptHandler.h>
|
||||
#include <Kernel/Time/HardwareTimer.h>
|
||||
#include <Kernel/Time/TimeManagement.h>
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
HardwareTimer::HardwareTimer(u8 irq_number, Function<void(const RegisterState&)> callback)
|
||||
: IRQHandler(irq_number)
|
||||
, m_callback(move(callback))
|
||||
{
|
||||
}
|
||||
class APICTimer final : public HardwareTimer<GenericInterruptHandler> {
|
||||
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<void(const RegisterState&)> callback)
|
||||
{
|
||||
disable_irq();
|
||||
m_callback = move(callback);
|
||||
enable_irq();
|
||||
}
|
||||
void enable_local_timer();
|
||||
void disable_local_timer();
|
||||
|
||||
private:
|
||||
explicit APICTimer(u8, Function<void(const RegisterState&)>);
|
||||
|
||||
bool calibrate(HardwareTimerBase&);
|
||||
|
||||
u32 m_timer_period { 0 };
|
||||
APIC::TimerMode m_timer_mode { APIC::TimerMode::Periodic };
|
||||
};
|
||||
|
||||
}
|
@ -32,7 +32,7 @@
|
||||
#include <Kernel/Time/HardwareTimer.h>
|
||||
|
||||
namespace Kernel {
|
||||
class HPETComparator final : public HardwareTimer {
|
||||
class HPETComparator final : public HardwareTimer<IRQHandler> {
|
||||
friend class HPET;
|
||||
|
||||
public:
|
||||
|
@ -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<HardwareTimer>
|
||||
, public IRQHandler {
|
||||
template<typename InterruptHandlerType>
|
||||
class HardwareTimer;
|
||||
|
||||
class HardwareTimerBase
|
||||
: public RefCounted<HardwareTimerBase> {
|
||||
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<void(const RegisterState&)>);
|
||||
|
||||
virtual size_t ticks_per_second() const = 0;
|
||||
virtual HardwareTimerType timer_type() const = 0;
|
||||
virtual Function<void(const RegisterState&)> set_callback(Function<void(const RegisterState&)>) = 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<IRQHandler>
|
||||
: 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<void(const RegisterState&)> set_callback(Function<void(const RegisterState&)> 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<void(const RegisterState&)> = nullptr);
|
||||
//^IRQHandler
|
||||
virtual void handle_irq(const RegisterState&) override;
|
||||
HardwareTimer(u8 irq_number, Function<void(const RegisterState&)> 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<void(const RegisterState&)> m_callback;
|
||||
};
|
||||
|
||||
template<>
|
||||
class HardwareTimer<GenericInterruptHandler>
|
||||
: public HardwareTimerBase
|
||||
, public GenericInterruptHandler {
|
||||
public:
|
||||
virtual const char* purpose() const override
|
||||
{
|
||||
return model();
|
||||
}
|
||||
|
||||
virtual Function<void(const RegisterState&)> set_callback(Function<void(const RegisterState&)> 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<void(const RegisterState&)> 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:
|
||||
|
@ -52,7 +52,7 @@ namespace Kernel {
|
||||
|
||||
#define BASE_FREQUENCY 1193182
|
||||
|
||||
class PIT final : public HardwareTimer {
|
||||
class PIT final : public HardwareTimer<IRQHandler> {
|
||||
public:
|
||||
static NonnullRefPtr<PIT> initialize(Function<void(const RegisterState&)>);
|
||||
virtual HardwareTimerType timer_type() const override { return HardwareTimerType::i8253; }
|
||||
|
@ -31,7 +31,7 @@
|
||||
#include <Kernel/Time/HardwareTimer.h>
|
||||
|
||||
namespace Kernel {
|
||||
class RealTimeClock final : public HardwareTimer {
|
||||
class RealTimeClock final : public HardwareTimer<IRQHandler> {
|
||||
public:
|
||||
static NonnullRefPtr<RealTimeClock> create(Function<void(const RegisterState&)> callback);
|
||||
virtual HardwareTimerType timer_type() const override { return HardwareTimerType::RTC; }
|
||||
|
@ -28,7 +28,9 @@
|
||||
#include <AK/Time.h>
|
||||
#include <Kernel/ACPI/Parser.h>
|
||||
#include <Kernel/CommandLine.h>
|
||||
#include <Kernel/Interrupts/APIC.h>
|
||||
#include <Kernel/Scheduler.h>
|
||||
#include <Kernel/Time/APICTimer.h>
|
||||
#include <Kernel/Time/HPET.h>
|
||||
#include <Kernel/Time/HPETComparator.h>
|
||||
#include <Kernel/Time/HardwareTimer.h>
|
||||
@ -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<HardwareTimer*> TimeManagement::scan_and_initialize_periodic_timers()
|
||||
Vector<HardwareTimerBase*> TimeManagement::scan_and_initialize_periodic_timers()
|
||||
{
|
||||
bool should_enable = is_hpet_periodic_mode_allowed();
|
||||
dbg() << "Time: Scanning for periodic timers";
|
||||
Vector<HardwareTimer*> timers;
|
||||
Vector<HardwareTimerBase*> timers;
|
||||
for (auto& hardware_timer : m_hardware_timers) {
|
||||
if (hardware_timer.is_periodic_capable()) {
|
||||
timers.append(&hardware_timer);
|
||||
@ -142,10 +167,10 @@ Vector<HardwareTimer*> TimeManagement::scan_and_initialize_periodic_timers()
|
||||
return timers;
|
||||
}
|
||||
|
||||
Vector<HardwareTimer*> TimeManagement::scan_for_non_periodic_timers()
|
||||
Vector<HardwareTimerBase*> TimeManagement::scan_for_non_periodic_timers()
|
||||
{
|
||||
dbg() << "Time: Scanning for non-periodic timers";
|
||||
Vector<HardwareTimer*> timers;
|
||||
Vector<HardwareTimerBase*> timers;
|
||||
for (auto& hardware_timer : m_hardware_timers) {
|
||||
if (!hardware_timer.is_periodic_capable())
|
||||
timers.append(&hardware_timer);
|
||||
|
@ -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<HardwareTimer*> scan_and_initialize_periodic_timers();
|
||||
Vector<HardwareTimer*> scan_for_non_periodic_timers();
|
||||
NonnullRefPtrVector<HardwareTimer> m_hardware_timers;
|
||||
Vector<HardwareTimerBase*> scan_and_initialize_periodic_timers();
|
||||
Vector<HardwareTimerBase*> scan_for_non_periodic_timers();
|
||||
NonnullRefPtrVector<HardwareTimerBase> 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<HardwareTimer> m_system_timer;
|
||||
RefPtr<HardwareTimer> m_time_keeper_timer;
|
||||
RefPtr<HardwareTimerBase> m_system_timer;
|
||||
RefPtr<HardwareTimerBase> m_time_keeper_timer;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -149,7 +149,7 @@ extern "C" [[noreturn]] void init()
|
||||
|
||||
__stack_chk_guard = get_fast_random<u32>();
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user