diff --git a/repos/base-hw/src/timer/hw/main.cc b/repos/base-hw/src/timer/hw/main.cc new file mode 100644 index 0000000000..91e10224d2 --- /dev/null +++ b/repos/base-hw/src/timer/hw/main.cc @@ -0,0 +1,305 @@ +/* + * \brief Timer driver for the base-hw kernel + * \author Norman Feske + * \date 2024-03-11 + */ + +/* + * Copyright (C) 2024 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 + +/* base-internal includes */ +#include + +/* base-hw includes */ +#include + +namespace Timer { + + using namespace Genode; + + struct Clock; + struct Device; + struct Alarm; + struct Root; + struct Session_component; + struct Main; + + using Alarms = Alarm_registry; +} + + +struct Timer::Clock +{ + uint64_t us; + + static constexpr uint64_t MASK = uint64_t(-1); + + uint64_t value() const { return us; } + + void print(Output &out) const { Genode::print(out, us/1000); } +}; + + +class Timer::Device : Noncopyable +{ + public: + + struct Wakeup_dispatcher : Interface + { + virtual void dispatch_device_wakeup() = 0; + }; + + struct Deadline : Clock { }; + + static constexpr Deadline infinite_deadline { uint64_t(-1) }; + + private: + + Env &_env; + + Kernel::time_t const _max_timeout_us = Kernel::timeout_max_us(); + + Wakeup_dispatcher &_dispatcher; + + Signal_handler _handler { _env.ep(), *this, &Device::_handle_timeout }; + + Signal_context_capability const _handler_cap = _handler; + + Kernel::capid_t const _sel = Kernel::capid_t(addr_t(_handler_cap.data()) & 0xffffu); + + void _handle_timeout() { _dispatcher.dispatch_device_wakeup(); } + + public: + + Device(Env &env, Wakeup_dispatcher &dispatcher) + : _env(env), _dispatcher(dispatcher) { } + + Clock now() const + { + return Clock { .us = Kernel::time() }; + } + + void update_deadline(Deadline const deadline) + { + uint64_t const now_us = now().us; + uint64_t const rel_us = (deadline.us > now_us) + ? min(_max_timeout_us, deadline.us - now_us) + : 0; + + Kernel::timeout(Kernel::timeout_t(rel_us), _sel); + } +}; + + +struct Timer::Alarm : Alarms::Element +{ + Session_component &session; + + Alarm(Alarms &alarms, Session_component &session, Clock time) + : + Alarms::Element(alarms, *this, time), session(session) + { } + + void print(Output &out) const; +}; + + +static Timer::Device::Deadline next_deadline(Timer::Alarms &alarms) +{ + using namespace Timer; + + return alarms.soonest(Clock { 0 }).convert( + [&] (Clock soonest) -> Device::Deadline { + + /* scan alarms for a cluster nearby the soonest */ + uint64_t const MAX_DELAY_US = 250; + Device::Deadline result { soonest.us }; + alarms.for_each_in_range(soonest, Clock { soonest.us + MAX_DELAY_US }, + [&] (Alarm const &alarm) { + result.us = max(result.us, alarm.time.us); }); + + return result; + }, + [&] (Alarms::None) { return Device::infinite_deadline; }); +} + + +struct Timer::Session_component : Session_object +{ + Alarms &_alarms; + Device &_device; + + Signal_context_capability _sigh { }; + + Clock const _creation_time = _device.now(); + + uint64_t _local_now_us() const { return _device.now().us - _creation_time.us; } + + struct Period { uint64_t us; }; + + Constructible _period { }; + Constructible _alarm { }; + + Session_component(Env &env, + Resources const &resources, + Label const &label, + Diag const &diag, + Alarms &alarms, + Device &device) + : + Session_object(env.ep(), resources, label, diag), + _alarms(alarms), _device(device) + { } + + /** + * Called by Device::Wakeup_dispatcher + */ + void handle_wakeup() + { + if (_sigh.valid()) + Signal_transmitter(_sigh).submit(); + + if (_period.constructed()) { + Clock const next = _alarm.constructed() + ? Clock { _alarm->time.us + _period->us } + : Clock { _device.now().us + _period->us }; + + _alarm.construct(_alarms, *this, next); + + } else /* response of 'trigger_once' */ { + _alarm.destruct(); + } + } + + /****************************** + ** Timer::Session interface ** + ******************************/ + + void trigger_once(uint64_t rel_us) override + { + _period.destruct(); + _alarm.destruct(); + + Clock const now = _device.now(); + + rel_us = max(rel_us, 250u); + _alarm.construct(_alarms, *this, Clock { now.us + rel_us }); + + _device.update_deadline(next_deadline(_alarms)); + } + + void trigger_periodic(uint64_t period_us) override + { + _period.destruct(); + _alarm.destruct(); + + if (period_us) { + period_us = max(period_us, 1000u); + _period.construct(period_us); + handle_wakeup(); + } + + _device.update_deadline(next_deadline(_alarms)); + } + + void sigh(Signal_context_capability sigh) override { _sigh = sigh; } + + uint64_t elapsed_ms() const override { return _local_now_us()/1000; } + uint64_t elapsed_us() const override { return _local_now_us(); } + + void msleep(uint64_t) override { } + void usleep(uint64_t) override { } +}; + + +struct Timer::Root : public Root_component +{ + private: + + Env &_env; + Alarms &_alarms; + Device &_device; + + protected: + + Session_component *_create_session(const char *args) override + { + return new (md_alloc()) + Session_component(_env, + session_resources_from_args(args), + session_label_from_args(args), + session_diag_from_args(args), + _alarms, _device); + } + + void _upgrade_session(Session_component *s, const char *args) override + { + s->upgrade(ram_quota_from_args(args)); + s->upgrade(cap_quota_from_args(args)); + } + + void _destroy_session(Session_component *session) override + { + Genode::destroy(md_alloc(), session); + } + + public: + + Root(Env &env, Allocator &md_alloc, Alarms &alarms, Device &device) + : + Root_component(&env.ep().rpc_ep(), &md_alloc), + _env(env), _alarms(alarms), _device(device) + { } +}; + + +void Timer::Alarm::print(Output &out) const { Genode::print(out, session.label()); } + + +struct Timer::Main : Device::Wakeup_dispatcher +{ + Env &_env; + + Device _device { _env, *this }; + + Alarms _alarms { }; + + Sliced_heap _sliced_heap { _env.ram(), _env.rm() }; + + Root _root { _env, _sliced_heap, _alarms, _device }; + + /** + * Device::Wakeup_dispatcher + */ + void dispatch_device_wakeup() override + { + Clock const now = _device.now(); + + /* handle and remove pending alarms */ + while (_alarms.with_any_in_range({ 0 }, now, [&] (Alarm &alarm) { + alarm.session.handle_wakeup(); })); + + /* schedule next wakeup */ + _device.update_deadline(next_deadline(_alarms)); + } + + Main(Genode::Env &env) : _env(env) + { + _env.parent().announce(_env.ep().manage(_root)); + } +}; + + +void Component::construct(Genode::Env &env) { static Timer::Main inst(env); } diff --git a/repos/base-hw/src/timer/hw/target.mk b/repos/base-hw/src/timer/hw/target.mk index bd3672a2d8..238e61952a 100644 --- a/repos/base-hw/src/timer/hw/target.mk +++ b/repos/base-hw/src/timer/hw/target.mk @@ -1,7 +1,5 @@ -TARGET = hw_timer_drv -REQUIRES = hw -LIBS = syscall-hw -INC_DIR += $(PRG_DIR) -SRC_CC += time_source.cc - -include $(call select_from_repositories,src/timer/target.inc) +TARGET = hw_timer_drv +REQUIRES = hw +LIBS = syscall-hw base +REP_INC_DIR += src/include +SRC_CC += main.cc diff --git a/repos/base-hw/src/timer/hw/time_source.cc b/repos/base-hw/src/timer/hw/time_source.cc deleted file mode 100644 index e95c2334d4..0000000000 --- a/repos/base-hw/src/timer/hw/time_source.cc +++ /dev/null @@ -1,64 +0,0 @@ -/* - * \brief Time source that uses the timeout syscalls of the HW kernel - * \author Martin Stein - * \date 2012-05-03 - */ - -/* - * Copyright (C) 2012-2017 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 - -/* local includes */ -#include - -using namespace Genode; - -enum { MIN_TIMEOUT_US = 1000 }; - - -Timer::Time_source::Time_source(Env &env) -: - Signalled_time_source(env), - _max_timeout_us(Kernel::timeout_max_us()) -{ - if (_max_timeout_us < MIN_TIMEOUT_US) { - error("minimum timeout greater then maximum timeout"); - throw Genode::Exception(); - } -} - - -void Timer::Time_source::set_timeout(Microseconds duration, - Timeout_handler &handler) -{ - Kernel::timeout_t duration_us = (Kernel::timeout_t)duration.value; - if (duration_us < MIN_TIMEOUT_US) { - duration_us = MIN_TIMEOUT_US; } - - if (duration_us > _max_timeout_us) { - duration_us = (Kernel::timeout_t)_max_timeout_us; } - - _handler = &handler; - Signal_context_capability cap = _signal_handler; - Kernel::timeout(duration_us, (Kernel::capid_t)((addr_t)cap.data() & 0xffff)); -} - - -Duration Timer::Time_source::curr_time() -{ - /* - * FIXME: the `Microseconds` constructor takes a machine-word as value - * thereby limiting the possible value to something ~1.19 hours. - * must be changed when the timeout framework internally does not use - * machine-word wide microseconds values anymore. - */ - return Duration(Microseconds(Kernel::time())); -} diff --git a/repos/base-hw/src/timer/hw/time_source.h b/repos/base-hw/src/timer/hw/time_source.h deleted file mode 100644 index 514c77e855..0000000000 --- a/repos/base-hw/src/timer/hw/time_source.h +++ /dev/null @@ -1,57 +0,0 @@ -/* - * \brief Time source that uses the timeout syscalls of the HW kernel - * \author Martin Stein - * \date 2012-05-03 - */ - -/* - * Copyright (C) 2012-2017 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. - */ - -#ifndef _TIME_SOURCE_H_ -#define _TIME_SOURCE_H_ - -/* Genode includes */ -#include - -/* base-hw includes */ -#include - -/* local includes */ -#include - -namespace Timer { - - using Microseconds = Genode::Microseconds; - using Duration = Genode::Duration; - using Timeout_handler = Genode::Timeout_handler; - - class Time_source; -} - - -class Timer::Time_source : public Genode::Signalled_time_source -{ - private: - - Kernel::time_t const _max_timeout_us; - - public: - - Time_source(Genode::Env &env); - - - /************************* - ** Genode::Time_source ** - *************************/ - - Duration curr_time() override; - void set_timeout(Microseconds duration, Timeout_handler &handler) override; - Microseconds max_timeout() const override { - return Microseconds(_max_timeout_us); }; -}; - -#endif /* _TIME_SOURCE_H_ */