1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-28 17:07:47 +00:00

Kernel: Reorganize Arch/x86 directory to Arch/x86_64 after i686 removal

No functional change.
This commit is contained in:
Liav A 2022-10-04 13:46:11 +03:00 committed by Andreas Kling
parent 5ff318cf3a
commit 91db482ad3
129 changed files with 482 additions and 1116 deletions

View file

@ -0,0 +1,179 @@
/*
* Copyright (c) 2020, the SerenityOS developers.
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <Kernel/Arch/x86_64/Interrupts/APIC.h>
#include <Kernel/Arch/x86_64/Time/APICTimer.h>
#include <Kernel/Panic.h>
#include <Kernel/Sections.h>
#include <Kernel/Time/TimeManagement.h>
namespace Kernel {
#define APIC_TIMER_MEASURE_CPU_CLOCK
UNMAP_AFTER_INIT APICTimer* APICTimer::initialize(u8 interrupt_number, HardwareTimerBase& calibration_source)
{
auto timer = adopt_lock_ref(*new APICTimer(interrupt_number, nullptr));
timer->register_interrupt_handler();
if (!timer->calibrate(calibration_source)) {
return nullptr;
}
return &timer.leak_ref();
}
UNMAP_AFTER_INIT APICTimer::APICTimer(u8 interrupt_number, Function<void(RegisterState const&)> callback)
: HardwareTimer<GenericInterruptHandler>(interrupt_number, move(callback))
{
disable_remap();
}
UNMAP_AFTER_INIT bool APICTimer::calibrate(HardwareTimerBase& calibration_source)
{
VERIFY_INTERRUPTS_DISABLED();
dmesgln("APICTimer: Using {} as calibration source", calibration_source.model());
struct {
#ifdef APIC_TIMER_MEASURE_CPU_CLOCK
bool supports_tsc { Processor::current().has_feature(CPUFeature::TSC) };
#endif
APIC& apic { APIC::the() };
size_t ticks_in_100ms { 0 };
Atomic<size_t, AK::memory_order_relaxed> calibration_ticks { 0 };
#ifdef APIC_TIMER_MEASURE_CPU_CLOCK
volatile u64 start_tsc { 0 }, end_tsc { 0 };
#endif
volatile u64 start_reference { 0 }, end_reference { 0 };
volatile u32 start_apic_count { 0 }, end_apic_count { 0 };
bool query_reference { false };
} state;
state.ticks_in_100ms = calibration_source.ticks_per_second() / 10;
state.query_reference = calibration_source.can_query_raw();
// temporarily replace the timer callbacks
auto original_source_callback = calibration_source.set_callback([&state, &calibration_source](RegisterState const&) {
u32 current_timer_count = state.apic.get_timer_current_count();
#ifdef APIC_TIMER_MEASURE_CPU_CLOCK
u64 current_tsc = state.supports_tsc ? read_tsc() : 0;
#endif
u64 current_reference = state.query_reference ? calibration_source.current_raw() : 0;
auto prev_tick = state.calibration_ticks.fetch_add(1);
if (prev_tick == 0) {
#ifdef APIC_TIMER_MEASURE_CPU_CLOCK
state.start_tsc = current_tsc;
#endif
state.start_apic_count = current_timer_count;
state.start_reference = current_reference;
} else if (prev_tick + 1 == state.ticks_in_100ms + 1) {
#ifdef APIC_TIMER_MEASURE_CPU_CLOCK
state.end_tsc = current_tsc;
#endif
state.end_apic_count = current_timer_count;
state.end_reference = current_reference;
}
});
// 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([&](RegisterState const&) {
// TODO: How should we handle this?
PANIC("APICTimer: Timer fired during calibration!");
});
state.apic.setup_local_timer(0xffffffff, APIC::TimerMode::Periodic, true);
sti();
// Loop for about 100 ms
while (state.calibration_ticks.load() <= state.ticks_in_100ms)
;
cli();
// Restore timer callbacks
calibration_source.set_callback(move(original_source_callback));
set_callback(move(original_callback));
disable_local_timer();
if (state.query_reference) {
u64 one_tick_ns = calibration_source.raw_to_ns((state.end_reference - state.start_reference) / state.ticks_in_100ms);
m_frequency = (u32)(1000000000ull / one_tick_ns);
dmesgln("APICTimer: Ticks per second: {} ({}.{}ms)", m_frequency, one_tick_ns / 1000000, one_tick_ns % 1000000);
} else {
// For now, assume the frequency is exactly the same
m_frequency = calibration_source.ticks_per_second();
dmesgln("APICTimer: Ticks per second: {} (assume same frequency as reference clock)", m_frequency);
}
auto delta_apic_count = state.start_apic_count - state.end_apic_count; // The APIC current count register decrements!
m_timer_period = (delta_apic_count * state.apic.get_timer_divisor()) / state.ticks_in_100ms;
u64 apic_freq = delta_apic_count * state.apic.get_timer_divisor() * 10;
dmesgln("APICTimer: Bus clock speed: {}.{} MHz", apic_freq / 1000000, apic_freq % 1000000);
if (apic_freq < 1000000) {
dmesgln("APICTimer: Frequency too slow!");
return false;
}
#ifdef APIC_TIMER_MEASURE_CPU_CLOCK
if (state.supports_tsc) {
auto delta_tsc = (state.end_tsc - state.start_tsc) * 10;
dmesgln("APICTimer: CPU clock speed: {}.{} MHz", delta_tsc / 1000000, delta_tsc % 1000000);
}
#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...
VERIFY_NOT_REACHED();
}
void APICTimer::set_non_periodic()
{
// FIXME: Implement it...
VERIFY_NOT_REACHED();
}
void APICTimer::reset_to_default_ticks_per_second()
{
}
bool APICTimer::try_to_set_frequency([[maybe_unused]] size_t frequency)
{
return true;
}
bool APICTimer::is_capable_of_frequency([[maybe_unused]] size_t frequency) const
{
return false;
}
size_t APICTimer::calculate_nearest_possible_frequency([[maybe_unused]] size_t frequency) const
{
return 0;
}
}

View file

@ -0,0 +1,47 @@
/*
* Copyright (c) 2020, the SerenityOS developers.
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/Types.h>
#include <Kernel/Arch/x86_64/Interrupts/APIC.h>
#include <Kernel/Interrupts/GenericInterruptHandler.h>
#include <Kernel/Time/HardwareTimer.h>
namespace Kernel {
class APICTimer final : public HardwareTimer<GenericInterruptHandler> {
public:
static APICTimer* initialize(u8, HardwareTimerBase&);
virtual HardwareTimerType timer_type() const override { return HardwareTimerType::LocalAPICTimer; }
virtual StringView model() const override { return "LocalAPIC"sv; }
virtual size_t ticks_per_second() const override;
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;
virtual void disable() override { }
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 will_be_destroyed() override { HardwareTimer<GenericInterruptHandler>::will_be_destroyed(); }
void enable_local_timer();
void disable_local_timer();
private:
explicit APICTimer(u8, Function<void(RegisterState const&)>);
bool calibrate(HardwareTimerBase&);
u32 m_timer_period { 0 };
APIC::TimerMode m_timer_mode { APIC::TimerMode::Periodic };
};
}

View file

@ -0,0 +1,462 @@
/*
* Copyright (c) 2020, Liav A. <liavalb@hotmail.co.il>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/StringView.h>
#include <Kernel/Arch/x86_64/Time/HPET.h>
#include <Kernel/Arch/x86_64/Time/HPETComparator.h>
#include <Kernel/Debug.h>
#include <Kernel/Firmware/ACPI/Parser.h>
#include <Kernel/Memory/MemoryManager.h>
#include <Kernel/Memory/TypedMapping.h>
#include <Kernel/Sections.h>
#include <Kernel/Time/TimeManagement.h>
namespace Kernel {
#define ABSOLUTE_MAXIMUM_COUNTER_TICK_PERIOD 0x05F5E100
#define NANOSECOND_PERIOD_TO_HERTZ(x) 1000000000 / x
#define HERTZ_TO_MEGAHERTZ(x) (x / 1000000)
namespace HPETFlags {
enum class Attributes {
Counter64BitCapable = 1 << 13,
LegacyReplacementRouteCapable = 1 << 15
};
enum class Configuration {
Enable = 1 << 0,
LegacyReplacementRoute = 1 << 1
};
enum class TimerConfiguration : u32 {
LevelTriggered = 1 << 1,
InterruptEnable = 1 << 2,
GeneratePeriodicInterrupt = 1 << 3,
PeriodicInterruptCapable = 1 << 4,
Timer64BitsCapable = 1 << 5,
ValueSet = 1 << 6,
Force32BitMode = 1 << 8,
FSBInterruptEnable = 1 << 14,
FSBInterruptDelivery = 1 << 15
};
};
struct [[gnu::packed]] HPETRegister {
union {
volatile u64 full;
struct {
volatile u32 low;
volatile u32 high;
};
};
};
struct [[gnu::packed]] TimerStructure {
volatile u32 capabilities;
volatile u32 interrupt_routing;
HPETRegister comparator_value;
volatile u64 fsb_interrupt_route;
u64 reserved;
};
struct [[gnu::packed]] HPETCapabilityRegister {
// Note: We must do a 32 bit access to offsets 0x0, or 0x4 only, according to HPET spec.
volatile u32 attributes;
volatile u32 main_counter_tick_period;
u64 reserved;
};
struct [[gnu::packed]] HPETRegistersBlock {
HPETCapabilityRegister capabilities;
HPETRegister configuration;
u64 reserved1;
HPETRegister interrupt_status;
u8 reserved2[0xF0 - 0x28];
HPETRegister main_counter_value;
u64 reserved3;
TimerStructure timers[32];
};
static_assert(__builtin_offsetof(HPETRegistersBlock, main_counter_value) == 0xf0);
static_assert(__builtin_offsetof(HPETRegistersBlock, timers[0]) == 0x100);
static_assert(__builtin_offsetof(HPETRegistersBlock, timers[1]) == 0x120);
// Note: The HPET specification says it reserves the range of byte 0x160 to
// 0x400 for comparators 3-31, but for implementing all 32 comparators the HPET
// MMIO space has to be 1280 bytes and not 1024 bytes.
static_assert(AssertSize<HPETRegistersBlock, 0x500>());
static u64 read_register_safe64(HPETRegister const& reg)
{
return reg.full;
}
static HPET* s_hpet;
static bool hpet_initialized { false };
bool HPET::initialized()
{
return hpet_initialized;
}
HPET& HPET::the()
{
VERIFY(HPET::initialized());
VERIFY(s_hpet != nullptr);
return *s_hpet;
}
UNMAP_AFTER_INIT bool HPET::test_and_initialize()
{
VERIFY(!HPET::initialized());
hpet_initialized = true;
auto hpet_table = ACPI::Parser::the()->find_table("HPET"sv);
if (!hpet_table.has_value())
return false;
dmesgln("HPET @ {}", hpet_table.value());
auto sdt_or_error = Memory::map_typed<ACPI::Structures::HPET>(hpet_table.value());
if (sdt_or_error.is_error()) {
dbgln("Failed mapping HPET table");
return false;
}
// Note: HPET is only usable from System Memory
VERIFY(sdt_or_error.value()->event_timer_block.address_space == (u8)ACPI::GenericAddressStructure::AddressSpace::SystemMemory);
if (TimeManagement::is_hpet_periodic_mode_allowed()) {
if (!check_for_exisiting_periodic_timers()) {
dbgln("HPET: No periodic capable timers");
return false;
}
}
new HPET(PhysicalAddress(hpet_table.value()));
return true;
}
UNMAP_AFTER_INIT bool HPET::check_for_exisiting_periodic_timers()
{
auto hpet_table = ACPI::Parser::the()->find_table("HPET"sv);
if (!hpet_table.has_value())
return false;
auto sdt_or_error = Memory::map_typed<ACPI::Structures::HPET>(hpet_table.value());
if (sdt_or_error.is_error())
return false;
auto sdt = sdt_or_error.release_value();
VERIFY(sdt->event_timer_block.address_space == 0);
auto registers_or_error = Memory::map_typed<HPETRegistersBlock>(PhysicalAddress(sdt->event_timer_block.address));
if (registers_or_error.is_error())
return false;
auto registers = registers_or_error.release_value();
size_t timers_count = ((registers->capabilities.attributes >> 8) & 0x1f) + 1;
for (size_t index = 0; index < timers_count; index++) {
if (registers->timers[index].capabilities & (u32)HPETFlags::TimerConfiguration::PeriodicInterruptCapable)
return true;
}
return false;
}
void HPET::global_disable()
{
auto& regs = registers();
regs.configuration.low = regs.configuration.low & ~(u32)HPETFlags::Configuration::Enable;
}
void HPET::global_enable()
{
auto& regs = registers();
regs.configuration.low = regs.configuration.low | (u32)HPETFlags::Configuration::Enable;
}
void HPET::update_periodic_comparator_value()
{
// According to 2.3.9.2.2 the only safe way to change the periodic timer frequency
// is to disable all periodic timers, reset the main counter and each timer's comparator value.
// This introduces time drift, so it should be avoided unless absolutely necessary.
global_disable();
auto& regs = registers();
u64 previous_main_value = (u64)regs.main_counter_value.low | ((u64)regs.main_counter_value.high << 32);
m_main_counter_drift += previous_main_value - m_main_counter_last_read;
m_main_counter_last_read = 0;
regs.main_counter_value.low = 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())
continue;
if (comparator.is_periodic()) {
// Note that this means we're restarting all periodic timers. There is no
// way to resume periodic timers properly because we reset the main counter
// and we can only write the period into the comparator value...
timer.capabilities = timer.capabilities | (u32)HPETFlags::TimerConfiguration::ValueSet;
u64 value = ns_to_raw_counter_ticks(1000000000ull / comparator.ticks_per_second());
dbgln_if(HPET_DEBUG, "HPET: Update periodic comparator {} comparator value to {} main value was: {}",
comparator.comparator_number(),
value,
previous_main_value);
timer.comparator_value.low = (u32)value;
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);
u64 value = current_value - previous_main_value;
dbgln_if(HPET_DEBUG, "HPET: Update non-periodic comparator {} comparator value from {} to {} main value was: {}",
comparator.comparator_number(),
current_value,
value,
previous_main_value);
timer.comparator_value.low = (u32)value;
if (comparator.is_64bit_capable())
timer.comparator_value.high = (u32)(value >> 32);
}
}
global_enable();
}
void HPET::update_non_periodic_comparator_value(HPETComparator const& comparator)
{
VERIFY_INTERRUPTS_DISABLED();
VERIFY(!comparator.is_periodic());
VERIFY(comparator.comparator_number() <= m_comparators.size());
auto& regs = registers();
auto& timer = regs.timers[comparator.comparator_number()];
u64 value = frequency() / comparator.ticks_per_second();
// NOTE: If the main counter passes this new value before we finish writing it, we will never receive an interrupt!
u64 new_counter_value = read_main_counter() + value;
timer.comparator_value.high = (u32)(new_counter_value >> 32);
timer.comparator_value.low = (u32)new_counter_value;
}
u64 HPET::update_time(u64& seconds_since_boot, u32& ticks_this_second, bool query_only)
{
// 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) {
delta_ticks += current_value - m_main_counter_last_read;
} else {
// the counter wrapped around
if (m_main_counter_64bits) {
delta_ticks += (NumericLimits<u64>::max() - m_main_counter_last_read + 1) + current_value;
} else {
delta_ticks += (NumericLimits<u32>::max() - m_main_counter_last_read + 1) + current_value;
m_32bit_main_counter_wraps++;
}
}
u64 ticks_since_last_second = (u64)ticks_this_second + delta_ticks;
auto ticks_per_second = frequency();
seconds_since_boot += ticks_since_last_second / ticks_per_second;
ticks_this_second = ticks_since_last_second % ticks_per_second;
if (!query_only) {
m_main_counter_drift = 0;
m_main_counter_last_read = current_value;
}
// Return the time passed (in ns) since last time update_time was called
return (delta_ticks * 1000000000ull) / ticks_per_second;
}
u64 HPET::read_main_counter_unsafe() const
{
auto& main_counter = registers().main_counter_value;
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
{
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(HPETComparator const& comparator)
{
dbgln_if(HPET_DEBUG, "HPET: Set comparator {} to be periodic.", comparator.comparator_number());
disable(comparator);
VERIFY(comparator.comparator_number() <= m_comparators.size());
auto& timer = registers().timers[comparator.comparator_number()];
auto capabilities = timer.capabilities;
VERIFY(capabilities & (u32)HPETFlags::TimerConfiguration::PeriodicInterruptCapable);
timer.capabilities = capabilities | (u32)HPETFlags::TimerConfiguration::GeneratePeriodicInterrupt;
if (comparator.is_enabled())
enable(comparator);
}
void HPET::disable_periodic_interrupt(HPETComparator const& comparator)
{
dbgln_if(HPET_DEBUG, "HPET: Disable periodic interrupt in comparator {}", comparator.comparator_number());
disable(comparator);
VERIFY(comparator.comparator_number() <= m_comparators.size());
auto& timer = registers().timers[comparator.comparator_number()];
auto capabilities = timer.capabilities;
VERIFY(capabilities & (u32)HPETFlags::TimerConfiguration::PeriodicInterruptCapable);
timer.capabilities = capabilities & ~(u32)HPETFlags::TimerConfiguration::GeneratePeriodicInterrupt;
if (comparator.is_enabled())
enable(comparator);
}
void HPET::disable(HPETComparator const& comparator)
{
dbgln_if(HPET_DEBUG, "HPET: Disable comparator {}", comparator.comparator_number());
VERIFY(comparator.comparator_number() <= m_comparators.size());
auto& timer = registers().timers[comparator.comparator_number()];
timer.capabilities = timer.capabilities & ~(u32)HPETFlags::TimerConfiguration::InterruptEnable;
}
void HPET::enable(HPETComparator const& comparator)
{
dbgln_if(HPET_DEBUG, "HPET: Enable comparator {}", comparator.comparator_number());
VERIFY(comparator.comparator_number() <= m_comparators.size());
auto& timer = registers().timers[comparator.comparator_number()];
timer.capabilities = timer.capabilities | (u32)HPETFlags::TimerConfiguration::InterruptEnable;
}
Vector<unsigned> HPET::capable_interrupt_numbers(HPETComparator const& comparator)
{
VERIFY(comparator.comparator_number() <= m_comparators.size());
Vector<unsigned> capable_interrupts;
auto& comparator_registers = registers().timers[comparator.comparator_number()];
u32 interrupt_bitfield = comparator_registers.interrupt_routing;
for (size_t index = 0; index < 32; index++) {
if (interrupt_bitfield & 1)
capable_interrupts.append(index);
interrupt_bitfield >>= 1;
}
return capable_interrupts;
}
Vector<unsigned> HPET::capable_interrupt_numbers(u8 comparator_number)
{
VERIFY(comparator_number <= m_comparators.size());
Vector<unsigned> capable_interrupts;
auto& comparator_registers = registers().timers[comparator_number];
u32 interrupt_bitfield = comparator_registers.interrupt_routing;
for (size_t index = 0; index < 32; index++) {
if (interrupt_bitfield & 1)
capable_interrupts.append(index);
interrupt_bitfield >>= 1;
}
return capable_interrupts;
}
void HPET::set_comparator_irq_vector(u8 comparator_number, u8 irq_vector)
{
VERIFY(comparator_number <= m_comparators.size());
auto& comparator_registers = registers().timers[comparator_number];
comparator_registers.capabilities = comparator_registers.capabilities | (irq_vector << 9);
}
bool HPET::is_periodic_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::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...
VERIFY_NOT_REACHED();
}
PhysicalAddress HPET::find_acpi_hpet_registers_block()
{
auto sdt = Memory::map_typed<const volatile ACPI::Structures::HPET>(m_physical_acpi_hpet_table).release_value_but_fixme_should_propagate_errors();
VERIFY(sdt->event_timer_block.address_space == (u8)ACPI::GenericAddressStructure::AddressSpace::SystemMemory);
return PhysicalAddress(sdt->event_timer_block.address);
}
HPETRegistersBlock const& HPET::registers() const
{
return *(HPETRegistersBlock const*)m_hpet_mmio_region->vaddr().offset(m_physical_acpi_hpet_registers.offset_in_page()).as_ptr();
}
HPETRegistersBlock& HPET::registers()
{
return *(HPETRegistersBlock*)m_hpet_mmio_region->vaddr().offset(m_physical_acpi_hpet_registers.offset_in_page()).as_ptr();
}
u64 HPET::raw_counter_ticks_to_ns(u64 raw_ticks) const
{
// ABSOLUTE_MAXIMUM_COUNTER_TICK_PERIOD == 100 nanoseconds
return (raw_ticks * (u64)registers().capabilities.main_counter_tick_period * 100ull) / ABSOLUTE_MAXIMUM_COUNTER_TICK_PERIOD;
}
u64 HPET::ns_to_raw_counter_ticks(u64 ns) const
{
return (ns * 1000000ull) / (u64)registers().capabilities.main_counter_tick_period;
}
UNMAP_AFTER_INIT HPET::HPET(PhysicalAddress acpi_hpet)
: m_physical_acpi_hpet_table(acpi_hpet)
, m_physical_acpi_hpet_registers(find_acpi_hpet_registers_block())
, m_hpet_mmio_region(MM.allocate_kernel_region(m_physical_acpi_hpet_registers.page_base(), PAGE_SIZE, "HPET MMIO"sv, Memory::Region::Access::ReadWrite).release_value())
{
s_hpet = this; // Make available as soon as possible so that IRQs can use it
auto sdt = Memory::map_typed<const volatile ACPI::Structures::HPET>(m_physical_acpi_hpet_table).release_value_but_fixme_should_propagate_errors();
m_vendor_id = sdt->pci_vendor_id;
m_minimum_tick = sdt->mininum_clock_tick;
dmesgln("HPET: Minimum clock tick - {}", m_minimum_tick);
auto& regs = registers();
// 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: {}", (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,
(capable_64_bit ? "64-bit" : "32-bit"),
((!capable_64_bit || (regs.timers[i].capabilities & (u32)HPETFlags::TimerConfiguration::Force32BitMode)) ? "32-bit" : "64-bit"));
}
VERIFY(timers_count >= 2);
global_disable();
m_frequency = NANOSECOND_PERIOD_TO_HERTZ(raw_counter_ticks_to_ns(1));
dmesgln("HPET: frequency {} Hz ({} MHz) resolution: {} ns", m_frequency, HERTZ_TO_MEGAHERTZ(m_frequency), raw_counter_ticks_to_ns(1));
VERIFY(regs.capabilities.main_counter_tick_period <= ABSOLUTE_MAXIMUM_COUNTER_TICK_PERIOD);
// Reset the counter, just in case... (needs to match m_main_counter_last_read)
regs.main_counter_value.high = 0;
regs.main_counter_value.low = 0;
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), is_64bit_capable(0)));
m_comparators.append(HPETComparator::create(1, 8, is_periodic_capable(1), is_64bit_capable(1)));
global_enable();
}
}

View file

@ -0,0 +1,83 @@
/*
* Copyright (c) 2020, Liav A. <liavalb@hotmail.co.il>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/OwnPtr.h>
#include <AK/Types.h>
#include <AK/Vector.h>
#include <Kernel/Library/NonnullLockRefPtrVector.h>
#include <Kernel/Memory/Region.h>
#include <Kernel/PhysicalAddress.h>
namespace Kernel {
class HPETComparator;
struct HPETRegistersBlock;
class HPET {
public:
static bool initialized();
static bool test_and_initialize();
static bool check_for_exisiting_periodic_timers();
static HPET& the();
u64 frequency() const { return m_frequency; }
u64 raw_counter_ticks_to_ns(u64) const;
u64 ns_to_raw_counter_ticks(u64) const;
NonnullLockRefPtrVector<HPETComparator> const& comparators() const { return m_comparators; }
void disable(HPETComparator const&);
void enable(HPETComparator const&);
void update_periodic_comparator_value();
void update_non_periodic_comparator_value(HPETComparator const& comparator);
void set_comparator_irq_vector(u8 comparator_number, u8 irq_vector);
void enable_periodic_interrupt(HPETComparator const& comparator);
void disable_periodic_interrupt(HPETComparator const& comparator);
u64 update_time(u64& seconds_since_boot, u32& ticks_this_second, bool query_only);
u64 read_main_counter_unsafe() const;
u64 read_main_counter() const;
Vector<unsigned> capable_interrupt_numbers(u8 comparator_number);
Vector<unsigned> capable_interrupt_numbers(HPETComparator const&);
private:
HPETRegistersBlock const& registers() const;
HPETRegistersBlock& registers();
void global_disable();
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;
PhysicalAddress find_acpi_hpet_registers_block();
explicit HPET(PhysicalAddress acpi_hpet);
PhysicalAddress m_physical_acpi_hpet_table;
PhysicalAddress m_physical_acpi_hpet_registers;
OwnPtr<Memory::Region> m_hpet_mmio_region;
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 m_main_counter_64bits : 1;
bool legacy_replacement_route_capable : 1;
NonnullLockRefPtrVector<HPETComparator> m_comparators;
};
}

View file

@ -0,0 +1,136 @@
/*
* Copyright (c) 2020, Liav A. <liavalb@hotmail.co.il>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <Kernel/Arch/x86_64/Time/HPETComparator.h>
#include <Kernel/Assertions.h>
#include <Kernel/Debug.h>
#include <Kernel/InterruptDisabler.h>
#include <Kernel/Sections.h>
#include <Kernel/Time/TimeManagement.h>
namespace Kernel {
UNMAP_AFTER_INIT NonnullLockRefPtr<HPETComparator> HPETComparator::create(u8 number, u8 irq, bool periodic_capable, bool is_64bit_capable)
{
auto timer = adopt_lock_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, 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)
{
}
void HPETComparator::disable()
{
if (!m_enabled)
return;
m_enabled = false;
HPET::the().disable(*this);
}
void HPETComparator::set_periodic()
{
VERIFY(m_periodic_capable);
m_periodic = true;
m_enabled = true;
HPET::the().enable_periodic_interrupt(*this);
}
void HPETComparator::set_non_periodic()
{
VERIFY(m_periodic_capable);
m_periodic = false;
m_enabled = true;
HPET::the().disable_periodic_interrupt(*this);
}
bool HPETComparator::handle_irq(RegisterState const& regs)
{
auto result = HardwareTimer::handle_irq(regs);
if (!is_periodic())
set_new_countdown();
return result;
}
void HPETComparator::set_new_countdown()
{
VERIFY_INTERRUPTS_DISABLED();
VERIFY(m_frequency <= HPET::the().frequency());
HPET::the().update_non_periodic_comparator_value(*this);
}
size_t HPETComparator::ticks_per_second() const
{
return m_frequency;
}
void HPETComparator::reset_to_default_ticks_per_second()
{
dbgln("reset_to_default_ticks_per_second");
m_frequency = OPTIMAL_TICKS_PER_SECOND_RATE;
if (!is_periodic())
set_new_countdown();
else
try_to_set_frequency(m_frequency);
}
bool HPETComparator::try_to_set_frequency(size_t frequency)
{
InterruptDisabler disabler;
if (!is_capable_of_frequency(frequency)) {
dbgln("HPETComparator: not capable of frequency: {}", frequency);
return false;
}
auto hpet_frequency = HPET::the().frequency();
VERIFY(frequency <= hpet_frequency);
m_frequency = frequency;
m_enabled = true;
dbgln_if(HPET_COMPARATOR_DEBUG, "HPET Comparator: Max frequency {} Hz, want to set {} Hz, periodic: {}", hpet_frequency, frequency, is_periodic());
if (is_periodic()) {
HPET::the().update_periodic_comparator_value();
} else {
HPET::the().update_non_periodic_comparator_value(*this);
}
HPET::the().enable(*this);
enable_irq(); // Enable if we haven't already
return true;
}
bool HPETComparator::is_capable_of_frequency(size_t frequency) const
{
if (frequency > HPET::the().frequency())
return false;
// HPET::update_periodic_comparator_value and HPET::update_non_periodic_comparator_value
// calculate the best counter based on the desired frequency.
return true;
}
size_t HPETComparator::calculate_nearest_possible_frequency(size_t frequency) const
{
if (frequency > HPET::the().frequency())
return HPET::the().frequency();
// HPET::update_periodic_comparator_value and HPET::update_non_periodic_comparator_value
// calculate the best counter based on the desired frequency.
return frequency;
}
u64 HPETComparator::current_raw() const
{
return HPET::the().read_main_counter();
}
u64 HPETComparator::raw_to_ns(u64 raw_delta) const
{
return HPET::the().raw_counter_ticks_to_ns(raw_delta);
}
}

View file

@ -0,0 +1,54 @@
/*
* Copyright (c) 2020, Liav A. <liavalb@hotmail.co.il>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/Function.h>
#include <AK/Types.h>
#include <Kernel/Arch/x86_64/Time/HPET.h>
#include <Kernel/Time/HardwareTimer.h>
namespace Kernel {
class HPETComparator final : public HardwareTimer<IRQHandler> {
friend class HPET;
public:
static NonnullLockRefPtr<HPETComparator> create(u8 number, u8 irq, bool periodic_capable, bool is_64bit_capable);
virtual HardwareTimerType timer_type() const override { return HardwareTimerType::HighPrecisionEventTimer; }
virtual StringView model() const override { return "HPET"sv; }
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;
virtual bool is_periodic() const override { return m_periodic; }
virtual bool is_periodic_capable() const override { return m_periodic_capable; }
virtual void set_periodic() override;
virtual void set_non_periodic() override;
virtual void disable() override;
virtual bool can_query_raw() const override { return true; }
virtual u64 current_raw() const override;
virtual u64 raw_to_ns(u64) const override;
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;
private:
void set_new_countdown();
virtual bool handle_irq(RegisterState const&) override;
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 };
};
}

View file

@ -0,0 +1,89 @@
/*
* Copyright (c) 2020, Liav A. <liavalb@hotmail.co.il>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <Kernel/Arch/x86_64/IO.h>
#include <Kernel/Arch/x86_64/Time/PIT.h>
#include <Kernel/InterruptDisabler.h>
#include <Kernel/Interrupts/GenericInterruptHandler.h>
#include <Kernel/Scheduler.h>
#include <Kernel/Sections.h>
#include <Kernel/Thread.h>
#include <Kernel/Time/TimeManagement.h>
#define IRQ_TIMER 0
namespace Kernel {
UNMAP_AFTER_INIT NonnullLockRefPtr<PIT> PIT::initialize(Function<void(RegisterState const&)> callback)
{
return adopt_lock_ref(*new PIT(move(callback)));
}
[[maybe_unused]] inline static void reset_countdown(u16 timer_reload)
{
IO::out8(PIT_CTL, TIMER0_SELECT | WRITE_WORD | MODE_COUNTDOWN);
IO::out8(TIMER0_CTL, LSB(timer_reload));
IO::out8(TIMER0_CTL, MSB(timer_reload));
}
PIT::PIT(Function<void(RegisterState const&)> callback)
: HardwareTimer(IRQ_TIMER, move(callback))
, m_periodic(true)
{
IO::out8(PIT_CTL, TIMER0_SELECT | WRITE_WORD | MODE_SQUARE_WAVE);
dmesgln("PIT: {} Hz, square wave ({:#08x})", OPTIMAL_TICKS_PER_SECOND_RATE, BASE_FREQUENCY / OPTIMAL_TICKS_PER_SECOND_RATE);
reset_to_default_ticks_per_second();
enable_irq();
}
size_t PIT::ticks_per_second() const
{
return m_frequency;
}
void PIT::set_periodic()
{
IO::out8(PIT_CTL, TIMER0_CTL | WRITE_WORD | MODE_SQUARE_WAVE);
m_periodic = true;
}
void PIT::set_non_periodic()
{
IO::out8(PIT_CTL, TIMER0_CTL | WRITE_WORD | MODE_ONESHOT);
m_periodic = false;
}
void PIT::reset_to_default_ticks_per_second()
{
InterruptDisabler disabler;
bool success = try_to_set_frequency(OPTIMAL_TICKS_PER_SECOND_RATE);
VERIFY(success);
}
bool PIT::try_to_set_frequency(size_t frequency)
{
InterruptDisabler disabler;
if (!is_capable_of_frequency(frequency))
return false;
disable_irq();
size_t reload_value = BASE_FREQUENCY / frequency;
IO::out8(TIMER0_CTL, LSB(reload_value));
IO::out8(TIMER0_CTL, MSB(reload_value));
m_frequency = frequency;
enable_irq();
return true;
}
bool PIT::is_capable_of_frequency(size_t frequency) const
{
VERIFY(frequency != 0);
return frequency <= BASE_FREQUENCY;
}
size_t PIT::calculate_nearest_possible_frequency(size_t frequency) const
{
VERIFY(frequency != 0);
return frequency;
}
}

View file

@ -0,0 +1,57 @@
/*
* Copyright (c) 2020, Liav A. <liavalb@hotmail.co.il>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/Types.h>
#include <Kernel/Library/NonnullLockRefPtr.h>
#include <Kernel/Time/HardwareTimer.h>
namespace Kernel {
/* Timer related ports */
#define TIMER0_CTL 0x40
#define TIMER1_CTL 0x41
#define TIMER2_CTL 0x42
#define PIT_CTL 0x43
/* Building blocks for PIT_CTL */
#define TIMER0_SELECT 0x00
#define TIMER1_SELECT 0x40
#define TIMER2_SELECT 0x80
#define MODE_COUNTDOWN 0x00
#define MODE_ONESHOT 0x02
#define MODE_RATE 0x04
#define MODE_SQUARE_WAVE 0x06
#define WRITE_WORD 0x30
#define BASE_FREQUENCY 1193182
class PIT final : public HardwareTimer<IRQHandler> {
public:
static NonnullLockRefPtr<PIT> initialize(Function<void(RegisterState const&)>);
virtual HardwareTimerType timer_type() const override { return HardwareTimerType::i8253; }
virtual StringView model() const override { return "i8254"sv; }
virtual size_t ticks_per_second() const override;
virtual bool is_periodic() const override { return m_periodic; }
virtual bool is_periodic_capable() const override { return true; }
virtual void set_periodic() override;
virtual void set_non_periodic() override;
virtual void disable() override { }
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;
private:
explicit PIT(Function<void(RegisterState const&)>);
bool m_periodic { true };
};
}

