Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Kernel/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -598,6 +598,8 @@ elseif("${SERENITY_ARCH}" STREQUAL "riscv64")
Bus/PCI/Controller/GenericECAMHostController.cpp
Bus/PCI/DeviceTreeHelpers.cpp
Bus/PCI/DeviceTreeInitializer.cpp

Devices/Time/GoldfishRTC.cpp
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I decided to not put it in Arch/riscv64, since the device isn't RISC-V-specific. But it's only used on riscv64 currently.

Copy link
Copy Markdown
Member

@supercomputer7 supercomputer7 Apr 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, maybe we should still put it under riscv64?

Like you said, nothing else uses that, and in case we ever want to make a system that shows what is supported or not (like kconfig on Linux), this might confuse such system.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we should have user-configurable config options, like linux kconfig. Also, there already are other files that are only compiled on devicetree systems.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nothing prevents QEMU or some other emulator/VM to use a goldfish RTC device, in which case we would need to move it out of Arch/.
In fact, QEMU will need to choose a different RTC device on AArch64 before 2038, since the PL031 only exposes a 32-bit time_t.

)

# NOTE: These files cannot use a stack protector and sanitizers, as these will cause accesses to global variables to be inserted
Expand Down
74 changes: 74 additions & 0 deletions Kernel/Devices/Time/GoldfishRTC.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/*
* Copyright (c) 2026, Sönke Holz <soenke.holz@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/

#include <AK/NeverDestroyed.h>
#include <Kernel/Devices/Time/GoldfishRTC.h>
#include <Kernel/Firmware/DeviceTree/Driver.h>
#include <Kernel/Firmware/DeviceTree/Management.h>

namespace Kernel {

// III. Goldfish real-time clock (RTC) https://android.googlesource.com/platform/external/qemu/+/master/docs/GOLDFISH-VIRTUAL-HARDWARE.TXT

struct GoldfishRTC::Registers {
u32 time_low; // 0x00 TIME_LOW R: Get current time, then return low-order 32-bits.
u32 time_high; // 0x04 TIME_HIGH R: Return high 32-bits from previous TIME_LOW read.
u32 alarm_low; // 0x08 ALARM_LOW W: Set low 32-bit value of alarm, then arm it.
u32 alarm_high; // 0x0c ALARM_HIGH W: Set high 32-bit value of alarm.
u32 clear_interrupt; // 0x10 CLEAR_INTERRUPT W: Lower device's irq level.
};
static_assert(AssertSize<GoldfishRTC::Registers, 0x14>());

static NeverDestroyed<Optional<GoldfishRTC>> s_goldfish_rtc;

GoldfishRTC::GoldfishRTC(Memory::TypedMapping<Registers volatile> rtc_registers)
: m_registers(move(rtc_registers))
{
m_boot_time = current_time();
}

GoldfishRTC* GoldfishRTC::the()
{
if (!s_goldfish_rtc->has_value())
return nullptr;

return &s_goldfish_rtc->value();
}

UnixDateTime GoldfishRTC::current_time() const
{
// "To read the value, the kernel must perform an IO_READ(TIME_LOW), which returns
// an unsigned 32-bit value, before an IO_READ(TIME_HIGH), which returns a signed
// 32-bit value, corresponding to the higher half of the full value."
u64 time_in_nanoseconds = m_registers->time_low;
time_in_nanoseconds |= static_cast<u64>(m_registers->time_high) << 32;

return UnixDateTime::from_nanoseconds_since_epoch(bit_cast<i64>(time_in_nanoseconds));
}

static constinit Array const compatibles_array = {
"google,goldfish-rtc"sv,
};

EARLY_DEVICETREE_DRIVER(GoldfishRTCDriver, compatibles_array);

// https://www.kernel.org/doc/Documentation/devicetree/bindings/rtc/trivial-rtc.yaml
ErrorOr<void> GoldfishRTCDriver::probe(DeviceTree::Device const& device, StringView) const
{
if (s_goldfish_rtc->has_value())
return {};

auto registers_resource = TRY(device.get_resource(0));
if (registers_resource.size < sizeof(GoldfishRTC::Registers))
return EINVAL;

auto rtc_registers = TRY(Memory::map_typed_writable<GoldfishRTC::Registers volatile>(registers_resource.paddr));
*s_goldfish_rtc = GoldfishRTC(move(rtc_registers));

return {};
}

}
29 changes: 29 additions & 0 deletions Kernel/Devices/Time/GoldfishRTC.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* Copyright (c) 2026, Sönke Holz <soenke.holz@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/

#pragma once

#include <Kernel/Memory/TypedMapping.h>

namespace Kernel {

class GoldfishRTC {
public:
struct Registers;

GoldfishRTC(Memory::TypedMapping<Registers volatile>);

static GoldfishRTC* the();

UnixDateTime boot_time() const { return m_boot_time; }
UnixDateTime current_time() const;

private:
Memory::TypedMapping<Registers volatile> m_registers;
UnixDateTime m_boot_time;
};

}
10 changes: 8 additions & 2 deletions Kernel/Time/TimeManagement.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
# include <Kernel/Arch/aarch64/Time/PL031.h>
#elif ARCH(RISCV64)
# include <Kernel/Arch/riscv64/Timer.h>
# include <Kernel/Devices/Time/GoldfishRTC.h>
#else
# error Unknown architecture
#endif
Expand Down Expand Up @@ -269,8 +270,10 @@ UnixDateTime TimeManagement::boot_time()
return UnixDateTime::epoch();
return rtc->boot_time();
#elif ARCH(RISCV64)
// FIXME: Return correct boot time
return UnixDateTime::epoch();
auto rtc = GoldfishRTC::the();
if (!rtc)
return UnixDateTime::epoch();
return rtc->boot_time();
#else
# error Unknown architecture
#endif
Expand Down Expand Up @@ -312,6 +315,9 @@ UNMAP_AFTER_INIT TimeManagement::TimeManagement()
m_epoch_time += boot_time().offset_to_epoch();
probe_and_set_aarch64_hardware_timers();
#elif ARCH(RISCV64)
auto rtc = GoldfishRTC::the();
if (rtc)
m_epoch_time += boot_time().offset_to_epoch();
probe_and_set_riscv64_hardware_timers();
#else
# error Unknown architecture
Expand Down
Loading