Skip to content

Commit

Permalink
aarch64: implement minimal PL031 support
Browse files Browse the repository at this point in the history
This patch provides basic implementation of a subset of
functionality of the PrimeCell Real Time Clock (PL031) device
that is enough to capture boot time as number of seconds since epoch.

Please note that this implementation does not handle any host
clock drift and provides current time functionality that is only
accurate up to a second.

This patch effectively fixes 2 unit tests - tst-time.cc and
tst-timerfd.cc.

Fixes #1091

Signed-off-by: Waldemar Kozaczuk <[email protected]>
  • Loading branch information
wkozaczuk committed Mar 30, 2021
1 parent cdb0ab6 commit f80864d
Show file tree
Hide file tree
Showing 7 changed files with 154 additions and 5 deletions.
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -835,6 +835,7 @@ endif # x64
ifeq ($(arch),aarch64)
drivers += drivers/mmio-isa-serial.o
drivers += drivers/pl011.o
drivers += drivers/pl031.o
drivers += drivers/cadence-uart.o
drivers += drivers/xenconsole.o
drivers += drivers/virtio.o
Expand Down
19 changes: 19 additions & 0 deletions arch/aarch64/arch-dtb.cc
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,25 @@ u64 dtb_get_uart(int *irqid)
return retval;
}

u64 dtb_get_rtc()
{
u64 retval;
int node; size_t len;

if (!dtb)
return 0;

node = fdt_node_offset_by_compatible(dtb, -1, "arm,pl031");
if (node < 0)
return 0;

len = dtb_get_reg(node, &retval);
if (!len)
return 0;

return retval;
}

u64 dtb_get_mmio_serial_console(int *irqid)
{
int node;
Expand Down
7 changes: 7 additions & 0 deletions arch/aarch64/arch-dtb.hh
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,13 @@ size_t dtb_get_phys_memory(u64 *addr);
*/
u64 dtb_get_uart(int *irqid);

/* u64 dtb_get_rtc()
*
* return the base address of the RTC (PL031)
* device or returns zero on failure.
*/
u64 dtb_get_rtc();

/* u64 dtb_get_mmio_serial_console(int *irqid)
*
* return the base address of the serial console and writes the
Expand Down
22 changes: 20 additions & 2 deletions arch/aarch64/arm-clock.cc
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

#include "drivers/clockevent.hh"
#include "drivers/clock.hh"
#include "drivers/pl031.hh"
#include "arm-clock.hh"
#include <osv/interrupt.hh>
#include "exceptions.hh"
Expand Down Expand Up @@ -36,6 +37,8 @@ class arm_clock : public clock {
u32 freq_hz; /* frequency in Hz (updates per second) */

friend class arm_clock_events;
private:
u64 _boot_time_in_ns;
};

