diff --git a/Kernel/Devices/PCSpeaker.cpp b/Kernel/Devices/PCSpeaker.cpp index 05a8e0ad8277c5..b14bf7f8d28610 100644 --- a/Kernel/Devices/PCSpeaker.cpp +++ b/Kernel/Devices/PCSpeaker.cpp @@ -26,7 +26,7 @@ #include #include -#include +#include #include void PCSpeaker::tone_on(int frequency) diff --git a/Kernel/Makefile b/Kernel/Makefile index b06c9f9bbf0b6b..22e10de020fc03 100644 --- a/Kernel/Makefile +++ b/Kernel/Makefile @@ -24,7 +24,12 @@ OBJS = \ Interrupts/IRQHandler.o \ Interrupts/SharedIRQHandler.o \ CMOS.o \ - Devices/PIT.o \ + Time/PIT.o \ + Time/TimeManagement.o \ + Time/HardwareTimer.o \ + Time/RTC.o \ + Time/HPET.o \ + Time/HPETComparator.o \ Devices/BXVGADevice.o \ Devices/BlockDevice.o \ Devices/CharacterDevice.o \ @@ -48,7 +53,6 @@ OBJS = \ Devices/SerialDevice.o \ Devices/ZeroDevice.o \ Devices/VMWareBackdoor.o \ - Devices/HardwareTimer.o \ DoubleBuffer.o \ FileSystem/Custody.o \ FileSystem/DevPtsFS.o \ diff --git a/Kernel/Process.cpp b/Kernel/Process.cpp index 9219a955e379c7..27df56452e6914 100644 --- a/Kernel/Process.cpp +++ b/Kernel/Process.cpp @@ -37,7 +37,6 @@ #include #include #include -#include #include #include #include @@ -67,6 +66,7 @@ #include #include #include +#include #include #include #include @@ -2105,13 +2105,13 @@ unsigned Process::sys$alarm(unsigned seconds) REQUIRE_PROMISE(stdio); unsigned previous_alarm_remaining = 0; if (m_alarm_deadline && m_alarm_deadline > g_uptime) { - previous_alarm_remaining = (m_alarm_deadline - g_uptime) / TICKS_PER_SECOND; + previous_alarm_remaining = (m_alarm_deadline - g_uptime) / TimeManagement::the().ticks_per_second(); } if (!seconds) { m_alarm_deadline = 0; return previous_alarm_remaining; } - m_alarm_deadline = g_uptime + seconds * TICKS_PER_SECOND; + m_alarm_deadline = g_uptime + seconds * TimeManagement::the().ticks_per_second(); return previous_alarm_remaining; } @@ -2231,10 +2231,10 @@ int Process::sys$sleep(unsigned seconds) REQUIRE_PROMISE(stdio); if (!seconds) return 0; - u64 wakeup_time = Thread::current->sleep(seconds * TICKS_PER_SECOND); + u64 wakeup_time = Thread::current->sleep(seconds * TimeManagement::the().ticks_per_second()); if (wakeup_time > g_uptime) { u32 ticks_left_until_original_wakeup_time = wakeup_time - g_uptime; - return ticks_left_until_original_wakeup_time / TICKS_PER_SECOND; + return ticks_left_until_original_wakeup_time / TimeManagement::the().ticks_per_second(); } return 0; } @@ -4268,8 +4268,12 @@ int Process::sys$clock_gettime(clockid_t clock_id, timespec* user_ts) switch (clock_id) { case CLOCK_MONOTONIC: - ts.tv_sec = g_uptime / TICKS_PER_SECOND; - ts.tv_nsec = (g_uptime % TICKS_PER_SECOND) * 1000000; + ts.tv_sec = TimeManagement::the().seconds_since_boot(); + ts.tv_nsec = TimeManagement::the().ticks_this_second() * 1000000; + break; + case CLOCK_REALTIME: + ts.tv_sec = TimeManagement::the().epoch_time(); + ts.tv_nsec = TimeManagement::the().ticks_this_second() * 1000000; break; default: return -EINVAL; @@ -4322,8 +4326,8 @@ int Process::sys$clock_nanosleep(const Syscall::SC_clock_nanosleep_params* user_ timespec remaining_sleep; memset(&remaining_sleep, 0, sizeof(timespec)); - remaining_sleep.tv_sec = ticks_left / TICKS_PER_SECOND; - ticks_left -= remaining_sleep.tv_sec * TICKS_PER_SECOND; + remaining_sleep.tv_sec = ticks_left / TimeManagement::the().ticks_per_second(); + ticks_left -= remaining_sleep.tv_sec * TimeManagement::the().ticks_per_second(); remaining_sleep.tv_nsec = ticks_left * 1000000; copy_to_user(params.remaining_sleep, &remaining_sleep); } diff --git a/Kernel/Scheduler.cpp b/Kernel/Scheduler.cpp index 7897fa82ae2b48..83917f5faf4351 100644 --- a/Kernel/Scheduler.cpp +++ b/Kernel/Scheduler.cpp @@ -26,13 +26,13 @@ #include #include -#include #include #include #include #include #include #include +#include #include //#define LOG_EVERY_CONTEXT_SWITCH @@ -588,8 +588,8 @@ void Scheduler::timer_tick(const RegisterState& regs) ++g_uptime; timeval tv; - tv.tv_sec = RTC::boot_time() + PIT::the().seconds_since_boot(); - tv.tv_usec = PIT::the().ticks_this_second() * 1000; + tv.tv_sec = TimeManagement::the().epoch_time(); + tv.tv_usec = TimeManagement::the().ticks_this_second() * 1000; Process::update_info_page_timestamp(tv); if (Process::current->is_profiling()) { diff --git a/Kernel/Time/HPET.cpp b/Kernel/Time/HPET.cpp new file mode 100644 index 00000000000000..ffac283411714e --- /dev/null +++ b/Kernel/Time/HPET.cpp @@ -0,0 +1,362 @@ +/* + * Copyright (c) 2020, Liav A. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include + +namespace Kernel { + +#define ABSOLUTE_MAXIMUM_COUNTER_TICK_PERIOD 0x05F5E100 +#define NANOSECOND_PERIOD_TO_HERTZ(x) 1000000000 / x +#define MEGAHERTZ_TO_HERTZ(x) (x / 1000000) + +//#define HPET_DEBUG + +namespace HPETFlags { +enum class Attributes { + Counter64BitCapable = 1 << 13, + LegacyReplacementRouteCapable = 1 << 15 +}; + +enum class Configuration { + Enable = 0x1, + LegacyReplacementRoute = 0x2 +}; + +enum class TimerConfiguration : u32 { + InterruptType = 1 << 1, + InterruptEnable = 1 << 2, + TimerType = 1 << 3, + PeriodicInterruptCapable = 1 << 4, + Timer64BitsCapable = 1 << 5, + ValueSet = 1 << 6, + Force32BitMode = 1 << 7, + FSBInterruptEnable = 1 << 14, + FSBInterruptDelivery = 1 << 15 +}; +}; + +struct [[gnu::packed]] TimerStructure +{ + u64 configuration_and_capability; + u64 comparator_value; + u64 fsb_interrupt_route; + u64 reserved; +}; + +struct [[gnu::packed]] HPETCapabilityRegister +{ + u8 revision_id; + u8 attributes; + u16 vendor_id; + u32 main_counter_tick_period; + u64 reserved; +}; + +struct [[gnu::packed]] HPETRegister +{ + u64 reg; + u64 reserved; +}; + +struct [[gnu::packed]] HPETRegistersBlock +{ + union { + volatile HPETCapabilityRegister capabilities; // Note: We must do a 32 bit access to offsets 0x0, or 0x4 only, according to HPET spec. + volatile HPETRegister raw_capabilites; + }; + HPETRegister configuration; + HPETRegister interrupt_status; + u8 reserved[0xF0 - 48]; + HPETRegister main_counter_value; + TimerStructure timers[3]; + u8 reserved2[0x400 - 0x160]; +}; + +static HPET* s_hpet; +static bool hpet_initialized { false }; + +bool HPET::initialized() +{ + return hpet_initialized; +} + +HPET& HPET::the() +{ + ASSERT(HPET::initialized()); + ASSERT(s_hpet != nullptr); + return *s_hpet; +} + +bool HPET::test_and_initialize() +{ + ASSERT(!HPET::initialized()); + hpet_initialized = true; + auto hpet = ACPI::Parser::the().find_table("HPET"); + if (hpet.is_null()) + return false; + klog() << "HPET @ " << hpet; + + auto region = MM.allocate_kernel_region(hpet.page_base(), (PAGE_SIZE * 2), "HPET Initialization", Region::Access::Read); + auto* sdt = (volatile ACPI::Structures::HPET*)region->vaddr().offset(hpet.offset_in_page()).as_ptr(); + + // Note: HPET is only usable from System Memory + ASSERT(sdt->event_timer_block.address_space == (u8)ACPI::GenericAddressStructure::AddressSpace::SystemMemory); + + if (TimeManagement::is_hpet_periodic_mode_allowed()) { + if (!check_for_exisiting_periodic_timers()) { + dbg() << "HPET: No periodic capable timers"; + return false; + } + } + s_hpet = new HPET(PhysicalAddress(hpet)); + return true; +} + +bool HPET::check_for_exisiting_periodic_timers() +{ + auto hpet = ACPI::Parser::the().find_table("HPET"); + if (hpet.is_null()) + return false; + auto region = MM.allocate_kernel_region(hpet.page_base(), (PAGE_SIZE * 2), "HPET Initialization", Region::Access::Read); + auto* sdt = (volatile ACPI::Structures::HPET*)region->vaddr().offset(hpet.offset_in_page()).as_ptr(); + + ASSERT(sdt->event_timer_block.address_space == 0); + + auto p_block = PhysicalAddress(sdt->event_timer_block.address); + auto block_region = MM.allocate_kernel_region(p_block, (PAGE_SIZE * 2), "HPET Initialization", Region::Access::Read); + auto* registers = (volatile HPETRegistersBlock*)block_region->vaddr().offset(p_block.offset_in_page()).as_ptr(); + + auto* capabilities_register = (volatile HPETCapabilityRegister*)®isters->capabilities; + size_t timers_count = (capabilities_register->attributes & 0x1f) + 1; + for (size_t index = 0; index < timers_count; index++) { + if (registers->timers[index].configuration_and_capability & (u32)HPETFlags::TimerConfiguration::PeriodicInterruptCapable) + return true; + } + return false; +} + +const FixedArray>& HPET::comparators() const +{ + return m_comparators; +} + +void HPET::global_disable() +{ + auto* registers_block = (volatile HPETRegistersBlock*)m_hpet_mmio_region->vaddr().offset(m_physical_acpi_hpet_registers.offset_in_page()).as_ptr(); + registers_block->configuration.reg &= ~(u32)HPETFlags::Configuration::Enable; +} +void HPET::global_enable() +{ + auto* registers_block = (volatile HPETRegistersBlock*)m_hpet_mmio_region->vaddr().offset(m_physical_acpi_hpet_registers.offset_in_page()).as_ptr(); + registers_block->configuration.reg |= (u32)HPETFlags::Configuration::Enable; +} + +void HPET::set_periodic_comparator_value(const HPETComparator& comparator, u64 value) +{ + disable(comparator); + ASSERT(comparator.is_periodic()); + ASSERT(comparator.comparator_number() <= m_comparators.size()); + auto* registers_block = (volatile HPETRegistersBlock*)m_hpet_mmio_region->vaddr().offset(m_physical_acpi_hpet_registers.offset_in_page()).as_ptr(); + registers_block->timers[comparator.comparator_number()].configuration_and_capability |= (u32)HPETFlags::TimerConfiguration::ValueSet; + registers_block->timers[comparator.comparator_number()].comparator_value = value; + enable(comparator); +} + +void HPET::set_non_periodic_comparator_value(const HPETComparator& comparator, u64 value) +{ + ASSERT_INTERRUPTS_DISABLED(); + ASSERT(!comparator.is_periodic()); + ASSERT(comparator.comparator_number() <= m_comparators.size()); + auto* registers_block = (volatile HPETRegistersBlock*)m_hpet_mmio_region->vaddr().offset(m_physical_acpi_hpet_registers.offset_in_page()).as_ptr(); + registers_block->timers[comparator.comparator_number()].comparator_value = main_counter_value() + value; +} +void HPET::enable_periodic_interrupt(const HPETComparator& comparator) +{ +#ifdef HPET_DEBUG + klog() << "HPET: Set comparator " << comparator.comparator_number() << " to be periodic."; +#endif + disable(comparator); + ASSERT(comparator.comparator_number() <= m_comparators.size()); + auto* registers_block = (volatile HPETRegistersBlock*)m_hpet_mmio_region->vaddr().offset(m_physical_acpi_hpet_registers.offset_in_page()).as_ptr(); + ASSERT(registers_block->timers[comparator.comparator_number()].configuration_and_capability & (u32)HPETFlags::TimerConfiguration::PeriodicInterruptCapable); + registers_block->timers[comparator.comparator_number()].configuration_and_capability |= (u32)HPETFlags::TimerConfiguration::TimerType; + enable(comparator); +} +void HPET::disable_periodic_interrupt(const HPETComparator& comparator) +{ +#ifdef HPET_DEBUG + klog() << "HPET: Disable periodic interrupt in comparator " << comparator.comparator_number() << "."; +#endif + disable(comparator); + ASSERT(comparator.comparator_number() <= m_comparators.size()); + auto* registers_block = (volatile HPETRegistersBlock*)m_hpet_mmio_region->vaddr().offset(m_physical_acpi_hpet_registers.offset_in_page()).as_ptr(); + ASSERT(registers_block->timers[comparator.comparator_number()].configuration_and_capability & (u32)HPETFlags::TimerConfiguration::PeriodicInterruptCapable); + registers_block->timers[comparator.comparator_number()].configuration_and_capability &= ~(u32)HPETFlags::TimerConfiguration::TimerType; + enable(comparator); +} + +void HPET::disable(const HPETComparator& comparator) +{ +#ifdef HPET_DEBUG + klog() << "HPET: Disable comparator " << comparator.comparator_number() << "."; +#endif + ASSERT(comparator.comparator_number() <= m_comparators.size()); + auto* registers_block = (volatile HPETRegistersBlock*)m_hpet_mmio_region->vaddr().offset(m_physical_acpi_hpet_registers.offset_in_page()).as_ptr(); + registers_block->timers[comparator.comparator_number()].configuration_and_capability &= ~(u32)HPETFlags::TimerConfiguration::InterruptEnable; +} +void HPET::enable(const HPETComparator& comparator) +{ +#ifdef HPET_DEBUG + klog() << "HPET: Enable comparator " << comparator.comparator_number() << "."; +#endif + ASSERT(comparator.comparator_number() <= m_comparators.size()); + auto* registers_block = (volatile HPETRegistersBlock*)m_hpet_mmio_region->vaddr().offset(m_physical_acpi_hpet_registers.offset_in_page()).as_ptr(); + registers_block->timers[comparator.comparator_number()].configuration_and_capability |= (u32)HPETFlags::TimerConfiguration::InterruptEnable; +} + +u64 HPET::main_counter_value() const +{ + auto* registers_block = (const volatile HPETRegistersBlock*)m_hpet_mmio_region->vaddr().offset(m_physical_acpi_hpet_registers.offset_in_page()).as_ptr(); + return registers_block->main_counter_value.reg; +} +u64 HPET::frequency() const +{ + return m_frequency; +} + +Vector HPET::capable_interrupt_numbers(const HPETComparator& comparator) +{ + ASSERT(comparator.comparator_number() <= m_comparators.size()); + Vector capable_interrupts; + auto* registers_block = (volatile HPETRegistersBlock*)m_hpet_mmio_region->vaddr().offset(m_physical_acpi_hpet_registers.offset_in_page()).as_ptr(); + auto* comparator_registers = (const volatile TimerStructure*)®isters_block->timers[comparator.comparator_number()]; + u32 interrupt_bitfield = comparator_registers->configuration_and_capability >> 32; + for (size_t index = 0; index < 32; index++) { + if (interrupt_bitfield & 1) + capable_interrupts.append(index); + interrupt_bitfield >>= 1; + } + return capable_interrupts; +} + +Vector HPET::capable_interrupt_numbers(u8 comparator_number) +{ + ASSERT(comparator_number <= m_comparators.size()); + Vector capable_interrupts; + auto* registers_block = (volatile HPETRegistersBlock*)m_hpet_mmio_region->vaddr().offset(m_physical_acpi_hpet_registers.offset_in_page()).as_ptr(); + auto* comparator_registers = (const volatile TimerStructure*)®isters_block->timers[comparator_number]; + u32 interrupt_bitfield = comparator_registers->configuration_and_capability >> 32; + 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) +{ + ASSERT(comparator_number <= m_comparators.size()); + auto* registers_block = (volatile HPETRegistersBlock*)m_hpet_mmio_region->vaddr().offset(m_physical_acpi_hpet_registers.offset_in_page()).as_ptr(); + auto* comparator_registers = (volatile TimerStructure*)®isters_block->timers[comparator_number]; + comparator_registers->configuration_and_capability |= (irq_vector << 9); +} + +bool HPET::is_periodic_capable(u8 comparator_number) const +{ + ASSERT(comparator_number <= m_comparators.size()); + auto* registers_block = (volatile HPETRegistersBlock*)m_hpet_mmio_region->vaddr().offset(m_physical_acpi_hpet_registers.offset_in_page()).as_ptr(); + auto* comparator_registers = (const volatile TimerStructure*)®isters_block->timers[comparator_number]; + return comparator_registers->configuration_and_capability & (u32)HPETFlags::TimerConfiguration::PeriodicInterruptCapable; +} + +void HPET::set_comparators_to_optimal_interrupt_state(size_t) +{ + // FIXME: Implement this method for allowing to use HPET timers 2-31... + ASSERT_NOT_REACHED(); +} + +PhysicalAddress HPET::find_acpi_hept_registers_block() +{ + auto region = MM.allocate_kernel_region(m_physical_acpi_hpet_table.page_base(), (PAGE_SIZE * 2), "HPET Initialization", Region::Access::Read); + auto* sdt = (ACPI::Structures::HPET*)region->vaddr().offset(m_physical_acpi_hpet_table.offset_in_page()).as_ptr(); + ASSERT(sdt->event_timer_block.address_space == (u8)ACPI::GenericAddressStructure::AddressSpace::SystemMemory); + return PhysicalAddress(sdt->event_timer_block.address); +} +u64 HPET::calculate_ticks_in_nanoseconds() const +{ + auto* registers_block = (const volatile HPETRegistersBlock*)m_hpet_mmio_region->vaddr().offset(m_physical_acpi_hpet_registers.offset_in_page()).as_ptr(); + return ABSOLUTE_MAXIMUM_COUNTER_TICK_PERIOD / registers_block->capabilities.main_counter_tick_period; +} +HPET::HPET(PhysicalAddress acpi_hpet) + : m_physical_acpi_hpet_table(acpi_hpet) + , m_physical_acpi_hpet_registers(find_acpi_hept_registers_block()) + , m_hpet_mmio_region(MM.allocate_kernel_region(m_physical_acpi_hpet_registers.page_base(), PAGE_SIZE, "HPET MMIO", Region::Access::Read | Region::Access::Write)) +{ + auto region = MM.allocate_kernel_region(m_physical_acpi_hpet_table.page_base(), (PAGE_SIZE * 2), "HPET Initialization", Region::Access::Read); + auto* sdt = (volatile ACPI::Structures::HPET*)region->vaddr().offset(m_physical_acpi_hpet_table.offset_in_page()).as_ptr(); + m_vendor_id = sdt->pci_vendor_id; + m_minimum_tick = sdt->mininum_clock_tick; + klog() << "HPET: Minimum clock tick - " << m_minimum_tick; + + auto* registers_block = (volatile HPETRegistersBlock*)m_hpet_mmio_region->vaddr().offset(m_physical_acpi_hpet_registers.offset_in_page()).as_ptr(); + + // Note: We must do a 32 bit access to offsets 0x0, or 0x4 only. + size_t timers_count = ((registers_block->raw_capabilites.reg >> 8) & 0x1f) + 1; + klog() << "HPET: Timers count - " << timers_count; + ASSERT(timers_count >= 2); + m_comparators.resize(timers_count); + auto* capabilities_register = (const volatile HPETCapabilityRegister*)®isters_block->raw_capabilites.reg; + + global_disable(); + + m_frequency = NANOSECOND_PERIOD_TO_HERTZ(calculate_ticks_in_nanoseconds()); + klog() << "HPET: frequency " << m_frequency << " Hz (" << MEGAHERTZ_TO_HERTZ(m_frequency) << " MHz)"; + ASSERT(capabilities_register->main_counter_tick_period <= ABSOLUTE_MAXIMUM_COUNTER_TICK_PERIOD); + + // Reset the counter, just in case... + registers_block->main_counter_value.reg = 0; + if (registers_block->raw_capabilites.reg & (u32)HPETFlags::Attributes::LegacyReplacementRouteCapable) + registers_block->configuration.reg |= (u32)HPETFlags::Configuration::LegacyReplacementRoute; + + for (size_t index = 0; index < m_comparators.size(); index++) { + bool periodic = is_periodic_capable(index); + if (index == 0) { + m_comparators[index] = HPETComparator::create(index, 0, periodic); + } + if (index == 1) { + m_comparators[index] = HPETComparator::create(index, 8, periodic); + } + } + + global_enable(); +} +} diff --git a/Kernel/Time/HPET.h b/Kernel/Time/HPET.h new file mode 100644 index 00000000000000..87b98ce6c55a19 --- /dev/null +++ b/Kernel/Time/HPET.h @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2020, Liav A. + * 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. + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +namespace Kernel { + +class HPETComparator; +class HPET { +public: + static bool initialized(); + static bool test_and_initialize(); + static bool check_for_exisiting_periodic_timers(); + static HPET& the(); + + u64 main_counter_value() const; + u64 frequency() const; + + const FixedArray>& comparators() const; + void disable(const HPETComparator&); + void enable(const HPETComparator&); + + void set_periodic_comparator_value(const HPETComparator& comparator, u64 value); + void set_non_periodic_comparator_value(const HPETComparator& comparator, u64 value); + + void set_comparator_irq_vector(u8 comparator_number, u8 irq_vector); + + void enable_periodic_interrupt(const HPETComparator& comparator); + void disable_periodic_interrupt(const HPETComparator& comparator); + + Vector capable_interrupt_numbers(u8 comparator_number); + Vector capable_interrupt_numbers(const HPETComparator&); + +private: + void global_disable(); + void global_enable(); + + bool is_periodic_capable(u8 comparator_number) const; + void set_comparators_to_optimal_interrupt_state(size_t timers_count); + + u64 calculate_ticks_in_nanoseconds() const; + + PhysicalAddress find_acpi_hept_registers_block(); + explicit HPET(PhysicalAddress acpi_hpet); + PhysicalAddress m_physical_acpi_hpet_table; + PhysicalAddress m_physical_acpi_hpet_registers; + OwnPtr m_hpet_mmio_region; + + u64 m_main_counter_clock_period { 0 }; + u16 m_vendor_id; + u16 m_minimum_tick; + u64 m_frequency; + u8 m_revision_id; + bool counter_is_64_bit_capable : 1; + bool legacy_replacement_route_capable : 1; + + FixedArray> m_comparators; +}; +} diff --git a/Kernel/Time/HPETComparator.cpp b/Kernel/Time/HPETComparator.cpp new file mode 100644 index 00000000000000..f70f3a96a4e36e --- /dev/null +++ b/Kernel/Time/HPETComparator.cpp @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2020, Liav A. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +//#define HPET_COMPARATOR_DEBUG + +namespace Kernel { + +NonnullRefPtr HPETComparator::create(u8 number, u8 irq, bool periodic_capable) +{ + return adopt(*new HPETComparator(number, irq, periodic_capable, [](const RegisterState& regs) { TimeManagement::stale_function(regs); })); +} + +HPETComparator::HPETComparator(u8 number, u8 irq, bool periodic_capable, Function callback) + : HardwareTimer(irq, move(callback)) + , m_periodic(false) + , m_periodic_capable(periodic_capable) + , m_comparator_number(number) +{ +} + +void HPETComparator::set_periodic() +{ + ASSERT(m_periodic_capable); + HPET::the().enable_periodic_interrupt(*this); + m_periodic = true; +} +void HPETComparator::set_non_periodic() +{ + ASSERT(m_periodic_capable); + HPET::the().disable_periodic_interrupt(*this); + m_periodic = false; +} + +void HPETComparator::handle_irq(const RegisterState& regs) +{ + HardwareTimer::handle_irq(regs); + if (!is_periodic()) + set_new_countdown(); +} + +void HPETComparator::set_new_countdown() +{ + ASSERT_INTERRUPTS_DISABLED(); + ASSERT(m_frequency <= HPET::the().frequency()); + HPET::the().set_non_periodic_comparator_value(*this, HPET::the().frequency() / m_frequency); +} + +size_t HPETComparator::ticks_per_second() const +{ + return m_frequency; +} + +void HPETComparator::reset_to_default_ticks_per_second() +{ + ASSERT(is_capable_of_frequency(OPTIMAL_TICKS_PER_SECOND_RATE)); + 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)) + return false; + disable_irq(); + auto hpet_frequency = HPET::the().frequency(); + ASSERT(frequency <= hpet_frequency); +#ifdef HPET_COMPARATOR_DEBUG + dbg() << "HPET Comparator: Max frequency - " << hpet_frequency << " Hz, want to set " << frequency << " Hz"; +#endif + if (is_periodic()) + HPET::the().set_periodic_comparator_value(*this, hpet_frequency / frequency); + else { + HPET::the().set_non_periodic_comparator_value(*this, hpet_frequency / frequency); + HPET::the().enable(*this); + } + m_frequency = frequency; + enable_irq(); + return true; +} +bool HPETComparator::is_capable_of_frequency(size_t frequency) const +{ + if (frequency > HPET::the().frequency()) + return false; + if ((HPET::the().frequency() % frequency) != 0) + return false; + return true; +} +size_t HPETComparator::calculate_nearest_possible_frequency(size_t frequency) const +{ + if (frequency >= HPET::the().frequency()) + return HPET::the().frequency(); + // FIXME: Use better math here + return (frequency + (HPET::the().frequency() % frequency)); +} + +} diff --git a/Kernel/Time/HPETComparator.h b/Kernel/Time/HPETComparator.h new file mode 100644 index 00000000000000..d6b17fce661e07 --- /dev/null +++ b/Kernel/Time/HPETComparator.h @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2020, Liav A. + * 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. + */ + +#pragma once + +#include +#include +#include +#include + +namespace Kernel { +class HPETComparator final : public HardwareTimer { + friend class HPET; + +public: + static NonnullRefPtr create(u8 number, u8 irq, bool periodic_capable); + + virtual HardwareTimerType timer_type() const override { return HardwareTimerType::HighPrecisionEventTimer; } + virtual const char* model() const override { return "HPET"; } + + u8 comparator_number() const { return m_comparator_number; } + + 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 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 void handle_irq(const RegisterState&) override; + HPETComparator(u8 number, u8 irq, bool periodic_capable, Function callback); + bool m_periodic : 1; + bool m_periodic_capable : 1; + bool m_edge_triggered : 1; + u8 m_comparator_number { 0 }; +}; +} diff --git a/Kernel/Time/HardwareTimer.cpp b/Kernel/Time/HardwareTimer.cpp new file mode 100644 index 00000000000000..654f4bcf06778f --- /dev/null +++ b/Kernel/Time/HardwareTimer.cpp @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2020, Liav A. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +namespace Kernel { + +HardwareTimer::HardwareTimer(u8 irq_number, Function callback) + : IRQHandler(irq_number) + , m_function_to_call(move(callback)) +{ +} +void HardwareTimer::handle_irq(const RegisterState& regs) +{ + m_function_to_call(regs); +} +const char* HardwareTimer::purpose() const +{ + if (TimeManagement::the().is_system_timer(*this)) + return "System Timer"; + return model(); +} +void HardwareTimer::change_function(Function callback) +{ + disable_irq(); + m_function_to_call = move(callback); + enable_irq(); +} + +} diff --git a/Kernel/Time/HardwareTimer.h b/Kernel/Time/HardwareTimer.h new file mode 100644 index 00000000000000..3d8493afb64874 --- /dev/null +++ b/Kernel/Time/HardwareTimer.h @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2020, Liav A. + * 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. + */ + +#pragma once + +#include +#include +#include +#include +#include + +namespace Kernel { + +enum class HardwareTimerType { + i8253 = 0x1, /* PIT */ + RTC = 0x2, /* Real Time Clock */ + HighPrecisionEventTimer = 0x3 /* also known as IA-PC HPET */ +}; + +class HardwareTimer : public RefCounted + , public IRQHandler { +public: + virtual HardwareTimerType timer_type() const = 0; + virtual const char* model() const = 0; + virtual const char* purpose() const override; + + virtual void change_function(Function); + virtual size_t ticks_per_second() const = 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 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; + +protected: + HardwareTimer(u8 irq_number, Function); + //^IRQHandler + virtual void handle_irq(const RegisterState&) override; + u64 m_frequency { OPTIMAL_TICKS_PER_SECOND_RATE }; + +private: + Function m_function_to_call; +}; +} diff --git a/Kernel/Time/PIT.cpp b/Kernel/Time/PIT.cpp new file mode 100644 index 00000000000000..7c38e1fb84c701 --- /dev/null +++ b/Kernel/Time/PIT.cpp @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2020, Liav A. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include + +#define IRQ_TIMER 0 +namespace Kernel { + +NonnullRefPtr PIT::initialize(Function callback) +{ + return adopt(*new PIT(move(callback))); +} + +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 callback) + : HardwareTimer(IRQ_TIMER, move(callback)) + , m_periodic(true) +{ + IO::out8(PIT_CTL, TIMER0_SELECT | WRITE_WORD | MODE_SQUARE_WAVE); + + klog() << "PIT: " << OPTIMAL_TICKS_PER_SECOND_RATE << " Hz, square wave (" << String::format("%x", 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() +{ + // FIXME: Implement it... + ASSERT_NOT_REACHED(); +} +void PIT::set_non_periodic() +{ + // FIXME: Implement it... + ASSERT_NOT_REACHED(); +} + +void PIT::reset_to_default_ticks_per_second() +{ + InterruptDisabler disabler; + bool success = try_to_set_frequency(OPTIMAL_TICKS_PER_SECOND_RATE); + ASSERT(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 +{ + ASSERT(frequency != 0); + return frequency <= BASE_FREQUENCY; +} +size_t PIT::calculate_nearest_possible_frequency(size_t frequency) const +{ + ASSERT(frequency != 0); + return frequency; +} + +} diff --git a/Kernel/Time/PIT.h b/Kernel/Time/PIT.h new file mode 100644 index 00000000000000..b6d2f3ad0191fa --- /dev/null +++ b/Kernel/Time/PIT.h @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2020, Liav A. + * 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. + */ + +#pragma once + +#include +#include +#include + +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 { +public: + static NonnullRefPtr initialize(Function); + virtual HardwareTimerType timer_type() const override { return HardwareTimerType::i8253; } + virtual const char* model() const override { return "i8254"; } + 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 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); + bool m_periodic { true }; +}; +} diff --git a/Kernel/Time/RTC.cpp b/Kernel/Time/RTC.cpp new file mode 100644 index 00000000000000..5e74c802c92eef --- /dev/null +++ b/Kernel/Time/RTC.cpp @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2020, Liav A. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include + +namespace Kernel { +#define IRQ_TIMER 8 +#define MAX_FREQUENCY 8000 + +NonnullRefPtr RealTimeClock::create(Function callback) +{ + return adopt(*new RealTimeClock(move(callback))); +} +RealTimeClock::RealTimeClock(Function 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(); +} +void RealTimeClock::handle_irq(const RegisterState& regs) +{ + HardwareTimer::handle_irq(regs); + CMOS::read(0x8C); +} + +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); + ASSERT(success); +} + +// FIXME: This is a quick & dirty log base 2 with a paramater. 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; + dbg() << "RTC: Set rate to " << rate; + CMOS::write(0x8A, (previous_rate & 0xF0) | rate); + m_frequency = frequency; + dbg() << "RTC: Set frequency to " << frequency << " Hz"; + enable_irq(); + return true; +} +bool RealTimeClock::is_capable_of_frequency(size_t frequency) const +{ + ASSERT(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 +{ + ASSERT(frequency != 0); + return frequency; +} + +} diff --git a/Kernel/Time/RTC.h b/Kernel/Time/RTC.h new file mode 100644 index 00000000000000..abdc7033ceb459 --- /dev/null +++ b/Kernel/Time/RTC.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2020, Liav A. + * 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. + */ + +#pragma once + +#include +#include +#include + +namespace Kernel { +class RealTimeClock final : public HardwareTimer { +public: + static NonnullRefPtr create(Function callback); + virtual HardwareTimerType timer_type() const override { return HardwareTimerType::RTC; } + virtual const char* model() const override { return "Real Time Clock"; } + 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 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 callback); + virtual void handle_irq(const RegisterState&) override; +}; +} diff --git a/Kernel/Time/TimeManagement.cpp b/Kernel/Time/TimeManagement.cpp new file mode 100644 index 00000000000000..389707653c9e99 --- /dev/null +++ b/Kernel/Time/TimeManagement.cpp @@ -0,0 +1,261 @@ +/* + * Copyright (c) 2020, Liav A. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//#define TIME_DEBUG + +namespace Kernel { + +static TimeManagement* s_time_management; + +bool TimeManagement::initialized() +{ + return s_time_management != nullptr; +} + +bool TimeManagement::is_system_timer(const HardwareTimer& timer) const +{ + return &timer == m_system_timer.ptr(); +} + +void TimeManagement::set_epoch_time(time_t value) +{ + InterruptDisabler disabler; + m_epoch_time = value; +} + +time_t TimeManagement::epoch_time() const +{ + return m_epoch_time; +} + +void TimeManagement::initialize(bool probe_non_legacy_hardware_timers) +{ + ASSERT(!TimeManagement::initialized()); + s_time_management = new TimeManagement(probe_non_legacy_hardware_timers); +} +time_t TimeManagement::seconds_since_boot() const +{ + return m_seconds_since_boot; +} +time_t TimeManagement::ticks_per_second() const +{ + return m_system_timer->ticks_per_second(); +} + +time_t TimeManagement::ticks_this_second() const +{ + return m_ticks_this_second; +} + +time_t TimeManagement::boot_time() const +{ + return RTC::boot_time(); +} + +void TimeManagement::stale_function(const RegisterState&) +{ +} + +TimeManagement::TimeManagement(bool probe_non_legacy_hardware_timers) +{ + if (ACPI::Parser::the().is_operable()) { + if (!ACPI::Parser::the().x86_specific_flags().cmos_rtc_not_present) { + RTC::initialize(); + m_epoch_time += boot_time(); + } else { + klog() << "ACPI: RTC CMOS Not present"; + } + } else { + // We just assume that we can access RTC CMOS, if ACPI isn't usable. + RTC::initialize(); + m_epoch_time += boot_time(); + } + if (probe_non_legacy_hardware_timers) { + if (!probe_and_set_non_legacy_hardware_timers()) + if (!probe_and_set_legacy_hardware_timers()) + ASSERT_NOT_REACHED(); + return; + } + if (probe_and_set_legacy_hardware_timers()) + return; + ASSERT_NOT_REACHED(); +} + +Vector TimeManagement::scan_and_initialize_periodic_timers() +{ + bool enable_periodic_mode = is_hpet_periodic_mode_allowed(); + dbg() << "Scanning for Periodic timers"; + Vector periodic_timers_indexes; + periodic_timers_indexes.ensure_capacity(m_hardware_timers.size()); + for (size_t index = 0; index < m_hardware_timers.size(); index++) { + if (!m_hardware_timers[index].is_null()) { + if (m_hardware_timers[index]->is_periodic_capable()) { + periodic_timers_indexes.append(index); + if (enable_periodic_mode) + m_hardware_timers[index]->set_periodic(); + } + } + } + return periodic_timers_indexes; +} + +Vector TimeManagement::scan_for_non_periodic_timers() +{ + dbg() << "Scanning for Non-Periodic timers"; + Vector non_periodic_timers_indexes; + non_periodic_timers_indexes.ensure_capacity(m_hardware_timers.size()); + for (size_t index = 0; index < m_hardware_timers.size(); index++) { + if (!m_hardware_timers[index].is_null()) + if (!m_hardware_timers[index]->is_periodic_capable()) + non_periodic_timers_indexes.append(index); + } + return non_periodic_timers_indexes; +} + +bool TimeManagement::is_hpet_periodic_mode_allowed() +{ + if (!KParams::the().has("hpet")) { + return true; + } + auto hpet_mode = KParams::the().get("hpet"); + if (hpet_mode == "periodic") + return true; + if (hpet_mode == "nonperiodic") + return false; + ASSERT_NOT_REACHED(); +} + +bool TimeManagement::probe_and_set_non_legacy_hardware_timers() +{ + if (!ACPI::Parser::the().is_operable()) + return false; + if (!HPET::test_and_initialize()) + return false; + if (!HPET::the().comparators().size()) { + dbg() << "HPET initialization aborted."; + return false; + } + dbg() << "HPET: Setting appropriate functions to timers."; + + m_hardware_timers.resize(HPET::the().comparators().size()); + for (size_t index = 0; index < m_hardware_timers.size(); index++) { + m_hardware_timers[index] = HPET::the().comparators()[index]; +#ifdef TIME_DEBUG + dbg() << m_hardware_timers[index].ptr() << " <- " << HPET::the().comparators()[index].ptr(); +#endif + } + + auto periodic_timer_indexes = scan_and_initialize_periodic_timers(); + auto non_periodic_timer_indexes = scan_for_non_periodic_timers(); + + if (is_hpet_periodic_mode_allowed()) + ASSERT(!periodic_timer_indexes.is_empty()); + + ASSERT(periodic_timer_indexes.size() + non_periodic_timer_indexes.size() >= 2); + + if (periodic_timer_indexes.size() >= 2) { + m_time_keeper_timer = m_hardware_timers[periodic_timer_indexes[1]]; + m_system_timer = m_hardware_timers[periodic_timer_indexes[0]]; + } else { + if (periodic_timer_indexes.size() == 1) { + m_time_keeper_timer = m_hardware_timers[periodic_timer_indexes[0]]; + m_system_timer = m_hardware_timers[non_periodic_timer_indexes[0]]; + } else { + m_time_keeper_timer = m_hardware_timers[non_periodic_timer_indexes[1]]; + m_system_timer = m_hardware_timers[non_periodic_timer_indexes[0]]; + } + } + + m_system_timer->change_function([](const RegisterState& regs) { update_scheduler_ticks(regs); }); + dbg() << "Reset timers"; + m_system_timer->try_to_set_frequency(m_system_timer->calculate_nearest_possible_frequency(1024)); + m_time_keeper_timer->change_function([](const RegisterState& regs) { update_time(regs); }); + m_time_keeper_timer->try_to_set_frequency(OPTIMAL_TICKS_PER_SECOND_RATE); + + return true; +} + +bool TimeManagement::probe_and_set_legacy_hardware_timers() +{ + if (ACPI::Parser::the().is_operable()) { + if (ACPI::Parser::the().x86_specific_flags().cmos_rtc_not_present) { + dbg() << "ACPI: CMOS RTC Not Present"; + return false; + } else { + dbg() << "ACPI: CMOS RTC Present"; + } + } + + m_hardware_timers[0] = PIT::initialize([](const RegisterState& regs) { update_time(regs); }); + m_hardware_timers[1] = RealTimeClock::create([](const RegisterState& regs) { update_scheduler_ticks(regs); }); + m_time_keeper_timer = m_hardware_timers[0]; + m_system_timer = m_hardware_timers[1]; + return true; +} + +TimeManagement& TimeManagement::the() +{ + ASSERT(TimeManagement::initialized()); + return *s_time_management; +} + +void TimeManagement::update_time(const RegisterState& regs) +{ + TimeManagement::the().increment_time_since_boot(regs); +} + +void TimeManagement::increment_time_since_boot(const RegisterState&) +{ + ASSERT(!m_time_keeper_timer.is_null()); + if (++m_ticks_this_second >= m_time_keeper_timer->ticks_per_second()) { + // FIXME: Synchronize with other clock somehow to prevent drifting apart. + ++m_seconds_since_boot; + ++m_epoch_time; + m_ticks_this_second = 0; + } +} + +void TimeManagement::update_scheduler_ticks(const RegisterState& regs) +{ + TimeManagement::the().update_ticks(regs); +} + +void TimeManagement::update_ticks(const RegisterState& regs) +{ + Scheduler::timer_tick(regs); +} +} diff --git a/Kernel/Time/TimeManagement.h b/Kernel/Time/TimeManagement.h new file mode 100644 index 00000000000000..eba89a491b4fe3 --- /dev/null +++ b/Kernel/Time/TimeManagement.h @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2020, Liav A. + * 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. + */ + +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace Kernel { + +#define OPTIMAL_TICKS_PER_SECOND_RATE 1000 + +class HardwareTimer; +class TimeManagement { +public: + static bool initialized(); + static void initialize(bool probe_non_legacy_hardware_timers); + static TimeManagement& the(); + + time_t epoch_time() const; + void set_epoch_time(time_t); + time_t seconds_since_boot() const; + time_t ticks_per_second() const; + time_t ticks_this_second() const; + time_t boot_time() const; + + bool is_system_timer(const HardwareTimer&) const; + + static void update_time(const RegisterState&); + void increment_time_since_boot(const RegisterState&); + + static void update_scheduler_ticks(const RegisterState& regs); + void update_ticks(const RegisterState&); + + static void stale_function(const RegisterState&); + static bool is_hpet_periodic_mode_allowed(); + +private: + explicit TimeManagement(bool probe_non_legacy_hardware_timers); + bool probe_and_set_legacy_hardware_timers(); + bool probe_and_set_non_legacy_hardware_timers(); + Vector scan_and_initialize_periodic_timers(); + Vector scan_for_non_periodic_timers(); + FixedArray> m_hardware_timers { 2 }; + + u32 m_ticks_this_second { 0 }; + u32 m_seconds_since_boot { 0 }; + time_t m_epoch_time { 0 }; + RefPtr m_system_timer; + RefPtr m_time_keeper_timer; + Function m_scheduler_ticking { update_time }; + Function m_stale_method { stale_function }; +}; + +} diff --git a/Kernel/TimerQueue.h b/Kernel/TimerQueue.h index 511e6e3bfc5592..4398a714488cfc 100644 --- a/Kernel/TimerQueue.h +++ b/Kernel/TimerQueue.h @@ -30,7 +30,7 @@ #include #include #include -#include +#include namespace Kernel { @@ -53,9 +53,9 @@ struct Timer { }; enum TimeUnit { - MS = TICKS_PER_SECOND / 1000, - S = TICKS_PER_SECOND, - M = TICKS_PER_SECOND * 60 + MS = OPTIMAL_TICKS_PER_SECOND_RATE / 1000, + S = OPTIMAL_TICKS_PER_SECOND_RATE, + M = OPTIMAL_TICKS_PER_SECOND_RATE * 60 }; class TimerQueue { diff --git a/Kernel/init.cpp b/Kernel/init.cpp index 0d268db2f8adf3..24208a3d7a61b4 100644 --- a/Kernel/init.cpp +++ b/Kernel/init.cpp @@ -48,7 +48,6 @@ #include #include #include -#include #include #include #include @@ -71,6 +70,7 @@ #include #include #include +#include #include // Defined in the linker script @@ -89,6 +89,7 @@ static void setup_acpi(); static void setup_vmmouse(); static void setup_pci(); static void setup_interrupts(); +static void setup_time_management(); VirtualConsole* tty0; @@ -121,8 +122,7 @@ extern "C" [[noreturn]] void init() __stack_chk_guard = get_good_random(); - PIT::initialize(); - RTC::initialize(); + setup_time_management(); // call global constructors after gtd and itd init for (ctor_func_t* ctor = &start_ctors; ctor < &end_ctors; ctor++) @@ -150,8 +150,6 @@ extern "C" [[noreturn]] void init() setup_pci(); - PIT::initialize(); - if (text_debug) { dbg() << "Text mode enabled"; } else { @@ -178,7 +176,7 @@ extern "C" [[noreturn]] void init() Process::create_kernel_process(syncd_thread, "syncd", [] { for (;;) { VFS::the().sync(); - Thread::current->sleep(1 * TICKS_PER_SECOND); + Thread::current->sleep(1 * TimeManagement::the().ticks_per_second()); } }); @@ -482,4 +480,24 @@ void setup_interrupts() klog() << "smp boot argmuent has an invalid value."; hang(); } + +void setup_time_management() +{ + if (!KParams::the().has("time")) { + TimeManagement::initialize(true); + return; + } + auto time = KParams::the().get("time"); + if (time == "legacy") { + TimeManagement::initialize(false); + return; + } + if (time == "modern") { + TimeManagement::initialize(true); + return; + } + + kprintf("time boot argmuent has an invalid value.\n"); + hang(); +} }