diff --git a/ports/src/noux/child.h b/ports/src/noux/child.h index ad819d9fc..588ea47e8 100644 --- a/ports/src/noux/child.h +++ b/ports/src/noux/child.h @@ -35,6 +35,7 @@ #include #include +#include namespace Noux { @@ -82,6 +83,11 @@ namespace Noux { */ Io_receptor_registry *io_receptor_registry(); + /** + * Return ELF binary of dynamic linker + */ + Dataspace_capability ldso_ds_cap(); + class Child; bool is_init_process(Child *child); @@ -181,9 +187,17 @@ namespace Noux { Local_ram_service _local_ram_service; Local_cpu_service _local_cpu_service; Local_rm_service _local_rm_service; + Local_rom_service _local_rom_service; Service_registry &_parent_services; + Static_dataspace_info _binary_ds_info; + Static_dataspace_info _sysio_ds_info; + Static_dataspace_info _ldso_ds_info; + Static_dataspace_info _args_ds_info; + Static_dataspace_info _env_ds_info; + Child_policy _child_policy; + Genode::Child _child; /** @@ -270,8 +284,7 @@ namespace Noux { _args(ARGS_DS_SIZE, args), _env(env), _root_dir(root_dir), - _binary_ds(forked ? Dataspace_capability() - : root_dir->dataspace(name)), + _binary_ds(root_dir->dataspace(name)), _sysio_ds(Genode::env()->ram_session(), SYSIO_DS_SIZE), _sysio(_sysio_ds.local_addr()), _noux_session_cap(Session_capability(_entrypoint.manage(this))), @@ -279,12 +292,20 @@ namespace Noux { _local_ram_service(_entrypoint), _local_cpu_service(_entrypoint, _resources.cpu.cpu_cap()), _local_rm_service(_entrypoint, _resources.ds_registry), + _local_rom_service(_entrypoint, _resources.ds_registry), _parent_services(parent_services), + _binary_ds_info(_resources.ds_registry, _binary_ds), + _sysio_ds_info(_resources.ds_registry, _sysio_ds.cap()), + _ldso_ds_info(_resources.ds_registry, ldso_ds_cap()), + _args_ds_info(_resources.ds_registry, _args.cap()), + _env_ds_info(_resources.ds_registry, _env.cap()), _child_policy(name, _binary_ds, _args.cap(), _env.cap(), _entrypoint, _local_noux_service, - _local_rm_service, _parent_services, + _local_rm_service, _local_rom_service, + _parent_services, *this, *this, _destruct_context_cap, _resources.ram), - _child(_binary_ds, _resources.ram.cap(), _resources.cpu.cap(), + _child(forked ? Dataspace_capability() : _binary_ds, + _resources.ram.cap(), _resources.cpu.cap(), _resources.rm.cap(), &_entrypoint, &_child_policy, /** * Override the implicit assignment to _parent_service diff --git a/ports/src/noux/child_policy.h b/ports/src/noux/child_policy.h index c02807cad..da3d77e1e 100644 --- a/ports/src/noux/child_policy.h +++ b/ports/src/noux/child_policy.h @@ -22,6 +22,7 @@ #include #include #include +#include namespace Noux { @@ -38,6 +39,7 @@ namespace Noux { Init::Child_policy_provide_rom_file _env_policy; Local_noux_service &_local_noux_service; Local_rm_service &_local_rm_service; + Local_rom_service &_local_rom_service; Service_registry &_parent_services; Family_member &_family_member; File_descriptor_registry &_file_descriptor_registry; @@ -53,6 +55,7 @@ namespace Noux { Rpc_entrypoint &entrypoint, Local_noux_service &local_noux_service, Local_rm_service &local_rm_service, + Local_rom_service &local_rom_service, Service_registry &parent_services, Family_member &family_member, File_descriptor_registry &file_descriptor_registry, @@ -66,6 +69,7 @@ namespace Noux { _env_policy( "env", env_ds, &entrypoint), _local_noux_service(local_noux_service), _local_rm_service(local_rm_service), + _local_rom_service(local_rom_service), _parent_services(parent_services), _family_member(family_member), _file_descriptor_registry(file_descriptor_registry), @@ -98,6 +102,12 @@ namespace Noux { if (strcmp(service_name, Rm_session::service_name()) == 0) return &_local_rm_service; + /* + * Check for local ROM service + */ + if (strcmp(service_name, Rom_session::service_name()) == 0) + return &_local_rom_service; + return _parent_services.find(service_name); } diff --git a/ports/src/noux/dataspace_registry.h b/ports/src/noux/dataspace_registry.h index faaba97fc..b3c8ce449 100644 --- a/ports/src/noux/dataspace_registry.h +++ b/ports/src/noux/dataspace_registry.h @@ -20,6 +20,13 @@ namespace Noux { class Dataspace_registry; + class Dataspace_info; + + + struct Dataspace_user : List::Element + { + virtual void dissolve(Dataspace_info &ds) = 0; + }; class Dataspace_info : public Object_pool::Entry @@ -28,6 +35,8 @@ namespace Noux { size_t _size; Dataspace_capability _ds_cap; + Lock _users_lock; + List _users; public: @@ -43,6 +52,32 @@ namespace Noux { size_t size() const { return _size; } Dataspace_capability ds_cap() const { return _ds_cap; } + void register_user(Dataspace_user &user) + { + Lock::Guard guard(_users_lock); + _users.insert(&user); + } + + void unregister_user(Dataspace_user &user) + { + Lock::Guard guard(_users_lock); + _users.remove(&user); + } + + void dissolve_users() + { + for (;;) { + Dataspace_user *user = 0; + { + Lock::Guard guard(_users_lock); + user = _users.first(); + if (!user) + break; + } + user->dissolve(*this); + } + } + /** * Create shadow copy of dataspace * @@ -112,6 +147,45 @@ namespace Noux { return _pool.lookup_and_lock(ds_cap); } }; + + + struct Static_dataspace_info : Dataspace_info + { + Dataspace_registry &_ds_registry; + + Static_dataspace_info(Dataspace_registry &ds_registry, + Dataspace_capability ds) + : Dataspace_info(ds), _ds_registry(ds_registry) + { + _ds_registry.insert(this); + } + + ~Static_dataspace_info() + { + Static_dataspace_info *info = + dynamic_cast(_ds_registry.lookup_info(ds_cap())); + + if (!info) { + PERR("lookup of binary ds info failed"); + return; + } + + info->dissolve_users(); + _ds_registry.remove(info); + } + + Dataspace_capability fork(Ram_session_capability, + Dataspace_registry &, + Rpc_entrypoint &) + { + return ds_cap(); + } + + void poke(addr_t dst_offset, void const *src, size_t len) + { + PERR("Attempt to poke onto a static dataspace"); + } + }; } #endif /* _NOUX__DATASPACE_REGISTRY_H_ */ diff --git a/ports/src/noux/local_ram_service.h b/ports/src/noux/local_ram_service.h index 64a4f4086..dbd057722 100644 --- a/ports/src/noux/local_ram_service.h +++ b/ports/src/noux/local_ram_service.h @@ -26,7 +26,7 @@ namespace Noux { { private: - Rpc_entrypoint &_ep; + Rpc_entrypoint &_ep; public: diff --git a/ports/src/noux/local_rm_service.h b/ports/src/noux/local_rm_service.h index f95c7d0df..7edd8a20c 100644 --- a/ports/src/noux/local_rm_service.h +++ b/ports/src/noux/local_rm_service.h @@ -133,6 +133,7 @@ namespace Noux { /* release dataspace info */ Dataspace_info *info = _ds_registry.lookup_info(ds_cap); if (info) { + info->dissolve_users(); _ds_registry.remove(info); destroy(env()->heap(), info); } else { diff --git a/ports/src/noux/local_rom_service.h b/ports/src/noux/local_rom_service.h new file mode 100644 index 000000000..c097cc3e8 --- /dev/null +++ b/ports/src/noux/local_rom_service.h @@ -0,0 +1,78 @@ +/* + * \brief ROM service provided to Noux processes + * \author Norman Feske + * \date 2013-07-18 + * + * The local ROM service has the sole purpose of tracking ROM dataspaces + * so that they are properly detached from RM sessions when the corresponding + * ROM sessions are closed. + */ + +/* + * Copyright (C) 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. + */ + +#ifndef _NOUX__LOCAL_ROM_SERVICE_H_ +#define _NOUX__LOCAL_ROM_SERVICE_H_ + +/* Genode includes */ +#include + +/* Noux includes */ +#include +#include + +namespace Noux { + + class Local_rom_service : public Service + { + private: + + Rpc_entrypoint &_ep; + Dataspace_registry &_ds_registry; + + public: + + Local_rom_service(Rpc_entrypoint &ep, Dataspace_registry &ds_registry) + : + Service(Rom_session::service_name()), _ep(ep), + _ds_registry(ds_registry) + { } + + Genode::Session_capability session(const char *args) + { + enum { NAME_MAX_LEN = 128 }; + char name[NAME_MAX_LEN]; + Arg_string::find_arg(args, "filename").string(name, sizeof(name), ""); + + Rom_session_component *rom = new (env()->heap()) + Rom_session_component(_ds_registry, name); + + return _ep.manage(rom); + } + + void upgrade(Genode::Session_capability, const char *args) { } + + void close(Genode::Session_capability session) + { + /* acquire locked session object */ + Rom_session_component *rom_session = + dynamic_cast(_ep.lookup_and_lock(session)); + + if (!rom_session) { + PWRN("Unexpected call of close with non-ROM-session argument"); + return; + } + + _ep.dissolve(rom_session); + + destroy(env()->heap(), rom_session); + + } + }; +} + +#endif /* _NOUX__LOCAL_ROM_SERVICE_H_ */ diff --git a/ports/src/noux/main.cc b/ports/src/noux/main.cc index 8ba1abec5..6caa94dfd 100644 --- a/ports/src/noux/main.cc +++ b/ports/src/noux/main.cc @@ -819,6 +819,18 @@ Terminal::Connection *Noux::terminal() } +Genode::Dataspace_capability Noux::ldso_ds_cap() +{ + try { + static Genode::Rom_connection rom("ld.lib.so"); + static Genode::Dataspace_capability ldso_ds = rom.dataspace(); + return ldso_ds; + } catch (...) { } + + return Genode::Dataspace_capability(); +} + + void *operator new (Genode::size_t size) { return Genode::env()->heap()->alloc(size); } @@ -828,11 +840,8 @@ int main(int argc, char **argv) using namespace Noux; PINF("--- noux started ---"); - /* look for dynamic linker */ - try { - static Genode::Rom_connection rom("ld.lib.so"); - Genode::Process::dynamic_linker(rom.dataspace()); - } catch (...) { } + /* register dynamic linker */ + Genode::Process::dynamic_linker(ldso_ds_cap()); /* whitelist of service requests to be routed to the parent */ static Genode::Service_registry parent_services; diff --git a/ports/src/noux/ram_session_component.h b/ports/src/noux/ram_session_component.h index 30247b045..38f758607 100644 --- a/ports/src/noux/ram_session_component.h +++ b/ports/src/noux/ram_session_component.h @@ -160,7 +160,9 @@ namespace Noux { PERR("RAM free: dataspace lookup failed"); return; } - + + ds_info->dissolve_users(); + _registry.remove(ds_info); _list.remove(ds_info); _used_quota -= ds_info->size(); diff --git a/ports/src/noux/rm_session_component.h b/ports/src/noux/rm_session_component.h index 1bcb49ca9..3118bd88d 100644 --- a/ports/src/noux/rm_session_component.h +++ b/ports/src/noux/rm_session_component.h @@ -24,6 +24,8 @@ namespace Noux { + static bool verbose_attach = false; + class Rm_session_component : public Rpc_object { private: @@ -31,17 +33,20 @@ namespace Noux { /** * Record of an attached dataspace */ - struct Region : List::Element + struct Region : List::Element, Dataspace_user { - Dataspace_capability ds; - size_t size; - off_t offset; - addr_t local_addr; + Rm_session_component &rm; + Dataspace_capability ds; + size_t size; + off_t offset; + addr_t local_addr; - Region(Dataspace_capability ds, size_t size, + Region(Rm_session_component &rm, + Dataspace_capability ds, size_t size, off_t offset, addr_t local_addr) : - ds(ds), size(size), offset(offset), local_addr(local_addr) + rm(rm), ds(ds), size(size), offset(offset), + local_addr(local_addr) { } /** @@ -52,6 +57,13 @@ namespace Noux { return (addr >= local_addr) && (addr < local_addr + size); } + + Region *next_region() + { + return List::Element::next(); + } + + inline void dissolve(Dataspace_info &ds); }; List _regions; @@ -59,7 +71,7 @@ namespace Noux { Region *_lookup_region_by_addr(addr_t local_addr) { Region *curr = _regions.first(); - for (; curr; curr = curr->next()) { + for (; curr; curr = curr->next_region()) { if (curr->contains(local_addr)) return curr; } @@ -103,7 +115,7 @@ namespace Noux { Dataspace_registry &ds_registry, Rpc_entrypoint &ep) { - for (Region *curr = _regions.first(); curr; curr = curr->next()) { + for (Region *curr = _regions.first(); curr; curr = curr->next_region()) { Dataspace_capability ds; @@ -121,6 +133,9 @@ namespace Noux { } else { + PERR("replay: missing ds_info for dataspace at addr 0x%lx", + curr->local_addr); + /* * If the dataspace is not a RAM dataspace, assume that * it's a ROM dataspace. @@ -195,21 +210,35 @@ namespace Noux { if (size == 0) size = Dataspace_client(ds).size() - offset; - /* - * XXX look if we can identify the specified dataspace. - * Is it a dataspace allocated via 'Local_ram_session'? - */ - local_addr = _rm.attach(ds, size, offset, use_local_addr, local_addr, executable); + Region *region = new (env()->heap()) + Region(*this, ds, size, offset, local_addr); + + /* register region as user of RAM dataspaces */ + Object_pool::Guard info(_ds_registry.lookup_info(ds)); + + + if (info) { + info->register_user(*region); + } else { + if (verbose_attach) { + PWRN("Trying to attach unknown dataspace type"); + PWRN(" ds_info@%p at 0x%lx size=%zd offset=0x%lx", + info.object(), (long)local_addr, + Dataspace_client(ds).size(), (long)offset); + } + } + /* * Record attachment for later replay (needed during * fork) */ - _regions.insert(new (env()->heap()) - Region(ds, size, offset, local_addr)); + _regions.insert(region); + + return local_addr; } @@ -225,6 +254,11 @@ namespace Noux { } _regions.remove(region); + + Object_pool::Guard info(_ds_registry.lookup_info(region->ds)); + if (info) + info->unregister_user(*region); + destroy(env()->heap(), region); } @@ -253,6 +287,20 @@ namespace Noux { return _rm.dataspace(); } }; + + + inline void Rm_session_component::Region::dissolve(Dataspace_info &ds) + { + /* + * When this function is called, the 'ds' is already locked by the + * caller. To prevent 'detach' from taking the lock twice + * ('_ds_registry.lookup_info'), the temporarily release and re-acquire + * the lock. + */ + ds.release(); + rm.detach(local_addr); + ds.acquire(); + } } #endif /* _NOUX__RM_SESSION_COMPONENT_H_ */ diff --git a/ports/src/noux/rom_session_component.h b/ports/src/noux/rom_session_component.h new file mode 100644 index 000000000..3d0756c60 --- /dev/null +++ b/ports/src/noux/rom_session_component.h @@ -0,0 +1,97 @@ +/* + * \brief ROM session implementation used by Noux processes + * \author Norman Feske + * \date 2013-07-18 + */ + +/* + * Copyright (C) 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. + */ + +#ifndef _NOUX__ROM_SESSION_COMPONENT_H_ +#define _NOUX__ROM_SESSION_COMPONENT_H_ + +/* Genode includes */ +#include +#include + +namespace Noux { + + struct Rom_dataspace_info : Dataspace_info + { + Rom_dataspace_info(Dataspace_capability ds) : Dataspace_info(ds) { } + + ~Rom_dataspace_info() { } + + Dataspace_capability fork(Ram_session_capability, + Dataspace_registry &ds_registry, + Rpc_entrypoint &) + { + return ds_cap(); + } + + void poke(addr_t dst_offset, void const *src, size_t len) + { + PERR("Attempt to poke onto a ROM dataspace"); + } + }; + + + class Rom_session_component : public Rpc_object + { + private: + + /** + * Wrapped ROM session at core + */ + Rom_connection _rom; + + Dataspace_registry &_ds_registry; + + Rom_dataspace_info _ds_info; + + public: + + Rom_session_component(Dataspace_registry &ds_registry, + char const *name) + : + _rom(name), _ds_registry(ds_registry), _ds_info(_rom.dataspace()) + { + _ds_registry.insert(&_ds_info); + } + + ~Rom_session_component() + { + /* + * Lookup and lock ds info instead of directly acccessing + * the '_ds_info' member. + */ + Object_pool::Guard + info(_ds_registry.lookup_info(_ds_info.ds_cap())); + + if (info) + info->dissolve_users(); + else + PERR("~Rom_session_component: unexpected !info"); + + _ds_registry.remove(&_ds_info); + } + + + /*************************** + ** ROM session interface ** + ***************************/ + + Rom_dataspace_capability dataspace() + { + return static_cap_cast(_ds_info.ds_cap()); + } + + void sigh(Signal_context_capability) { } + }; +} + +#endif /* _NOUX__ROM_SESSION_COMPONENT_H_ */