View file

@ -0,0 +1,91 @@
/*
* Copyright (c) 2020, Liav A. <liavalb@hotmail.co.il>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <Kernel/Arch/x86_64/CMOS.h>
#include <Kernel/Arch/x86_64/IO.h>
#include <Kernel/Arch/x86_64/NonMaskableInterruptDisabler.h>
#include <Kernel/Arch/x86_64/Time/RTC.h>
#include <Kernel/InterruptDisabler.h>
#include <Kernel/Time/TimeManagement.h>
namespace Kernel {
#define IRQ_TIMER 8
#define MAX_FREQUENCY 8000
NonnullLockRefPtr<RealTimeClock> RealTimeClock::create(Function<void(RegisterState const&)> callback)
{
return adopt_lock_ref(*new RealTimeClock(move(callback)));
}
RealTimeClock::RealTimeClock(Function<void(RegisterState const&)> callback)
: HardwareTimer(IRQ_TIMER, move(callback))
{
InterruptDisabler disabler;
NonMaskableInterruptDisabler nmi_disabler;
enable_irq();
CMOS::write(0x8B, CMOS::read(0xB) | 0x40);
reset_to_default_ticks_per_second();
}
bool RealTimeClock::handle_irq(RegisterState const& regs)
{
auto result = HardwareTimer::handle_irq(regs);
CMOS::read(0x8C);
return result;
}
size_t RealTimeClock::ticks_per_second() const
{
return m_frequency;
}
void RealTimeClock::reset_to_default_ticks_per_second()
{
InterruptDisabler disabler;
bool success = try_to_set_frequency(1024);
VERIFY(success);
}
// FIXME: This is a quick & dirty log base 2 with a parameter. Please provide something better in the future.
static int quick_log2(size_t number)
{
int count = 0;
while (number >>= 1)
count++;
return count;
}
bool RealTimeClock::try_to_set_frequency(size_t frequency)
{
InterruptDisabler disabler;
if (!is_capable_of_frequency(frequency))
return false;
disable_irq();
u8 previous_rate = CMOS::read(0x8A);
u8 rate = quick_log2(32768 / frequency) + 1;
dbgln("RTC: Set rate to {}", rate);
CMOS::write(0x8A, (previous_rate & 0xF0) | rate);
m_frequency = frequency;
dbgln("RTC: Set frequency to {} Hz", frequency);
enable_irq();
return true;
}
bool RealTimeClock::is_capable_of_frequency(size_t frequency) const
{
VERIFY(frequency != 0);
if (frequency > MAX_FREQUENCY)
return false;
if (32768 % frequency)
return false;
u16 divider = 32768 / frequency;
return (divider <= 16384 && divider >= 4); // Frequency can be in range of 2 Hz to 8 KHz
}
size_t RealTimeClock::calculate_nearest_possible_frequency(size_t frequency) const
{
VERIFY(frequency != 0);
return frequency;
}
}

View file

@ -0,0 +1,36 @@
/*
* Copyright (c) 2020, Liav A. <liavalb@hotmail.co.il>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <Kernel/Arch/x86_64/RTC.h>
#include <Kernel/Library/NonnullLockRefPtr.h>
#include <Kernel/Time/HardwareTimer.h>
namespace Kernel {
class RealTimeClock final : public HardwareTimer<IRQHandler> {
public:
static NonnullLockRefPtr<RealTimeClock> create(Function<void(RegisterState const&)> callback);
virtual HardwareTimerType timer_type() const override { return HardwareTimerType::RTC; }
virtual StringView model() const override { return "Real Time Clock"sv; }
virtual size_t ticks_per_second() const override;
virtual bool is_periodic() const override { return true; }
virtual bool is_periodic_capable() const override { return true; }
virtual void set_periodic() override { }
virtual void set_non_periodic() override { }
virtual void disable() override { }
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;
private:
explicit RealTimeClock(Function<void(RegisterState const&)> callback);
virtual bool handle_irq(RegisterState const&) override;
};
}