From 47e6d72bf2b07ee5cc8443509f4b4e57fdcbe318 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Josef=20S=C3=B6ntgen?= Date: Mon, 1 Jul 2019 17:23:33 +0200 Subject: [PATCH] rtc_drv: add support for setting RTC The rtc_drv on x86 can now by used to also set the RTC. If the config attribute 'allow_setting_rtc' is set to 'yes' the driver will update the RTC from the content of the 'set-rtc' ROM module. A valid ROM must contain a top node with the following attributes: 'year', 'month', 'day', 'hour', 'minute' and 'second'. * Only rudimentary checking of the provided values is done. * '12H' mode is not supported. Fixes #3438. --- repos/os/recipes/src/test-rtc/used_apis | 1 + repos/os/run/rtc.run | 59 +++++++++++++--- repos/os/src/drivers/rtc/README | 17 +++++ repos/os/src/drivers/rtc/main.cc | 82 +++++++++++++++++++++- repos/os/src/drivers/rtc/rtc.h | 3 +- repos/os/src/drivers/rtc/spec/linux/rtc.cc | 6 ++ repos/os/src/drivers/rtc/spec/x86/rtc.cc | 78 ++++++++++++++++---- repos/os/src/test/rtc/main.cc | 66 +++++++++++++++-- 8 files changed, 281 insertions(+), 31 deletions(-) create mode 100644 repos/os/src/drivers/rtc/README 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); }