/* * \brief PD service used by Noux processes * \author Norman Feske * \date 2016-04-20 * * The custom implementation of the PD session interface provides a pool of * RAM shared by Noux and all Noux processes. The use of a shared pool * alleviates the need to assign RAM quota to individual Noux processes. * * Furthermore, the custom implementation is needed to get hold of the RAM * dataspaces allocated by each Noux process. When forking a process, the * acquired information (in the form of 'Ram_dataspace_info' objects) is used * to create a shadow copy of the forking address space. */ /* * Copyright (C) 2016-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. */ #ifndef _NOUX__PD_SESSION_COMPONENT_H_ #define _NOUX__PD_SESSION_COMPONENT_H_ /* Genode includes */ #include #include #include /* Noux includes */ #include #include namespace Noux { struct Ram_dataspace_info; struct Pd_session_component; using namespace Genode; } struct Noux::Ram_dataspace_info : Dataspace_info, private List::Element { friend class List; Ram_dataspace_info(Ram_dataspace_capability ds_cap) : Dataspace_info(ds_cap) { } Dataspace_capability fork(Ram_allocator &ram, Region_map &local_rm, Allocator &alloc, Dataspace_registry &ds_registry, Rpc_entrypoint &) override { size_t const size = Dataspace_client(ds_cap()).size(); Ram_dataspace_capability dst_ds_cap; try { dst_ds_cap = ram.alloc(size); Attached_dataspace src_ds(local_rm, ds_cap()); Attached_dataspace dst_ds(local_rm, dst_ds_cap); memcpy(dst_ds.local_addr(), src_ds.local_addr(), size); ds_registry.insert(new (alloc) Ram_dataspace_info(dst_ds_cap)); return dst_ds_cap; } catch (...) { error("fork of RAM dataspace failed"); if (dst_ds_cap.valid()) ram.free(dst_ds_cap); return Dataspace_capability(); } } void poke(Region_map &rm, addr_t dst_offset, char const *src, size_t len) override { if (!src) return; if ((dst_offset >= size()) || (dst_offset + len > size())) { error("illegal attemt to write beyond dataspace boundary"); return; } try { Attached_dataspace ds(rm, ds_cap()); memcpy(ds.local_addr() + dst_offset, src, len); } catch (...) { warning("poke: failed to attach RAM dataspace"); } } }; class Noux::Pd_session_component : public Rpc_object { private: Rpc_entrypoint &_ep; Pd_connection _pd; Pd_session &_ref_pd; Region_map_component _address_space; Region_map_component _stack_area; Region_map_component _linker_area; Allocator &_alloc; Ram_allocator &_ram; Ram_quota _used_ram_quota { 0 }; List _ds_list { }; Dataspace_registry &_ds_registry; template auto _with_automatic_cap_upgrade(FUNC func) -> decltype(func()) { Cap_quota upgrade { 10 }; enum { NUM_ATTEMPTS = 3 }; return retry( [&] () { return func(); }, [&] () { _ref_pd.transfer_quota(_pd.rpc_cap(), upgrade); }, NUM_ATTEMPTS); } public: /** * Constructor */ Pd_session_component(Allocator &alloc, Env &env, Rpc_entrypoint &ep, Child_policy::Name const &name, Dataspace_registry &ds_registry) : _ep(ep), _pd(env, name.string()), _ref_pd(env.pd()), _address_space(alloc, _ep, ds_registry, _pd, _pd.address_space()), _stack_area (alloc, _ep, ds_registry, _pd, _pd.stack_area()), _linker_area (alloc, _ep, ds_registry, _pd, _pd.linker_area()), _alloc(alloc), _ram(env.ram()), _ds_registry(ds_registry) { _ep.manage(this); /* * Equip the PD with an initial cap quota that suffices in the * common case. Further capabilities are provisioned on demand * via '_with_automatic_cap_upgrade'. */ _pd.ref_account(env.pd_session_cap()); _ref_pd.transfer_quota(_pd.rpc_cap(), Cap_quota{10}); } ~Pd_session_component() { _ep.dissolve(this); Ram_dataspace_info *info = 0; while ((info = _ds_list.first())) free(static_cap_cast(info->ds_cap())); } Pd_session_capability core_pd_cap() { return _pd.cap(); } void poke(Region_map &rm, addr_t dst_addr, char const *src, size_t len) { _address_space.poke(rm, dst_addr, src, len); } Capability lookup_region_map(addr_t const addr) { return _address_space.lookup_region_map(addr); } Region_map &address_space_region_map() { return _address_space; } Region_map &linker_area_region_map() { return _linker_area; } Region_map &stack_area_region_map() { return _stack_area; } void replay(Pd_session_component &dst_pd, Region_map &local_rm, Allocator &alloc, Dataspace_registry &ds_registry, Rpc_entrypoint &ep) { /* replay region map into new protection domain */ _stack_area .replay(dst_pd, dst_pd.stack_area_region_map(), local_rm, alloc, ds_registry, ep); _linker_area .replay(dst_pd, dst_pd.linker_area_region_map(), local_rm, alloc, ds_registry, ep); _address_space.replay(dst_pd, dst_pd.address_space_region_map(), local_rm, alloc, ds_registry, ep); Region_map &dst_address_space = dst_pd.address_space_region_map(); Region_map &dst_stack_area = dst_pd.stack_area_region_map(); Region_map &dst_linker_area = dst_pd.linker_area_region_map(); /* attach stack area */ dst_address_space.attach(dst_stack_area.dataspace(), Dataspace_client(dst_stack_area.dataspace()).size(), 0, true, _address_space.lookup_region_base(_stack_area.dataspace())); /* attach linker area */ dst_address_space.attach(dst_linker_area.dataspace(), Dataspace_client(dst_linker_area.dataspace()).size(), 0, true, _address_space.lookup_region_base(_linker_area.dataspace()), true); } /************************** ** Pd_session interface ** **************************/ void assign_parent(Capability parent) override { _pd.assign_parent(parent); } bool assign_pci(addr_t addr, uint16_t bdf) override { return _pd.assign_pci(addr, bdf); } void map(addr_t virt, addr_t size) override { return _pd.map(virt, size); } Signal_source_capability alloc_signal_source() override { return _with_automatic_cap_upgrade([&] () { return _pd.alloc_signal_source(); }); } void free_signal_source(Signal_source_capability cap) override { _pd.free_signal_source(cap); } Capability alloc_context(Signal_source_capability source, unsigned long imprint) override { return _with_automatic_cap_upgrade([&] () { return _pd.alloc_context(source, imprint); }); } void free_context(Capability cap) override { _pd.free_context(cap); } void submit(Capability context, unsigned cnt) override { _pd.submit(context, cnt); } Native_capability alloc_rpc_cap(Native_capability ep) override { return _with_automatic_cap_upgrade([&] () { return _pd.alloc_rpc_cap(ep); }); } void free_rpc_cap(Native_capability cap) override { _pd.free_rpc_cap(cap); } Capability address_space() override { return _address_space.Rpc_object::cap(); } Capability stack_area() override { return _stack_area.Rpc_object::cap(); } Capability linker_area() override { return _linker_area.Rpc_object::cap(); } void ref_account(Capability) override { } void transfer_quota(Capability, Cap_quota) override { } Cap_quota cap_quota() const override { return _pd.cap_quota(); } Cap_quota used_caps() const override { return _pd.used_caps(); } Ram_dataspace_capability alloc(size_t size, Cache_attribute cached) override { Ram_dataspace_capability ds_cap = _ram.alloc(size, cached); Ram_dataspace_info *ds_info = new (_alloc) Ram_dataspace_info(ds_cap); _ds_registry.insert(ds_info); _ds_list.insert(ds_info); _used_ram_quota = Ram_quota { _used_ram_quota.value + size }; return ds_cap; } void free(Ram_dataspace_capability ds_cap) override { Ram_dataspace_info *ds_info; auto lambda = [&] (Ram_dataspace_info *rdi) { ds_info = rdi; if (!ds_info) { error("RAM free: dataspace lookup failed"); return; } size_t const ds_size = rdi->size(); _ds_registry.remove(ds_info); ds_info->dissolve_users(); _ds_list.remove(ds_info); _ram.free(ds_cap); _used_ram_quota = Ram_quota { _used_ram_quota.value - ds_size }; }; _ds_registry.apply(ds_cap, lambda); destroy(_alloc, ds_info); } size_t dataspace_size(Ram_dataspace_capability ds_cap) const override { size_t result = 0; _ds_registry.apply(ds_cap, [&] (Ram_dataspace_info *rdi) { if (rdi) result = rdi->size(); }); return result; } void transfer_quota(Pd_session_capability, Ram_quota) override { } Ram_quota ram_quota() const override { return _pd.ram_quota(); } Ram_quota used_ram() const override { return Ram_quota{_used_ram_quota}; } Capability native_pd() override { return _pd.native_pd(); } }; #endif /* _NOUX__PD_SESSION_COMPONENT_H_ */