l4re-base-25.08.0

This commit is contained in:
2025-09-12 15:55:45 +02:00
commit d959eaab98
37938 changed files with 9382688 additions and 0 deletions

4
src/l4/pkg/rtc/Control Normal file
View File

@@ -0,0 +1,4 @@
provides: rtc rtc_libc_be
requires: stdlibs libio cxx_libc_io cxx_io libstdc++
optional: drivers-frst i2c-server
Maintainer: adam.lackorzynski@kernkonzept.com

14
src/l4/pkg/rtc/Kconfig.L4 Normal file
View File

@@ -0,0 +1,14 @@
menu "RTC"
config RTC_DS3231
bool "DS3231"
depends on HAVE_BIDPC_I2C_DRIVER
config RTC_PCF85063A
bool "NXP PCF85063A RTC chip"
depends on HAVE_BIDPC_I2C_DRIVER
comment "Some RTC drivers might be unavailable due to missing i2c-driver package."
depends on !HAVE_BIDPC_I2C_DRIVER
endmenu

4
src/l4/pkg/rtc/Makefile Normal file
View File

@@ -0,0 +1,4 @@
PKGDIR = .
L4DIR ?= $(PKGDIR)/../..
include $(L4DIR)/mk/subdir.mk

20
src/l4/pkg/rtc/README.md Normal file
View File

