mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-12-29 14:14:45 +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
|
Tasks/SyncTask.cpp
|
||||||
Thread.cpp
|
Thread.cpp
|
||||||
ThreadTracer.cpp
|
ThreadTracer.cpp
|
||||||
|
Time/APICTimer.cpp
|
||||||
Time/HPET.cpp
|
Time/HPET.cpp
|
||||||
Time/HPETComparator.cpp
|
Time/HPETComparator.cpp
|
||||||
Time/HardwareTimer.cpp
|
|
||||||
Time/PIT.cpp
|
Time/PIT.cpp
|
||||||
Time/RTC.cpp
|
Time/RTC.cpp
|
||||||
Time/TimeManagement.cpp
|
Time/TimeManagement.cpp
|
||||||
|
@ -36,6 +36,7 @@
|
|||||||
#include <Kernel/Interrupts/APIC.h>
|
#include <Kernel/Interrupts/APIC.h>
|
||||||
#include <Kernel/Interrupts/SpuriousInterruptHandler.h>
|
#include <Kernel/Interrupts/SpuriousInterruptHandler.h>
|
||||||
#include <Kernel/Thread.h>
|
#include <Kernel/Thread.h>
|
||||||
|
#include <Kernel/Time/APICTimer.h>
|
||||||
#include <Kernel/VM/MemoryManager.h>
|
#include <Kernel/VM/MemoryManager.h>
|
||||||
#include <Kernel/VM/PageDirectory.h>
|
#include <Kernel/VM/PageDirectory.h>
|
||||||
#include <Kernel/VM/TypedMapping.h>
|
#include <Kernel/VM/TypedMapping.h>
|
||||||
@ -43,6 +44,7 @@
|
|||||||
//#define APIC_DEBUG
|
//#define APIC_DEBUG
|
||||||
//#define APIC_SMP_DEBUG
|
//#define APIC_SMP_DEBUG
|
||||||
|
|
||||||
|
#define IRQ_APIC_TIMER (0xfc - IRQ_VECTOR_BASE)
|
||||||
#define IRQ_APIC_IPI (0xfd - IRQ_VECTOR_BASE)
|
#define IRQ_APIC_IPI (0xfd - IRQ_VECTOR_BASE)
|
||||||
#define IRQ_APIC_ERR (0xfe - IRQ_VECTOR_BASE)
|
#define IRQ_APIC_ERR (0xfe - IRQ_VECTOR_BASE)
|
||||||
#define IRQ_APIC_SPURIOUS (0xff - 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_LINT0 0x350
|
||||||
#define APIC_REG_LVT_LINT1 0x360
|
#define APIC_REG_LVT_LINT1 0x360
|
||||||
#define APIC_REG_LVT_ERR 0x370
|
#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 {
|
namespace Kernel {
|
||||||
|
|
||||||
@ -197,9 +202,13 @@ void APIC::write_icr(const ICRReg& icr)
|
|||||||
write_register(APIC_REG_ICR_LOW, icr.low());
|
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_MASKED (1 << 16)
|
||||||
#define APIC_LVT_TRIGGER_LEVEL (1 << 14)
|
#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" void apic_ap_start(void);
|
||||||
extern "C" u16 apic_ap_start_size;
|
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));
|
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&)
|
void APICIPIInterruptHandler::handle_interrupt(const RegisterState&)
|
||||||
{
|
{
|
||||||
#ifdef APIC_SMP_DEBUG
|
#ifdef APIC_SMP_DEBUG
|
||||||
@ -546,4 +632,10 @@ bool APICErrInterruptHandler::eoi()
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool HardwareTimer<GenericInterruptHandler>::eoi()
|
||||||
|
{
|
||||||
|
APIC::the().eoi();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -27,10 +27,13 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <AK/Types.h>
|
#include <AK/Types.h>
|
||||||
|
#include <Kernel/Time/HardwareTimer.h>
|
||||||
#include <Kernel/VM/MemoryManager.h>
|
#include <Kernel/VM/MemoryManager.h>
|
||||||
|
|
||||||
namespace Kernel {
|
namespace Kernel {
|
||||||
|
|
||||||
|
class APICTimer;
|
||||||
|
|
||||||
struct LocalAPIC {
|
struct LocalAPIC {
|
||||||
u32 apic_id;
|
u32 apic_id;
|
||||||
};
|
};
|
||||||
@ -52,6 +55,17 @@ public:
|
|||||||
Thread* get_idle_thread(u32 cpu) const;
|
Thread* get_idle_thread(u32 cpu) const;
|
||||||
u32 enabled_processor_count() const { return m_processor_enabled_cnt; }
|
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:
|
private:
|
||||||
class ICRReg {
|
class ICRReg {
|
||||||
u32 m_low { 0 };
|
u32 m_low { 0 };
|
||||||
@ -102,6 +116,7 @@ private:
|
|||||||
AK::Atomic<u8> m_apic_ap_continue { 0 };
|
AK::Atomic<u8> m_apic_ap_continue { 0 };
|
||||||
u32 m_processor_cnt { 0 };
|
u32 m_processor_cnt { 0 };
|
||||||
u32 m_processor_enabled_cnt { 0 };
|
u32 m_processor_enabled_cnt { 0 };
|
||||||
|
APICTimer* m_apic_timer { nullptr };
|
||||||
|
|
||||||
static PhysicalAddress get_base();
|
static PhysicalAddress get_base();
|
||||||
static void set_base(const PhysicalAddress& base);
|
static void set_base(const PhysicalAddress& base);
|
||||||
|
@ -65,6 +65,8 @@ protected:
|
|||||||
void change_interrupt_number(u8 number);
|
void change_interrupt_number(u8 number);
|
||||||
GenericInterruptHandler(u8 interrupt_number, bool disable_remap = false);
|
GenericInterruptHandler(u8 interrupt_number, bool disable_remap = false);
|
||||||
|
|
||||||
|
void disable_remap() { m_disable_remap = true; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
size_t m_invoking_count { 0 };
|
size_t m_invoking_count { 0 };
|
||||||
u8 m_interrupt_number { 0 };
|
u8 m_interrupt_number { 0 };
|
||||||
|
@ -777,15 +777,19 @@ void Scheduler::timer_tick(const RegisterState& regs)
|
|||||||
ASSERT_INTERRUPTS_DISABLED();
|
ASSERT_INTERRUPTS_DISABLED();
|
||||||
ASSERT(Processor::current().in_irq());
|
ASSERT(Processor::current().in_irq());
|
||||||
|
|
||||||
if (Processor::current().id() > 0)
|
|
||||||
return;
|
|
||||||
auto current_thread = Processor::current().current_thread();
|
auto current_thread = Processor::current().current_thread();
|
||||||
if (!current_thread)
|
if (!current_thread)
|
||||||
return;
|
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()) {
|
if (current_thread->process().is_profiling()) {
|
||||||
SmapDisabler disabler;
|
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())
|
if (current_thread->tick())
|
||||||
return;
|
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.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without
|
* 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.
|
* 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/HardwareTimer.h>
|
||||||
#include <Kernel/Time/TimeManagement.h>
|
|
||||||
|
|
||||||
namespace Kernel {
|
namespace Kernel {
|
||||||
|
|
||||||
HardwareTimer::HardwareTimer(u8 irq_number, Function<void(const RegisterState&)> callback)
|
class APICTimer final : public HardwareTimer<GenericInterruptHandler> {
|
||||||
: IRQHandler(irq_number)
|
public:
|
||||||
, m_callback(move(callback))
|
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)
|
virtual bool is_periodic() const override { return m_timer_mode == APIC::TimerMode::Periodic; }
|
||||||
{
|
virtual bool is_periodic_capable() const override { return true; }
|
||||||
if (m_callback)
|
virtual void set_periodic() override;
|
||||||
m_callback(regs);
|
virtual void set_non_periodic() override;
|
||||||
}
|
|
||||||
|
|
||||||
const char* HardwareTimer::purpose() const
|
virtual void reset_to_default_ticks_per_second() override;
|
||||||
{
|
virtual bool try_to_set_frequency(size_t frequency) override;
|
||||||
if (TimeManagement::the().is_system_timer(*this))
|
virtual bool is_capable_of_frequency(size_t frequency) const override;
|
||||||
return "System Timer";
|
virtual size_t calculate_nearest_possible_frequency(size_t frequency) const override;
|
||||||
return model();
|
|
||||||
}
|
|
||||||
|
|
||||||
void HardwareTimer::set_callback(Function<void(const RegisterState&)> callback)
|
void enable_local_timer();
|
||||||
{
|
void disable_local_timer();
|
||||||
disable_irq();
|
|
||||||
m_callback = move(callback);
|
private:
|
||||||
enable_irq();
|
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>
|
#include <Kernel/Time/HardwareTimer.h>
|
||||||
|
|
||||||
namespace Kernel {
|
namespace Kernel {
|
||||||
class HPETComparator final : public HardwareTimer {
|
class HPETComparator final : public HardwareTimer<IRQHandler> {
|
||||||
friend class HPET;
|
friend class HPET;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
@ -35,37 +35,114 @@
|
|||||||
namespace Kernel {
|
namespace Kernel {
|
||||||
|
|
||||||
enum class HardwareTimerType {
|
enum class HardwareTimerType {
|
||||||
i8253 = 0x1, /* PIT */
|
i8253 = 0x1, /* PIT */
|
||||||
RTC = 0x2, /* Real Time Clock */
|
RTC = 0x2, /* Real Time Clock */
|
||||||
HighPrecisionEventTimer = 0x3 /* also known as IA-PC HPET */
|
HighPrecisionEventTimer = 0x3, /* also known as IA-PC HPET */
|
||||||
|
LocalAPICTimer = 0x4 /* Local APIC */
|
||||||
};
|
};
|
||||||
|
|
||||||
class HardwareTimer
|
template<typename InterruptHandlerType>
|
||||||
: public RefCounted<HardwareTimer>
|
class HardwareTimer;
|
||||||
, public IRQHandler {
|
|
||||||
|
class HardwareTimerBase
|
||||||
|
: public RefCounted<HardwareTimerBase> {
|
||||||
public:
|
public:
|
||||||
virtual HardwareTimerType timer_type() const = 0;
|
virtual ~HardwareTimerBase() { }
|
||||||
|
|
||||||
virtual const char* model() const = 0;
|
virtual const char* model() const = 0;
|
||||||
virtual const char* purpose() const override;
|
virtual HardwareTimerType timer_type() const = 0;
|
||||||
|
virtual Function<void(const RegisterState&)> set_callback(Function<void(const RegisterState&)>) = 0;
|
||||||
void set_callback(Function<void(const RegisterState&)>);
|
|
||||||
|
|
||||||
virtual size_t ticks_per_second() const = 0;
|
|
||||||
|
|
||||||
virtual bool is_periodic() const = 0;
|
virtual bool is_periodic() const = 0;
|
||||||
virtual bool is_periodic_capable() const = 0;
|
virtual bool is_periodic_capable() const = 0;
|
||||||
virtual void set_periodic() = 0;
|
virtual void set_periodic() = 0;
|
||||||
virtual void set_non_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 void reset_to_default_ticks_per_second() = 0;
|
||||||
virtual bool try_to_set_frequency(size_t frequency) = 0;
|
virtual bool try_to_set_frequency(size_t frequency) = 0;
|
||||||
virtual bool is_capable_of_frequency(size_t frequency) const = 0;
|
virtual bool is_capable_of_frequency(size_t frequency) const = 0;
|
||||||
virtual size_t calculate_nearest_possible_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:
|
protected:
|
||||||
HardwareTimer(u8 irq_number, Function<void(const RegisterState&)> = nullptr);
|
HardwareTimer(u8 irq_number, Function<void(const RegisterState&)> callback = nullptr)
|
||||||
//^IRQHandler
|
: IRQHandler(irq_number)
|
||||||
virtual void handle_irq(const RegisterState&) override;
|
, 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 };
|
u64 m_frequency { OPTIMAL_TICKS_PER_SECOND_RATE };
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -52,7 +52,7 @@ namespace Kernel {
|
|||||||
|
|
||||||
#define BASE_FREQUENCY 1193182
|
#define BASE_FREQUENCY 1193182
|
||||||
|
|
||||||
class PIT final : public HardwareTimer {
|
class PIT final : public HardwareTimer<IRQHandler> {
|
||||||
public:
|
public:
|
||||||
static NonnullRefPtr<PIT> initialize(Function<void(const RegisterState&)>);
|
static NonnullRefPtr<PIT> initialize(Function<void(const RegisterState&)>);
|
||||||
virtual HardwareTimerType timer_type() const override { return HardwareTimerType::i8253; }
|
virtual HardwareTimerType timer_type() const override { return HardwareTimerType::i8253; }
|
||||||
|
@ -31,7 +31,7 @@
|
|||||||
#include <Kernel/Time/HardwareTimer.h>
|
#include <Kernel/Time/HardwareTimer.h>
|
||||||
|
|
||||||
namespace Kernel {
|
namespace Kernel {
|
||||||
class RealTimeClock final : public HardwareTimer {
|
class RealTimeClock final : public HardwareTimer<IRQHandler> {
|
||||||
public:
|
public:
|
||||||
static NonnullRefPtr<RealTimeClock> create(Function<void(const RegisterState&)> callback);
|
static NonnullRefPtr<RealTimeClock> create(Function<void(const RegisterState&)> callback);
|
||||||
virtual HardwareTimerType timer_type() const override { return HardwareTimerType::RTC; }
|
virtual HardwareTimerType timer_type() const override { return HardwareTimerType::RTC; }
|
||||||
|
@ -28,7 +28,9 @@
|
|||||||
#include <AK/Time.h>
|
#include <AK/Time.h>
|
||||||
#include <Kernel/ACPI/Parser.h>
|
#include <Kernel/ACPI/Parser.h>
|
||||||
#include <Kernel/CommandLine.h>
|
#include <Kernel/CommandLine.h>
|
||||||
|
#include <Kernel/Interrupts/APIC.h>
|
||||||
#include <Kernel/Scheduler.h>
|
#include <Kernel/Scheduler.h>
|
||||||
|
#include <Kernel/Time/APICTimer.h>
|
||||||
#include <Kernel/Time/HPET.h>
|
#include <Kernel/Time/HPET.h>
|
||||||
#include <Kernel/Time/HPETComparator.h>
|
#include <Kernel/Time/HPETComparator.h>
|
||||||
#include <Kernel/Time/HardwareTimer.h>
|
#include <Kernel/Time/HardwareTimer.h>
|
||||||
@ -48,7 +50,7 @@ TimeManagement& TimeManagement::the()
|
|||||||
return *s_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();
|
return &timer == m_system_timer.ptr();
|
||||||
}
|
}
|
||||||
@ -69,11 +71,36 @@ timespec TimeManagement::epoch_time() const
|
|||||||
return ts;
|
return ts;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TimeManagement::initialize()
|
void TimeManagement::initialize(u32 cpu)
|
||||||
{
|
{
|
||||||
ASSERT(!s_the.is_initialized());
|
if (cpu == 0) {
|
||||||
s_the.ensure_instance();
|
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
|
time_t TimeManagement::seconds_since_boot() const
|
||||||
{
|
{
|
||||||
return m_seconds_since_boot;
|
return m_seconds_since_boot;
|
||||||
@ -112,11 +139,9 @@ TimeManagement::TimeManagement()
|
|||||||
if (!probe_and_set_non_legacy_hardware_timers())
|
if (!probe_and_set_non_legacy_hardware_timers())
|
||||||
if (!probe_and_set_legacy_hardware_timers())
|
if (!probe_and_set_legacy_hardware_timers())
|
||||||
ASSERT_NOT_REACHED();
|
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()
|
timeval TimeManagement::now_as_timeval()
|
||||||
@ -127,11 +152,11 @@ timeval TimeManagement::now_as_timeval()
|
|||||||
return tv;
|
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();
|
bool should_enable = is_hpet_periodic_mode_allowed();
|
||||||
dbg() << "Time: Scanning for periodic timers";
|
dbg() << "Time: Scanning for periodic timers";
|
||||||
Vector<HardwareTimer*> timers;
|
Vector<HardwareTimerBase*> timers;
|
||||||
for (auto& hardware_timer : m_hardware_timers) {
|
for (auto& hardware_timer : m_hardware_timers) {
|
||||||
if (hardware_timer.is_periodic_capable()) {
|
if (hardware_timer.is_periodic_capable()) {
|
||||||
timers.append(&hardware_timer);
|
timers.append(&hardware_timer);
|
||||||
@ -142,10 +167,10 @@ Vector<HardwareTimer*> TimeManagement::scan_and_initialize_periodic_timers()
|
|||||||
return 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";
|
dbg() << "Time: Scanning for non-periodic timers";
|
||||||
Vector<HardwareTimer*> timers;
|
Vector<HardwareTimerBase*> timers;
|
||||||
for (auto& hardware_timer : m_hardware_timers) {
|
for (auto& hardware_timer : m_hardware_timers) {
|
||||||
if (!hardware_timer.is_periodic_capable())
|
if (!hardware_timer.is_periodic_capable())
|
||||||
timers.append(&hardware_timer);
|
timers.append(&hardware_timer);
|
||||||
|
@ -35,7 +35,7 @@ namespace Kernel {
|
|||||||
|
|
||||||
#define OPTIMAL_TICKS_PER_SECOND_RATE 1000
|
#define OPTIMAL_TICKS_PER_SECOND_RATE 1000
|
||||||
|
|
||||||
class HardwareTimer;
|
class HardwareTimerBase;
|
||||||
|
|
||||||
class TimeManagement {
|
class TimeManagement {
|
||||||
AK_MAKE_ETERNAL;
|
AK_MAKE_ETERNAL;
|
||||||
@ -43,7 +43,7 @@ class TimeManagement {
|
|||||||
public:
|
public:
|
||||||
TimeManagement();
|
TimeManagement();
|
||||||
static bool initialized();
|
static bool initialized();
|
||||||
static void initialize();
|
static void initialize(u32 cpu);
|
||||||
static TimeManagement& the();
|
static TimeManagement& the();
|
||||||
|
|
||||||
timespec epoch_time() const;
|
timespec epoch_time() const;
|
||||||
@ -53,7 +53,7 @@ public:
|
|||||||
time_t ticks_this_second() const;
|
time_t ticks_this_second() const;
|
||||||
time_t boot_time() 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&);
|
static void update_time(const RegisterState&);
|
||||||
void increment_time_since_boot(const RegisterState&);
|
void increment_time_since_boot(const RegisterState&);
|
||||||
@ -65,15 +65,16 @@ public:
|
|||||||
private:
|
private:
|
||||||
bool probe_and_set_legacy_hardware_timers();
|
bool probe_and_set_legacy_hardware_timers();
|
||||||
bool probe_and_set_non_legacy_hardware_timers();
|
bool probe_and_set_non_legacy_hardware_timers();
|
||||||
Vector<HardwareTimer*> scan_and_initialize_periodic_timers();
|
Vector<HardwareTimerBase*> scan_and_initialize_periodic_timers();
|
||||||
Vector<HardwareTimer*> scan_for_non_periodic_timers();
|
Vector<HardwareTimerBase*> scan_for_non_periodic_timers();
|
||||||
NonnullRefPtrVector<HardwareTimer> m_hardware_timers;
|
NonnullRefPtrVector<HardwareTimerBase> m_hardware_timers;
|
||||||
|
void set_system_timer(HardwareTimerBase&);
|
||||||
|
|
||||||
u32 m_ticks_this_second { 0 };
|
u32 m_ticks_this_second { 0 };
|
||||||
u32 m_seconds_since_boot { 0 };
|
u32 m_seconds_since_boot { 0 };
|
||||||
timespec m_epoch_time { 0, 0 };
|
timespec m_epoch_time { 0, 0 };
|
||||||
RefPtr<HardwareTimer> m_system_timer;
|
RefPtr<HardwareTimerBase> m_system_timer;
|
||||||
RefPtr<HardwareTimer> m_time_keeper_timer;
|
RefPtr<HardwareTimerBase> m_time_keeper_timer;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -149,7 +149,7 @@ extern "C" [[noreturn]] void init()
|
|||||||
|
|
||||||
__stack_chk_guard = get_fast_random<u32>();
|
__stack_chk_guard = get_fast_random<u32>();
|
||||||
|
|
||||||
TimeManagement::initialize();
|
TimeManagement::initialize(0);
|
||||||
|
|
||||||
NullDevice::initialize();
|
NullDevice::initialize();
|
||||||
if (!get_serial_debug())
|
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()?
|
// TODO: we can reuse the boot stack, maybe for kmalloc()?
|
||||||
} else {
|
} else {
|
||||||
APIC::the().init_finished(cpu);
|
APIC::the().init_finished(cpu);
|
||||||
|
TimeManagement::initialize(cpu);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user