Kernel: Add a Timer class for aarch64

For now, this can only query microseconds since boot.

Use this to print a timestamp every second. This busy-loops
until a second has passed. This might be a good first use of
interrupts soon.

qemu used to not implement this timer at some point, but
it seems to work fine even in qemu now (qemu v 5.2.0).
This commit is contained in:
Nico Weber 2021-10-02 15:50:23 -04:00 committed by Linus Groh
parent 496d2e3c29
commit bc213ad7a2
Notes: sideshowbarker 2024-07-18 03:11:30 +09:00
4 changed files with 91 additions and 1 deletions

View file

@ -0,0 +1,51 @@
/*
* Copyright (c) 2021, Nico Weber <thakis@chromium.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <Kernel/Prekernel/Arch/aarch64/MMIO.h>
#include <Kernel/Prekernel/Arch/aarch64/Timer.h>
namespace Prekernel {
// "12.1 System Timer Registers" / "10.2 System Timer Registers"
struct TimerRegisters {
u32 control_and_status;
u32 counter_low;
u32 counter_high;
u32 compare[4];
};
// Bits of the `control_and_status` register.
// See "CS register" in Broadcom doc for details.
enum FlagBits {
SystemTimerMatch0 = 1 << 0,
SystemTimerMatch1 = 1 << 1,
SystemTimerMatch2 = 1 << 2,
SystemTimerMatch3 = 1 << 3,
};
Timer::Timer()
: m_registers(MMIO::the().peripheral<TimerRegisters>(0x3000))
{
}
Timer& Timer::the()
{
static Timer instance;
return instance;
}
u64 Timer::microseconds_since_boot()
{
u32 high = m_registers->counter_high;
u32 low = m_registers->counter_low;
if (high != m_registers->counter_high) {
high = m_registers->counter_high;
low = m_registers->counter_low;
}
return (static_cast<u64>(high) << 32) | low;
}
}

View file

@ -0,0 +1,27 @@
/*
* Copyright (c) 2021, Nico Weber <thakis@chromium.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/Types.h>
namespace Prekernel {
struct TimerRegisters;
class Timer {
public:
static Timer& the();
u64 microseconds_since_boot();
private:
Timer();
TimerRegisters volatile* m_registers;
};
}

View file

@ -6,6 +6,7 @@
#include <AK/Types.h>
#include <Kernel/Prekernel/Arch/aarch64/Mailbox.h>
#include <Kernel/Prekernel/Arch/aarch64/Timer.h>
#include <Kernel/Prekernel/Arch/aarch64/UART.h>
extern "C" [[noreturn]] void halt();
@ -24,7 +25,17 @@ extern "C" [[noreturn]] void init()
uart.print_num(firmware_version);
uart.print_str("\r\n");
halt();
auto& timer = Prekernel::Timer::the();
u64 start_musec = 0;
for (;;) {
u64 now_musec;
while ((now_musec = timer.microseconds_since_boot()) - start_musec < 1'000'000)
;
start_musec = now_musec;
uart.print_str("Timer: ");
uart.print_num(now_musec);
uart.print_str("\r\n");
}
}
// FIXME: Share this with the Intel Prekernel.

View file

@ -9,6 +9,7 @@ if ("${SERENITY_ARCH}" STREQUAL "aarch64")
Arch/aarch64/Mailbox.cpp
Arch/aarch64/MainIdRegister.cpp
Arch/aarch64/MMIO.cpp
Arch/aarch64/Timer.cpp
Arch/aarch64/UART.cpp
Arch/aarch64/boot.S
Arch/aarch64/init.cpp