@@ -0,0 +1,20 @@
# L4Re RTC server
rtc is an L4Re server that provides wallclock time to its clients. The server
uses the I/O port-based RTC on x86 and the PL031 RTC on ARM.
# Documentation
This package is part of the L4Re Operating System Framework. For documentation
and build instructions see the [L4Re
wiki](https://kernkonzept.com/L4Re/guides/l4re).
# Contributions
We welcome contributions. Please see our contributors guide on
[how to contribute](https://kernkonzept.com/L4Re/contributing/l4re).
# License
Detailed licensing and copyright information can be found in
the [LICENSE](LICENSE.spdx) file.

View File

@@ -0,0 +1,29 @@
# Security Policy
This document outlines security procedures for the open-source projects of the
L4Re Operating System Framework as found on https://github.com/kernkonzept.
# Reporting a vulnerability
Security is very important to us and we take all security vulnerabilities
seriously. Thank you for improving the security of our open source software. If
you have discovered a security issue, we appreciate your efforts and your
responsible disclosure.
Please report a security vulnerability by sending an encrypted email to our
security team using our [public
key](https://www.kernkonzept.com/dl/security-at-kernkonzept.pub)
to **security@kernkonzept.com**. The fingerprint of our public key is
````
C4DC 2909 A22E D080 C012 5373 4055 CBA2 A4FD 855B
````
Please include the following in your report:
* A description of the vulnerability
* Steps to reproduce the vulnerability
A member of Kernkonzept's security team will confirm the vulnerability,
determine its impact, and develop a fix. The fix will be applied to the master
branch, tested, and released.

View File

@@ -0,0 +1,2 @@
INPUT += %PKGDIR%/doc/rtc.dox
INPUT += %PKGDIR%/doc/usage.md

View File

@@ -0,0 +1,6 @@
// vi:ft=c
/**
* \page l4re_servers L4Re Servers
*
* - \subpage l4re_servers_rtc_driver
*/

View File

@@ -0,0 +1,29 @@
# RTC driver {#l4re_servers_rtc_driver}
The RTC driver can drive various real-time clocks and provides an
interface for other components, e.g., uvmm, to read and write the
time.
It needs access to the hardware, depending on the clock, either via a
vbus or via an I2C device.
## Command Line Options
There are no command line options.
## Environment
Several capabilities can be used to interact with the environment:
* 'rtc' (server)
The capability with which clients talk to the service. Note that writing
to the RTC is only possible when having the rtc capability with write
rights, read-only clients must have the capability with read rights
only.
* 'vbus'
The vbus used to access hardware for port-based clock on X86 and pl031 on arm.
The vbus is also needed for receiving the inhibitor signal from IO.

View File

@@ -0,0 +1,6 @@
PKGDIR = ..
L4DIR ?= $(PKGDIR)/../..
EXTRA_TARGET = rtc
include $(L4DIR)/mk/include.mk

View File

@@ -0,0 +1,24 @@
// vi:ft=cpp
#pragma once
#include <l4/sys/capability>
#include <l4/sys/icu>
#include <l4/sys/cxx/ipc_iface>
namespace L4rtc {
class Rtc : public L4::Kobject_t<Rtc, L4::Icu>
{
public:
typedef l4_uint64_t Time; ///< in ns, relative to l4_rdtsc()
L4_INLINE_RPC(long, get_timer_offset, (Time *offset));
L4_INLINE_RPC(long, set_timer_offset, (Time offset), L4::Ipc::Call_t<L4_CAP_FPAGE_W>);
typedef L4::Typeid::Rpcs<get_timer_offset_t, set_timer_offset_t> Rpcs;
static Time get_timer();
};
}

View File

@@ -0,0 +1,35 @@
/**
* \file rtc/include/rtc.h
* \brief RTC library interface
*
* \date 06/15/2001
* \author Frank Mehnert <fm3@os.inf.tu-dresden.de> */
/*
* (c) 2003-2009 Author(s)
* economic rights: Technische Universität Dresden (Germany)
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#ifndef L4_RTC_RTC_H
#define L4_RTC_RTC_H
#include <l4/sys/compiler.h>
#include <l4/sys/types.h>
#include <l4/sys/l4int.h>
L4_BEGIN_DECLS
L4_CV int
l4rtc_get_offset_to_realtime(l4_cap_idx_t server, l4_uint64_t *nanoseconds);
L4_CV int
l4rtc_set_offset_to_realtime(l4_cap_idx_t server, l4_uint64_t nanoseconds);
L4_CV l4_uint64_t l4rtc_get_timer(void);
L4_END_DECLS
#endif

View File

@@ -0,0 +1,7 @@
PKGDIR ?= ..
L4DIR ?= $(PKGDIR)/../..
TARGET = client libc_backend
include $(L4DIR)/mk/subdir.mk
libc_backend: client

View File

@@ -0,0 +1,7 @@
PKGDIR ?= ../..
L4DIR ?= $(PKGDIR)/../..
TARGET = librtc.a librtc.so
SRC_CC = librtc.cc
include $(L4DIR)/mk/lib.mk

View File

@@ -0,0 +1,53 @@
/**
* \file rtc/lib/client/librtc.cc
* \brief client stub
*
* (c) 2014 Steffen Liebergeld <steffen.liebergeld@kernkonzept.com>
*
* Based on work by Frank Mehnert:
* \date 09/23/2003
* \author Frank Mehnert <fm3@os.inf.tu-dresden.de> */
/*
* (c) 2003-2009 Author(s)
* economic rights: Technische Universität Dresden (Germany)
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#include <l4/sys/err.h>
#include <l4/rtc/rtc.h>
#include <l4/sys/types.h>
#include <l4/rtc/rtc>
#include <l4/re/env>
#include <l4/sys/kip.h>
static inline
l4_uint64_t get_timer()
{
return l4_kip_clock_ns(l4re_kip());
}
L4rtc::Rtc::Time L4rtc::Rtc::get_timer()
{ return ::get_timer(); }
l4_uint64_t l4rtc_get_timer()
{ return get_timer(); }
/**
* Deliver the offset between real time and system's uptime in seconds and
* nanoseconds.
* Some applications want to compute their time in other ways as done
* in l4rtc_get_seconds_since_1970(). */
int
l4rtc_get_offset_to_realtime(l4_cap_idx_t server, l4_uint64_t *nanoseconds)
{
return L4::Cap<L4rtc::Rtc>(server)->get_timer_offset(nanoseconds);
}
int
l4rtc_set_offset_to_realtime(l4_cap_idx_t server, l4_uint64_t nanoseconds)
{
return L4::Cap<L4rtc::Rtc>(server)->set_timer_offset(nanoseconds);
}

View File

@@ -0,0 +1,10 @@
PKGDIR ?= ../..
L4DIR ?= $(PKGDIR)/../..
TARGET = libc_be_rtc.a libc_be_rtc.so
LINK_INCR = libc_be_rtc.a
SRC_CC = gettime.cc
PC_FILENAME = rtc_libc_be
REQUIRES_LIBS= rtc
include $(L4DIR)/mk/lib.mk

View File

@@ -0,0 +1,5 @@
This backend provides a time backend. It is currently based on the TSC,
but could easily be changed to KIP time or another time source.
Currently, this backend does not provide the 'realtime' but just the
system's uptime.

View File

@@ -0,0 +1,52 @@
/*
* (c) 2008-2009 Adam Lackorzynski <adam@os.inf.tu-dresden.de>
* economic rights: Technische Universität Dresden (Germany)
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#include <l4/sys/types.h>
#include <l4/re/env>
#include <l4/libc_backends/clk.h>
#include <l4/rtc/rtc>
#include <l4/crtn/initpriorities.h>
#include <cstdio>
#include "gettime.h"
namespace {
struct Rtc_be
{
L4rtc::Rtc::Time offset;
Rtc_be()
{
libc_backend_rtc_init();
int ret;
offset = 0;
L4::Cap<L4rtc::Rtc> rtc = L4Re::Env::env()->get_cap<L4rtc::Rtc>("rtc");
if (!rtc)
return;
ret = rtc->get_timer_offset(&offset);
// error, assume offset 0
if (ret)
printf("RTC server not found, assuming 1.1.1970, 0:00 ...\n");
}
};
static Rtc_be _rtc_be __attribute__((init_priority(INIT_PRIO_RTC_L4LIBC_INIT)));
}
int libc_backend_rt_clock_gettime(struct timespec *tp)
{
L4rtc::Rtc::Time now = _rtc_be.offset + L4rtc::Rtc::get_timer();
tp->tv_sec = now / 1000000000;
tp->tv_nsec = now % 1000000000;
return 0;
}

View File

@@ -0,0 +1,50 @@
/*!
* \file rtc/lib/libc_backend/time/gettime.h
* \brief architecture specific handling
*
* \date 2007-11-23
* \author Adam Lackorzynski <adam@os.inf.tu-dresden.de>
*
*/
/*
* (c) 2007-2009 Author(s)
* economic rights: Technische Universität Dresden (Germany)
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#ifndef __RTC__LIB__LIBC_BACKEND__TIME__GETTIME_H__
#define __RTC__LIB__LIBC_BACKEND__TIME__GETTIME_H__
#include <l4/re/env.h>
#include <l4/sys/l4int.h>
#if defined(ARCH_x86) || defined(ARCH_amd64)
#include <l4/util/rdtsc.h>
static inline void libc_backend_rtc_init(void)
{
l4_calibrate_tsc(l4re_kip());
}
static inline void libc_backend_rtc_get_s_and_ns(l4_uint32_t *s, l4_uint32_t *ns)
{
l4_tsc_to_s_and_ns(l4_rdtsc(), s, ns);
}
#else
static inline void libc_backend_rtc_init(void)
{
}
static inline void libc_backend_rtc_get_s_and_ns(l4_uint32_t *s, l4_uint32_t *ns)
{
l4_kernel_clock_t c = l4_kip_clock(l4re_kip());
*s = c / 1000000;
*ns = (c % 1000000) * 1000;
}
#endif
#endif /* ! __RTC__LIB__LIBC_BACKEND__TIME__GETTIME_H__ */

View File

@@ -0,0 +1,4 @@
PKGDIR ?= ..
L4DIR ?= $(PKGDIR)/../..
include $(L4DIR)/mk/subdir.mk

View File

@@ -0,0 +1,14 @@
PKGDIR ?= ../..
L4DIR ?= $(PKGDIR)/../..
TARGET = rtc
SRC_CC_x86-l4f = x86.cc
SRC_CC_amd64-l4f = x86.cc
SRC_CC_arm-l4f = pl031.cc
SRC_CC_arm64-l4f = pl031.cc
SRC_CC = main.cc
SRC_CC-$(CONFIG_RTC_DS3231) += ds3231.cc
SRC_CC-$(CONFIG_RTC_PCF85063A) += pcf85063a.cc
REQUIRES_LIBS = libio cxx_libc_io cxx_io libstdc++
include $(L4DIR)/mk/prog.mk

View File

@@ -0,0 +1,22 @@
/*
* Copyright (C) 2025 Kernkonzept GmbH.
* Author(s): Andreas Wiese <andreas.wiese@kernkonzept.com>
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#pragma once
#include <l4/sys/l4int.h>
/* convert binary coded decimal to binary */
static inline l4_uint8_t bcd2bin(l4_uint8_t value)
{
return (value >> 4) * 10 + (value & 0x0f);
}
/* convert binary to binary coded decimal */
static inline l4_uint8_t bin2bcd(l4_uint8_t value)
{
return (((value / 10) % 10) << 4) | (value % 10);
}

View File

@@ -0,0 +1,262 @@
/*
* Copyright (C) 2025 Kernkonzept GmbH.
* Author(s): Martin Kuettler martin.kuettler@kernkonzept.com
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
/**
* \file The DS3231 rtc. Is expected to always live on a I2C-Bus.
*/
// Documentation:
// https://www.analog.com/media/en/technical-documentation/data-sheets/ds3231.pdf
#include "rtc.h"
#include "bcd.h"
#include <l4/re/env>
#include <l4/re/error_helper>
#include <l4/i2c-driver/i2c_device_if.h>
#include <cstdio>
#include <cstring>
#include <time.h>
class DS3231_rtc : public Rtc
{
private:
using raw_t = l4_uint8_t[7];
struct Reg_addr
{
enum
{
Seconds = 0x00,
Minutes = 0x01,
Hours = 0x02,
Wday = 0x03,
Mday = 0x04,
Month_and_century = 0x05,
Year = 0x06,
};
};
// seconds in range 0..59
static int seconds(raw_t data)
{
return bcd2bin(data[Reg_addr::Seconds]);
}
// minutes in range 0..59
static int minutes(raw_t data)
{
return bcd2bin(data[Reg_addr::Minutes]);
}
// hours in range 0..23
static int hours(raw_t data)
{
if (data[Reg_addr::Hours] & 0x40) // am/pm format
{
int const value = data[Reg_addr::Hours];
return (value & 0x0f)
+ ((value & 0x10) ? 10 : 0)
+ ((value & 0x20) ? 12 : 0);
}
else // 24h format
{
int const value = data[Reg_addr::Hours];
return (value & 0x0f)
+ ((value & 0x10) ? 10 : 0)
+ ((value & 0x20) ? 20 : 0);
}
}
// month day in range 0..31
static int mday(raw_t data)
{
int const value = data[Reg_addr::Mday] & 0x3f;
return bcd2bin(value);
}
// month in range 0..11
static int month(raw_t data)
{
return bcd2bin(data[Reg_addr::Month_and_century] & 0x1f) - 1;
}
// year in range 100..299, representing 2000..2199 (offset 1900)
static int year(raw_t data)
{
return bcd2bin(data[Reg_addr::Year])
+ ((data[Reg_addr::Month_and_century] & 0x80) ? 100 : 0)
+ 100;
}
// set the seconds value in data
static void set_seconds(int sec, raw_t data)
{
data[Reg_addr::Seconds] = bin2bcd(sec);
}
// set the minutes value in data
static void set_minutes(int min, raw_t data)
{
data[Reg_addr::Minutes] = bin2bcd(min);
}
// set the hours value in data
static void set_hours(int hours, raw_t data)
{
// this sets the 24h format.
l4_uint8_t val = hours % 10;
if (hours >= 10 && hours <= 19)
val |= 0x10;
else if (hours >= 20)
val |= 0x20;
data[Reg_addr::Hours] = val;
}
// set the week day value in data
static void set_wday(int wday, raw_t data)
{
data[Reg_addr::Wday] = wday;
}
// set the month day value in data
static void set_mday(int mday, raw_t data)
{
data[Reg_addr::Mday] = bin2bcd(mday);
}
// set the month value in data
// the value passed here is in range 0 .. 11
static void set_month(int month, raw_t data)
{
data[Reg_addr::Month_and_century] &= 0xc0;
data[Reg_addr::Month_and_century] |= bin2bcd(month + 1);
}
// set the year value in data
// due to internal encoding this needs to touch two elements of data
static void set_year(int year, raw_t data)
{
// we expect the year as stored in struct tm, i.e., relative to 1900
year = year - 100;
data[Reg_addr::Year] = bin2bcd(year % 100);
if (year > 100)
data[Reg_addr::Month_and_century] |= 0x80;
else
data[Reg_addr::Month_and_century] &= 0x7f;
}
public:
bool probe()
{
_ds3231 = L4Re::Env::env()->get_cap<I2c_device_ops>("ds3231");
if (_ds3231.is_valid())
{
l4_uint64_t time;
if (int err = get_time(&time); err != L4_EOK)
{
printf("get_time() in probe returned %d\n", err);
return false;
}
l4_uint64_t ns = time + l4_kip_clock_ns(l4re_kip());
time_t secs = ns / 1'000'000'000;
char buf[26];
ctime_r(&secs, buf);
printf("Found DS3231 RTC. Current time: %s", buf);
return true;
}
// TODO: check for alternative vbus containing i2c
return false;
}
int set_time(l4_uint64_t offset_nsec)
{
l4_uint64_t const ns = offset_nsec + l4_kip_clock_ns(l4re_kip());
time_t const sec = ns / 1'000'000'000ull;
struct tm time;
gmtime_r(&sec, &time);
raw_t data = {0, 0, 0, 0, 0, 0, 0};
set_year(time.tm_year, data);
set_month(time.tm_mon, data);
set_mday(time.tm_mday, data);
set_wday(time.tm_wday, data);
set_hours(time.tm_hour, data);
set_minutes(time.tm_min, data);
set_seconds(time.tm_sec, data);
if (_ds3231.is_valid())
{
l4_uint8_t send_data[1 + sizeof(data)];
send_data[0] = 0; // reg-addr to write to
memcpy(&send_data[1], data, sizeof(data));
L4::Ipc::Array<l4_uint8_t const> send_buffer{sizeof(send_data),
send_data};
if (int err = _ds3231->write(send_buffer); err != L4_EOK)
{
printf("write time data returned error code %d\n", err);
return err;
}
}
else
{
printf("Direct device access is needed for now\n");
return -L4_ENODEV;
}
return 0;
}
int get_time(l4_uint64_t *offset_nsec)
{
enum : unsigned int
{
Size = 7
};
l4_uint8_t data[Size];
if (_ds3231.is_valid())
{
l4_uint8_t addr = 0;
L4::Ipc::Array<l4_uint8_t const> send_buffer{sizeof(addr), &addr};
L4::Ipc::Array<l4_uint8_t> buffer{Size, data};
if (int err = _ds3231->write_read(send_buffer, buffer.length, buffer);
err != L4_EOK)
{
printf("ERROR: writing and reading register returned %d\n", err);
return err;
}
}
else
{
printf("Direct device access is needed for now\n");
return -L4_ENODEV;
}
struct tm time;
time.tm_sec = seconds(data);
time.tm_min = minutes(data);
time.tm_hour = hours(data);
time.tm_mday = mday(data);
time.tm_mon = month(data);
time.tm_year = year(data);
time.tm_wday = 0; // this is ignored
time.tm_yday = 0;
time.tm_isdst = 0;
time.tm_gmtoff = 0;
time.tm_zone = nullptr;
l4_uint64_t ns = timegm(&time) * 1'000'000'000ull;
*offset_nsec = ns - l4_kip_clock_ns(l4re_kip());
return 0;
}
private:
L4::Cap<I2c_device_ops> _ds3231;
};
static DS3231_rtc _ds3231;

View File

@@ -0,0 +1,373 @@
/**
* \file rtc/server/src/main.cc
* \brief Initialization and main server loop
*
* \date 09/23/2003
* \author Frank Mehnert <fm3@os.inf.tu-dresden.de>
* \author Adam Lackorzynski <adam@os.inf.tu-dresden.de> */
/*
* (c) 2003-2009 Author(s)
* economic rights: Technische Universität Dresden (Germany)
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#include <l4/rtc/rtc>
#if defined ARCH_x86 || defined ARCH_amd64
#include <l4/util/rdtsc.h>
#endif
#include <l4/re/env>
#include <l4/re/inhibitor>
#include <l4/re/util/object_registry>
#include <l4/re/util/br_manager>
#include <l4/re/event>
#include <l4/re/event_enums.h>
#include <l4/re/util/event>
#include <l4/vbus/vbus>
#include <l4/vbus/vbus_inhibitor.h>
#include <l4/cxx/exceptions>
#include <l4/re/error_helper>
#include <l4/re/util/cap_alloc>
#include <l4/sys/factory>
#include <l4/sys/cxx/ipc_epiface>
#include <l4/cxx/l4iostream>
#include <l4/cxx/iostream>
#include <l4/sys/ipc_gate>
#include <l4/sys/irq>
#include <cstdlib>
#include <cstdio>
#include <vector>
#include <l4/cxx/hlist>
#include "rtc.h"
cxx::H_list_t<Rtc> Rtc::_rtcs(true); // allocate in BSS w/o ctor
struct Time_update_observer : cxx::H_list_item_t<Time_update_observer>
{
virtual void update_time() = 0;
virtual ~Time_update_observer() = 0;
};
inline Time_update_observer::~Time_update_observer() {}
class Clock
{
private:
l4_uint64_t _offset_1970;
bool _valid;
Rtc *_rtc;
cxx::H_list_t<Time_update_observer> _observers;
public:
explicit Clock(Rtc *rtc)
: _offset_1970(0), _valid(false), _rtc(rtc)
{
update_clock();
}
void add_observer(Time_update_observer *o) { _observers.add(o); }
l4_uint64_t get_offset_1970() const
{ return _offset_1970; }
void update_clock()
{
l4_uint64_t offset;
int err = _rtc->get_time(&offset);
_valid = false;
if (err < 0)
printf("error: could not read hardware clock: %d\n", err);
else
{
_offset_1970 = offset;
_valid = true;
for (auto o = _observers.begin(); o != _observers.end(); ++o)
o->update_time();
}
}
void set_offset_1970(l4_uint64_t offset)
{
_offset_1970 = offset;
int err = _rtc->set_time(offset);
if (err < 0)
{
printf("error: could not set hardware clock: %d\n", err);
_valid = false;
}
for (auto o = _observers.begin(); o != _observers.end(); ++o)
o->update_time();
}
bool valid() const { return _valid; }
};
class Rtc_svr :
public L4::Epiface_t<Rtc_svr, L4rtc::Rtc>,
public Time_update_observer
{
private:
Clock *_clock;
void add_client(L4Re::Util::Unique_cap<L4::Irq> irq)
{ _client_irqs.push_back(std::move(irq)); }
void remove_client(L4::Cap<L4::Irq> irq)
{
auto task = L4Re::Env::env()->task();
for (auto it = _client_irqs.begin(); it != _client_irqs.end();)
{
l4_msgtag_t msg = task->cap_equal(it->get(), irq);
if (msg.label() == 1)
it = _client_irqs.erase(it);
else
++it;
}
}
public:
explicit
Rtc_svr(Clock *clock) : _clock(clock)
{ _clock->add_observer(this); }
void notify_clients()
{
for (auto i = _client_irqs.begin(); i != _client_irqs.end();)
{
if (auto err = l4_ipc_error((*i)->trigger(), l4_utcb()))
if (err == L4_IPC_ENOT_EXISTENT)
{
// Robustness. If a client did not call unbind(), we will have
// a stale IRQ capability. Here we clean up our list.
i = _client_irqs.erase(i);
continue;
}
++i;
}
}
void update_time() { notify_clients(); }
long op_get_timer_offset(L4rtc::Rtc::Rights, L4rtc::Rtc::Time &offset)
{
offset = _clock->get_offset_1970();
return _clock->valid() ? 0 : 1;
}
long op_set_timer_offset(L4rtc::Rtc::Rights, L4rtc::Rtc::Time offset)
{
_clock->set_offset_1970(offset);
return _clock->valid() ? 0 : 1;
}
long op_bind(L4::Icu::Rights, unsigned irqnum,
L4::Ipc::Snd_fpage irq)
{
if (irqnum != 0)
return -L4_ERANGE;
if (!irq.cap_received())
return -L4_EINVAL;
L4::Cap<L4::Irq> irqc = server_iface()->rcv_cap<L4::Irq>(0);
if (!irqc.is_valid())
return -L4_EINVAL; // server internal error, anyway
l4_msgtag_t msg = L4Re::Env::env()->task()->cap_valid(irqc);
if (msg.label() == 0)
return -L4_EINVAL; // no cap received, bail out
// The received capability is implicitly reference counted in the kernel.
// If the client vanishes without calling unbind(), this can cause the
// kernel to not discard the IRQ object because the RTC server still holds
// a reference.
// Here we remap the capability as a "weak" --non-reference-counted--
// capability. We do that with a grant operation, which both maps the IRQ
// object to a new capability and unmaps the received capability. This
// ensures that the RTC server does not hold a reference to the IRQ kernel
// object.
auto cap = L4Re::Util::make_unique_cap<L4::Irq>();
if (!cap)
return -L4_ENOMEM;
auto task = L4Re::Env::env()->task();
if (l4_error(task->map(task, irqc.fpage(L4_CAP_FPAGE_RWSD),
l4_map_obj_control(cap.cap(), L4_MAP_ITEM_GRANT)
| L4_FPAGE_C_OBJ_RIGHTS | L4_FPAGE_C_NO_REF_CNT)))
return -L4_EINVAL;
add_client(std::move(cap));
return L4_EOK;
}
long op_unbind(L4::Icu::Rights , unsigned, L4::Ipc::Snd_fpage irq)
{
if (!irq.cap_received())
return -L4_EINVAL;
L4::Cap<L4::Irq> irqc = server_iface()->rcv_cap<L4::Irq>(0);
if (!irqc.is_valid())
return -L4_EINVAL;
remove_client(irqc);
// We must unmap our cap to free our reference. Otherwise kernel objects
// that should be discarded, will not be discarded.
L4Re::Env::env()->task()->unmap(irqc.fpage(), L4_FP_ALL_SPACES);
return L4_EOK;
}
long op_info(L4::Icu::Rights, L4::Icu::_Info &info)
{
info.features = 0;
info.nr_irqs = 1;
info.nr_msis = 0;
return 0;
}
long op_msi_info(L4::Icu::Rights, l4_umword_t, l4_uint64_t,
l4_icu_msi_info_t &)
{
return -L4_ENOSYS;
}
long op_mask(L4::Icu::Rights, unsigned)
{
return -L4_ENOREPLY;
}
long op_unmask(L4::Icu::Rights, unsigned)
{
return -L4_ENOREPLY;
}
long op_set_mode(L4::Icu::Rights, unsigned, l4_umword_t)
{
return 0;
}
private:
std::vector<L4Re::Util::Unique_cap<L4::Irq> > _client_irqs;
};
#if 0
case L4RTC_OPCODE_register_irq:
{
L4::Cap<L4::Irq> irqc = server_iface()->rcv_cap<L4::Irq>(0);
if (!irqc.is_valid())
return -L4_EINVAL;
msg = L4Re::Env::env()->task()->cap_valid(irqc);
if (msg.label() == 0)
return -L4_EINVAL;
client_manager.add_client(irqc);
server_iface()->realloc_rcv_cap(0);
return L4_EOK;
}
default:
return -L4_ENOSYS;
}
}
#endif
class System_state_tracker :
public L4::Irqep_t<System_state_tracker>,
public Clock
{
public:
explicit
System_state_tracker(Rtc *rtc);
void handle_irq();
L4::Cap<L4::Irq> event_irq() const
{ return L4::cap_cast<L4::Irq>(_vbus_event.irq()); }
private:
L4Re::Util::Event _vbus_event;
L4::Cap<L4Re::Inhibitor> _vbus_inhibitor;
};
System_state_tracker::System_state_tracker(Rtc *rtc) : Clock(rtc)
{
// get vbus (contains rtc ioports on x86, might be empty on arm)
// We need the vbus for the inhibitors!
L4::Cap<L4vbus::Vbus> vbus = L4Re::chkcap(L4Re::Env::env()->get_cap<L4vbus::Vbus>("vbus"),
"Did not find cap 'vbus'");
_vbus_inhibitor = vbus;
_vbus_inhibitor->acquire(L4VBUS_INHIBITOR_SUSPEND,
"I need to invalidate my timestamp upon system suspend");
_vbus_inhibitor->acquire(L4VBUS_INHIBITOR_WAKEUP,
"I need to get a fresh timestamp on system wakeup");
_vbus_event.init<L4::Irq>(vbus);
}
inline void
System_state_tracker::handle_irq()
{
L4Re::Event_buffer::Event *e;
while ((e = _vbus_event.buffer().next()) != NULL)
{
auto type = e->payload.type;
auto code = e->payload.code;
e->free();
if (type != L4RE_EV_PM)
continue;
switch (code)
{
case L4VBUS_INHIBITOR_SUSPEND:
printf("Received suspend event.\n");
_vbus_inhibitor->release(L4VBUS_INHIBITOR_SUSPEND);
break;
case L4VBUS_INHIBITOR_WAKEUP:
printf("Received wakeup event.\n");
_vbus_inhibitor->acquire(L4VBUS_INHIBITOR_SUSPEND,
"I need to invalidate my timestamp upon system suspend");
update_clock();
break;
default:
break;
}
}
}
static L4Re::Util::Registry_server<L4Re::Util::Br_manager_hooks> server;
int
main()
{
Rtc *rtc;
if (!(rtc = Rtc::find_rtc()))
{
printf("RTC: Initialization failed, exiting\n");
return 1;
}
try
{
static System_state_tracker tracker(rtc);
static Rtc_svr rtc_server(&tracker);
L4Re::chkcap(server.registry()->register_obj(&tracker, tracker.event_irq()),
"Could not register state tracker");
L4Re::chkcap(server.registry()->register_obj(&rtc_server, "rtc"),
"Could not register RTC server. 'rtc' cap missing?");
}
catch (L4::Runtime_error const &e)
{
L4::cerr << e << "TERMINATED\n";
abort();
}
server.loop();
}

View File

@@ -0,0 +1,212 @@
/*
* Copyright (C) 2025 Kernkonzept GmbH.
* Author(s): Martin Kuettler <martin.kuettler@kernkonzept.com>
* Andreas Wiese <andreas.wiese@kernkonzept.com>
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
/**
* \file The PCF85063A rtc. Is expected to always live on the I2C-Bus.
*/
// Documentation:
// https://www.nxp.com/docs/en/data-sheet/PCF85063A.pdf
#include "rtc.h"
#include "bcd.h"
#include <l4/re/env>
#include <l4/util/util.h>
#include <l4/re/error_helper>
#include <l4/i2c-driver/i2c_device_if.h>
#include <cstdio>
#include <cstring>
#include <time.h>
class PCF85063A_rtc : public Rtc
{
private:
struct Reg_addr
{
enum
{
Control_1 = 0x00,
Control_2 = 0x01,
Offset = 0x02,
RAM_byte = 0x03,
Seconds = 0x04,
Minutes = 0x05,
Hours = 0x06,
Mday = 0x07,
Wday = 0x08,
Month = 0x09,
Year = 0x0a,
Reg_size = 11,
};
};
using raw_t = l4_uint8_t[Reg_addr::Reg_size];
/* convert register file to struct tm */
static void raw2tm(raw_t data, struct tm &tm)
{
memset(&tm, 0, sizeof(tm));
tm.tm_sec = bcd2bin(data[Reg_addr::Seconds] & 0x7f);
tm.tm_min = bcd2bin(data[Reg_addr::Minutes] & 0x7f);
tm.tm_hour =
data[Reg_addr::Control_1] & 0x02 // 12hr mode?
? (bcd2bin(data[Reg_addr::Hours] & 0x1f) % 12
+ (data[Reg_addr::Hours] & 0x20 ? 12 : 0))
: bcd2bin(data[Reg_addr::Hours] & 0x3f);
tm.tm_mday = bcd2bin(data[Reg_addr::Mday] & 0x3f);
tm.tm_mon = bcd2bin(data[Reg_addr::Month] & 0x1f) - 1;
tm.tm_year = bcd2bin(data[Reg_addr::Year]) + 100;
tm.tm_wday = bcd2bin(data[Reg_addr::Wday] & 0x07);
// missing, hence 0: tm_yday, tm_isdst, tm_gmtoff, tm_zone
}
/* convert struct tm to register file, touching only those bits which
actually are date and time, leaving the others intact */
static void tm2raw(struct tm &tm, raw_t &data)
{
/* don't mask out OS bit, but explicitly clear it */
data[Reg_addr::Seconds] = 0;
data[Reg_addr::Seconds] |= 0x7f & bin2bcd(tm.tm_sec);
data[Reg_addr::Minutes] &= ~0x7f;
data[Reg_addr::Minutes] |= 0x7f & bin2bcd(tm.tm_min);
data[Reg_addr::Hours] &= ~0x3f;
if (data[Reg_addr::Control_1] & 0x02) /* 12hr mode */
{
/* Linux ignores the existence of this, maybe we should, too. */
l4_uint8_t hr = tm.tm_hour;
l4_uint8_t pmbit = hr / 12 ? 0x20 : 0;
if ((hr %= 12) == 0) hr = 12;
data[Reg_addr::Hours] |= 0x3f & (pmbit | bin2bcd(hr));
}
else
{
data[Reg_addr::Hours] |= 0x3f & bin2bcd(tm.tm_hour);
}
data[Reg_addr::Mday] &= ~0x3f;
data[Reg_addr::Mday] |= 0x3f & bin2bcd(tm.tm_mday);
data[Reg_addr::Wday] &= ~0x07;
data[Reg_addr::Wday] |= 0x07 & bin2bcd(tm.tm_wday);
data[Reg_addr::Month] &= ~0x1f;
data[Reg_addr::Month] |= 0x1f & bin2bcd(tm.tm_mon + 1);
data[Reg_addr::Year] = bin2bcd(tm.tm_year - 100);
}
private:
L4::Cap<I2c_device_ops> _pcf85063a;
/* read register file */
inline int read_data(raw_t &data)
{
l4_uint8_t addr = 0;
L4::Ipc::Array<l4_uint8_t const> send_buffer{sizeof(addr), &addr};
L4::Ipc::Array<l4_uint8_t> buffer{sizeof(data), data};
if (int err = _pcf85063a->write_read(send_buffer, buffer.length, buffer);
err != L4_EOK)
{
printf("ERROR: writing and reading register returned %d\n", err);
return err;
}
return L4_EOK;
}
/* write register file */
inline int write_data(raw_t &data)
{
l4_uint8_t buf[sizeof(data) + 1] = { 0, };
memcpy(buf+1, data, sizeof(data));
L4::Ipc::Array<l4_uint8_t const> send_buffer{sizeof(buf), buf};
if (int err = _pcf85063a->write(send_buffer); err != L4_EOK)
{
printf("ERROR: writing registers returned %d\n", err);
return err;
}
return L4_EOK;
}
public:
bool probe()
{
_pcf85063a = L4Re::Env::env()->get_cap<I2c_device_ops>("pcf85063a");
if (!_pcf85063a.is_valid())
return false;
l4_uint64_t nsecs;
if (int err = get_time(&nsecs); err != L4_EOK)
return false;
nsecs += l4_kip_clock_ns(l4re_kip());
{
time_t secs = nsecs / 1'000'000'000ull;
char buf[26];
ctime_r(&secs, buf); /* time + '\n' */
printf("Found PCF85063A RTC reports time is %s", buf);
};
return true;
}
int get_time(l4_uint64_t *offset_nsec)
{
raw_t data;
if (!_pcf85063a.is_valid())
return -L4_ENODEV;
if (int err = read_data(data); err != L4_EOK)
return err;
if (data[Reg_addr::Seconds] & 0x80)
printf("WARNING: PCF85063A power-loss detected, time is invalid\n");
struct tm stime;
raw2tm(data, stime);
l4_uint64_t ns = timegm(&stime) * 1'000'000'000ull;
*offset_nsec = ns - l4_kip_clock_ns(l4re_kip());
return 0;
}
int set_time(l4_uint64_t offset_nsec)
{
raw_t data;
struct tm stime;
if (!_pcf85063a.is_valid())
return -L4_ENODEV;
if (int err = read_data(data); err != L4_EOK)
return err;
l4_uint64_t ns = l4_kip_clock_ns(l4re_kip()) + offset_nsec;
time_t const secs = ns / 1'000'000'000ull;
gmtime_r(&secs, &stime);
tm2raw(stime, data);
if (int err = write_data(data); err != L4_EOK)
return err;
/* TODO: double-check whether OS flag cleared? */
return 0;
}
};
static PCF85063A_rtc _pcf85063a;

View File

@@ -0,0 +1,96 @@
/*
* Copyright (C) 2022-2024 Kernkonzept GmbH.
* Author(s): Steffen Liebergeld <steffen.liebergeld@kernkonzept.com>
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
/*
* Simplistic driver for the pl031 RTC. Does not support write.
*/
#include <l4/re/env>
#include <l4/re/rm>
#include <l4/re/error_helper>
#include <l4/util/util.h>
#include <l4/io/io.h>
#include <l4/vbus/vbus>
#include <l4/drivers/hw_mmio_register_block>
#include <cstdio>
#include <time.h>
#include "rtc.h"
static bool debug = false;
struct Pl031_rtc : Rtc
{
bool probe()
{
auto vbus = L4Re::Env::env()->get_cap<L4vbus::Vbus>("vbus");
if (!vbus.is_valid())
return false;
L4vbus::Device dev;
l4vbus_device_t devinfo;
int found_dev = 0;
while (vbus->root().next_device(&dev, L4VBUS_MAX_DEPTH, &devinfo) == 0)
if ((found_dev = dev.is_compatible("arm,pl031")) == 1)
break;
if (found_dev <= 0)
return false;
for (unsigned i = 0; i < devinfo.num_resources; ++i)
{
l4vbus_resource_t res;
L4Re::chksys(dev.get_resource(i, &res),
"Get shared memory resources");
if (res.type != L4VBUS_RESOURCE_MEM)
continue;
auto iods =
L4::Ipc::make_cap_rw(L4::cap_reinterpret_cast<L4Re::Dataspace>(vbus));
l4_size_t sz = res.end - res.start + 1;
l4_addr_t addr = 0;
if (debug)
printf("Found resource %p..%p, sz=0x%zx\n",
(void*)res.start, (void*)res.end, sz);
const L4Re::Env *env = L4Re::Env::env();
L4Re::chksys(env->rm()->attach(&addr, sz,
L4Re::Rm::F::Search_addr
| L4Re::Rm::F::Cache_uncached
| L4Re::Rm::F::RW,
iods, res.start, L4_PAGESHIFT),
"Attach rtc io memory");
_regs = new L4drivers::Mmio_register_block<32>(addr);
time_t t = _regs[0];
printf("Found a pl031 device. Current time: %s", ctime(&t));
break;
}
return true;
};
int set_time(l4_uint64_t /* offset */)
{
return 0;
}
int get_time(l4_uint64_t *nsec_offset_1970)
{
l4_uint64_t ns = ((l4_uint64_t)_regs[0]) * 1000000000;
*nsec_offset_1970 = ns - l4_kip_clock_ns(l4re_kip());
return 0;
}
private:
L4drivers::Register_block<32> _regs;
};
static Pl031_rtc _pl031_rtc;

View File

@@ -0,0 +1,43 @@
/**
* \file
* \brief Shared header file
*
* \date
* \author Adam Lackorzynski <adam@os.inf.tu-dresden.de> */
/*
* (c) 2014
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#pragma once
#include <sys/cdefs.h>
#include <l4/sys/l4int.h>
#include <l4/cxx/hlist>
struct Rtc : cxx::H_list_item_t<Rtc>
{
virtual int set_time(l4_uint64_t nsec_offset_1970) = 0;
virtual int get_time(l4_uint64_t *nsec_offset_1970) = 0;
virtual bool probe() = 0;
virtual ~Rtc() = 0;
static Rtc* find_rtc()
{
for (auto o = _rtcs.begin(); o != _rtcs.end(); ++o)
{
if (o->probe())
return *o;
}
return 0;
}
Rtc() { _rtcs.add(this); }
private:
static cxx::H_list_t<Rtc> _rtcs;
};
inline Rtc::~Rtc() {}

View File

@@ -0,0 +1,352 @@
/**
* \file rtc/server/src/x86.c
* \brief Get current data and time from CMOS
*
* \date 09/23/2003
* \author Frank Mehnert <fm3@os.inf.tu-dresden.de> */
/*
* (c) 2003-2009 Author(s)
* economic rights: Technische Universität Dresden (Germany)
*
* License: see LICENSE.spdx (in this directory or the directories above)
*/
/* most stuff taken from Linux 2.4.22 */
#include <stdio.h>
#include <l4/util/port_io.h>
#include <l4/re/env>
#include <l4/util/util.h>
#include <l4/io/io.h>
#include "rtc.h"
#define RTC_SECONDS 0
#define RTC_SECONDS_ALARM 1
#define RTC_MINUTES 2
#define RTC_MINUTES_ALARM 3
#define RTC_HOURS 4
#define RTC_HOURS_ALARM 5
#define RTC_DAY_OF_WEEK 6
#define RTC_DAY_OF_MONTH 7
#define RTC_MONTH 8
#define RTC_YEAR 9
#define RTC_REG_A 10
#define RTC_REG_B 11
#define RTC_REG_C 12
#define RTC_REG_D 13
#define RTC_FREQ_SELECT RTC_REG_A
# define RTC_UIP 0x80
# define RTC_DIV_CTL 0x70
# define RTC_REF_CLCK_4MHZ 0x00
# define RTC_REF_CLCK_1MHZ 0x10
# define RTC_REF_CLCK_32KHZ 0x20
# define RTC_DIV_RESET1 0x60
# define RTC_DIV_RESET2 0x70
# define RTC_RATE_SELECT 0x0F
#define RTC_CONTROL RTC_REG_B
# define RTC_SET 0x80
# define RTC_PIE 0x40
# define RTC_AIE 0x20
# define RTC_UIE 0x10
# define RTC_SQWE 0x08
# define RTC_DM_BINARY 0x04
# define RTC_24H 0x02
# define RTC_DST_EN 0x01
#define RTC_PORT(x) (0x70 + (x))
#define RTC_ALWAYS_BCD 1 /* RTC operates in binary mode */
#define sleep_as_iodelay_port80() l4_sleep(0)
#define CMOS_READ(addr) ({ \
l4_uint8_t val; \
l4util_out8(addr, RTC_PORT(0)); \
sleep_as_iodelay_port80(); \
val = l4util_in8(RTC_PORT(1)); \
sleep_as_iodelay_port80(); \
val; \
})
#define CMOS_WRITE(val, addr) ({ \
l4util_out8(addr, RTC_PORT(0)); \
sleep_as_iodelay_port80(); \
l4util_out8(val, RTC_PORT(1)); \
sleep_as_iodelay_port80(); \
})
#define BCD_TO_BIN(val) ((val)=((val)&15) + ((val)>>4)*10)
#define BIN_TO_BCD(val) ((((val)/10)<<4) + (val)%10)
/* Converts Gregorian date to seconds since 1970-01-01 00:00:00.
* Assumes input in normal date format, i.e. 1980-12-31 23:59:59
* => year=1980, mon=12, day=31, hour=23, min=59, sec=59.
*
* [For the Julian calendar (which was used in Russia before 1917,
* Britain & colonies before 1752, anywhere else before 1582,
* and is still in use by some communities) leave out the
* -year/100+year/400 terms, and add 10.]
*
* This algorithm was first published by Gauss (I think).
*
* WARNING: this function will overflow on 2106-02-07 06:28:16 on
* machines were long is 32-bit! (However, as time_t is signed, we
* will already get problems at other places on 2038-01-19 03:14:08) */
static inline l4_uint32_t
mktime (l4_uint32_t year, l4_uint32_t mon, l4_uint32_t day,
l4_uint32_t hour, l4_uint32_t min, l4_uint32_t sec)
{
if (0 >= (int) (mon -= 2))
{
/* 1..12 -> 11,12,1..10 */
mon += 12; /* puts Feb last since it has leap day */
year -= 1;
}
return ((( (year/4 - year/100 + year/400 + 367*mon/12 + day)
+ year*365 - 719499
)*24 + hour /* now have hours */
)*60 + min /* now have minutes */
)*60 + sec; /* finally seconds */
}
/*
* Get current time from CMOS and initialize values.
*/
static int
get_base_time_x86(l4_uint64_t *offset)
{
l4_uint32_t year, mon, day, hour, min, sec;
l4_uint64_t time;
long i;
/* The Linux interpretation of the CMOS clock register contents:
* When the Update-In-Progress (UIP) flag goes from 1 to 0, the
* RTC registers show the second which has precisely just started.
* Let's hope other operating systems interpret the RTC the same way. */
/* read RTC exactly on falling edge of update flag */
for (i=0 ; i<1000000 ; i++) /* may take up to 1 second... */
if (CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP)
break;
for (i=0 ; i<1000000 ; i++) /* must try at least 2.228 ms */
if (!(CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP))
break;
do
{
time = l4_kip_clock_ns(l4re_kip());
sec = CMOS_READ(RTC_SECONDS);
min = CMOS_READ(RTC_MINUTES);
hour = CMOS_READ(RTC_HOURS);
day = CMOS_READ(RTC_DAY_OF_MONTH);
mon = CMOS_READ(RTC_MONTH);
year = CMOS_READ(RTC_YEAR);
} while (sec != CMOS_READ(RTC_SECONDS));
if (!(CMOS_READ(RTC_CONTROL) & RTC_DM_BINARY) || RTC_ALWAYS_BCD)
{
BCD_TO_BIN(sec);
BCD_TO_BIN(min);
BCD_TO_BIN(hour);
BCD_TO_BIN(day);
BCD_TO_BIN(mon);
BCD_TO_BIN(year);
}
if ((year += 1900) < 1970)
year += 100;
l4_uint64_t seconds_since_1970 = mktime(year, mon, day, hour, min, sec);
printf("Date:%02d.%02d.%04d Time:%02d:%02d:%02d\n",
day, mon, year, hour, min, sec);
*offset = seconds_since_1970 * 1000000000 - time;
return 0;
}
struct rtc_time {
int tm_sec;
int tm_min;
int tm_hour;
int tm_mday;
int tm_mon;
int tm_year;
int tm_wday;
int tm_yday;
int tm_isdst;
};
static inline void gettime(l4_uint32_t *s, l4_uint32_t *ns)
{
l4_uint64_t time = l4_kip_clock_ns(l4re_kip());
*s = time / 1000000000;
*ns = time % 1000000000;
}
/*
* taken from Linux 3.17
*/
#define LEAPS_THRU_END_OF(y) ((y)/4 - (y)/100 + (y)/400)
static inline bool is_leap_year(unsigned int year)
{
return (!(year % 4) && (year % 100)) || !(year % 400);
}
static const unsigned char rtc_days_in_month[] = {
31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
};
#define LEAPS_THRU_END_OF(y) ((y)/4 - (y)/100 + (y)/400)
/*
* The number of days in the month.
*/
static int rtc_month_days(unsigned int month, unsigned int year)
{
return rtc_days_in_month[month] + (is_leap_year(year) && month == 1);
}
/*
* Convert seconds since 01-01-1970 00:00:00 to Gregorian date.
*/
static void rtc_time_to_tm(unsigned long time, struct rtc_time *tm)
{
unsigned int month, year;
int days;
days = time / 86400;
time -= (unsigned int) days * 86400;
/* day of the week, 1970-01-01 was a Thursday */
tm->tm_wday = (days + 4) % 7;
year = 1970 + days / 365;
days -= (year - 1970) * 365
+ LEAPS_THRU_END_OF(year - 1)
- LEAPS_THRU_END_OF(1970 - 1);
if (days < 0) {
year -= 1;
days += 365 + is_leap_year(year);
}
tm->tm_year = year - 1900;
tm->tm_yday = days + 1;
for (month = 0; month < 11; month++) {
int newdays;
newdays = days - rtc_month_days(month, year);
if (newdays < 0)
break;
days = newdays;
}
tm->tm_mon = month;
tm->tm_mday = days + 1;
tm->tm_hour = time / 3600;
time -= tm->tm_hour * 3600;
tm->tm_min = time / 60;
tm->tm_sec = time - tm->tm_min * 60;
tm->tm_isdst = 0;
}
/* Set the current date and time in the real time clock. */
static inline int __set_rtc_time(struct rtc_time *time)
{
unsigned char mon, day, hrs, min, sec;
unsigned char save_control, save_freq_select;
unsigned int yrs;
yrs = time->tm_year;
mon = time->tm_mon + 1; /* tm_mon starts at zero */
day = time->tm_mday;
hrs = time->tm_hour;
min = time->tm_min;
sec = time->tm_sec;
if (yrs > 255) /* They are unsigned */
return -L4_EINVAL;
/* These limits and adjustments are independent of
* whether the chip is in binary mode or not.
*/
if (yrs > 169)
return -L4_EINVAL;
if (yrs >= 100)
yrs -= 100;
if (!(CMOS_READ(RTC_CONTROL) & RTC_DM_BINARY)
|| RTC_ALWAYS_BCD) {
sec = BIN_TO_BCD(sec);
min = BIN_TO_BCD(min);
hrs = BIN_TO_BCD(hrs);
day = BIN_TO_BCD(day);
mon = BIN_TO_BCD(mon);
yrs = BIN_TO_BCD(yrs);
}
save_control = CMOS_READ(RTC_CONTROL);
CMOS_WRITE((save_control|RTC_SET), RTC_CONTROL);
save_freq_select = CMOS_READ(RTC_FREQ_SELECT);
CMOS_WRITE((save_freq_select|RTC_DIV_RESET2), RTC_FREQ_SELECT);
CMOS_WRITE(yrs, RTC_YEAR);
CMOS_WRITE(mon, RTC_MONTH);
CMOS_WRITE(day, RTC_DAY_OF_MONTH);
CMOS_WRITE(hrs, RTC_HOURS);
CMOS_WRITE(min, RTC_MINUTES);
CMOS_WRITE(sec, RTC_SECONDS);
CMOS_WRITE(save_control, RTC_CONTROL);
CMOS_WRITE(save_freq_select, RTC_FREQ_SELECT);
return 0;
}
struct X86_rtc : Rtc
{
bool probe()
{
printf("probe x86 RTC\n");
if (long i = l4io_request_ioport(RTC_PORT(0), 2))
{
printf("Could not get required port %x and %x, error: %lx\n",
RTC_PORT(0), RTC_PORT(0) + 1, i);
return false;
}
return true;
};
/**
* Take the given offset to 01-01-1970, calculate the current time using the
* kip_clock, and write it into the rtc.
*/
int set_time(l4_uint64_t offset)
{
l4_uint32_t s, ns, current_time;
l4_uint32_t offset_ns = offset % 1000000000;
l4_uint32_t offset_s = offset / 1000000000;
do {
gettime(&s, &ns);
} while ((offset_ns / 8) != (ns / 8));
current_time = offset_s + s;
struct rtc_time time;
rtc_time_to_tm(current_time, &time);
return __set_rtc_time(&time);
}
int get_time(l4_uint64_t *nsec_offset_1970)
{ return get_base_time_x86(nsec_offset_1970); }
};
static X86_rtc _x86_rtc;