/* * \brief Loader service * \author Norman Feske * \date 2012-04-17 */ /* * Copyright (C) 2010-2013 Genode Labs GmbH * * This file is part of the Genode OS framework, which is distributed * under the terms of the GNU General Public License version 2. */ /* Genode includes */ #include #include #include #include #include #include #include #include /* local includes */ #include #include #include #include namespace Loader { using namespace Genode; class Session_component; class Root; } class Loader::Session_component : public Rpc_object { private: struct Local_rom_service : Service { Rpc_entrypoint &_ep; Allocator &_md_alloc; Parent_service _parent_rom_service; Rom_module_registry &_rom_modules; Lock _lock; List _rom_sessions; void _close(Rom_session_component *rom) { _rom_sessions.remove(rom); destroy(&_md_alloc, rom); } Local_rom_service(Rpc_entrypoint &ep, Allocator &md_alloc, Rom_module_registry &rom_modules) : Service("virtual_rom"), _ep(ep), _md_alloc(md_alloc), _parent_rom_service(Rom_session::service_name()), _rom_modules(rom_modules) { } ~Local_rom_service() { Lock::Guard guard(_lock); while (_rom_sessions.first()) { _ep.remove(_rom_sessions.first()); _close(_rom_sessions.first()); } } Genode::Session_capability session(char const *args, Affinity const &affinity) { /* try to find ROM module at local ROM service */ try { Lock::Guard guard(_lock); char name[Session::Name::MAX_SIZE]; /* extract filename from session arguments */ Arg_string::find_arg(args, "filename") .string(name, sizeof(name), ""); Rom_module &module = _rom_modules.lookup_and_lock(name); Rom_session_component *rom = new (&_md_alloc) Rom_session_component(module); _rom_sessions.insert(rom); return _ep.manage(rom); } catch (...) { } /* fall back to parent_rom_service */ return _parent_rom_service.session(args, affinity); } void close(Session_capability session) { Lock::Guard guard(_lock); Rom_session_component *component; _ep.apply(session, [&] (Rom_session_component *rsc) { component = rsc; if (component) _ep.remove(component); }); if (component) { _close(component); return; } _parent_rom_service.close(session); } void upgrade(Session_capability session, const char *) { } }; /** * Common base class of 'Local_cpu_service' and 'Local_rm_service' */ struct Intercepted_parent_service : Service { Signal_context_capability fault_sigh; Intercepted_parent_service(char const *name) : Service(name) { } void close(Session_capability session) { env()->parent()->close(session); } void upgrade(Session_capability session, const char *) { } }; /** * Intercept CPU session requests to install default exception * handler */ struct Local_cpu_service : Intercepted_parent_service { Local_cpu_service() : Intercepted_parent_service("CPU") { } Genode::Session_capability session(char const *args, Affinity const &affinity) { Capability cap = env()->parent()->session(args, affinity); Cpu_session_client(cap).exception_handler(Thread_capability(), fault_sigh); return cap; } void upgrade(Session_capability session, const char *args) { try { env()->parent()->upgrade(session, args); } catch (Genode::Ipc_error) { throw Unavailable(); } } }; /** * Intercept RM session requests to install default fault handler */ struct Local_rm_service : Intercepted_parent_service { Local_rm_service() : Intercepted_parent_service("RM") { } Genode::Session_capability session(char const *args, Affinity const &affinity) { Capability cap = env()->parent()->session(args, affinity); Rm_session_client(cap).fault_handler(fault_sigh); return cap; } }; struct Local_nitpicker_service : Service { Rpc_entrypoint &_ep; Ram_session &_ram; Allocator &_md_alloc; Area _max_size; Nitpicker::View_capability _parent_view; Signal_context_capability view_ready_sigh; Nitpicker::Session_component *open_session; Local_nitpicker_service(Rpc_entrypoint &ep, Ram_session &ram, Allocator &md_alloc) : Service("virtual_nitpicker"), _ep(ep), _ram(ram), _md_alloc(md_alloc), open_session(0) { } ~Local_nitpicker_service() { if (!open_session) return; _ep.dissolve(open_session); destroy(&_md_alloc, open_session); } void constrain_geometry(Area size) { _max_size = size; } void parent_view(Nitpicker::View_capability view) { _parent_view = view; } Genode::Session_capability session(char const *args, Affinity const &) { if (open_session) throw Unavailable(); open_session = new (&_md_alloc) Nitpicker::Session_component(_ep, _ram, _max_size, _parent_view, view_ready_sigh, args); return _ep.manage(open_session); } void upgrade(Genode::Session_capability session, const char *) { } }; enum { STACK_SIZE = 2*4096 }; size_t _ram_quota; Ram_session_client_guard _ram_session_client; Heap _md_alloc; size_t _subsystem_ram_quota_limit; Rpc_entrypoint _ep; Service_registry _parent_services; Rom_module_registry _rom_modules; Local_rom_service _rom_service; Local_cpu_service _cpu_service; Local_rm_service _rm_service; Local_nitpicker_service _nitpicker_service; Signal_context_capability _fault_sigh; Child *_child; /** * Return virtual nitpicker session component */ Nitpicker::Session_component &_virtual_nitpicker_session() const { if (!_nitpicker_service.open_session) throw View_does_not_exist(); return *_nitpicker_service.open_session; } public: /** * Constructor */ Session_component(size_t quota, Ram_session &ram, Cap_session &cap) : _ram_quota(quota), _ram_session_client(env()->ram_session_cap(), _ram_quota), _md_alloc(&_ram_session_client, env()->rm_session()), _subsystem_ram_quota_limit(0), _ep(&cap, STACK_SIZE, "session_ep"), _rom_modules(_ram_session_client, _md_alloc), _rom_service(_ep, _md_alloc, _rom_modules), _nitpicker_service(_ep, _ram_session_client, _md_alloc), _child(0) { } ~Session_component() { if (_child) destroy(&_md_alloc, _child); /* * The parent-service registry is populated by the 'Child' * on demand. Revert those allocations. */ while (Service *service = _parent_services.find_by_server(0)) { _parent_services.remove(service); destroy(env()->heap(), service); } } /****************************** ** Loader session interface ** ******************************/ Dataspace_capability alloc_rom_module(Name const &name, size_t size) override { return _rom_modules.alloc_rom_module(name.string(), size); } void commit_rom_module(Name const &name) override { try { _rom_modules.commit_rom_module(name.string()); } catch (Rom_module_registry::Lookup_failed) { throw Rom_module_does_not_exist(); } } void ram_quota(size_t quantum) override { _subsystem_ram_quota_limit = quantum; } void constrain_geometry(Area size) override { _nitpicker_service.constrain_geometry(size); } void parent_view(Nitpicker::View_capability view) override { _nitpicker_service.parent_view(view); } void view_ready_sigh(Signal_context_capability sigh) override { _nitpicker_service.view_ready_sigh = sigh; } void fault_sigh(Signal_context_capability sigh) override { /* * CPU exception handler for CPU sessions originating from the * subsystem. */ _cpu_service.fault_sigh = sigh; /* * RM fault handler for RM sessions originating from the * subsystem. */ _rm_service.fault_sigh = sigh; /* * CPU exception and RM fault handler for the immediate child. */ _fault_sigh = sigh; } void start(Name const &binary_name, Name const &label) override { if (_child) { PWRN("cannot start subsystem twice"); return; } size_t const ram_quota = (_subsystem_ram_quota_limit > 0) ? min(_subsystem_ram_quota_limit, _ram_session_client.avail()) : _ram_session_client.avail(); try { _child = new (&_md_alloc) Child(binary_name.string(), label.string(), _ep, _ram_session_client, ram_quota, _parent_services, _rom_service, _cpu_service, _rm_service, _nitpicker_service, _fault_sigh); } catch (Genode::Parent::Service_denied) { throw Rom_module_does_not_exist(); } } void view_geometry(Rect rect, Point offset) override { _virtual_nitpicker_session().loader_view_geometry(rect, offset); } Area view_size() const override { return _virtual_nitpicker_session().loader_view_size(); } }; class Loader::Root : public Root_component { private: Ram_session &_ram; Cap_session &_cap; protected: Session_component *_create_session(const char *args) { size_t quota = Arg_string::find_arg(args, "ram_quota").ulong_value(0); return new (md_alloc()) Session_component(quota, _ram, _cap); } public: /** * Constructor * * \param session_ep entry point for managing ram session objects * \param md_alloc meta-data allocator to be used by root * component */ Root(Rpc_entrypoint &session_ep, Allocator &md_alloc, Ram_session &ram, Cap_session &cap) : Root_component(&session_ep, &md_alloc), _ram(ram), _cap(cap) { } }; int main() { using namespace Genode; enum { STACK_SIZE = 8*1024 }; static Cap_connection cap; static Rpc_entrypoint ep(&cap, STACK_SIZE, "loader_ep"); static Loader::Root root(ep, *env()->heap(), *env()->ram_session(), cap); env()->parent()->announce(ep.manage(&root)); sleep_forever(); return 0; }