From 4292b0ead78c3e6e98389c60f5a09acaae101ecc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6nke=20Holz?= Date: Fri, 29 Dec 2023 14:34:23 +0100 Subject: [PATCH] Kernel/riscv64: Add a Timer class for RISC-V This is a basic Timer class based on the aarch64 RPi Timer. It uses the hart-local timer, as defined by the privileged ISA. --- Kernel/Arch/riscv64/Timer.cpp | 89 +++++++++++++++++++++++++++++++++++ Kernel/Arch/riscv64/Timer.h | 61 ++++++++++++++++++++++++ Kernel/CMakeLists.txt | 1 + Kernel/Time/HardwareTimer.h | 9 +++- 4 files changed, 158 insertions(+), 2 deletions(-) create mode 100644 Kernel/Arch/riscv64/Timer.cpp create mode 100644 Kernel/Arch/riscv64/Timer.h diff --git a/Kernel/Arch/riscv64/Timer.cpp b/Kernel/Arch/riscv64/Timer.cpp new file mode 100644 index 00000000000..84d3b68b564 --- /dev/null +++ b/Kernel/Arch/riscv64/Timer.cpp @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2023, Sönke Holz + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include +#include + +namespace Kernel::RISCV64 { + +Timer::Timer() + : HardwareTimer(to_underlying(CSR::SCAUSE::SupervisorTimerInterrupt) & ~CSR::SCAUSE_INTERRUPT_MASK) +{ + // FIXME: Actually query the frequency of the timer from the device tree. + // Based on the "/cpus/timebase-frequency" device tree node for the QEMU virt machine + m_frequency = 10'000'000; // in Hz + + set_interrupt_interval_usec(m_frequency / OPTIMAL_TICKS_PER_SECOND_RATE); + enable_interrupt_mode(); +} + +Timer::~Timer() = default; + +NonnullLockRefPtr Timer::initialize() +{ + return adopt_lock_ref(*new Timer); +} + +u64 Timer::microseconds_since_boot() +{ + return RISCV64::CSR::read(RISCV64::CSR::Address::TIME); +} + +bool Timer::handle_irq(RegisterState const& regs) +{ + auto result = HardwareTimer::handle_irq(regs); + + set_compare(microseconds_since_boot() + m_interrupt_interval); + + return result; +} + +u64 Timer::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 = microseconds_since_boot(); + 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 + delta_ticks += (NumericLimits::max() - m_main_counter_last_read + 1) + current_value; + } + + 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; +} + +void Timer::enable_interrupt_mode() +{ + set_compare(microseconds_since_boot() + m_interrupt_interval); + enable_irq(); +} + +void Timer::set_interrupt_interval_usec(u32 interrupt_interval) +{ + m_interrupt_interval = interrupt_interval; +} + +void Timer::set_compare(u64 compare) +{ + if (SBI::Timer::set_timer(compare).is_error()) + MUST(SBI::Legacy::set_timer(compare)); +} + +} diff --git a/Kernel/Arch/riscv64/Timer.h b/Kernel/Arch/riscv64/Timer.h new file mode 100644 index 00000000000..3988b833aba --- /dev/null +++ b/Kernel/Arch/riscv64/Timer.h @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2023, Sönke Holz + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include +#include + +namespace Kernel::RISCV64 { + +struct TimerRegisters; + +class Timer final : public HardwareTimer { +public: + virtual ~Timer(); + + static NonnullLockRefPtr initialize(); + + virtual HardwareTimerType timer_type() const override { return HardwareTimerType::RISCVTimer; } + virtual StringView model() const override { return "RISC-V Timer"sv; } + virtual size_t ticks_per_second() const override { return m_frequency; } + + virtual bool is_periodic() const override { TODO_RISCV64(); } + virtual bool is_periodic_capable() const override { TODO_RISCV64(); } + virtual void set_periodic() override { TODO_RISCV64(); } + virtual void set_non_periodic() override { TODO_RISCV64(); } + virtual void disable() override { TODO_RISCV64(); } + + virtual void reset_to_default_ticks_per_second() override { TODO_RISCV64(); } + virtual bool try_to_set_frequency(size_t) override { TODO_RISCV64(); } + virtual bool is_capable_of_frequency(size_t) const override { TODO_RISCV64(); } + virtual size_t calculate_nearest_possible_frequency(size_t) const override { TODO_RISCV64(); } + + // FIXME: Share code with HPET::update_time + u64 update_time(u64& seconds_since_boot, u32& ticks_this_second, bool query_only); + + u64 microseconds_since_boot(); + + void set_interrupt_interval_usec(u32); + void enable_interrupt_mode(); + +private: + Timer(); + + void set_compare(u64 compare); + + //^ IRQHandler + virtual bool handle_irq(RegisterState const&) override; + + u32 m_interrupt_interval { 0 }; + + u64 m_main_counter_last_read { 0 }; + u64 m_main_counter_drift { 0 }; +}; + +} diff --git a/Kernel/CMakeLists.txt b/Kernel/CMakeLists.txt index 61d294e92e1..76094d236fc 100644 --- a/Kernel/CMakeLists.txt +++ b/Kernel/CMakeLists.txt @@ -530,6 +530,7 @@ elseif("${SERENITY_ARCH}" STREQUAL "riscv64") Arch/riscv64/SafeMem.cpp Arch/riscv64/SBI.cpp Arch/riscv64/SmapDisabler.cpp + Arch/riscv64/Timer.cpp ) add_compile_options(-fno-stack-protector -fno-sanitize=all) diff --git a/Kernel/Time/HardwareTimer.h b/Kernel/Time/HardwareTimer.h index 537b393488a..fe08df0a5ac 100644 --- a/Kernel/Time/HardwareTimer.h +++ b/Kernel/Time/HardwareTimer.h @@ -14,12 +14,17 @@ namespace Kernel { enum class HardwareTimerType { +#if ARCH(X86_64) i8253 = 0x1, /* PIT */ RTC = 0x2, /* Real Time Clock */ HighPrecisionEventTimer = 0x3, /* also known as IA-PC HPET */ LocalAPICTimer = 0x4, /* Local APIC */ -#if ARCH(AARCH64) - RPiTimer = 0x5 +#elif ARCH(AARCH64) + RPiTimer = 0x5, +#elif ARCH(RISCV64) + RISCVTimer = 0x6, +#else +# error Unknown architecture #endif };