From b5657ab546f6a89d0cd75239e6fad4259a7bdc46 Mon Sep 17 00:00:00 2001 From: Michael Mueller Date: Fri, 20 Jun 2025 17:32:18 +0200 Subject: [PATCH] ealanos: Added new "Shell" service to control a habitat from a user-space component. --- repos/ealanos/include/ealanos/shell/client.h | 27 ++++++ .../ealanos/include/ealanos/shell/component.h | 43 +++++++++ .../include/ealanos/shell/connection.h | 33 +++++++ repos/ealanos/include/ealanos/shell/session.h | 59 ++++++++++++ repos/ealanos/src/hoitaja/main.cc | 95 +++++++++++++++++-- 5 files changed, 249 insertions(+), 8 deletions(-) create mode 100644 repos/ealanos/include/ealanos/shell/client.h create mode 100644 repos/ealanos/include/ealanos/shell/component.h create mode 100644 repos/ealanos/include/ealanos/shell/connection.h create mode 100644 repos/ealanos/include/ealanos/shell/session.h diff --git a/repos/ealanos/include/ealanos/shell/client.h b/repos/ealanos/include/ealanos/shell/client.h new file mode 100644 index 0000000000..e045cc93b4 --- /dev/null +++ b/repos/ealanos/include/ealanos/shell/client.h @@ -0,0 +1,27 @@ +#ifndef __EALANOS__INCLUDE__SHELL__CLIENT_H_ +#define __EALANOS__INCLUDE__SHELL__CLIENT_H_ + +#include "base/capability.h" +#include "base/ram_allocator.h" +#include "region_map/region_map.h" +#include +#include + +namespace Ealan +{ + namespace Shell + { + using Capability = Genode::Capability; + + struct Client : Genode::Rpc_client { + explicit Client(Capability cap) : Genode::Rpc_client(cap) { } + + Genode::Ram_dataspace_capability connect() override { return call(); } + + void disconnect() override { call(); } + void commit() override { call(); } + }; + } // namespace Shell +} + +#endif \ No newline at end of file diff --git a/repos/ealanos/include/ealanos/shell/component.h b/repos/ealanos/include/ealanos/shell/component.h new file mode 100644 index 0000000000..2ec94ec542 --- /dev/null +++ b/repos/ealanos/include/ealanos/shell/component.h @@ -0,0 +1,43 @@ +#ifndef __EALANOS__INCLUDE__SHELL__COMPONENT_H_ +#define __EALANOS__INCLUDE__SHELL__COMPONENT_H_ + +#include "base/capability.h" +#include "base/entrypoint.h" +#include "base/ram_allocator.h" +#include "region_map/region_map.h" +#include +#include +#include +#include + +#include + +namespace Ealan { + class Hoitaja; + + namespace Shell { + class Session_component : public Genode::Session_object + { + private: + + Genode::Entrypoint &_ep; + Ealan::Hoitaja &_hoitaja; + + public: + + template + Session_component(Hoitaja &hoitaja, Genode::Entrypoint &ep, + Genode::Session::Resources const &res, ARGS &&...args) + : Genode::Session_object(ep, res, args...), _ep(ep), _hoitaja(hoitaja) + { + } + + Genode::Ram_dataspace_capability connect() override; + void disconnect() override; + + void commit() override; + }; + } +} // namespace Ealan + +#endif \ No newline at end of file diff --git a/repos/ealanos/include/ealanos/shell/connection.h b/repos/ealanos/include/ealanos/shell/connection.h new file mode 100644 index 0000000000..aadbdd19f7 --- /dev/null +++ b/repos/ealanos/include/ealanos/shell/connection.h @@ -0,0 +1,33 @@ +#ifndef __EALANOS__INCLUDE__SHELL__CONNECTION_H_ +#define __EALANOS__INCLUDE__SHELL__CONNECTION_H_ + +#include "base/affinity.h" +#include "base/capability.h" +#include "base/ram_allocator.h" +#include "region_map/region_map.h" +#include +#include + +#include + +namespace Ealan +{ + namespace Shell + { + struct Connection : Genode::Connection, Client { + Connection(Genode::Env &env, Genode::Affinity affinity = Genode::Affinity(Genode::Affinity::Space(1,1), Genode::Affinity::Location(0,0)), + Label const &label = Label()) + : Genode::Connection(env, label, Genode::Ram_quota{RAM_QUOTA}, + affinity, Args("")), Client(cap()) + { + } + + Genode::Ram_dataspace_capability connect() override { return Client::connect(); } + + void disconnect() override { Client::disconnect(); } + void commit() override { Client::commit(); } + + }; + } // namespace Shell +} // namespace Ealan +#endif diff --git a/repos/ealanos/include/ealanos/shell/session.h b/repos/ealanos/include/ealanos/shell/session.h new file mode 100644 index 0000000000..81bb952060 --- /dev/null +++ b/repos/ealanos/include/ealanos/shell/session.h @@ -0,0 +1,59 @@ +#ifndef __EALANOS__INCLUDE__SHELL__SESSION_H_ +#define __EALANOS__INCLUDE__SHELL__SESSION_H_ + +#include "base/capability.h" +#include "base/ram_allocator.h" +#include "base/rpc.h" +#include "dataspace/capability.h" +#include "region_map/region_map.h" +#include +#include +#include + +namespace Ealan +{ + namespace Shell + { + struct Session : Genode::Session + { + static const char *service_name() { return "Shell"; } + + enum { CAP_QUOTA = 2, RAM_QUOTA = 1024 }; + + /** + * @brief Connect to a shell session by supplying a region map + * + * @param rm - region map used for mapping the habitat's config to the client's virtual address space + * @return true - Mapping was successful + * @return false - An invalid region map was supplied or the mapping failed (e.g. lack of quota or caps) + */ + virtual Genode::Ram_dataspace_capability connect() = 0; + + /** + * @brief Disconnects a client's shell session + * + */ + virtual void disconnect() = 0; + + /** + * @brief Commit a change to the habitat's configuration made by the client. + * @details In order to let changes made in the habitat's configuration take effect, the + * changes must be reported by using this method. This will cause Hoitaja to + * re-read its configuration and applying any changes, such as creating new cells, destroying cells etc. + */ + virtual void commit() = 0; + + /***************** + * RPC interface * + *****************/ + + GENODE_RPC(Rpc_connect, Genode::Ram_dataspace_capability, connect); + GENODE_RPC(Rpc_disconnect, void, disconnect); + GENODE_RPC(Rpc_commit, void, commit); + + GENODE_RPC_INTERFACE(Rpc_connect, Rpc_disconnect, Rpc_commit); + }; + } // namespace Shell +} // namespace Ealan + +#endif diff --git a/repos/ealanos/src/hoitaja/main.cc b/repos/ealanos/src/hoitaja/main.cc index 231426cb1c..306b930319 100644 --- a/repos/ealanos/src/hoitaja/main.cc +++ b/repos/ealanos/src/hoitaja/main.cc @@ -12,8 +12,14 @@ */ /* Genode includes */ +#include "base/capability.h" +#include "base/ipc.h" +#include "base/ram_allocator.h" +#include "region_map/client.h" +#include "region_map/region_map.h" #include #include +#include #include #include #include @@ -21,6 +27,10 @@ #include #include + +#include + +#include namespace Ealan { using namespace Genode; @@ -31,7 +41,10 @@ namespace Ealan { class Ealan::Hoitaja : Genode::Sandbox::State_handler, Genode::Sandbox::Local_service_base::Wakeup { - private: + private: + + friend class Ealan::Shell::Session_component; + Env &_env; Genode::Sandbox _sandbox { _env, *this }; @@ -40,6 +53,7 @@ class Ealan::Hoitaja : Genode::Sandbox::State_handler, Genode::Sandbox::Local_se Genode::Xml_node *_habitat_config{nullptr}; + Genode::Ram_dataspace_capability _config_ds{}; char *_config_dataspace{nullptr}; void _handle_resource_avail() { } @@ -49,21 +63,23 @@ class Ealan::Hoitaja : Genode::Sandbox::State_handler, Genode::Sandbox::Local_se Constructible _reporter { }; - Genode::Rpc_entrypoint _ep{&_env.pd(), 4096, "launcher_ep", Genode::Affinity::Location(0, 0, 1, 1)}; - - Genode::Entrypoint _launcher_ep{_env, 4*4096, "launcher", Genode::Affinity::Location()}; - Genode::Sliced_heap _md_alloc{_env.ram(), _env.rm()}; Genode::Heap _heap{_env.ram(), _env.rm()}; + Genode::Mutex _config_lock{}; + using Launcher_service = Genode::Sandbox::Local_service; Launcher_service _launcher_service{_sandbox, *this}; + using Shell_service = Genode::Sandbox::Local_service; + Shell_service _shell_service{_sandbox, *this}; + size_t _report_buffer_size = 0; void _handle_config() { + _config_lock.acquire(); _config.update(); if (_habitat_config) { @@ -71,7 +87,19 @@ class Ealan::Hoitaja : Genode::Sandbox::State_handler, Genode::Sandbox::Local_se delete _config_dataspace; } - _config_dataspace = static_cast(_heap.alloc(_config.size()*32)); + _config_ds = _env.ram().alloc(32 * _config.size()); + + Genode::Region_map::Attr attr{}; + attr.writeable = true; + + _config_dataspace = _env.rm() + .attach(_config_ds, attr) + .convert( + [&](Genode::Region_map::Range r) { + return reinterpret_cast(r.start); + }, + [&](Genode::Region_map::Attach_error) { return nullptr; }); + //_config_dataspace = static_cast(_heap.alloc(_config.size()*32)); Genode::memcpy(_config_dataspace, _config.local_addr(), _config.size()); _habitat_config = new (_sandbox._heap) Genode::Xml_node(_config_dataspace); @@ -96,6 +124,7 @@ class Ealan::Hoitaja : Genode::Sandbox::State_handler, Genode::Sandbox::Local_se _reporter->enabled(reporter_enabled); _sandbox.apply_config(config); + _config_lock.release(); } Signal_handler _config_handler { @@ -136,31 +165,41 @@ class Ealan::Hoitaja : Genode::Sandbox::State_handler, Genode::Sandbox::Local_se do { try { + _config_lock.acquire(); Genode::log("Updating state of child ", child.name()); _habitat_config = _sandbox.update(child, _habitat_config); Genode::log("Updated config length:", _habitat_config->content_size()); + _config_lock.release(); } catch (Genode::Quota_guard::Limit_exceeded) { Genode::log("Caps exceeded while handling child state"); + _config_lock.release(); _env.parent().exit(1); } catch (Genode::Ipc_error) { Genode::error("Failed to update child state for <", child.name(), ">"); + _config_lock.release(); repeat = true; } } while (repeat); } void wakeup_local_service() override { - _launcher_service.for_each_requested_session([&](Launcher_service::Request &req) - { req.deliver_session(*new (_md_alloc) Ealan::Launcher_session_component(*this, _env.ep() , req.resources, "", req.diag)); }); + _launcher_service.for_each_requested_session([&](Launcher_service::Request &req) { + req.deliver_session(*new (_md_alloc) Ealan::Launcher_session_component( + *this, _env.ep(), req.resources, "", req.diag)); + }); + _shell_service.for_each_requested_session([&](Shell_service::Request &req) { + req.deliver_session(*new (_md_alloc) Ealan::Shell::Session_component(*this, _env.ep(), req.resources, "", req.diag)); + }); } void add_cell_from_xml(const char *start_node){ char *dest = nullptr; + _config_lock.acquire(); _habitat_config->with_raw_content([&](char const *content, Genode::size_t) { dest = const_cast(content); }); dest += _habitat_config->content_size(); @@ -183,8 +222,27 @@ class Ealan::Hoitaja : Genode::Sandbox::State_handler, Genode::Sandbox::Local_se repeat = true; } } while (repeat); + _config_lock.release(); } + void update_config() + { + _config_lock.acquire(); + _sandbox._heap.free(_habitat_config, sizeof(Xml_node)); + _habitat_config = new (_sandbox._heap) Xml_node(_config_dataspace); + + bool repeat = false; + do { + try { + _sandbox.apply_config(*_habitat_config); + } catch (Genode::Ipc_error) { + Genode::warning("IPC error while applying new configuration."); + repeat = true; + } + } while (repeat); + _config_lock.release(); + } + Hoitaja(Env &env) : _env(env) { _config.sigh(_config_handler); @@ -207,5 +265,26 @@ void Ealan::Launcher_session_component::launch(Genode::String<640> start_node) _hoitaja.add_cell_from_xml(start_node.string()); } +/******************************* + * Shell server implementation * + *******************************/ + +Genode::Ram_dataspace_capability + Ealan::Shell::Session_component::connect() +{ + return _hoitaja._config_ds; +} + +void Ealan::Shell::Session_component::disconnect() +{ +} + +void Ealan::Shell::Session_component::commit() +{ + _hoitaja.update_config(); +} + + + void Component::construct(Genode::Env &env) { static Ealan::Hoitaja main(env); }