ealanos: Added new "Shell" service to control a habitat from a user-space component.

This commit is contained in:
Michael Mueller
2025-06-20 17:32:18 +02:00
parent 07a2adefe4
commit b5657ab546
5 changed files with 249 additions and 8 deletions

View File

@@ -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 <base/rpc_client.h>
#include <ealanos/shell/session.h>
namespace Ealan
{
namespace Shell
{
using Capability = Genode::Capability<Ealan::Shell::Session>;
struct Client : Genode::Rpc_client<Ealan::Shell::Session> {
explicit Client(Capability cap) : Genode::Rpc_client<Ealan::Shell::Session>(cap) { }
Genode::Ram_dataspace_capability connect() override { return call<Rpc_connect>(); }
void disconnect() override { call<Rpc_disconnect>(); }
void commit() override { call<Rpc_commit>(); }
};
} // namespace Shell
}
#endif

View File

@@ -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 <base/rpc_server.h>
#include <base/session_label.h>
#include <root/component.h>
#include <base/session_object.h>
#include <ealanos/shell/session.h>
namespace Ealan {
class Hoitaja;
namespace Shell {
class Session_component : public Genode::Session_object<Ealan::Shell::Session>
{
private:
Genode::Entrypoint &_ep;
Ealan::Hoitaja &_hoitaja;
public:
template <typename... ARGS>
Session_component(Hoitaja &hoitaja, Genode::Entrypoint &ep,
Genode::Session::Resources const &res, ARGS &&...args)
: Genode::Session_object<Session>(ep, res, args...), _ep(ep), _hoitaja(hoitaja)
{
}
Genode::Ram_dataspace_capability connect() override;
void disconnect() override;
void commit() override;
};
}
} // namespace Ealan
#endif

View File

@@ -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 <base/connection.h>
#include <base/env.h>
#include <ealanos/shell/client.h>
namespace Ealan
{
namespace Shell
{
struct Connection : Genode::Connection<Session>, 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<Session>(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

View File

@@ -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 <base/rpc_args.h>
#include <session/session.h>
#include <base/env.h>
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

View File

@@ -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 <base/component.h>
#include <base/attached_rom_dataspace.h>
#include <base/attached_ram_dataspace.h>
#include <sandbox/sandbox.h>
#include <os/reporter.h>
#include <base/log.h>
@@ -21,6 +27,10 @@
#include <child.h>
#include <ealanos/laucher/component.h>
#include <ealanos/shell/component.h>
#include <base/mutex.h>
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> _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<Ealan::Launcher_session_component>;
Launcher_service _launcher_service{_sandbox, *this};
using Shell_service = Genode::Sandbox::Local_service<Ealan::Shell::Session_component>;
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<char*>(_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<char *>(
[&](Genode::Region_map::Range r) {
return reinterpret_cast<char*>(r.start);
},
[&](Genode::Region_map::Attach_error) { return nullptr; });
//_config_dataspace = static_cast<char*>(_heap.alloc(_config.size()*32));
Genode::memcpy(_config_dataspace, _config.local_addr<char*>(), _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<Hoitaja> _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<Genode::Cap_quota>::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<char*>(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); }