genode/repos/gems/src/app/sculpt_manager/model/runtime_config.h

402 lines
11 KiB
C++

/*
* \brief Cached information of the current runtime configuration
* \author Norman Feske
* \date 2019-02-22
*/
/*
* Copyright (C) 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.
*/
#ifndef _MODEL__RUNTIME_CONFIG_H_
#define _MODEL__RUNTIME_CONFIG_H_
/* Genode includes */
#include <util/xml_node.h>
#include <util/list_model.h>
#include <base/registry.h>
/* local includes */
#include <types.h>
#include <model/service.h>
namespace Sculpt { class Runtime_config; }
class Sculpt::Runtime_config
{
private:
Allocator &_alloc;
/**
* Return target name of route specified by <service> node
*
* For a route to another child, the target name is the child name.
* For a route to the parent, the target name expresses a role of
* the parent:
*
* - 'hardware' provides access to hardware
* - 'config' allows the change of the systems configuration
* - 'info' reveals system information
* - 'GUI' connects to the nitpicker GUI server
*/
static Start_name _to_name(Xml_node node)
{
Start_name result { };
node.with_sub_node("child", [&] (Xml_node child) {
result = child.attribute_value("name", Start_name()); });
if (result.valid())
return result;
node.with_sub_node("parent", [&] (Xml_node parent) {
Service::Type_name const service =
node.attribute_value("name", Service::Type_name());
Label const dst_label =
parent.attribute_value("label", Label());
bool const ignored_service = (service == "CPU")
|| (service == "PD")
|| (service == "Report")
|| (service == "Timer")
|| (service == "LOG");
if (ignored_service)
return;
bool const hardware = (service == "Platform")
|| (service == "IO_PORT")
|| (service == "IO_MEM")
|| (service == "Rtc")
|| (service == "IRQ")
|| (service == "TRACE");
if (hardware) {
result = "hardware";
return;
}
bool const usb = (service == "Usb");
if (usb) {
result = "usb";
return;
}
bool const storage = (service == "Block");
if (storage) {
result = "storage";
return;
}
if (service == "ROM") {
/*
* ROM sessions for plain binaries (e.g, as requested by
* the sculpt-managed inspect or part_block instances) are
* not interesting for the graph. Non-sculpt-managed
* subsystems can only be connected to the few ROMs
* whitelisted in the 'Parent_services' definition below.
*/
bool const interesting_rom =
dst_label.valid() &&
(strcmp("config", dst_label.string(), 5) == 0 ||
dst_label == "platform_info" ||
dst_label == "capslock");
if (interesting_rom) {
result = "info";
return;
}
}
if (service == "File_system") {
if (dst_label == "config") {
result = "config";
return;
}
if (dst_label == "fonts" || dst_label == "report") {
result = "info";
return;
}
}
if (service == "Nitpicker") {
result = "GUI";
return;
}
});
return result;
}
/**
* Return component name targeted by the first route of the start node
*/
static Start_name _primary_dependency(Xml_node const start)
{
Start_name result { };
start.with_sub_node("route", [&] (Xml_node route) {
route.with_sub_node("service", [&] (Xml_node service) {
result = _to_name(service); }); });
return result;
}
struct Child_service : Service, List_model<Child_service>::Element
{
static Service::Type type_from_xml(Xml_node service)
{
auto const name = service.attribute_value("name", Service::Type_name());
for (unsigned i = 0; i < (unsigned)Type::UNDEFINED; i++) {
Type const t = (Type)i;
if (name == Service::name_attr(t))
return t;
}
return Type::UNDEFINED;
}
Child_service(Start_name server, Xml_node provides)
: Service(server, type_from_xml(provides), Label()) { }
struct Update_policy
{
typedef Child_service Element;
Start_name _server;
Allocator &_alloc;
Update_policy(Start_name const &server, Allocator &alloc)
: _server(server), _alloc(alloc) { }
void destroy_element(Element &elem) { destroy(_alloc, &elem); }
Element &create_element(Xml_node node)
{
return *new (_alloc) Child_service(_server, node);
}
void update_element(Element &, Xml_node) { }
static bool element_matches_xml_node(Element const &elem, Xml_node node)
{
return type_from_xml(node) == elem.type;
}
static bool node_is_element(Xml_node node)
{
return type_from_xml(node) != Service::Type::UNDEFINED;
}
};
};
public:
struct Component : List_model<Component>::Element
{
Start_name const name;
Start_name primary_dependency { };
struct Dep : List_model<Dep>::Element
{
Start_name const to_name;
Dep(Start_name to_name) : to_name(to_name) { }
struct Update_policy
{
typedef Dep Element;
Allocator &_alloc;
Update_policy(Allocator &alloc) : _alloc(alloc) { }
void destroy_element(Dep &elem) { destroy(_alloc, &elem); }
Dep &create_element(Xml_node node)
{
return *new (_alloc) Dep(_to_name(node));
}
void update_element(Dep &, Xml_node) { }
static bool element_matches_xml_node(Dep const &elem, Xml_node node)
{
return _to_name(node) == elem.to_name;
}
static bool node_is_element(Xml_node node)
{
return _to_name(node).valid();
}
};
};
/* dependencies on other child components */
List_model<Dep> deps { };
template <typename FN>
void for_each_secondary_dep(FN const &fn) const
{
deps.for_each([&] (Dep const &dep) {
if (dep.to_name != primary_dependency)
fn(dep.to_name); });
}
List_model<Child_service> _child_services { };
Component(Start_name const &name) : name(name) { }
template <typename FN>
void for_each_service(FN const &fn) const
{
_child_services.for_each(fn);
}
struct Update_policy
{
typedef Component Element;
Allocator &_alloc;
Update_policy(Allocator &alloc) : _alloc(alloc) { }
void destroy_element(Component &elem)
{
/* flush list models */
update_element(elem, Xml_node("<start> <route/> <provides/> </start>"));
destroy(_alloc, &elem);
}
Component &create_element(Xml_node node)
{
return *new (_alloc)
Component(node.attribute_value("name", Start_name()));
}
void update_element(Component &elem, Xml_node node)
{
elem.primary_dependency = _primary_dependency(node);
{
Dep::Update_policy policy { _alloc };
node.with_sub_node("route", [&] (Xml_node route) {
elem.deps.update_from_xml(policy, route); });
}
{
Child_service::Update_policy policy { elem.name, _alloc };
node.with_sub_node("provides", [&] (Xml_node provides) {
elem._child_services.update_from_xml(policy,
provides); });
}
}
static bool element_matches_xml_node(Component const &elem, Xml_node node)
{
return node.attribute_value("name", Start_name()) == elem.name;
}
static bool node_is_element(Xml_node node) { return node.has_type("start"); }
};
};
private:
List_model<Component> _components { };
struct Parent_services
{
typedef Registered_no_delete<Service> Parent_service;
typedef Service::Type Type;
Registry<Parent_service> _r { };
Parent_service const
_focus { _r, Type::NITPICKER, "keyboard focus", "focus" },
_backdrop { _r, Type::NITPICKER, "desktop background", "backdrop" },
_lockscreen{ _r, Type::NITPICKER, "desktop lock screen", "lock_screen" },
_nitpicker { _r, Type::NITPICKER, "system GUI server" },
_config_fs { _r, Type::FILE_SYSTEM, "writeable system configuration", "config" },
_report_fs { _r, Type::FILE_SYSTEM, "read-only system reports", "report" },
_capslock { _r, Type::ROM, "global capslock state", "capslock" },
_vimrc { _r, Type::ROM, "default vim configuration", "config -> vimrc" },
_fonts { _r, Type::ROM, "system font configuration", "config -> managed/fonts" },
_pf_info { _r, Type::ROM, "platform information", "platform_info" },
_system { _r, Type::ROM, "system status", "config -> system" },
_report { _r, Type::REPORT, "system reports" },
_shape { _r, Type::REPORT, "pointer shape", "shape", Service::Match_label::LAST },
_copy { _r, Type::REPORT, "global clipboard", "clipboard", Service::Match_label::LAST },
_paste { _r, Type::ROM, "global clipboard", "clipboard", Service::Match_label::LAST },
_rm { _r, Type::RM, "custom virtual memory objects" },
_io_mem { _r, Type::IO_MEM, "raw hardware access" },
_io_port { _r, Type::IO_PORT, "raw hardware access" },
_irq { _r, Type::IRQ, "raw hardware access" },
_rtc { _r, Type::RTC, "system clock" },
_block { _r, Type::BLOCK, "direct block-device access" },
_usb { _r, Type::USB, "direct USB-device access" },
_pci_wifi { _r, Type::PLATFORM, "wifi hardware", "wifi" },
_pci_net { _r, Type::PLATFORM, "network hardware", "nic" },
_pci_audio { _r, Type::PLATFORM, "audio hardware", "audio" },
_pci_acpi { _r, Type::PLATFORM, "ACPI", "acpica" },
_trace { _r, Type::TRACE, "system-global tracing" },
_vm { _r, Type::VM, "virtualization hardware" };
template <typename FN>
void for_each(FN const &fn) const { _r.for_each(fn); }
} _parent_services { };
Service const _used_fs_service { "default_fs_rw",
Service::Type::FILE_SYSTEM,
Label(), "used file system" };
public:
Runtime_config(Allocator &alloc) : _alloc(alloc) { }
void update_from_xml(Xml_node config)
{
Component::Update_policy policy(_alloc);
_components.update_from_xml(policy, config);
}
template <typename FN>
void for_each_component(FN const &fn) const { _components.for_each(fn); }
/**
* Call 'fn' with the name of each dependency of component 'name'
*/
template <typename FN>
void for_each_dependency(Start_name const &name, FN const &fn) const
{
_components.for_each([&] (Component const &component) {
if (component.name == name) {
component.deps.for_each([&] (Component::Dep const &dep) {
fn(dep.to_name); }); } });
}
template <typename FN>
void for_each_service(FN const &fn) const
{
_parent_services.for_each(fn);
fn(_used_fs_service);
_components.for_each([&] (Component const &component) {
component.for_each_service(fn); });
}
};
#endif /* _MODEL__RUNTIME_CONFIG_H_ */