diff --git a/repos/os/recipes/src/test-rtc/used_apis b/repos/os/recipes/src/test-rtc/used_apis
index 246c4e388..bfc34f644 100644
--- a/repos/os/recipes/src/test-rtc/used_apis
+++ b/repos/os/recipes/src/test-rtc/used_apis
@@ -1,4 +1,5 @@
base
os
timer_session
+report_session
rtc_session
diff --git a/repos/os/run/rtc.run b/repos/os/run/rtc.run
index 0f9e7a40e..fa24e86f0 100644
--- a/repos/os/run/rtc.run
+++ b/repos/os/run/rtc.run
@@ -1,15 +1,17 @@
# RTC test
-if {(![have_spec x86] || [have_spec linux])} {
- puts "Platform is unsupported."
- exit 0
-}
+assert_spec x86
-build { core init drivers/rtc timer test/rtc }
+set test_update [have_include power_on/qemu]
+
+set build_components { core init drivers/rtc timer test/rtc }
+append_if $test_update build_components { server/report_rom }
+
+build $build_components
create_boot_directory
-install_config {
+set config {
@@ -28,17 +30,52 @@ install_config {
-
-
+ }
+append_if $test_update config {
+
-
+
+
+
+
+ }
+append_if [have_spec linux] config {
+ }
+append_if [expr ![have_spec linux]] config {
+ }
+append config {
+
+ }
+append_if $test_update config {
+
+
+
+
+
+
+ }
+append config {
-
+ }
+append_if $test_update config {
+ }
+append config {
}
-build_boot_image { core ld.lib.so init timer rtc_drv test-rtc }
+install_config $config
+
+set boot_components {
+ core ld.lib.so init timer test-rtc
+}
+
+append_if $test_update boot_components { report_rom }
+
+append_if [have_spec linux] boot_components { linux_rtc_drv }
+append_if [expr ![have_spec linux]] boot_components { rtc_drv }
+
+build_boot_image $boot_components
append qemu_args " -nographic "
diff --git a/repos/os/src/drivers/rtc/README b/repos/os/src/drivers/rtc/README
new file mode 100644
index 000000000..d9f26bd5a
--- /dev/null
+++ b/repos/os/src/drivers/rtc/README
@@ -0,0 +1,17 @@
+The RTC driver component provides access to the CMOS RTC on x86 via
+the Rtc session. On base-linux 'gettimeofday' is used.
+
+The component will use the content of the 'set_rtc' ROM if the
+'allow_setting_rtc' attribute in the 'config' node is set to 'yes'
+to allow setting the RTC. 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 use 24H mode and relies on the BIOS/firmware
+to do the right thing regarding the year.
diff --git a/repos/os/src/drivers/rtc/main.cc b/repos/os/src/drivers/rtc/main.cc
index 6a76d99fb..86fae8875 100644
--- a/repos/os/src/drivers/rtc/main.cc
+++ b/repos/os/src/drivers/rtc/main.cc
@@ -5,13 +5,14 @@
*/
/*
- * Copyright (C) 2015-2017 Genode Labs GmbH
+ * Copyright (C) 2015-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 */
+#include
#include
#include
#include
@@ -77,8 +78,85 @@ struct Rtc::Main
Root root { env, sliced_heap };
- Main(Env &env) : env(env) { env.parent().announce(env.ep().manage(root)); }
+ Attached_rom_dataspace _config_rom { env, "config" };
+ bool const _set_rtc {
+ _config_rom.xml().attribute_value("allow_setting_rtc", false) };
+
+ Constructible _update_rom { };
+
+ void _handle_update();
+
+ Signal_handler _update_sigh {
+ env.ep(), *this, &Main::_handle_update };
+
+ Main(Env &env) : env(env)
+ {
+ if (_set_rtc) {
+ _update_rom.construct(env, "set_rtc");
+ _update_rom->sigh(_update_sigh);
+ }
+
+ env.parent().announce(env.ep().manage(root));
+ }
};
+void Rtc::Main::_handle_update()
+{
+ _update_rom->update();
+
+ if (!_update_rom->valid()) { return; }
+
+ Genode::Xml_node node = _update_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);
+
+ Rtc::set_time(env, ts);
+}
+
+
void Component::construct(Genode::Env &env) { static Rtc::Main main(env); }
diff --git a/repos/os/src/drivers/rtc/rtc.h b/repos/os/src/drivers/rtc/rtc.h
index cdb692780..27245f4f9 100644
--- a/repos/os/src/drivers/rtc/rtc.h
+++ b/repos/os/src/drivers/rtc/rtc.h
@@ -5,7 +5,7 @@
*/
/*
- * Copyright (C) 2015-2017 Genode Labs GmbH
+ * Copyright (C) 2015-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.
@@ -22,6 +22,7 @@ namespace Rtc {
using namespace Genode;
Timestamp get_time(Env &env);
+ void set_time(Env &env, Timestamp ts);
}
#endif /* _DRIVERS__RTC__SPEC__X86__RTC_H_ */
diff --git a/repos/os/src/drivers/rtc/spec/linux/rtc.cc b/repos/os/src/drivers/rtc/spec/linux/rtc.cc
index a63cd2615..2d1592d39 100644
--- a/repos/os/src/drivers/rtc/spec/linux/rtc.cc
+++ b/repos/os/src/drivers/rtc/spec/linux/rtc.cc
@@ -34,3 +34,9 @@ Rtc::Timestamp Rtc::get_time(Env &)
return ts;
}
+
+
+void Rtc::set_time(Env &, Timestamp)
+{
+ Genode::warning("setting RTC not implemented on Linux");
+}
diff --git a/repos/os/src/drivers/rtc/spec/x86/rtc.cc b/repos/os/src/drivers/rtc/spec/x86/rtc.cc
index 6f771e81d..39b0be252 100644
--- a/repos/os/src/drivers/rtc/spec/x86/rtc.cc
+++ b/repos/os/src/drivers/rtc/spec/x86/rtc.cc
@@ -6,7 +6,7 @@
*/
/*
- * Copyright (C) 2007-2017 Genode Labs GmbH
+ * Copyright (C) 2007-2019 Genode Labs GmbH
* Copyright (C) 2012 Intel Corporation
*
* This file is part of the Genode OS framework, which is distributed
@@ -77,17 +77,33 @@ static inline unsigned cmos_read(Io_port_connection &rtc_ports, unsigned char ad
}
+static inline void cmos_write(Io_port_connection &rtc_ports, unsigned char addr,
+ unsigned char value)
+{
+ rtc_ports.outb(RTC_PORT_ADDR, addr);
+ // iodelay();
+ rtc_ports.outb(RTC_PORT_DATA, value);
+}
+
+
#define RTC_ALWAYS_BCD 1 /* RTC operates in binary mode */
#define BCD_TO_BIN(val) ((val) = ((val) & 15) + ((val) >> 4) * 10)
#define BIN_TO_BCD(val) ((val) = (((val)/10) << 4) + (val) % 10)
+static Io_port_connection &rtc_ports(Env *env = nullptr)
+{
+ static Io_port_connection inst(*env, RTC_PORT_BASE, RTC_PORT_SIZE);
+ return inst;
+}
+
+
Rtc::Timestamp Rtc::get_time(Env &env)
{
/*
* Our RTC port session
*/
- static Io_port_connection rtc_ports(env, RTC_PORT_BASE, RTC_PORT_SIZE);
+ rtc_ports(&env);
unsigned year, mon, day, hour, min, sec;
int i;
@@ -99,22 +115,22 @@ Rtc::Timestamp Rtc::get_time(Env &env)
/* read RTC exactly on falling edge of update flag */
for (i = 0 ; i < 1000000 ; i++)
- if (cmos_read(rtc_ports, RTC_FREQ_SELECT) & RTC_UIP) break;
+ if (cmos_read(rtc_ports(), RTC_FREQ_SELECT) & RTC_UIP) break;
for (i = 0 ; i < 1000000 ; i++)
- if (!(cmos_read(rtc_ports, RTC_FREQ_SELECT) & RTC_UIP)) break;
+ if (!(cmos_read(rtc_ports(), RTC_FREQ_SELECT) & RTC_UIP)) break;
do {
- sec = cmos_read(rtc_ports, RTC_SECONDS);
- min = cmos_read(rtc_ports, RTC_MINUTES);
- hour = cmos_read(rtc_ports, RTC_HOURS);
- day = cmos_read(rtc_ports, RTC_DAY_OF_MONTH);
- mon = cmos_read(rtc_ports, RTC_MONTH);
- year = cmos_read(rtc_ports, RTC_YEAR);
- } while (sec != cmos_read(rtc_ports, RTC_SECONDS));
+ sec = cmos_read(rtc_ports(), RTC_SECONDS);
+ min = cmos_read(rtc_ports(), RTC_MINUTES);
+ hour = cmos_read(rtc_ports(), RTC_HOURS);
+ day = cmos_read(rtc_ports(), RTC_DAY_OF_MONTH);
+ mon = cmos_read(rtc_ports(), RTC_MONTH);
+ year = cmos_read(rtc_ports(), RTC_YEAR);
+ } while (sec != cmos_read(rtc_ports(), RTC_SECONDS));
/* convert BCD to binary format if needed */
- if (!(cmos_read(rtc_ports, RTC_CONTROL) & RTC_DM_BINARY) || RTC_ALWAYS_BCD) {
+ if (!(cmos_read(rtc_ports(), RTC_CONTROL) & RTC_DM_BINARY) || RTC_ALWAYS_BCD) {
BCD_TO_BIN(sec);
BCD_TO_BIN(min);
BCD_TO_BIN(hour);
@@ -127,3 +143,41 @@ Rtc::Timestamp Rtc::get_time(Env &env)
return Timestamp { 0, sec, min, hour, day, mon, year };
}
+
+
+void Rtc::set_time(Env &env, Timestamp ts)
+{
+ /*
+ * Our RTC port session
+ */
+ rtc_ports(&env);
+
+ unsigned const ctl = cmos_read(rtc_ports(), RTC_CONTROL);
+ unsigned const freq = cmos_read(rtc_ports(), RTC_FREQ_SELECT);
+ bool const bcd = (!(ctl & RTC_DM_BINARY) || RTC_ALWAYS_BCD);
+
+ /* ignore century and hope for the best */
+ ts.year %= 100;
+
+ unsigned const sec = bcd ? BIN_TO_BCD(ts.second) : ts.second;
+ unsigned const min = bcd ? BIN_TO_BCD(ts.minute) : ts.minute;
+ unsigned const hour = bcd ? BIN_TO_BCD(ts.hour) : ts.hour;
+ unsigned const day = bcd ? BIN_TO_BCD(ts.day) : ts.day;
+ unsigned const mon = bcd ? BIN_TO_BCD(ts.month) : ts.month;
+ unsigned const year = bcd ? BIN_TO_BCD(ts.year) : ts.year;
+
+ /* disable updating */
+ cmos_write(rtc_ports(), RTC_CONTROL, ctl | RTC_SET);
+ cmos_write(rtc_ports(), RTC_FREQ_SELECT, freq | RTC_DIV_RESET2);
+
+ cmos_write(rtc_ports(), RTC_SECONDS, sec);
+ cmos_write(rtc_ports(), RTC_MINUTES, min);
+ cmos_write(rtc_ports(), RTC_HOURS, hour);
+ cmos_write(rtc_ports(), RTC_DAY_OF_MONTH, day);
+ cmos_write(rtc_ports(), RTC_MONTH, mon);
+ cmos_write(rtc_ports(), RTC_YEAR, year);
+
+ /* enable updating */
+ cmos_write(rtc_ports(), RTC_CONTROL, ctl);
+ cmos_write(rtc_ports(), RTC_FREQ_SELECT, freq);
+}
diff --git a/repos/os/src/test/rtc/main.cc b/repos/os/src/test/rtc/main.cc
index b360f9e5f..2cd735a8a 100644
--- a/repos/os/src/test/rtc/main.cc
+++ b/repos/os/src/test/rtc/main.cc
@@ -1,18 +1,22 @@
/*
* \brief Test for RTC driver
* \author Christian Helmuth
+ * \author Josef Soentgen
* \date 2015-01-06
*/
/*
- * Copyright (C) 2015-2017 Genode Labs GmbH
+ * Copyright (C) 2015-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
#include
#include
+#include
#include
#include
@@ -23,6 +27,8 @@ struct Main
{
Main(Genode::Env &env)
{
+ int exit_code = 0;
+
Genode::log("--- RTC test started ---");
/* open sessions */
@@ -33,17 +39,67 @@ struct Main
Rtc::Timestamp now[] = { rtc[0].current_time(), rtc[1].current_time() };
for (unsigned j = 0; j < sizeof(rtc)/sizeof(*rtc); ++j)
- log("RTC[", j, "]: ",
- now[j].year, "-", now[j].month, "-", now[j].day, " ",
- now[j].hour, ":", now[j].minute, ":", now[j].second);
+ log("RTC[", j, "]: ", now[j]);
timer.msleep(1000);
}
+ /* test setting the RTC */
+ Attached_rom_dataspace config_rom { env, "config" };
+ bool const test_update = config_rom.xml().attribute_value("set_rtc", false);
+ if (test_update) {
+ try {
+ Reporter reporter { env, "set_rtc" };
+ reporter.enabled(true);
+
+ Rtc::Timestamp ts = rtc[0].current_time();
+ ts.year = 2069;
+ ts.month = 12;
+ ts.day = 31;
+ ts.hour = 23;
+ ts.minute = 55;
+ ts.second = 0;
+
+ 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);
+ });
+
+ /*
+ * Wait a reasonable amount of time for the RTC update
+ * to go through.
+ */
+ timer.msleep(3000);
+
+ Rtc::Timestamp got = rtc[0].current_time();
+
+ Genode::log("Set RTC to: '", ts, "' got: '", got,
+ "' (ignoring seconds)");
+
+ if ( ts.year != got.year
+ || ts.month != got.month
+ || ts.day != got.day
+ || ts.hour != got.hour
+ || ts.minute != got.minute) {
+ error("updating RTC failed");
+ exit_code = 2;
+ }
+
+ } catch (...) {
+ error("could not test RTC update");
+ exit_code = 1;
+ }
+ }
+
Genode::log("--- RTC test finished ---");
- env.parent().exit(0);
+ env.parent().exit(exit_code);
}
};
+
void Component::construct(Genode::Env &env) { static Main main(env); }