libports: system RTC server and test

For now in libports because it relies on contrib sources from musl
doing the time data conversion.

Issue #3450
This commit is contained in:
Josef Söntgen 2019-07-15 17:32:20 +02:00 committed by Christian Helmuth
parent 0037edfeee
commit 99cb585b6e
6 changed files with 587 additions and 0 deletions

View File

@ -0,0 +1,99 @@
# RTC test
assert_spec x86
if {[expr ![have_include power_on/qemu]]} {
puts "Test requires Qemu."
exit 0
}
set build_components {
core init timer drivers/rtc server/system_rtc test/system_rtc
server/report_rom
}
build $build_components
create_boot_directory
set config {
<config prio_levels="2" verbose="yes">
<parent-provides>
<service name="ROM"/>
<service name="IRQ"/>
<service name="IO_MEM"/>
<service name="IO_PORT"/>
<service name="PD"/>
<service name="RM"/>
<service name="CPU"/>
<service name="LOG"/>
</parent-provides>
<default-route>
<any-service> <parent/> <any-child/> </any-service>
</default-route>
<default caps="100"/>
<start name="timer">
<resource name="RAM" quantum="1M"/>
<provides> <service name="Timer"/> </provides>
</start>
<start name="report_rom">
<resource name="RAM" quantum="1M"/>
<provides> <service name="Report"/> <service name="ROM"/> </provides>
<config verbose="yes">
<policy label="rtc_drv -> set_rtc" report="test-system_rtc -> drv_set_rtc"/>
<policy label="system_rtc -> set_rtc" report="test-system_rtc -> sys_set_rtc"/>
</config>
</start>
<start name="rtc_drv" priority="-1">
<resource name="RAM" quantum="1M"/>
<provides><service name="Rtc"/></provides>
<config allow_setting_rtc="true"/>
<route>
<service name="ROM" label="set_rtc">
<child name="report_rom"/>
</service>
<any-service> <parent/> </any-service>
</route>
</start>
<start name="system_rtc" priority="-1">
<resource name="RAM" quantum="1M"/>
<provides><service name="Rtc"/></provides>
<config allow_setting_rtc="true"/>
<route>
<service name="ROM" label="set_rtc"> <child name="report_rom"/> </service>
<service name="Rtc"> <child name="rtc_drv"/> </service>
<service name="Timer"> <child name="timer"/> </service>
<any-service> <parent/> </any-service>
</route>
</start>
<start name="test-system_rtc" priority="-1">
<resource name="RAM" quantum="1M"/>
<config/>
<route>
<service name="Report"> <child name="report_rom"/> </service>
<service name="Rtc"> <child name="system_rtc"/> </service>
<service name="Timer"> <child name="timer"/> </service>
<any-service> <parent/> </any-service>
</route>
</start>
</config>}
install_config $config
set boot_components {
core ld.lib.so init timer rtc_drv test-system_rtc system_rtc
report_rom
}
build_boot_image $boot_components
append qemu_args " -nographic "
run_genode_until ".*--- system RTC test finished ---.*\n" 60

View File

@ -0,0 +1,17 @@
The system RTC component provides access to a synthetic real time clock. While
it can get its initial value from a hardware RTC, it will interpolate the time
by using a Timer session at runtime.
Like the RTC driver component, if the 'allow_setting_rtc' attribute in the
'config' node is set to 'yes', it will use the content of the 'set_rtc' ROM to
update the RTC value. A valid ROM must contain a top node with the following
attributes:
* 'year' (e.g. 2019)
* 'month' (1 - 12)
* 'day' (1 - 31)
* 'hour' (0 - 23)
* 'minute' (0 - 59)
* 'second' (0 - 59)
The component will always report the time as 24h day in UTC.

View File

