diff --git a/repos/gems/src/app/sculpt_manager/model/component.h b/repos/gems/src/app/sculpt_manager/model/component.h index e1e003028d..cdbd19bf8b 100644 --- a/repos/gems/src/app/sculpt_manager/model/component.h +++ b/repos/gems/src/app/sculpt_manager/model/component.h @@ -44,7 +44,8 @@ struct Sculpt::Component : Noncopyable bool blueprint_known = false; - List_model routes { }; + List_model routes { }; + Route pd_route { "" }; Component(Allocator &alloc, Path const &path, Info const &info, Affinity::Space const space) @@ -92,6 +93,22 @@ struct Sculpt::Component : Noncopyable }); } + void gen_pd_cpu_route(Xml_generator &xml) const + { + /* by default pd route goes to parent if nothing is specified */ + if (!pd_route.selected_service.constructed()) + return; + + /* + * Until PD & CPU gets merged, enforce on Sculpt that PD and CPU routes + * go to the same server. + */ + gen_named_node(xml, "service", Sculpt::Service::name_attr(pd_route.required), [&] () { + pd_route.selected_service->gen_xml(xml); }); + gen_named_node(xml, "service", "CPU", [&] () { + pd_route.selected_service->gen_xml(xml); }); + } + bool all_routes_defined() const { bool result = true; diff --git a/repos/gems/src/app/sculpt_manager/model/route.h b/repos/gems/src/app/sculpt_manager/model/route.h index e4ceee7067..7ae5eae67c 100644 --- a/repos/gems/src/app/sculpt_manager/model/route.h +++ b/repos/gems/src/app/sculpt_manager/model/route.h @@ -46,6 +46,7 @@ struct Sculpt::Route : List_model::Element case Service::Type::RTC: return "rtc"; case Service::Type::PLATFORM: return "platform"; case Service::Type::VM: return "vm"; + case Service::Type::PD: return "pd"; case Service::Type::UNDEFINED: break; } return "undefined"; @@ -72,6 +73,7 @@ struct Sculpt::Route : List_model::Element case Service::Type::RTC: return "Real-time clock"; case Service::Type::PLATFORM: return "Device access"; case Service::Type::VM: return "Hardware-based virtualization"; + case Service::Type::PD: return "Protection domain"; case Service::Type::UNDEFINED: break; } return ""; diff --git a/repos/gems/src/app/sculpt_manager/model/runtime_config.h b/repos/gems/src/app/sculpt_manager/model/runtime_config.h index fe073a203d..cba6f24c7e 100644 --- a/repos/gems/src/app/sculpt_manager/model/runtime_config.h +++ b/repos/gems/src/app/sculpt_manager/model/runtime_config.h @@ -350,7 +350,8 @@ class Sculpt::Runtime_config _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" }; + _vm { _r, Type::VM, "virtualization hardware" }, + _pd { _r, Type::PD, "system PD service" }; template void for_each(FN const &fn) const { _r.for_each(fn); } diff --git a/repos/gems/src/app/sculpt_manager/model/runtime_state.h b/repos/gems/src/app/sculpt_manager/model/runtime_state.h index 6c2e6cbedd..f2a24a50da 100644 --- a/repos/gems/src/app/sculpt_manager/model/runtime_state.h +++ b/repos/gems/src/app/sculpt_manager/model/runtime_state.h @@ -157,6 +157,8 @@ class Sculpt::Runtime_state : public Runtime_info construction->gen_affinity_xml(xml); xml.node("route", [&] () { + construction->gen_pd_cpu_route(xml); + construction->routes.for_each([&] (Route const &route) { route.gen_xml(xml); }); }); } diff --git a/repos/gems/src/app/sculpt_manager/model/service.h b/repos/gems/src/app/sculpt_manager/model/service.h index 06e4355f5f..2cca0d5a4e 100644 --- a/repos/gems/src/app/sculpt_manager/model/service.h +++ b/repos/gems/src/app/sculpt_manager/model/service.h @@ -27,7 +27,7 @@ struct Sculpt::Service enum class Type { AUDIO_IN, AUDIO_OUT, BLOCK, FILE_SYSTEM, NIC, GUI, RM, IO_MEM, IO_PORT, IRQ, REPORT, ROM, TERMINAL, TRACE, - USB, RTC, PLATFORM, VM, UNDEFINED }; + USB, RTC, PLATFORM, VM, PD, UNDEFINED }; enum class Match_label { EXACT, LAST }; @@ -61,6 +61,7 @@ struct Sculpt::Service case Type::RTC: return "Rtc"; case Type::PLATFORM: return "Platform"; case Type::VM: return "VM"; + case Type::PD: return "PD"; case Type::UNDEFINED: break; } return "undefined"; diff --git a/repos/gems/src/app/sculpt_manager/view/pd_route_dialog.cc b/repos/gems/src/app/sculpt_manager/view/pd_route_dialog.cc new file mode 100644 index 0000000000..d04b3c3c49 --- /dev/null +++ b/repos/gems/src/app/sculpt_manager/view/pd_route_dialog.cc @@ -0,0 +1,107 @@ +/* + * \brief PD/CPU route assignment dialog + * \author Alexander Boettcher + * \date 2021-02-26 + */ + +/* + * Copyright (C) 2021 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. + */ + +#include + +using namespace Sculpt; + +void Pd_route_dialog::generate(Xml_generator &xml) const +{ + /* find out number of available PD services */ + unsigned count_pd_services = 0; + _runtime_config.for_each_service([&] (Service const &service) { + + if (service.type == Service::Type::PD) + count_pd_services ++; + }); + + /* don't show the PD menu if just the system PD service is available */ + if (count_pd_services <= 1) + return; + + { + Route::Id const pd_id("pd_route"); + + gen_named_node(xml, "frame", pd_id, [&] () { + + xml.node("vbox", [&] () { + + bool const defined = _route.selected_service.constructed(); + + if (!_menu_selected) { + _gen_route_entry(xml, pd_id, + defined ? Component::Info(_route.selected_service->info) + : Component::Info(_route), + defined); + } + + /* + * List of routing options + */ + if (_menu_selected) { + _gen_route_entry(xml, "back", Component::Info(_route), true, "back"); + + unsigned cnt = 0; + _runtime_config.for_each_service([&] (Service const &service) { + + Hoverable_item::Id const id("service.", cnt++); + + bool const service_selected = + _route.selected_service.constructed() && + id == _route.selected_service_id; + + if (service.type == _route.required) + _gen_route_entry(xml, id, service.info, service_selected); + }); + } + }); + }); + } +} + +void Pd_route_dialog::click(Component &component) +{ + if (_route_item.hovered("pd_route")) { + _menu_selected = true; + } + + if (!_menu_selected) + return; + + unsigned cnt = 0; + _runtime_config.for_each_service([&] (Service const &service) { + + Hoverable_item::Id const id("service.", cnt++); + + if (!_route_item.hovered(id)) + return; + + if (_route.selected_service.constructed()) { + if (component.pd_route.selected_service.constructed()) + component.pd_route.selected_service.destruct(); + + _route.selected_service.destruct(); + if (_route_item.hovered(_route.selected_service_id)) { + _route.selected_service_id = Hoverable_item::Id(); + return; + } + } + + component.pd_route.selected_service.construct(service); + + _route.selected_service.construct(service); + _route.selected_service_id = id; + + _menu_selected = false; + }); +} diff --git a/repos/gems/src/app/sculpt_manager/view/pd_route_dialog.h b/repos/gems/src/app/sculpt_manager/view/pd_route_dialog.h new file mode 100644 index 0000000000..6604553c3a --- /dev/null +++ b/repos/gems/src/app/sculpt_manager/view/pd_route_dialog.h @@ -0,0 +1,107 @@ +/* + * \brief PD/CPU route assignment dialog + * \author Alexander Boettcher + * \date 2021-02-26 + */ + +/* + * Copyright (C) 2021 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 _VIEW__PD_ROUTE_DIALOG_H_ +#define _VIEW__PD_ROUTE_DIALOG_H_ + +/* Genode includes */ +#include +#include + +/* local includes */ +#include +#include +#include +#include + +namespace Sculpt { struct Pd_route_dialog; } + + +struct Sculpt::Pd_route_dialog : Noncopyable, Dialog +{ + Route _route { "" }; + Hoverable_item _route_item { }; + bool _menu_selected { false }; + + Runtime_config const &_runtime_config; + + Pd_route_dialog(Runtime_config const &runtime_config) + : + _runtime_config(runtime_config) + { } + + Hover_result hover(Xml_node hover_node) override + { + Dialog::Hover_result const hover_result = hover(hover_node); + return hover_result; + } + + template + Hover_result hover(Xml_node hover, ARGS &&... args) + { + Dialog::Hover_result const hover_result = Dialog::any_hover_changed( + _route_item.match(hover, args...)); + + return hover_result; + } + + void click(Component &); + + void generate(Xml_generator &xml) const override; + + void reset() override + { + if (_route.selected_service.constructed()) + _route.selected_service.destruct(); + _route_item._hovered = Hoverable_item::Id(); + _menu_selected = false; + } + + void click() + { + if (_route_item.hovered("pd_route")) + _menu_selected = true; + } + + void _gen_route_entry(Xml_generator &xml, + Start_name const &name, + Start_name const &text, + bool selected, char const *style = "radio") const + { + gen_named_node(xml, "hbox", name, [&] () { + + gen_named_node(xml, "float", "left", [&] () { + xml.attribute("west", "yes"); + + xml.node("hbox", [&] () { + gen_named_node(xml, "button", "button", [&] () { + + if (selected) + xml.attribute("selected", "yes"); + + xml.attribute("style", style); + _route_item.gen_hovered_attr(xml, name); + + xml.node("hbox", [&] () { }); + }); + gen_named_node(xml, "label", "name", [&] () { + xml.attribute("text", Path(" ", text)); }); + }); + }); + + gen_named_node(xml, "hbox", "right", [&] () { }); + }); + } +}; + +#endif /* _VIEW__PD_ROUTE_DIALOG_H_ */ diff --git a/repos/gems/src/app/sculpt_manager/view/popup_dialog.cc b/repos/gems/src/app/sculpt_manager/view/popup_dialog.cc index ced6416e91..b57488db18 100644 --- a/repos/gems/src/app/sculpt_manager/view/popup_dialog.cc +++ b/repos/gems/src/app/sculpt_manager/view/popup_dialog.cc @@ -85,6 +85,8 @@ void Popup_dialog::_gen_pkg_elements(Xml_generator &xml, }); }); + _pd_route.generate(xml); + if (_resources.constructed() && component.affinity_space.total() > 1) { xml.node("frame", [&] { xml.node("vbox", [&] () { @@ -302,6 +304,7 @@ void Popup_dialog::click(Action &action) _action_item .propose_activation_on_click(); _install_item.propose_activation_on_click(); + _pd_route.click(); Route::Id const clicked_route = _route_item._hovered; @@ -440,7 +443,7 @@ void Popup_dialog::click(Action &action) if (clicked_route == "back") { _state = PKG_SHOWN; _selected_route.destruct(); - + _pd_route.reset(); } else { bool clicked_on_selected_route = false; @@ -490,6 +493,10 @@ void Popup_dialog::click(Action &action) _resources->click(component); }); } + + action.apply_to_construction([&] (Component &component) { + _pd_route.click(component); + }); } } } diff --git a/repos/gems/src/app/sculpt_manager/view/popup_dialog.h b/repos/gems/src/app/sculpt_manager/view/popup_dialog.h index ac99fd98ff..033c615f2b 100644 --- a/repos/gems/src/app/sculpt_manager/view/popup_dialog.h +++ b/repos/gems/src/app/sculpt_manager/view/popup_dialog.h @@ -30,6 +30,7 @@ #include #include +#include #include namespace Sculpt { struct Popup_dialog; } @@ -114,6 +115,7 @@ struct Sculpt::Popup_dialog : Dialog Activatable_item _action_item { }; Activatable_item _install_item { }; Hoverable_item _route_item { }; + Pd_route_dialog _pd_route { _runtime_config }; Constructible _resources { }; @@ -198,6 +200,8 @@ struct Sculpt::Popup_dialog : Dialog _install_item.match(hover, "frame", "vbox", "float", "vbox", "float", "button", "name"), _route_item .match(hover, "frame", "vbox", "frame", "vbox", "hbox", "name")); + _pd_route.hover(hover, "frame", "vbox", "frame", "vbox", "hbox", "name"); + if (_resources.constructed() && hover_result == Dialog::Hover_result::UNMODIFIED) return _resources->hover(hover, "frame", "vbox", "frame", "vbox"); @@ -407,6 +411,7 @@ struct Sculpt::Popup_dialog : Dialog _selected_route.destruct(); _menu._level = 0; _resources.destruct(); + _pd_route.reset(); } Popup_dialog(Env &env, Refresh &refresh,