diff --git a/Cargo.toml b/Cargo.toml
index 945bd9c2c76440d46f48bb27f5b096255cba59dc..9872574c59847442a0c81cbf28762068cc46deba 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -91,6 +91,7 @@ num = { version = "0.4", default-features = false }
 num-traits = { version = "0.2", default-features = false }
 num-derive = "0.3"
 zerocopy = "0.6"
+time = { version = "0.3", default-features = false }
 
 [dependencies.smoltcp]
 version = "0.9"
@@ -111,7 +112,6 @@ features = [
 
 [target.'cfg(target_arch = "x86_64")'.dependencies]
 multiboot = "0.8"
-time = { version = "0.3", default-features = false }
 uart_16550 = "0.2"
 x86 = { version = "0.52", default-features = false }
 x86_64 = "0.14"
diff --git a/src/arch/aarch64/kernel/mod.rs b/src/arch/aarch64/kernel/mod.rs
index 870342bcae2616dc91ff0401ff762114946c2335..48134b0b240071097d12095d09a39a91d7906093 100644
--- a/src/arch/aarch64/kernel/mod.rs
+++ b/src/arch/aarch64/kernel/mod.rs
@@ -185,10 +185,7 @@ pub fn boot_processor_init() {
 	interrupts::enable();
 	processor::detect_frequency();
 	processor::print_information();
-
-	/*
 	systemtime::init();
-	*/
 
 	finish_processor_init();
 }
diff --git a/src/arch/aarch64/kernel/systemtime.rs b/src/arch/aarch64/kernel/systemtime.rs
index 616d0b6f1429c33e987a60c376b7482d5d895fb8..41b6d0bdc789dbc1654ea3a96e703fc0081c3a91 100644
--- a/src/arch/aarch64/kernel/systemtime.rs
+++ b/src/arch/aarch64/kernel/systemtime.rs
@@ -1,9 +1,99 @@
-use crate::env;
+use alloc::vec::Vec;
+use core::arch::asm;
+use core::str;
 
-extern "C" {
-	static mut boot_gtod: u64;
+use hermit_dtb::Dtb;
+use hermit_sync::OnceCell;
+use time::OffsetDateTime;
+
+use crate::arch::aarch64::mm::paging::{self, BasePageSize, PageSize, PageTableEntryFlags};
+use crate::arch::aarch64::mm::{virtualmem, PhysAddr, VirtAddr};
+use crate::kernel::boot_info;
+
+static PL031_ADDRESS: OnceCell<VirtAddr> = OnceCell::new();
+static BOOT_TIME: OnceCell<u64> = OnceCell::new();
+
+const RTC_DR: usize = 0x00;
+const RTC_MR: usize = 0x04;
+const RTC_LR: usize = 0x08;
+const RTC_CR: usize = 0x0c;
+/// Interrupt mask and set register
+const RTC_IRQ_MASK: usize = 0x10;
+/// Raw interrupt status
+const RTC_RAW_IRQ_STATUS: usize = 0x14;
+/// Masked interrupt status
+const RTC_MASK_IRQ_STATUS: usize = 0x18;
+/// Interrupt clear register
+const RTC_IRQ_CLEAR: usize = 0x1c;
+
+#[inline]
+fn rtc_read(off: usize) -> u32 {
+	let value: u32;
+
+	// we have to use inline assembly to guarantee 32bit memory access
+	unsafe {
+		asm!("ldar {value:w}, [{addr}]",
+			value = out(reg) value,
+			addr = in(reg) (PL031_ADDRESS.get().unwrap().as_usize() + off),
+			options(nostack, readonly),
+		);
+	}
+
+	value
 }
 
 pub fn get_boot_time() -> u64 {
-	unsafe { boot_gtod }
+	*BOOT_TIME.get().unwrap()
+}
+
+pub fn init() {
+	let dtb = unsafe {
+		Dtb::from_raw(boot_info().hardware_info.device_tree.unwrap().get() as *const u8)
+			.expect(".dtb file has invalid header")
+	};
+
+	for node in dtb.enum_subnodes("/") {
+		let parts: Vec<_> = node.split('@').collect();
+
+		if let Some(compatible) = dtb.get_property(parts.first().unwrap(), "compatible") {
+			if str::from_utf8(compatible).unwrap().find("pl031").is_some() {
+				let reg = dtb.get_property(parts.first().unwrap(), "reg").unwrap();
+				let (slice, residual_slice) = reg.split_at(core::mem::size_of::<u64>());
+				let addr = PhysAddr(u64::from_be_bytes(slice.try_into().unwrap()));
+				let (slice, residual_slice) = residual_slice.split_at(core::mem::size_of::<u64>());
+				let size = u64::from_be_bytes(slice.try_into().unwrap());
+
+				debug!("Found RTC at {:#X} (size {:#X})", addr, size);
+
+				let pl031_address = virtualmem::allocate_aligned(
+					size.try_into().unwrap(),
+					BasePageSize::SIZE.try_into().unwrap(),
+				)
+				.unwrap();
+				PL031_ADDRESS.set(pl031_address).unwrap();
+				debug!("Mapping RTC to virtual address {pl031_address:p}",);
+
+				let mut flags = PageTableEntryFlags::empty();
+				flags.device().writable().execute_disable();
+				paging::map::<BasePageSize>(
+					pl031_address,
+					addr,
+					(size / BasePageSize::SIZE).try_into().unwrap(),
+					flags,
+				);
+
+				let boot_time =
+					OffsetDateTime::from_unix_timestamp(rtc_read(RTC_DR) as i64).unwrap();
+				info!("HermitCore-rs booted on {boot_time}");
+
+				let micros = u64::try_from(boot_time.unix_timestamp_nanos() / 1000).unwrap();
+				BOOT_TIME.set(micros).unwrap();
+
+				return;
+			}
+		}
+	}
+
+	PL031_ADDRESS.set(VirtAddr::zero()).unwrap();
+	BOOT_TIME.set(0).unwrap();
 }