@ -0,0 +1,291 @@
/*
* \brief System RTC server
* \author Josef Soentgen
* \date 2019-07-15
*/
/*
* Copyright (C) 2019 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
/* Genode includes */
#include <base/attached_rom_dataspace.h>
#include <base/component.h>
#include <base/heap.h>
#include <base/registry.h>
#include <root/component.h>
#include <rtc_session/connection.h>
#include <timer_session/connection.h>
/* local includes */
namespace Contrib {
#include <tm.h>
}; /* Contrib */
namespace Util {
Genode::int64_t convert(Rtc::Timestamp const &ts)
{
Contrib::tm tm;
Genode::memset(&tm, 0, sizeof(Contrib::tm));
tm.tm_sec = ts.second;
tm.tm_min = ts.minute;
tm.tm_hour = ts.hour;
tm.tm_mday = ts.day;
tm.tm_mon = ts.month - 1;
tm.tm_year = ts.year - 1900;
return Contrib::tm_to_secs(&tm);
}
Rtc::Timestamp convert(Genode::int64_t t)
{
Contrib::tm tm;
Genode::memset(&tm, 0, sizeof(Contrib::tm));
int const err = Contrib::secs_to_tm((long long)t, &tm);
if (err) { Genode::warning("could not convert timestamp"); }
return Rtc::Timestamp {
.microsecond = 0,
.second = err ? 0 : (unsigned)tm.tm_sec,
.minute = err ? 0 : (unsigned)tm.tm_min,
.hour = err ? 0 : (unsigned)tm.tm_hour,
.day = err ? 1 : (unsigned)tm.tm_mday,
.month = err ? 1 : (unsigned)tm.tm_mon + 1,
.year = err ? 1970 : (unsigned)tm.tm_year + 1900
};
}
struct Point_in_time
{
Genode::int64_t rtc_seconds;
Genode::int64_t curr_seconds;
};
Rtc::Timestamp generate(Point_in_time const &p, Genode::uint64_t secs)
{
Genode::int64_t const s = ((Genode::int64_t)secs - p.curr_seconds) + p.rtc_seconds;
return convert(s);
}
} /* namespace Util */
namespace Rtc {
using namespace Genode;
struct Time;
struct Session_component;
struct Root;
struct Main;
} /* namespace Rtc */
struct Rtc::Time
{
Env &_env;
Signal_context_capability _notify_sigh;
Timer::Connection _timer { _env };
Rtc::Connection _rtc { _env };
Util::Point_in_time _time_base { };
void _update_time(Timestamp const &ts)
{
_time_base.rtc_seconds = Util::convert(ts);
_time_base.curr_seconds = _timer.curr_time().trunc_to_plain_ms().value / 1000;
if (_notify_sigh.valid()) {
Signal_transmitter(_notify_sigh).submit();
}
}
void _handle_rtc_set()
{
Timestamp ts = _rtc.current_time();
log("Set RTC base from RTC driver to ", ts);
_update_time(ts);
}
Signal_handler<Time> _rtc_set_sigh {
_env.ep(), *this, &Time::_handle_rtc_set };
Attached_rom_dataspace _config_rom { _env, "config" };
bool const _set_rtc {
_config_rom.xml().attribute_value("allow_setting_rtc", false) };
Constructible<Attached_rom_dataspace> _set_rtc_rom { };
Signal_handler<Time> _set_rtc_sigh {
_env.ep(), *this, &Time::_handle_set_rtc_rom };
void _handle_set_rtc_rom()
{
_set_rtc_rom->update();
if (!_set_rtc_rom->valid()) { return; }
Genode::Xml_node node = _set_rtc_rom->xml();
bool const complete = node.has_attribute("year")
&& node.has_attribute("month")
&& node.has_attribute("day")
&& node.has_attribute("hour")
&& node.has_attribute("minute")
&& node.has_attribute("second");
if (!complete) {
Genode::warning("set_rtc: ignoring incomplete RTC update");
return;
}
Timestamp ts { };
ts.second = node.attribute_value("second", 0u);
if (ts.second > 59) {
Genode::error("set_rtc: second invalid");
return;
}
ts.minute = node.attribute_value("minute", 0u);
if (ts.minute > 59) {
Genode::error("set_rtc: minute invalid");
return;
}
ts.hour = node.attribute_value("hour", 0u);
if (ts.hour > 23) {
Genode::error("set_rtc: hour invalid");
return;
}
ts.day = node.attribute_value("day", 1u);
if (ts.day > 31 || ts.day == 0) {
Genode::error("set_rtc: day invalid");
return;
}
ts.month = node.attribute_value("month", 1u);
if (ts.month > 12 || ts.month == 0) {
Genode::error("set_rtc: month invalid");
return;
}
ts.year = node.attribute_value("year", 2019u);
ts.microsecond = 0;
log("Set RTC base from 'set_rtc' ROM to ", ts);
_update_time(ts);
}
Time(Env &env, Signal_context_capability notify_sigh)
: _env { env }, _notify_sigh { notify_sigh }
{
_rtc.set_sigh(_rtc_set_sigh);
_handle_rtc_set();
if (_set_rtc) {
_set_rtc_rom.construct(env, "set_rtc");
_set_rtc_rom->sigh(_set_rtc_sigh);
}
}
Timestamp current_time()
{
Milliseconds curr_ms = _timer.curr_time().trunc_to_plain_ms();
return Util::generate(_time_base, curr_ms.value / 1000);
}
};
struct Rtc::Session_component : public Genode::Rpc_object<Session>
{
Rtc::Time &_time;
Signal_context_capability _set_sig_cap { };
Session_component(Rtc::Time &time) : _time { time } { }
virtual ~Session_component() { }
void set_sigh(Signal_context_capability sigh) override
{
_set_sig_cap = sigh;
}
Timestamp current_time() override
{
return _time.current_time();
}
void notify_client()
{
if (_set_sig_cap.valid()) {
Signal_transmitter(_set_sig_cap).submit();
}
}
};
class Rtc::Root : public Genode::Root_component<Session_component>
{
private:
Signal_handler<Rtc::Root> _set_sigh;
void _handle_set_signal()
{
_sessions.for_each([&] (Session_component &session) {
session.notify_client();
});
}
Time _time;
Registry<Registered<Session_component> > _sessions { };
protected:
Session_component *_create_session(const char *) override
{
return new (md_alloc())
Registered<Session_component>(_sessions, _time);
}
public:
Root(Env &env, Allocator &md_alloc)
:
Genode::Root_component<Session_component> { &env.ep().rpc_ep(), &md_alloc },
_set_sigh { env.ep(), *this, &Rtc::Root::_handle_set_signal },
_time { env, _set_sigh }
{ }
};
struct Rtc::Main
{
Env &_env;
Sliced_heap _sliced_heap { _env.ram(), _env.rm() };
Root _root { _env, _sliced_heap };
Main(Env &env) : _env(env)
{
env.parent().announce(env.ep().manage(_root));
}
};
void Component::construct(Genode::Env &env)
{
static Rtc::Main main(env);
}

