Skip to content

Commit 14cc6d8

Browse files
committed
Kernel/riscv64: Support the Google Goldfish real-time clock
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.
1 parent 77f058c commit 14cc6d8

File tree

4 files changed

+113
-2
lines changed

4 files changed

+113
-2
lines changed

Kernel/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -598,6 +598,8 @@ elseif("${SERENITY_ARCH}" STREQUAL "riscv64")
598598
Bus/PCI/Controller/GenericECAMHostController.cpp
599599
Bus/PCI/DeviceTreeHelpers.cpp
600600
Bus/PCI/DeviceTreeInitializer.cpp
601+
602+
Devices/Time/GoldfishRTC.cpp
601603
)
602604

603605
# NOTE: These files cannot use a stack protector and sanitizers, as these will cause accesses to global variables to be inserted
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
/*
2+
* Copyright (c) 2026, Sönke Holz <soenke.holz@serenityos.org>
3+
*
4+
* SPDX-License-Identifier: BSD-2-Clause
5+
*/
6+
7+
#include <AK/NeverDestroyed.h>
8+
#include <Kernel/Devices/Time/GoldfishRTC.h>
9+
#include <Kernel/Firmware/DeviceTree/Driver.h>
10+
#include <Kernel/Firmware/DeviceTree/Management.h>
11+
12+
namespace Kernel {
13+
14+
// III. Goldfish real-time clock (RTC) https://android.googlesource.com/platform/external/qemu/+/master/docs/GOLDFISH-VIRTUAL-HARDWARE.TXT
15+
16+
struct GoldfishRTC::Registers {
17+
u32 time_low; // 0x00 TIME_LOW R: Get current time, then return low-order 32-bits.
18+
u32 time_high; // 0x04 TIME_HIGH R: Return high 32-bits from previous TIME_LOW read.
19+
u32 alarm_low; // 0x08 ALARM_LOW W: Set low 32-bit value of alarm, then arm it.
20+
u32 alarm_high; // 0x0c ALARM_HIGH W: Set high 32-bit value of alarm.
21+
u32 clear_interrupt; // 0x10 CLEAR_INTERRUPT W: Lower device's irq level.
22+
};
23+
static_assert(AssertSize<GoldfishRTC::Registers, 0x14>());
24+
25+
static NeverDestroyed<Optional<GoldfishRTC>> s_goldfish_rtc;
26+
27+
GoldfishRTC::GoldfishRTC(Memory::TypedMapping<Registers volatile> rtc_registers)
28+
: m_registers(move(rtc_registers))
29+
{
30+
m_boot_time = current_time();
31+
}
32+
33+
GoldfishRTC* GoldfishRTC::the()
34+
{
35+
if (!s_goldfish_rtc->has_value())
36+
return nullptr;
37+
38+
return &s_goldfish_rtc->value();
39+
}
40+
41+
UnixDateTime GoldfishRTC::current_time() const
42+
{
43+
// "To read the value, the kernel must perform an IO_READ(TIME_LOW), which returns
44+
// an unsigned 32-bit value, before an IO_READ(TIME_HIGH), which returns a signed
45+
// 32-bit value, corresponding to the higher half of the full value."
46+
u64 time_in_nanoseconds = m_registers->time_low;
47+
time_in_nanoseconds |= static_cast<u64>(m_registers->time_high) << 32;
48+
49+
return UnixDateTime::from_nanoseconds_since_epoch(bit_cast<i64>(time_in_nanoseconds));
50+
}
51+
52+
static constinit Array const compatibles_array = {
53+
"google,goldfish-rtc"sv,
54+
};
55+
56+
EARLY_DEVICETREE_DRIVER(GoldfishRTCDriver, compatibles_array);
57+
58+
// https://www.kernel.org/doc/Documentation/devicetree/bindings/rtc/trivial-rtc.yaml
59+
ErrorOr<void> GoldfishRTCDriver::probe(DeviceTree::Device const& device, StringView) const
60+
{
61+
if (s_goldfish_rtc->has_value())
62+
return {};
63+
64+
auto registers_resource = TRY(device.get_resource(0));
65+
if (registers_resource.size < sizeof(GoldfishRTC::Registers))
66+
return EINVAL;
67+
68+
auto rtc_registers = TRY(Memory::map_typed_writable<GoldfishRTC::Registers volatile>(registers_resource.paddr));
69+
*s_goldfish_rtc = GoldfishRTC(move(rtc_registers));
70+
71+
return {};
72+
}
73+
74+
}

Kernel/Devices/Time/GoldfishRTC.h

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/*
2+
* Copyright (c) 2026, Sönke Holz <soenke.holz@serenityos.org>
3+
*
4+
* SPDX-License-Identifier: BSD-2-Clause
5+
*/
6+
7+
#pragma once
8+
9+
#include <Kernel/Memory/TypedMapping.h>
10+
11+
namespace Kernel {
12+
13+
class GoldfishRTC {
14+
public:
15+
struct Registers;
16+
17+
GoldfishRTC(Memory::TypedMapping<Registers volatile>);
18+
19+
static GoldfishRTC* the();
20+
21+
UnixDateTime boot_time() const { return m_boot_time; }
22+
UnixDateTime current_time() const;
23+
24+
private:
25+
Memory::TypedMapping<Registers volatile> m_registers;
26+
UnixDateTime m_boot_time;
27+
};
28+
29+
}

Kernel/Time/TimeManagement.cpp

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
# include <Kernel/Arch/aarch64/Time/PL031.h>
2424
#elif ARCH(RISCV64)
2525
# include <Kernel/Arch/riscv64/Timer.h>
26+
# include <Kernel/Devices/Time/GoldfishRTC.h>
2627
#else
2728
# error Unknown architecture
2829
#endif
@@ -269,8 +270,10 @@ UnixDateTime TimeManagement::boot_time()
269270
return UnixDateTime::epoch();
270271
return rtc->boot_time();
271272
#elif ARCH(RISCV64)
272-
// FIXME: Return correct boot time
273-
return UnixDateTime::epoch();
273+
auto rtc = GoldfishRTC::the();
274+
if (!rtc)
275+
return UnixDateTime::epoch();
276+
return rtc->boot_time();
274277
#else
275278
# error Unknown architecture
276279
#endif
@@ -312,6 +315,9 @@ UNMAP_AFTER_INIT TimeManagement::TimeManagement()
312315
m_epoch_time += boot_time().offset_to_epoch();
313316
probe_and_set_aarch64_hardware_timers();
314317
#elif ARCH(RISCV64)
318+
auto rtc = GoldfishRTC::the();
319+
if (rtc)
320+
m_epoch_time += boot_time().offset_to_epoch();
315321
probe_and_set_riscv64_hardware_timers();
316322
#else
317323
# error Unknown architecture

0 commit comments

Comments
 (0)