As it requires MMIO access, reading the raw value of the HPET timer is
quite expensive, so doing it every time the system timer fired (~4ms)
resulted in a heavy overload over the system. As shown in [1] it is
quite known that HPET timers are slow to read. Moreover, when using QEMU
reading from MMIO causes a VM exit and makes the whole virtualized world
stop. The consequence is that reading the HPET timer would not appear in
profiles as it is not slow from the perspective of the VM.
To solve that issue, we're now using the Time Stamp Counter (TSC) as our
time-keeping source. Whenever the system interrupt fires, we read the
TSC (which is cheap, see [1], and doesn't cause a VM exit) and compute
the elapsed time based on how many cycle the CPU made during that time.
[1] https://docs.redhat.com/en/documentation/red_hat_enterprise_linux_for_real_time/7/html/reference_guide/chap-timestamping#Reading_hardware_clock_sources
This makes all non-legacy timers use the same callback implementation,
and remove some code duplication between platforms.
Now, platform specific code provides a `m_read_time_from_hardware`
function that is then called by the generic code.
The QEMU virt machine uses a Goldfish RTC device as its real-time clock.
This means that we now display the correct time in riscv64 QEMU and
date-dependent tests no longer fail!
This driver is based on the PL031 driver.
Now that interrupt controllers are probed in a separate stage before
ProbeStage::Early (used by timer drivers), there is no need to delay
timer driver initialization anymore.
Previously, this was needed since any device driver using interrupts
depends on InterruptManagement being initialized.
This required making the list of hardware timers accessible before
TimeManagement is initialized by converting it to a static variable.
This makes it not as easy to forgot to handle OOMs in the kernel.
This commit replaces most usages of this function with
`try_append(...).release_value_but_fixme_should_propagate_errors()`.
But in some cases, using the `TRY` macro or `unchecked_append()` is
already possible.
In places where allocations should not fail or an OOM would be fatal
anyways, `MUST(try_append(...))` should be used explicitly.
AArch64 already implements `monotonic_time(TimePrecision::Precise)`, but
didn't advertise support for it by setting `m_can_query_precise_time`.
This causes SystemMonitor to no longer claim that all CPU time is spent
in the kernel.
This timer is used by the Raspberry Pi 3+4 and the QEMU virt machine.
Remove the `#if ARCH`s from HardwareTimerType, as all other enum values
would otherwise have to be updated to keep the values consistent across
all architectures.
Linux did the same thing 18 years ago and their reasons for the change
are similar to ours - https://github.com/torvalds/linux/commit/7d12e78
Most interrupt handlers (i.e. IRQ handlers) never used the register
state reference anywhere so there's simply no need of passing it around.
I didn't measure the performance boost but surely this change can't make
things worse anyway.
We have many places in the kernel code that we have boolean flags that
are only set once, and never reset again but are checked multiple times
before and after the time they're being set, which matches the purpose
of the SetOnce class.
"Wherever applicable" = most places, actually :^), especially for
networking and filesystem timestamps.
This includes changes to unzip, which uses DOSPackedTime, since that is
changed for the FAT file systems.
That's what this class really is; in fact that's what the first line of
the comment says it is.
This commit does not rename the main files, since those will contain
other time-related classes in a little bit.
Support all the available clocks in clock_getres(). Also, fix this
function to use the actual ticks per second value, not the constant
`_SC_CLK_TCK` (which is always equal to 8) and move the resolution
computation logic to TimeManagement.
This sets up the RPi::Timer to trigger an interurpt every 4ms using one
of the comparators. The actual time is calculated by looking at the main
counter of the RPi::Timer using the Timer::update_time function.
A stub for Scheduler::timer_tick is also added, since the TimeManagement
code now calls the function.
The APICTimer, HPET and RTC (the RTC timer is in the context of the PC
RTC here) are timers that exist only in x86 platforms, therefore, we
move the handling code and the initialization code to the Arch/x86/Time
directory. Other related code patterns in the TimeManagement singleton
and in the Random.cpp file are guarded with #ifdef to ensure they are
only compiled for x86 builds.
The PIC and APIC code are specific to x86 platforms, so move them out of
the general Interrupts directory to Arch/x86/common/Interrupts directory
instead.
The RTC and CMOS are currently only supported for x86 platforms and use
specific x86 instructions to produce only certain x86 plaform operations
and results, therefore, we move them to the Arch/x86 specific directory.
We were unconditionally trying to update it in the interrupt, which
would depend on the timer interrupt not being received too soon after
the timers are initialized (before the time page was initialized),
which was the case when using HPET timers via the ACPI tables, but not
when using the PIT when ACPI was disabled.
Currently the APIC class is constructed irrespective of whether it
is used or not.
So, move APIC initialization from init to the InterruptManagement
class and construct the APIC class only when it is needed.
This will somwhat help unify them also under the same SysFS directory in
the commit.
Also, it feels much more like this change reflects the reality that both
ACPI and the BIOS are part of the firmware on x86 computers.
This expands the reach of error propagation greatly throughout the
kernel. Sadly, it also exposes the fact that we're allocating (and
doing other fallible things) in constructors all over the place.
This patch doesn't attempt to address that of course. That's work for
our future selves.