432 lines
11 KiB
C++
432 lines
11 KiB
C++
/*
|
|
* \brief Loader service
|
|
* \author Norman Feske
|
|
* \date 2012-04-17
|
|
*/
|
|
|
|
/*
|
|
* Copyright (C) 2010-2017 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.
|
|
*/
|
|
|
|
/* Genode includes */
|
|
#include <base/component.h>
|
|
#include <base/heap.h>
|
|
#include <base/sleep.h>
|
|
#include <loader_session/loader_session.h>
|
|
#include <root/component.h>
|
|
#include <os/session_policy.h>
|
|
|
|
/* local includes */
|
|
#include <child.h>
|
|
#include <nitpicker.h>
|
|
#include <rom.h>
|
|
|
|
|
|
namespace Loader {
|
|
|
|
using namespace Genode;
|
|
|
|
class Session_component;
|
|
class Root;
|
|
}
|
|
|
|
|
|
class Loader::Session_component : public Rpc_object<Session>
|
|
{
|
|
private:
|
|
|
|
struct Local_rom_factory : Local_service<Rom_session_component>::Factory
|
|
{
|
|
Entrypoint &_ep;
|
|
Allocator &_md_alloc;
|
|
Rom_module_registry &_rom_modules;
|
|
Lock _lock { };
|
|
List<Rom_session_component> _rom_sessions { };
|
|
|
|
void _close(Rom_session_component &rom)
|
|
{
|
|
_rom_sessions.remove(&rom);
|
|
Genode::destroy(_md_alloc, &rom);
|
|
}
|
|
|
|
Local_rom_factory(Entrypoint &ep,
|
|
Allocator &md_alloc,
|
|
Rom_module_registry &rom_modules)
|
|
:
|
|
_ep(ep), _md_alloc(md_alloc), _rom_modules(rom_modules)
|
|
{ }
|
|
|
|
~Local_rom_factory()
|
|
{
|
|
Lock::Guard guard(_lock);
|
|
|
|
while (_rom_sessions.first())
|
|
_close(*_rom_sessions.first());
|
|
}
|
|
|
|
Rom_session_component &create(Args const &args, Affinity) override
|
|
{
|
|
/* try to find ROM module at local ROM service */
|
|
try {
|
|
Lock::Guard guard(_lock);
|
|
|
|
Session_label const label = label_from_args(args.string());
|
|
Session_label const name = label.last_element();
|
|
|
|
Rom_module &module = _rom_modules.lookup_and_lock(name.string());
|
|
|
|
Rom_session_component *rom = new (_md_alloc)
|
|
Rom_session_component(_ep, module);
|
|
|
|
_rom_sessions.insert(rom);
|
|
|
|
return *rom;
|
|
|
|
} catch (...) { }
|
|
|
|
throw Service_denied();
|
|
}
|
|
|
|
void upgrade(Rom_session_component &, Args const &) override { }
|
|
|
|
void destroy(Rom_session_component &session) override
|
|
{
|
|
Lock::Guard guard(_lock);
|
|
|
|
_close(session);
|
|
}
|
|
};
|
|
|
|
typedef Local_service<Rom_session_component> Local_rom_service;
|
|
|
|
/**
|
|
* Common base class of 'Local_cpu_service' and 'Local_pd_service'
|
|
*/
|
|
struct Intercepted_parent_service : Genode::Parent_service
|
|
{
|
|
Signal_context_capability fault_sigh { };
|
|
|
|
Intercepted_parent_service(Env &env, Service::Name const &name)
|
|
: Parent_service(env, name) { }
|
|
};
|
|
|
|
/**
|
|
* Intercept CPU session requests to install default exception
|
|
* handler
|
|
*/
|
|
struct Local_cpu_service : Intercepted_parent_service
|
|
{
|
|
Local_cpu_service(Env &env) : Intercepted_parent_service(env, "CPU") { }
|
|
|
|
void initiate_request(Session_state &session) override
|
|
{
|
|
Intercepted_parent_service::initiate_request(session);
|
|
|
|
if (session.phase != Session_state::AVAILABLE)
|
|
return;
|
|
|
|
Cpu_session_client cpu(reinterpret_cap_cast<Cpu_session>(session.cap));
|
|
cpu.exception_sigh(fault_sigh);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Intercept PD session requests to install default fault handler
|
|
*/
|
|
struct Local_pd_service : Intercepted_parent_service
|
|
{
|
|
Local_pd_service(Env &env) : Intercepted_parent_service(env, "PD") { }
|
|
|
|
void initiate_request(Session_state &session) override
|
|
{
|
|
Intercepted_parent_service::initiate_request(session);
|
|
|
|
if (session.phase != Session_state::AVAILABLE)
|
|
return;
|
|
|
|
Pd_session_client pd(reinterpret_cap_cast<Pd_session>(session.cap));
|
|
|
|
Region_map_client(pd.address_space()).fault_handler(fault_sigh);
|
|
Region_map_client(pd.stack_area()) .fault_handler(fault_sigh);
|
|
Region_map_client(pd.linker_area()) .fault_handler(fault_sigh);
|
|
}
|
|
};
|
|
|
|
struct Local_nitpicker_factory : Local_service<Nitpicker::Session_component>::Factory
|
|
{
|
|
Entrypoint &_ep;
|
|
Env &_env;
|
|
Region_map &_rm;
|
|
Ram_allocator &_ram;
|
|
|
|
Area _max_size { };
|
|
Nitpicker::View_capability _parent_view { };
|
|
|
|
Signal_context_capability view_ready_sigh { };
|
|
|
|
Constructible<Nitpicker::Session_component> session { };
|
|
|
|
Local_nitpicker_factory(Entrypoint &ep, Env &env,
|
|
Region_map &rm, Ram_allocator &ram)
|
|
: _ep(ep), _env(env), _rm(rm), _ram(ram) { }
|
|
|
|
void constrain_geometry(Area size) { _max_size = size; }
|
|
|
|
void parent_view(Nitpicker::View_capability view)
|
|
{
|
|
_parent_view = view;
|
|
}
|
|
|
|
Nitpicker::Session_component &create(Args const &args, Affinity) override
|
|
{
|
|
if (session.constructed()) {
|
|
warning("attempt to open more than one nitpicker session");
|
|
throw Service_denied();
|
|
}
|
|
|
|
session.construct(_ep, _env, _rm, _ram, _max_size,
|
|
_parent_view, view_ready_sigh, args.string());
|
|
return *session;
|
|
}
|
|
|
|
void upgrade(Nitpicker::Session_component &, Args const &) override { }
|
|
void destroy(Nitpicker::Session_component &) override { }
|
|
};
|
|
|
|
typedef Local_service<Nitpicker::Session_component> Local_nitpicker_service;
|
|
|
|
enum { STACK_SIZE = 2*4096 };
|
|
|
|
Env &_env;
|
|
Session_label const _label;
|
|
Xml_node const _config;
|
|
Cap_quota const _cap_quota;
|
|
Ram_quota const _ram_quota;
|
|
Cap_quota_guard _cap_guard { _cap_quota };
|
|
Ram_quota_guard _ram_guard { _ram_quota };
|
|
Constrained_ram_allocator _local_ram { _env.ram(), _ram_guard, _cap_guard };
|
|
Heap _md_alloc { _local_ram, _env.rm() };
|
|
size_t _subsystem_cap_quota_limit = 0;
|
|
size_t _subsystem_ram_quota_limit = 0;
|
|
Parent_services _parent_services { };
|
|
Rom_module_registry _rom_modules { _env, _config, _local_ram, _md_alloc };
|
|
Local_rom_factory _rom_factory { _env.ep(), _md_alloc, _rom_modules };
|
|
Local_rom_service _rom_service { _rom_factory };
|
|
Local_cpu_service _cpu_service { _env };
|
|
Local_pd_service _pd_service { _env };
|
|
Local_nitpicker_factory _nitpicker_factory { _env.ep(), _env, _env.rm(), _local_ram };
|
|
Local_nitpicker_service _nitpicker_service { _nitpicker_factory };
|
|
Signal_context_capability _fault_sigh { };
|
|
Constructible<Child> _child { };
|
|
|
|
/**
|
|
* Return virtual nitpicker session component
|
|
*/
|
|
Nitpicker::Session_component &_virtual_nitpicker_session()
|
|
{
|
|
if (!_nitpicker_factory.session.constructed())
|
|
throw View_does_not_exist();
|
|
|
|
return *_nitpicker_factory.session;
|
|
}
|
|
|
|
Nitpicker::Session_component const &_virtual_nitpicker_session() const
|
|
{
|
|
if (!_nitpicker_factory.session.constructed())
|
|
throw View_does_not_exist();
|
|
|
|
return *_nitpicker_factory.session;
|
|
}
|
|
|
|
public:
|
|
|
|
/**
|
|
* Constructor
|
|
*/
|
|
Session_component(Env &env, Session_label const &label, Xml_node config,
|
|
Cap_quota cap_quota, Ram_quota ram_quota)
|
|
:
|
|
_env(env), _label(label), _config(config),
|
|
_cap_quota(cap_quota), _ram_quota(ram_quota)
|
|
{
|
|
/* fetch all parent-provided ROMs according to the config */
|
|
config.for_each_sub_node("parent-rom", [&] (Xml_node rom)
|
|
{
|
|
typedef Rom_module::Name Name;
|
|
Name name = rom.attribute_value("name", Name());
|
|
_rom_modules.fetch_parent_rom_module(name);
|
|
});
|
|
}
|
|
|
|
~Session_component()
|
|
{
|
|
_child.destruct();
|
|
|
|
/*
|
|
* The parent-service registry is populated by the 'Child'
|
|
* on demand. Revert those allocations.
|
|
*/
|
|
_parent_services.for_each([&] (Parent_service &service) {
|
|
destroy(_md_alloc, &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 cap_quota(Cap_quota caps) override
|
|
{
|
|
_subsystem_cap_quota_limit = caps.value;
|
|
}
|
|
|
|
void ram_quota(Ram_quota quantum) override
|
|
{
|
|
_subsystem_ram_quota_limit = quantum.value;
|
|
}
|
|
|
|
void constrain_geometry(Area size) override
|
|
{
|
|
_nitpicker_factory.constrain_geometry(size);
|
|
}
|
|
|
|
void parent_view(Nitpicker::View_capability view) override
|
|
{
|
|
_nitpicker_factory.parent_view(view);
|
|
}
|
|
|
|
void view_ready_sigh(Signal_context_capability sigh) override
|
|
{
|
|
_nitpicker_factory.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;
|
|
|
|
/*
|
|
* Region-map fault handler for PD sessions originating from the
|
|
* subsystem.
|
|
*/
|
|
_pd_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.constructed()) {
|
|
warning("cannot start subsystem twice");
|
|
return;
|
|
}
|
|
|
|
size_t const cap_quota = (_subsystem_cap_quota_limit > 0)
|
|
? min(_subsystem_cap_quota_limit, _cap_quota.value)
|
|
: _cap_quota.value;
|
|
|
|
size_t const ram_quota = (_subsystem_ram_quota_limit > 0)
|
|
? min(_subsystem_ram_quota_limit, _ram_quota.value)
|
|
: _ram_quota.value;
|
|
|
|
try {
|
|
_child.construct(_env, _md_alloc, binary_name.string(),
|
|
prefixed_label(_label, Session_label(label.string())),
|
|
Cap_quota{cap_quota}, Ram_quota{ram_quota},
|
|
_parent_services, _rom_service,
|
|
_cpu_service, _pd_service, _nitpicker_service,
|
|
_fault_sigh);
|
|
}
|
|
catch (Genode::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<Session_component>
|
|
{
|
|
private:
|
|
|
|
Env &_env;
|
|
Xml_node const _config;
|
|
|
|
protected:
|
|
|
|
Session_component *_create_session(const char *args)
|
|
{
|
|
Xml_node session_config("<policy/>");
|
|
|
|
Session_label const label = label_from_args(args);
|
|
|
|
try { session_config = Session_policy(label, _config); }
|
|
catch (...) { }
|
|
|
|
return new (md_alloc()) Session_component(_env, label, session_config,
|
|
cap_quota_from_args(args),
|
|
ram_quota_from_args(args));
|
|
}
|
|
|
|
public:
|
|
|
|
Root(Env &env, Xml_node config, Allocator &md_alloc)
|
|
:
|
|
Root_component<Session_component>(&env.ep().rpc_ep(), &md_alloc),
|
|
_env(env), _config(config)
|
|
{ }
|
|
};
|
|
|
|
|
|
namespace Loader { struct Main; }
|
|
|
|
|
|
struct Loader::Main
|
|
{
|
|
Env &_env;
|
|
|
|
Heap _heap { _env.ram(), _env.rm() };
|
|
|
|
Attached_rom_dataspace _config { _env, "config" };
|
|
|
|
Root _root { _env, _config.xml(), _heap };
|
|
|
|
Main(Env &env) : _env(env)
|
|
{
|
|
_env.parent().announce(_env.ep().manage(_root));
|
|
}
|
|
};
|
|
|
|
|
|
void Component::construct(Genode::Env &env) { static Loader::Main main(env); }
|