arm_clock::arm_clock() {
Expand All @@ -49,6 +52,13 @@ arm_clock::arm_clock() {
#if CONF_logger_debug
debug_early_u64("arm_clock(): frequency read as ", freq_hz);
#endif
u64 rtc_address = dtb_get_rtc();
if (rtc_address) {
pl031 rtc(rtc_address);
_boot_time_in_ns = rtc.wallclock_ns();
} else {
_boot_time_in_ns = 0;
}
}

static __attribute__((constructor(init_prio::clock))) void setup_arm_clock()
Expand All @@ -61,6 +71,10 @@ static __attribute__((constructor(init_prio::clock))) void setup_arm_clock()

s64 arm_clock::uptime()
{
//Please note we read CNTVCT cpu system register which provides
//the accross-system consistent value of the virtual system counter.
//This means that uptime() should return the exact same value
//on each cpu.
u64 cntvct;
asm volatile ("isb; mrs %0, cntvct_el0; isb; " : "=r"(cntvct) :: "memory");

Expand All @@ -70,12 +84,16 @@ s64 arm_clock::uptime()

s64 arm_clock::time()
{
return uptime();
//Given that _boot_time_in_ns came from RTC which gives only 1 second
//precision the resulting time() value is only accurate up to a second
//On top of this, this implementation does not account for any host
//clock drifts
return _boot_time_in_ns + uptime();
}

s64 arm_clock::boot_time()
{
return uptime();
return _boot_time_in_ns;
}

u64 arm_clock::processor_to_nano(u64 ticks)
Expand Down
56 changes: 56 additions & 0 deletions drivers/pl031.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/*
* Copyright (C) 2021 Waldemar Kozaczuk
*
* This work is open source software, licensed under the terms of the
* BSD license as described in the LICENSE file in the top-level directory.
*/

#include "pl031.hh"
#include <osv/mmu.hh>

/* spec: see PrimeCell Real Time Clock (PL031) Technical Reference Manual.
* implemented according to Revision: r1p3.
* See https://static.docs.arm.com/ddi0224/c/real_time_clock_pl031_r1p3_technical_reference_manual_DDI0224C.pdf
* Please note that this a minimal implementation of a subset of the
* functionality which is enough to read the current time in seconds
* at the boot time.
*/

#define rtc_reg(ADDR,REG_OFFSET) (*(volatile u32 *)(ADDR+REG_OFFSET))

pl031::pl031(u64 address)
{
_address = address;
mmu::linear_map((void *)_address, _address, mmu::page_size, mmu::page_size,
mmu::mattr::dev);
}

pl031::~pl031()
{
mmu::munmap((void*)_address, mmu::page_size);
}

uint64_t pl031::wallclock_ns()
{
// Let us read various identificatin registers to
// verify that it is indeed a valid PL031 device
if( rtc_reg(_address, RTCPeriphID0) == RTCPeriphID0_val &&
rtc_reg(_address, RTCPeriphID1) == RTCPeriphID1_val &&
(rtc_reg(_address, RTCPeriphID2) & RTCPeriphID2_mask) == RTCPeriphID2_val &&
rtc_reg(_address, RTCPeriphID3) == RTCPeriphID3_val &&
rtc_reg(_address, RTCPCellID0) == RTCPCellID0_val &&
rtc_reg(_address, RTCPCellID1) == RTCPCellID1_val &&
rtc_reg(_address, RTCPCellID2) == RTCPCellID2_val &&
rtc_reg(_address, RTCPCellID3) == RTCPCellID3_val) {
// Read value of RTC which is number of seconds since epoch
// representing current time
u64 epoch_time_in_seconds = rtc_reg(_address, RTCDR);
#if CONF_logger_debug
debug_early_u64("pl031::wallclock_ns(): RTC seconds since epoch read as ", epoch_time_in_seconds);
#endif
return epoch_time_in_seconds * 1000000000;
} else {
debug_early("pl031: could no detect!");
return 0;
}
}
50 changes: 50 additions & 0 deletions drivers/pl031.hh
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* Copyright (C) 2021 Waldemar Kozaczuk
*
* This work is open source software, licensed under the terms of the
* BSD license as described in the LICENSE file in the top-level directory.
*/

#ifndef OSV_PL031_HH
#define OSV_PL031_HH

#include <osv/types.h>

#define RTCDR 0x000 /* data */
#define RTCMR 0x004 /* match */
#define RTCLR 0x008 /* load */
#define RTCCR 0x00c /* control */
#define RTCIMSC 0x010 /* interrupt mask set or clear */
#define RTCRIS 0x014 /* raw interrupt status */
#define RTCMIS 0x018 /* masked interrupt status */
#define RTCICR 0x01c /* interrupt clear */
#define RTCPeriphID0 0xfe0 /* peripheral ID bits [7:0] */
#define RTCPeriphID1 0xfe4 /* peripheral ID bits [15:8] */
#define RTCPeriphID2 0xfe8 /* peripheral ID bits [23:16] */
#define RTCPeriphID3 0xfec /* peripheral ID bits [31:24] */
#define RTCPCellID0 0xff0 /* PrimeCell ID bits [7:0] */
#define RTCPCellID1 0xff4 /* PrimeCell ID bits [7:0] */
#define RTCPCellID2 0xff8 /* PrimeCell ID bits [7:0] */
#define RTCPCellID3 0xffc /* PrimeCell ID bits [7:0] */

#define RTCPeriphID0_val 0x31
#define RTCPeriphID1_val 0x10
#define RTCPeriphID2_mask 0x0f
#define RTCPeriphID2_val 0x04
#define RTCPeriphID3_val 0x00

#define RTCPCellID0_val 0x0d
#define RTCPCellID1_val 0xf0
#define RTCPCellID2_val 0x05
#define RTCPCellID3_val 0xb1

class pl031 {
public:
pl031(u64 address);
~pl031();
uint64_t wallclock_ns();
private:
u64 _address;
};

#endif //OSV_PL031_HH
4 changes: 1 addition & 3 deletions scripts/test.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
"tcp_close_without_reading_on_qemu"
]

#At this point there are 120 out of 131 unit tests that pass on aarch64.
#At this point there are 122 out of 131 unit tests that pass on aarch64.
#The remaining ones are disabled below until we fix various
#issues that prevent those tests from passing.
aarch64_disabled_list= [
Expand All @@ -46,8 +46,6 @@
#Please see comments on the right side for more details
"tst-elf-permissions.so", # Infinite page fault
"tst-mmap.so", # Infinite page fault
"tst-time.so", # One assertion fails - 'tst-time.cc(70): fatal error: in "time_time": critical check (static_cast<time_t>(0)) != (t1) has failed'
"tst-timerfd.so", # Some assertions fail - 'SUMMARY: 212 tests, 10 failures'
#These tests fail due to some other shortcomings in the test scripts
"tracing_smoke_test",
"tcp_close_without_reading_on_fc",
Expand Down

0 comments on commit f80864d

Please sign in to comment.