View File

@ -0,0 +1,10 @@
TARGET := system_rtc
SRC_CC := main.cc
LIBS := base
# musl-libc contrib sources
MUSL_TM = $(REP_DIR)/src/lib/musl_tm
SRC_C = secs_to_tm.c tm_to_secs.c
INC_DIR += $(MUSL_TM)
vpath %.c $(MUSL_TM)

View File

@ -0,0 +1,167 @@
/*
* \brief Test for system RTC server
* \author Josef Soentgen
* \date 2019-07-16
*/
/*
* Copyright (C) 2019 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
/* Genode includes */
#include <base/attached_rom_dataspace.h>
#include <base/component.h>
#include <base/env.h>
#include <os/reporter.h>
#include <rtc_session/connection.h>
#include <timer_session/connection.h>
using namespace Genode;
struct Main
{
Env &_env;
Timer::Connection _timer { _env };
Rtc::Connection _rtc { _env };
void _set_rtc(Reporter &reporter, Rtc::Timestamp const &ts)
{
Reporter::Xml_generator xml(reporter, [&] () {
xml.attribute("year", ts.year);
xml.attribute("month", ts.month);
xml.attribute("day", ts.day);
xml.attribute("hour", ts.hour);
xml.attribute("minute", ts.minute);
xml.attribute("second", ts.second);
});
}
Signal_handler<Main> _set_sigh {
_env.ep(), *this, &Main::_handle_set_signal };
Rtc::Timestamp _ts { };
bool _sys_rtc_set { false };
bool _drv_rtc_set { false };
static bool _equal(Rtc::Timestamp const &ts1,
Rtc::Timestamp const &ts2)
{
return ts1.year == ts2.year
&& ts1.month == ts2.month
&& ts1.day == ts2.day
&& ts1.hour == ts2.hour
&& ts1.minute == ts2.minute;
}
bool _check_rtc(Rtc::Timestamp const &ts1,
Rtc::Timestamp const &ts2, bool system)
{
Genode::log("Set ", system ? "system" : "driver",
" RTC to: '", ts1, "' got: '", ts2, "' (ignoring seconds)");
if (!_equal(ts1, ts2)) {
error("updating ", system ? "system" : "driver", " RTC failed");
_parent_exit(1);
return false;
}
return true;
}
void _handle_set_signal()
{
Rtc::Timestamp got = _rtc.current_time();
if (_sys_rtc_set) {
_sys_rtc_set = false;
if (!_check_rtc(_ts, got, true)) {
return;
}
_ts.year = 2018;
_ts.month = 2;
_ts.day = 17;
_ts.hour = 10;
_ts.minute = 15;
_ts.second = 3;
log("Set driver RTC value: ", _ts);
_drv_rtc_set = true;
_set_rtc(*_drv_reporter, _ts);
_timer.msleep(5000);
} else if (_drv_rtc_set) {
_drv_rtc_set = false;
if (!_check_rtc(_ts, got, false)) {
return;
}
_ts.year = 2019;
_ts.month = 12;
_ts.day = 17;
_ts.hour = 11;
_ts.minute = 15;
_ts.second = 22;
log("Set system RTC value: ", _ts);
_set_rtc(*_sys_reporter, _ts);
_timer.msleep(3500);
} else {
log("RTC value: ", _ts);
_parent_exit(0);
}
}
Constructible<Reporter> _sys_reporter { };
Constructible<Reporter> _drv_reporter { };
void _parent_exit(int exit_code)
{
Genode::log("--- system RTC test finished ---");
_env.parent().exit(exit_code);
}
Main(Genode::Env &env) : _env(env)
{
_sys_reporter.construct(_env, "sys_set_rtc");
_sys_reporter->enabled(true);
_drv_reporter.construct(_env, "drv_set_rtc");
_drv_reporter->enabled(true);
Genode::log("--- system RTC test started ---");
_rtc.set_sigh(_set_sigh);
_ts = _rtc.current_time();
log("Initial RTC value: ", _ts);
_ts.year = 2020;
_ts.month = 7;
_ts.day = 16;
_ts.hour = 12;
_ts.minute = 23;
_ts.second = 1;
log("Set system RTC value: ", _ts);
_sys_rtc_set = true;
_set_rtc(*_sys_reporter, _ts);
_timer.msleep(5000);
}
};
void Component::construct(Genode::Env &env) { static Main main(env); }

View File

@ -0,0 +1,3 @@
TARGET = test-system_rtc
SRC_CC = main.cc
LIBS = base